@burdenoff/microfe-cosmicintersection 2026.704.1 → 2026.704.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/cosmicintersection/CosmicIntersectionRoutes.js +45 -25
  2. package/dist/cosmicintersection/CosmicIntersectionRoutes.js.map +1 -1
  3. package/dist/cosmicintersection/api/cosmicApi.js +137 -66
  4. package/dist/cosmicintersection/api/cosmicApi.js.map +1 -1
  5. package/dist/cosmicintersection/components/ActivityFeedPanel.js +68 -0
  6. package/dist/cosmicintersection/components/ActivityFeedPanel.js.map +1 -0
  7. package/dist/cosmicintersection/components/DiscussionPanel.js +57 -0
  8. package/dist/cosmicintersection/components/DiscussionPanel.js.map +1 -0
  9. package/dist/cosmicintersection/components/FacetPanel.js +66 -0
  10. package/dist/cosmicintersection/components/FacetPanel.js.map +1 -0
  11. package/dist/cosmicintersection/components/LineageGraph.js +388 -0
  12. package/dist/cosmicintersection/components/LineageGraph.js.map +1 -0
  13. package/dist/cosmicintersection/components/ResultsChart.js +329 -0
  14. package/dist/cosmicintersection/components/ResultsChart.js.map +1 -0
  15. package/dist/cosmicintersection/components/StepDAG.js +104 -0
  16. package/dist/cosmicintersection/components/StepDAG.js.map +1 -0
  17. package/dist/cosmicintersection/components/index.js +5 -3
  18. package/dist/cosmicintersection/pages/access/AccessPage.js +220 -161
  19. package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -1
  20. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js +332 -172
  21. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -1
  22. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js +167 -109
  23. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js.map +1 -1
  24. package/dist/cosmicintersection/pages/collections/CollectionFormPage.js +87 -61
  25. package/dist/cosmicintersection/pages/collections/CollectionFormPage.js.map +1 -1
  26. package/dist/cosmicintersection/pages/collections/CollectionsPage.js +51 -42
  27. package/dist/cosmicintersection/pages/collections/CollectionsPage.js.map +1 -1
  28. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js +31 -18
  29. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -1
  30. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js +330 -184
  31. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js.map +1 -1
  32. package/dist/cosmicintersection/pages/datasets/DatasetFormPage.js +7 -7
  33. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js +321 -163
  34. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -1
  35. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js +358 -0
  36. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js.map +1 -0
  37. package/dist/cosmicintersection/pages/lineage/LineagePage.js +171 -0
  38. package/dist/cosmicintersection/pages/lineage/LineagePage.js.map +1 -0
  39. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js +339 -260
  40. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js.map +1 -1
  41. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js +143 -82
  42. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -1
  43. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js +286 -142
  44. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js.map +1 -1
  45. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js +305 -103
  46. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js.map +1 -1
  47. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js +183 -79
  48. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -1
  49. package/dist/cosmicintersection/pages/releases/ReleasesPage.js +346 -204
  50. package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -1
  51. package/dist/cosmicintersection/pages/runs/RunDetailPage.js +184 -136
  52. package/dist/cosmicintersection/pages/runs/RunDetailPage.js.map +1 -1
  53. package/dist/cosmicintersection/pages/runs/RunsPage.js +182 -120
  54. package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -1
  55. package/dist/cosmicintersection/pages/sources/SourceDetailPage.js +234 -0
  56. package/dist/cosmicintersection/pages/sources/SourceDetailPage.js.map +1 -0
  57. package/dist/cosmicintersection/pages/sources/SourcesPage.js +250 -133
  58. package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -1
  59. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AccessPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/access/AccessPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { KeyRound, ShieldCheck } from 'lucide-react';\nimport {\n Badge,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosAccessGrantsQuery,\n useCosDatasetsQuery,\n useCosGrantAccessMutation,\n useCosRevokeAccessMutation,\n type CosDataAccessGrant,\n type CosGrantAccessInput,\n type CosGrantStatus,\n} from '../../api/cosmicApi';\nimport {\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n StatusBadge,\n formatEnumLabel,\n} from '../../components';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { notify } from '../../lib/toast';\n\nconst ALL = 'ALL';\nconst GRANT_STATUSES: CosGrantStatus[] = ['PENDING', 'ACTIVE', 'EXPIRED', 'REVOKED'];\n\n/** Only live grants can be revoked. */\nfunction canRevoke(status: CosGrantStatus): boolean {\n return status === 'ACTIVE' || status === 'PENDING';\n}\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return '—';\n }\n return parsed.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\ninterface GrantFormState {\n datasetId: string;\n granteeId: string;\n scopes: string[];\n reason: string;\n expiresAt: string;\n}\n\nconst EMPTY_FORM: GrantFormState = {\n datasetId: '',\n granteeId: '',\n scopes: [],\n reason: '',\n expiresAt: '',\n};\n\nexport default function AccessPage() {\n const [datasetFilter, setDatasetFilter] = useState<string>(ALL);\n const [statusFilter, setStatusFilter] = useState<string>(ALL);\n\n const datasetsQuery = useCosDatasetsQuery({ pagination: { limit: 50, offset: 0 } });\n const datasets = useMemo(\n () => datasetsQuery.data?.cosListDatasets.items ?? [],\n [datasetsQuery.data]\n );\n const datasetName = useMemo(() => {\n const map = new Map<string, string>();\n for (const dataset of datasets) {\n map.set(dataset.id, dataset.name);\n }\n return map;\n }, [datasets]);\n\n const { data, loading, error, refetch } = useCosAccessGrantsQuery({\n datasetId: datasetFilter === ALL ? undefined : datasetFilter,\n status: statusFilter === ALL ? undefined : (statusFilter as CosGrantStatus),\n });\n const grants = useMemo(() => data?.cosListAccessGrants ?? [], [data]);\n\n const [grantAccess, { loading: granting }] = useCosGrantAccessMutation();\n const [revokeAccess] = useCosRevokeAccessMutation();\n\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<GrantFormState>(EMPTY_FORM);\n\n const openGrantDialog = () => {\n setForm({\n ...EMPTY_FORM,\n datasetId: datasetFilter === ALL ? '' : datasetFilter,\n });\n setDialogOpen(true);\n };\n\n const setField = <K extends keyof GrantFormState>(field: K, value: GrantFormState[K]) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const submitGrant = async () => {\n if (!form.datasetId) {\n notify.error('Dataset is required', 'Choose which dataset this grant applies to.');\n return;\n }\n if (!form.granteeId.trim()) {\n notify.error('Grantee is required', 'Enter the workspace User ID to grant access to.');\n return;\n }\n\n let expiresAt: string | undefined;\n if (form.expiresAt) {\n const parsed = new Date(form.expiresAt);\n if (Number.isNaN(parsed.getTime())) {\n notify.error('Invalid expiry', 'Enter a valid expiry date or leave it blank.');\n return;\n }\n expiresAt = parsed.toISOString();\n }\n\n const input: CosGrantAccessInput = {\n datasetId: form.datasetId,\n granteeId: form.granteeId.trim(),\n scopes: form.scopes.length ? form.scopes : undefined,\n reason: form.reason.trim() || undefined,\n expiresAt,\n };\n\n const response = await grantAccess({ variables: { input } });\n if (response.data?.cosGrantAccess?.id) {\n notify.success('Access granted');\n setDialogOpen(false);\n setForm(EMPTY_FORM);\n await refetch();\n }\n };\n\n const handleRevoke = async (grant: CosDataAccessGrant) => {\n const response = await revokeAccess({ variables: { id: grant.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.cosRevokeAccess?.id) {\n notify.error('Could not revoke grant', 'The server did not confirm the change.');\n throw new Error('Revoke not confirmed');\n }\n notify.success('Access revoked');\n await refetch();\n };\n\n const grantButton = (\n <Button type=\"button\" onClick={openGrantDialog}>\n <KeyRound className=\"size-4\" />\n Grant access\n </Button>\n );\n\n const showDatasetColumn = datasetFilter === ALL;\n\n const renderBody = () => {\n if (loading) {\n return <LoadingState variant=\"rows\" rows={6} label=\"Loading access grants…\" />;\n }\n if (error) {\n return <ErrorState message={error.message} onRetry={() => void refetch()} />;\n }\n if (grants.length === 0) {\n const filtered = datasetFilter !== ALL || statusFilter !== ALL;\n return (\n <EmptyState\n icon={ShieldCheck}\n title={filtered ? 'No grants match these filters' : 'No access grants yet'}\n description={\n filtered\n ? 'Try clearing the dataset or status filter to see more grants.'\n : 'Grant a workspace member scoped access to a restricted or embargoed dataset.'\n }\n action={filtered ? undefined : grantButton}\n />\n );\n }\n\n return (\n <div className=\"overflow-x-auto rounded-card border border-border-default bg-bg-elevated\">\n <Table>\n <TableHeader>\n <TableRow>\n {showDatasetColumn ? <TableHead>Dataset</TableHead> : null}\n <TableHead>Grantee</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Scopes</TableHead>\n <TableHead>Reason</TableHead>\n <TableHead>Granted</TableHead>\n <TableHead>Expires</TableHead>\n <TableHead>Revoked</TableHead>\n <TableHead className=\"text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {grants.map((grant) => (\n <TableRow key={grant.id}>\n {showDatasetColumn ? (\n <TableCell className=\"max-w-[12rem]\">\n <span className=\"line-clamp-1 text-sm text-text-primary\">\n {datasetName.get(grant.datasetId) ?? (\n <span className=\"font-mono text-xs text-text-secondary\">\n {grant.datasetId}\n </span>\n )}\n </span>\n </TableCell>\n ) : null}\n <TableCell className=\"max-w-[12rem]\">\n <span className=\"block truncate font-mono text-xs text-text-primary\">\n {grant.granteeId}\n </span>\n </TableCell>\n <TableCell>\n <StatusBadge status={grant.status} />\n </TableCell>\n <TableCell>\n <div className=\"flex max-w-[12rem] flex-wrap gap-1\">\n {grant.scopes.length === 0 ? (\n <span className=\"text-xs text-text-secondary\">—</span>\n ) : (\n grant.scopes.map((scope) => (\n <Badge key={scope} variant=\"outline\" className=\"text-xs\">\n {scope}\n </Badge>\n ))\n )}\n </div>\n </TableCell>\n <TableCell className=\"max-w-[14rem]\">\n {grant.reason ? (\n <span className=\"line-clamp-1 text-sm text-text-secondary\">{grant.reason}</span>\n ) : (\n <span className=\"text-sm text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(grant.grantedAt)}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(grant.expiresAt)}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(grant.revokedAt)}\n </TableCell>\n <TableCell className=\"text-right\">\n {canRevoke(grant.status) ? (\n <ActionMenu\n ariaLabel={`Actions for grant to ${grant.granteeId}`}\n entityName={grant.granteeId}\n onDelete={() => handleRevoke(grant)}\n deleteLabel=\"Revoke grant\"\n />\n ) : (\n <span className=\"text-xs text-text-secondary\">—</span>\n )}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n );\n };\n\n return (\n <PageLayout\n title=\"Data access\"\n description=\"Govern who can reach restricted and embargoed datasets, with scoped, expiring grants.\"\n actions={grantButton}\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"w-full max-w-[18rem]\">\n <Select value={datasetFilter} onValueChange={setDatasetFilter}>\n <SelectTrigger aria-label=\"Filter by dataset\">\n <SelectValue placeholder=\"All datasets\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All datasets</SelectItem>\n {datasets.map((dataset) => (\n <SelectItem key={dataset.id} value={dataset.id}>\n {dataset.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"w-full max-w-[14rem]\">\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger aria-label=\"Filter by status\">\n <SelectValue placeholder=\"All statuses\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All statuses</SelectItem>\n {GRANT_STATUSES.map((status) => (\n <SelectItem key={status} value={status}>\n {formatEnumLabel(status)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {renderBody()}\n </div>\n\n {/* Grant access dialog */}\n <Dialog\n open={dialogOpen}\n onOpenChange={(open) => (open || granting ? setDialogOpen(open) : setDialogOpen(false))}\n >\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>Grant data access</DialogTitle>\n <DialogDescription>\n Give a workspace member scoped access to a dataset. Access is identified by the\n member&apos;s User ID.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"grant-dataset\">Dataset</Label>\n <Select\n value={form.datasetId || undefined}\n onValueChange={(value) => setField('datasetId', value)}\n >\n <SelectTrigger id=\"grant-dataset\" aria-label=\"Dataset\">\n <SelectValue placeholder=\"Select a dataset\" />\n </SelectTrigger>\n <SelectContent>\n {datasets.map((dataset) => (\n <SelectItem key={dataset.id} value={dataset.id}>\n {dataset.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"grant-grantee\">Grantee (User ID)</Label>\n <Input\n id=\"grant-grantee\"\n value={form.granteeId}\n onChange={(event) => setField('granteeId', event.target.value)}\n placeholder=\"e.g. 4fe35990-…\"\n className=\"font-mono\"\n />\n </div>\n\n <EntityTagsField\n value={form.scopes}\n onChange={(scopes) => setField('scopes', scopes)}\n label=\"Scopes\"\n placeholder=\"e.g. read, download\"\n />\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"grant-reason\">Reason</Label>\n <Textarea\n id=\"grant-reason\"\n value={form.reason}\n onChange={(event) => setField('reason', event.target.value)}\n rows={2}\n placeholder=\"Why this member needs access.\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"grant-expires\">Expires (optional)</Label>\n <Input\n id=\"grant-expires\"\n type=\"datetime-local\"\n value={form.expiresAt}\n onChange={(event) => setField('expiresAt', event.target.value)}\n />\n <p className=\"text-xs text-text-secondary\">Leave blank for an open-ended grant.</p>\n </div>\n </div>\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setDialogOpen(false)}\n disabled={granting}\n >\n Cancel\n </Button>\n <Button type=\"button\" onClick={() => void submitGrant()} disabled={granting}>\n <KeyRound className=\"size-4\" />\n {granting ? 'Granting…' : 'Grant access'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AccessPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/access/AccessPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { KeyRound, ShieldCheck } from 'lucide-react';\nimport {\n Badge,\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosAccessGrantsQuery,\n useCosDatasetsQuery,\n useCosGrantAccessMutation,\n useCosRevokeAccessMutation,\n type CosDataAccessGrant,\n type CosGrantAccessInput,\n type CosGrantStatus,\n} from '../../api/cosmicApi';\nimport {\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n StatusBadge,\n formatEnumLabel,\n} from '../../components';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { notify } from '../../lib/toast';\n\nconst ALL = 'ALL';\nconst GRANT_STATUSES: CosGrantStatus[] = ['PENDING', 'ACTIVE', 'EXPIRED', 'REVOKED'];\n\n/** Only live grants can be revoked. */\nfunction canRevoke(status: CosGrantStatus): boolean {\n return status === 'ACTIVE' || status === 'PENDING';\n}\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return '—';\n }\n return parsed.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\ninterface GrantFormState {\n datasetId: string;\n granteeId: string;\n scopes: string[];\n reason: string;\n expiresAt: string;\n}\n\nconst EMPTY_FORM: GrantFormState = {\n datasetId: '',\n granteeId: '',\n scopes: [],\n reason: '',\n expiresAt: '',\n};\n\nexport default function AccessPage() {\n const [datasetFilter, setDatasetFilter] = useState<string>(ALL);\n const [statusFilter, setStatusFilter] = useState<string>(ALL);\n\n const datasetsQuery = useCosDatasetsQuery({ pagination: { limit: 50, offset: 0 } });\n const datasets = useMemo(\n () => datasetsQuery.data?.cosListDatasets.items ?? [],\n [datasetsQuery.data]\n );\n const datasetName = useMemo(() => {\n const map = new Map<string, string>();\n for (const dataset of datasets) {\n map.set(dataset.id, dataset.name);\n }\n return map;\n }, [datasets]);\n\n const { data, loading, error, refetch } = useCosAccessGrantsQuery({\n datasetId: datasetFilter === ALL ? undefined : datasetFilter,\n status: statusFilter === ALL ? undefined : (statusFilter as CosGrantStatus),\n });\n const grants = useMemo(() => data?.cosListAccessGrants ?? [], [data]);\n\n const [grantAccess, { loading: granting }] = useCosGrantAccessMutation();\n const [revokeAccess] = useCosRevokeAccessMutation();\n\n const [dialogOpen, setDialogOpen] = useState(false);\n const [form, setForm] = useState<GrantFormState>(EMPTY_FORM);\n\n const openGrantDialog = () => {\n setForm({\n ...EMPTY_FORM,\n datasetId: datasetFilter === ALL ? '' : datasetFilter,\n });\n setDialogOpen(true);\n };\n\n const setField = <K extends keyof GrantFormState>(field: K, value: GrantFormState[K]) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const submitGrant = async () => {\n if (!form.datasetId) {\n notify.error('Dataset is required', 'Choose which dataset this grant applies to.');\n return;\n }\n if (!form.granteeId.trim()) {\n notify.error('Grantee is required', 'Enter the workspace User ID to grant access to.');\n return;\n }\n\n let expiresAt: string | undefined;\n if (form.expiresAt) {\n const parsed = new Date(form.expiresAt);\n if (Number.isNaN(parsed.getTime())) {\n notify.error('Invalid expiry', 'Enter a valid expiry date or leave it blank.');\n return;\n }\n expiresAt = parsed.toISOString();\n }\n\n const input: CosGrantAccessInput = {\n datasetId: form.datasetId,\n granteeId: form.granteeId.trim(),\n scopes: form.scopes.length ? form.scopes : undefined,\n reason: form.reason.trim() || undefined,\n expiresAt,\n };\n\n const response = await grantAccess({ variables: { input } });\n if (response.data?.cosGrantAccess?.id) {\n notify.success('Access granted');\n setDialogOpen(false);\n setForm(EMPTY_FORM);\n await refetch();\n }\n };\n\n const handleRevoke = async (grant: CosDataAccessGrant) => {\n const response = await revokeAccess({ variables: { id: grant.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.cosRevokeAccess?.id) {\n notify.error('Could not revoke grant', 'The server did not confirm the change.');\n throw new Error('Revoke not confirmed');\n }\n notify.success('Access revoked');\n await refetch();\n };\n\n const grantButton = (\n <Button type=\"button\" onClick={openGrantDialog}>\n <KeyRound className=\"size-4\" />\n Grant access\n </Button>\n );\n\n const showDatasetColumn = datasetFilter === ALL;\n\n const renderBody = () => {\n if (loading) {\n return <LoadingState variant=\"rows\" rows={6} label=\"Loading access grants…\" />;\n }\n if (error) {\n return <ErrorState message={error.message} onRetry={() => void refetch()} />;\n }\n if (grants.length === 0) {\n const filtered = datasetFilter !== ALL || statusFilter !== ALL;\n return (\n <EmptyState\n icon={ShieldCheck}\n title={filtered ? 'No grants match these filters' : 'No access grants yet'}\n description={\n filtered\n ? 'Try clearing the dataset or status filter to see more grants.'\n : 'Grant a workspace member scoped access to a restricted or embargoed dataset.'\n }\n action={filtered ? undefined : grantButton}\n />\n );\n }\n\n return (\n <>\n {/* Desktop table */}\n <div className=\"hidden rounded-card border border-border-default bg-bg-elevated md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n {showDatasetColumn ? <TableHead>Dataset</TableHead> : null}\n <TableHead>Grantee</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Scopes</TableHead>\n <TableHead>Reason</TableHead>\n <TableHead>Granted</TableHead>\n <TableHead>Expires</TableHead>\n <TableHead>Revoked</TableHead>\n <TableHead className=\"text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {grants.map((grant) => (\n <TableRow key={grant.id}>\n {showDatasetColumn ? (\n <TableCell className=\"max-w-[12rem]\">\n <span className=\"line-clamp-1 text-sm text-text-primary\">\n {datasetName.get(grant.datasetId) ?? (\n <span className=\"font-mono text-xs text-text-secondary\">\n {grant.datasetId}\n </span>\n )}\n </span>\n </TableCell>\n ) : null}\n <TableCell className=\"max-w-[12rem]\">\n <span className=\"block truncate font-mono text-xs text-text-primary\">\n {grant.granteeId}\n </span>\n </TableCell>\n <TableCell>\n <StatusBadge status={grant.status} />\n </TableCell>\n <TableCell>\n <div className=\"flex max-w-[12rem] flex-wrap gap-1\">\n {grant.scopes.length === 0 ? (\n <span className=\"text-xs text-text-secondary\">—</span>\n ) : (\n grant.scopes.map((scope) => (\n <Badge key={scope} variant=\"outline\" className=\"text-xs\">\n {scope}\n </Badge>\n ))\n )}\n </div>\n </TableCell>\n <TableCell className=\"max-w-[14rem]\">\n {grant.reason ? (\n <span className=\"line-clamp-1 text-sm text-text-secondary\">\n {grant.reason}\n </span>\n ) : (\n <span className=\"text-sm text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(grant.grantedAt)}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(grant.expiresAt)}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(grant.revokedAt)}\n </TableCell>\n <TableCell className=\"text-right\">\n {canRevoke(grant.status) ? (\n <ActionMenu\n ariaLabel={`Actions for grant to ${grant.granteeId}`}\n entityName={grant.granteeId}\n onDelete={() => handleRevoke(grant)}\n deleteLabel=\"Revoke grant\"\n />\n ) : (\n <span className=\"text-xs text-text-secondary\">—</span>\n )}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n\n {/* Mobile card list */}\n <ul className=\"space-y-3 md:hidden\">\n {grants.map((grant) => (\n <li key={grant.id} className=\"rounded-card border border-border-default bg-bg-elevated\">\n <div className=\"flex items-start justify-between gap-2 p-4\">\n <div className=\"min-w-0 flex-1 space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n {showDatasetColumn ? (\n <span className=\"truncate text-sm font-medium text-text-primary\">\n {datasetName.get(grant.datasetId) ?? 'Dataset'}\n </span>\n ) : null}\n <StatusBadge status={grant.status} />\n </div>\n\n <div>\n <p className=\"text-xs text-text-secondary\">Grantee (User ID)</p>\n <p className=\"truncate font-mono text-xs text-text-primary\">\n {grant.granteeId}\n </p>\n </div>\n\n {grant.scopes.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {grant.scopes.map((scope) => (\n <Badge key={scope} variant=\"outline\" className=\"text-xs\">\n {scope}\n </Badge>\n ))}\n </div>\n ) : null}\n\n {grant.reason ? (\n <p className=\"line-clamp-2 text-xs text-text-secondary\">{grant.reason}</p>\n ) : null}\n\n <div className=\"flex flex-wrap gap-x-3 gap-y-1 text-xs text-text-secondary\">\n <span>Granted {formatDate(grant.grantedAt)}</span>\n <span>Expires {formatDate(grant.expiresAt)}</span>\n {grant.revokedAt ? <span>Revoked {formatDate(grant.revokedAt)}</span> : null}\n </div>\n </div>\n\n {canRevoke(grant.status) ? (\n <ActionMenu\n ariaLabel={`Actions for grant to ${grant.granteeId}`}\n entityName={grant.granteeId}\n onDelete={() => handleRevoke(grant)}\n deleteLabel=\"Revoke grant\"\n />\n ) : null}\n </div>\n </li>\n ))}\n </ul>\n </>\n );\n };\n\n return (\n <PageLayout\n title=\"Data access\"\n description=\"Govern who can reach restricted and embargoed datasets, with scoped, expiring grants.\"\n actions={grantButton}\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"w-full max-w-[18rem]\">\n <Select value={datasetFilter} onValueChange={setDatasetFilter}>\n <SelectTrigger aria-label=\"Filter by dataset\">\n <SelectValue placeholder=\"All datasets\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All datasets</SelectItem>\n {datasets.map((dataset) => (\n <SelectItem key={dataset.id} value={dataset.id}>\n {dataset.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"w-full max-w-[14rem]\">\n <Select value={statusFilter} onValueChange={setStatusFilter}>\n <SelectTrigger aria-label=\"Filter by status\">\n <SelectValue placeholder=\"All statuses\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All statuses</SelectItem>\n {GRANT_STATUSES.map((status) => (\n <SelectItem key={status} value={status}>\n {formatEnumLabel(status)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {renderBody()}\n </div>\n\n {/* Grant access dialog */}\n <Dialog\n open={dialogOpen}\n onOpenChange={(open) => (open || granting ? setDialogOpen(open) : setDialogOpen(false))}\n >\n <DialogContent className=\"max-h-[85vh] max-w-md overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>Grant data access</DialogTitle>\n <DialogDescription>\n Give a workspace member scoped access to a dataset. Access is identified by the\n member&apos;s User ID.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"grant-dataset\">Dataset</Label>\n <Select\n value={form.datasetId || undefined}\n onValueChange={(value) => setField('datasetId', value)}\n >\n <SelectTrigger id=\"grant-dataset\" aria-label=\"Dataset\">\n <SelectValue placeholder=\"Select a dataset\" />\n </SelectTrigger>\n <SelectContent>\n {datasets.map((dataset) => (\n <SelectItem key={dataset.id} value={dataset.id}>\n {dataset.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"grant-grantee\">Grantee (User ID)</Label>\n <Input\n id=\"grant-grantee\"\n value={form.granteeId}\n onChange={(event) => setField('granteeId', event.target.value)}\n placeholder=\"e.g. 4fe35990-…\"\n className=\"font-mono\"\n />\n <p className=\"text-xs text-text-secondary\">\n Paste the workspace member&apos;s User ID. You can copy it from the member&apos;s\n profile (&ldquo;Get ID&rdquo;) or the workspace member directory.\n </p>\n </div>\n\n <EntityTagsField\n value={form.scopes}\n onChange={(scopes) => setField('scopes', scopes)}\n label=\"Scopes\"\n placeholder=\"e.g. read, download\"\n />\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"grant-reason\">Reason</Label>\n <Textarea\n id=\"grant-reason\"\n value={form.reason}\n onChange={(event) => setField('reason', event.target.value)}\n rows={2}\n placeholder=\"Why this member needs access.\"\n />\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"grant-expires\">Expires (optional)</Label>\n <Input\n id=\"grant-expires\"\n type=\"datetime-local\"\n value={form.expiresAt}\n onChange={(event) => setField('expiresAt', event.target.value)}\n />\n <p className=\"text-xs text-text-secondary\">Leave blank for an open-ended grant.</p>\n </div>\n </div>\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setDialogOpen(false)}\n disabled={granting}\n >\n Cancel\n </Button>\n <Button type=\"button\" onClick={() => void submitGrant()} disabled={granting}>\n <KeyRound className=\"size-4\" />\n {granting ? 'Granting…' : 'Grant access'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}