@burdenoff/microfe-cosmicintersection 2026.704.1 → 2026.704.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/cosmicintersection/CosmicIntersectionRoutes.js +45 -25
  2. package/dist/cosmicintersection/CosmicIntersectionRoutes.js.map +1 -1
  3. package/dist/cosmicintersection/api/cosmicApi.js +137 -66
  4. package/dist/cosmicintersection/api/cosmicApi.js.map +1 -1
  5. package/dist/cosmicintersection/components/ActivityFeedPanel.js +68 -0
  6. package/dist/cosmicintersection/components/ActivityFeedPanel.js.map +1 -0
  7. package/dist/cosmicintersection/components/DiscussionPanel.js +57 -0
  8. package/dist/cosmicintersection/components/DiscussionPanel.js.map +1 -0
  9. package/dist/cosmicintersection/components/FacetPanel.js +66 -0
  10. package/dist/cosmicintersection/components/FacetPanel.js.map +1 -0
  11. package/dist/cosmicintersection/components/LineageGraph.js +388 -0
  12. package/dist/cosmicintersection/components/LineageGraph.js.map +1 -0
  13. package/dist/cosmicintersection/components/ResultsChart.js +329 -0
  14. package/dist/cosmicintersection/components/ResultsChart.js.map +1 -0
  15. package/dist/cosmicintersection/components/StepDAG.js +104 -0
  16. package/dist/cosmicintersection/components/StepDAG.js.map +1 -0
  17. package/dist/cosmicintersection/components/index.js +5 -3
  18. package/dist/cosmicintersection/pages/access/AccessPage.js +220 -161
  19. package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -1
  20. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js +332 -172
  21. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -1
  22. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js +167 -109
  23. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js.map +1 -1
  24. package/dist/cosmicintersection/pages/collections/CollectionFormPage.js +87 -61
  25. package/dist/cosmicintersection/pages/collections/CollectionFormPage.js.map +1 -1
  26. package/dist/cosmicintersection/pages/collections/CollectionsPage.js +51 -42
  27. package/dist/cosmicintersection/pages/collections/CollectionsPage.js.map +1 -1
  28. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js +31 -18
  29. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -1
  30. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js +330 -184
  31. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js.map +1 -1
  32. package/dist/cosmicintersection/pages/datasets/DatasetFormPage.js +7 -7
  33. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js +321 -163
  34. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -1
  35. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js +358 -0
  36. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js.map +1 -0
  37. package/dist/cosmicintersection/pages/lineage/LineagePage.js +171 -0
  38. package/dist/cosmicintersection/pages/lineage/LineagePage.js.map +1 -0
  39. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js +339 -260
  40. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js.map +1 -1
  41. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js +143 -82
  42. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -1
  43. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js +286 -142
  44. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js.map +1 -1
  45. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js +305 -103
  46. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js.map +1 -1
  47. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js +183 -79
  48. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -1
  49. package/dist/cosmicintersection/pages/releases/ReleasesPage.js +346 -204
  50. package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -1
  51. package/dist/cosmicintersection/pages/runs/RunDetailPage.js +184 -136
  52. package/dist/cosmicintersection/pages/runs/RunDetailPage.js.map +1 -1
  53. package/dist/cosmicintersection/pages/runs/RunsPage.js +182 -120
  54. package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -1
  55. package/dist/cosmicintersection/pages/sources/SourceDetailPage.js +234 -0
  56. package/dist/cosmicintersection/pages/sources/SourceDetailPage.js.map +1 -0
  57. package/dist/cosmicintersection/pages/sources/SourcesPage.js +250 -133
  58. package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -1
  59. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"RunDetailPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/runs/RunDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ListTree, Plus } from 'lucide-react';\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosAddRunStepMutation,\n useCosRunQuery,\n useCosRunStepsQuery,\n useCosTransitionRunMutation,\n type CosRunStatus,\n type CosStepKind,\n} from '../../api/cosmicApi';\nimport {\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n SafeLink,\n StatusBadge,\n formatEnumLabel,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\nconst STEP_KIND_OPTIONS: CosStepKind[] = [\n 'INGEST',\n 'CALIBRATE',\n 'TRANSFORM',\n 'ANALYZE',\n 'EXPORT',\n 'NOTIFY',\n];\n\n// Valid forward transitions for the run state machine. Terminal states have none.\nconst NEXT_STATUSES: Record<CosRunStatus, CosRunStatus[]> = {\n QUEUED: ['RUNNING', 'CANCELLED'],\n RUNNING: ['SUCCEEDED', 'FAILED', 'CANCELLED'],\n SUCCEEDED: [],\n FAILED: [],\n CANCELLED: [],\n};\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();\n}\n\nfunction formatDuration(ms?: number | null): string {\n if (ms === null || ms === undefined) {\n return '—';\n }\n if (ms < 1000) {\n return `${ms} ms`;\n }\n const seconds = ms / 1000;\n if (seconds < 60) {\n return `${seconds.toFixed(1)} s`;\n }\n const minutes = Math.floor(seconds / 60);\n const rest = Math.round(seconds % 60);\n return `${minutes}m ${rest}s`;\n}\n\nfunction DetailRow({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"flex flex-col gap-1 border-b border-border-seam py-3 last:border-0 sm:flex-row sm:items-start sm:gap-4\">\n <span className=\"w-40 shrink-0 text-sm font-medium text-text-secondary\">{label}</span>\n <span className=\"min-w-0 text-sm text-text-primary\">{children}</span>\n </div>\n );\n}\n\nfunction JsonBlock({ value }: { value?: Record<string, unknown> | null }) {\n if (!value || Object.keys(value).length === 0) {\n return <span className=\"text-sm text-text-secondary\">—</span>;\n }\n return (\n <pre className=\"overflow-x-auto rounded-card border border-border-default bg-bg-sunken p-3 font-mono text-xs text-text-primary\">\n {JSON.stringify(value, null, 2)}\n </pre>\n );\n}\n\nexport default function RunDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useCosmicIntersection();\n\n const { data, loading, error, refetch } = useCosRunQuery(id);\n const {\n data: stepsData,\n loading: stepsLoading,\n error: stepsError,\n refetch: refetchSteps,\n } = useCosRunStepsQuery(id);\n\n const [transitionRun, { loading: transitioning }] = useCosTransitionRunMutation();\n const [addStep, { loading: addingStep }] = useCosAddRunStepMutation();\n\n const [stepDialogOpen, setStepDialogOpen] = useState(false);\n const [stepName, setStepName] = useState('');\n const [stepKind, setStepKind] = useState<CosStepKind>('INGEST');\n const [stepPosition, setStepPosition] = useState('');\n const [stepInputs, setStepInputs] = useState('');\n\n const run = data?.cosRun;\n const steps = useMemo(\n () => [...(stepsData?.cosListRunSteps ?? [])].sort((a, b) => a.position - b.position),\n [stepsData]\n );\n\n const handleTransition = async (nextStatus: CosRunStatus) => {\n if (!run) {\n return;\n }\n const response = await transitionRun({ variables: { id: run.id, status: nextStatus } });\n if (\n !mutationSucceeded(response, response.data?.cosTransitionRun?.id, 'Could not update the run')\n ) {\n return;\n }\n notify.success('Run updated', `Run #${run.runNumber} moved to ${formatEnumLabel(nextStatus)}.`);\n await Promise.all([refetch(), refetchSteps()]);\n };\n\n const resetStepForm = () => {\n setStepName('');\n setStepKind('INGEST');\n setStepPosition('');\n setStepInputs('');\n };\n\n const handleAddStep = async () => {\n if (!run) {\n return;\n }\n if (!stepName.trim()) {\n notify.error('Step name is required', 'Give the step a name before adding it.');\n return;\n }\n\n let inputs: Record<string, unknown> | undefined;\n if (stepInputs.trim()) {\n try {\n const parsed = JSON.parse(stepInputs.trim()) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error('Inputs must be a JSON object.');\n }\n inputs = parsed as Record<string, unknown>;\n } catch (parseError) {\n notify.error(\n 'Invalid inputs JSON',\n describeError(parseError) ?? 'Please provide valid JSON.'\n );\n return;\n }\n }\n\n const positionValue = stepPosition.trim() ? Number(stepPosition.trim()) : undefined;\n if (positionValue !== undefined && (Number.isNaN(positionValue) || positionValue < 0)) {\n notify.error('Invalid position', 'Position must be a non-negative number.');\n return;\n }\n\n const response = await addStep({\n variables: {\n input: {\n runId: run.id,\n name: stepName.trim(),\n kind: stepKind,\n inputs,\n position: positionValue,\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.cosAddRunStep?.id, 'Could not add the step')) {\n return;\n }\n notify.success('Step added', `“${stepName.trim()}” was added to the run.`);\n setStepDialogOpen(false);\n resetStepForm();\n await refetchSteps();\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Run\" backPath=\"runs\">\n <LoadingState label=\"Loading run…\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout title=\"Run\" backPath=\"runs\">\n <ErrorState onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!run) {\n return (\n <PageLayout title=\"Run\" backPath=\"runs\">\n <EmptyState\n title=\"Run not found\"\n description=\"This run may have been removed or you may not have access to it.\"\n action={\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('runs')}>\n Back to runs\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const validTransitions = NEXT_STATUSES[run.status] ?? [];\n\n return (\n <PageLayout\n title={`Run #${run.runNumber}`}\n description={run.pipeline ? `Pipeline: ${run.pipeline.name}` : undefined}\n backPath=\"runs\"\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n {validTransitions.map((next) => (\n <Button\n key={next}\n type=\"button\"\n variant={next === 'CANCELLED' || next === 'FAILED' ? 'outline' : 'default'}\n onClick={() => void handleTransition(next)}\n disabled={transitioning}\n >\n {`Mark ${formatEnumLabel(next)}`}\n </Button>\n ))}\n <Button type=\"button\" variant=\"outline\" onClick={() => setStepDialogOpen(true)}>\n <Plus className=\"size-4\" />\n Add step\n </Button>\n </div>\n }\n >\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Overview</CardTitle>\n </CardHeader>\n <CardContent>\n <DetailRow label=\"Status\">\n <StatusBadge status={run.status} />\n </DetailRow>\n <DetailRow label=\"Pipeline\">\n {run.pipeline ? (\n <button\n type=\"button\"\n className=\"text-text-primary underline-offset-2 hover:underline\"\n onClick={() => navigate(`pipelines/${run.pipelineId}`)}\n >\n {run.pipeline.name}\n </button>\n ) : (\n <span className=\"font-mono text-xs\">{run.pipelineId}</span>\n )}\n </DetailRow>\n <DetailRow label=\"Dataset\">\n {run.datasetId ? (\n <button\n type=\"button\"\n className=\"text-text-primary underline-offset-2 hover:underline\"\n onClick={() => navigate(`datasets/${run.datasetId}`)}\n >\n <span className=\"font-mono text-xs\">{run.datasetId}</span>\n </button>\n ) : (\n '—'\n )}\n </DetailRow>\n <DetailRow label=\"Triggered by\">{run.triggeredBy || '—'}</DetailRow>\n <DetailRow label=\"Started\">{formatDate(run.startedAt)}</DetailRow>\n <DetailRow label=\"Finished\">{formatDate(run.finishedAt)}</DetailRow>\n <DetailRow label=\"Duration\">{formatDuration(run.durationMs)}</DetailRow>\n {run.errorMessage ? (\n <DetailRow label=\"Error\">\n <span className=\"text-status-error-text\">{run.errorMessage}</span>\n </DetailRow>\n ) : null}\n {validTransitions.length === 0 ? (\n <DetailRow label=\"Discuss\">\n <SafeLink\n href=\"/conversations\"\n className=\"text-text-primary underline-offset-2 hover:underline\"\n >\n Open discussions\n </SafeLink>\n </DetailRow>\n ) : null}\n </CardContent>\n </Card>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Parameters</CardTitle>\n </CardHeader>\n <CardContent>\n <JsonBlock value={run.params} />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Metrics</CardTitle>\n </CardHeader>\n <CardContent>\n <JsonBlock value={run.metrics} />\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Steps</CardTitle>\n </CardHeader>\n <CardContent>\n {stepsLoading ? (\n <LoadingState variant=\"rows\" rows={4} label=\"Loading steps…\" />\n ) : stepsError ? (\n <ErrorState message=\"Could not load steps.\" onRetry={() => void refetchSteps()} />\n ) : steps.length === 0 ? (\n <EmptyState\n icon={ListTree}\n title=\"No steps yet\"\n description=\"Add steps to record the ingest, calibration, transform, analyze or export stages of this run.\"\n action={\n <Button type=\"button\" onClick={() => setStepDialogOpen(true)}>\n <Plus className=\"size-4\" />\n Add step\n </Button>\n }\n />\n ) : (\n <ol className=\"space-y-3\">\n {steps.map((step) => (\n <li\n key={step.id}\n className=\"flex flex-col gap-2 rounded-card border border-border-default bg-bg-elevated p-4 sm:flex-row sm:items-start sm:justify-between\"\n >\n <div className=\"flex min-w-0 items-start gap-3\">\n <span className=\"mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-semibold tabular-nums text-text-secondary\">\n {step.position}\n </span>\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"font-medium text-text-primary\">{step.name}</span>\n <StatusBadge status={step.kind} tone=\"neutral\" />\n </div>\n {step.errorMessage ? (\n <p className=\"mt-1 text-xs text-status-error-text\">{step.errorMessage}</p>\n ) : null}\n <p className=\"mt-1 text-xs text-text-secondary\">\n Duration: {formatDuration(step.durationMs)}\n </p>\n </div>\n </div>\n <div className=\"shrink-0\">\n <StatusBadge status={step.status} />\n </div>\n </li>\n ))}\n </ol>\n )}\n </CardContent>\n </Card>\n </div>\n\n <Dialog\n open={stepDialogOpen}\n onOpenChange={(next) => {\n if (addingStep) {\n return;\n }\n setStepDialogOpen(next);\n if (!next) {\n resetStepForm();\n }\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Add step</DialogTitle>\n <DialogDescription>\n Record a stage of this run — its kind, order and optional inputs.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"step-name\">Name</Label>\n <Input\n id=\"step-name\"\n value={stepName}\n onChange={(event) => setStepName(event.target.value)}\n placeholder=\"e.g. Calibrate flat fields\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"step-kind\">Kind</Label>\n <Select\n value={stepKind}\n onValueChange={(value) => setStepKind(value as CosStepKind)}\n >\n <SelectTrigger id=\"step-kind\" aria-label=\"Step kind\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {STEP_KIND_OPTIONS.map((option) => (\n <SelectItem key={option} value={option}>\n {formatEnumLabel(option)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"step-position\">Position</Label>\n <Input\n id=\"step-position\"\n type=\"number\"\n min=\"0\"\n value={stepPosition}\n onChange={(event) => setStepPosition(event.target.value)}\n placeholder=\"Auto\"\n />\n </div>\n </div>\n\n <div className=\"space-y-1\">\n <Label htmlFor=\"step-inputs\">Inputs (JSON)</Label>\n <Textarea\n id=\"step-inputs\"\n value={stepInputs}\n onChange={(event) => setStepInputs(event.target.value)}\n rows={4}\n spellCheck={false}\n className=\"font-mono text-xs\"\n placeholder={'{\\n \"source\": \"raw-frames\"\\n}'}\n />\n <p className=\"text-xs text-text-secondary\">Optional. A JSON object of step inputs.</p>\n </div>\n </div>\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setStepDialogOpen(false);\n resetStepForm();\n }}\n disabled={addingStep}\n >\n Cancel\n </Button>\n <Button type=\"button\" onClick={() => void handleAddStep()} disabled={addingStep}>\n <Plus className=\"size-4\" />\n {addingStep ? 'Adding…' : 'Add step'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RunDetailPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/runs/RunDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport type { LucideIcon } from 'lucide-react';\nimport { Check, Circle, ListTree, Loader2, Plus, X } from 'lucide-react';\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Label,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosAddRunStepMutation,\n useCosRunQuery,\n useCosRunStepsQuery,\n useCosTransitionRunMutation,\n type CosRunStatus,\n type CosStepKind,\n} from '../../api/cosmicApi';\nimport {\n ActivityFeedPanel,\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n SafeLink,\n StatusBadge,\n formatEnumLabel,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\nconst STEP_KIND_OPTIONS: CosStepKind[] = [\n 'INGEST',\n 'CALIBRATE',\n 'TRANSFORM',\n 'ANALYZE',\n 'EXPORT',\n 'NOTIFY',\n];\n\n// Valid forward transitions for the run state machine. Terminal states have none.\nconst NEXT_STATUSES: Record<CosRunStatus, CosRunStatus[]> = {\n QUEUED: ['RUNNING', 'CANCELLED'],\n RUNNING: ['SUCCEEDED', 'FAILED', 'CANCELLED'],\n SUCCEEDED: [],\n FAILED: [],\n CANCELLED: [],\n};\n\n// Per-status timeline node: the icon rendered inside the spine circle + its\n// tone classes. Colour comes only from the semantic status token families.\nconst STEP_NODE: Record<CosRunStatus, { icon: LucideIcon; spin: boolean; wrap: string }> = {\n SUCCEEDED: {\n icon: Check,\n spin: false,\n wrap: 'border-status-success-border bg-status-success-bg-subtle text-status-success-text',\n },\n RUNNING: {\n icon: Loader2,\n spin: true,\n wrap: 'border-status-warning-border bg-status-warning-bg-subtle text-status-warning-text',\n },\n FAILED: {\n icon: X,\n spin: false,\n wrap: 'border-status-error-border bg-status-error-bg-subtle text-status-error-text',\n },\n QUEUED: {\n icon: Circle,\n spin: false,\n wrap: 'border-status-info-border bg-status-info-bg-subtle text-status-info-text',\n },\n CANCELLED: {\n icon: X,\n spin: false,\n wrap: 'border-border-default bg-bg-sunken text-text-secondary',\n },\n};\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();\n}\n\nfunction formatDuration(ms?: number | null): string {\n if (ms === null || ms === undefined) {\n return '—';\n }\n if (ms < 1000) {\n return `${ms} ms`;\n }\n const seconds = ms / 1000;\n if (seconds < 60) {\n return `${seconds.toFixed(1)} s`;\n }\n const minutes = Math.floor(seconds / 60);\n const rest = Math.round(seconds % 60);\n return `${minutes}m ${rest}s`;\n}\n\nfunction DetailRow({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"flex flex-col gap-1 border-b border-border-seam py-3 last:border-0 sm:flex-row sm:items-start sm:gap-4\">\n <span className=\"w-40 shrink-0 text-sm font-medium text-text-secondary\">{label}</span>\n <span className=\"min-w-0 text-sm text-text-primary\">{children}</span>\n </div>\n );\n}\n\nfunction JsonBlock({ value }: { value?: Record<string, unknown> | null }) {\n if (!value || Object.keys(value).length === 0) {\n return <span className=\"text-sm text-text-secondary\">—</span>;\n }\n return (\n <pre className=\"overflow-x-auto rounded-card border border-border-default bg-bg-sunken p-3 font-mono text-xs text-text-primary\">\n {JSON.stringify(value, null, 2)}\n </pre>\n );\n}\n\nexport default function RunDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useCosmicIntersection();\n\n const { data, loading, error, refetch } = useCosRunQuery(id);\n const {\n data: stepsData,\n loading: stepsLoading,\n error: stepsError,\n refetch: refetchSteps,\n } = useCosRunStepsQuery(id);\n\n const [transitionRun, { loading: transitioning }] = useCosTransitionRunMutation();\n const [addStep, { loading: addingStep }] = useCosAddRunStepMutation();\n\n const [stepDialogOpen, setStepDialogOpen] = useState(false);\n const [stepName, setStepName] = useState('');\n const [stepKind, setStepKind] = useState<CosStepKind>('INGEST');\n const [stepPosition, setStepPosition] = useState('');\n const [stepInputs, setStepInputs] = useState('');\n\n const run = data?.cosRun;\n const steps = useMemo(\n () => [...(stepsData?.cosListRunSteps ?? [])].sort((a, b) => a.position - b.position),\n [stepsData]\n );\n\n const handleTransition = async (nextStatus: CosRunStatus) => {\n if (!run) {\n return;\n }\n const response = await transitionRun({ variables: { id: run.id, status: nextStatus } });\n if (\n !mutationSucceeded(response, response.data?.cosTransitionRun?.id, 'Could not update the run')\n ) {\n return;\n }\n notify.success('Run updated', `Run #${run.runNumber} moved to ${formatEnumLabel(nextStatus)}.`);\n await Promise.all([refetch(), refetchSteps()]);\n };\n\n const resetStepForm = () => {\n setStepName('');\n setStepKind('INGEST');\n setStepPosition('');\n setStepInputs('');\n };\n\n const handleAddStep = async () => {\n if (!run) {\n return;\n }\n if (!stepName.trim()) {\n notify.error('Step name is required', 'Give the step a name before adding it.');\n return;\n }\n\n let inputs: Record<string, unknown> | undefined;\n if (stepInputs.trim()) {\n try {\n const parsed = JSON.parse(stepInputs.trim()) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error('Inputs must be a JSON object.');\n }\n inputs = parsed as Record<string, unknown>;\n } catch (parseError) {\n notify.error(\n 'Invalid inputs JSON',\n describeError(parseError) ?? 'Please provide valid JSON.'\n );\n return;\n }\n }\n\n const positionValue = stepPosition.trim() ? Number(stepPosition.trim()) : undefined;\n if (positionValue !== undefined && (Number.isNaN(positionValue) || positionValue < 0)) {\n notify.error('Invalid position', 'Position must be a non-negative number.');\n return;\n }\n\n const response = await addStep({\n variables: {\n input: {\n runId: run.id,\n name: stepName.trim(),\n kind: stepKind,\n inputs,\n position: positionValue,\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.cosAddRunStep?.id, 'Could not add the step')) {\n return;\n }\n notify.success('Step added', `“${stepName.trim()}” was added to the run.`);\n setStepDialogOpen(false);\n resetStepForm();\n await refetchSteps();\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Run\" backPath=\"runs\">\n <LoadingState label=\"Loading run…\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout title=\"Run\" backPath=\"runs\">\n <ErrorState onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!run) {\n return (\n <PageLayout title=\"Run\" backPath=\"runs\">\n <EmptyState\n title=\"Run not found\"\n description=\"This run may have been removed or you may not have access to it.\"\n action={\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('runs')}>\n Back to runs\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const validTransitions = NEXT_STATUSES[run.status] ?? [];\n\n return (\n <PageLayout\n title={`Run #${run.runNumber}`}\n description={run.pipeline ? `Pipeline: ${run.pipeline.name}` : undefined}\n backPath=\"runs\"\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n {validTransitions.map((next) => (\n <Button\n key={next}\n type=\"button\"\n variant={next === 'CANCELLED' || next === 'FAILED' ? 'outline' : 'default'}\n onClick={() => void handleTransition(next)}\n disabled={transitioning}\n >\n {`Mark ${formatEnumLabel(next)}`}\n </Button>\n ))}\n <Button type=\"button\" variant=\"outline\" onClick={() => setStepDialogOpen(true)}>\n <Plus className=\"size-4\" />\n Add step\n </Button>\n </div>\n }\n >\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Overview</CardTitle>\n </CardHeader>\n <CardContent>\n <DetailRow label=\"Status\">\n <StatusBadge status={run.status} />\n </DetailRow>\n <DetailRow label=\"Pipeline\">\n {run.pipeline ? (\n <button\n type=\"button\"\n className=\"text-text-primary underline-offset-2 hover:underline\"\n onClick={() => navigate(`pipelines/${run.pipelineId}`)}\n >\n {run.pipeline.name}\n </button>\n ) : (\n <span className=\"font-mono text-xs\">{run.pipelineId}</span>\n )}\n </DetailRow>\n <DetailRow label=\"Dataset\">\n {run.datasetId ? (\n <button\n type=\"button\"\n className=\"text-text-primary underline-offset-2 hover:underline\"\n onClick={() => navigate(`datasets/${run.datasetId}`)}\n >\n <span className=\"font-mono text-xs\">{run.datasetId}</span>\n </button>\n ) : (\n '—'\n )}\n </DetailRow>\n <DetailRow label=\"Triggered by\">{run.triggeredBy || '—'}</DetailRow>\n <DetailRow label=\"Started\">{formatDate(run.startedAt)}</DetailRow>\n <DetailRow label=\"Finished\">{formatDate(run.finishedAt)}</DetailRow>\n <DetailRow label=\"Duration\">{formatDuration(run.durationMs)}</DetailRow>\n {run.errorMessage ? (\n <DetailRow label=\"Error\">\n <span className=\"text-status-error-text\">{run.errorMessage}</span>\n </DetailRow>\n ) : null}\n {validTransitions.length === 0 ? (\n <DetailRow label=\"Discuss\">\n <SafeLink\n href=\"/conversations\"\n className=\"text-text-primary underline-offset-2 hover:underline\"\n >\n Open discussions\n </SafeLink>\n </DetailRow>\n ) : null}\n </CardContent>\n </Card>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle>Parameters</CardTitle>\n </CardHeader>\n <CardContent>\n <JsonBlock value={run.params} />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Metrics</CardTitle>\n </CardHeader>\n <CardContent>\n <JsonBlock value={run.metrics} />\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Steps</CardTitle>\n </CardHeader>\n <CardContent>\n {stepsLoading ? (\n <LoadingState variant=\"rows\" rows={4} label=\"Loading steps…\" />\n ) : stepsError ? (\n <ErrorState message=\"Could not load steps.\" onRetry={() => void refetchSteps()} />\n ) : steps.length === 0 ? (\n <EmptyState\n icon={ListTree}\n title=\"No steps yet\"\n description=\"Add steps to record the ingest, calibration, transform, analyze or export stages of this run.\"\n action={\n <Button type=\"button\" onClick={() => setStepDialogOpen(true)}>\n <Plus className=\"size-4\" />\n Add step\n </Button>\n }\n />\n ) : (\n <ol className=\"flex flex-col\">\n {steps.map((step, index) => {\n const node = STEP_NODE[step.status] ?? STEP_NODE.QUEUED;\n const NodeIcon = node.icon;\n const isLast = index === steps.length - 1;\n return (\n <li key={step.id} className=\"flex gap-4\">\n {/* Spine: status node + connecting line to the next step. */}\n <div className=\"flex flex-col items-center\">\n <span\n className={`flex size-8 shrink-0 items-center justify-center rounded-full border ${node.wrap}`}\n >\n <NodeIcon\n className={`size-4 ${node.spin ? 'animate-spin' : ''}`}\n aria-hidden=\"true\"\n />\n </span>\n {!isLast ? (\n <span className=\"w-px flex-1 bg-border-default\" aria-hidden=\"true\" />\n ) : null}\n </div>\n\n <div\n className={`min-w-0 flex-1 rounded-card border border-border-default bg-bg-elevated p-4 ${\n isLast ? '' : 'mb-4'\n }`}\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-xs font-semibold tabular-nums text-text-secondary\">\n {index + 1}.\n </span>\n <span className=\"min-w-0 truncate font-medium text-text-primary\">\n {step.name}\n </span>\n <StatusBadge status={step.kind} tone=\"neutral\" />\n <StatusBadge status={step.status} className=\"ml-auto\" />\n </div>\n {step.errorMessage ? (\n <p className=\"mt-1.5 text-xs text-status-error-text\">\n {step.errorMessage}\n </p>\n ) : null}\n <p className=\"mt-1.5 text-xs text-text-secondary\">\n Duration: {formatDuration(step.durationMs)}\n </p>\n </div>\n </li>\n );\n })}\n </ol>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Activity</CardTitle>\n </CardHeader>\n <CardContent>\n <ActivityFeedPanel resourceId={run.id} resourceType=\"cosmicintersection-run\" />\n </CardContent>\n </Card>\n </div>\n\n <Dialog\n open={stepDialogOpen}\n onOpenChange={(next) => {\n if (addingStep) {\n return;\n }\n setStepDialogOpen(next);\n if (!next) {\n resetStepForm();\n }\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Add step</DialogTitle>\n <DialogDescription>\n Record a stage of this run — its kind, order and optional inputs.\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"step-name\">Name</Label>\n <Input\n id=\"step-name\"\n value={stepName}\n onChange={(event) => setStepName(event.target.value)}\n placeholder=\"e.g. Calibrate flat fields\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"step-kind\">Kind</Label>\n <Select\n value={stepKind}\n onValueChange={(value) => setStepKind(value as CosStepKind)}\n >\n <SelectTrigger id=\"step-kind\" aria-label=\"Step kind\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {STEP_KIND_OPTIONS.map((option) => (\n <SelectItem key={option} value={option}>\n {formatEnumLabel(option)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-1\">\n <Label htmlFor=\"step-position\">Position</Label>\n <Input\n id=\"step-position\"\n type=\"number\"\n min=\"0\"\n value={stepPosition}\n onChange={(event) => setStepPosition(event.target.value)}\n placeholder=\"Auto\"\n />\n </div>\n </div>\n\n <div className=\"space-y-1\">\n <Label htmlFor=\"step-inputs\">Inputs (JSON)</Label>\n <Textarea\n id=\"step-inputs\"\n value={stepInputs}\n onChange={(event) => setStepInputs(event.target.value)}\n rows={4}\n spellCheck={false}\n className=\"font-mono text-xs\"\n placeholder={'{\\n \"source\": \"raw-frames\"\\n}'}\n />\n <p className=\"text-xs text-text-secondary\">Optional. A JSON object of step inputs.</p>\n </div>\n </div>\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => {\n setStepDialogOpen(false);\n resetStepForm();\n }}\n disabled={addingStep}\n >\n Cancel\n </Button>\n <Button type=\"button\" onClick={() => void handleAddStep()} disabled={addingStep}>\n <Plus className=\"size-4\" />\n {addingStep ? 'Adding…' : 'Add step'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,180 +1,242 @@
