@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":"NotebookDetailPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/notebooks/NotebookDetailPage.tsx"],"sourcesContent":["/**\n * NotebookDetailPage — full record for one notebook.\n *\n * Header renders the notebook's metadata (status, runtime, environment JSON,\n * description, sharing, attached file download). Below it an Analyses section\n * lists `cosListAnalyses(notebookId)` and a \"Run analysis\" dialog kicks off a\n * new `cosRunAnalysis` bound to this notebook. Archive is available from the\n * header action menu. No fabricated data — everything is read from the backend.\n */\nimport { useState, type FormEvent } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n Beaker,\n Download,\n FileCode2,\n MessageSquare,\n Pencil,\n Play,\n Share2,\n Sparkles,\n} 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 Switch,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosAnalysesQuery,\n useCosArchiveNotebookMutation,\n useCosNotebookQuery,\n useCosRunAnalysisMutation,\n type CosAnalysis,\n} from '../../api/cosmicApi';\nimport {\n ActionMenu,\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n SafeLink,\n StatusBadge,\n useFileDownloadUrl,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\n\nfunction formatDateTime(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return '—';\n }\n return date.toLocaleString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n/** Renders a presigned download link for an attached wspace-files artifact. */\nfunction FileDownloadLink({ fileId, label }: { fileId: string; label: string }) {\n const { data, loading } = useFileDownloadUrl(fileId);\n const url = data?.getFileDownloadUrl ?? null;\n if (loading) {\n return <span className=\"text-xs text-text-secondary\">Resolving link…</span>;\n }\n if (!url) {\n return <span className=\"text-xs text-text-secondary\">File {fileId}</span>;\n }\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1 text-sm text-text-primary underline-offset-2 hover:underline\"\n >\n <Download className=\"size-3.5\" aria-hidden=\"true\" />\n {label}\n </a>\n );\n}\n\ninterface RunAnalysisDialogProps {\n notebookId: string;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onCompleted: () => void;\n}\n\n/** Dialog to launch a new analysis on this notebook. */\nfunction RunAnalysisDialog({\n notebookId,\n open,\n onOpenChange,\n onCompleted,\n}: RunAnalysisDialogProps) {\n const [title, setTitle] = useState('');\n const [method, setMethod] = useState('');\n const [aiAssisted, setAiAssisted] = useState(false);\n const [runAnalysis, { loading }] = useCosRunAnalysisMutation();\n\n const reset = () => {\n setTitle('');\n setMethod('');\n setAiAssisted(false);\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const trimmed = title.trim();\n if (!trimmed) {\n notify.error('Title is required', 'Give the analysis a descriptive title.');\n return;\n }\n const response = await runAnalysis({\n variables: {\n input: {\n notebookId,\n title: trimmed,\n method: method.trim() || undefined,\n aiAssisted,\n },\n },\n });\n if (response.error || !response.data?.cosRunAnalysis?.id) {\n return;\n }\n notify.success('Analysis started', `“${trimmed}” is now running.`);\n reset();\n onOpenChange(false);\n onCompleted();\n };\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!loading) {\n onOpenChange(next);\n }\n }}\n >\n <DialogContent>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <DialogHeader>\n <DialogTitle>Run analysis</DialogTitle>\n <DialogDescription>\n Launch a new analysis run against this notebook. It starts in the running state and\n can be completed later with results.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-analysis-title\">Title</Label>\n <Input\n id=\"run-analysis-title\"\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n placeholder=\"e.g. Period-folding search\"\n required\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-analysis-method\">Method</Label>\n <Input\n id=\"run-analysis-method\"\n value={method}\n onChange={(event) => setMethod(event.target.value)}\n placeholder=\"e.g. box-least-squares\"\n />\n </div>\n <div className=\"flex items-center gap-3\">\n <Switch\n id=\"run-analysis-ai\"\n checked={aiAssisted}\n onCheckedChange={(checked) => setAiAssisted(checked)}\n />\n <Label htmlFor=\"run-analysis-ai\" className=\"cursor-pointer\">\n AI-assisted analysis\n </Label>\n </div>\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={loading}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={loading}>\n <Play className=\"size-4\" />\n {loading ? 'Starting…' : 'Run analysis'}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport default function NotebookDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useCosmicIntersection();\n const [runOpen, setRunOpen] = useState(false);\n\n const { data, loading, error, refetch } = useCosNotebookQuery(id);\n const {\n data: analysesData,\n loading: analysesLoading,\n error: analysesError,\n refetch: refetchAnalyses,\n } = useCosAnalysesQuery({ notebookId: id });\n const [archiveNotebook] = useCosArchiveNotebookMutation();\n\n const notebook = data?.cosNotebook ?? null;\n const analyses = analysesData?.cosListAnalyses ?? [];\n\n const handleArchive = async () => {\n if (!notebook) {\n return;\n }\n const response = await archiveNotebook({ variables: { id: notebook.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.cosArchiveNotebook) {\n notify.error('Could not archive notebook', 'The server did not confirm the change.');\n throw new Error('Archive not confirmed');\n }\n await refetch();\n notify.success('Notebook archived', `“${notebook.title}” was archived.`);\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Notebook\" backPath=\"notebooks\">\n <LoadingState label=\"Loading notebook…\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout title=\"Notebook\" backPath=\"notebooks\">\n <ErrorState\n message=\"We could not load this notebook. Please try again.\"\n onRetry={() => void refetch()}\n />\n </PageLayout>\n );\n }\n\n if (!notebook) {\n return (\n <PageLayout title=\"Notebook\" backPath=\"notebooks\">\n <EmptyState\n icon={FileCode2}\n title=\"Notebook not found.\"\n description=\"This notebook may have been removed or is outside your current scope.\"\n action={\n <Button type=\"button\" onClick={() => navigate('notebooks')}>\n Back to notebooks\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const environmentJson =\n notebook.environment && Object.keys(notebook.environment).length > 0\n ? JSON.stringify(notebook.environment, null, 2)\n : null;\n\n const actions = (\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n Run analysis\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => navigate(`notebooks/${notebook.id}/edit`)}\n >\n <Pencil className=\"size-4\" />\n Edit\n </Button>\n <ActionMenu\n ariaLabel={`Actions for ${notebook.title}`}\n entityName={notebook.title}\n onEdit={() => navigate(`notebooks/${notebook.id}/edit`)}\n onDelete={notebook.status === 'ARCHIVED' ? undefined : handleArchive}\n deleteLabel=\"Archive notebook\"\n />\n </div>\n );\n\n return (\n <PageLayout\n title={notebook.title}\n description=\"Notebook detail\"\n backPath=\"notebooks\"\n actions={actions}\n >\n <div className=\"space-y-6\">\n {/* Overview */}\n <section className=\"rounded-card border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <StatusBadge status={notebook.status} />\n {notebook.runtime ? <Badge variant=\"outline\">Runtime: {notebook.runtime}</Badge> : null}\n {notebook.isShared ? (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <Share2 className=\"size-3\" aria-hidden=\"true\" />\n Shared{notebook.shareSlug ? `: ${notebook.shareSlug}` : ''}\n </Badge>\n ) : (\n <Badge variant=\"outline\">Private</Badge>\n )}\n </div>\n\n {notebook.description ? (\n <p className=\"mt-4 text-sm text-text-secondary\">{notebook.description}</p>\n ) : (\n <p className=\"mt-4 text-sm text-text-secondary\">No description provided.</p>\n )}\n\n <dl className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <div>\n <dt className=\"text-xs uppercase tracking-wide text-text-secondary\">\n Linked datasets\n </dt>\n <dd className=\"mt-1 text-sm tabular-nums text-text-primary\">\n {notebook.datasetIds.length}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs uppercase tracking-wide text-text-secondary\">Created</dt>\n <dd className=\"mt-1 text-sm text-text-primary\">\n {formatDateTime(notebook.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs uppercase tracking-wide text-text-secondary\">Updated</dt>\n <dd className=\"mt-1 text-sm text-text-primary\">\n {formatDateTime(notebook.updatedAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs uppercase tracking-wide text-text-secondary\">Notebook file</dt>\n <dd className=\"mt-1 text-sm text-text-primary\">\n {notebook.file?.id ? (\n <FileDownloadLink fileId={notebook.file.id} label=\"Download notebook\" />\n ) : (\n <span className=\"text-text-secondary\">No file attached</span>\n )}\n </dd>\n </div>\n </dl>\n\n {notebook.tags.length > 0 ? (\n <div className=\"mt-6\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Tags</p>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {notebook.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\">\n {tag}\n </Badge>\n ))}\n </div>\n </div>\n ) : null}\n\n {environmentJson ? (\n <div className=\"mt-6\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Environment</p>\n <pre className=\"mt-2 max-h-64 overflow-auto rounded-card border border-border-default bg-bg-sunken p-4 font-mono text-xs text-text-primary\">\n {environmentJson}\n </pre>\n </div>\n ) : null}\n\n <div className=\"mt-6 flex flex-wrap items-center gap-2\">\n <SafeLink\n href=\"/conversations\"\n className=\"inline-flex items-center gap-1.5 rounded-button border border-border-default px-3 py-1.5 text-sm text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <MessageSquare className=\"size-4\" aria-hidden=\"true\" />\n Discuss in conversations\n </SafeLink>\n </div>\n </section>\n\n {/* Analyses */}\n <section className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Analyses</h2>\n <p className=\"text-sm text-text-secondary\">\n Analysis runs executed against this notebook.\n </p>\n </div>\n <Button type=\"button\" variant=\"outline\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n Run analysis\n </Button>\n </div>\n\n {analysesLoading ? (\n <LoadingState variant=\"rows\" rows={3} label=\"Loading analyses…\" />\n ) : analysesError ? (\n <ErrorState\n message=\"We could not load analyses for this notebook.\"\n onRetry={() => void refetchAnalyses()}\n />\n ) : analyses.length === 0 ? (\n <EmptyState\n icon={Beaker}\n title=\"No analyses yet.\"\n description=\"Run an analysis to generate results and artifacts from this notebook.\"\n action={\n <Button type=\"button\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n Run analysis\n </Button>\n }\n />\n ) : (\n <div className=\"rounded-card border border-border-default bg-bg-elevated\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Title</TableHead>\n <TableHead>Method</TableHead>\n <TableHead>AI</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Started</TableHead>\n <TableHead>Output</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {analyses.map((analysis: CosAnalysis) => (\n <TableRow key={analysis.id}>\n <TableCell className=\"font-medium text-text-primary\">\n {analysis.title}\n </TableCell>\n <TableCell className=\"text-text-secondary\">\n {analysis.method ?? '—'}\n </TableCell>\n <TableCell>\n {analysis.aiAssisted ? (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <Sparkles className=\"size-3\" aria-hidden=\"true\" />\n AI\n </Badge>\n ) : (\n <span className=\"text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell>\n <StatusBadge status={analysis.status} />\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDateTime(analysis.startedAt)}\n </TableCell>\n <TableCell>\n {analysis.outputFile?.id ? (\n <FileDownloadLink fileId={analysis.outputFile.id} label=\"Download\" />\n ) : analysis.results && Object.keys(analysis.results).length > 0 ? (\n <span className=\"text-xs text-text-secondary\">Results recorded</span>\n ) : (\n <span className=\"text-text-secondary\">—</span>\n )}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n )}\n </section>\n </div>\n\n <RunAnalysisDialog\n notebookId={notebook.id}\n open={runOpen}\n onOpenChange={setRunOpen}\n onCompleted={() => void refetchAnalyses()}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"NotebookDetailPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/notebooks/NotebookDetailPage.tsx"],"sourcesContent":["/**\n * NotebookDetailPage — full record for one notebook.\n *\n * Header renders the notebook's metadata (status, runtime, environment JSON,\n * description, sharing, attached file download). Below it an Analyses section\n * lists `cosListAnalyses(notebookId)` and a \"Run analysis\" dialog kicks off a\n * new `cosRunAnalysis` bound to this notebook. Archive is available from the\n * header action menu. No fabricated data — everything is read from the backend.\n */\nimport { useMemo, useState, type FormEvent } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n Beaker,\n Database,\n Download,\n FileCode2,\n MessageSquare,\n Pencil,\n Play,\n Share2,\n Sparkles,\n} 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 Switch,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosAnalysesQuery,\n useCosArchiveNotebookMutation,\n useCosDatasetsQuery,\n useCosNotebookQuery,\n useCosRunAnalysisMutation,\n type CosAnalysis,\n} from '../../api/cosmicApi';\nimport {\n ActionMenu,\n DiscussionPanel,\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n StatusBadge,\n useFileDownloadUrl,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\n\nfunction formatDateTime(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return '—';\n }\n return date.toLocaleString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n/** Renders a presigned download link for an attached wspace-files artifact. */\nfunction FileDownloadLink({ fileId, label }: { fileId: string; label: string }) {\n const { data, loading } = useFileDownloadUrl(fileId);\n const url = data?.getFileDownloadUrl ?? null;\n if (loading) {\n return <span className=\"text-xs text-text-secondary\">Resolving link…</span>;\n }\n if (!url) {\n return <span className=\"text-xs text-text-secondary\">File {fileId}</span>;\n }\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1 text-sm text-text-primary underline-offset-2 hover:underline\"\n >\n <Download className=\"size-3.5\" aria-hidden=\"true\" />\n {label}\n </a>\n );\n}\n\ninterface RunAnalysisDialogProps {\n notebookId: string;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onCompleted: () => void;\n}\n\n/** Dialog to launch a new analysis on this notebook. */\nfunction RunAnalysisDialog({\n notebookId,\n open,\n onOpenChange,\n onCompleted,\n}: RunAnalysisDialogProps) {\n const [title, setTitle] = useState('');\n const [method, setMethod] = useState('');\n const [aiAssisted, setAiAssisted] = useState(false);\n const [runAnalysis, { loading }] = useCosRunAnalysisMutation();\n\n const reset = () => {\n setTitle('');\n setMethod('');\n setAiAssisted(false);\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const trimmed = title.trim();\n if (!trimmed) {\n notify.error('Title is required', 'Give the analysis a descriptive title.');\n return;\n }\n const response = await runAnalysis({\n variables: {\n input: {\n notebookId,\n title: trimmed,\n method: method.trim() || undefined,\n aiAssisted,\n },\n },\n });\n if (response.error || !response.data?.cosRunAnalysis?.id) {\n return;\n }\n notify.success('Analysis started', `“${trimmed}” is now running.`);\n reset();\n onOpenChange(false);\n onCompleted();\n };\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!loading) {\n onOpenChange(next);\n }\n }}\n >\n <DialogContent>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <DialogHeader>\n <DialogTitle>Run analysis</DialogTitle>\n <DialogDescription>\n Launch a new analysis run against this notebook. It starts in the running state and\n can be completed later with results.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-analysis-title\">Title</Label>\n <Input\n id=\"run-analysis-title\"\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n placeholder=\"e.g. Period-folding search\"\n required\n />\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-analysis-method\">Method</Label>\n <Input\n id=\"run-analysis-method\"\n value={method}\n onChange={(event) => setMethod(event.target.value)}\n placeholder=\"e.g. box-least-squares\"\n />\n </div>\n <div className=\"flex items-center gap-3\">\n <Switch\n id=\"run-analysis-ai\"\n checked={aiAssisted}\n onCheckedChange={(checked) => setAiAssisted(checked)}\n />\n <Label htmlFor=\"run-analysis-ai\" className=\"cursor-pointer\">\n AI-assisted analysis\n </Label>\n </div>\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={loading}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={loading}>\n <Play className=\"size-4\" />\n {loading ? 'Starting…' : 'Run analysis'}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\nexport default function NotebookDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useCosmicIntersection();\n const [runOpen, setRunOpen] = useState(false);\n\n const { data, loading, error, refetch } = useCosNotebookQuery(id);\n const {\n data: analysesData,\n loading: analysesLoading,\n error: analysesError,\n refetch: refetchAnalyses,\n } = useCosAnalysesQuery({ notebookId: id });\n // Resolve the notebook's linked dataset UUIDs to human names for chips.\n const { data: datasetsData } = useCosDatasetsQuery({ pagination: { limit: 100, offset: 0 } });\n const [archiveNotebook] = useCosArchiveNotebookMutation();\n\n const notebook = data?.cosNotebook ?? null;\n const analyses = analysesData?.cosListAnalyses ?? [];\n\n const datasetNameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const dataset of datasetsData?.cosListDatasets.items ?? []) {\n map.set(dataset.id, dataset.name);\n }\n return map;\n }, [datasetsData]);\n\n const handleArchive = async () => {\n if (!notebook) {\n return;\n }\n const response = await archiveNotebook({ variables: { id: notebook.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.cosArchiveNotebook) {\n notify.error('Could not archive notebook', 'The server did not confirm the change.');\n throw new Error('Archive not confirmed');\n }\n await refetch();\n notify.success('Notebook archived', `“${notebook.title}” was archived.`);\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Notebook\" backPath=\"notebooks\">\n <LoadingState label=\"Loading notebook…\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout title=\"Notebook\" backPath=\"notebooks\">\n <ErrorState\n message=\"We could not load this notebook. Please try again.\"\n onRetry={() => void refetch()}\n />\n </PageLayout>\n );\n }\n\n if (!notebook) {\n return (\n <PageLayout title=\"Notebook\" backPath=\"notebooks\">\n <EmptyState\n icon={FileCode2}\n title=\"Notebook not found.\"\n description=\"This notebook may have been removed or is outside your current scope.\"\n action={\n <Button type=\"button\" onClick={() => navigate('notebooks')}>\n Back to notebooks\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const environmentJson =\n notebook.environment && Object.keys(notebook.environment).length > 0\n ? JSON.stringify(notebook.environment, null, 2)\n : null;\n\n const actions = (\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n Run analysis\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => navigate(`notebooks/${notebook.id}/edit`)}\n >\n <Pencil className=\"size-4\" />\n Edit\n </Button>\n <ActionMenu\n ariaLabel={`Actions for ${notebook.title}`}\n entityName={notebook.title}\n onDelete={notebook.status === 'ARCHIVED' ? undefined : handleArchive}\n deleteLabel=\"Archive notebook\"\n />\n </div>\n );\n\n return (\n <PageLayout\n title={notebook.title}\n description=\"Notebook detail\"\n backPath=\"notebooks\"\n actions={actions}\n >\n <div className=\"space-y-6\">\n {/* Overview */}\n <section className=\"rounded-card border border-border-default bg-bg-elevated p-6\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <StatusBadge status={notebook.status} />\n {notebook.runtime ? <Badge variant=\"outline\">Runtime: {notebook.runtime}</Badge> : null}\n {notebook.isShared ? (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <Share2 className=\"size-3\" aria-hidden=\"true\" />\n Shared{notebook.shareSlug ? `: ${notebook.shareSlug}` : ''}\n </Badge>\n ) : (\n <Badge variant=\"outline\">Private</Badge>\n )}\n </div>\n\n {notebook.description ? (\n <p className=\"mt-4 text-sm text-text-secondary\">{notebook.description}</p>\n ) : (\n <p className=\"mt-4 text-sm text-text-secondary\">No description provided.</p>\n )}\n\n <div className=\"mt-6\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Linked datasets</p>\n {notebook.datasetIds.length === 0 ? (\n <p className=\"mt-2 text-sm text-text-secondary\">No datasets linked.</p>\n ) : (\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {notebook.datasetIds.map((datasetId) => {\n const name = datasetNameById.get(datasetId);\n return (\n <Button\n key={datasetId}\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-auto gap-1.5 px-2.5 py-1\"\n onClick={() => navigate(`datasets/${datasetId}`)}\n >\n <Database className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className={name ? 'max-w-[16rem] truncate' : 'font-mono text-xs'}>\n {name ?? datasetId}\n </span>\n </Button>\n );\n })}\n </div>\n )}\n </div>\n\n <dl className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <div>\n <dt className=\"text-xs uppercase tracking-wide text-text-secondary\">Created</dt>\n <dd className=\"mt-1 text-sm text-text-primary\">\n {formatDateTime(notebook.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs uppercase tracking-wide text-text-secondary\">Updated</dt>\n <dd className=\"mt-1 text-sm text-text-primary\">\n {formatDateTime(notebook.updatedAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-xs uppercase tracking-wide text-text-secondary\">Notebook file</dt>\n <dd className=\"mt-1 text-sm text-text-primary\">\n {notebook.file?.id ? (\n <FileDownloadLink fileId={notebook.file.id} label=\"Download notebook\" />\n ) : (\n <span className=\"text-text-secondary\">No file attached</span>\n )}\n </dd>\n </div>\n </dl>\n\n {notebook.tags.length > 0 ? (\n <div className=\"mt-6\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Tags</p>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {notebook.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\">\n {tag}\n </Badge>\n ))}\n </div>\n </div>\n ) : null}\n\n {environmentJson ? (\n <div className=\"mt-6\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Environment</p>\n <pre className=\"mt-2 max-h-64 overflow-auto rounded-card border border-border-default bg-bg-sunken p-4 font-mono text-xs text-text-primary\">\n {environmentJson}\n </pre>\n </div>\n ) : null}\n </section>\n\n {/* Discussion */}\n <section className=\"space-y-4\">\n <div className=\"flex items-center gap-2\">\n <MessageSquare className=\"size-5 text-text-secondary\" aria-hidden=\"true\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">Discussion</h2>\n </div>\n <DiscussionPanel\n entityType=\"cosmicintersection-notebook\"\n entityId={notebook.id}\n title={notebook.title}\n widgetKey=\"notebook-discussion\"\n />\n </section>\n\n {/* Analyses */}\n <section className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Analyses</h2>\n <p className=\"text-sm text-text-secondary\">\n Analysis runs executed against this notebook.\n </p>\n </div>\n <Button type=\"button\" variant=\"outline\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n Run analysis\n </Button>\n </div>\n\n {analysesLoading ? (\n <LoadingState variant=\"rows\" rows={3} label=\"Loading analyses…\" />\n ) : analysesError ? (\n <ErrorState\n message=\"We could not load analyses for this notebook.\"\n onRetry={() => void refetchAnalyses()}\n />\n ) : analyses.length === 0 ? (\n <EmptyState\n icon={Beaker}\n title=\"No analyses yet.\"\n description=\"Run an analysis to generate results and artifacts from this notebook.\"\n action={\n <Button type=\"button\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n Run analysis\n </Button>\n }\n />\n ) : (\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 <TableHead>Title</TableHead>\n <TableHead>Method</TableHead>\n <TableHead>AI</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Started</TableHead>\n <TableHead>Output</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {analyses.map((analysis: CosAnalysis) => (\n <TableRow key={analysis.id}>\n <TableCell className=\"font-medium text-text-primary\">\n {analysis.title}\n </TableCell>\n <TableCell className=\"text-text-secondary\">\n {analysis.method ?? '—'}\n </TableCell>\n <TableCell>\n {analysis.aiAssisted ? (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <Sparkles className=\"size-3\" aria-hidden=\"true\" />\n AI\n </Badge>\n ) : (\n <span className=\"text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell>\n <StatusBadge status={analysis.status} />\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDateTime(analysis.startedAt)}\n </TableCell>\n <TableCell>\n {analysis.outputFile?.id ? (\n <FileDownloadLink fileId={analysis.outputFile.id} label=\"Download\" />\n ) : analysis.results && Object.keys(analysis.results).length > 0 ? (\n <span className=\"text-xs text-text-secondary\">Results recorded</span>\n ) : (\n <span className=\"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 {analyses.map((analysis: CosAnalysis) => (\n <li\n key={analysis.id}\n className=\"space-y-2 rounded-card border border-border-default bg-bg-elevated p-4\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{analysis.title}</p>\n {analysis.method ? (\n <p className=\"truncate text-xs text-text-secondary\">{analysis.method}</p>\n ) : null}\n </div>\n <StatusBadge status={analysis.status} />\n </div>\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-secondary\">\n {analysis.aiAssisted ? (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <Sparkles className=\"size-3\" aria-hidden=\"true\" />\n AI\n </Badge>\n ) : null}\n <span>Started {formatDateTime(analysis.startedAt)}</span>\n </div>\n <div>\n {analysis.outputFile?.id ? (\n <FileDownloadLink fileId={analysis.outputFile.id} label=\"Download\" />\n ) : analysis.results && Object.keys(analysis.results).length > 0 ? (\n <span className=\"text-xs text-text-secondary\">Results recorded</span>\n ) : (\n <span className=\"text-xs text-text-secondary\">No output</span>\n )}\n </div>\n </li>\n ))}\n </ul>\n </>\n )}\n </section>\n </div>\n\n <RunAnalysisDialog\n notebookId={notebook.id}\n open={runOpen}\n onOpenChange={setRunOpen}\n onCompleted={() => void refetchAnalyses()}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -10,11 +10,11 @@ import l, { usePagination as u } from "../../components/Pagination.js";
10
10
  import d from "../../components/StatusBadge.js";
11
11
  import "../../components/index.js";
12
12
  import { useState as f } from "react";
13
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
14
- import { NotebookText as h, Plus as g, Share2 as _ } from "lucide-react";
15
- import { Badge as v, Button as y, Select as b, SelectContent as x, SelectItem as S, SelectTrigger as C, SelectValue as w, Table as T, TableBody as E, TableCell as D, TableHead as O, TableHeader as k, TableRow as A } from "@burdenoff/fe-libs/ui";
13
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
14
+ import { NotebookText as g, Plus as _, Share2 as v } from "lucide-react";
15
+ import { Badge as y, Button as b, Select as x, SelectContent as S, SelectItem as C, SelectTrigger as w, SelectValue as T, Table as E, TableBody as D, TableCell as O, TableHead as k, TableHeader as A, TableRow as j } from "@burdenoff/fe-libs/ui";
16
16
  //#region src/cosmicintersection/pages/notebooks/NotebooksPage.tsx
17
- var j = [
17
+ var M = [
18
18
  {
19
19
  value: "DRAFT",
20
20
  label: "Draft"
@@ -31,8 +31,8 @@ var j = [
31
31
  value: "ARCHIVED",
32
32
  label: "Archived"
33
33
  }
34
- ], M = "ALL";
35
- function N(e) {
34
+ ], N = "ALL";
35
+ function P(e) {
36
36
  if (!e) return "—";
37
37
  let t = new Date(e);
38
38
  return Number.isNaN(t.getTime()) ? "—" : t.toLocaleDateString(void 0, {
@@ -41,163 +41,224 @@ function N(e) {
41
41
  day: "numeric"
42
42
  });
43
43
  }
44
- function P() {
45
- let { navigate: P } = e(), [F, I] = f(void 0), L = u({
44
+ function F() {
45
+ let { navigate: F } = e(), [I, L] = f(void 0), R = u({
46
46
  pageSize: 50,
47
- resetKey: F
48
- }), { data: R, loading: z, error: B, refetch: V } = r({
49
- status: F,
47
+ resetKey: I
48
+ }), { data: z, loading: B, error: V, refetch: H } = r({
49
+ status: I,
50
50
  pagination: {
51
- limit: L.limit,
52
- offset: L.offset
51
+ limit: R.limit,
52
+ offset: R.offset
53
53
  }
54
- }), [H] = n(), U = R?.cosListNotebooks.items ?? [], W = R?.cosListNotebooks.pageInfo, G = async (e) => {
55
- let n = await H({ variables: { id: e.id } });
54
+ }), [U] = n(), W = z?.cosListNotebooks.items ?? [], G = z?.cosListNotebooks.pageInfo, K = async (e) => {
55
+ let n = await U({ variables: { id: e.id } });
56
56
  if (n.error) throw Error(n.error.message);
57
57
  if (!n.data?.cosArchiveNotebook) throw t.error("Could not archive notebook", "The server did not confirm the change."), Error("Archive not confirmed");
58
- await V(), t.success("Notebook archived", `“${e.title}” was archived.`);
58
+ await H(), t.success("Notebook archived", `“${e.title}” was archived.`);
59
59
  };
60
- return /* @__PURE__ */ p(c, {
60
+ return /* @__PURE__ */ m(c, {
61
61
  title: "Notebooks",
62
62
  description: "Computational notebooks for running analyses over federated science datasets.",
63
- actions: /* @__PURE__ */ m(y, {
63
+ actions: /* @__PURE__ */ h(b, {
64
64
  type: "button",
65
- onClick: () => P("notebooks/create"),
66
- children: [/* @__PURE__ */ p(g, { className: "size-4" }), "New notebook"]
65
+ onClick: () => F("notebooks/create"),
66
+ children: [/* @__PURE__ */ m(_, { className: "size-4" }), "New notebook"]
67
67
  }),
68
- children: /* @__PURE__ */ m("div", {
68
+ children: /* @__PURE__ */ h("div", {
69
69
  className: "space-y-4",
70
70
  children: [
71
- /* @__PURE__ */ m("div", {
71
+ /* @__PURE__ */ h("div", {
72
72
  className: "flex flex-wrap items-center gap-3",
73
- children: [/* @__PURE__ */ p("label", {
73
+ children: [/* @__PURE__ */ m("label", {
74
74
  className: "text-sm text-text-secondary",
75
75
  htmlFor: "notebook-status-filter",
76
76
  children: "Status"
77
- }), /* @__PURE__ */ m(b, {
78
- value: F ?? M,
79
- onValueChange: (e) => I(e === M ? void 0 : e),
80
- children: [/* @__PURE__ */ p(C, {
77
+ }), /* @__PURE__ */ h(x, {
78
+ value: I ?? N,
79
+ onValueChange: (e) => L(e === N ? void 0 : e),
80
+ children: [/* @__PURE__ */ m(w, {
81
81
  id: "notebook-status-filter",
82
82
  className: "w-48",
83
83
  "aria-label": "Filter by status",
84
- children: /* @__PURE__ */ p(w, {})
85
- }), /* @__PURE__ */ m(x, { children: [/* @__PURE__ */ p(S, {
86
- value: M,
84
+ children: /* @__PURE__ */ m(T, {})
85
+ }), /* @__PURE__ */ h(S, { children: [/* @__PURE__ */ m(C, {
86
+ value: N,
87
87
  children: "All statuses"
88
- }), j.map((e) => /* @__PURE__ */ p(S, {
88
+ }), M.map((e) => /* @__PURE__ */ m(C, {
89
89
  value: e.value,
90
90
  children: e.label
91
91
  }, e.value))] })]
92
92
  })]
93
93
  }),
94
- z ? /* @__PURE__ */ p(s, {
94
+ B ? /* @__PURE__ */ m(s, {
95
95
  variant: "rows",
96
96
  rows: 6,
97
97
  label: "Loading notebooks…"
98
- }) : B ? /* @__PURE__ */ p(o, {
98
+ }) : V ? /* @__PURE__ */ m(o, {
99
99
  message: "We could not load your notebooks. Please try again.",
100
- onRetry: () => void V()
101
- }) : U.length === 0 ? /* @__PURE__ */ p(a, {
102
- icon: h,
103
- title: F ? "No notebooks match this filter." : "No notebooks yet.",
104
- description: F ? "Try a different status filter." : "Create your first notebook to start analysing federated datasets.",
105
- action: F ? void 0 : /* @__PURE__ */ m(y, {
100
+ onRetry: () => void H()
101
+ }) : W.length === 0 ? /* @__PURE__ */ m(a, {
102
+ icon: g,
103
+ title: I ? "No notebooks match this filter." : "No notebooks yet.",
104
+ description: I ? "Try a different status filter." : "Create your first notebook to start analysing federated datasets.",
105
+ action: I ? void 0 : /* @__PURE__ */ h(b, {
106
106
  type: "button",
107
- onClick: () => P("notebooks/create"),
108
- children: [/* @__PURE__ */ p(g, { className: "size-4" }), "New notebook"]
107
+ onClick: () => F("notebooks/create"),
108
+ children: [/* @__PURE__ */ m(_, { className: "size-4" }), "New notebook"]
109
109
  })
110
- }) : /* @__PURE__ */ p("div", {
111
- className: "rounded-card border border-border-default bg-bg-elevated",
112
- children: /* @__PURE__ */ m(T, { children: [/* @__PURE__ */ p(k, { children: /* @__PURE__ */ m(A, { children: [
113
- /* @__PURE__ */ p(O, { children: "Title" }),
114
- /* @__PURE__ */ p(O, { children: "Status" }),
115
- /* @__PURE__ */ p(O, { children: "Runtime" }),
116
- /* @__PURE__ */ p(O, {
110
+ }) : /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("div", {
111
+ className: "hidden rounded-card border border-border-default bg-bg-elevated md:block",
112
+ children: /* @__PURE__ */ h(E, { children: [/* @__PURE__ */ m(A, { children: /* @__PURE__ */ h(j, { children: [
113
+ /* @__PURE__ */ m(k, { children: "Title" }),
114
+ /* @__PURE__ */ m(k, { children: "Status" }),
115
+ /* @__PURE__ */ m(k, { children: "Runtime" }),
116
+ /* @__PURE__ */ m(k, {
117
117
  className: "text-right",
118
118
  children: "Datasets"
119
119
  }),
120
- /* @__PURE__ */ p(O, { children: "Sharing" }),
121
- /* @__PURE__ */ p(O, { children: "Tags" }),
122
- /* @__PURE__ */ p(O, { children: "Updated" }),
123
- /* @__PURE__ */ p(O, {
120
+ /* @__PURE__ */ m(k, { children: "Sharing" }),
121
+ /* @__PURE__ */ m(k, { children: "Tags" }),
122
+ /* @__PURE__ */ m(k, { children: "Updated" }),
123
+ /* @__PURE__ */ m(k, {
124
124
  className: "w-12 text-right",
125
125
  children: "Actions"
126
126
  })
127
- ] }) }), /* @__PURE__ */ p(E, { children: U.map((e) => /* @__PURE__ */ m(A, {
127
+ ] }) }), /* @__PURE__ */ m(D, { children: W.map((e) => /* @__PURE__ */ h(j, {
128
128
  className: "cursor-pointer",
129
- onClick: () => P(`notebooks/${e.id}`),
129
+ onClick: () => F(`notebooks/${e.id}`),
130
130
  children: [
131
- /* @__PURE__ */ p(D, {
131
+ /* @__PURE__ */ m(O, {
132
132
  className: "font-medium text-text-primary",
133
133
  children: e.title
134
134
  }),
135
- /* @__PURE__ */ p(D, { children: /* @__PURE__ */ p(d, { status: e.status }) }),
136
- /* @__PURE__ */ p(D, {
135
+ /* @__PURE__ */ m(O, { children: /* @__PURE__ */ m(d, { status: e.status }) }),
136
+ /* @__PURE__ */ m(O, {
137
137
  className: "text-text-secondary",
138
138
  children: e.runtime ?? "—"
139
139
  }),
140
- /* @__PURE__ */ p(D, {
140
+ /* @__PURE__ */ m(O, {
141
141
  className: "text-right tabular-nums text-text-secondary",
142
142
  children: e.datasetIds.length
143
143
  }),
144
- /* @__PURE__ */ p(D, { children: e.isShared ? /* @__PURE__ */ m(v, {
144
+ /* @__PURE__ */ m(O, { children: e.isShared ? /* @__PURE__ */ h(y, {
145
145
  variant: "secondary",
146
146
  className: "gap-1",
147
- children: [/* @__PURE__ */ p(_, {
147
+ children: [/* @__PURE__ */ m(v, {
148
148
  className: "size-3",
149
149
  "aria-hidden": "true"
150
150
  }), "Shared"]
151
- }) : /* @__PURE__ */ p("span", {
151
+ }) : /* @__PURE__ */ m("span", {
152
152
  className: "text-text-secondary",
153
153
  children: "Private"
154
154
  }) }),
155
- /* @__PURE__ */ p(D, { children: e.tags.length > 0 ? /* @__PURE__ */ m("div", {
155
+ /* @__PURE__ */ m(O, { children: e.tags.length > 0 ? /* @__PURE__ */ h("div", {
156
156
  className: "flex flex-wrap gap-1",
157
- children: [e.tags.slice(0, 3).map((e) => /* @__PURE__ */ p(v, {
157
+ children: [e.tags.slice(0, 3).map((e) => /* @__PURE__ */ m(y, {
158
158
  variant: "outline",
159
159
  children: e
160
- }, e)), e.tags.length > 3 ? /* @__PURE__ */ m("span", {
160
+ }, e)), e.tags.length > 3 ? /* @__PURE__ */ h("span", {
161
161
  className: "text-xs text-text-secondary",
162
162
  children: ["+", e.tags.length - 3]
163
163
  }) : null]
164
- }) : /* @__PURE__ */ p("span", {
164
+ }) : /* @__PURE__ */ m("span", {
165
165
  className: "text-text-secondary",
166
166
  children: "—"
167
167
  }) }),
168
- /* @__PURE__ */ p(D, {
168
+ /* @__PURE__ */ m(O, {
169
169
  className: "whitespace-nowrap text-text-secondary",
170
- children: N(e.updatedAt)
170
+ children: P(e.updatedAt)
171
171
  }),
172
- /* @__PURE__ */ p(D, {
172
+ /* @__PURE__ */ m(O, {
173
173
  className: "text-right",
174
- children: /* @__PURE__ */ p(i, {
174
+ children: /* @__PURE__ */ m(i, {
175
175
  ariaLabel: `Actions for ${e.title}`,
176
176
  entityName: e.title,
177
- onView: () => P(`notebooks/${e.id}`),
178
- onEdit: () => P(`notebooks/${e.id}/edit`),
179
- onDelete: e.status === "ARCHIVED" ? void 0 : () => G(e),
177
+ onView: () => F(`notebooks/${e.id}`),
178
+ onEdit: () => F(`notebooks/${e.id}/edit`),
179
+ onDelete: e.status === "ARCHIVED" ? void 0 : () => K(e),
180
180
  deleteLabel: "Archive notebook"
181
181
  })
182
182
  })
183
183
  ]
184
184
  }, e.id)) })] })
185
- }),
186
- !z && !B && U.length > 0 ? /* @__PURE__ */ p(l, {
187
- offset: L.offset,
188
- limit: L.limit,
189
- loadedCount: U.length,
190
- hasMore: W?.hasMore ?? !1,
191
- onPrev: L.prevPage,
192
- onNext: L.nextPage,
185
+ }), /* @__PURE__ */ m("ul", {
186
+ className: "space-y-3 md:hidden",
187
+ children: W.map((e) => /* @__PURE__ */ m("li", {
188
+ className: "rounded-card border border-border-default bg-bg-elevated",
189
+ children: /* @__PURE__ */ h("div", {
190
+ className: "flex items-start justify-between gap-2 p-4",
191
+ children: [/* @__PURE__ */ h("button", {
192
+ type: "button",
193
+ onClick: () => F(`notebooks/${e.id}`),
194
+ className: "min-w-0 flex-1 space-y-2 text-left",
195
+ children: [
196
+ /* @__PURE__ */ h("div", {
197
+ className: "flex flex-wrap items-center gap-2",
198
+ children: [/* @__PURE__ */ m("span", {
199
+ className: "truncate font-medium text-text-primary",
200
+ children: e.title
201
+ }), /* @__PURE__ */ m(d, { status: e.status })]
202
+ }),
203
+ /* @__PURE__ */ h("div", {
204
+ className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary",
205
+ children: [
206
+ /* @__PURE__ */ h("span", { children: ["Runtime: ", e.runtime ?? "—"] }),
207
+ /* @__PURE__ */ h("span", {
208
+ className: "tabular-nums",
209
+ children: [
210
+ e.datasetIds.length,
211
+ " dataset",
212
+ e.datasetIds.length === 1 ? "" : "s"
213
+ ]
214
+ }),
215
+ e.isShared ? /* @__PURE__ */ h("span", {
216
+ className: "inline-flex items-center gap-1",
217
+ children: [/* @__PURE__ */ m(v, {
218
+ className: "size-3",
219
+ "aria-hidden": "true"
220
+ }), "Shared"]
221
+ }) : /* @__PURE__ */ m("span", { children: "Private" }),
222
+ /* @__PURE__ */ h("span", { children: ["Updated ", P(e.updatedAt)] })
223
+ ]
224
+ }),
225
+ e.tags.length > 0 ? /* @__PURE__ */ h("div", {
226
+ className: "flex flex-wrap gap-1",
227
+ children: [e.tags.slice(0, 4).map((e) => /* @__PURE__ */ m(y, {
228
+ variant: "outline",
229
+ children: e
230
+ }, e)), e.tags.length > 4 ? /* @__PURE__ */ h("span", {
231
+ className: "text-xs text-text-secondary",
232
+ children: ["+", e.tags.length - 4]
233
+ }) : null]
234
+ }) : null
235
+ ]
236
+ }), /* @__PURE__ */ m(i, {
237
+ ariaLabel: `Actions for ${e.title}`,
238
+ entityName: e.title,
239
+ onView: () => F(`notebooks/${e.id}`),
240
+ onEdit: () => F(`notebooks/${e.id}/edit`),
241
+ onDelete: e.status === "ARCHIVED" ? void 0 : () => K(e),
242
+ deleteLabel: "Archive notebook"
243
+ })]
244
+ })
245
+ }, e.id))
246
+ })] }),
247
+ !B && !V && W.length > 0 ? /* @__PURE__ */ m(l, {
248
+ offset: R.offset,
249
+ limit: R.limit,
250
+ loadedCount: W.length,
251
+ hasMore: G?.hasMore ?? !1,
252
+ onPrev: R.prevPage,
253
+ onNext: R.nextPage,
193
254
  itemLabel: "notebooks",
194
- loading: z
255
+ loading: B
195
256
  }) : null
196
257
  ]
197
258
  })
198
259
  });
199
260
  }
200
261
  //#endregion
201
- export { P as default };
262
+ export { F as default };
202
263
 
203
264
  //# sourceMappingURL=NotebooksPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NotebooksPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/notebooks/NotebooksPage.tsx"],"sourcesContent":["/**\n * NotebooksPage — paginated list of analysis notebooks.\n *\n * Notebooks are computational documents (title, runtime, environment, linked\n * datasets) that scientists run analyses inside. This surface lists them from\n * `cosListNotebooks` with a status filter, per-row View / Edit / Archive\n * actions, and offset pagination. Every value here comes from the backend —\n * there is no fabricated data.\n */\nimport { useState } from 'react';\nimport { NotebookText, Plus, Share2 } from 'lucide-react';\nimport {\n Badge,\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosArchiveNotebookMutation,\n useCosNotebooksQuery,\n type CosNotebook,\n type CosNotebookStatus,\n} from '../../api/cosmicApi';\nimport {\n ActionMenu,\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n Pagination,\n StatusBadge,\n usePagination,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\n\nconst STATUS_OPTIONS: { value: CosNotebookStatus; label: string }[] = [\n { value: 'DRAFT', label: 'Draft' },\n { value: 'RUNNING', label: 'Running' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nconst ALL = 'ALL';\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return '—';\n }\n return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\nexport default function NotebooksPage() {\n const { navigate } = useCosmicIntersection();\n const [statusFilter, setStatusFilter] = useState<CosNotebookStatus | undefined>(undefined);\n const pagination = usePagination({ pageSize: 50, resetKey: statusFilter });\n\n const { data, loading, error, refetch } = useCosNotebooksQuery({\n status: statusFilter,\n pagination: { limit: pagination.limit, offset: pagination.offset },\n });\n const [archiveNotebook] = useCosArchiveNotebookMutation();\n\n const notebooks = data?.cosListNotebooks.items ?? [];\n const pageInfo = data?.cosListNotebooks.pageInfo;\n\n const handleArchive = async (notebook: CosNotebook) => {\n const response = await archiveNotebook({ variables: { id: notebook.id } });\n if (response.error) {\n // onError already surfaced the toast; rethrow so the confirm dialog stays open.\n throw new Error(response.error.message);\n }\n if (!response.data?.cosArchiveNotebook) {\n notify.error('Could not archive notebook', 'The server did not confirm the change.');\n throw new Error('Archive not confirmed');\n }\n await refetch();\n notify.success('Notebook archived', `“${notebook.title}” was archived.`);\n };\n\n const actions = (\n <Button type=\"button\" onClick={() => navigate('notebooks/create')}>\n <Plus className=\"size-4\" />\n New notebook\n </Button>\n );\n\n return (\n <PageLayout\n title=\"Notebooks\"\n description=\"Computational notebooks for running analyses over federated science datasets.\"\n actions={actions}\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"text-sm text-text-secondary\" htmlFor=\"notebook-status-filter\">\n Status\n </label>\n <Select\n value={statusFilter ?? ALL}\n onValueChange={(value) =>\n setStatusFilter(value === ALL ? undefined : (value as CosNotebookStatus))\n }\n >\n <SelectTrigger\n id=\"notebook-status-filter\"\n className=\"w-48\"\n aria-label=\"Filter by status\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All statuses</SelectItem>\n {STATUS_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {loading ? (\n <LoadingState variant=\"rows\" rows={6} label=\"Loading notebooks…\" />\n ) : error ? (\n <ErrorState\n message=\"We could not load your notebooks. Please try again.\"\n onRetry={() => void refetch()}\n />\n ) : notebooks.length === 0 ? (\n <EmptyState\n icon={NotebookText}\n title={statusFilter ? 'No notebooks match this filter.' : 'No notebooks yet.'}\n description={\n statusFilter\n ? 'Try a different status filter.'\n : 'Create your first notebook to start analysing federated datasets.'\n }\n action={\n statusFilter ? undefined : (\n <Button type=\"button\" onClick={() => navigate('notebooks/create')}>\n <Plus className=\"size-4\" />\n New notebook\n </Button>\n )\n }\n />\n ) : (\n <div className=\"rounded-card border border-border-default bg-bg-elevated\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Title</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Runtime</TableHead>\n <TableHead className=\"text-right\">Datasets</TableHead>\n <TableHead>Sharing</TableHead>\n <TableHead>Tags</TableHead>\n <TableHead>Updated</TableHead>\n <TableHead className=\"w-12 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {notebooks.map((notebook) => (\n <TableRow\n key={notebook.id}\n className=\"cursor-pointer\"\n onClick={() => navigate(`notebooks/${notebook.id}`)}\n >\n <TableCell className=\"font-medium text-text-primary\">\n {notebook.title}\n </TableCell>\n <TableCell>\n <StatusBadge status={notebook.status} />\n </TableCell>\n <TableCell className=\"text-text-secondary\">{notebook.runtime ?? '—'}</TableCell>\n <TableCell className=\"text-right tabular-nums text-text-secondary\">\n {notebook.datasetIds.length}\n </TableCell>\n <TableCell>\n {notebook.isShared ? (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <Share2 className=\"size-3\" aria-hidden=\"true\" />\n Shared\n </Badge>\n ) : (\n <span className=\"text-text-secondary\">Private</span>\n )}\n </TableCell>\n <TableCell>\n {notebook.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {notebook.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"outline\">\n {tag}\n </Badge>\n ))}\n {notebook.tags.length > 3 ? (\n <span className=\"text-xs text-text-secondary\">\n +{notebook.tags.length - 3}\n </span>\n ) : null}\n </div>\n ) : (\n <span className=\"text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDate(notebook.updatedAt)}\n </TableCell>\n <TableCell className=\"text-right\">\n <ActionMenu\n ariaLabel={`Actions for ${notebook.title}`}\n entityName={notebook.title}\n onView={() => navigate(`notebooks/${notebook.id}`)}\n onEdit={() => navigate(`notebooks/${notebook.id}/edit`)}\n onDelete={\n notebook.status === 'ARCHIVED' ? undefined : () => handleArchive(notebook)\n }\n deleteLabel=\"Archive notebook\"\n />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n )}\n\n {!loading && !error && notebooks.length > 0 ? (\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={notebooks.length}\n hasMore={pageInfo?.hasMore ?? false}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"notebooks\"\n loading={loading}\n />\n ) : null}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"NotebooksPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/notebooks/NotebooksPage.tsx"],"sourcesContent":["/**\n * NotebooksPage — paginated list of analysis notebooks.\n *\n * Notebooks are computational documents (title, runtime, environment, linked\n * datasets) that scientists run analyses inside. This surface lists them from\n * `cosListNotebooks` with a status filter, per-row View / Edit / Archive\n * actions, and offset pagination. Every value here comes from the backend —\n * there is no fabricated data.\n */\nimport { useState } from 'react';\nimport { NotebookText, Plus, Share2 } from 'lucide-react';\nimport {\n Badge,\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosArchiveNotebookMutation,\n useCosNotebooksQuery,\n type CosNotebook,\n type CosNotebookStatus,\n} from '../../api/cosmicApi';\nimport {\n ActionMenu,\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n Pagination,\n StatusBadge,\n usePagination,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\n\nconst STATUS_OPTIONS: { value: CosNotebookStatus; label: string }[] = [\n { value: 'DRAFT', label: 'Draft' },\n { value: 'RUNNING', label: 'Running' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nconst ALL = 'ALL';\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return '—';\n }\n return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\nexport default function NotebooksPage() {\n const { navigate } = useCosmicIntersection();\n const [statusFilter, setStatusFilter] = useState<CosNotebookStatus | undefined>(undefined);\n const pagination = usePagination({ pageSize: 50, resetKey: statusFilter });\n\n const { data, loading, error, refetch } = useCosNotebooksQuery({\n status: statusFilter,\n pagination: { limit: pagination.limit, offset: pagination.offset },\n });\n const [archiveNotebook] = useCosArchiveNotebookMutation();\n\n const notebooks = data?.cosListNotebooks.items ?? [];\n const pageInfo = data?.cosListNotebooks.pageInfo;\n\n const handleArchive = async (notebook: CosNotebook) => {\n const response = await archiveNotebook({ variables: { id: notebook.id } });\n if (response.error) {\n // onError already surfaced the toast; rethrow so the confirm dialog stays open.\n throw new Error(response.error.message);\n }\n if (!response.data?.cosArchiveNotebook) {\n notify.error('Could not archive notebook', 'The server did not confirm the change.');\n throw new Error('Archive not confirmed');\n }\n await refetch();\n notify.success('Notebook archived', `“${notebook.title}” was archived.`);\n };\n\n const actions = (\n <Button type=\"button\" onClick={() => navigate('notebooks/create')}>\n <Plus className=\"size-4\" />\n New notebook\n </Button>\n );\n\n return (\n <PageLayout\n title=\"Notebooks\"\n description=\"Computational notebooks for running analyses over federated science datasets.\"\n actions={actions}\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"text-sm text-text-secondary\" htmlFor=\"notebook-status-filter\">\n Status\n </label>\n <Select\n value={statusFilter ?? ALL}\n onValueChange={(value) =>\n setStatusFilter(value === ALL ? undefined : (value as CosNotebookStatus))\n }\n >\n <SelectTrigger\n id=\"notebook-status-filter\"\n className=\"w-48\"\n aria-label=\"Filter by status\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All statuses</SelectItem>\n {STATUS_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {loading ? (\n <LoadingState variant=\"rows\" rows={6} label=\"Loading notebooks…\" />\n ) : error ? (\n <ErrorState\n message=\"We could not load your notebooks. Please try again.\"\n onRetry={() => void refetch()}\n />\n ) : notebooks.length === 0 ? (\n <EmptyState\n icon={NotebookText}\n title={statusFilter ? 'No notebooks match this filter.' : 'No notebooks yet.'}\n description={\n statusFilter\n ? 'Try a different status filter.'\n : 'Create your first notebook to start analysing federated datasets.'\n }\n action={\n statusFilter ? undefined : (\n <Button type=\"button\" onClick={() => navigate('notebooks/create')}>\n <Plus className=\"size-4\" />\n New notebook\n </Button>\n )\n }\n />\n ) : (\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 <TableHead>Title</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Runtime</TableHead>\n <TableHead className=\"text-right\">Datasets</TableHead>\n <TableHead>Sharing</TableHead>\n <TableHead>Tags</TableHead>\n <TableHead>Updated</TableHead>\n <TableHead className=\"w-12 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {notebooks.map((notebook) => (\n <TableRow\n key={notebook.id}\n className=\"cursor-pointer\"\n onClick={() => navigate(`notebooks/${notebook.id}`)}\n >\n <TableCell className=\"font-medium text-text-primary\">\n {notebook.title}\n </TableCell>\n <TableCell>\n <StatusBadge status={notebook.status} />\n </TableCell>\n <TableCell className=\"text-text-secondary\">\n {notebook.runtime ?? '—'}\n </TableCell>\n <TableCell className=\"text-right tabular-nums text-text-secondary\">\n {notebook.datasetIds.length}\n </TableCell>\n <TableCell>\n {notebook.isShared ? (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <Share2 className=\"size-3\" aria-hidden=\"true\" />\n Shared\n </Badge>\n ) : (\n <span className=\"text-text-secondary\">Private</span>\n )}\n </TableCell>\n <TableCell>\n {notebook.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {notebook.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"outline\">\n {tag}\n </Badge>\n ))}\n {notebook.tags.length > 3 ? (\n <span className=\"text-xs text-text-secondary\">\n +{notebook.tags.length - 3}\n </span>\n ) : null}\n </div>\n ) : (\n <span className=\"text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDate(notebook.updatedAt)}\n </TableCell>\n <TableCell className=\"text-right\">\n <ActionMenu\n ariaLabel={`Actions for ${notebook.title}`}\n entityName={notebook.title}\n onView={() => navigate(`notebooks/${notebook.id}`)}\n onEdit={() => navigate(`notebooks/${notebook.id}/edit`)}\n onDelete={\n notebook.status === 'ARCHIVED'\n ? undefined\n : () => handleArchive(notebook)\n }\n deleteLabel=\"Archive notebook\"\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 {notebooks.map((notebook) => (\n <li\n key={notebook.id}\n className=\"rounded-card border border-border-default bg-bg-elevated\"\n >\n <div className=\"flex items-start justify-between gap-2 p-4\">\n <button\n type=\"button\"\n onClick={() => navigate(`notebooks/${notebook.id}`)}\n className=\"min-w-0 flex-1 space-y-2 text-left\"\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"truncate font-medium text-text-primary\">\n {notebook.title}\n </span>\n <StatusBadge status={notebook.status} />\n </div>\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary\">\n <span>Runtime: {notebook.runtime ?? '—'}</span>\n <span className=\"tabular-nums\">\n {notebook.datasetIds.length} dataset\n {notebook.datasetIds.length === 1 ? '' : 's'}\n </span>\n {notebook.isShared ? (\n <span className=\"inline-flex items-center gap-1\">\n <Share2 className=\"size-3\" aria-hidden=\"true\" />\n Shared\n </span>\n ) : (\n <span>Private</span>\n )}\n <span>Updated {formatDate(notebook.updatedAt)}</span>\n </div>\n {notebook.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {notebook.tags.slice(0, 4).map((tag) => (\n <Badge key={tag} variant=\"outline\">\n {tag}\n </Badge>\n ))}\n {notebook.tags.length > 4 ? (\n <span className=\"text-xs text-text-secondary\">\n +{notebook.tags.length - 4}\n </span>\n ) : null}\n </div>\n ) : null}\n </button>\n <ActionMenu\n ariaLabel={`Actions for ${notebook.title}`}\n entityName={notebook.title}\n onView={() => navigate(`notebooks/${notebook.id}`)}\n onEdit={() => navigate(`notebooks/${notebook.id}/edit`)}\n onDelete={\n notebook.status === 'ARCHIVED' ? undefined : () => handleArchive(notebook)\n }\n deleteLabel=\"Archive notebook\"\n />\n </div>\n </li>\n ))}\n </ul>\n </>\n )}\n\n {!loading && !error && notebooks.length > 0 ? (\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={notebooks.length}\n hasMore={pageInfo?.hasMore ?? false}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"notebooks\"\n loading={loading}\n />\n ) : null}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}