@burdenoff/microfe-cosmicintersection 2026.830.1 → 2026.1012.1
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.
- package/dist/cosmicintersection/CosmicIntersectionRoutes.d.ts +1 -1
- package/dist/cosmicintersection/CosmicIntersectionRoutes.d.ts.map +1 -1
- package/dist/cosmicintersection/api/cosmicApi.d.ts +48 -48
- package/dist/cosmicintersection/api/cosmicApi.d.ts.map +1 -1
- package/dist/cosmicintersection/components/ActionMenu.d.ts +1 -1
- package/dist/cosmicintersection/components/ActionMenu.d.ts.map +1 -1
- package/dist/cosmicintersection/components/ActionMenu.js.map +1 -1
- package/dist/cosmicintersection/components/ActivityFeedPanel.d.ts +1 -1
- package/dist/cosmicintersection/components/ActivityFeedPanel.d.ts.map +1 -1
- package/dist/cosmicintersection/components/DiscussionPanel.d.ts +1 -1
- package/dist/cosmicintersection/components/DiscussionPanel.d.ts.map +1 -1
- package/dist/cosmicintersection/components/EmptyState.d.ts +1 -1
- package/dist/cosmicintersection/components/EmptyState.d.ts.map +1 -1
- package/dist/cosmicintersection/components/EntityFormShell.d.ts +1 -1
- package/dist/cosmicintersection/components/EntityFormShell.d.ts.map +1 -1
- package/dist/cosmicintersection/components/EntityTagsField.d.ts +1 -1
- package/dist/cosmicintersection/components/EntityTagsField.d.ts.map +1 -1
- package/dist/cosmicintersection/components/ErrorState.d.ts +1 -1
- package/dist/cosmicintersection/components/ErrorState.d.ts.map +1 -1
- package/dist/cosmicintersection/components/ErrorState.js.map +1 -1
- package/dist/cosmicintersection/components/FacetPanel.d.ts +1 -1
- package/dist/cosmicintersection/components/FacetPanel.d.ts.map +1 -1
- package/dist/cosmicintersection/components/FileUploadField.d.ts +2 -2
- package/dist/cosmicintersection/components/FileUploadField.d.ts.map +1 -1
- package/dist/cosmicintersection/components/FileUploadField.js +1 -0
- package/dist/cosmicintersection/components/FileUploadField.js.map +1 -1
- package/dist/cosmicintersection/components/LineageGraph.d.ts +1 -1
- package/dist/cosmicintersection/components/LineageGraph.d.ts.map +1 -1
- package/dist/cosmicintersection/components/LoadingState.d.ts +1 -1
- package/dist/cosmicintersection/components/LoadingState.d.ts.map +1 -1
- package/dist/cosmicintersection/components/PageLayout.d.ts +1 -1
- package/dist/cosmicintersection/components/PageLayout.d.ts.map +1 -1
- package/dist/cosmicintersection/components/Pagination.d.ts +1 -1
- package/dist/cosmicintersection/components/Pagination.d.ts.map +1 -1
- package/dist/cosmicintersection/components/Pagination.js.map +1 -1
- package/dist/cosmicintersection/components/ResultsChart.d.ts +1 -1
- package/dist/cosmicintersection/components/ResultsChart.d.ts.map +1 -1
- package/dist/cosmicintersection/components/SafeLink.d.ts +1 -1
- package/dist/cosmicintersection/components/SafeLink.d.ts.map +1 -1
- package/dist/cosmicintersection/components/StatusBadge.d.ts +1 -1
- package/dist/cosmicintersection/components/StatusBadge.d.ts.map +1 -1
- package/dist/cosmicintersection/components/StatusBadge.js.map +1 -1
- package/dist/cosmicintersection/components/StepDAG.d.ts +1 -1
- package/dist/cosmicintersection/components/StepDAG.d.ts.map +1 -1
- package/dist/cosmicintersection/context/CosmicIntersectionContext.d.ts +1 -1
- package/dist/cosmicintersection/context/CosmicIntersectionContext.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/access/AccessPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/access/AccessPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -1
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionDetailPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionDetailPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionFormPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionFormPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionsPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionsPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetFormPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetFormPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/discovery/DiscoveryPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/discovery/DiscoveryPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js.map +1 -1
- package/dist/cosmicintersection/pages/lineage/LineagePage.d.ts +1 -1
- package/dist/cosmicintersection/pages/lineage/LineagePage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/lineage/LineagePage.js.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebookFormPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebookFormPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/releases/ReleaseFormPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/releases/ReleaseFormPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/releases/ReleasesPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/releases/ReleasesPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/runs/RunDetailPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/runs/RunDetailPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/runs/RunsPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/runs/RunsPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourceDetailPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/sources/SourceDetailPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourceFormPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/sources/SourceFormPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourcesPage.d.ts +1 -1
- package/dist/cosmicintersection/pages/sources/SourcesPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +96 -17
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
- package/dist/shared/hooks/useTr.d.ts.map +1 -1
- package/dist/shared/hooks/useTr.js.map +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalysesPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/analyses/AnalysesPage.tsx"],"sourcesContent":["/**\n * AnalysesPage — every analysis run across the workspace.\n *\n * Lists `cosListAnalyses` (a plain array, no pagination) with a status filter.\n * A \"Run analysis\" dialog launches a new `cosRunAnalysis` against EITHER a\n * notebook OR a dataset (the schema requires one), chosen via a small selector.\n * In-flight (PENDING / RUNNING) analyses expose a \"Complete\" action that calls\n * `cosCompleteAnalysis` with COMPLETED | FAILED, optional results JSON, and an\n * optional output artifact uploaded through FileUploadField. All values are read\n * from the backend — nothing is fabricated.\n */\nimport { useMemo, useState, type FormEvent } from 'react';\nimport { BarChart3, Beaker, Download, ExternalLink, Play, Sparkles } from 'lucide-react';\nimport {\n Badge,\n Button,\n Dialog,\n DialogClose,\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 Switch,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosAnalysesQuery,\n useCosCompleteAnalysisMutation,\n useCosDatasetsQuery,\n useCosNotebooksQuery,\n useCosRunAnalysisMutation,\n type CosAnalysis,\n type CosAnalysisStatus,\n} from '../../api/cosmicApi';\nimport {\n ActionMenu,\n EmptyState,\n ErrorState,\n FileUploadField,\n LoadingState,\n PageLayout,\n StatusBadge,\n useFileDownloadUrl,\n} from '../../components';\nimport { ResultsChart } from '../../components/ResultsChart';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/** Anything worth opening the results viewer for. */\nfunction hasViewableResults(analysis: CosAnalysis): boolean {\n return (\n Boolean(analysis.outputFile?.id) ||\n Boolean(analysis.results && Object.keys(analysis.results).length > 0)\n );\n}\n\nconst STATUS_OPTIONS: { value: CosAnalysisStatus; label: string }[] = [\n { value: 'PENDING', label: 'Pending' },\n { value: 'RUNNING', label: 'Running' },\n { value: 'COMPLETED', label: 'Completed' },\n { value: 'FAILED', label: 'Failed' },\n];\n\nconst ALL = 'ALL';\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/**\n * Presigned output artifact: attempts an inline image preview (astronomy output\n * is often a plot/spectrum PNG) and always offers a download link. If the file\n * is not an image the preview is dropped on the img error and only the link\n * remains — no assumption is made about the artifact's type.\n */\nfunction OutputArtifact({ fileId }: { fileId: string }) {\n const { data, loading } = useFileDownloadUrl(fileId);\n const [previewFailed, setPreviewFailed] = useState(false);\n const url = data?.getFileDownloadUrl ?? null;\n\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 <div className=\"space-y-2\">\n {!previewFailed ? (\n <img\n src={url}\n alt=\"Analysis output preview\"\n onError={() => setPreviewFailed(true)}\n className=\"max-h-72 w-full rounded-card border border-border-default bg-bg-sunken object-contain\"\n />\n ) : null}\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 Download output\n </a>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Results viewer dialog\n// ---------------------------------------------------------------------------\n\n/**\n * Inline results viewer for a finished analysis: a rendered <ResultsChart> when\n * the payload is chartable (or its key/value fallback otherwise) plus an output\n * artifact preview/download. Everything is read straight from the analysis\n * record.\n */\nfunction ResultsViewerDialog({\n analysis,\n onOpenChange,\n}: {\n analysis: CosAnalysis | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const results = analysis?.results ?? null;\n const hasResults = Boolean(results && Object.keys(results).length > 0);\n const hasOutput = Boolean(analysis?.outputFile?.id);\n\n return (\n <Dialog open={Boolean(analysis)} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[85vh] max-w-2xl overflow-y-auto\">\n {analysis ? (\n <>\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"size-5 text-text-secondary\" aria-hidden=\"true\" />\n Analysis results\n </DialogTitle>\n <DialogDescription>\n {analysis.title}\n {analysis.method ? ` · ${analysis.method}` : ''}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-5\">\n {hasResults && results ? <ResultsChart results={results} /> : null}\n\n {hasOutput && analysis.outputFile ? (\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Output artifact\n </p>\n <OutputArtifact fileId={analysis.outputFile.id} />\n </div>\n ) : null}\n\n {!hasResults && !hasOutput ? (\n <p className=\"rounded-card border border-border-default bg-bg-sunken px-4 py-6 text-center text-sm text-text-secondary\">\n No results or output artifact has been recorded for this analysis yet.\n </p>\n ) : null}\n </div>\n\n <DialogFooter>\n <DialogClose asChild>\n <Button type=\"button\" variant=\"outline\">\n Close\n </Button>\n </DialogClose>\n </DialogFooter>\n </>\n ) : null}\n </DialogContent>\n </Dialog>\n );\n}\n\n/** Parse optional results JSON; blank -> undefined, invalid -> error. */\nfunction parseResults(\n raw: string,\n tr: ReturnType<typeof useTr>,\n): { ok: true; value?: Record<string, unknown> } | { ok: false; error: string } {\n const trimmed = raw.trim();\n if (!trimmed) {\n return { ok: true, value: undefined };\n }\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return {\n ok: false,\n error: tr(\n 'cosmicintersection.pages.analyses.resultsMustBeObject',\n 'Results must be a JSON object.',\n ),\n };\n }\n return { ok: true, value: parsed as Record<string, unknown> };\n } catch {\n return {\n ok: false,\n error: tr(\n 'cosmicintersection.pages.analyses.resultsNotValidJson',\n 'Results are not valid JSON.',\n ),\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Run analysis dialog\n// ---------------------------------------------------------------------------\n\ntype RunSourceKind = 'NOTEBOOK' | 'DATASET';\n\ninterface RunDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onCompleted: () => void;\n}\n\nfunction RunAnalysisDialog({ open, onOpenChange, onCompleted }: RunDialogProps) {\n const tr = useTr();\n const [title, setTitle] = useState('');\n const [method, setMethod] = useState('');\n const [aiAssisted, setAiAssisted] = useState(false);\n const [sourceKind, setSourceKind] = useState<RunSourceKind>('NOTEBOOK');\n const [sourceId, setSourceId] = useState('');\n\n const { data: notebooksData, loading: notebooksLoading } = useCosNotebooksQuery({\n pagination: { limit: 100, offset: 0 },\n });\n const { data: datasetsData, loading: datasetsLoading } = useCosDatasetsQuery({\n pagination: { limit: 100, offset: 0 },\n });\n const [runAnalysis, { loading }] = useCosRunAnalysisMutation();\n\n const notebooks = notebooksData?.cosListNotebooks.items ?? [];\n const datasets = datasetsData?.cosListDatasets.items ?? [];\n\n const reset = () => {\n setTitle('');\n setMethod('');\n setAiAssisted(false);\n setSourceKind('NOTEBOOK');\n setSourceId('');\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const trimmed = title.trim();\n if (!trimmed) {\n notify.error(\n tr('cosmicintersection.pages.analyses.titleRequired', 'Title is required'),\n tr(\n 'cosmicintersection.pages.analyses.titleRequiredDescription',\n 'Give the analysis a descriptive title.',\n ),\n );\n return;\n }\n if (!sourceId) {\n notify.error(\n tr('cosmicintersection.pages.analyses.selectSource', 'Select a source'),\n tr(\n 'cosmicintersection.pages.analyses.selectSourceDescription',\n 'Choose a {{kind}} to analyse.',\n { kind: sourceKind === 'NOTEBOOK' ? tr('nav.notebooks', 'notebook').toLowerCase() : tr('nav.datasets', 'dataset').toLowerCase() },\n ),\n );\n return;\n }\n const response = await runAnalysis({\n variables: {\n input: {\n title: trimmed,\n method: method.trim() || undefined,\n aiAssisted,\n ...(sourceKind === 'NOTEBOOK' ? { notebookId: sourceId } : { datasetId: sourceId }),\n },\n },\n });\n if (response.error || !response.data?.cosRunAnalysis?.id) {\n return;\n }\n notify.success(\n tr('cosmicintersection.pages.analyses.analysisStarted', 'Analysis started'),\n tr('cosmicintersection.pages.analyses.analysisStartedDescription', '“{{name}}” is now running.', { name: trimmed }),\n );\n reset();\n onOpenChange(false);\n onCompleted();\n };\n\n const sourceLoading = sourceKind === 'NOTEBOOK' ? notebooksLoading : datasetsLoading;\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 against a notebook or a dataset. Exactly one source is required.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-title\">Title</Label>\n <Input\n id=\"run-title\"\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n placeholder=\"e.g. Spectral line fitting\"\n required\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-source-kind\">Source type</Label>\n <Select\n value={sourceKind}\n onValueChange={(value) => {\n setSourceKind(value as RunSourceKind);\n setSourceId('');\n }}\n >\n <SelectTrigger id=\"run-source-kind\" aria-label=\"Source type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"NOTEBOOK\">Notebook</SelectItem>\n <SelectItem value=\"DATASET\">Dataset</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-source-id\">\n {sourceKind === 'NOTEBOOK' ? 'Notebook' : 'Dataset'}\n </Label>\n <Select value={sourceId} onValueChange={(value) => setSourceId(value)}>\n <SelectTrigger id=\"run-source-id\" aria-label=\"Analysis source\">\n <SelectValue\n placeholder={\n sourceLoading\n ? 'Loading…'\n : `Select a ${sourceKind === 'NOTEBOOK' ? 'notebook' : 'dataset'}`\n }\n />\n </SelectTrigger>\n <SelectContent>\n {sourceKind === 'NOTEBOOK'\n ? notebooks.map((notebook) => (\n <SelectItem key={notebook.id} value={notebook.id}>\n {notebook.title}\n </SelectItem>\n ))\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>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-method\">Method</Label>\n <Input\n id=\"run-method\"\n value={method}\n onChange={(event) => setMethod(event.target.value)}\n placeholder=\"e.g. gaussian-fit\"\n />\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Switch\n id=\"run-ai\"\n checked={aiAssisted}\n onCheckedChange={(checked) => setAiAssisted(checked)}\n />\n <Label htmlFor=\"run-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\n// ---------------------------------------------------------------------------\n// Complete analysis dialog\n// ---------------------------------------------------------------------------\n\ninterface CompleteDialogProps {\n analysis: CosAnalysis | null;\n onOpenChange: (open: boolean) => void;\n onCompleted: () => void;\n}\n\nfunction CompleteAnalysisDialog({ analysis, onOpenChange, onCompleted }: CompleteDialogProps) {\n const [status, setStatus] = useState<'COMPLETED' | 'FAILED'>('COMPLETED');\n const [results, setResults] = useState('');\n const [outputFileId, setOutputFileId] = useState('');\n const [completeAnalysis, { loading }] = useCosCompleteAnalysisMutation();\n const tr = useTr();\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!analysis) {\n return;\n }\n const parsed = parseResults(results, tr);\n if (!parsed.ok) {\n notify.error(\n tr('cosmicintersection.pages.analyses.invalidResults', 'Invalid results'),\n parsed.error,\n );\n return;\n }\n const response = await completeAnalysis({\n variables: {\n id: analysis.id,\n status,\n results: parsed.value,\n outputFileId: outputFileId || undefined,\n },\n });\n if (response.error || !response.data?.cosCompleteAnalysis) {\n return;\n }\n notify.success(\n status === 'COMPLETED'\n ? tr('cosmicintersection.pages.analyses.analysisCompleted', 'Analysis completed')\n : tr('cosmicintersection.pages.analyses.analysisMarkedFailed', 'Analysis marked failed'),\n tr('cosmicintersection.pages.analyses.analysisUpdatedDescription', '“{{name}}” was updated.', { name: analysis.title }),\n );\n onOpenChange(false);\n onCompleted();\n };\n\n return (\n <Dialog\n open={Boolean(analysis)}\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>Complete analysis</DialogTitle>\n <DialogDescription>\n Record the outcome for {analysis ? `“${analysis.title}”` : 'this analysis'}. Attach\n results or an output artifact if you have them.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"complete-status\">Outcome</Label>\n <Select\n value={status}\n onValueChange={(value) => setStatus(value as 'COMPLETED' | 'FAILED')}\n >\n <SelectTrigger id=\"complete-status\" aria-label=\"Outcome\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"COMPLETED\">Completed</SelectItem>\n <SelectItem value=\"FAILED\">Failed</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"complete-results\">Results (JSON)</Label>\n <Textarea\n id=\"complete-results\"\n value={results}\n onChange={(event) => setResults(event.target.value)}\n rows={4}\n className=\"font-mono text-xs\"\n placeholder='{ \"snr\": 12.4, \"detections\": 3 }'\n />\n <p className=\"text-xs text-text-secondary\">\n Optional structured results as a JSON object.\n </p>\n </div>\n\n <FileUploadField\n value={outputFileId}\n onChange={(fileId) => setOutputFileId(fileId)}\n label=\"Attach output file\"\n hint=\"Optional output artifact (plots, tables, exports). Stored in workspace files.\"\n />\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 {loading ? 'Saving…' : 'Save outcome'}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Page\n// ---------------------------------------------------------------------------\n\nexport default function AnalysesPage() {\n const { navigate } = useCosmicIntersection();\n const tr = useTr();\n const [statusFilter, setStatusFilter] = useState<CosAnalysisStatus | undefined>(undefined);\n const [runOpen, setRunOpen] = useState(false);\n const [completing, setCompleting] = useState<CosAnalysis | null>(null);\n const [viewing, setViewing] = useState<CosAnalysis | null>(null);\n\n const { data, loading, error, refetch } = useCosAnalysesQuery({ status: statusFilter });\n const { data: notebooksData } = useCosNotebooksQuery({ pagination: { limit: 100, offset: 0 } });\n const { data: datasetsData } = useCosDatasetsQuery({ pagination: { limit: 100, offset: 0 } });\n\n const analyses = data?.cosListAnalyses ?? [];\n\n const notebookNames = useMemo(() => {\n const map = new Map<string, string>();\n for (const notebook of notebooksData?.cosListNotebooks.items ?? []) {\n map.set(notebook.id, notebook.title);\n }\n return map;\n }, [notebooksData]);\n\n const datasetNames = 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 /** Resolve a friendly label + route for an analysis' notebook OR dataset source. */\n const sourceInfo = (analysis: CosAnalysis): { label: string; path: string } | null => {\n if (analysis.notebookId) {\n return {\n label: notebookNames.get(analysis.notebookId) ?? 'Notebook',\n path: `notebooks/${analysis.notebookId}`,\n };\n }\n if (analysis.datasetId) {\n return {\n label: datasetNames.get(analysis.datasetId) ?? 'Dataset',\n path: `datasets/${analysis.datasetId}`,\n };\n }\n return null;\n };\n\n /** Extra ActionMenu items for a row/card (view results + complete). */\n const extraActions = (analysis: CosAnalysis) => {\n const inFlight = analysis.status === 'PENDING' || analysis.status === 'RUNNING';\n return [\n ...(hasViewableResults(analysis)\n ? [\n {\n key: 'view-results',\n label: 'View results',\n icon: BarChart3,\n onSelect: () => setViewing(analysis),\n },\n ]\n : []),\n ...(inFlight\n ? [\n {\n key: 'complete',\n label: 'Complete…',\n icon: Beaker,\n onSelect: () => setCompleting(analysis),\n },\n ]\n : []),\n ];\n };\n\n const actions = (\n <Button type=\"button\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n {tr('cosmicintersection.pages.analyses.create', 'Run analysis')}\n </Button>\n );\n\n return (\n <PageLayout\n title={tr('cosmicintersection.pages.analyses.title', 'Analyses')}\n description={tr(\n 'cosmicintersection.pages.analyses.description',\n 'Analysis runs executed over notebooks and federated datasets.',\n )}\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=\"analysis-status-filter\">\n Status\n </label>\n <Select\n value={statusFilter ?? ALL}\n onValueChange={(value) =>\n setStatusFilter(value === ALL ? undefined : (value as CosAnalysisStatus))\n }\n >\n <SelectTrigger\n id=\"analysis-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\n variant=\"rows\"\n rows={6}\n label={tr('cosmicintersection.pages.analyses.loading', 'Loading analyses…')}\n />\n ) : error ? (\n <ErrorState\n message={tr(\n 'cosmicintersection.pages.analyses.error',\n 'We could not load analyses. Please try again.',\n )}\n onRetry={() => void refetch()}\n />\n ) : analyses.length === 0 ? (\n <EmptyState\n icon={Beaker}\n title={\n statusFilter\n ? tr(\n 'cosmicintersection.pages.analyses.emptyFilterTitle',\n 'No analyses match this filter.',\n )\n : tr('cosmicintersection.pages.analyses.emptyTitle', 'No analyses yet.')\n }\n description={\n statusFilter\n ? tr(\n 'cosmicintersection.pages.analyses.emptyFilterDescription',\n 'Try a different status filter.',\n )\n : tr(\n 'cosmicintersection.pages.analyses.emptyDescription',\n 'Run an analysis against a notebook or dataset to see results here.',\n )\n }\n action={\n statusFilter ? undefined : (\n <Button type=\"button\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n {tr('cosmicintersection.pages.analyses.create', 'Run analysis')}\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>Method</TableHead>\n <TableHead>AI</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Source</TableHead>\n <TableHead>Started</TableHead>\n <TableHead>Finished</TableHead>\n <TableHead>Output</TableHead>\n <TableHead className=\"w-12 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {analyses.map((analysis) => {\n const source = sourceInfo(analysis);\n return (\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>\n {source ? (\n <button\n type=\"button\"\n onClick={() => navigate(source.path)}\n className=\"inline-flex max-w-[12rem] items-center gap-1 truncate text-sm text-text-primary underline-offset-2 hover:underline\"\n >\n <ExternalLink className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">{source.label}</span>\n </button>\n ) : (\n <span className=\"text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDateTime(analysis.startedAt)}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDateTime(analysis.finishedAt)}\n </TableCell>\n <TableCell>\n {hasViewableResults(analysis) ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-auto gap-1 px-2 py-1\"\n onClick={() => setViewing(analysis)}\n >\n <BarChart3 className=\"size-3.5\" aria-hidden=\"true\" />\n View\n </Button>\n ) : (\n <span className=\"text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"text-right\">\n <ActionMenu\n ariaLabel={`Actions for ${analysis.title}`}\n onView={source ? () => navigate(source.path) : undefined}\n extraItems={extraActions(analysis)}\n />\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n\n {/* Mobile card list */}\n <ul className=\"space-y-3 md:hidden\">\n {analyses.map((analysis) => {\n const source = sourceInfo(analysis);\n const inFlight = analysis.status === 'PENDING' || analysis.status === 'RUNNING';\n return (\n <li\n key={analysis.id}\n className=\"space-y-3 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\n <div className=\"flex flex-wrap items-center gap-2\">\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 {source ? (\n <button\n type=\"button\"\n onClick={() => navigate(source.path)}\n className=\"inline-flex max-w-full items-center gap-1 truncate text-sm text-text-primary underline-offset-2 hover:underline\"\n >\n <ExternalLink className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">{source.label}</span>\n </button>\n ) : null}\n </div>\n\n <dl className=\"grid grid-cols-2 gap-2 text-xs\">\n <div>\n <dt className=\"text-text-secondary\">Started</dt>\n <dd className=\"text-text-primary\">{formatDateTime(analysis.startedAt)}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">Finished</dt>\n <dd className=\"text-text-primary\">{formatDateTime(analysis.finishedAt)}</dd>\n </div>\n </dl>\n\n {hasViewableResults(analysis) || inFlight ? (\n <div className=\"flex flex-wrap gap-2\">\n {hasViewableResults(analysis) ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setViewing(analysis)}\n >\n <BarChart3 className=\"size-4\" />\n View results\n </Button>\n ) : null}\n {inFlight ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setCompleting(analysis)}\n >\n <Beaker className=\"size-4\" />\n Complete\n </Button>\n ) : null}\n </div>\n ) : null}\n </li>\n );\n })}\n </ul>\n </>\n )}\n </div>\n\n <RunAnalysisDialog\n open={runOpen}\n onOpenChange={setRunOpen}\n onCompleted={() => void refetch()}\n />\n <CompleteAnalysisDialog\n key={completing?.id ?? 'none'}\n analysis={completing}\n onOpenChange={(open) => {\n if (!open) {\n setCompleting(null);\n }\n }}\n onCompleted={() => void refetch()}\n />\n <ResultsViewerDialog\n analysis={viewing}\n onOpenChange={(open) => {\n if (!open) {\n setViewing(null);\n }\n }}\n />\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiEA,SAAS,EAAmB,GAAgC;CAC1D,OACE,EAAQ,EAAS,YAAY,MAC7B,GAAQ,EAAS,WAAW,OAAO,KAAK,EAAS,OAAO,EAAE,SAAS;AAEvE;AAEA,IAAM,KAAgE;CACpE;EAAE,OAAO;EAAW,OAAO;CAAU;CACrC;EAAE,OAAO;EAAW,OAAO;CAAU;CACrC;EAAE,OAAO;EAAa,OAAO;CAAY;CACzC;EAAE,OAAO;EAAU,OAAO;CAAS;AACrC,GAEM,IAAM;AAEZ,SAAS,EAAe,GAA+B;CACrD,IAAI,CAAC,GACH,OAAO;CAET,IAAM,IAAO,IAAI,KAAK,CAAK;CAI3B,OAHI,OAAO,MAAM,EAAK,QAAQ,CAAC,IACtB,MAEF,EAAK,eAAe,KAAA,GAAW;EACpC,MAAM;EACN,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;CACV,CAAC;AACH;AAQA,SAAS,GAAe,EAAE,aAA8B;CACtD,IAAM,EAAE,SAAM,eAAY,EAAmB,CAAM,GAC7C,CAAC,GAAe,KAAoB,EAAS,EAAK,GAClD,IAAM,GAAM,sBAAsB;CAQxC,OANI,IACK,kBAAC,QAAD;EAAM,WAAU;YAA8B;CAAqB,CAAA,IAEvE,IAIH,kBAAC,OAAD;EAAK,WAAU;YAAf,CACI,IAOE,OANF,kBAAC,OAAD;GACE,KAAK;GACL,KAAI;GACJ,eAAe,EAAiB,EAAI;GACpC,WAAU;EACX,CAAA,GAEH,kBAAC,KAAD;GACE,MAAM;GACN,QAAO;GACP,KAAI;GACJ,WAAU;aAJZ,CAME,kBAAC,GAAD;IAAU,WAAU;IAAW,eAAY;GAAQ,CAAA,GAAC,iBAEnD;IACA;MArBE,kBAAC,QAAD;EAAM,WAAU;YAAhB,CAA8C,SAAM,CAAa;;AAuB5E;AAYA,SAAS,GAAoB,EAC3B,aACA,mBAIC;CACD,IAAM,IAAU,GAAU,WAAW,MAC/B,IAAa,GAAQ,KAAW,OAAO,KAAK,CAAO,EAAE,SAAS,IAC9D,IAAY,EAAQ,GAAU,YAAY;CAEhD,OACE,kBAAC,GAAD;EAAQ,MAAM,EAAQ;EAAyB;YAC7C,kBAAC,GAAD;GAAe,WAAU;aACtB,IACC,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAa,WAAU;eAAvB,CACE,kBAAC,GAAD;MAAW,WAAU;MAA6B,eAAY;KAAQ,CAAA,GAAC,kBAE5D;QACb,kBAAC,GAAD,EAAA,UAAA,CACG,EAAS,OACT,EAAS,SAAS,MAAM,EAAS,WAAW,EAC5B,EAAA,CAAA,CACP,EAAA,CAAA;IAEd,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KAAc,IAAU,kBAAC,GAAD,EAAuB,WAAU,CAAA,IAAI;MAE7D,KAAa,EAAS,aACrB,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAkE;OAE5E,CAAA,GACH,kBAAC,IAAD,EAAgB,QAAQ,EAAS,WAAW,GAAK,CAAA,CAC9C;WACH;MAEH,CAAC,KAAc,CAAC,IACf,kBAAC,KAAD;OAAG,WAAU;iBAA2G;MAErH,CAAA,IACD;KACD;;IAEL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAa,SAAA;eACX,kBAAC,GAAD;MAAQ,MAAK;MAAS,SAAQ;gBAAU;KAEhC,CAAA;IACG,CAAA,EACD,CAAA;GACd,EAAA,CAAA,IACA;EACS,CAAA;CACT,CAAA;AAEZ;AAGA,SAAS,EACP,GACA,GAC8E;CAC9E,IAAM,IAAU,EAAI,KAAK;CACzB,IAAI,CAAC,GACH,OAAO;EAAE,IAAI;EAAM,OAAO,KAAA;CAAU;CAEtC,IAAI;EACF,IAAM,IAAkB,KAAK,MAAM,CAAO;EAU1C,OATI,OAAO,KAAW,aAAY,KAAmB,MAAM,QAAQ,CAAM,IAChE;GACL,IAAI;GACJ,OAAO,EACL,yDACA,gCACF;EACF,IAEK;GAAE,IAAI;GAAM,OAAO;EAAkC;CAC9D,QAAQ;EACN,OAAO;GACL,IAAI;GACJ,OAAO,EACL,yDACA,6BACF;EACF;CACF;AACF;AAcA,SAAS,GAAkB,EAAE,SAAM,iBAAc,kBAA+B;CAC9E,IAAM,IAAK,EAAM,GACX,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAQ,KAAa,EAAS,EAAE,GACjC,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAY,KAAiB,EAAwB,UAAU,GAChE,CAAC,GAAU,KAAe,EAAS,EAAE,GAErC,EAAE,MAAM,GAAe,SAAS,MAAqB,EAAqB,EAC9E,YAAY;EAAE,OAAO;EAAK,QAAQ;CAAE,EACtC,CAAC,GACK,EAAE,MAAM,GAAc,SAAS,MAAoB,EAAoB,EAC3E,YAAY;EAAE,OAAO;EAAK,QAAQ;CAAE,EACtC,CAAC,GACK,CAAC,GAAa,EAAE,gBAAa,EAA0B,GAEvD,IAAY,GAAe,iBAAiB,SAAS,CAAC,GACtD,IAAW,GAAc,gBAAgB,SAAS,CAAC,GAEnD,WAAc;EAKlB,AAJA,EAAS,EAAE,GACX,EAAU,EAAE,GACZ,EAAc,EAAK,GACnB,EAAc,UAAU,GACxB,EAAY,EAAE;CAChB;CAkDA,OACE,kBAAC,GAAD;EACQ;EACN,eAAe,MAAS;GACtB,AAAK,KACH,EAAa,CAAI;EAErB;YAEA,kBAAC,GAAD,EAAA,UACE,kBAAC,QAAD;GAAM,UAAU,OA1DM,MAAsC;IAChE,EAAM,eAAe;IACrB,IAAM,IAAU,EAAM,KAAK;IAC3B,IAAI,CAAC,GAAS;KACZ,EAAO,MACL,EAAG,mDAAmD,mBAAmB,GACzE,EACE,8DACA,wCACF,CACF;KACA;IACF;IACA,IAAI,CAAC,GAAU;KACb,EAAO,MACL,EAAG,kDAAkD,iBAAiB,GACtE,EACE,6DACA,iCACA,EAAE,MAAM,MAAe,aAAa,EAAG,iBAAiB,UAAU,EAAE,YAAY,IAAI,EAAG,gBAAgB,SAAS,EAAE,YAAY,EAAE,CAClI,CACF;KACA;IACF;IACA,IAAM,IAAW,MAAM,EAAY,EACjC,WAAW,EACT,OAAO;KACL,OAAO;KACP,QAAQ,EAAO,KAAK,KAAK,KAAA;KACzB;KACA,GAAI,MAAe,aAAa,EAAE,YAAY,EAAS,IAAI,EAAE,WAAW,EAAS;IACnF,EACF,EACF,CAAC;IACG,EAAS,SAAS,CAAC,EAAS,MAAM,gBAAgB,OAGtD,EAAO,QACL,EAAG,qDAAqD,kBAAkB,GAC1E,EAAG,gEAAgE,8BAA8B,EAAE,MAAM,EAAQ,CAAC,CACpH,GACA,GAAM,GACN,EAAa,EAAK,GAClB,EAAY;GACd;GAcoC,WAAU;aAAxC;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAa,eAAyB,CAAA,GACtC,kBAAC,GAAD,EAAA,UAAmB,yFAEA,CAAA,CACP,EAAA,CAAA;IAEd,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAO,SAAQ;gBAAY;KAAY,CAAA,GACvC,kBAAC,GAAD;MACE,IAAG;MACH,OAAO;MACP,WAAW,MAAU,EAAS,EAAM,OAAO,KAAK;MAChD,aAAY;MACZ,UAAA;KACD,CAAA,CACE;;IAEL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAO,SAAQ;iBAAkB;MAAkB,CAAA,GACnD,kBAAC,GAAD;OACE,OAAO;OACP,gBAAgB,MAAU;QAExB,AADA,EAAc,CAAsB,GACpC,EAAY,EAAE;OAChB;iBALF,CAOE,kBAAC,GAAD;QAAe,IAAG;QAAkB,cAAW;kBAC7C,kBAAC,GAAD,CAAc,CAAA;OACD,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QAAY,OAAM;kBAAW;OAAoB,CAAA,GACjD,kBAAC,GAAD;QAAY,OAAM;kBAAU;OAAmB,CAAA,CAClC,EAAA,CAAA,CACT;QACL;SACL,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAO,SAAQ;iBACZ,MAAe,aAAa,aAAa;MACrC,CAAA,GACP,kBAAC,GAAD;OAAQ,OAAO;OAAU,gBAAgB,MAAU,EAAY,CAAK;iBAApE,CACE,kBAAC,GAAD;QAAe,IAAG;QAAgB,cAAW;kBAC3C,kBAAC,GAAD,EACE,cAzDI,MAAe,aAAa,IAAmB,KA2D7C,aACA,YAAY,MAAe,aAAa,aAAa,YAE5D,CAAA;OACY,CAAA,GACf,kBAAC,GAAD,EAAA,UACG,MAAe,aACZ,EAAU,KAAK,MACb,kBAAC,GAAD;QAA8B,OAAO,EAAS;kBAC3C,EAAS;OACA,GAFK,EAAS,EAEd,CACb,IACD,EAAS,KAAK,MACZ,kBAAC,GAAD;QAA6B,OAAO,EAAQ;kBACzC,EAAQ;OACC,GAFK,EAAQ,EAEb,CACb,EACQ,CAAA,CACT;QACL;OACF;;IAEL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAO,SAAQ;gBAAa;KAAa,CAAA,GACzC,kBAAC,GAAD;MACE,IAAG;MACH,OAAO;MACP,WAAW,MAAU,EAAU,EAAM,OAAO,KAAK;MACjD,aAAY;KACb,CAAA,CACE;;IAEL,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,IAAG;MACH,SAAS;MACT,kBAAkB,MAAY,EAAc,CAAO;KACpD,CAAA,GACD,kBAAC,GAAD;MAAO,SAAQ;MAAS,WAAU;gBAAiB;KAE5C,CAAA,CACJ;;IAEL,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,eAAe,EAAa,EAAK;KACjC,UAAU;eACX;IAEO,CAAA,GACR,kBAAC,GAAD;KAAQ,MAAK;KAAS,UAAU;eAAhC,CACE,kBAAC,GAAD,EAAM,WAAU,SAAU,CAAA,GACzB,IAAU,cAAc,cACnB;MACI,EAAA,CAAA;GACV;KACO,CAAA;CACT,CAAA;AAEZ;AAYA,SAAS,GAAuB,EAAE,aAAU,iBAAc,kBAAoC;CAC5F,IAAM,CAAC,GAAQ,KAAa,EAAiC,WAAW,GAClE,CAAC,GAAS,KAAc,EAAS,EAAE,GACnC,CAAC,GAAc,KAAmB,EAAS,EAAE,GAC7C,CAAC,GAAkB,EAAE,gBAAa,EAA+B,GACjE,IAAK,EAAM;CAoCjB,OACE,kBAAC,GAAD;EACE,MAAM,EAAQ;EACd,eAAe,MAAS;GACtB,AAAK,KACH,EAAa,CAAI;EAErB;YAEA,kBAAC,GAAD,EAAA,UACE,kBAAC,QAAD;GAAM,UAAU,OA5CM,MAAsC;IAEhE,IADA,EAAM,eAAe,GACjB,CAAC,GACH;IAEF,IAAM,IAAS,EAAa,GAAS,CAAE;IACvC,IAAI,CAAC,EAAO,IAAI;KACd,EAAO,MACL,EAAG,oDAAoD,iBAAiB,GACxE,EAAO,KACT;KACA;IACF;IACA,IAAM,IAAW,MAAM,EAAiB,EACtC,WAAW;KACT,IAAI,EAAS;KACb;KACA,SAAS,EAAO;KAChB,cAAc,KAAgB,KAAA;IAChC,EACF,CAAC;IACG,EAAS,SAAS,CAAC,EAAS,MAAM,wBAGtC,EAAO,QACL,MAAW,cACP,EAAG,uDAAuD,oBAAoB,IAC9E,EAAG,0DAA0D,wBAAwB,GACzF,EAAG,gEAAgE,2BAA2B,EAAE,MAAM,EAAS,MAAM,CAAC,CACxH,GACA,EAAa,EAAK,GAClB,EAAY;GACd;GAYoC,WAAU;aAAxC;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAa,oBAA8B,CAAA,GAC3C,kBAAC,GAAD,EAAA,UAAA;KAAmB;KACO,IAAW,IAAI,EAAS,MAAM,KAAK;KAAgB;IAE1D,EAAA,CAAA,CACP,EAAA,CAAA;IAEd,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAO,SAAQ;gBAAkB;KAAc,CAAA,GAC/C,kBAAC,GAAD;MACE,OAAO;MACP,gBAAgB,MAAU,EAAU,CAA+B;gBAFrE,CAIE,kBAAC,GAAD;OAAe,IAAG;OAAkB,cAAW;iBAC7C,kBAAC,GAAD,CAAc,CAAA;MACD,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OAAY,OAAM;iBAAY;MAAqB,CAAA,GACnD,kBAAC,GAAD;OAAY,OAAM;iBAAS;MAAkB,CAAA,CAChC,EAAA,CAAA,CACT;OACL;;IAEL,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAO,SAAQ;iBAAmB;MAAqB,CAAA;MACvD,kBAAC,GAAD;OACE,IAAG;OACH,OAAO;OACP,WAAW,MAAU,EAAW,EAAM,OAAO,KAAK;OAClD,MAAM;OACN,WAAU;OACV,aAAY;MACb,CAAA;MACD,kBAAC,KAAD;OAAG,WAAU;iBAA8B;MAExC,CAAA;KACA;;IAEL,kBAAC,GAAD;KACE,OAAO;KACP,WAAW,MAAW,EAAgB,CAAM;KAC5C,OAAM;KACN,MAAK;IACN,CAAA;IAED,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,eAAe,EAAa,EAAK;KACjC,UAAU;eACX;IAEO,CAAA,GACR,kBAAC,GAAD;KAAQ,MAAK;KAAS,UAAU;eAC7B,IAAU,YAAY;IACjB,CAAA,CACI,EAAA,CAAA;GACV;KACO,CAAA;CACT,CAAA;AAEZ;AAMA,SAAwB,KAAe;CACrC,IAAM,EAAE,gBAAa,EAAsB,GACrC,IAAK,EAAM,GACX,CAAC,GAAc,KAAmB,EAAwC,KAAA,CAAS,GACnF,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAY,KAAiB,EAA6B,IAAI,GAC/D,CAAC,GAAS,KAAc,EAA6B,IAAI,GAEzD,EAAE,SAAM,YAAS,UAAO,eAAY,EAAoB,EAAE,QAAQ,EAAa,CAAC,GAChF,EAAE,MAAM,MAAkB,EAAqB,EAAE,YAAY;EAAE,OAAO;EAAK,QAAQ;CAAE,EAAE,CAAC,GACxF,EAAE,MAAM,MAAiB,EAAoB,EAAE,YAAY;EAAE,OAAO;EAAK,QAAQ;CAAE,EAAE,CAAC,GAEtF,IAAW,GAAM,mBAAmB,CAAC,GAErC,IAAgB,QAAc;EAClC,IAAM,oBAAM,IAAI,IAAoB;EACpC,KAAK,IAAM,KAAY,GAAe,iBAAiB,SAAS,CAAC,GAC/D,EAAI,IAAI,EAAS,IAAI,EAAS,KAAK;EAErC,OAAO;CACT,GAAG,CAAC,CAAa,CAAC,GAEZ,KAAe,QAAc;EACjC,IAAM,oBAAM,IAAI,IAAoB;EACpC,KAAK,IAAM,KAAW,GAAc,gBAAgB,SAAS,CAAC,GAC5D,EAAI,IAAI,EAAQ,IAAI,EAAQ,IAAI;EAElC,OAAO;CACT,GAAG,CAAC,CAAY,CAAC,GAGX,KAAc,MACd,EAAS,aACJ;EACL,OAAO,EAAc,IAAI,EAAS,UAAU,KAAK;EACjD,MAAM,aAAa,EAAS;CAC9B,IAEE,EAAS,YACJ;EACL,OAAO,GAAa,IAAI,EAAS,SAAS,KAAK;EAC/C,MAAM,YAAY,EAAS;CAC7B,IAEK,MAIH,MAAgB,MAA0B;EAC9C,IAAM,IAAW,EAAS,WAAW,aAAa,EAAS,WAAW;EACtE,OAAO,CACL,GAAI,EAAmB,CAAQ,IAC3B,CACE;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,gBAAgB,EAAW,CAAQ;EACrC,CACF,IACA,CAAC,GACL,GAAI,IACA,CACE;GACE,KAAK;GACL,OAAO;GACP,MAAM;GACN,gBAAgB,EAAc,CAAQ;EACxC,CACF,IACA,CAAC,CACP;CACF,GAEM,KACJ,kBAAC,GAAD;EAAQ,MAAK;EAAS,eAAe,EAAW,EAAI;YAApD,CACE,kBAAC,GAAD,EAAM,WAAU,SAAU,CAAA,GACzB,EAAG,4CAA4C,cAAc,CACxD;;CAGV,OACE,kBAAC,GAAD;EACE,OAAO,EAAG,2CAA2C,UAAU;EAC/D,aAAa,EACX,iDACA,+DACF;EACS;YANX;GAQE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,SAAD;MAAO,WAAU;MAA8B,SAAQ;gBAAyB;KAEzE,CAAA,GACP,kBAAC,GAAD;MACE,OAAO,KAAgB;MACvB,gBAAgB,MACd,EAAgB,MAAU,IAAM,KAAA,IAAa,CAA2B;gBAH5E,CAME,kBAAC,GAAD;OACE,IAAG;OACH,WAAU;OACV,cAAW;iBAEX,kBAAC,GAAD,CAAc,CAAA;MACD,CAAA,GACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OAAY,OAAO;iBAAK;MAAwB,CAAA,GAC/C,GAAe,KAAK,MACnB,kBAAC,GAAD;OAA+B,OAAO,EAAO;iBAC1C,EAAO;MACE,GAFK,EAAO,KAEZ,CACb,CACY,EAAA,CAAA,CACT;OACL;QAEJ,IACC,kBAAC,GAAD;KACE,SAAQ;KACR,MAAM;KACN,OAAO,EAAG,6CAA6C,mBAAmB;IAC3E,CAAA,IACC,IACF,kBAAC,GAAD;KACE,SAAS,EACP,2CACA,+CACF;KACA,eAAe,KAAK,EAAQ;IAC7B,CAAA,IACC,EAAS,WAAW,IACtB,kBAAC,GAAD;KACE,MAAM;KACN,OACE,IACI,EACE,sDACA,gCACF,IACA,EAAG,gDAAgD,kBAAkB;KAE3E,aACE,IACI,EACE,4DACA,gCACF,IACA,EACE,sDACA,oEACF;KAEN,QACE,IAAe,KAAA,IACb,kBAAC,GAAD;MAAQ,MAAK;MAAS,eAAe,EAAW,EAAI;gBAApD,CACE,kBAAC,GAAD,EAAM,WAAU,SAAU,CAAA,GACzB,EAAG,4CAA4C,cAAc,CACxD;;IAGb,CAAA,IAED,kBAAA,GAAA,EAAA,UAAA,CAEE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;MACE,kBAAC,GAAD,EAAA,UAAW,QAAgB,CAAA;MAC3B,kBAAC,GAAD,EAAA,UAAW,SAAiB,CAAA;MAC5B,kBAAC,GAAD,EAAA,UAAW,KAAa,CAAA;MACxB,kBAAC,GAAD,EAAA,UAAW,SAAiB,CAAA;MAC5B,kBAAC,GAAD,EAAA,UAAW,SAAiB,CAAA;MAC5B,kBAAC,GAAD,EAAA,UAAW,UAAkB,CAAA;MAC7B,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA;MAC9B,kBAAC,GAAD,EAAA,UAAW,SAAiB,CAAA;MAC5B,kBAAC,GAAD;OAAW,WAAU;iBAAkB;MAAkB,CAAA;KACjD,EAAA,CAAA,EACC,CAAA,GACb,kBAAC,IAAD,EAAA,UACG,EAAS,KAAK,MAAa;MAC1B,IAAM,IAAS,EAAW,CAAQ;MAClC,OACE,kBAAC,GAAD,EAAA,UAAA;OACE,kBAAC,GAAD;QAAW,WAAU;kBAClB,EAAS;OACD,CAAA;OACX,kBAAC,GAAD;QAAW,WAAU;kBAClB,EAAS,UAAU;OACX,CAAA;OACX,kBAAC,GAAD,EAAA,UACG,EAAS,aACR,kBAAC,GAAD;QAAO,SAAQ;QAAY,WAAU;kBAArC,CACE,kBAAC,GAAD;SAAU,WAAU;SAAS,eAAY;QAAQ,CAAA,GAAC,IAE7C;YAEP,kBAAC,QAAD;QAAM,WAAU;kBAAsB;OAAO,CAAA,EAEtC,CAAA;OACX,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAa,QAAQ,EAAS,OAAS,CAAA,EAC9B,CAAA;OACX,kBAAC,GAAD,EAAA,UACG,IACC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAS,EAAO,IAAI;QACnC,WAAU;kBAHZ,CAKE,kBAAC,GAAD;SAAc,WAAU;SAAoB,eAAY;QAAQ,CAAA,GAChE,kBAAC,QAAD;SAAM,WAAU;mBAAY,EAAO;QAAY,CAAA,CACzC;YAER,kBAAC,QAAD;QAAM,WAAU;kBAAsB;OAAO,CAAA,EAEtC,CAAA;OACX,kBAAC,GAAD;QAAW,WAAU;kBAClB,EAAe,EAAS,SAAS;OACzB,CAAA;OACX,kBAAC,GAAD;QAAW,WAAU;kBAClB,EAAe,EAAS,UAAU;OAC1B,CAAA;OACX,kBAAC,GAAD,EAAA,UACG,EAAmB,CAAQ,IAC1B,kBAAC,GAAD;QACE,MAAK;QACL,SAAQ;QACR,MAAK;QACL,WAAU;QACV,eAAe,EAAW,CAAQ;kBALpC,CAOE,kBAAC,GAAD;SAAW,WAAU;SAAW,eAAY;QAAQ,CAAA,GAAC,MAE/C;YAER,kBAAC,QAAD;QAAM,WAAU;kBAAsB;OAAO,CAAA,EAEtC,CAAA;OACX,kBAAC,GAAD;QAAW,WAAU;kBACnB,kBAAC,GAAD;SACE,WAAW,eAAe,EAAS;SACnC,QAAQ,UAAe,EAAS,EAAO,IAAI,IAAI,KAAA;SAC/C,YAAY,GAAa,CAAQ;QAClC,CAAA;OACQ,CAAA;MACH,EAAA,GA/DK,EAAS,EA+Dd;KAEd,CAAC,EACQ,CAAA,CACN,EAAA,CAAA;IACJ,CAAA,GAGL,kBAAC,MAAD;KAAI,WAAU;eACX,EAAS,KAAK,MAAa;MAC1B,IAAM,IAAS,EAAW,CAAQ,GAC5B,IAAW,EAAS,WAAW,aAAa,EAAS,WAAW;MACtE,OACE,kBAAC,MAAD;OAEE,WAAU;iBAFZ;QAIE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAA0C,EAAS;UAAS,CAAA,GACxE,EAAS,SACR,kBAAC,KAAD;WAAG,WAAU;qBAAwC,EAAS;UAAU,CAAA,IACtE,IACD;aACL,kBAAC,GAAD,EAAa,QAAQ,EAAS,OAAS,CAAA,CACpC;;QAEL,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACG,EAAS,aACR,kBAAC,GAAD;UAAO,SAAQ;UAAY,WAAU;oBAArC,CACE,kBAAC,GAAD;WAAU,WAAU;WAAS,eAAY;UAAQ,CAAA,GAAC,IAE7C;cACL,MACH,IACC,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAS,EAAO,IAAI;UACnC,WAAU;oBAHZ,CAKE,kBAAC,GAAD;WAAc,WAAU;WAAoB,eAAY;UAAQ,CAAA,GAChE,kBAAC,QAAD;WAAM,WAAU;qBAAY,EAAO;UAAY,CAAA,CACzC;cACN,IACD;;QAEL,kBAAC,MAAD;SAAI,WAAU;mBAAd,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;UAAI,WAAU;oBAAsB;SAAW,CAAA,GAC/C,kBAAC,MAAD;UAAI,WAAU;oBAAqB,EAAe,EAAS,SAAS;SAAM,CAAA,CACvE,EAAA,CAAA,GACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;UAAI,WAAU;oBAAsB;SAAY,CAAA,GAChD,kBAAC,MAAD;UAAI,WAAU;oBAAqB,EAAe,EAAS,UAAU;SAAM,CAAA,CACxE,EAAA,CAAA,CACH;;QAEH,EAAmB,CAAQ,KAAK,IAC/B,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACG,EAAmB,CAAQ,IAC1B,kBAAC,GAAD;UACE,MAAK;UACL,SAAQ;UACR,MAAK;UACL,eAAe,EAAW,CAAQ;oBAJpC,CAME,kBAAC,GAAD,EAAW,WAAU,SAAU,CAAA,GAAC,cAE1B;cACN,MACH,IACC,kBAAC,GAAD;UACE,MAAK;UACL,SAAQ;UACR,MAAK;UACL,eAAe,EAAc,CAAQ;oBAJvC,CAME,kBAAC,GAAD,EAAQ,WAAU,SAAU,CAAA,GAAC,UAEvB;cACN,IACD;aACH;OACF;SArEG,EAAS,EAqEZ;KAER,CAAC;IACC,CAAA,CACJ,EAAA,CAAA,CAED;;GAEL,kBAAC,IAAD;IACE,MAAM;IACN,cAAc;IACd,mBAAmB,KAAK,EAAQ;GACjC,CAAA;GACD,kBAAC,IAAD;IAEE,UAAU;IACV,eAAe,MAAS;KACtB,AAAK,KACH,EAAc,IAAI;IAEtB;IACA,mBAAmB,KAAK,EAAQ;GACjC,GARM,GAAY,MAAM,MAQxB;GACD,kBAAC,IAAD;IACE,UAAU;IACV,eAAe,MAAS;KACtB,AAAK,KACH,EAAW,IAAI;IAEnB;GACD,CAAA;EACS;;AAEhB"}
|
|
1
|
+
{"version":3,"file":"AnalysesPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/analyses/AnalysesPage.tsx"],"sourcesContent":["/**\n * AnalysesPage — every analysis run across the workspace.\n *\n * Lists `cosListAnalyses` (a plain array, no pagination) with a status filter.\n * A \"Run analysis\" dialog launches a new `cosRunAnalysis` against EITHER a\n * notebook OR a dataset (the schema requires one), chosen via a small selector.\n * In-flight (PENDING / RUNNING) analyses expose a \"Complete\" action that calls\n * `cosCompleteAnalysis` with COMPLETED | FAILED, optional results JSON, and an\n * optional output artifact uploaded through FileUploadField. All values are read\n * from the backend — nothing is fabricated.\n */\nimport { useMemo, useState, type FormEvent } from 'react';\nimport { BarChart3, Beaker, Download, ExternalLink, Play, Sparkles } from 'lucide-react';\nimport {\n Badge,\n Button,\n Dialog,\n DialogClose,\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 Switch,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosAnalysesQuery,\n useCosCompleteAnalysisMutation,\n useCosDatasetsQuery,\n useCosNotebooksQuery,\n useCosRunAnalysisMutation,\n type CosAnalysis,\n type CosAnalysisStatus,\n} from '../../api/cosmicApi';\nimport {\n ActionMenu,\n EmptyState,\n ErrorState,\n FileUploadField,\n LoadingState,\n PageLayout,\n StatusBadge,\n useFileDownloadUrl,\n} from '../../components';\nimport { ResultsChart } from '../../components/ResultsChart';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/** Anything worth opening the results viewer for. */\nfunction hasViewableResults(analysis: CosAnalysis): boolean {\n return (\n Boolean(analysis.outputFile?.id) ||\n Boolean(analysis.results && Object.keys(analysis.results).length > 0)\n );\n}\n\nconst STATUS_OPTIONS: { value: CosAnalysisStatus; label: string }[] = [\n { value: 'PENDING', label: 'Pending' },\n { value: 'RUNNING', label: 'Running' },\n { value: 'COMPLETED', label: 'Completed' },\n { value: 'FAILED', label: 'Failed' },\n];\n\nconst ALL = 'ALL';\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/**\n * Presigned output artifact: attempts an inline image preview (astronomy output\n * is often a plot/spectrum PNG) and always offers a download link. If the file\n * is not an image the preview is dropped on the img error and only the link\n * remains — no assumption is made about the artifact's type.\n */\nfunction OutputArtifact({ fileId }: { fileId: string }) {\n const { data, loading } = useFileDownloadUrl(fileId);\n const [previewFailed, setPreviewFailed] = useState(false);\n const url = data?.getFileDownloadUrl ?? null;\n\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 <div className=\"space-y-2\">\n {!previewFailed ? (\n <img\n src={url}\n alt=\"Analysis output preview\"\n onError={() => setPreviewFailed(true)}\n className=\"max-h-72 w-full rounded-card border border-border-default bg-bg-sunken object-contain\"\n />\n ) : null}\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 Download output\n </a>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Results viewer dialog\n// ---------------------------------------------------------------------------\n\n/**\n * Inline results viewer for a finished analysis: a rendered <ResultsChart> when\n * the payload is chartable (or its key/value fallback otherwise) plus an output\n * artifact preview/download. Everything is read straight from the analysis\n * record.\n */\nfunction ResultsViewerDialog({\n analysis,\n onOpenChange,\n}: {\n analysis: CosAnalysis | null;\n onOpenChange: (open: boolean) => void;\n}) {\n const results = analysis?.results ?? null;\n const hasResults = Boolean(results && Object.keys(results).length > 0);\n const hasOutput = Boolean(analysis?.outputFile?.id);\n\n return (\n <Dialog open={Boolean(analysis)} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[85vh] max-w-2xl overflow-y-auto\">\n {analysis ? (\n <>\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"size-5 text-text-secondary\" aria-hidden=\"true\" />\n Analysis results\n </DialogTitle>\n <DialogDescription>\n {analysis.title}\n {analysis.method ? ` · ${analysis.method}` : ''}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-5\">\n {hasResults && results ? <ResultsChart results={results} /> : null}\n\n {hasOutput && analysis.outputFile ? (\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Output artifact\n </p>\n <OutputArtifact fileId={analysis.outputFile.id} />\n </div>\n ) : null}\n\n {!hasResults && !hasOutput ? (\n <p className=\"rounded-card border border-border-default bg-bg-sunken px-4 py-6 text-center text-sm text-text-secondary\">\n No results or output artifact has been recorded for this analysis yet.\n </p>\n ) : null}\n </div>\n\n <DialogFooter>\n <DialogClose asChild>\n <Button type=\"button\" variant=\"outline\">\n Close\n </Button>\n </DialogClose>\n </DialogFooter>\n </>\n ) : null}\n </DialogContent>\n </Dialog>\n );\n}\n\n/** Parse optional results JSON; blank -> undefined, invalid -> error. */\nfunction parseResults(\n raw: string,\n tr: ReturnType<typeof useTr>\n): { ok: true; value?: Record<string, unknown> } | { ok: false; error: string } {\n const trimmed = raw.trim();\n if (!trimmed) {\n return { ok: true, value: undefined };\n }\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return {\n ok: false,\n error: tr(\n 'cosmicintersection.pages.analyses.resultsMustBeObject',\n 'Results must be a JSON object.'\n ),\n };\n }\n return { ok: true, value: parsed as Record<string, unknown> };\n } catch {\n return {\n ok: false,\n error: tr(\n 'cosmicintersection.pages.analyses.resultsNotValidJson',\n 'Results are not valid JSON.'\n ),\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Run analysis dialog\n// ---------------------------------------------------------------------------\n\ntype RunSourceKind = 'NOTEBOOK' | 'DATASET';\n\ninterface RunDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onCompleted: () => void;\n}\n\nfunction RunAnalysisDialog({ open, onOpenChange, onCompleted }: RunDialogProps) {\n const tr = useTr();\n const [title, setTitle] = useState('');\n const [method, setMethod] = useState('');\n const [aiAssisted, setAiAssisted] = useState(false);\n const [sourceKind, setSourceKind] = useState<RunSourceKind>('NOTEBOOK');\n const [sourceId, setSourceId] = useState('');\n\n const { data: notebooksData, loading: notebooksLoading } = useCosNotebooksQuery({\n pagination: { limit: 100, offset: 0 },\n });\n const { data: datasetsData, loading: datasetsLoading } = useCosDatasetsQuery({\n pagination: { limit: 100, offset: 0 },\n });\n const [runAnalysis, { loading }] = useCosRunAnalysisMutation();\n\n const notebooks = notebooksData?.cosListNotebooks.items ?? [];\n const datasets = datasetsData?.cosListDatasets.items ?? [];\n\n const reset = () => {\n setTitle('');\n setMethod('');\n setAiAssisted(false);\n setSourceKind('NOTEBOOK');\n setSourceId('');\n };\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n const trimmed = title.trim();\n if (!trimmed) {\n notify.error(\n tr('cosmicintersection.pages.analyses.titleRequired', 'Title is required'),\n tr(\n 'cosmicintersection.pages.analyses.titleRequiredDescription',\n 'Give the analysis a descriptive title.'\n )\n );\n return;\n }\n if (!sourceId) {\n notify.error(\n tr('cosmicintersection.pages.analyses.selectSource', 'Select a source'),\n tr(\n 'cosmicintersection.pages.analyses.selectSourceDescription',\n 'Choose a {{kind}} to analyse.',\n {\n kind:\n sourceKind === 'NOTEBOOK'\n ? tr('nav.notebooks', 'notebook').toLowerCase()\n : tr('nav.datasets', 'dataset').toLowerCase(),\n }\n )\n );\n return;\n }\n const response = await runAnalysis({\n variables: {\n input: {\n title: trimmed,\n method: method.trim() || undefined,\n aiAssisted,\n ...(sourceKind === 'NOTEBOOK' ? { notebookId: sourceId } : { datasetId: sourceId }),\n },\n },\n });\n if (response.error || !response.data?.cosRunAnalysis?.id) {\n return;\n }\n notify.success(\n tr('cosmicintersection.pages.analyses.analysisStarted', 'Analysis started'),\n tr(\n 'cosmicintersection.pages.analyses.analysisStartedDescription',\n '“{{name}}” is now running.',\n { name: trimmed }\n )\n );\n reset();\n onOpenChange(false);\n onCompleted();\n };\n\n const sourceLoading = sourceKind === 'NOTEBOOK' ? notebooksLoading : datasetsLoading;\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 against a notebook or a dataset. Exactly one source is required.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-title\">Title</Label>\n <Input\n id=\"run-title\"\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n placeholder=\"e.g. Spectral line fitting\"\n required\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-source-kind\">Source type</Label>\n <Select\n value={sourceKind}\n onValueChange={(value) => {\n setSourceKind(value as RunSourceKind);\n setSourceId('');\n }}\n >\n <SelectTrigger id=\"run-source-kind\" aria-label=\"Source type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"NOTEBOOK\">Notebook</SelectItem>\n <SelectItem value=\"DATASET\">Dataset</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-source-id\">\n {sourceKind === 'NOTEBOOK' ? 'Notebook' : 'Dataset'}\n </Label>\n <Select value={sourceId} onValueChange={(value) => setSourceId(value)}>\n <SelectTrigger id=\"run-source-id\" aria-label=\"Analysis source\">\n <SelectValue\n placeholder={\n sourceLoading\n ? 'Loading…'\n : `Select a ${sourceKind === 'NOTEBOOK' ? 'notebook' : 'dataset'}`\n }\n />\n </SelectTrigger>\n <SelectContent>\n {sourceKind === 'NOTEBOOK'\n ? notebooks.map((notebook) => (\n <SelectItem key={notebook.id} value={notebook.id}>\n {notebook.title}\n </SelectItem>\n ))\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>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"run-method\">Method</Label>\n <Input\n id=\"run-method\"\n value={method}\n onChange={(event) => setMethod(event.target.value)}\n placeholder=\"e.g. gaussian-fit\"\n />\n </div>\n\n <div className=\"flex items-center gap-3\">\n <Switch\n id=\"run-ai\"\n checked={aiAssisted}\n onCheckedChange={(checked) => setAiAssisted(checked)}\n />\n <Label htmlFor=\"run-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\n// ---------------------------------------------------------------------------\n// Complete analysis dialog\n// ---------------------------------------------------------------------------\n\ninterface CompleteDialogProps {\n analysis: CosAnalysis | null;\n onOpenChange: (open: boolean) => void;\n onCompleted: () => void;\n}\n\nfunction CompleteAnalysisDialog({ analysis, onOpenChange, onCompleted }: CompleteDialogProps) {\n const [status, setStatus] = useState<'COMPLETED' | 'FAILED'>('COMPLETED');\n const [results, setResults] = useState('');\n const [outputFileId, setOutputFileId] = useState('');\n const [completeAnalysis, { loading }] = useCosCompleteAnalysisMutation();\n const tr = useTr();\n\n const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (!analysis) {\n return;\n }\n const parsed = parseResults(results, tr);\n if (!parsed.ok) {\n notify.error(\n tr('cosmicintersection.pages.analyses.invalidResults', 'Invalid results'),\n parsed.error\n );\n return;\n }\n const response = await completeAnalysis({\n variables: {\n id: analysis.id,\n status,\n results: parsed.value,\n outputFileId: outputFileId || undefined,\n },\n });\n if (response.error || !response.data?.cosCompleteAnalysis) {\n return;\n }\n notify.success(\n status === 'COMPLETED'\n ? tr('cosmicintersection.pages.analyses.analysisCompleted', 'Analysis completed')\n : tr('cosmicintersection.pages.analyses.analysisMarkedFailed', 'Analysis marked failed'),\n tr(\n 'cosmicintersection.pages.analyses.analysisUpdatedDescription',\n '“{{name}}” was updated.',\n { name: analysis.title }\n )\n );\n onOpenChange(false);\n onCompleted();\n };\n\n return (\n <Dialog\n open={Boolean(analysis)}\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>Complete analysis</DialogTitle>\n <DialogDescription>\n Record the outcome for {analysis ? `“${analysis.title}”` : 'this analysis'}. Attach\n results or an output artifact if you have them.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"complete-status\">Outcome</Label>\n <Select\n value={status}\n onValueChange={(value) => setStatus(value as 'COMPLETED' | 'FAILED')}\n >\n <SelectTrigger id=\"complete-status\" aria-label=\"Outcome\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"COMPLETED\">Completed</SelectItem>\n <SelectItem value=\"FAILED\">Failed</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"space-y-1.5\">\n <Label htmlFor=\"complete-results\">Results (JSON)</Label>\n <Textarea\n id=\"complete-results\"\n value={results}\n onChange={(event) => setResults(event.target.value)}\n rows={4}\n className=\"font-mono text-xs\"\n placeholder='{ \"snr\": 12.4, \"detections\": 3 }'\n />\n <p className=\"text-xs text-text-secondary\">\n Optional structured results as a JSON object.\n </p>\n </div>\n\n <FileUploadField\n value={outputFileId}\n onChange={(fileId) => setOutputFileId(fileId)}\n label=\"Attach output file\"\n hint=\"Optional output artifact (plots, tables, exports). Stored in workspace files.\"\n />\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 {loading ? 'Saving…' : 'Save outcome'}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Page\n// ---------------------------------------------------------------------------\n\nexport default function AnalysesPage() {\n const { navigate } = useCosmicIntersection();\n const tr = useTr();\n const [statusFilter, setStatusFilter] = useState<CosAnalysisStatus | undefined>(undefined);\n const [runOpen, setRunOpen] = useState(false);\n const [completing, setCompleting] = useState<CosAnalysis | null>(null);\n const [viewing, setViewing] = useState<CosAnalysis | null>(null);\n\n const { data, loading, error, refetch } = useCosAnalysesQuery({ status: statusFilter });\n const { data: notebooksData } = useCosNotebooksQuery({ pagination: { limit: 100, offset: 0 } });\n const { data: datasetsData } = useCosDatasetsQuery({ pagination: { limit: 100, offset: 0 } });\n\n const analyses = data?.cosListAnalyses ?? [];\n\n const notebookNames = useMemo(() => {\n const map = new Map<string, string>();\n for (const notebook of notebooksData?.cosListNotebooks.items ?? []) {\n map.set(notebook.id, notebook.title);\n }\n return map;\n }, [notebooksData]);\n\n const datasetNames = 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 /** Resolve a friendly label + route for an analysis' notebook OR dataset source. */\n const sourceInfo = (analysis: CosAnalysis): { label: string; path: string } | null => {\n if (analysis.notebookId) {\n return {\n label: notebookNames.get(analysis.notebookId) ?? 'Notebook',\n path: `notebooks/${analysis.notebookId}`,\n };\n }\n if (analysis.datasetId) {\n return {\n label: datasetNames.get(analysis.datasetId) ?? 'Dataset',\n path: `datasets/${analysis.datasetId}`,\n };\n }\n return null;\n };\n\n /** Extra ActionMenu items for a row/card (view results + complete). */\n const extraActions = (analysis: CosAnalysis) => {\n const inFlight = analysis.status === 'PENDING' || analysis.status === 'RUNNING';\n return [\n ...(hasViewableResults(analysis)\n ? [\n {\n key: 'view-results',\n label: 'View results',\n icon: BarChart3,\n onSelect: () => setViewing(analysis),\n },\n ]\n : []),\n ...(inFlight\n ? [\n {\n key: 'complete',\n label: 'Complete…',\n icon: Beaker,\n onSelect: () => setCompleting(analysis),\n },\n ]\n : []),\n ];\n };\n\n const actions = (\n <Button type=\"button\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n {tr('cosmicintersection.pages.analyses.create', 'Run analysis')}\n </Button>\n );\n\n return (\n <PageLayout\n title={tr('cosmicintersection.pages.analyses.title', 'Analyses')}\n description={tr(\n 'cosmicintersection.pages.analyses.description',\n 'Analysis runs executed over notebooks and federated datasets.'\n )}\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=\"analysis-status-filter\">\n Status\n </label>\n <Select\n value={statusFilter ?? ALL}\n onValueChange={(value) =>\n setStatusFilter(value === ALL ? undefined : (value as CosAnalysisStatus))\n }\n >\n <SelectTrigger\n id=\"analysis-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\n variant=\"rows\"\n rows={6}\n label={tr('cosmicintersection.pages.analyses.loading', 'Loading analyses…')}\n />\n ) : error ? (\n <ErrorState\n message={tr(\n 'cosmicintersection.pages.analyses.error',\n 'We could not load analyses. Please try again.'\n )}\n onRetry={() => void refetch()}\n />\n ) : analyses.length === 0 ? (\n <EmptyState\n icon={Beaker}\n title={\n statusFilter\n ? tr(\n 'cosmicintersection.pages.analyses.emptyFilterTitle',\n 'No analyses match this filter.'\n )\n : tr('cosmicintersection.pages.analyses.emptyTitle', 'No analyses yet.')\n }\n description={\n statusFilter\n ? tr(\n 'cosmicintersection.pages.analyses.emptyFilterDescription',\n 'Try a different status filter.'\n )\n : tr(\n 'cosmicintersection.pages.analyses.emptyDescription',\n 'Run an analysis against a notebook or dataset to see results here.'\n )\n }\n action={\n statusFilter ? undefined : (\n <Button type=\"button\" onClick={() => setRunOpen(true)}>\n <Play className=\"size-4\" />\n {tr('cosmicintersection.pages.analyses.create', 'Run analysis')}\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>Method</TableHead>\n <TableHead>AI</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Source</TableHead>\n <TableHead>Started</TableHead>\n <TableHead>Finished</TableHead>\n <TableHead>Output</TableHead>\n <TableHead className=\"w-12 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {analyses.map((analysis) => {\n const source = sourceInfo(analysis);\n return (\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>\n {source ? (\n <button\n type=\"button\"\n onClick={() => navigate(source.path)}\n className=\"inline-flex max-w-[12rem] items-center gap-1 truncate text-sm text-text-primary underline-offset-2 hover:underline\"\n >\n <ExternalLink className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">{source.label}</span>\n </button>\n ) : (\n <span className=\"text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDateTime(analysis.startedAt)}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDateTime(analysis.finishedAt)}\n </TableCell>\n <TableCell>\n {hasViewableResults(analysis) ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-auto gap-1 px-2 py-1\"\n onClick={() => setViewing(analysis)}\n >\n <BarChart3 className=\"size-3.5\" aria-hidden=\"true\" />\n View\n </Button>\n ) : (\n <span className=\"text-text-secondary\">—</span>\n )}\n </TableCell>\n <TableCell className=\"text-right\">\n <ActionMenu\n ariaLabel={`Actions for ${analysis.title}`}\n onView={source ? () => navigate(source.path) : undefined}\n extraItems={extraActions(analysis)}\n />\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n\n {/* Mobile card list */}\n <ul className=\"space-y-3 md:hidden\">\n {analyses.map((analysis) => {\n const source = sourceInfo(analysis);\n const inFlight = analysis.status === 'PENDING' || analysis.status === 'RUNNING';\n return (\n <li\n key={analysis.id}\n className=\"space-y-3 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\n <div className=\"flex flex-wrap items-center gap-2\">\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 {source ? (\n <button\n type=\"button\"\n onClick={() => navigate(source.path)}\n className=\"inline-flex max-w-full items-center gap-1 truncate text-sm text-text-primary underline-offset-2 hover:underline\"\n >\n <ExternalLink className=\"size-3.5 shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">{source.label}</span>\n </button>\n ) : null}\n </div>\n\n <dl className=\"grid grid-cols-2 gap-2 text-xs\">\n <div>\n <dt className=\"text-text-secondary\">Started</dt>\n <dd className=\"text-text-primary\">{formatDateTime(analysis.startedAt)}</dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">Finished</dt>\n <dd className=\"text-text-primary\">{formatDateTime(analysis.finishedAt)}</dd>\n </div>\n </dl>\n\n {hasViewableResults(analysis) || inFlight ? (\n <div className=\"flex flex-wrap gap-2\">\n {hasViewableResults(analysis) ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setViewing(analysis)}\n >\n <BarChart3 className=\"size-4\" />\n View results\n </Button>\n ) : null}\n {inFlight ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setCompleting(analysis)}\n >\n <Beaker className=\"size-4\" />\n Complete\n </Button>\n ) : null}\n </div>\n ) : null}\n </li>\n );\n })}\n </ul>\n </>\n )}\n </div>\n\n <RunAnalysisDialog\n open={runOpen}\n onOpenChange={setRunOpen}\n onCompleted={() => void refetch()}\n />\n <CompleteAnalysisDialog\n key={completing?.id ?? 'none'}\n analysis={completing}\n onOpenChange={(open) => {\n if (!open) {\n setCompleting(null);\n }\n }}\n onCompleted={() => void refetch()}\n />\n <ResultsViewerDialog\n analysis={viewing}\n onOpenChange={(open) => {\n if (!open) {\n setViewing(null);\n }\n }}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export default function CollectionDetailPage(): import("react
|
|
1
|
+
export default function CollectionDetailPage(): import("react").JSX.Element;
|
|
2
2
|
//# sourceMappingURL=CollectionDetailPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollectionDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/collections/CollectionDetailPage.tsx"],"names":[],"mappings":"AA0CA,MAAM,CAAC,OAAO,UAAU,oBAAoB,
|
|
1
|
+
{"version":3,"file":"CollectionDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/collections/CollectionDetailPage.tsx"],"names":[],"mappings":"AA0CA,MAAM,CAAC,OAAO,UAAU,oBAAoB,gCAsP3C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export default function CollectionFormPage(): import("react
|
|
1
|
+
export default function CollectionFormPage(): import("react").JSX.Element;
|
|
2
2
|
//# sourceMappingURL=CollectionFormPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollectionFormPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/collections/CollectionFormPage.tsx"],"names":[],"mappings":"AAkCA,MAAM,CAAC,OAAO,UAAU,kBAAkB,
|
|
1
|
+
{"version":3,"file":"CollectionFormPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/collections/CollectionFormPage.tsx"],"names":[],"mappings":"AAkCA,MAAM,CAAC,OAAO,UAAU,kBAAkB,gCA+IzC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export default function CollectionsPage(): import("react
|
|
1
|
+
export default function CollectionsPage(): import("react").JSX.Element;
|
|
2
2
|
//# sourceMappingURL=CollectionsPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollectionsPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/collections/CollectionsPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CollectionsPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/collections/CollectionsPage.tsx"],"names":[],"mappings":"AAwCA,MAAM,CAAC,OAAO,UAAU,eAAe,gCA8JtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollectionsPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/collections/CollectionsPage.tsx"],"sourcesContent":["import { Database, FolderOpen, Globe, Lock, Plus } from 'lucide-react';\nimport { Badge, Button } from '@burdenoff/fe-libs/ui';\n\nimport {\n COS_RELATION_PAGE_SIZE,\n useCosArchiveCollectionMutation,\n useCosCollectionsQuery,\n type CosCollectionListItem,\n} from '../../api/cosmicApi';\nimport { ActionMenu, EmptyState, ErrorState, LoadingState, PageLayout } from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/**\n * Label for how many datasets a collection holds.\n *\n * `collection.datasets` is the subgraph's id-only relation selection: it is\n * already access-filtered to this actor and capped at COS_RELATION_PAGE_SIZE.\n * At the cap the true total is unknown, so we say `50+` rather than assert a\n * number the server never promised.\n */\nfunction datasetCountLabel(count: number, trFn: (k: string, f: string, p?: Record<string, string | number>) => string): string {\n if (count >= COS_RELATION_PAGE_SIZE) {\n return trFn(\n 'cosmicintersection.pages.collections.datasetCountCapped',\n '{{count}}+ datasets',\n { count: COS_RELATION_PAGE_SIZE },\n );\n }\n return trFn(\n count === 1\n ? 'cosmicintersection.pages.collections.datasetCountOne'\n : 'cosmicintersection.pages.collections.datasetCount',\n count === 1 ? '{{count}} dataset' : '{{count}} datasets',\n { count },\n );\n}\n\nexport default function CollectionsPage() {\n const { navigate } = useCosmicIntersection();\n const tr = useTr();\n const { data, loading, error, refetch } = useCosCollectionsQuery();\n const [archiveCollection] = useCosArchiveCollectionMutation();\n\n const collections = data?.cosListCollections ?? [];\n\n const handleArchive = async (collection: CosCollectionListItem) => {\n const response = await archiveCollection({ variables: { id: collection.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.cosArchiveCollection) {\n notify.error(\n tr('cosmicintersection.pages.collections.archiveError', 'Could not archive collection'),\n tr('cosmicintersection.pages.collections.archiveErrorDescription', 'The server did not confirm the change.'),\n );\n throw new Error('Archive not confirmed');\n }\n notify.success(tr('cosmicintersection.pages.collections.archiveSuccess', 'Collection archived'));\n await refetch();\n };\n\n const newButton = (\n <Button type=\"button\" onClick={() => navigate('collections/create')}>\n <Plus className=\"size-4\" />\n {tr('cosmicintersection.pages.collections.create', 'New collection')}\n </Button>\n );\n\n const renderBody = () => {\n if (loading) {\n return <LoadingState variant=\"rows\" rows={4} label={tr('cosmicintersection.pages.collections.loading', 'Loading collections…')} />;\n }\n if (error) {\n return <ErrorState message={error.message} onRetry={() => void refetch()} />;\n }\n if (collections.length === 0) {\n return (\n <EmptyState\n icon={FolderOpen}\n title={tr('cosmicintersection.pages.collections.emptyTitle', 'No collections yet')}\n description={tr(\n 'cosmicintersection.pages.collections.emptyDescription',\n 'Group related datasets — by mission, survey or research theme — into a shareable collection.',\n )}\n action={newButton}\n />\n );\n }\n\n return (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {collections.map((collection) => (\n <div\n key={collection.id}\n role=\"button\"\n tabIndex={0}\n aria-label={tr('cosmicintersection.common.open', 'Open {{name}}', { name: collection.name })}\n onClick={() => navigate(`collections/${collection.id}`)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n navigate(`collections/${collection.id}`);\n }\n }}\n className=\"flex cursor-pointer flex-col gap-3 rounded-card border border-border-default bg-bg-elevated p-5 text-left outline-none transition-colors hover:bg-bg-sunken focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <h2 className=\"truncate text-base font-semibold text-text-primary\">\n {collection.name}\n </h2>\n {collection.slug ? (\n <p className=\"mt-0.5 truncate text-xs text-text-secondary\">/{collection.slug}</p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {collection.isPublic ? (\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-status-success-border bg-status-success-bg-subtle text-status-success-text\"\n >\n <Globe className=\"size-3\" aria-hidden=\"true\" />\n {tr('cosmicintersection.status.public', 'Public')}\n </Badge>\n ) : (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <Lock className=\"size-3\" aria-hidden=\"true\" />\n {tr('cosmicintersection.status.private', 'Private')}\n </Badge>\n )}\n <ActionMenu\n ariaLabel={tr('cosmicintersection.common.actionsFor', 'Actions for {{name}}', { name: collection.name })}\n entityName={collection.name}\n onEdit={() => navigate(`collections/${collection.id}/edit`)}\n onDelete={() => handleArchive(collection)}\n deleteLabel={tr('cosmicintersection.pages.collections.archive', 'Archive collection')}\n />\n </div>\n </div>\n\n <p className=\"line-clamp-2 min-h-[2.5rem] text-sm text-text-secondary\">\n {collection.description ?? tr('cosmicintersection.common.noDescription', 'No description provided.')}\n </p>\n\n <div className=\"mt-auto flex flex-wrap items-center gap-1\">\n <Badge variant=\"secondary\" className=\"gap-1 text-xs\">\n <Database className=\"size-3\" aria-hidden=\"true\" />\n {datasetCountLabel(collection.datasets.length, tr)}\n </Badge>\n {collection.tags.slice(0, 4).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n {collection.tags.length > 4 ? (\n <span className=\"text-xs text-text-secondary\">+{collection.tags.length - 4}</span>\n ) : null}\n </div>\n </div>\n ))}\n </div>\n );\n };\n\n return (\n <PageLayout\n title={tr('cosmicintersection.pages.collections.title', 'Collections')}\n description={tr(\n 'cosmicintersection.pages.collections.description',\n 'Curated groupings of datasets by mission, survey or research theme.',\n )}\n actions={newButton}\n >\n {renderBody()}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,SAAS,EAAkB,GAAe,GAAqF;CAQ7H,OAPI,KAAA,KACK,EACL,2DACA,uBACA,EAAE,OAAA,GAA8B,CAClC,IAEK,EACL,MAAU,IACN,yDACA,qDACJ,MAAU,IAAI,sBAAsB,sBACpC,EAAE,SAAM,CACV;AACF;AAEA,SAAwB,IAAkB;CACxC,IAAM,EAAE,gBAAa,EAAsB,GACrC,IAAK,EAAM,GACX,EAAE,SAAM,YAAS,UAAO,eAAY,EAAuB,GAC3D,CAAC,KAAqB,EAAgC,GAEtD,IAAc,GAAM,sBAAsB,CAAC,GAE3C,IAAgB,OAAO,MAAsC;EACjE,IAAM,IAAW,MAAM,EAAkB,EAAE,WAAW,EAAE,IAAI,EAAW,GAAG,EAAE,CAAC;EAC7E,IAAI,EAAS,OACX,MAAU,MAAM,EAAS,MAAM,OAAO;EAExC,IAAI,CAAC,EAAS,MAAM,sBAKlB,MAJA,EAAO,MACL,EAAG,qDAAqD,8BAA8B,GACtF,EAAG,gEAAgE,wCAAwC,CAC7G,GACU,MAAM,uBAAuB;EAGzC,AADA,EAAO,QAAQ,EAAG,uDAAuD,qBAAqB,CAAC,GAC/F,MAAM,EAAQ;CAChB,GAEM,IACJ,kBAAC,GAAD;EAAQ,MAAK;EAAS,eAAe,EAAS,oBAAoB;YAAlE,CACE,kBAAC,GAAD,EAAM,WAAU,SAAU,CAAA,GACzB,EAAG,+CAA+C,gBAAgB,CAC7D;;CAmGV,OACE,kBAAC,GAAD;EACE,OAAO,EAAG,8CAA8C,aAAa;EACrE,aAAa,EACX,oDACA,qEACF;EACA,SAAS;YAtGP,IACK,kBAAC,GAAD;GAAc,SAAQ;GAAO,MAAM;GAAG,OAAO,EAAG,gDAAgD,sBAAsB;EAAI,CAAA,IAE/H,IACK,kBAAC,GAAD;GAAY,SAAS,EAAM;GAAS,eAAe,KAAK,EAAQ;EAAI,CAAA,IAEzE,EAAY,WAAW,IAEvB,kBAAC,GAAD;GACE,MAAM;GACN,OAAO,EAAG,mDAAmD,oBAAoB;GACjF,aAAa,EACX,yDACA,8FACF;GACA,QAAQ;EACT,CAAA,IAKH,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAY,KAAK,MAChB,kBAAC,OAAD;IAEE,MAAK;IACL,UAAU;IACV,cAAY,EAAG,kCAAkC,iBAAiB,EAAE,MAAM,EAAW,KAAK,CAAC;IAC3F,eAAe,EAAS,eAAe,EAAW,IAAI;IACtD,YAAY,MAAU;KACpB,CAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,SACzC,EAAM,eAAe,GACrB,EAAS,eAAe,EAAW,IAAI;IAE3C;IACA,WAAU;cAZZ;KAcE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAW;OACV,CAAA,GACH,EAAW,OACV,kBAAC,KAAD;QAAG,WAAU;kBAAb,CAA2D,KAAE,EAAW,IAAQ;YAC9E,IACD;UACL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,EAAW,WACV,kBAAC,GAAD;QACE,SAAQ;QACR,WAAU;kBAFZ,CAIE,kBAAC,GAAD;SAAO,WAAU;SAAS,eAAY;QAAQ,CAAA,GAC7C,EAAG,oCAAoC,QAAQ,CAC3C;YAEP,kBAAC,GAAD;QAAO,SAAQ;QAAY,WAAU;kBAArC,CACE,kBAAC,GAAD;SAAM,WAAU;SAAS,eAAY;QAAQ,CAAA,GAC5C,EAAG,qCAAqC,SAAS,CAC7C;WAET,kBAAC,GAAD;QACE,WAAW,EAAG,wCAAwC,wBAAwB,EAAE,MAAM,EAAW,KAAK,CAAC;QACvG,YAAY,EAAW;QACvB,cAAc,EAAS,eAAe,EAAW,GAAG,MAAM;QAC1D,gBAAgB,EAAc,CAAU;QACxC,aAAa,EAAG,gDAAgD,oBAAoB;OACrF,CAAA,CACE;QACF;;KAEL,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAW,eAAe,EAAG,2CAA2C,0BAA0B;KAClG,CAAA;KAEH,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD;QAAO,SAAQ;QAAY,WAAU;kBAArC,CACE,kBAAC,GAAD;SAAU,WAAU;SAAS,eAAY;QAAQ,CAAA,GAChD,EAAkB,EAAW,SAAS,QAAQ,CAAE,CAC5C;;OACN,EAAW,KAAK,MAAM,GAAG,CAAC,EAAE,KAAK,MAChC,kBAAC,GAAD;QAAiB,SAAQ;QAAU,WAAU;kBAC1C;OACI,GAFK,CAEL,CACR;OACA,EAAW,KAAK,SAAS,IACxB,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAA8C,KAAE,EAAW,KAAK,SAAS,CAAQ;YAC/E;MACD;;IACF;MAjEE,EAAW,EAiEb,CACN;EACE,CAAA;CAcK,CAAA;AAEhB"}
|
|
1
|
+
{"version":3,"file":"CollectionsPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/collections/CollectionsPage.tsx"],"sourcesContent":["import { Database, FolderOpen, Globe, Lock, Plus } from 'lucide-react';\nimport { Badge, Button } from '@burdenoff/fe-libs/ui';\n\nimport {\n COS_RELATION_PAGE_SIZE,\n useCosArchiveCollectionMutation,\n useCosCollectionsQuery,\n type CosCollectionListItem,\n} from '../../api/cosmicApi';\nimport { ActionMenu, EmptyState, ErrorState, LoadingState, PageLayout } from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/**\n * Label for how many datasets a collection holds.\n *\n * `collection.datasets` is the subgraph's id-only relation selection: it is\n * already access-filtered to this actor and capped at COS_RELATION_PAGE_SIZE.\n * At the cap the true total is unknown, so we say `50+` rather than assert a\n * number the server never promised.\n */\nfunction datasetCountLabel(\n count: number,\n trFn: (k: string, f: string, p?: Record<string, string | number>) => string\n): string {\n if (count >= COS_RELATION_PAGE_SIZE) {\n return trFn('cosmicintersection.pages.collections.datasetCountCapped', '{{count}}+ datasets', {\n count: COS_RELATION_PAGE_SIZE,\n });\n }\n return trFn(\n count === 1\n ? 'cosmicintersection.pages.collections.datasetCountOne'\n : 'cosmicintersection.pages.collections.datasetCount',\n count === 1 ? '{{count}} dataset' : '{{count}} datasets',\n { count }\n );\n}\n\nexport default function CollectionsPage() {\n const { navigate } = useCosmicIntersection();\n const tr = useTr();\n const { data, loading, error, refetch } = useCosCollectionsQuery();\n const [archiveCollection] = useCosArchiveCollectionMutation();\n\n const collections = data?.cosListCollections ?? [];\n\n const handleArchive = async (collection: CosCollectionListItem) => {\n const response = await archiveCollection({ variables: { id: collection.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.cosArchiveCollection) {\n notify.error(\n tr('cosmicintersection.pages.collections.archiveError', 'Could not archive collection'),\n tr(\n 'cosmicintersection.pages.collections.archiveErrorDescription',\n 'The server did not confirm the change.'\n )\n );\n throw new Error('Archive not confirmed');\n }\n notify.success(\n tr('cosmicintersection.pages.collections.archiveSuccess', 'Collection archived')\n );\n await refetch();\n };\n\n const newButton = (\n <Button type=\"button\" onClick={() => navigate('collections/create')}>\n <Plus className=\"size-4\" />\n {tr('cosmicintersection.pages.collections.create', 'New collection')}\n </Button>\n );\n\n const renderBody = () => {\n if (loading) {\n return (\n <LoadingState\n variant=\"rows\"\n rows={4}\n label={tr('cosmicintersection.pages.collections.loading', 'Loading collections…')}\n />\n );\n }\n if (error) {\n return <ErrorState message={error.message} onRetry={() => void refetch()} />;\n }\n if (collections.length === 0) {\n return (\n <EmptyState\n icon={FolderOpen}\n title={tr('cosmicintersection.pages.collections.emptyTitle', 'No collections yet')}\n description={tr(\n 'cosmicintersection.pages.collections.emptyDescription',\n 'Group related datasets — by mission, survey or research theme — into a shareable collection.'\n )}\n action={newButton}\n />\n );\n }\n\n return (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {collections.map((collection) => (\n <div\n key={collection.id}\n role=\"button\"\n tabIndex={0}\n aria-label={tr('cosmicintersection.common.open', 'Open {{name}}', {\n name: collection.name,\n })}\n onClick={() => navigate(`collections/${collection.id}`)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n navigate(`collections/${collection.id}`);\n }\n }}\n className=\"flex cursor-pointer flex-col gap-3 rounded-card border border-border-default bg-bg-elevated p-5 text-left outline-none transition-colors hover:bg-bg-sunken focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <h2 className=\"truncate text-base font-semibold text-text-primary\">\n {collection.name}\n </h2>\n {collection.slug ? (\n <p className=\"mt-0.5 truncate text-xs text-text-secondary\">/{collection.slug}</p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {collection.isPublic ? (\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-status-success-border bg-status-success-bg-subtle text-status-success-text\"\n >\n <Globe className=\"size-3\" aria-hidden=\"true\" />\n {tr('cosmicintersection.status.public', 'Public')}\n </Badge>\n ) : (\n <Badge variant=\"secondary\" className=\"gap-1\">\n <Lock className=\"size-3\" aria-hidden=\"true\" />\n {tr('cosmicintersection.status.private', 'Private')}\n </Badge>\n )}\n <ActionMenu\n ariaLabel={tr('cosmicintersection.common.actionsFor', 'Actions for {{name}}', {\n name: collection.name,\n })}\n entityName={collection.name}\n onEdit={() => navigate(`collections/${collection.id}/edit`)}\n onDelete={() => handleArchive(collection)}\n deleteLabel={tr(\n 'cosmicintersection.pages.collections.archive',\n 'Archive collection'\n )}\n />\n </div>\n </div>\n\n <p className=\"line-clamp-2 min-h-[2.5rem] text-sm text-text-secondary\">\n {collection.description ??\n tr('cosmicintersection.common.noDescription', 'No description provided.')}\n </p>\n\n <div className=\"mt-auto flex flex-wrap items-center gap-1\">\n <Badge variant=\"secondary\" className=\"gap-1 text-xs\">\n <Database className=\"size-3\" aria-hidden=\"true\" />\n {datasetCountLabel(collection.datasets.length, tr)}\n </Badge>\n {collection.tags.slice(0, 4).map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n {collection.tags.length > 4 ? (\n <span className=\"text-xs text-text-secondary\">+{collection.tags.length - 4}</span>\n ) : null}\n </div>\n </div>\n ))}\n </div>\n );\n };\n\n return (\n <PageLayout\n title={tr('cosmicintersection.pages.collections.title', 'Collections')}\n description={tr(\n 'cosmicintersection.pages.collections.description',\n 'Curated groupings of datasets by mission, survey or research theme.'\n )}\n actions={newButton}\n >\n {renderBody()}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export default function DashboardPage(): import("react
|
|
1
|
+
export default function DashboardPage(): import("react").JSX.Element;
|
|
2
2
|
//# sourceMappingURL=DashboardPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/dashboard/DashboardPage.tsx"],"names":[],"mappings":"AAyEA,MAAM,CAAC,OAAO,UAAU,aAAa,
|
|
1
|
+
{"version":3,"file":"DashboardPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/dashboard/DashboardPage.tsx"],"names":[],"mappings":"AAyEA,MAAM,CAAC,OAAO,UAAU,aAAa,gCA4OpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/dashboard/DashboardPage.tsx"],"sourcesContent":["import type { LucideIcon } from 'lucide-react';\nimport {\n Boxes,\n Database,\n FileText,\n NotebookPen,\n Plus,\n Radar,\n Rocket,\n Workflow,\n} from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosDatasetsQuery,\n useCosNotebooksQuery,\n useCosPipelinesQuery,\n useCosReleasesQuery,\n useCosRunsQuery,\n useCosSourcesQuery,\n type CosConnection,\n} from '../../api/cosmicApi';\nimport { EmptyState, PageLayout, StatusBadge } from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { useTr } from '../../../shared/hooks/useTr';\n\nconst TILE_LIMIT = 50;\n\nfunction formatDateTime(raw: string | null | undefined): string {\n if (!raw) {\n return '—';\n }\n const date = new Date(raw);\n return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();\n}\n\n/** \"13\" or \"50+\" — the honest count of the page we fetched, never a fabricated total. */\nfunction connectionCount<T>(connection: CosConnection<T> | undefined): string {\n if (!connection) {\n return '—';\n }\n const loaded = connection.items.length;\n return connection.pageInfo?.hasMore ? `${loaded}+` : String(loaded);\n}\n\ninterface SummaryTileProps {\n label: string;\n count: string;\n icon: LucideIcon;\n loading: boolean;\n onClick: () => void;\n}\n\nfunction SummaryTile({ label, count, icon: Icon, loading, onClick }: SummaryTileProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex flex-col gap-2 rounded-card border border-border-default bg-bg-elevated p-4 text-left transition-colors hover:border-border-strong hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium text-text-secondary\">{label}</span>\n <span className=\"flex size-8 items-center justify-center rounded-button border border-border-default bg-bg-sunken text-text-secondary transition-colors group-hover:border-border-strong\">\n <Icon className=\"size-4\" aria-hidden=\"true\" />\n </span>\n </div>\n <span className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {loading ? '…' : count}\n </span>\n </button>\n );\n}\n\nexport default function DashboardPage() {\n const { navigate } = useCosmicIntersection();\n const tr = useTr();\n\n const datasetsQuery = useCosDatasetsQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const sourcesQuery = useCosSourcesQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const pipelinesQuery = useCosPipelinesQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const runsQuery = useCosRunsQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const notebooksQuery = useCosNotebooksQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const releasesQuery = useCosReleasesQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const publishedQuery = useCosReleasesQuery({\n status: 'PUBLISHED',\n pagination: { limit: 5, offset: 0 },\n });\n\n const recentRuns = (runsQuery.data?.cosListRuns?.items ?? []).slice(0, 6);\n const publishedReleases = publishedQuery.data?.cosListReleases?.items ?? [];\n\n return (\n <PageLayout\n title={tr('cosmicintersection.pages.dashboard.title', 'CosmicIntersection')}\n description={tr(\n 'cosmicintersection.pages.dashboard.description',\n 'A federation workspace for science data — sources, datasets, pipelines, notebooks and releases in one place.',\n )}\n actions={\n <Button type=\"button\" onClick={() => navigate('datasets/create')}>\n <Plus className=\"size-4\" />\n {tr('cosmicintersection.pages.dashboard.newDataset', 'New dataset')}\n </Button>\n }\n >\n <div className=\"space-y-6\">\n {/* Summary tiles */}\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6\">\n <SummaryTile\n label={tr('nav.datasets', 'Datasets')}\n icon={Database}\n loading={datasetsQuery.loading}\n count={connectionCount(datasetsQuery.data?.cosListDatasets)}\n onClick={() => navigate('datasets')}\n />\n <SummaryTile\n label={tr('nav.sources', 'Sources')}\n icon={Radar}\n loading={sourcesQuery.loading}\n count={connectionCount(sourcesQuery.data?.cosListSources)}\n onClick={() => navigate('sources')}\n />\n <SummaryTile\n label={tr('nav.pipelines', 'Pipelines')}\n icon={Workflow}\n loading={pipelinesQuery.loading}\n count={connectionCount(pipelinesQuery.data?.cosListPipelines)}\n onClick={() => navigate('pipelines')}\n />\n <SummaryTile\n label={tr('nav.runs', 'Runs')}\n icon={Boxes}\n loading={runsQuery.loading}\n count={connectionCount(runsQuery.data?.cosListRuns)}\n onClick={() => navigate('runs')}\n />\n <SummaryTile\n label={tr('nav.notebooks', 'Notebooks')}\n icon={NotebookPen}\n loading={notebooksQuery.loading}\n count={connectionCount(notebooksQuery.data?.cosListNotebooks)}\n onClick={() => navigate('notebooks')}\n />\n <SummaryTile\n label={tr('nav.releases', 'Releases')}\n icon={FileText}\n loading={releasesQuery.loading}\n count={connectionCount(releasesQuery.data?.cosListReleases)}\n onClick={() => navigate('releases')}\n />\n </div>\n\n {/* Quick actions */}\n <div className=\"rounded-card border border-border-default bg-bg-elevated p-5\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('cosmicintersection.pages.dashboard.quickActions', 'Quick actions')}\n </h2>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('datasets/create')}>\n <Database className=\"size-4\" />\n {tr('cosmicintersection.pages.dashboard.registerDataset', 'Register dataset')}\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('sources/create')}>\n <Radar className=\"size-4\" />\n {tr('cosmicintersection.pages.dashboard.connectSource', 'Connect source')}\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('pipelines/create')}>\n <Workflow className=\"size-4\" />\n {tr('cosmicintersection.pages.dashboard.createPipeline', 'Create pipeline')}\n </Button>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Recent runs */}\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('cosmicintersection.pages.dashboard.recentRuns', 'Recent runs')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigate('runs')}\n className=\"text-sm text-text-secondary underline-offset-2 hover:text-text-primary hover:underline\"\n >\n {tr('cosmicintersection.pages.dashboard.viewAll', 'View all')}\n </button>\n </div>\n {runsQuery.loading ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"h-14 animate-pulse rounded-card bg-bg-sunken\" />\n ))}\n </div>\n ) : recentRuns.length === 0 ? (\n <EmptyState\n icon={Boxes}\n title={tr('cosmicintersection.pages.dashboard.noRunsYet', 'No pipeline runs yet.')}\n description={tr(\n 'cosmicintersection.pages.dashboard.noRunsDescription',\n 'Start a pipeline run to see execution history here.',\n )}\n />\n ) : (\n <ul className=\"space-y-2\">\n {recentRuns.map((run) => (\n <li key={run.id}>\n <button\n type=\"button\"\n onClick={() => navigate(`runs/${run.id}`)}\n className=\"flex w-full items-center justify-between gap-3 rounded-card border border-border-default bg-bg-elevated px-4 py-3 text-left transition-colors hover:border-border-strong hover:bg-bg-sunken\"\n >\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary\">\n {tr('cosmicintersection.pages.dashboard.runNumber', 'Run #{{number}}', {\n number: run.runNumber,\n })}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {run.startedAt\n ? tr(\n 'cosmicintersection.pages.dashboard.startedAt',\n 'Started {{date}}',\n { date: formatDateTime(run.startedAt) },\n )\n : tr(\n 'cosmicintersection.pages.dashboard.runNotStarted',\n 'Not started',\n )}\n </p>\n </div>\n <StatusBadge status={run.status} />\n </button>\n </li>\n ))}\n </ul>\n )}\n </section>\n\n {/* Recently published releases */}\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('cosmicintersection.pages.dashboard.recentlyPublished', 'Recently published')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigate('releases')}\n className=\"text-sm text-text-secondary underline-offset-2 hover:text-text-primary hover:underline\"\n >\n {tr('cosmicintersection.pages.dashboard.viewAll', 'View all')}\n </button>\n </div>\n {publishedQuery.loading ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"h-14 animate-pulse rounded-card bg-bg-sunken\" />\n ))}\n </div>\n ) : publishedReleases.length === 0 ? (\n <EmptyState\n icon={Rocket}\n title={tr(\n 'cosmicintersection.pages.dashboard.noPublishedReleases',\n 'No published releases yet.',\n )}\n description={tr(\n 'cosmicintersection.pages.dashboard.noPublishedReleasesDescription',\n 'Publish a release to share datasets and notebooks with a citable version.',\n )}\n />\n ) : (\n <ul className=\"space-y-2\">\n {publishedReleases.map((release) => (\n <li key={release.id}>\n <button\n type=\"button\"\n onClick={() => navigate('releases')}\n className=\"flex w-full items-center justify-between gap-3 rounded-card border border-border-default bg-bg-elevated px-4 py-3 text-left transition-colors hover:border-border-strong hover:bg-bg-sunken\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{release.title}</p>\n <p className=\"text-xs text-text-secondary\">\n v{release.version}\n {release.publishedAt\n ? ` · ${tr(\n 'cosmicintersection.pages.dashboard.publishedAt',\n 'Published {{date}}',\n { date: formatDateTime(release.publishedAt) },\n )}`\n : ''}\n </p>\n {release.doi ? (\n <p\n className=\"mt-0.5 truncate font-mono text-xs text-text-tertiary\"\n title={release.doi}\n >\n DOI: {release.doi}\n </p>\n ) : null}\n </div>\n <StatusBadge status={release.status} />\n </button>\n </li>\n ))}\n </ul>\n )}\n </section>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/dashboard/DashboardPage.tsx"],"sourcesContent":["import type { LucideIcon } from 'lucide-react';\nimport {\n Boxes,\n Database,\n FileText,\n NotebookPen,\n Plus,\n Radar,\n Rocket,\n Workflow,\n} from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosDatasetsQuery,\n useCosNotebooksQuery,\n useCosPipelinesQuery,\n useCosReleasesQuery,\n useCosRunsQuery,\n useCosSourcesQuery,\n type CosConnection,\n} from '../../api/cosmicApi';\nimport { EmptyState, PageLayout, StatusBadge } from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { useTr } from '../../../shared/hooks/useTr';\n\nconst TILE_LIMIT = 50;\n\nfunction formatDateTime(raw: string | null | undefined): string {\n if (!raw) {\n return '—';\n }\n const date = new Date(raw);\n return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();\n}\n\n/** \"13\" or \"50+\" — the honest count of the page we fetched, never a fabricated total. */\nfunction connectionCount<T>(connection: CosConnection<T> | undefined): string {\n if (!connection) {\n return '—';\n }\n const loaded = connection.items.length;\n return connection.pageInfo?.hasMore ? `${loaded}+` : String(loaded);\n}\n\ninterface SummaryTileProps {\n label: string;\n count: string;\n icon: LucideIcon;\n loading: boolean;\n onClick: () => void;\n}\n\nfunction SummaryTile({ label, count, icon: Icon, loading, onClick }: SummaryTileProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"group flex flex-col gap-2 rounded-card border border-border-default bg-bg-elevated p-4 text-left transition-colors hover:border-border-strong hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium text-text-secondary\">{label}</span>\n <span className=\"flex size-8 items-center justify-center rounded-button border border-border-default bg-bg-sunken text-text-secondary transition-colors group-hover:border-border-strong\">\n <Icon className=\"size-4\" aria-hidden=\"true\" />\n </span>\n </div>\n <span className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {loading ? '…' : count}\n </span>\n </button>\n );\n}\n\nexport default function DashboardPage() {\n const { navigate } = useCosmicIntersection();\n const tr = useTr();\n\n const datasetsQuery = useCosDatasetsQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const sourcesQuery = useCosSourcesQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const pipelinesQuery = useCosPipelinesQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const runsQuery = useCosRunsQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const notebooksQuery = useCosNotebooksQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const releasesQuery = useCosReleasesQuery({ pagination: { limit: TILE_LIMIT, offset: 0 } });\n const publishedQuery = useCosReleasesQuery({\n status: 'PUBLISHED',\n pagination: { limit: 5, offset: 0 },\n });\n\n const recentRuns = (runsQuery.data?.cosListRuns?.items ?? []).slice(0, 6);\n const publishedReleases = publishedQuery.data?.cosListReleases?.items ?? [];\n\n return (\n <PageLayout\n title={tr('cosmicintersection.pages.dashboard.title', 'CosmicIntersection')}\n description={tr(\n 'cosmicintersection.pages.dashboard.description',\n 'A federation workspace for science data — sources, datasets, pipelines, notebooks and releases in one place.'\n )}\n actions={\n <Button type=\"button\" onClick={() => navigate('datasets/create')}>\n <Plus className=\"size-4\" />\n {tr('cosmicintersection.pages.dashboard.newDataset', 'New dataset')}\n </Button>\n }\n >\n <div className=\"space-y-6\">\n {/* Summary tiles */}\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6\">\n <SummaryTile\n label={tr('nav.datasets', 'Datasets')}\n icon={Database}\n loading={datasetsQuery.loading}\n count={connectionCount(datasetsQuery.data?.cosListDatasets)}\n onClick={() => navigate('datasets')}\n />\n <SummaryTile\n label={tr('nav.sources', 'Sources')}\n icon={Radar}\n loading={sourcesQuery.loading}\n count={connectionCount(sourcesQuery.data?.cosListSources)}\n onClick={() => navigate('sources')}\n />\n <SummaryTile\n label={tr('nav.pipelines', 'Pipelines')}\n icon={Workflow}\n loading={pipelinesQuery.loading}\n count={connectionCount(pipelinesQuery.data?.cosListPipelines)}\n onClick={() => navigate('pipelines')}\n />\n <SummaryTile\n label={tr('nav.runs', 'Runs')}\n icon={Boxes}\n loading={runsQuery.loading}\n count={connectionCount(runsQuery.data?.cosListRuns)}\n onClick={() => navigate('runs')}\n />\n <SummaryTile\n label={tr('nav.notebooks', 'Notebooks')}\n icon={NotebookPen}\n loading={notebooksQuery.loading}\n count={connectionCount(notebooksQuery.data?.cosListNotebooks)}\n onClick={() => navigate('notebooks')}\n />\n <SummaryTile\n label={tr('nav.releases', 'Releases')}\n icon={FileText}\n loading={releasesQuery.loading}\n count={connectionCount(releasesQuery.data?.cosListReleases)}\n onClick={() => navigate('releases')}\n />\n </div>\n\n {/* Quick actions */}\n <div className=\"rounded-card border border-border-default bg-bg-elevated p-5\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n {tr('cosmicintersection.pages.dashboard.quickActions', 'Quick actions')}\n </h2>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('datasets/create')}>\n <Database className=\"size-4\" />\n {tr('cosmicintersection.pages.dashboard.registerDataset', 'Register dataset')}\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('sources/create')}>\n <Radar className=\"size-4\" />\n {tr('cosmicintersection.pages.dashboard.connectSource', 'Connect source')}\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('pipelines/create')}>\n <Workflow className=\"size-4\" />\n {tr('cosmicintersection.pages.dashboard.createPipeline', 'Create pipeline')}\n </Button>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Recent runs */}\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('cosmicintersection.pages.dashboard.recentRuns', 'Recent runs')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigate('runs')}\n className=\"text-sm text-text-secondary underline-offset-2 hover:text-text-primary hover:underline\"\n >\n {tr('cosmicintersection.pages.dashboard.viewAll', 'View all')}\n </button>\n </div>\n {runsQuery.loading ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"h-14 animate-pulse rounded-card bg-bg-sunken\" />\n ))}\n </div>\n ) : recentRuns.length === 0 ? (\n <EmptyState\n icon={Boxes}\n title={tr('cosmicintersection.pages.dashboard.noRunsYet', 'No pipeline runs yet.')}\n description={tr(\n 'cosmicintersection.pages.dashboard.noRunsDescription',\n 'Start a pipeline run to see execution history here.'\n )}\n />\n ) : (\n <ul className=\"space-y-2\">\n {recentRuns.map((run) => (\n <li key={run.id}>\n <button\n type=\"button\"\n onClick={() => navigate(`runs/${run.id}`)}\n className=\"flex w-full items-center justify-between gap-3 rounded-card border border-border-default bg-bg-elevated px-4 py-3 text-left transition-colors hover:border-border-strong hover:bg-bg-sunken\"\n >\n <div className=\"min-w-0\">\n <p className=\"font-medium text-text-primary\">\n {tr('cosmicintersection.pages.dashboard.runNumber', 'Run #{{number}}', {\n number: run.runNumber,\n })}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {run.startedAt\n ? tr(\n 'cosmicintersection.pages.dashboard.startedAt',\n 'Started {{date}}',\n { date: formatDateTime(run.startedAt) }\n )\n : tr('cosmicintersection.pages.dashboard.runNotStarted', 'Not started')}\n </p>\n </div>\n <StatusBadge status={run.status} />\n </button>\n </li>\n ))}\n </ul>\n )}\n </section>\n\n {/* Recently published releases */}\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('cosmicintersection.pages.dashboard.recentlyPublished', 'Recently published')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigate('releases')}\n className=\"text-sm text-text-secondary underline-offset-2 hover:text-text-primary hover:underline\"\n >\n {tr('cosmicintersection.pages.dashboard.viewAll', 'View all')}\n </button>\n </div>\n {publishedQuery.loading ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"h-14 animate-pulse rounded-card bg-bg-sunken\" />\n ))}\n </div>\n ) : publishedReleases.length === 0 ? (\n <EmptyState\n icon={Rocket}\n title={tr(\n 'cosmicintersection.pages.dashboard.noPublishedReleases',\n 'No published releases yet.'\n )}\n description={tr(\n 'cosmicintersection.pages.dashboard.noPublishedReleasesDescription',\n 'Publish a release to share datasets and notebooks with a citable version.'\n )}\n />\n ) : (\n <ul className=\"space-y-2\">\n {publishedReleases.map((release) => (\n <li key={release.id}>\n <button\n type=\"button\"\n onClick={() => navigate('releases')}\n className=\"flex w-full items-center justify-between gap-3 rounded-card border border-border-default bg-bg-elevated px-4 py-3 text-left transition-colors hover:border-border-strong hover:bg-bg-sunken\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{release.title}</p>\n <p className=\"text-xs text-text-secondary\">\n v{release.version}\n {release.publishedAt\n ? ` · ${tr(\n 'cosmicintersection.pages.dashboard.publishedAt',\n 'Published {{date}}',\n { date: formatDateTime(release.publishedAt) }\n )}`\n : ''}\n </p>\n {release.doi ? (\n <p\n className=\"mt-0.5 truncate font-mono text-xs text-text-tertiary\"\n title={release.doi}\n >\n DOI: {release.doi}\n </p>\n ) : null}\n </div>\n <StatusBadge status={release.status} />\n </button>\n </li>\n ))}\n </ul>\n )}\n </section>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export default function DatasetDetailPage(): import("react
|
|
1
|
+
export default function DatasetDetailPage(): import("react").JSX.Element;
|
|
2
2
|
//# sourceMappingURL=DatasetDetailPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatasetDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/datasets/DatasetDetailPage.tsx"],"names":[],"mappings":"AAiJA,MAAM,CAAC,OAAO,UAAU,iBAAiB,
|
|
1
|
+
{"version":3,"file":"DatasetDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/datasets/DatasetDetailPage.tsx"],"names":[],"mappings":"AAiJA,MAAM,CAAC,OAAO,UAAU,iBAAiB,gCAwmBxC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export default function DatasetFormPage(): import("react
|
|
1
|
+
export default function DatasetFormPage(): import("react").JSX.Element;
|
|
2
2
|
//# sourceMappingURL=DatasetFormPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatasetFormPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/datasets/DatasetFormPage.tsx"],"names":[],"mappings":"AA2DA,MAAM,CAAC,OAAO,UAAU,eAAe,
|
|
1
|
+
{"version":3,"file":"DatasetFormPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/datasets/DatasetFormPage.tsx"],"names":[],"mappings":"AA2DA,MAAM,CAAC,OAAO,UAAU,eAAe,gCA+MtC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export default function DatasetsPage(): import("react
|
|
1
|
+
export default function DatasetsPage(): import("react").JSX.Element;
|
|
2
2
|
//# sourceMappingURL=DatasetsPage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatasetsPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/datasets/DatasetsPage.tsx"],"names":[],"mappings":"AAqFA,MAAM,CAAC,OAAO,UAAU,YAAY,
|
|
1
|
+
{"version":3,"file":"DatasetsPage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/datasets/DatasetsPage.tsx"],"names":[],"mappings":"AAqFA,MAAM,CAAC,OAAO,UAAU,YAAY,gCA6fnC"}
|