1
1
  import { useCosmicIntersection as e } from "../../context/CosmicIntersectionContext.js";
2
2
  import { useCosPipelinesQuery as t, useCosRunsQuery as n } from "../../api/cosmicApi.js";
3
- import r from "../../components/ActionMenu.js";
4
- import i from "../../components/EmptyState.js";
5
- import a from "../../components/ErrorState.js";
6
- import o from "../../components/LoadingState.js";
7
- import s from "../../components/PageLayout.js";
8
- import c, { usePagination as l } from "../../components/Pagination.js";
9
- import u, { formatEnumLabel as d } from "../../components/StatusBadge.js";
3
+ import r from "../../components/EmptyState.js";
4
+ import i from "../../components/ErrorState.js";
5
+ import a from "../../components/LoadingState.js";
6
+ import o from "../../components/PageLayout.js";
7
+ import s, { usePagination as c } from "../../components/Pagination.js";
8
+ import l, { formatEnumLabel as u } from "../../components/StatusBadge.js";
10
9
  import "../../components/index.js";
11
- import { useMemo as f, useState as p } from "react";
12
- import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
13
- import { Activity as _, Eye as v } from "lucide-react";
14
- import { Button as y, Select as b, SelectContent as x, SelectItem as S, SelectTrigger as C, SelectValue as w, Table as T, TableBody as E, TableCell as D, TableHead as O, TableHeader as k, TableRow as A } from "@burdenoff/fe-libs/ui";
10
+ import { useMemo as d, useState as f } from "react";
11
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
12
+ import { Activity as g, Eye as _ } from "lucide-react";
13
+ import { Button as v, Select as y, SelectContent as b, SelectItem as x, SelectTrigger as S, SelectValue as C, Table as w, TableBody as T, TableCell as E, TableHead as D, TableHeader as O, TableRow as k } from "@burdenoff/fe-libs/ui";
15
14
  //#region src/cosmicintersection/pages/runs/RunsPage.tsx
