ViewAppSheetInfo.tsx135 lines · main
1import { formatDistanceToNow } from 'date-fns'
2import { Check, Copy } from 'lucide-react'
3import { useState } from 'react'
4import { toast } from 'sonner'
5import {
6 Badge,
7 Card,
8 CardContent,
9 copyToClipboard,
10 Table,
11 TableBody,
12 TableCell,
13 TableHead,
14 TableHeader,
15 TableRow,
16} from 'ui'
17
18import type { Installation, PrivateApp } from '../../PrivateAppsContext'
19
20interface ViewAppSheetInfoProps {
21 app: PrivateApp
22 isInstalled: boolean
23 installation: Installation | undefined
24}
25
26function CopyableId({ id, label }: { id: string; label: string }) {
27 const [isCopied, setIsCopied] = useState(false)
28
29 function handleCopy(e: React.MouseEvent) {
30 e.stopPropagation()
31 copyToClipboard(id, () => {
32 setIsCopied(true)
33 toast.success(`Copied ${label} to clipboard`)
34 setTimeout(() => setIsCopied(false), 2000)
35 })
36 }
37
38 return (
39 <button
40 onClick={handleCopy}
41 className="inline-flex items-center gap-x-1 cursor-pointer border border-transparent border-dashed rounded-sm transition-colors hover:bg-surface-100 hover:border hover:border-strong group font-mono text-xs text-foreground-light px-1 -ml-1"
42 >
43 <span className="truncate">{id}</span>
44 {isCopied ? (
45 <Check size={12} strokeWidth={1.25} className="text-brand shrink-0" />
46 ) : (
47 <Copy
48 size={12}
49 strokeWidth={1.25}
50 className="opacity-0 group-hover:opacity-100 transition-opacity shrink-0"
51 />
52 )}
53 </button>
54 )
55}
56
57export function ViewAppSheetInfo({ app, isInstalled, installation }: ViewAppSheetInfoProps) {
58 return (
59 <div className="px-5 sm:px-6 py-6 space-y-3">
60 <h3 className="text-sm font-medium text-foreground">Metadata</h3>
61 <Card className="w-full overflow-hidden bg-surface-100">
62 <CardContent className="p-0">
63 <Table className="table-fixed w-full">
64 <TableHeader>
65 <TableRow>
66 <TableHead className="text-left font-mono uppercase text-xs text-foreground-lighter h-auto py-2 w-[40%]">
67 Field
68 </TableHead>
69 <TableHead className="text-left font-mono uppercase text-xs text-foreground-lighter h-auto py-2 w-[60%]">
70 Value
71 </TableHead>
72 </TableRow>
73 </TableHeader>
74 <TableBody>
75 <TableRow>
76 <TableCell>
77 <p className="text-foreground-light truncate">Name</p>
78 </TableCell>
79 <TableCell>
80 <p className="font-medium truncate">{app.name}</p>
81 </TableCell>
82 </TableRow>
83 <TableRow>
84 <TableCell>
85 <p className="text-foreground-light truncate">Created</p>
86 </TableCell>
87 <TableCell>
88 <p className="truncate">
89 {formatDistanceToNow(new Date(app.created_at), { addSuffix: true })}
90 </p>
91 </TableCell>
92 </TableRow>
93 <TableRow>
94 <TableCell>
95 <p className="text-foreground-light truncate">Status</p>
96 </TableCell>
97 <TableCell>
98 {isInstalled ? (
99 <Badge variant="success" className="uppercase">
100 Installed
101 </Badge>
102 ) : (
103 <Badge variant="default" className="uppercase">
104 Uninstalled
105 </Badge>
106 )}
107 </TableCell>
108 </TableRow>
109 <TableRow>
110 <TableCell>
111 <p className="text-foreground-light truncate">App ID</p>
112 </TableCell>
113 <TableCell>
114 <CopyableId id={app.id} label="App ID" />
115 </TableCell>
116 </TableRow>
117 <TableRow>
118 <TableCell>
119 <p className="text-foreground-light truncate">Installation ID</p>
120 </TableCell>
121 <TableCell>
122 {installation ? (
123 <CopyableId id={installation.id} label="Installation ID" />
124 ) : (
125 <p className="font-mono text-xs text-foreground-light">—</p>
126 )}
127 </TableCell>
128 </TableRow>
129 </TableBody>
130 </Table>
131 </CardContent>
132 </Card>
133 </div>
134 )
135}