@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.
- package/dist/cosmicintersection/CosmicIntersectionRoutes.js +45 -25
- package/dist/cosmicintersection/CosmicIntersectionRoutes.js.map +1 -1
- package/dist/cosmicintersection/api/cosmicApi.js +137 -66
- package/dist/cosmicintersection/api/cosmicApi.js.map +1 -1
- package/dist/cosmicintersection/components/ActivityFeedPanel.js +68 -0
- package/dist/cosmicintersection/components/ActivityFeedPanel.js.map +1 -0
- package/dist/cosmicintersection/components/DiscussionPanel.js +57 -0
- package/dist/cosmicintersection/components/DiscussionPanel.js.map +1 -0
- package/dist/cosmicintersection/components/FacetPanel.js +66 -0
- package/dist/cosmicintersection/components/FacetPanel.js.map +1 -0
- package/dist/cosmicintersection/components/LineageGraph.js +388 -0
- package/dist/cosmicintersection/components/LineageGraph.js.map +1 -0
- package/dist/cosmicintersection/components/ResultsChart.js +329 -0
- package/dist/cosmicintersection/components/ResultsChart.js.map +1 -0
- package/dist/cosmicintersection/components/StepDAG.js +104 -0
- package/dist/cosmicintersection/components/StepDAG.js.map +1 -0
- package/dist/cosmicintersection/components/index.js +5 -3
- package/dist/cosmicintersection/pages/access/AccessPage.js +220 -161
- package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -1
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.js +332 -172
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js +167 -109
- package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionFormPage.js +87 -61
- package/dist/cosmicintersection/pages/collections/CollectionFormPage.js.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionsPage.js +51 -42
- package/dist/cosmicintersection/pages/collections/CollectionsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.js +31 -18
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js +330 -184
- package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetFormPage.js +7 -7
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.js +321 -163
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js +358 -0
- package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js.map +1 -0
- package/dist/cosmicintersection/pages/lineage/LineagePage.js +171 -0
- package/dist/cosmicintersection/pages/lineage/LineagePage.js.map +1 -0
- package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js +339 -260
- package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js +143 -82
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js +286 -142
- package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js +305 -103
- package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js +183 -79
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/releases/ReleasesPage.js +346 -204
- package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/runs/RunDetailPage.js +184 -136
- package/dist/cosmicintersection/pages/runs/RunDetailPage.js.map +1 -1
- package/dist/cosmicintersection/pages/runs/RunsPage.js +182 -120
- package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourceDetailPage.js +234 -0
- package/dist/cosmicintersection/pages/sources/SourceDetailPage.js.map +1 -0
- package/dist/cosmicintersection/pages/sources/SourcesPage.js +250 -133
- package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PipelineDetailPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/pipelines/PipelineDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Archive, Pencil, Play } from 'lucide-react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n Button,\n buttonVariants,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosArchivePipelineMutation,\n useCosPipelineQuery,\n useCosRunsQuery,\n useCosStartRunMutation,\n} 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';\nimport { mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\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\nexport default function PipelineDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useCosmicIntersection();\n\n const { data, loading, error, refetch } = useCosPipelineQuery(id);\n const runsPagination = usePagination({ pageSize: 10, resetKey: id });\n const {\n data: runsData,\n loading: runsLoading,\n error: runsError,\n refetch: refetchRuns,\n } = useCosRunsQuery({\n pipelineId: id,\n pagination: { limit: runsPagination.limit, offset: runsPagination.offset },\n });\n\n const [startRun, { loading: starting }] = useCosStartRunMutation();\n const [archivePipeline, { loading: archiving }] = useCosArchivePipelineMutation();\n const [confirmArchive, setConfirmArchive] = useState(false);\n\n const pipeline = data?.cosPipeline;\n const runs = useMemo(() => runsData?.cosListRuns.items ?? [], [runsData]);\n const runsPageInfo = runsData?.cosListRuns.pageInfo;\n\n const handleStartRun = async () => {\n if (!pipeline) {\n return;\n }\n const response = await startRun({ variables: { input: { pipelineId: pipeline.id } } });\n const runId = response.data?.cosStartRun?.id;\n if (!mutationSucceeded(response, runId, 'Could not start a run')) {\n return;\n }\n notify.success('Run started', 'A new run was queued.');\n await Promise.all([refetch(), refetchRuns()]);\n if (runId) {\n navigate(`runs/${runId}`);\n }\n };\n\n const handleArchive = async () => {\n if (!pipeline) {\n return;\n }\n const response = await archivePipeline({ variables: { id: pipeline.id } });\n if (\n !mutationSucceeded(response, response.data?.cosArchivePipeline, 'Could not archive pipeline')\n ) {\n return;\n }\n setConfirmArchive(false);\n notify.success('Pipeline archived', `“${pipeline.name}” was archived.`);\n navigate('pipelines');\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Pipeline\" backPath=\"pipelines\">\n <LoadingState label=\"Loading pipeline…\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout title=\"Pipeline\" backPath=\"pipelines\">\n <ErrorState onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!pipeline) {\n return (\n <PageLayout title=\"Pipeline\" backPath=\"pipelines\">\n <EmptyState\n title=\"Pipeline not found\"\n description=\"This pipeline may have been archived or you may not have access to it.\"\n action={\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('pipelines')}>\n Back to pipelines\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const isArchived = pipeline.status === 'ARCHIVED';\n\n return (\n <PageLayout\n title={pipeline.name}\n description={pipeline.description ?? undefined}\n backPath=\"pipelines\"\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n type=\"button\"\n onClick={() => void handleStartRun()}\n disabled={starting || isArchived}\n >\n <Play className=\"size-4\" />\n {starting ? 'Starting…' : 'Start run'}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => navigate(`pipelines/${pipeline.id}/edit`)}\n >\n <Pencil className=\"size-4\" />\n Edit\n </Button>\n {!isArchived ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => setConfirmArchive(true)}>\n <Archive className=\"size-4\" />\n Archive\n </Button>\n ) : null}\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={pipeline.status} />\n </DetailRow>\n <DetailRow label=\"Trigger\">{formatEnumLabel(pipeline.trigger)}</DetailRow>\n <DetailRow label=\"Schedule\">\n {pipeline.schedule ? (\n <span className=\"font-mono text-xs\">{pipeline.schedule}</span>\n ) : (\n '—'\n )}\n </DetailRow>\n <DetailRow label=\"Version\">v{pipeline.version}</DetailRow>\n <DetailRow label=\"Last run\">{formatDate(pipeline.lastRunAt)}</DetailRow>\n <DetailRow label=\"Created\">{formatDate(pipeline.createdAt)}</DetailRow>\n <DetailRow label=\"Tags\">\n {pipeline.tags.length === 0 ? (\n '—'\n ) : (\n <span className=\"flex flex-wrap gap-1\">\n {pipeline.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </span>\n )}\n </DetailRow>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Definition</CardTitle>\n </CardHeader>\n <CardContent>\n {pipeline.definition && Object.keys(pipeline.definition).length > 0 ? (\n <pre className=\"overflow-x-auto rounded-card border border-border-default bg-bg-sunken p-4 font-mono text-xs text-text-primary\">\n {JSON.stringify(pipeline.definition, null, 2)}\n </pre>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n No definition configured yet. Edit the pipeline to describe its steps.\n </p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Runs</CardTitle>\n </CardHeader>\n <CardContent>\n {runsLoading ? (\n <LoadingState variant=\"rows\" rows={4} label=\"Loading runs…\" />\n ) : runsError ? (\n <ErrorState message=\"Could not load runs.\" onRetry={() => void refetchRuns()} />\n ) : runs.length === 0 ? (\n <EmptyState\n icon={Play}\n title=\"No runs yet\"\n description=\"Start a run to process data through this pipeline.\"\n action={\n <Button type=\"button\" onClick={() => void handleStartRun()} disabled={isArchived}>\n <Play className=\"size-4\" />\n Start run\n </Button>\n }\n />\n ) : (\n <>\n <div className=\"overflow-x-auto rounded-card border border-border-default\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Run</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Started</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\">\n #{run.runNumber}\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=\"tabular-nums text-text-secondary\">\n {formatDuration(run.durationMs)}\n </TableCell>\n <TableCell className=\"text-text-secondary\">\n {run.triggeredBy || '—'}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n <Pagination\n offset={runsPagination.offset}\n limit={runsPagination.limit}\n loadedCount={runs.length}\n hasMore={runsPageInfo?.hasMore ?? false}\n onPrev={runsPagination.prevPage}\n onNext={runsPagination.nextPage}\n itemLabel=\"runs\"\n loading={runsLoading}\n />\n </>\n )}\n </CardContent>\n </Card>\n </div>\n\n <AlertDialog\n open={confirmArchive}\n onOpenChange={(next) => (archiving ? undefined : setConfirmArchive(next))}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Archive “{pipeline.name}”?</AlertDialogTitle>\n <AlertDialogDescription>\n Archiving stops the pipeline from being triggered. Existing runs are kept for\n reference.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={archiving}>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={(event) => {\n event.preventDefault();\n void handleArchive();\n }}\n disabled={archiving}\n className={buttonVariants({ variant: 'destructive' })}\n >\n {archiving ? 'Archiving…' : 'Archive pipeline'}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PipelineDetailPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/pipelines/PipelineDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Archive, Clock, Hash, Pencil, Play } from 'lucide-react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n Button,\n buttonVariants,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosArchivePipelineMutation,\n useCosPipelineQuery,\n useCosRunStepsQuery,\n useCosRunsQuery,\n useCosStartRunMutation,\n type CosStepKind,\n} from '../../api/cosmicApi';\nimport {\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n Pagination,\n StatusBadge,\n formatEnumLabel,\n usePagination,\n} from '../../components';\nimport { StepDAG, type StepDAGStep } from '../../components/StepDAG';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\nconst STEP_KINDS: CosStepKind[] = [\n 'INGEST',\n 'CALIBRATE',\n 'TRANSFORM',\n 'ANALYZE',\n 'EXPORT',\n 'NOTIFY',\n];\n\nfunction isStepKind(value: unknown): value is CosStepKind {\n return typeof value === 'string' && (STEP_KINDS as string[]).includes(value);\n}\n\n/**\n * Parse an ordered step list out of a pipeline definition object. Tolerates\n * unknown/legacy shapes — only well-formed `{ kind, name? }` entries survive.\n */\nfunction parseDefinitionSteps(definition?: Record<string, unknown> | null): StepDAGStep[] {\n if (!definition) {\n return [];\n }\n const raw = (definition as { steps?: unknown }).steps;\n if (!Array.isArray(raw)) {\n return [];\n }\n const out: StepDAGStep[] = [];\n for (const item of raw) {\n if (item && typeof item === 'object' && !Array.isArray(item)) {\n const kind = (item as { kind?: unknown }).kind;\n const name = (item as { name?: unknown }).name;\n if (isStepKind(kind)) {\n out.push({ kind, name: typeof name === 'string' ? name : undefined });\n }\n }\n }\n return out;\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\n/** Small pill used in the header meta row (trigger / cron / version). */\nfunction MetaPill({ icon: Icon, children }: { icon?: typeof Clock; children: React.ReactNode }) {\n return (\n <span className=\"inline-flex items-center gap-1.5 rounded-full border border-border-default bg-bg-elevated px-2.5 py-0.5 text-xs text-text-secondary\">\n {Icon ? <Icon className=\"size-3.5\" aria-hidden=\"true\" /> : null}\n {children}\n </span>\n );\n}\n\nexport default function PipelineDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useCosmicIntersection();\n\n const { data, loading, error, refetch } = useCosPipelineQuery(id);\n const runsPagination = usePagination({ pageSize: 10, resetKey: id });\n const {\n data: runsData,\n loading: runsLoading,\n error: runsError,\n refetch: refetchRuns,\n } = useCosRunsQuery({\n pipelineId: id,\n pagination: { limit: runsPagination.limit, offset: runsPagination.offset },\n });\n\n const pipeline = data?.cosPipeline;\n const runs = useMemo(() => runsData?.cosListRuns.items ?? [], [runsData]);\n const runsPageInfo = runsData?.cosListRuns.pageInfo;\n\n // Prefer the authored definition steps. If the definition has none, fall back\n // to the most recent run's recorded steps so the flow is never empty when the\n // pipeline has actually executed.\n const definitionSteps = useMemo(\n () => parseDefinitionSteps(pipeline?.definition),\n [pipeline?.definition]\n );\n const latestRunId = runs[0]?.id ?? '';\n const shouldDerive = Boolean(pipeline) && definitionSteps.length === 0 && Boolean(latestRunId);\n const { data: latestStepsData } = useCosRunStepsQuery(shouldDerive ? latestRunId : '');\n const derivedSteps = useMemo<StepDAGStep[]>(() => {\n const list = latestStepsData?.cosListRunSteps ?? [];\n return [...list]\n .sort((a, b) => a.position - b.position)\n .map((step) => ({ kind: step.kind, name: step.name, status: step.status }));\n }, [latestStepsData]);\n const dagSteps = definitionSteps.length > 0 ? definitionSteps : derivedSteps;\n const derivedFromRun = definitionSteps.length === 0 && derivedSteps.length > 0;\n\n const [startRun, { loading: starting }] = useCosStartRunMutation();\n const [archivePipeline, { loading: archiving }] = useCosArchivePipelineMutation();\n const [confirmArchive, setConfirmArchive] = useState(false);\n\n const handleStartRun = async () => {\n if (!pipeline) {\n return;\n }\n const response = await startRun({ variables: { input: { pipelineId: pipeline.id } } });\n const runId = response.data?.cosStartRun?.id;\n if (!mutationSucceeded(response, runId, 'Could not start a run')) {\n return;\n }\n notify.success('Run started', 'A new run was queued.');\n await Promise.all([refetch(), refetchRuns()]);\n if (runId) {\n navigate(`runs/${runId}`);\n }\n };\n\n const handleArchive = async () => {\n if (!pipeline) {\n return;\n }\n const response = await archivePipeline({ variables: { id: pipeline.id } });\n if (\n !mutationSucceeded(response, response.data?.cosArchivePipeline, 'Could not archive pipeline')\n ) {\n return;\n }\n setConfirmArchive(false);\n notify.success('Pipeline archived', `“${pipeline.name}” was archived.`);\n navigate('pipelines');\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Pipeline\" backPath=\"pipelines\">\n <LoadingState label=\"Loading pipeline…\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout title=\"Pipeline\" backPath=\"pipelines\">\n <ErrorState onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!pipeline) {\n return (\n <PageLayout title=\"Pipeline\" backPath=\"pipelines\">\n <EmptyState\n title=\"Pipeline not found\"\n description=\"This pipeline may have been archived or you may not have access to it.\"\n action={\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('pipelines')}>\n Back to pipelines\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const isArchived = pipeline.status === 'ARCHIVED';\n const hasDefinition = Boolean(pipeline.definition && Object.keys(pipeline.definition).length > 0);\n\n return (\n <PageLayout\n title={pipeline.name}\n description={pipeline.description ?? undefined}\n backPath=\"pipelines\"\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button\n type=\"button\"\n onClick={() => void handleStartRun()}\n disabled={starting || isArchived}\n >\n <Play className=\"size-4\" />\n {starting ? 'Starting…' : 'Start run'}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => navigate(`pipelines/${pipeline.id}/edit`)}\n >\n <Pencil className=\"size-4\" />\n Edit\n </Button>\n {!isArchived ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => setConfirmArchive(true)}>\n <Archive className=\"size-4\" />\n Archive\n </Button>\n ) : null}\n </div>\n }\n >\n <div className=\"space-y-6\">\n {/* Header meta — status, trigger, cron, version — mirrors the mock's badge row. */}\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={pipeline.status} />\n <MetaPill icon={Clock}>{formatEnumLabel(pipeline.trigger)}</MetaPill>\n {pipeline.schedule ? (\n <MetaPill>\n <span className=\"font-mono\">{pipeline.schedule}</span>\n </MetaPill>\n ) : null}\n <MetaPill icon={Hash}>v{pipeline.version}</MetaPill>\n </div>\n\n {/* Overview — the step-flow DAG (mockup 11). */}\n <Card>\n <CardHeader>\n <CardTitle>Overview</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex flex-wrap items-baseline justify-between gap-2\">\n <span className=\"text-sm font-medium text-text-secondary\">Step flow</span>\n {derivedFromRun ? (\n <span className=\"text-xs text-text-secondary\">Derived from the latest run.</span>\n ) : null}\n </div>\n\n {dagSteps.length > 0 ? (\n <StepDAG steps={dagSteps} />\n ) : (\n <div className=\"rounded-card border border-dashed border-border-default bg-bg-sunken px-4 py-8 text-center\">\n <p className=\"text-sm text-text-secondary\">\n No steps defined yet. Edit the pipeline to add ingest, calibrate, transform,\n analyze or export steps.\n </p>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"mt-3\"\n onClick={() => navigate(`pipelines/${pipeline.id}/edit`)}\n >\n <Pencil className=\"size-4\" />\n Add steps\n </Button>\n </div>\n )}\n\n {hasDefinition ? (\n <details className=\"group rounded-card border border-border-default bg-bg-sunken\">\n <summary className=\"flex cursor-pointer list-none items-center justify-between px-4 py-2.5 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary\">\n Advanced — raw definition JSON\n <span className=\"text-xs text-text-secondary transition-transform group-open:rotate-180\">\n ▾\n </span>\n </summary>\n <pre className=\"overflow-x-auto border-t border-border-seam p-4 font-mono text-xs text-text-primary\">\n {JSON.stringify(pipeline.definition, null, 2)}\n </pre>\n </details>\n ) : null}\n </CardContent>\n </Card>\n\n {/* Details — the full attribute list (app value-add over the mock). */}\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent>\n <DetailRow label=\"Status\">\n <StatusBadge status={pipeline.status} />\n </DetailRow>\n <DetailRow label=\"Trigger\">{formatEnumLabel(pipeline.trigger)}</DetailRow>\n <DetailRow label=\"Schedule\">\n {pipeline.schedule ? (\n <span className=\"font-mono text-xs\">{pipeline.schedule}</span>\n ) : (\n '—'\n )}\n </DetailRow>\n <DetailRow label=\"Version\">v{pipeline.version}</DetailRow>\n <DetailRow label=\"Last run\">{formatDate(pipeline.lastRunAt)}</DetailRow>\n <DetailRow label=\"Created\">{formatDate(pipeline.createdAt)}</DetailRow>\n <DetailRow label=\"Tags\">\n {pipeline.tags.length === 0 ? (\n '—'\n ) : (\n <span className=\"flex flex-wrap gap-1\">\n {pipeline.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </span>\n )}\n </DetailRow>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Runs</CardTitle>\n </CardHeader>\n <CardContent>\n {runsLoading ? (\n <LoadingState variant=\"rows\" rows={4} label=\"Loading runs…\" />\n ) : runsError ? (\n <ErrorState message=\"Could not load runs.\" onRetry={() => void refetchRuns()} />\n ) : runs.length === 0 ? (\n <EmptyState\n icon={Play}\n title=\"No runs yet\"\n description=\"Start a run to process data through this pipeline.\"\n action={\n <Button type=\"button\" onClick={() => void handleStartRun()} disabled={isArchived}>\n <Play className=\"size-4\" />\n Start run\n </Button>\n }\n />\n ) : (\n <>\n {/* Desktop: table. */}\n <div className=\"hidden rounded-card border border-border-default md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Run</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Started</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\">\n #{run.runNumber}\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=\"tabular-nums text-text-secondary\">\n {formatDuration(run.durationMs)}\n </TableCell>\n <TableCell className=\"text-text-secondary\">\n {run.triggeredBy || '—'}\n </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 <dl className=\"grid grid-cols-2 gap-x-3 gap-y-1 text-xs text-text-secondary\">\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=\"col-span-2 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={runsPagination.offset}\n limit={runsPagination.limit}\n loadedCount={runs.length}\n hasMore={runsPageInfo?.hasMore ?? false}\n onPrev={runsPagination.prevPage}\n onNext={runsPagination.nextPage}\n itemLabel=\"runs\"\n loading={runsLoading}\n />\n </>\n )}\n </CardContent>\n </Card>\n </div>\n\n <AlertDialog\n open={confirmArchive}\n onOpenChange={(next) => (archiving ? undefined : setConfirmArchive(next))}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Archive “{pipeline.name}”?</AlertDialogTitle>\n <AlertDialogDescription>\n Archiving stops the pipeline from being triggered. Existing runs are kept for\n reference.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={archiving}>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={(event) => {\n event.preventDefault();\n void handleArchive();\n }}\n disabled={archiving}\n className={buttonVariants({ variant: 'destructive' })}\n >\n {archiving ? 'Archiving…' : 'Archive pipeline'}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiDA,IAAM,IAA4B;CAChC;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAS,EAAW,GAAsC;AACxD,QAAO,OAAO,KAAU,YAAa,EAAwB,SAAS,EAAM;;AAO9E,SAAS,EAAqB,GAA4D;AACxF,KAAI,CAAC,EACH,QAAO,EAAE;CAEX,IAAM,IAAO,EAAmC;AAChD,KAAI,CAAC,MAAM,QAAQ,EAAI,CACrB,QAAO,EAAE;CAEX,IAAM,IAAqB,EAAE;AAC7B,MAAK,IAAM,KAAQ,EACjB,KAAI,KAAQ,OAAO,KAAS,YAAY,CAAC,MAAM,QAAQ,EAAK,EAAE;EAC5D,IAAM,IAAQ,EAA4B,MACpC,IAAQ,EAA4B;AAC1C,EAAI,EAAW,EAAK,IAClB,EAAI,KAAK;GAAE;GAAM,MAAM,OAAO,KAAS,WAAW,IAAO,KAAA;GAAW,CAAC;;AAI3E,QAAO;;AAGT,SAAS,EAAW,GAA+B;AACjD,KAAI,CAAC,EACH,QAAO;CAET,IAAM,IAAO,IAAI,KAAK,EAAM;AAC5B,QAAO,OAAO,MAAM,EAAK,SAAS,CAAC,GAAG,MAAM,EAAK,gBAAgB;;AAGnE,SAAS,EAAe,GAA4B;AAClD,KAAI,KAAO,KACT,QAAO;AAET,KAAI,IAAK,IACP,QAAO,GAAG,EAAG;CAEf,IAAM,IAAU,IAAK;AAMrB,QALI,IAAU,KACL,GAAG,EAAQ,QAAQ,EAAE,CAAC,MAIxB,GAFS,KAAK,MAAM,IAAU,GAAG,CAEtB,IADL,KAAK,MAAM,IAAU,GAAG,CACV;;AAG7B,SAAS,EAAU,EAAE,UAAO,eAA0D;AACpF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,QAAD;GAAM,WAAU;aAAyD;GAAa,CAAA,EACtF,kBAAC,QAAD;GAAM,WAAU;GAAqC;GAAgB,CAAA,CACjE;;;AAKV,SAAS,EAAS,EAAE,MAAM,GAAM,eAAgE;AAC9F,QACE,kBAAC,QAAD;EAAM,WAAU;YAAhB,CACG,IAAO,kBAAC,GAAD;GAAM,WAAU;GAAW,eAAY;GAAS,CAAA,GAAG,MAC1D,EACI;;;AAIX,SAAwB,IAAqB;CAC3C,IAAM,EAAE,QAAK,OAAO,IAA2B,EACzC,EAAE,gBAAa,GAAuB,EAEtC,EAAE,SAAM,aAAS,WAAO,eAAY,EAAoB,EAAG,EAC3D,IAAiB,EAAc;EAAE,UAAU;EAAI,UAAU;EAAI,CAAC,EAC9D,EACJ,MAAM,GACN,SAAS,GACT,OAAO,IACP,SAAS,MACP,GAAgB;EAClB,YAAY;EACZ,YAAY;GAAE,OAAO,EAAe;GAAO,QAAQ,EAAe;GAAQ;EAC3E,CAAC,EAEI,IAAW,GAAM,aACjB,IAAO,QAAc,GAAU,YAAY,SAAS,EAAE,EAAE,CAAC,EAAS,CAAC,EACnE,KAAe,GAAU,YAAY,UAKrC,IAAkB,QAChB,EAAqB,GAAU,WAAW,EAChD,CAAC,GAAU,WAAW,CACvB,EACK,IAAc,EAAK,IAAI,MAAM,IAE7B,EAAE,MAAM,MAAoB,EADL,KAAa,EAAgB,WAAW,KAAa,IACb,IAAc,GAAG,EAChF,IAAe,QAEZ,CAAC,GADK,GAAiB,mBAAmB,EAAE,CACnC,CACb,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS,CACvC,KAAK,OAAU;EAAE,MAAM,EAAK;EAAM,MAAM,EAAK;EAAM,QAAQ,EAAK;EAAQ,EAAE,EAC5E,CAAC,EAAgB,CAAC,EACf,IAAW,EAAgB,SAAS,IAAI,IAAkB,GAC1D,KAAiB,EAAgB,WAAW,KAAK,EAAa,SAAS,GAEvE,CAAC,GAAU,EAAE,SAAS,OAAc,IAAwB,EAC5D,CAAC,IAAiB,EAAE,SAAS,OAAe,GAA+B,EAC3E,CAAC,IAAgB,KAAqB,GAAS,GAAM,EAErD,IAAiB,YAAY;AACjC,MAAI,CAAC,EACH;EAEF,IAAM,IAAW,MAAM,EAAS,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,EAAS,IAAI,EAAE,EAAE,CAAC,EAChF,IAAQ,EAAS,MAAM,aAAa;AACrC,IAAkB,GAAU,GAAO,wBAAwB,KAGhE,EAAO,QAAQ,eAAe,wBAAwB,EACtD,MAAM,QAAQ,IAAI,CAAC,GAAS,EAAE,GAAa,CAAC,CAAC,EACzC,KACF,EAAS,QAAQ,IAAQ;IAIvB,KAAgB,YAAY;AAChC,MAAI,CAAC,EACH;EAEF,IAAM,IAAW,MAAM,GAAgB,EAAE,WAAW,EAAE,IAAI,EAAS,IAAI,EAAE,CAAC;AAEvE,IAAkB,GAAU,EAAS,MAAM,oBAAoB,6BAA6B,KAI/F,EAAkB,GAAM,EACxB,EAAO,QAAQ,qBAAqB,IAAI,EAAS,KAAK,iBAAiB,EACvE,EAAS,YAAY;;AAGvB,KAAI,GACF,QACE,kBAAC,GAAD;EAAY,OAAM;EAAW,UAAS;YACpC,kBAAC,GAAD,EAAc,OAAM,qBAAsB,CAAA;EAC/B,CAAA;AAIjB,KAAI,GACF,QACE,kBAAC,GAAD;EAAY,OAAM;EAAW,UAAS;YACpC,kBAAC,GAAD,EAAY,eAAe,KAAK,GAAS,EAAI,CAAA;EAClC,CAAA;AAIjB,KAAI,CAAC,EACH,QACE,kBAAC,GAAD;EAAY,OAAM;EAAW,UAAS;YACpC,kBAAC,GAAD;GACE,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,GAAD;IAAQ,MAAK;IAAS,SAAQ;IAAU,eAAe,EAAS,YAAY;cAAE;IAErE,CAAA;GAEX,CAAA;EACS,CAAA;CAIjB,IAAM,IAAa,EAAS,WAAW,YACjC,KAAgB,GAAQ,EAAS,cAAc,OAAO,KAAK,EAAS,WAAW,CAAC,SAAS;AAE/F,QACE,kBAAC,GAAD;EACE,OAAO,EAAS;EAChB,aAAa,EAAS,eAAe,KAAA;EACrC,UAAS;EACT,SACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,MAAK;KACL,eAAe,KAAK,GAAgB;KACpC,UAAU,KAAY;eAHxB,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,IAAW,cAAc,YACnB;;IACT,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,eAAe,EAAS,aAAa,EAAS,GAAG,OAAO;eAH1D,CAKE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAAA,OAEtB;;IACP,IAKE,OAJF,kBAAC,GAAD;KAAQ,MAAK;KAAS,SAAQ;KAAU,eAAe,EAAkB,GAAK;eAA9E,CACE,kBAAC,IAAD,EAAS,WAAU,UAAW,CAAA,EAAA,UAEvB;;IAEP;;YA5BV,CA+BE,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAa,QAAQ,EAAS,QAAU,CAAA;MACxC,kBAAC,GAAD;OAAU,MAAM;iBAAQ,EAAgB,EAAS,QAAQ;OAAY,CAAA;MACpE,EAAS,WACR,kBAAC,GAAD,EAAA,UACE,kBAAC,QAAD;OAAM,WAAU;iBAAa,EAAS;OAAgB,CAAA,EAC7C,CAAA,GACT;MACJ,kBAAC,GAAD;OAAU,MAAM;iBAAhB,CAAsB,KAAE,EAAS,QAAmB;;MAChD;;IAGN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,YAAoB,CAAA,EACpB,CAAA,EACb,kBAAC,GAAD;KAAa,WAAU;eAAvB;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAA0C;QAAgB,CAAA,EACzE,KACC,kBAAC,QAAD;QAAM,WAAU;kBAA8B;QAAmC,CAAA,GAC/E,KACA;;MAEL,EAAS,SAAS,IACjB,kBAAC,IAAD,EAAS,OAAO,GAAY,CAAA,GAE5B,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAGvC,CAAA,EACJ,kBAAC,GAAD;QACE,MAAK;QACL,SAAQ;QACR,MAAK;QACL,WAAU;QACV,eAAe,EAAS,aAAa,EAAS,GAAG,OAAO;kBAL1D,CAOE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAAA,YAEtB;UACL;;MAGP,KACC,kBAAC,WAAD;OAAS,WAAU;iBAAnB,CACE,kBAAC,WAAD;QAAS,WAAU;kBAAnB,CAA8K,kCAE5K,kBAAC,QAAD;SAAM,WAAU;mBAAyE;SAElF,CAAA,CACC;WACV,kBAAC,OAAD;QAAK,WAAU;kBACZ,KAAK,UAAU,EAAS,YAAY,MAAM,EAAE;QACzC,CAAA,CACE;WACR;MACQ;OACT,EAAA,CAAA;IAGP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA,EACnB,CAAA,EACb,kBAAC,GAAD,EAAA,UAAA;KACE,kBAAC,GAAD;MAAW,OAAM;gBACf,kBAAC,GAAD,EAAa,QAAQ,EAAS,QAAU,CAAA;MAC9B,CAAA;KACZ,kBAAC,GAAD;MAAW,OAAM;gBAAW,EAAgB,EAAS,QAAQ;MAAa,CAAA;KAC1E,kBAAC,GAAD;MAAW,OAAM;gBACd,EAAS,WACR,kBAAC,QAAD;OAAM,WAAU;iBAAqB,EAAS;OAAgB,CAAA,GAE9D;MAEQ,CAAA;KACZ,kBAAC,GAAD;MAAW,OAAM;gBAAjB,CAA2B,KAAE,EAAS,QAAoB;;KAC1D,kBAAC,GAAD;MAAW,OAAM;gBAAY,EAAW,EAAS,UAAU;MAAa,CAAA;KACxE,kBAAC,GAAD;MAAW,OAAM;gBAAW,EAAW,EAAS,UAAU;MAAa,CAAA;KACvE,kBAAC,GAAD;MAAW,OAAM;gBACd,EAAS,KAAK,WAAW,IACxB,MAEA,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAS,KAAK,KAAK,MAClB,kBAAC,QAAD;QAEE,WAAU;kBAET;QACI,EAJA,EAIA,CACP;OACG,CAAA;MAEC,CAAA;KACA,EAAA,CAAA,CACT,EAAA,CAAA;IAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,QAAgB,CAAA,EAChB,CAAA,EACb,kBAAC,GAAD,EAAA,UACG,IACC,kBAAC,GAAD;KAAc,SAAQ;KAAO,MAAM;KAAG,OAAM;KAAkB,CAAA,GAC5D,KACF,kBAAC,GAAD;KAAY,SAAQ;KAAuB,eAAe,KAAK,GAAa;KAAI,CAAA,GAC9E,EAAK,WAAW,IAClB,kBAAC,GAAD;KACE,MAAM;KACN,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,GAAD;MAAQ,MAAK;MAAS,eAAe,KAAK,GAAgB;MAAE,UAAU;gBAAtE,CACE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAAA,YAEpB;;KAEX,CAAA,GAEF,kBAAA,GAAA,EAAA,UAAA;KAEE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;OACE,kBAAC,GAAD,EAAA,UAAW,OAAe,CAAA;OAC1B,kBAAC,GAAD,EAAA,UAAW,UAAkB,CAAA;OAC7B,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA;OAC9B,kBAAC,GAAD,EAAA,UAAW,YAAoB,CAAA;OAC/B,kBAAC,GAAD,EAAA,UAAW,gBAAwB,CAAA;OAC1B,EAAA,CAAA,EACC,CAAA,EACd,kBAAC,IAAD,EAAA,UACG,EAAK,KAAK,MACT,kBAAC,GAAD;OAEE,WAAU;OACV,eAAe,EAAS,QAAQ,EAAI,KAAK;iBAH3C;QAKE,kBAAC,GAAD;SAAW,WAAU;mBAArB,CAAqD,KACjD,EAAI,UACI;;QACZ,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAa,QAAQ,EAAI,QAAU,CAAA,EACzB,CAAA;QACZ,kBAAC,GAAD;SAAW,WAAU;mBAClB,EAAW,EAAI,UAAU;SAChB,CAAA;QACZ,kBAAC,GAAD;SAAW,WAAU;mBAClB,EAAe,EAAI,WAAW;SACrB,CAAA;QACZ,kBAAC,GAAD;SAAW,WAAU;mBAClB,EAAI,eAAe;SACV,CAAA;QACH;SAnBJ,EAAI,GAmBA,CACX,EACQ,CAAA,CACN,EAAA,CAAA;MACJ,CAAA;KAGN,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAK,KAAK,MACT,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAS,QAAQ,EAAI,KAAK;OACzC,WAAU;iBAHZ,CAKE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAAgD,KAAE,EAAI,UAAiB;YACvE,kBAAC,GAAD,EAAa,QAAQ,EAAI,QAAU,CAAA,CAC/B;WACN,kBAAC,MAAD;QAAI,WAAU;kBAAd;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBAAsB;WAAY,CAAA,EAChD,kBAAC,MAAD;WAAI,WAAU;qBAAqB,EAAW,EAAI,UAAU;WAAM,CAAA,CAC9D;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBAAsB;WAAa,CAAA,EACjD,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAe,EAAI,WAAW;WAC5B,CAAA,CACD;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBAAsB;WAAiB,CAAA,EACrD,kBAAC,MAAD;WAAI,WAAU;qBAAqB,EAAI,eAAe;WAAS,CAAA,CAC3D;;SACH;UACE;UACN,EA3BI,EAAI,GA2BR,CACL;MACC,CAAA;KAEL,kBAAC,GAAD;MACE,QAAQ,EAAe;MACvB,OAAO,EAAe;MACtB,aAAa,EAAK;MAClB,SAAS,IAAc,WAAW;MAClC,QAAQ,EAAe;MACvB,QAAQ,EAAe;MACvB,WAAU;MACV,SAAS;MACT,CAAA;KACD,EAAA,CAAA,EAEO,CAAA,CACT,EAAA,CAAA;IACH;MAEN,kBAAC,IAAD;GACE,MAAM;GACN,eAAe,MAAU,IAAY,KAAA,IAAY,EAAkB,EAAK;aAExE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD,EAAA,UAAA;IAAkB;IAAU,EAAS;IAAK;IAAqB,EAAA,CAAA,EAC/D,kBAAC,IAAD,EAAA,UAAwB,4FAGC,CAAA,CACP,EAAA,CAAA,EACpB,kBAAC,IAAD,EAAA,UAAA,CACE,kBAAC,IAAD;IAAmB,UAAU;cAAW;IAA0B,CAAA,EAClE,kBAAC,IAAD;IACE,UAAU,MAAU;AAEb,KADL,EAAM,gBAAgB,EACjB,IAAe;;IAEtB,UAAU;IACV,WAAW,GAAe,EAAE,SAAS,eAAe,CAAC;cAEpD,IAAY,eAAe;IACV,CAAA,CACF,EAAA,CAAA,CACD,EAAA,CAAA;GACT,CAAA,CACH"}
|
|
@@ -2,127 +2,178 @@ import { useCosmicIntersection as e } from "../../context/CosmicIntersectionCont
|
|
|
2
2
|
import { notify as t } from "../../lib/toast.js";
|
|
3
3
|
import { useCosCreatePipelineMutation as n, useCosPipelineQuery as r, useCosUpdatePipelineMutation as i } from "../../api/cosmicApi.js";
|
|
4
4
|
import a from "../../components/EntityFormShell.js";
|
|
5
|
-
import
|
|
6
|
-
import { formatEnumLabel as
|
|
5
|
+
import ee from "../../components/EntityTagsField.js";
|
|
6
|
+
import { formatEnumLabel as o } from "../../components/StatusBadge.js";
|
|
7
7
|
import "../../components/index.js";
|
|
8
|
-
import { describeError as
|
|
9
|
-
import { useState as u } from "react";
|
|
8
|
+
import { describeError as s, mutationSucceeded as c } from "../../lib/mutationResult.js";
|
|
9
|
+
import { useMemo as l, useState as u } from "react";
|
|
10
10
|
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
11
11
|
import { useParams as p } from "react-router-dom";
|
|
12
|
-
import {
|
|
12
|
+
import { ArrowDown as m, ArrowUp as te, GripVertical as h, Plus as g, Trash2 as _ } from "lucide-react";
|
|
13
|
+
import { Button as v, Input as y, Label as b, Select as x, SelectContent as S, SelectItem as C, SelectTrigger as w, SelectValue as T, Textarea as E } from "@burdenoff/fe-libs/ui";
|
|
13
14
|
//#region src/cosmicintersection/pages/pipelines/PipelineFormPage.tsx
|
|
14
|
-
var
|
|
15
|
+
var D = [
|
|
15
16
|
"MANUAL",
|
|
16
17
|
"SCHEDULED",
|
|
17
18
|
"ON_INGEST",
|
|
18
19
|
"WEBHOOK"
|
|
19
|
-
],
|
|
20
|
+
], O = [
|
|
21
|
+
"INGEST",
|
|
22
|
+
"CALIBRATE",
|
|
23
|
+
"TRANSFORM",
|
|
24
|
+
"ANALYZE",
|
|
25
|
+
"EXPORT",
|
|
26
|
+
"NOTIFY"
|
|
27
|
+
];
|
|
28
|
+
function k(e) {
|
|
29
|
+
return typeof e == "string" && O.includes(e);
|
|
30
|
+
}
|
|
31
|
+
var A = 0, j = () => `step-${A += 1}`, M = {
|
|
20
32
|
name: "",
|
|
21
33
|
description: "",
|
|
22
34
|
trigger: "MANUAL",
|
|
23
35
|
schedule: "",
|
|
24
|
-
|
|
36
|
+
steps: [],
|
|
25
37
|
tags: []
|
|
26
38
|
};
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
function N(e) {
|
|
40
|
+
if (!e) return [];
|
|
41
|
+
let t = e.steps;
|
|
42
|
+
if (!Array.isArray(t)) return [];
|
|
43
|
+
let n = [];
|
|
44
|
+
for (let e of t) if (e && typeof e == "object" && !Array.isArray(e)) {
|
|
45
|
+
let t = e.kind, r = e.name;
|
|
46
|
+
k(t) && n.push({
|
|
47
|
+
id: j(),
|
|
48
|
+
kind: t,
|
|
49
|
+
name: typeof r == "string" ? r : ""
|
|
50
|
+
});
|
|
51
|
+
}
|
|
32
52
|
return n;
|
|
33
53
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
function P(e) {
|
|
55
|
+
return e.map((e) => {
|
|
56
|
+
let t = { kind: e.kind }, n = e.name.trim();
|
|
57
|
+
return n && (t.name = n), t;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function F() {
|
|
61
|
+
let { id: A = "" } = p(), F = !!A, { navigate: I } = e(), { data: ne, loading: L } = r(A), [R, { loading: z }] = n(), [B, { loading: V }] = i(), [H, U] = u(M), [W, G] = u({}), [K, q] = u(""), [J, Y] = u(null), X = F ? ne?.cosPipeline : null, [re, ie] = u(null);
|
|
62
|
+
if (X && X.id !== re) {
|
|
63
|
+
ie(X.id), U({
|
|
64
|
+
name: X.name ?? "",
|
|
65
|
+
description: X.description ?? "",
|
|
66
|
+
trigger: X.trigger ?? "MANUAL",
|
|
67
|
+
schedule: X.schedule ?? "",
|
|
68
|
+
steps: N(X.definition),
|
|
69
|
+
tags: X.tags ?? []
|
|
70
|
+
});
|
|
71
|
+
let e = { ...X.definition ?? {} };
|
|
72
|
+
delete e.steps, G(e);
|
|
73
|
+
}
|
|
74
|
+
let Z = (e, t) => {
|
|
75
|
+
U((n) => ({
|
|
46
76
|
...n,
|
|
47
77
|
[e]: t
|
|
48
78
|
}));
|
|
79
|
+
}, ae = l(() => JSON.stringify({
|
|
80
|
+
...W,
|
|
81
|
+
steps: P(H.steps)
|
|
82
|
+
}, null, 2), [W, H.steps]), oe = () => {
|
|
83
|
+
Z("steps", [...H.steps, {
|
|
84
|
+
id: j(),
|
|
85
|
+
kind: "INGEST",
|
|
86
|
+
name: ""
|
|
87
|
+
}]);
|
|
88
|
+
}, se = (e) => {
|
|
89
|
+
Z("steps", H.steps.filter((t) => t.id !== e));
|
|
90
|
+
}, Q = (e, t) => {
|
|
91
|
+
Z("steps", H.steps.map((n) => n.id === e ? {
|
|
92
|
+
...n,
|
|
93
|
+
...t
|
|
94
|
+
} : n));
|
|
95
|
+
}, $ = (e, t) => {
|
|
96
|
+
let n = e + t;
|
|
97
|
+
if (n < 0 || n >= H.steps.length) return;
|
|
98
|
+
let r = [...H.steps], [i] = r.splice(e, 1);
|
|
99
|
+
r.splice(n, 0, i), Z("steps", r);
|
|
49
100
|
};
|
|
50
101
|
return /* @__PURE__ */ f(a, {
|
|
51
|
-
title:
|
|
102
|
+
title: F ? "Edit pipeline" : "New pipeline",
|
|
52
103
|
description: "Define how raw data is ingested, processed and released.",
|
|
53
|
-
purpose: "A pipeline chains ordered steps (ingest, calibrate, transform, analyze, export) over your datasets. Choose how it is triggered, add an optional cron schedule, and
|
|
54
|
-
backLabel:
|
|
55
|
-
onBack: () =>
|
|
104
|
+
purpose: "A pipeline chains ordered steps (ingest, calibrate, transform, analyze, export) over your datasets. Choose how it is triggered, add an optional cron schedule, and build its steps below.",
|
|
105
|
+
backLabel: F ? "Back to pipeline" : "Back to pipelines",
|
|
106
|
+
onBack: () => I(F ? `pipelines/${A}` : "pipelines"),
|
|
56
107
|
onSubmit: async (e) => {
|
|
57
|
-
if (e.preventDefault(), !
|
|
108
|
+
if (e.preventDefault(), !H.name.trim()) {
|
|
58
109
|
t.error("Name is required", "Give the pipeline a name before saving.");
|
|
59
110
|
return;
|
|
60
111
|
}
|
|
61
|
-
let n;
|
|
112
|
+
let n = P(H.steps), r = { ...W };
|
|
113
|
+
delete r.steps;
|
|
114
|
+
let i;
|
|
115
|
+
i = n.length > 0 ? {
|
|
116
|
+
...r,
|
|
117
|
+
steps: n
|
|
118
|
+
} : Object.keys(r).length > 0 ? r : void 0;
|
|
119
|
+
let a = H.trigger === "SCHEDULED" ? H.schedule.trim() : "";
|
|
62
120
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
let r = P.trigger === "SCHEDULED" ? P.schedule.trim() : "";
|
|
69
|
-
try {
|
|
70
|
-
if (E) {
|
|
71
|
-
let e = await M({ variables: {
|
|
72
|
-
id: T,
|
|
121
|
+
if (F) {
|
|
122
|
+
let e = await B({ variables: {
|
|
123
|
+
id: A,
|
|
73
124
|
input: {
|
|
74
|
-
name:
|
|
75
|
-
description:
|
|
76
|
-
trigger:
|
|
77
|
-
schedule:
|
|
78
|
-
definition:
|
|
79
|
-
tags:
|
|
125
|
+
name: H.name.trim(),
|
|
126
|
+
description: H.description.trim() || void 0,
|
|
127
|
+
trigger: H.trigger,
|
|
128
|
+
schedule: a || void 0,
|
|
129
|
+
definition: i,
|
|
130
|
+
tags: H.tags
|
|
80
131
|
}
|
|
81
132
|
} });
|
|
82
|
-
if (!
|
|
83
|
-
t.success("Pipeline updated", `“${
|
|
133
|
+
if (!c(e, e.data?.cosUpdatePipeline?.id, "Could not update pipeline")) return;
|
|
134
|
+
t.success("Pipeline updated", `“${H.name.trim()}” was saved.`), I(`pipelines/${A}`);
|
|
84
135
|
return;
|
|
85
136
|
}
|
|
86
|
-
let e = await
|
|
87
|
-
name:
|
|
88
|
-
description:
|
|
89
|
-
trigger:
|
|
90
|
-
schedule:
|
|
91
|
-
definition:
|
|
92
|
-
tags:
|
|
93
|
-
} } }),
|
|
94
|
-
if (!
|
|
95
|
-
t.success("Pipeline created", `“${
|
|
137
|
+
let e = await R({ variables: { input: {
|
|
138
|
+
name: H.name.trim(),
|
|
139
|
+
description: H.description.trim() || void 0,
|
|
140
|
+
trigger: H.trigger,
|
|
141
|
+
schedule: a || void 0,
|
|
142
|
+
definition: i,
|
|
143
|
+
tags: H.tags
|
|
144
|
+
} } }), n = e.data?.cosCreatePipeline?.id;
|
|
145
|
+
if (!c(e, n, "Could not create pipeline")) return;
|
|
146
|
+
t.success("Pipeline created", `“${H.name.trim()}” is ready.`), I(n ? `pipelines/${n}` : "pipelines");
|
|
96
147
|
} catch (e) {
|
|
97
|
-
t.error("Could not save pipeline",
|
|
148
|
+
t.error("Could not save pipeline", s(e));
|
|
98
149
|
}
|
|
99
150
|
},
|
|
100
|
-
submitLabel:
|
|
101
|
-
saving:
|
|
102
|
-
submitDisabled:
|
|
151
|
+
submitLabel: F ? "Save changes" : "Create pipeline",
|
|
152
|
+
saving: z || V,
|
|
153
|
+
submitDisabled: F && L,
|
|
103
154
|
children: [
|
|
104
155
|
/* @__PURE__ */ f("div", {
|
|
105
156
|
className: "space-y-1",
|
|
106
|
-
children: [/* @__PURE__ */ d(
|
|
157
|
+
children: [/* @__PURE__ */ d(b, {
|
|
107
158
|
htmlFor: "pipeline-name",
|
|
108
159
|
children: "Name"
|
|
109
|
-
}), /* @__PURE__ */ d(
|
|
160
|
+
}), /* @__PURE__ */ d(y, {
|
|
110
161
|
id: "pipeline-name",
|
|
111
|
-
value:
|
|
112
|
-
onChange: (e) =>
|
|
162
|
+
value: H.name,
|
|
163
|
+
onChange: (e) => Z("name", e.target.value),
|
|
113
164
|
placeholder: "e.g. Kepler DR3 ingest",
|
|
114
165
|
required: !0
|
|
115
166
|
})]
|
|
116
167
|
}),
|
|
117
168
|
/* @__PURE__ */ f("div", {
|
|
118
169
|
className: "space-y-1",
|
|
119
|
-
children: [/* @__PURE__ */ d(
|
|
170
|
+
children: [/* @__PURE__ */ d(b, {
|
|
120
171
|
htmlFor: "pipeline-description",
|
|
121
172
|
children: "Description"
|
|
122
|
-
}), /* @__PURE__ */ d(
|
|
173
|
+
}), /* @__PURE__ */ d(E, {
|
|
123
174
|
id: "pipeline-description",
|
|
124
|
-
value:
|
|
125
|
-
onChange: (e) =>
|
|
175
|
+
value: H.description,
|
|
176
|
+
onChange: (e) => Z("description", e.target.value),
|
|
126
177
|
rows: 3,
|
|
127
178
|
placeholder: "What this pipeline does and when it runs."
|
|
128
179
|
})]
|
|
@@ -131,32 +182,32 @@ function T() {
|
|
|
131
182
|
className: "grid grid-cols-1 gap-4 md:grid-cols-2",
|
|
132
183
|
children: [/* @__PURE__ */ f("div", {
|
|
133
184
|
className: "space-y-1",
|
|
134
|
-
children: [/* @__PURE__ */ d(
|
|
185
|
+
children: [/* @__PURE__ */ d(b, {
|
|
135
186
|
htmlFor: "pipeline-trigger",
|
|
136
187
|
children: "Trigger"
|
|
137
|
-
}), /* @__PURE__ */ f(
|
|
138
|
-
value:
|
|
139
|
-
onValueChange: (e) =>
|
|
140
|
-
children: [/* @__PURE__ */ d(
|
|
188
|
+
}), /* @__PURE__ */ f(x, {
|
|
189
|
+
value: H.trigger,
|
|
190
|
+
onValueChange: (e) => Z("trigger", e),
|
|
191
|
+
children: [/* @__PURE__ */ d(w, {
|
|
141
192
|
id: "pipeline-trigger",
|
|
142
193
|
"aria-label": "Trigger",
|
|
143
|
-
children: /* @__PURE__ */ d(
|
|
144
|
-
}), /* @__PURE__ */ d(
|
|
194
|
+
children: /* @__PURE__ */ d(T, {})
|
|
195
|
+
}), /* @__PURE__ */ d(S, { children: D.map((e) => /* @__PURE__ */ d(C, {
|
|
145
196
|
value: e,
|
|
146
|
-
children:
|
|
197
|
+
children: o(e)
|
|
147
198
|
}, e)) })]
|
|
148
199
|
})]
|
|
149
|
-
}),
|
|
200
|
+
}), H.trigger === "SCHEDULED" ? /* @__PURE__ */ f("div", {
|
|
150
201
|
className: "space-y-1",
|
|
151
202
|
children: [
|
|
152
|
-
/* @__PURE__ */ d(
|
|
203
|
+
/* @__PURE__ */ d(b, {
|
|
153
204
|
htmlFor: "pipeline-schedule",
|
|
154
205
|
children: "Schedule (cron)"
|
|
155
206
|
}),
|
|
156
|
-
/* @__PURE__ */ d(
|
|
207
|
+
/* @__PURE__ */ d(y, {
|
|
157
208
|
id: "pipeline-schedule",
|
|
158
|
-
value:
|
|
159
|
-
onChange: (e) =>
|
|
209
|
+
value: H.schedule,
|
|
210
|
+
onChange: (e) => Z("schedule", e.target.value),
|
|
160
211
|
placeholder: "0 2 * * *",
|
|
161
212
|
className: "font-mono"
|
|
162
213
|
}),
|
|
@@ -176,35 +227,186 @@ function T() {
|
|
|
176
227
|
}) : null]
|
|
177
228
|
}),
|
|
178
229
|
/* @__PURE__ */ f("div", {
|
|
179
|
-
className: "space-y-
|
|
230
|
+
className: "space-y-2",
|
|
180
231
|
children: [
|
|
181
|
-
/* @__PURE__ */ d(
|
|
182
|
-
htmlFor: "pipeline-definition",
|
|
183
|
-
children: "Definition (JSON)"
|
|
184
|
-
}),
|
|
185
|
-
/* @__PURE__ */ d(x, {
|
|
186
|
-
id: "pipeline-definition",
|
|
187
|
-
value: P.definition,
|
|
188
|
-
onChange: (e) => z("definition", e.target.value),
|
|
189
|
-
rows: 8,
|
|
190
|
-
spellCheck: !1,
|
|
191
|
-
className: "font-mono text-xs",
|
|
192
|
-
placeholder: "{\n \"steps\": [\n { \"kind\": \"INGEST\", \"name\": \"Fetch raw frames\" }\n ]\n}"
|
|
193
|
-
}),
|
|
232
|
+
/* @__PURE__ */ d(b, { children: "Definition — steps" }),
|
|
194
233
|
/* @__PURE__ */ d("p", {
|
|
195
234
|
className: "text-xs text-text-secondary",
|
|
196
|
-
children: "
|
|
235
|
+
children: "The ordered stages this pipeline runs. Add a step, pick its kind, optionally name it, and reorder with the arrows."
|
|
236
|
+
}),
|
|
237
|
+
H.steps.length === 0 ? /* @__PURE__ */ d("div", {
|
|
238
|
+
className: "rounded-card border border-dashed border-border-default bg-bg-sunken px-4 py-6 text-center",
|
|
239
|
+
children: /* @__PURE__ */ d("p", {
|
|
240
|
+
className: "text-sm text-text-secondary",
|
|
241
|
+
children: "No steps yet. Add the first stage to start building the flow."
|
|
242
|
+
})
|
|
243
|
+
}) : /* @__PURE__ */ d("ol", {
|
|
244
|
+
className: "space-y-2",
|
|
245
|
+
children: H.steps.map((e, t) => /* @__PURE__ */ f("li", {
|
|
246
|
+
className: "flex items-center gap-2 rounded-card border border-border-default bg-bg-elevated p-2.5",
|
|
247
|
+
children: [
|
|
248
|
+
/* @__PURE__ */ f("span", {
|
|
249
|
+
className: "flex items-center gap-1.5 text-text-secondary",
|
|
250
|
+
children: [/* @__PURE__ */ d(h, {
|
|
251
|
+
className: "size-4 shrink-0",
|
|
252
|
+
"aria-hidden": "true"
|
|
253
|
+
}), /* @__PURE__ */ d("span", {
|
|
254
|
+
className: "w-4 text-xs font-semibold tabular-nums",
|
|
255
|
+
children: t + 1
|
|
256
|
+
})]
|
|
257
|
+
}),
|
|
258
|
+
/* @__PURE__ */ f("div", {
|
|
259
|
+
className: "grid min-w-0 flex-1 grid-cols-1 gap-2 sm:grid-cols-[10rem_1fr]",
|
|
260
|
+
children: [/* @__PURE__ */ f(x, {
|
|
261
|
+
value: e.kind,
|
|
262
|
+
onValueChange: (t) => Q(e.id, { kind: t }),
|
|
263
|
+
children: [/* @__PURE__ */ d(w, {
|
|
264
|
+
"aria-label": `Kind for step ${t + 1}`,
|
|
265
|
+
children: /* @__PURE__ */ d(T, {})
|
|
266
|
+
}), /* @__PURE__ */ d(S, { children: O.map((e) => /* @__PURE__ */ d(C, {
|
|
267
|
+
value: e,
|
|
268
|
+
children: o(e)
|
|
269
|
+
}, e)) })]
|
|
270
|
+
}), /* @__PURE__ */ d(y, {
|
|
271
|
+
value: e.name,
|
|
272
|
+
onChange: (t) => Q(e.id, { name: t.target.value }),
|
|
273
|
+
placeholder: "Optional step name",
|
|
274
|
+
"aria-label": `Name for step ${t + 1}`
|
|
275
|
+
})]
|
|
276
|
+
}),
|
|
277
|
+
/* @__PURE__ */ f("div", {
|
|
278
|
+
className: "flex shrink-0 items-center gap-0.5",
|
|
279
|
+
children: [
|
|
280
|
+
/* @__PURE__ */ d(v, {
|
|
281
|
+
type: "button",
|
|
282
|
+
variant: "ghost",
|
|
283
|
+
size: "icon-sm",
|
|
284
|
+
onClick: () => $(t, -1),
|
|
285
|
+
disabled: t === 0,
|
|
286
|
+
"aria-label": `Move step ${t + 1} up`,
|
|
287
|
+
children: /* @__PURE__ */ d(te, { className: "size-4" })
|
|
288
|
+
}),
|
|
289
|
+
/* @__PURE__ */ d(v, {
|
|
290
|
+
type: "button",
|
|
291
|
+
variant: "ghost",
|
|
292
|
+
size: "icon-sm",
|
|
293
|
+
onClick: () => $(t, 1),
|
|
294
|
+
disabled: t === H.steps.length - 1,
|
|
295
|
+
"aria-label": `Move step ${t + 1} down`,
|
|
296
|
+
children: /* @__PURE__ */ d(m, { className: "size-4" })
|
|
297
|
+
}),
|
|
298
|
+
/* @__PURE__ */ d(v, {
|
|
299
|
+
type: "button",
|
|
300
|
+
variant: "ghost",
|
|
301
|
+
size: "icon-sm",
|
|
302
|
+
onClick: () => se(e.id),
|
|
303
|
+
"aria-label": `Remove step ${t + 1}`,
|
|
304
|
+
className: "text-text-secondary hover:text-status-error-text",
|
|
305
|
+
children: /* @__PURE__ */ d(_, { className: "size-4" })
|
|
306
|
+
})
|
|
307
|
+
]
|
|
308
|
+
})
|
|
309
|
+
]
|
|
310
|
+
}, e.id))
|
|
311
|
+
}),
|
|
312
|
+
/* @__PURE__ */ f(v, {
|
|
313
|
+
type: "button",
|
|
314
|
+
variant: "outline",
|
|
315
|
+
size: "sm",
|
|
316
|
+
onClick: oe,
|
|
317
|
+
children: [/* @__PURE__ */ d(g, { className: "size-4" }), "Add step"]
|
|
318
|
+
}),
|
|
319
|
+
/* @__PURE__ */ f("details", {
|
|
320
|
+
className: "group mt-2 rounded-card border border-border-default bg-bg-sunken",
|
|
321
|
+
onToggle: (e) => {
|
|
322
|
+
e.currentTarget.open && (q(ae), Y(null));
|
|
323
|
+
},
|
|
324
|
+
children: [/* @__PURE__ */ f("summary", {
|
|
325
|
+
className: "flex cursor-pointer list-none items-center justify-between px-4 py-2.5 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary",
|
|
326
|
+
children: ["Advanced — edit as JSON", /* @__PURE__ */ d("span", {
|
|
327
|
+
className: "text-xs transition-transform group-open:rotate-180",
|
|
328
|
+
children: "▾"
|
|
329
|
+
})]
|
|
330
|
+
}), /* @__PURE__ */ f("div", {
|
|
331
|
+
className: "space-y-2 border-t border-border-seam p-4",
|
|
332
|
+
children: [
|
|
333
|
+
/* @__PURE__ */ d(E, {
|
|
334
|
+
value: K,
|
|
335
|
+
onChange: (e) => {
|
|
336
|
+
q(e.target.value), Y(null);
|
|
337
|
+
},
|
|
338
|
+
rows: 8,
|
|
339
|
+
spellCheck: !1,
|
|
340
|
+
className: "font-mono text-xs",
|
|
341
|
+
"aria-label": "Definition JSON"
|
|
342
|
+
}),
|
|
343
|
+
J ? /* @__PURE__ */ d("p", {
|
|
344
|
+
className: "text-xs text-status-error-text",
|
|
345
|
+
children: J
|
|
346
|
+
}) : null,
|
|
347
|
+
/* @__PURE__ */ f("div", {
|
|
348
|
+
className: "flex items-center justify-between gap-2",
|
|
349
|
+
children: [/* @__PURE__ */ d("p", {
|
|
350
|
+
className: "text-xs text-text-secondary",
|
|
351
|
+
children: "Edit the raw definition, then apply it back to the builder above."
|
|
352
|
+
}), /* @__PURE__ */ d(v, {
|
|
353
|
+
type: "button",
|
|
354
|
+
variant: "outline",
|
|
355
|
+
size: "sm",
|
|
356
|
+
onClick: () => {
|
|
357
|
+
let e;
|
|
358
|
+
try {
|
|
359
|
+
e = JSON.parse(K);
|
|
360
|
+
} catch (e) {
|
|
361
|
+
Y(s(e) ?? "Invalid JSON.");
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (!e || typeof e != "object" || Array.isArray(e)) {
|
|
365
|
+
Y("Definition must be a JSON object with a \"steps\" array.");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
let n = e.steps;
|
|
369
|
+
if (!Array.isArray(n)) {
|
|
370
|
+
Y("Definition must contain a \"steps\" array.");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
let r = [];
|
|
374
|
+
for (let e of n) {
|
|
375
|
+
if (!e || typeof e != "object" || Array.isArray(e)) {
|
|
376
|
+
Y("Each step must be an object with a \"kind\".");
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
let t = e.kind;
|
|
380
|
+
if (!k(t)) {
|
|
381
|
+
Y(`Invalid step kind "${String(t)}". Allowed: ${O.join(", ")}.`);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
let n = e.name;
|
|
385
|
+
r.push({
|
|
386
|
+
id: j(),
|
|
387
|
+
kind: t,
|
|
388
|
+
name: typeof n == "string" ? n : ""
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
let i = { ...e };
|
|
392
|
+
delete i.steps, G(i), Z("steps", r), Y(null), t.success("Steps updated", "The builder now reflects your JSON.");
|
|
393
|
+
},
|
|
394
|
+
children: "Apply to builder"
|
|
395
|
+
})]
|
|
396
|
+
})
|
|
397
|
+
]
|
|
398
|
+
})]
|
|
197
399
|
})
|
|
198
400
|
]
|
|
199
401
|
}),
|
|
200
|
-
/* @__PURE__ */ d(
|
|
201
|
-
value:
|
|
202
|
-
onChange: (e) =>
|
|
402
|
+
/* @__PURE__ */ d(ee, {
|
|
403
|
+
value: H.tags,
|
|
404
|
+
onChange: (e) => Z("tags", e)
|
|
203
405
|
})
|
|
204
406
|
]
|
|
205
407
|
});
|
|
206
408
|
}
|
|
207
409
|
//#endregion
|
|
208
|
-
export {
|
|
410
|
+
export { F as default };
|
|
209
411
|
|
|
210
412
|
//# sourceMappingURL=PipelineFormPage.js.map
|