16
- var j = [
15
+ var A = [
17
16
  "QUEUED",
18
17
  "RUNNING",
19
18
  "SUCCEEDED",
20
19
  "FAILED",
21
20
  "CANCELLED"
22
- ], M = "ALL";
23
- function N(e) {
21
+ ], j = "ALL";
22
+ function M(e) {
24
23
  if (!e) return "—";
25
24
  let t = new Date(e);
26
25
  return Number.isNaN(t.getTime()) ? "—" : t.toLocaleString();
27
26
  }
28
- function P(e) {
27
+ function N(e) {
29
28
  if (e == null) return "—";
30
29
  if (e < 1e3) return `${e} ms`;
31
30
  let t = e / 1e3;
32
31
  return t < 60 ? `${t.toFixed(1)} s` : `${Math.floor(t / 60)}m ${Math.round(t % 60)}s`;
33
32
  }
34
- function F() {
35
- let { navigate: F } = e(), [I, L] = p(M), [R, z] = p(M), B = l({ resetKey: `${I}:${R}` }), { data: V } = t({ pagination: {
33
+ function P() {
34
+ let { navigate: P } = e(), [F, I] = f(j), [L, R] = f(j), z = c({ resetKey: `${F}:${L}` }), { data: B } = t({ pagination: {
36
35
  limit: 100,
37
36
  offset: 0
38
- } }), H = f(() => V?.cosListPipelines.items ?? [], [V]), U = f(() => {
37
+ } }), V = d(() => B?.cosListPipelines.items ?? [], [B]), H = d(() => {
39
38
  let e = /* @__PURE__ */ new Map();
40
- return H.forEach((t) => e.set(t.id, t.name)), e;
41
- }, [H]), { data: W, loading: G, error: K, refetch: q } = n({
42
- status: I === M ? void 0 : I,
43
- pipelineId: R === M ? void 0 : R,
39
+ return V.forEach((t) => e.set(t.id, t.name)), e;
40
+ }, [V]), { data: U, loading: W, error: G, refetch: K } = n({
41
+ status: F === j ? void 0 : F,
42
+ pipelineId: L === j ? void 0 : L,
44
43
  pagination: {
45
- limit: B.limit,
46
- offset: B.offset
44
+ limit: z.limit,
45
+ offset: z.offset
47
46
  }
48
- }), J = f(() => W?.cosListRuns.items ?? [], [W]), Y = W?.cosListRuns.pageInfo;
49
- return /* @__PURE__ */ h(s, {
47
+ }), q = d(() => U?.cosListRuns.items ?? [], [U]), J = U?.cosListRuns.pageInfo;
48
+ return /* @__PURE__ */ m(o, {
50
49
  title: "Runs",
51
50
  description: "Every pipeline execution, with status, timings and outcomes.",
52
- actions: /* @__PURE__ */ g(y, {
51
+ actions: /* @__PURE__ */ h(v, {
53
52
  type: "button",
54
53
  variant: "outline",
55
- onClick: () => F("pipelines"),
56
- children: [/* @__PURE__ */ h(v, { className: "size-4" }), "View pipelines"]
54
+ onClick: () => P("pipelines"),
55
+ children: [/* @__PURE__ */ m(_, { className: "size-4" }), "View pipelines"]
57
56
  }),
58
- children: /* @__PURE__ */ g("div", {
57
+ children: /* @__PURE__ */ h("div", {
59
58
  className: "space-y-4",
60
- children: [/* @__PURE__ */ g("div", {
59
+ children: [/* @__PURE__ */ h("div", {
61
60
  className: "flex flex-wrap items-center gap-3",
62
- children: [/* @__PURE__ */ g("label", {
61
+ children: [/* @__PURE__ */ h("label", {
63
62
  className: "flex items-center gap-2 text-sm text-text-secondary",
64
- children: [/* @__PURE__ */ h("span", { children: "Status" }), /* @__PURE__ */ g(b, {
65
- value: I,
66
- onValueChange: (e) => L(e),
67
- children: [/* @__PURE__ */ h(C, {
63
+ children: [/* @__PURE__ */ m("span", { children: "Status" }), /* @__PURE__ */ h(y, {
64
+ value: F,
65
+ onValueChange: (e) => I(e),
66
+ children: [/* @__PURE__ */ m(S, {
68
67
  "aria-label": "Filter by status",
69
68
  className: "w-40",
70
- children: /* @__PURE__ */ h(w, {})
71
- }), /* @__PURE__ */ g(x, { children: [/* @__PURE__ */ h(S, {
72
- value: M,
69
+ children: /* @__PURE__ */ m(C, {})
70
+ }), /* @__PURE__ */ h(b, { children: [/* @__PURE__ */ m(x, {
71
+ value: j,
73
72
  children: "All statuses"
74
- }), j.map((e) => /* @__PURE__ */ h(S, {
73
+ }), A.map((e) => /* @__PURE__ */ m(x, {
75
74
  value: e,
76
- children: d(e)
75
+ children: u(e)
77
76
  }, e))] })]
78
77
  })]
79
- }), /* @__PURE__ */ g("label", {
78
+ }), /* @__PURE__ */ h("label", {
80
79
  className: "flex items-center gap-2 text-sm text-text-secondary",
81
- children: [/* @__PURE__ */ h("span", { children: "Pipeline" }), /* @__PURE__ */ g(b, {
82
- value: R,
83
- onValueChange: (e) => z(e),
84
- children: [/* @__PURE__ */ h(C, {
80
+ children: [/* @__PURE__ */ m("span", { children: "Pipeline" }), /* @__PURE__ */ h(y, {
81
+ value: L,
82
+ onValueChange: (e) => R(e),
83
+ children: [/* @__PURE__ */ m(S, {
85
84
  "aria-label": "Filter by pipeline",
86
85
  className: "w-56",
87
- children: /* @__PURE__ */ h(w, {})
88
- }), /* @__PURE__ */ g(x, { children: [/* @__PURE__ */ h(S, {
89
- value: M,
86
+ children: /* @__PURE__ */ m(C, {})
87
+ }), /* @__PURE__ */ h(b, { children: [/* @__PURE__ */ m(x, {
88
+ value: j,
90
89
  children: "All pipelines"
91
- }), H.map((e) => /* @__PURE__ */ h(S, {
90
+ }), V.map((e) => /* @__PURE__ */ m(x, {
92
91
  value: e.id,
93
92
  children: e.name
94
93
  }, e.id))] })]
95
94
  })]
96
95
  })]
97
- }), G ? /* @__PURE__ */ h(o, {
96
+ }), W ? /* @__PURE__ */ m(a, {
98
97
  variant: "rows",
99
98
  rows: 6,
100
99
  label: "Loading runs…"
101
- }) : K ? /* @__PURE__ */ h(a, {
100
+ }) : G ? /* @__PURE__ */ m(i, {
102
101
  message: "We could not load pipeline runs. Please try again.",
103
- onRetry: () => void q()
104
- }) : J.length === 0 ? /* @__PURE__ */ h(i, {
105
- icon: _,
106
- title: I === M && R === M ? "No runs yet" : "No runs match these filters",
107
- description: I === M && R === M ? "Runs appear here once you start a pipeline. Each run tracks its steps, timings and metrics." : "Try a different status or pipeline filter."
108
- }) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h("div", {
109
- className: "overflow-x-auto rounded-card border border-border-default bg-bg-elevated",
110
- children: /* @__PURE__ */ g(T, { children: [/* @__PURE__ */ h(k, { children: /* @__PURE__ */ g(A, { children: [
111
- /* @__PURE__ */ h(O, { children: "Run" }),
112
- /* @__PURE__ */ h(O, { children: "Pipeline" }),
113
- /* @__PURE__ */ h(O, { children: "Status" }),
114
- /* @__PURE__ */ h(O, { children: "Started" }),
115
- /* @__PURE__ */ h(O, { children: "Finished" }),
116
- /* @__PURE__ */ h(O, { children: "Duration" }),
117
- /* @__PURE__ */ h(O, {
118
- className: "w-12 text-right",
119
- children: /* @__PURE__ */ h("span", {
120
- className: "sr-only",
121
- children: "Actions"
122
- })
123
- })
124
- ] }) }), /* @__PURE__ */ h(E, { children: J.map((e) => /* @__PURE__ */ g(A, {
125
- className: "cursor-pointer",
126
- onClick: () => F(`runs/${e.id}`),
127
- children: [
128
- /* @__PURE__ */ g(D, {
129
- className: "font-medium text-text-primary",
130
- children: ["#", e.runNumber]
131
- }),
132
- /* @__PURE__ */ h(D, {
133
- className: "text-text-secondary",
134
- children: /* @__PURE__ */ h("span", {
135
- className: "truncate",
136
- children: U.get(e.pipelineId) ?? /* @__PURE__ */ h("span", {
102
+ onRetry: () => void K()
103
+ }) : q.length === 0 ? /* @__PURE__ */ m(r, {
104
+ icon: g,
105
+ title: F === j && L === j ? "No runs yet" : "No runs match these filters",
106
+ description: F === j && L === j ? "Runs appear here once you start a pipeline. Each run tracks its steps, timings and metrics." : "Try a different status or pipeline filter."
107
+ }) : /* @__PURE__ */ h(p, { children: [
108
+ /* @__PURE__ */ m("div", {
109
+ className: "hidden rounded-card border border-border-default bg-bg-elevated md:block",
110
+ children: /* @__PURE__ */ h(w, { children: [/* @__PURE__ */ m(O, { children: /* @__PURE__ */ h(k, { children: [
111
+ /* @__PURE__ */ m(D, { children: "Run" }),
112
+ /* @__PURE__ */ m(D, { children: "Pipeline" }),
113
+ /* @__PURE__ */ m(D, { children: "Status" }),
114
+ /* @__PURE__ */ m(D, { children: "Started" }),
115
+ /* @__PURE__ */ m(D, { children: "Finished" }),
116
+ /* @__PURE__ */ m(D, { children: "Duration" }),
117
+ /* @__PURE__ */ m(D, { children: "Triggered by" })
118
+ ] }) }), /* @__PURE__ */ m(T, { children: q.map((e) => /* @__PURE__ */ h(k, {
119
+ className: "cursor-pointer",
120
+ onClick: () => P(`runs/${e.id}`),
121
+ children: [
122
+ /* @__PURE__ */ h(E, {
123
+ className: "font-medium text-text-primary",
124
+ children: ["#", e.runNumber]
125
+ }),
126
+ /* @__PURE__ */ m(E, {
127
+ className: "text-text-secondary",
128
+ children: /* @__PURE__ */ m("span", {
129
+ className: "truncate",
130
+ children: H.get(e.pipelineId) ?? /* @__PURE__ */ m("span", {
131
+ className: "font-mono text-xs",
132
+ children: e.pipelineId
133
+ })
134
+ })
135
+ }),
136
+ /* @__PURE__ */ m(E, { children: /* @__PURE__ */ m(l, { status: e.status }) }),
137
+ /* @__PURE__ */ m(E, {
138
+ className: "whitespace-nowrap text-text-secondary",
139
+ children: M(e.startedAt)
140
+ }),
141
+ /* @__PURE__ */ m(E, {
142
+ className: "whitespace-nowrap text-text-secondary",
143
+ children: M(e.finishedAt)
144
+ }),
145
+ /* @__PURE__ */ m(E, {
146
+ className: "tabular-nums text-text-secondary",
147
+ children: N(e.durationMs)
148
+ }),
149
+ /* @__PURE__ */ m(E, {
150
+ className: "text-text-secondary",
151
+ children: e.triggeredBy || "—"
152
+ })
153
+ ]
154
+ }, e.id)) })] })
155
+ }),
156
+ /* @__PURE__ */ m("ul", {
157
+ className: "space-y-3 md:hidden",
158
+ children: q.map((e) => /* @__PURE__ */ m("li", { children: /* @__PURE__ */ h("button", {
159
+ type: "button",
160
+ onClick: () => P(`runs/${e.id}`),
161
+ className: "flex w-full flex-col gap-2 rounded-card border border-border-default bg-bg-elevated p-4 text-left transition-colors hover:border-border-strong",
162
+ children: [
163
+ /* @__PURE__ */ h("div", {
164
+ className: "flex items-center justify-between gap-2",
165
+ children: [/* @__PURE__ */ h("span", {
166
+ className: "font-medium text-text-primary",
167
+ children: ["#", e.runNumber]
168
+ }), /* @__PURE__ */ m(l, { status: e.status })]
169
+ }),
170
+ /* @__PURE__ */ m("p", {
171
+ className: "truncate text-sm text-text-secondary",
172
+ children: H.get(e.pipelineId) ?? /* @__PURE__ */ m("span", {
137
173
  className: "font-mono text-xs",
138
174
  children: e.pipelineId
139
175
  })
176
+ }),
177
+ /* @__PURE__ */ h("dl", {
178
+ className: "grid grid-cols-2 gap-x-3 gap-y-1 text-xs",
179
+ children: [
180
+ /* @__PURE__ */ h("div", {
181
+ className: "flex flex-col",
182
+ children: [/* @__PURE__ */ m("dt", {
183
+ className: "text-text-secondary",
184
+ children: "Started"
185
+ }), /* @__PURE__ */ m("dd", {
186
+ className: "text-text-primary",
187
+ children: M(e.startedAt)
188
+ })]
189
+ }),
190
+ /* @__PURE__ */ h("div", {
191
+ className: "flex flex-col",
192
+ children: [/* @__PURE__ */ m("dt", {
193
+ className: "text-text-secondary",
194
+ children: "Duration"
195
+ }), /* @__PURE__ */ m("dd", {
196
+ className: "tabular-nums text-text-primary",
197
+ children: N(e.durationMs)
198
+ })]
199
+ }),
200
+ /* @__PURE__ */ h("div", {
201
+ className: "flex flex-col",
202
+ children: [/* @__PURE__ */ m("dt", {
203
+ className: "text-text-secondary",
204
+ children: "Finished"
205
+ }), /* @__PURE__ */ m("dd", {
206
+ className: "text-text-primary",
207
+ children: M(e.finishedAt)
208
+ })]
209
+ }),
210
+ /* @__PURE__ */ h("div", {
211
+ className: "flex flex-col",
212
+ children: [/* @__PURE__ */ m("dt", {
213
+ className: "text-text-secondary",
214
+ children: "Triggered by"
215
+ }), /* @__PURE__ */ m("dd", {
216
+ className: "text-text-primary",
217
+ children: e.triggeredBy || "—"
218
+ })]
219
+ })
220
+ ]
140
221
  })
141
- }),
142
- /* @__PURE__ */ h(D, { children: /* @__PURE__ */ h(u, { status: e.status }) }),
143
- /* @__PURE__ */ h(D, {
144
- className: "whitespace-nowrap text-text-secondary",
145
- children: N(e.startedAt)
146
- }),
147
- /* @__PURE__ */ h(D, {
148
- className: "whitespace-nowrap text-text-secondary",
149
- children: N(e.finishedAt)
150
- }),
151
- /* @__PURE__ */ h(D, {
152
- className: "tabular-nums text-text-secondary",
153
- children: P(e.durationMs)
154
- }),
155
- /* @__PURE__ */ h(D, {
156
- className: "text-right",
157
- children: /* @__PURE__ */ h(r, {
158
- ariaLabel: `Actions for run #${e.runNumber}`,
159
- onView: () => F(`runs/${e.id}`)
160
- })
161
- })
162
- ]
163
- }, e.id)) })] })
164
- }), /* @__PURE__ */ h(c, {
165
- offset: B.offset,
166
- limit: B.limit,
167
- loadedCount: J.length,
168
- hasMore: Y?.hasMore ?? !1,
169
- onPrev: B.prevPage,
170
- onNext: B.nextPage,
171
- itemLabel: "runs",
172
- loading: G
173
- })] })]
222
+ ]
223
+ }) }, e.id))
224
+ }),
225
+ /* @__PURE__ */ m(s, {
226
+ offset: z.offset,
227
+ limit: z.limit,
228
+ loadedCount: q.length,
229
+ hasMore: J?.hasMore ?? !1,
230
+ onPrev: z.prevPage,
231
+ onNext: z.nextPage,
232
+ itemLabel: "runs",
233
+ loading: W
234
+ })
235
+ ] })]
174
236
  })
175
237
  });
176
238
  }
177
239
  //#endregion
178
- export { F as default };
240
+ export { P as default };
179
241
 
180
242
  //# sourceMappingURL=RunsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RunsPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/runs/RunsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { Activity, Eye } from 'lucide-react';\nimport {\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport { useCosPipelinesQuery, useCosRunsQuery, type CosRunStatus } from '../../api/cosmicApi';\nimport {\n ActionMenu,\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n Pagination,\n StatusBadge,\n formatEnumLabel,\n usePagination,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\n\nconst STATUS_OPTIONS: CosRunStatus[] = ['QUEUED', 'RUNNING', 'SUCCEEDED', 'FAILED', 'CANCELLED'];\nconst ALL = 'ALL';\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();\n}\n\nfunction formatDuration(ms?: number | null): string {\n if (ms === null || ms === undefined) {\n return '—';\n }\n if (ms < 1000) {\n return `${ms} ms`;\n }\n const seconds = ms / 1000;\n if (seconds < 60) {\n return `${seconds.toFixed(1)} s`;\n }\n const minutes = Math.floor(seconds / 60);\n const rest = Math.round(seconds % 60);\n return `${minutes}m ${rest}s`;\n}\n\nexport default function RunsPage() {\n const { navigate } = useCosmicIntersection();\n const [status, setStatus] = useState<CosRunStatus | typeof ALL>(ALL);\n const [pipelineId, setPipelineId] = useState<string | typeof ALL>(ALL);\n const pagination = usePagination({ resetKey: `${status}:${pipelineId}` });\n\n // Load pipelines to populate the filter and to resolve run -> pipeline names.\n const { data: pipelinesData } = useCosPipelinesQuery({ pagination: { limit: 100, offset: 0 } });\n const pipelines = useMemo(() => pipelinesData?.cosListPipelines.items ?? [], [pipelinesData]);\n const pipelineNameById = useMemo(() => {\n const map = new Map<string, string>();\n pipelines.forEach((pipeline) => map.set(pipeline.id, pipeline.name));\n return map;\n }, [pipelines]);\n\n const { data, loading, error, refetch } = useCosRunsQuery({\n status: status === ALL ? undefined : status,\n pipelineId: pipelineId === ALL ? undefined : pipelineId,\n pagination: { limit: pagination.limit, offset: pagination.offset },\n });\n\n const runs = useMemo(() => data?.cosListRuns.items ?? [], [data]);\n const pageInfo = data?.cosListRuns.pageInfo;\n\n const renderBody = () => {\n if (loading) {\n return <LoadingState variant=\"rows\" rows={6} label=\"Loading runs…\" />;\n }\n if (error) {\n return (\n <ErrorState\n message=\"We could not load pipeline runs. Please try again.\"\n onRetry={() => void refetch()}\n />\n );\n }\n if (runs.length === 0) {\n return (\n <EmptyState\n icon={Activity}\n title={\n status === ALL && pipelineId === ALL ? 'No runs yet' : 'No runs match these filters'\n }\n description={\n status === ALL && pipelineId === ALL\n ? 'Runs appear here once you start a pipeline. Each run tracks its steps, timings and metrics.'\n : 'Try a different status or pipeline filter.'\n }\n />\n );\n }\n\n return (\n <>\n <div className=\"overflow-x-auto rounded-card border border-border-default bg-bg-elevated\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Run</TableHead>\n <TableHead>Pipeline</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Started</TableHead>\n <TableHead>Finished</TableHead>\n <TableHead>Duration</TableHead>\n <TableHead className=\"w-12 text-right\">\n <span className=\"sr-only\">Actions</span>\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {runs.map((run) => (\n <TableRow\n key={run.id}\n className=\"cursor-pointer\"\n onClick={() => navigate(`runs/${run.id}`)}\n >\n <TableCell className=\"font-medium text-text-primary\">#{run.runNumber}</TableCell>\n <TableCell className=\"text-text-secondary\">\n <span className=\"truncate\">\n {pipelineNameById.get(run.pipelineId) ?? (\n <span className=\"font-mono text-xs\">{run.pipelineId}</span>\n )}\n </span>\n </TableCell>\n <TableCell>\n <StatusBadge status={run.status} />\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDate(run.startedAt)}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDate(run.finishedAt)}\n </TableCell>\n <TableCell className=\"tabular-nums text-text-secondary\">\n {formatDuration(run.durationMs)}\n </TableCell>\n <TableCell className=\"text-right\">\n <ActionMenu\n ariaLabel={`Actions for run #${run.runNumber}`}\n onView={() => navigate(`runs/${run.id}`)}\n />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={runs.length}\n hasMore={pageInfo?.hasMore ?? false}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"runs\"\n loading={loading}\n />\n </>\n );\n };\n\n return (\n <PageLayout\n title=\"Runs\"\n description=\"Every pipeline execution, with status, timings and outcomes.\"\n actions={\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('pipelines')}>\n <Eye className=\"size-4\" />\n View pipelines\n </Button>\n }\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span>Status</span>\n <Select\n value={status}\n onValueChange={(value) => setStatus(value as CosRunStatus | typeof ALL)}\n >\n <SelectTrigger aria-label=\"Filter by status\" className=\"w-40\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All statuses</SelectItem>\n {STATUS_OPTIONS.map((option) => (\n <SelectItem key={option} value={option}>\n {formatEnumLabel(option)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span>Pipeline</span>\n <Select\n value={pipelineId}\n onValueChange={(value) => setPipelineId(value as string | typeof ALL)}\n >\n <SelectTrigger aria-label=\"Filter by pipeline\" className=\"w-56\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All pipelines</SelectItem>\n {pipelines.map((pipeline) => (\n <SelectItem key={pipeline.id} value={pipeline.id}>\n {pipeline.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n {renderBody()}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RunsPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/runs/RunsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { Activity, Eye } from 'lucide-react';\nimport {\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport { useCosPipelinesQuery, useCosRunsQuery, type CosRunStatus } from '../../api/cosmicApi';\nimport {\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n Pagination,\n StatusBadge,\n formatEnumLabel,\n usePagination,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\n\nconst STATUS_OPTIONS: CosRunStatus[] = ['QUEUED', 'RUNNING', 'SUCCEEDED', 'FAILED', 'CANCELLED'];\nconst ALL = 'ALL';\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();\n}\n\nfunction formatDuration(ms?: number | null): string {\n if (ms === null || ms === undefined) {\n return '—';\n }\n if (ms < 1000) {\n return `${ms} ms`;\n }\n const seconds = ms / 1000;\n if (seconds < 60) {\n return `${seconds.toFixed(1)} s`;\n }\n const minutes = Math.floor(seconds / 60);\n const rest = Math.round(seconds % 60);\n return `${minutes}m ${rest}s`;\n}\n\nexport default function RunsPage() {\n const { navigate } = useCosmicIntersection();\n const [status, setStatus] = useState<CosRunStatus | typeof ALL>(ALL);\n const [pipelineId, setPipelineId] = useState<string | typeof ALL>(ALL);\n const pagination = usePagination({ resetKey: `${status}:${pipelineId}` });\n\n // Load pipelines to populate the filter and to resolve run -> pipeline names.\n const { data: pipelinesData } = useCosPipelinesQuery({ pagination: { limit: 100, offset: 0 } });\n const pipelines = useMemo(() => pipelinesData?.cosListPipelines.items ?? [], [pipelinesData]);\n const pipelineNameById = useMemo(() => {\n const map = new Map<string, string>();\n pipelines.forEach((pipeline) => map.set(pipeline.id, pipeline.name));\n return map;\n }, [pipelines]);\n\n const { data, loading, error, refetch } = useCosRunsQuery({\n status: status === ALL ? undefined : status,\n pipelineId: pipelineId === ALL ? undefined : pipelineId,\n pagination: { limit: pagination.limit, offset: pagination.offset },\n });\n\n const runs = useMemo(() => data?.cosListRuns.items ?? [], [data]);\n const pageInfo = data?.cosListRuns.pageInfo;\n\n const renderBody = () => {\n if (loading) {\n return <LoadingState variant=\"rows\" rows={6} label=\"Loading runs…\" />;\n }\n if (error) {\n return (\n <ErrorState\n message=\"We could not load pipeline runs. Please try again.\"\n onRetry={() => void refetch()}\n />\n );\n }\n if (runs.length === 0) {\n return (\n <EmptyState\n icon={Activity}\n title={\n status === ALL && pipelineId === ALL ? 'No runs yet' : 'No runs match these filters'\n }\n description={\n status === ALL && pipelineId === ALL\n ? 'Runs appear here once you start a pipeline. Each run tracks its steps, timings and metrics.'\n : 'Try a different status or pipeline filter.'\n }\n />\n );\n }\n\n return (\n <>\n {/* Desktop: table. */}\n <div className=\"hidden rounded-card border border-border-default bg-bg-elevated md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Run</TableHead>\n <TableHead>Pipeline</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Started</TableHead>\n <TableHead>Finished</TableHead>\n <TableHead>Duration</TableHead>\n <TableHead>Triggered by</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {runs.map((run) => (\n <TableRow\n key={run.id}\n className=\"cursor-pointer\"\n onClick={() => navigate(`runs/${run.id}`)}\n >\n <TableCell className=\"font-medium text-text-primary\">#{run.runNumber}</TableCell>\n <TableCell className=\"text-text-secondary\">\n <span className=\"truncate\">\n {pipelineNameById.get(run.pipelineId) ?? (\n <span className=\"font-mono text-xs\">{run.pipelineId}</span>\n )}\n </span>\n </TableCell>\n <TableCell>\n <StatusBadge status={run.status} />\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDate(run.startedAt)}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-text-secondary\">\n {formatDate(run.finishedAt)}\n </TableCell>\n <TableCell className=\"tabular-nums text-text-secondary\">\n {formatDuration(run.durationMs)}\n </TableCell>\n <TableCell className=\"text-text-secondary\">{run.triggeredBy || '—'}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n\n {/* Mobile: stacked cards. */}\n <ul className=\"space-y-3 md:hidden\">\n {runs.map((run) => (\n <li key={run.id}>\n <button\n type=\"button\"\n onClick={() => navigate(`runs/${run.id}`)}\n className=\"flex w-full flex-col gap-2 rounded-card border border-border-default bg-bg-elevated p-4 text-left transition-colors hover:border-border-strong\"\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"font-medium text-text-primary\">#{run.runNumber}</span>\n <StatusBadge status={run.status} />\n </div>\n <p className=\"truncate text-sm text-text-secondary\">\n {pipelineNameById.get(run.pipelineId) ?? (\n <span className=\"font-mono text-xs\">{run.pipelineId}</span>\n )}\n </p>\n <dl className=\"grid grid-cols-2 gap-x-3 gap-y-1 text-xs\">\n <div className=\"flex flex-col\">\n <dt className=\"text-text-secondary\">Started</dt>\n <dd className=\"text-text-primary\">{formatDate(run.startedAt)}</dd>\n </div>\n <div className=\"flex flex-col\">\n <dt className=\"text-text-secondary\">Duration</dt>\n <dd className=\"tabular-nums text-text-primary\">\n {formatDuration(run.durationMs)}\n </dd>\n </div>\n <div className=\"flex flex-col\">\n <dt className=\"text-text-secondary\">Finished</dt>\n <dd className=\"text-text-primary\">{formatDate(run.finishedAt)}</dd>\n </div>\n <div className=\"flex flex-col\">\n <dt className=\"text-text-secondary\">Triggered by</dt>\n <dd className=\"text-text-primary\">{run.triggeredBy || '—'}</dd>\n </div>\n </dl>\n </button>\n </li>\n ))}\n </ul>\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={runs.length}\n hasMore={pageInfo?.hasMore ?? false}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"runs\"\n loading={loading}\n />\n </>\n );\n };\n\n return (\n <PageLayout\n title=\"Runs\"\n description=\"Every pipeline execution, with status, timings and outcomes.\"\n actions={\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('pipelines')}>\n <Eye className=\"size-4\" />\n View pipelines\n </Button>\n }\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span>Status</span>\n <Select\n value={status}\n onValueChange={(value) => setStatus(value as CosRunStatus | typeof ALL)}\n >\n <SelectTrigger aria-label=\"Filter by status\" className=\"w-40\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All statuses</SelectItem>\n {STATUS_OPTIONS.map((option) => (\n <SelectItem key={option} value={option}>\n {formatEnumLabel(option)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n\n <label className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span>Pipeline</span>\n <Select\n value={pipelineId}\n onValueChange={(value) => setPipelineId(value as string | typeof ALL)}\n >\n <SelectTrigger aria-label=\"Filter by pipeline\" className=\"w-56\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value={ALL}>All pipelines</SelectItem>\n {pipelines.map((pipeline) => (\n <SelectItem key={pipeline.id} value={pipeline.id}>\n {pipeline.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n {renderBody()}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}