ViewAppSheet.tsx 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { X } from 'lucide-react'
  2. import { useState } from 'react'
  3. import { toast } from 'sonner'
  4. import { Button, cn, ScrollArea, Sheet, SheetContent, SheetHeader } from 'ui'
  5. import { PrivateApp, usePrivateApps } from '../../PrivateAppsContext'
  6. import { PERMISSIONS, type Permission } from '../Apps.constants'
  7. import { DeleteAppModal } from '../DeleteAppModal'
  8. import { ViewAppSheetDangerZone } from './ViewAppSheetDangerZone'
  9. import { ViewAppSheetInfo } from './ViewAppSheetInfo'
  10. import { ViewAppSheetPermissions } from './ViewAppSheetPermissions'
  11. import { usePlatformAppDeleteMutation } from '@/data/platform-apps/platform-app-delete-mutation'
  12. import { usePlatformAppQuery } from '@/data/platform-apps/platform-app-query'
  13. interface ViewAppSheetProps {
  14. app: PrivateApp | null
  15. visible: boolean
  16. onClose: () => void
  17. onDeleted: () => void
  18. }
  19. export function ViewAppSheet({ app, visible, onClose, onDeleted }: ViewAppSheetProps) {
  20. const { slug, installations, removeInstallationsByAppId } = usePrivateApps()
  21. const installation = installations.find((i) => i.app_id === app?.id)
  22. const isInstalled = installation !== undefined
  23. const [showDeleteModal, setShowDeleteModal] = useState(false)
  24. const { data: detail, isLoading: isLoadingDetail } = usePlatformAppQuery(
  25. { slug, id: app?.id },
  26. { enabled: visible && app !== null }
  27. )
  28. const { mutate: deleteApp, isPending: isDeleting } = usePlatformAppDeleteMutation({
  29. onSuccess: (_, vars) => {
  30. toast.success(`Deleted "${app?.name}"`)
  31. removeInstallationsByAppId(vars.appId)
  32. setShowDeleteModal(false)
  33. onClose()
  34. onDeleted()
  35. },
  36. })
  37. function handleDelete() {
  38. if (!app || !slug) return
  39. deleteApp({ slug, appId: app.id })
  40. }
  41. const permissions: Permission[] = detail
  42. ? detail.permissions
  43. .map((id) => PERMISSIONS.find((p) => p.id === id))
  44. .filter((p): p is Permission => p !== undefined)
  45. : []
  46. return (
  47. <>
  48. <Sheet
  49. open={visible}
  50. onOpenChange={(open) => {
  51. if (!open) onClose()
  52. }}
  53. >
  54. <SheetContent
  55. showClose={false}
  56. size="default"
  57. className="min-w-[600px]! flex flex-col h-full gap-0"
  58. >
  59. <SheetHeader
  60. className={cn('flex flex-row justify-between gap-x-4 items-center border-b')}
  61. >
  62. <p className="truncate font-medium">{app?.name}</p>
  63. <Button type="text" icon={<X size={16} />} className="px-1" onClick={onClose} />
  64. </SheetHeader>
  65. <ScrollArea className="flex-1 max-h-[calc(100vh-60px)]">
  66. {app && (
  67. <div className="flex flex-col gap-0">
  68. <ViewAppSheetInfo app={app} isInstalled={isInstalled} installation={installation} />
  69. <ViewAppSheetPermissions permissions={permissions} isLoading={isLoadingDetail} />
  70. <ViewAppSheetDangerZone onDelete={() => setShowDeleteModal(true)} />
  71. </div>
  72. )}
  73. </ScrollArea>
  74. </SheetContent>
  75. </Sheet>
  76. <DeleteAppModal
  77. app={app}
  78. visible={showDeleteModal}
  79. isLoading={isDeleting}
  80. onClose={() => setShowDeleteModal(false)}
  81. onConfirm={handleDelete}
  82. />
  83. </>
  84. )
  85. }