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