@burdenoff/microfe-cosmicintersection 2026.525.1 → 2026.704.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cosmicintersection/CosmicIntersectionRoot.js +38 -113
- package/dist/cosmicintersection/CosmicIntersectionRoot.js.map +1 -1
- package/dist/cosmicintersection/CosmicIntersectionRoutes.js +102 -44
- package/dist/cosmicintersection/CosmicIntersectionRoutes.js.map +1 -1
- package/dist/cosmicintersection/api/cosmicApi.js +853 -0
- package/dist/cosmicintersection/api/cosmicApi.js.map +1 -0
- package/dist/cosmicintersection/components/ActionMenu.js +106 -0
- package/dist/cosmicintersection/components/ActionMenu.js.map +1 -0
- package/dist/cosmicintersection/components/EmptyState.js +35 -0
- package/dist/cosmicintersection/components/EmptyState.js.map +1 -0
- package/dist/cosmicintersection/components/EntityFormShell.js +57 -0
- package/dist/cosmicintersection/components/EntityFormShell.js.map +1 -0
- package/dist/cosmicintersection/components/EntityTagsField.js +77 -0
- package/dist/cosmicintersection/components/EntityTagsField.js.map +1 -0
- package/dist/cosmicintersection/components/ErrorState.js +40 -0
- package/dist/cosmicintersection/components/ErrorState.js.map +1 -0
- package/dist/cosmicintersection/components/FileUploadField.js +146 -0
- package/dist/cosmicintersection/components/FileUploadField.js.map +1 -0
- package/dist/cosmicintersection/components/LoadingState.js +23 -0
- package/dist/cosmicintersection/components/LoadingState.js.map +1 -0
- package/dist/cosmicintersection/components/PageLayout.js +46 -0
- package/dist/cosmicintersection/components/PageLayout.js.map +1 -0
- package/dist/cosmicintersection/components/Pagination.js +65 -0
- package/dist/cosmicintersection/components/Pagination.js.map +1 -0
- package/dist/cosmicintersection/components/SafeLink.js +35 -0
- package/dist/cosmicintersection/components/SafeLink.js.map +1 -0
- package/dist/cosmicintersection/components/StatusBadge.js +53 -0
- package/dist/cosmicintersection/components/StatusBadge.js.map +1 -0
- package/dist/cosmicintersection/components/index.js +11 -0
- package/dist/cosmicintersection/context/CosmicIntersectionContext.js +46 -0
- package/dist/cosmicintersection/context/CosmicIntersectionContext.js.map +1 -0
- package/dist/cosmicintersection/index.js +0 -2
- package/dist/cosmicintersection/lib/mutationResult.js +12 -0
- package/dist/cosmicintersection/lib/mutationResult.js.map +1 -0
- package/dist/cosmicintersection/lib/toast.js +36 -0
- package/dist/cosmicintersection/lib/toast.js.map +1 -0
- package/dist/cosmicintersection/pages/access/AccessPage.js +340 -0
- package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -0
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.js +484 -0
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -0
- package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js +164 -0
- package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js.map +1 -0
- package/dist/cosmicintersection/pages/collections/CollectionFormPage.js +117 -0
- package/dist/cosmicintersection/pages/collections/CollectionFormPage.js.map +1 -0
- package/dist/cosmicintersection/pages/collections/CollectionsPage.js +103 -0
- package/dist/cosmicintersection/pages/collections/CollectionsPage.js.map +1 -0
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.js +244 -0
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -0
- package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js +460 -0
- package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js.map +1 -0
- package/dist/cosmicintersection/pages/datasets/DatasetFormPage.js +218 -0
- package/dist/cosmicintersection/pages/datasets/DatasetFormPage.js.map +1 -0
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.js +252 -0
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -0
- package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js +393 -0
- package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js.map +1 -0
- package/dist/cosmicintersection/pages/notebooks/NotebookFormPage.js +313 -0
- package/dist/cosmicintersection/pages/notebooks/NotebookFormPage.js.map +1 -0
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js +203 -0
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -0
- package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js +244 -0
- package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js.map +1 -0
- package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js +210 -0
- package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js.map +1 -0
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js +193 -0
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -0
- package/dist/cosmicintersection/pages/releases/ReleaseFormPage.js +184 -0
- package/dist/cosmicintersection/pages/releases/ReleaseFormPage.js.map +1 -0
- package/dist/cosmicintersection/pages/releases/ReleasesPage.js +368 -0
- package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -0
- package/dist/cosmicintersection/pages/runs/RunDetailPage.js +375 -0
- package/dist/cosmicintersection/pages/runs/RunDetailPage.js.map +1 -0
- package/dist/cosmicintersection/pages/runs/RunsPage.js +180 -0
- package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -0
- package/dist/cosmicintersection/pages/sources/SourceFormPage.js +229 -0
- package/dist/cosmicintersection/pages/sources/SourceFormPage.js.map +1 -0
- package/dist/cosmicintersection/pages/sources/SourcesPage.js +217 -0
- package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -0
- package/dist/cosmicintersection/utils/cn.js +10 -0
- package/dist/cosmicintersection/utils/cn.js.map +1 -0
- package/dist/index.js +2 -4
- package/dist/node_modules/clsx/dist/clsx.js +18 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/package.json +2 -1
- package/dist/cosmicintersection/pages/AiDiscoveryPage.js +0 -363
- package/dist/cosmicintersection/pages/AiDiscoveryPage.js.map +0 -1
- package/dist/cosmicintersection/pages/DashboardPage.js +0 -306
- package/dist/cosmicintersection/pages/DashboardPage.js.map +0 -1
- package/dist/cosmicintersection/pages/SpaceScienceCitizenPage.js +0 -287
- package/dist/cosmicintersection/pages/SpaceScienceCitizenPage.js.map +0 -1
- package/dist/cosmicintersection/pages/SpaceScienceDataPage.js +0 -385
- package/dist/cosmicintersection/pages/SpaceScienceDataPage.js.map +0 -1
- package/dist/cosmicintersection/pages/SpaceScienceDataPage1.js +0 -420
- package/dist/cosmicintersection/pages/SpaceScienceDataPage1.js.map +0 -1
- package/dist/cosmicintersection/pages/SpaceScienceLearningPage.js +0 -309
- package/dist/cosmicintersection/pages/SpaceScienceLearningPage.js.map +0 -1
- package/dist/cosmicintersection/pages/SpaceScienceLearningPage1.js +0 -361
- package/dist/cosmicintersection/pages/SpaceScienceLearningPage1.js.map +0 -1
- package/dist/cosmicintersection/pages/SpaceSciencePage.js +0 -194
- package/dist/cosmicintersection/pages/SpaceSciencePage.js.map +0 -1
- package/dist/cosmicintersection/pages/SpaceScienceResearchPage.js +0 -290
- package/dist/cosmicintersection/pages/SpaceScienceResearchPage.js.map +0 -1
- package/dist/cosmicintersection/pages/SpaceScienceSimulationPage.js +0 -279
- package/dist/cosmicintersection/pages/SpaceScienceSimulationPage.js.map +0 -1
- package/dist/cosmicintersection/providers/CosmicIntersectionProvider.js +0 -27
- package/dist/cosmicintersection/providers/CosmicIntersectionProvider.js.map +0 -1
- package/dist/cosmicintersection/store/cosmicintersectionStore.js +0 -478
- package/dist/cosmicintersection/store/cosmicintersectionStore.js.map +0 -1
- package/dist/cosmicintersection/utils/i18n.js +0 -15
- package/dist/cosmicintersection/utils/i18n.js.map +0 -1
- package/dist/cosmicintersection/utils/navigation.js +0 -8
- package/dist/cosmicintersection/utils/navigation.js.map +0 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { useCosmicIntersection as e } from "../../context/CosmicIntersectionContext.js";
|
|
2
|
+
import { notify as t } from "../../lib/toast.js";
|
|
3
|
+
import { useCosArchivePipelineMutation as n, useCosPipelineQuery as ee, useCosRunsQuery as te, useCosStartRunMutation as r } from "../../api/cosmicApi.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 ne, { usePagination as re } from "../../components/Pagination.js";
|
|
9
|
+
import c, { formatEnumLabel as l } from "../../components/StatusBadge.js";
|
|
10
|
+
import "../../components/index.js";
|
|
11
|
+
import { mutationSucceeded as u } from "../../lib/mutationResult.js";
|
|
12
|
+
import { useMemo as d, useState as f } from "react";
|
|
13
|
+
import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
14
|
+
import { useParams as g } from "react-router-dom";
|
|
15
|
+
import { Archive as ie, Pencil as ae, Play as _ } from "lucide-react";
|
|
16
|
+
import { AlertDialog as oe, AlertDialogAction as se, AlertDialogCancel as v, AlertDialogContent as y, AlertDialogDescription as b, AlertDialogFooter as ce, AlertDialogHeader as x, AlertDialogTitle as S, Button as C, Card as w, CardContent as T, CardHeader as E, CardTitle as D, Table as le, TableBody as O, TableCell as k, TableHead as A, TableHeader as j, TableRow as M, buttonVariants as N } from "@burdenoff/fe-libs/ui";
|
|
17
|
+
//#region src/cosmicintersection/pages/pipelines/PipelineDetailPage.tsx
|
|
18
|
+
function P(e) {
|
|
19
|
+
if (!e) return "—";
|
|
20
|
+
let t = new Date(e);
|
|
21
|
+
return Number.isNaN(t.getTime()) ? "—" : t.toLocaleString();
|
|
22
|
+
}
|
|
23
|
+
function F(e) {
|
|
24
|
+
if (e == null) return "—";
|
|
25
|
+
if (e < 1e3) return `${e} ms`;
|
|
26
|
+
let t = e / 1e3;
|
|
27
|
+
return t < 60 ? `${t.toFixed(1)} s` : `${Math.floor(t / 60)}m ${Math.round(t % 60)}s`;
|
|
28
|
+
}
|
|
29
|
+
function I({ label: e, children: t }) {
|
|
30
|
+
return /* @__PURE__ */ h("div", {
|
|
31
|
+
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",
|
|
32
|
+
children: [/* @__PURE__ */ m("span", {
|
|
33
|
+
className: "w-40 shrink-0 text-sm font-medium text-text-secondary",
|
|
34
|
+
children: e
|
|
35
|
+
}), /* @__PURE__ */ m("span", {
|
|
36
|
+
className: "min-w-0 text-sm text-text-primary",
|
|
37
|
+
children: t
|
|
38
|
+
})]
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function L() {
|
|
42
|
+
let { id: L = "" } = g(), { navigate: R } = e(), { data: z, loading: B, error: V, refetch: H } = ee(L), U = re({
|
|
43
|
+
pageSize: 10,
|
|
44
|
+
resetKey: L
|
|
45
|
+
}), { data: W, loading: G, error: ue, refetch: K } = te({
|
|
46
|
+
pipelineId: L,
|
|
47
|
+
pagination: {
|
|
48
|
+
limit: U.limit,
|
|
49
|
+
offset: U.offset
|
|
50
|
+
}
|
|
51
|
+
}), [de, { loading: q }] = r(), [fe, { loading: J }] = n(), [pe, Y] = f(!1), X = z?.cosPipeline, Z = d(() => W?.cosListRuns.items ?? [], [W]), me = W?.cosListRuns.pageInfo, Q = async () => {
|
|
52
|
+
if (!X) return;
|
|
53
|
+
let e = await de({ variables: { input: { pipelineId: X.id } } }), n = e.data?.cosStartRun?.id;
|
|
54
|
+
u(e, n, "Could not start a run") && (t.success("Run started", "A new run was queued."), await Promise.all([H(), K()]), n && R(`runs/${n}`));
|
|
55
|
+
}, he = async () => {
|
|
56
|
+
if (!X) return;
|
|
57
|
+
let e = await fe({ variables: { id: X.id } });
|
|
58
|
+
u(e, e.data?.cosArchivePipeline, "Could not archive pipeline") && (Y(!1), t.success("Pipeline archived", `“${X.name}” was archived.`), R("pipelines"));
|
|
59
|
+
};
|
|
60
|
+
if (B) return /* @__PURE__ */ m(s, {
|
|
61
|
+
title: "Pipeline",
|
|
62
|
+
backPath: "pipelines",
|
|
63
|
+
children: /* @__PURE__ */ m(o, { label: "Loading pipeline…" })
|
|
64
|
+
});
|
|
65
|
+
if (V) return /* @__PURE__ */ m(s, {
|
|
66
|
+
title: "Pipeline",
|
|
67
|
+
backPath: "pipelines",
|
|
68
|
+
children: /* @__PURE__ */ m(a, { onRetry: () => void H() })
|
|
69
|
+
});
|
|
70
|
+
if (!X) return /* @__PURE__ */ m(s, {
|
|
71
|
+
title: "Pipeline",
|
|
72
|
+
backPath: "pipelines",
|
|
73
|
+
children: /* @__PURE__ */ m(i, {
|
|
74
|
+
title: "Pipeline not found",
|
|
75
|
+
description: "This pipeline may have been archived or you may not have access to it.",
|
|
76
|
+
action: /* @__PURE__ */ m(C, {
|
|
77
|
+
type: "button",
|
|
78
|
+
variant: "outline",
|
|
79
|
+
onClick: () => R("pipelines"),
|
|
80
|
+
children: "Back to pipelines"
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
});
|
|
84
|
+
let $ = X.status === "ARCHIVED";
|
|
85
|
+
return /* @__PURE__ */ h(s, {
|
|
86
|
+
title: X.name,
|
|
87
|
+
description: X.description ?? void 0,
|
|
88
|
+
backPath: "pipelines",
|
|
89
|
+
actions: /* @__PURE__ */ h("div", {
|
|
90
|
+
className: "flex flex-wrap items-center gap-2",
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ h(C, {
|
|
93
|
+
type: "button",
|
|
94
|
+
onClick: () => void Q(),
|
|
95
|
+
disabled: q || $,
|
|
96
|
+
children: [/* @__PURE__ */ m(_, { className: "size-4" }), q ? "Starting…" : "Start run"]
|
|
97
|
+
}),
|
|
98
|
+
/* @__PURE__ */ h(C, {
|
|
99
|
+
type: "button",
|
|
100
|
+
variant: "outline",
|
|
101
|
+
onClick: () => R(`pipelines/${X.id}/edit`),
|
|
102
|
+
children: [/* @__PURE__ */ m(ae, { className: "size-4" }), "Edit"]
|
|
103
|
+
}),
|
|
104
|
+
$ ? null : /* @__PURE__ */ h(C, {
|
|
105
|
+
type: "button",
|
|
106
|
+
variant: "outline",
|
|
107
|
+
onClick: () => Y(!0),
|
|
108
|
+
children: [/* @__PURE__ */ m(ie, { className: "size-4" }), "Archive"]
|
|
109
|
+
})
|
|
110
|
+
]
|
|
111
|
+
}),
|
|
112
|
+
children: [/* @__PURE__ */ h("div", {
|
|
113
|
+
className: "space-y-6",
|
|
114
|
+
children: [
|
|
115
|
+
/* @__PURE__ */ h(w, { children: [/* @__PURE__ */ m(E, { children: /* @__PURE__ */ m(D, { children: "Overview" }) }), /* @__PURE__ */ h(T, { children: [
|
|
116
|
+
/* @__PURE__ */ m(I, {
|
|
117
|
+
label: "Status",
|
|
118
|
+
children: /* @__PURE__ */ m(c, { status: X.status })
|
|
119
|
+
}),
|
|
120
|
+
/* @__PURE__ */ m(I, {
|
|
121
|
+
label: "Trigger",
|
|
122
|
+
children: l(X.trigger)
|
|
123
|
+
}),
|
|
124
|
+
/* @__PURE__ */ m(I, {
|
|
125
|
+
label: "Schedule",
|
|
126
|
+
children: X.schedule ? /* @__PURE__ */ m("span", {
|
|
127
|
+
className: "font-mono text-xs",
|
|
128
|
+
children: X.schedule
|
|
129
|
+
}) : "—"
|
|
130
|
+
}),
|
|
131
|
+
/* @__PURE__ */ h(I, {
|
|
132
|
+
label: "Version",
|
|
133
|
+
children: ["v", X.version]
|
|
134
|
+
}),
|
|
135
|
+
/* @__PURE__ */ m(I, {
|
|
136
|
+
label: "Last run",
|
|
137
|
+
children: P(X.lastRunAt)
|
|
138
|
+
}),
|
|
139
|
+
/* @__PURE__ */ m(I, {
|
|
140
|
+
label: "Created",
|
|
141
|
+
children: P(X.createdAt)
|
|
142
|
+
}),
|
|
143
|
+
/* @__PURE__ */ m(I, {
|
|
144
|
+
label: "Tags",
|
|
145
|
+
children: X.tags.length === 0 ? "—" : /* @__PURE__ */ m("span", {
|
|
146
|
+
className: "flex flex-wrap gap-1",
|
|
147
|
+
children: X.tags.map((e) => /* @__PURE__ */ m("span", {
|
|
148
|
+
className: "rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
|
|
149
|
+
children: e
|
|
150
|
+
}, e))
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
] })] }),
|
|
154
|
+
/* @__PURE__ */ h(w, { children: [/* @__PURE__ */ m(E, { children: /* @__PURE__ */ m(D, { children: "Definition" }) }), /* @__PURE__ */ m(T, { children: X.definition && Object.keys(X.definition).length > 0 ? /* @__PURE__ */ m("pre", {
|
|
155
|
+
className: "overflow-x-auto rounded-card border border-border-default bg-bg-sunken p-4 font-mono text-xs text-text-primary",
|
|
156
|
+
children: JSON.stringify(X.definition, null, 2)
|
|
157
|
+
}) : /* @__PURE__ */ m("p", {
|
|
158
|
+
className: "text-sm text-text-secondary",
|
|
159
|
+
children: "No definition configured yet. Edit the pipeline to describe its steps."
|
|
160
|
+
}) })] }),
|
|
161
|
+
/* @__PURE__ */ h(w, { children: [/* @__PURE__ */ m(E, { children: /* @__PURE__ */ m(D, { children: "Runs" }) }), /* @__PURE__ */ m(T, { children: G ? /* @__PURE__ */ m(o, {
|
|
162
|
+
variant: "rows",
|
|
163
|
+
rows: 4,
|
|
164
|
+
label: "Loading runs…"
|
|
165
|
+
}) : ue ? /* @__PURE__ */ m(a, {
|
|
166
|
+
message: "Could not load runs.",
|
|
167
|
+
onRetry: () => void K()
|
|
168
|
+
}) : Z.length === 0 ? /* @__PURE__ */ m(i, {
|
|
169
|
+
icon: _,
|
|
170
|
+
title: "No runs yet",
|
|
171
|
+
description: "Start a run to process data through this pipeline.",
|
|
172
|
+
action: /* @__PURE__ */ h(C, {
|
|
173
|
+
type: "button",
|
|
174
|
+
onClick: () => void Q(),
|
|
175
|
+
disabled: $,
|
|
176
|
+
children: [/* @__PURE__ */ m(_, { className: "size-4" }), "Start run"]
|
|
177
|
+
})
|
|
178
|
+
}) : /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("div", {
|
|
179
|
+
className: "overflow-x-auto rounded-card border border-border-default",
|
|
180
|
+
children: /* @__PURE__ */ h(le, { children: [/* @__PURE__ */ m(j, { children: /* @__PURE__ */ h(M, { children: [
|
|
181
|
+
/* @__PURE__ */ m(A, { children: "Run" }),
|
|
182
|
+
/* @__PURE__ */ m(A, { children: "Status" }),
|
|
183
|
+
/* @__PURE__ */ m(A, { children: "Started" }),
|
|
184
|
+
/* @__PURE__ */ m(A, { children: "Duration" }),
|
|
185
|
+
/* @__PURE__ */ m(A, { children: "Triggered by" })
|
|
186
|
+
] }) }), /* @__PURE__ */ m(O, { children: Z.map((e) => /* @__PURE__ */ h(M, {
|
|
187
|
+
className: "cursor-pointer",
|
|
188
|
+
onClick: () => R(`runs/${e.id}`),
|
|
189
|
+
children: [
|
|
190
|
+
/* @__PURE__ */ h(k, {
|
|
191
|
+
className: "font-medium text-text-primary",
|
|
192
|
+
children: ["#", e.runNumber]
|
|
193
|
+
}),
|
|
194
|
+
/* @__PURE__ */ m(k, { children: /* @__PURE__ */ m(c, { status: e.status }) }),
|
|
195
|
+
/* @__PURE__ */ m(k, {
|
|
196
|
+
className: "whitespace-nowrap text-text-secondary",
|
|
197
|
+
children: P(e.startedAt)
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ m(k, {
|
|
200
|
+
className: "tabular-nums text-text-secondary",
|
|
201
|
+
children: F(e.durationMs)
|
|
202
|
+
}),
|
|
203
|
+
/* @__PURE__ */ m(k, {
|
|
204
|
+
className: "text-text-secondary",
|
|
205
|
+
children: e.triggeredBy || "—"
|
|
206
|
+
})
|
|
207
|
+
]
|
|
208
|
+
}, e.id)) })] })
|
|
209
|
+
}), /* @__PURE__ */ m(ne, {
|
|
210
|
+
offset: U.offset,
|
|
211
|
+
limit: U.limit,
|
|
212
|
+
loadedCount: Z.length,
|
|
213
|
+
hasMore: me?.hasMore ?? !1,
|
|
214
|
+
onPrev: U.prevPage,
|
|
215
|
+
onNext: U.nextPage,
|
|
216
|
+
itemLabel: "runs",
|
|
217
|
+
loading: G
|
|
218
|
+
})] }) })] })
|
|
219
|
+
]
|
|
220
|
+
}), /* @__PURE__ */ m(oe, {
|
|
221
|
+
open: pe,
|
|
222
|
+
onOpenChange: (e) => J ? void 0 : Y(e),
|
|
223
|
+
children: /* @__PURE__ */ h(y, { children: [/* @__PURE__ */ h(x, { children: [/* @__PURE__ */ h(S, { children: [
|
|
224
|
+
"Archive “",
|
|
225
|
+
X.name,
|
|
226
|
+
"”?"
|
|
227
|
+
] }), /* @__PURE__ */ m(b, { children: "Archiving stops the pipeline from being triggered. Existing runs are kept for reference." })] }), /* @__PURE__ */ h(ce, { children: [/* @__PURE__ */ m(v, {
|
|
228
|
+
disabled: J,
|
|
229
|
+
children: "Cancel"
|
|
230
|
+
}), /* @__PURE__ */ m(se, {
|
|
231
|
+
onClick: (e) => {
|
|
232
|
+
e.preventDefault(), he();
|
|
233
|
+
},
|
|
234
|
+
disabled: J,
|
|
235
|
+
className: N({ variant: "destructive" }),
|
|
236
|
+
children: J ? "Archiving…" : "Archive pipeline"
|
|
237
|
+
})] })] })
|
|
238
|
+
})]
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
export { L as default };
|
|
243
|
+
|
|
244
|
+
//# sourceMappingURL=PipelineDetailPage.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { useCosmicIntersection as e } from "../../context/CosmicIntersectionContext.js";
|
|
2
|
+
import { notify as t } from "../../lib/toast.js";
|
|
3
|
+
import { useCosCreatePipelineMutation as n, useCosPipelineQuery as r, useCosUpdatePipelineMutation as i } from "../../api/cosmicApi.js";
|
|
4
|
+
import a from "../../components/EntityFormShell.js";
|
|
5
|
+
import o from "../../components/EntityTagsField.js";
|
|
6
|
+
import { formatEnumLabel as s } from "../../components/StatusBadge.js";
|
|
7
|
+
import "../../components/index.js";
|
|
8
|
+
import { describeError as c, mutationSucceeded as l } from "../../lib/mutationResult.js";
|
|
9
|
+
import { useState as u } from "react";
|
|
10
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
11
|
+
import { useParams as p } from "react-router-dom";
|
|
12
|
+
import { Input as m, Label as h, Select as g, SelectContent as _, SelectItem as v, SelectTrigger as y, SelectValue as b, Textarea as x } from "@burdenoff/fe-libs/ui";
|
|
13
|
+
//#region src/cosmicintersection/pages/pipelines/PipelineFormPage.tsx
|
|
14
|
+
var S = [
|
|
15
|
+
"MANUAL",
|
|
16
|
+
"SCHEDULED",
|
|
17
|
+
"ON_INGEST",
|
|
18
|
+
"WEBHOOK"
|
|
19
|
+
], C = {
|
|
20
|
+
name: "",
|
|
21
|
+
description: "",
|
|
22
|
+
trigger: "MANUAL",
|
|
23
|
+
schedule: "",
|
|
24
|
+
definition: "",
|
|
25
|
+
tags: []
|
|
26
|
+
};
|
|
27
|
+
function w(e) {
|
|
28
|
+
let t = e.trim();
|
|
29
|
+
if (!t) return;
|
|
30
|
+
let n = JSON.parse(t);
|
|
31
|
+
if (typeof n != "object" || !n || Array.isArray(n)) throw Error("Definition must be a JSON object.");
|
|
32
|
+
return n;
|
|
33
|
+
}
|
|
34
|
+
function T() {
|
|
35
|
+
let { id: T = "" } = p(), E = !!T, { navigate: D } = e(), { data: O, loading: k } = r(T), [A, { loading: j }] = n(), [M, { loading: N }] = i(), [P, F] = u(C), I = E ? O?.cosPipeline : null, [L, R] = u(null);
|
|
36
|
+
I && I.id !== L && (R(I.id), F({
|
|
37
|
+
name: I.name ?? "",
|
|
38
|
+
description: I.description ?? "",
|
|
39
|
+
trigger: I.trigger ?? "MANUAL",
|
|
40
|
+
schedule: I.schedule ?? "",
|
|
41
|
+
definition: I.definition ? JSON.stringify(I.definition, null, 2) : "",
|
|
42
|
+
tags: I.tags ?? []
|
|
43
|
+
}));
|
|
44
|
+
let z = (e, t) => {
|
|
45
|
+
F((n) => ({
|
|
46
|
+
...n,
|
|
47
|
+
[e]: t
|
|
48
|
+
}));
|
|
49
|
+
};
|
|
50
|
+
return /* @__PURE__ */ f(a, {
|
|
51
|
+
title: E ? "Edit pipeline" : "New pipeline",
|
|
52
|
+
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 describe its steps as a JSON definition.",
|
|
54
|
+
backLabel: E ? "Back to pipeline" : "Back to pipelines",
|
|
55
|
+
onBack: () => D(E ? `pipelines/${T}` : "pipelines"),
|
|
56
|
+
onSubmit: async (e) => {
|
|
57
|
+
if (e.preventDefault(), !P.name.trim()) {
|
|
58
|
+
t.error("Name is required", "Give the pipeline a name before saving.");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let n;
|
|
62
|
+
try {
|
|
63
|
+
n = w(P.definition);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
t.error("Invalid definition JSON", c(e) ?? "Please provide valid JSON.");
|
|
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,
|
|
73
|
+
input: {
|
|
74
|
+
name: P.name.trim(),
|
|
75
|
+
description: P.description.trim() || void 0,
|
|
76
|
+
trigger: P.trigger,
|
|
77
|
+
schedule: r || void 0,
|
|
78
|
+
definition: n,
|
|
79
|
+
tags: P.tags
|
|
80
|
+
}
|
|
81
|
+
} });
|
|
82
|
+
if (!l(e, e.data?.cosUpdatePipeline?.id, "Could not update pipeline")) return;
|
|
83
|
+
t.success("Pipeline updated", `“${P.name.trim()}” was saved.`), D(`pipelines/${T}`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
let e = await A({ variables: { input: {
|
|
87
|
+
name: P.name.trim(),
|
|
88
|
+
description: P.description.trim() || void 0,
|
|
89
|
+
trigger: P.trigger,
|
|
90
|
+
schedule: r || void 0,
|
|
91
|
+
definition: n,
|
|
92
|
+
tags: P.tags
|
|
93
|
+
} } }), i = e.data?.cosCreatePipeline?.id;
|
|
94
|
+
if (!l(e, i, "Could not create pipeline")) return;
|
|
95
|
+
t.success("Pipeline created", `“${P.name.trim()}” is ready.`), D(i ? `pipelines/${i}` : "pipelines");
|
|
96
|
+
} catch (e) {
|
|
97
|
+
t.error("Could not save pipeline", c(e));
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
submitLabel: E ? "Save changes" : "Create pipeline",
|
|
101
|
+
saving: j || N,
|
|
102
|
+
submitDisabled: E && k,
|
|
103
|
+
children: [
|
|
104
|
+
/* @__PURE__ */ f("div", {
|
|
105
|
+
className: "space-y-1",
|
|
106
|
+
children: [/* @__PURE__ */ d(h, {
|
|
107
|
+
htmlFor: "pipeline-name",
|
|
108
|
+
children: "Name"
|
|
109
|
+
}), /* @__PURE__ */ d(m, {
|
|
110
|
+
id: "pipeline-name",
|
|
111
|
+
value: P.name,
|
|
112
|
+
onChange: (e) => z("name", e.target.value),
|
|
113
|
+
placeholder: "e.g. Kepler DR3 ingest",
|
|
114
|
+
required: !0
|
|
115
|
+
})]
|
|
116
|
+
}),
|
|
117
|
+
/* @__PURE__ */ f("div", {
|
|
118
|
+
className: "space-y-1",
|
|
119
|
+
children: [/* @__PURE__ */ d(h, {
|
|
120
|
+
htmlFor: "pipeline-description",
|
|
121
|
+
children: "Description"
|
|
122
|
+
}), /* @__PURE__ */ d(x, {
|
|
123
|
+
id: "pipeline-description",
|
|
124
|
+
value: P.description,
|
|
125
|
+
onChange: (e) => z("description", e.target.value),
|
|
126
|
+
rows: 3,
|
|
127
|
+
placeholder: "What this pipeline does and when it runs."
|
|
128
|
+
})]
|
|
129
|
+
}),
|
|
130
|
+
/* @__PURE__ */ f("div", {
|
|
131
|
+
className: "grid grid-cols-1 gap-4 md:grid-cols-2",
|
|
132
|
+
children: [/* @__PURE__ */ f("div", {
|
|
133
|
+
className: "space-y-1",
|
|
134
|
+
children: [/* @__PURE__ */ d(h, {
|
|
135
|
+
htmlFor: "pipeline-trigger",
|
|
136
|
+
children: "Trigger"
|
|
137
|
+
}), /* @__PURE__ */ f(g, {
|
|
138
|
+
value: P.trigger,
|
|
139
|
+
onValueChange: (e) => z("trigger", e),
|
|
140
|
+
children: [/* @__PURE__ */ d(y, {
|
|
141
|
+
id: "pipeline-trigger",
|
|
142
|
+
"aria-label": "Trigger",
|
|
143
|
+
children: /* @__PURE__ */ d(b, {})
|
|
144
|
+
}), /* @__PURE__ */ d(_, { children: S.map((e) => /* @__PURE__ */ d(v, {
|
|
145
|
+
value: e,
|
|
146
|
+
children: s(e)
|
|
147
|
+
}, e)) })]
|
|
148
|
+
})]
|
|
149
|
+
}), P.trigger === "SCHEDULED" ? /* @__PURE__ */ f("div", {
|
|
150
|
+
className: "space-y-1",
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ d(h, {
|
|
153
|
+
htmlFor: "pipeline-schedule",
|
|
154
|
+
children: "Schedule (cron)"
|
|
155
|
+
}),
|
|
156
|
+
/* @__PURE__ */ d(m, {
|
|
157
|
+
id: "pipeline-schedule",
|
|
158
|
+
value: P.schedule,
|
|
159
|
+
onChange: (e) => z("schedule", e.target.value),
|
|
160
|
+
placeholder: "0 2 * * *",
|
|
161
|
+
className: "font-mono"
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ f("p", {
|
|
164
|
+
className: "text-xs text-text-secondary",
|
|
165
|
+
children: [
|
|
166
|
+
"Standard 5-field cron expression, e.g. ",
|
|
167
|
+
/* @__PURE__ */ d("span", {
|
|
168
|
+
className: "font-mono",
|
|
169
|
+
children: "0 2 * * *"
|
|
170
|
+
}),
|
|
171
|
+
" ",
|
|
172
|
+
"for daily at 02:00."
|
|
173
|
+
]
|
|
174
|
+
})
|
|
175
|
+
]
|
|
176
|
+
}) : null]
|
|
177
|
+
}),
|
|
178
|
+
/* @__PURE__ */ f("div", {
|
|
179
|
+
className: "space-y-1",
|
|
180
|
+
children: [
|
|
181
|
+
/* @__PURE__ */ d(h, {
|
|
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
|
+
}),
|
|
194
|
+
/* @__PURE__ */ d("p", {
|
|
195
|
+
className: "text-xs text-text-secondary",
|
|
196
|
+
children: "Optional. A JSON object describing the pipeline steps. Leave blank to configure steps later."
|
|
197
|
+
})
|
|
198
|
+
]
|
|
199
|
+
}),
|
|
200
|
+
/* @__PURE__ */ d(o, {
|
|
201
|
+
value: P.tags,
|
|
202
|
+
onChange: (e) => z("tags", e)
|
|
203
|
+
})
|
|
204
|
+
]
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
export { T as default };
|
|
209
|
+
|
|
210
|
+
//# sourceMappingURL=PipelineFormPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PipelineFormPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/pipelines/PipelineFormPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\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 useCosCreatePipelineMutation,\n useCosPipelineQuery,\n useCosUpdatePipelineMutation,\n type CosPipelineTrigger,\n} from '../../api/cosmicApi';\nimport { EntityFormShell, EntityTagsField, formatEnumLabel } from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\nconst TRIGGER_OPTIONS: CosPipelineTrigger[] = ['MANUAL', 'SCHEDULED', 'ON_INGEST', 'WEBHOOK'];\n\ninterface PipelineFormState {\n name: string;\n description: string;\n trigger: CosPipelineTrigger;\n schedule: string;\n definition: string;\n tags: string[];\n}\n\nconst INITIAL_STATE: PipelineFormState = {\n name: '',\n description: '',\n trigger: 'MANUAL',\n schedule: '',\n definition: '',\n tags: [],\n};\n\n/** Parse the definition textarea into a JSON object, or throw a readable error. */\nfunction parseDefinition(raw: string): Record<string, unknown> | undefined {\n const trimmed = raw.trim();\n if (!trimmed) {\n return undefined;\n }\n const parsed = JSON.parse(trimmed) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n throw new Error('Definition must be a JSON object.');\n }\n return parsed as Record<string, unknown>;\n}\n\nexport default function PipelineFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = Boolean(id);\n const { navigate } = useCosmicIntersection();\n\n const { data, loading: loadingPipeline } = useCosPipelineQuery(id);\n const [createPipeline, { loading: creating }] = useCosCreatePipelineMutation();\n const [updatePipeline, { loading: updating }] = useCosUpdatePipelineMutation();\n\n const [form, setForm] = useState<PipelineFormState>(INITIAL_STATE);\n\n // Hydrate the form once the edited pipeline loads. Derive-during-render (the\n // documented React pattern) keyed on the loaded id so a later refetch of the\n // same record never clobbers the user's in-progress edits.\n const loaded = isEdit ? data?.cosPipeline : null;\n const [hydratedId, setHydratedId] = useState<string | null>(null);\n if (loaded && loaded.id !== hydratedId) {\n setHydratedId(loaded.id);\n setForm({\n name: loaded.name ?? '',\n description: loaded.description ?? '',\n trigger: loaded.trigger ?? 'MANUAL',\n schedule: loaded.schedule ?? '',\n definition: loaded.definition ? JSON.stringify(loaded.definition, null, 2) : '',\n tags: loaded.tags ?? [],\n });\n }\n\n const setField = <K extends keyof PipelineFormState>(field: K, value: PipelineFormState[K]) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.name.trim()) {\n notify.error('Name is required', 'Give the pipeline a name before saving.');\n return;\n }\n\n let definition: Record<string, unknown> | undefined;\n try {\n definition = parseDefinition(form.definition);\n } catch (error) {\n notify.error('Invalid definition JSON', describeError(error) ?? 'Please provide valid JSON.');\n return;\n }\n\n const schedule = form.trigger === 'SCHEDULED' ? form.schedule.trim() : '';\n\n try {\n if (isEdit) {\n const response = await updatePipeline({\n variables: {\n id,\n input: {\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n trigger: form.trigger,\n schedule: schedule || undefined,\n definition,\n tags: form.tags,\n },\n },\n });\n if (\n !mutationSucceeded(\n response,\n response.data?.cosUpdatePipeline?.id,\n 'Could not update pipeline'\n )\n ) {\n return;\n }\n notify.success('Pipeline updated', `“${form.name.trim()}” was saved.`);\n navigate(`pipelines/${id}`);\n return;\n }\n\n const response = await createPipeline({\n variables: {\n input: {\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n trigger: form.trigger,\n schedule: schedule || undefined,\n definition,\n tags: form.tags,\n },\n },\n });\n const createdId = response.data?.cosCreatePipeline?.id;\n if (!mutationSucceeded(response, createdId, 'Could not create pipeline')) {\n return;\n }\n notify.success('Pipeline created', `“${form.name.trim()}” is ready.`);\n navigate(createdId ? `pipelines/${createdId}` : 'pipelines');\n } catch (error) {\n notify.error('Could not save pipeline', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit pipeline' : 'New pipeline'}\n description=\"Define how raw data is ingested, processed and released.\"\n 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 describe its steps as a JSON definition.\"\n backLabel={isEdit ? 'Back to pipeline' : 'Back to pipelines'}\n onBack={() => navigate(isEdit ? `pipelines/${id}` : 'pipelines')}\n onSubmit={onSubmit}\n submitLabel={isEdit ? 'Save changes' : 'Create pipeline'}\n saving={saving}\n submitDisabled={isEdit && loadingPipeline}\n >\n <div className=\"space-y-1\">\n <Label htmlFor=\"pipeline-name\">Name</Label>\n <Input\n id=\"pipeline-name\"\n value={form.name}\n onChange={(event) => setField('name', event.target.value)}\n placeholder=\"e.g. Kepler DR3 ingest\"\n required\n />\n </div>\n\n <div className=\"space-y-1\">\n <Label htmlFor=\"pipeline-description\">Description</Label>\n <Textarea\n id=\"pipeline-description\"\n value={form.description}\n onChange={(event) => setField('description', event.target.value)}\n rows={3}\n placeholder=\"What this pipeline does and when it runs.\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <div className=\"space-y-1\">\n <Label htmlFor=\"pipeline-trigger\">Trigger</Label>\n <Select\n value={form.trigger}\n onValueChange={(value) => setField('trigger', value as CosPipelineTrigger)}\n >\n <SelectTrigger id=\"pipeline-trigger\" aria-label=\"Trigger\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TRIGGER_OPTIONS.map((option) => (\n <SelectItem key={option} value={option}>\n {formatEnumLabel(option)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {form.trigger === 'SCHEDULED' ? (\n <div className=\"space-y-1\">\n <Label htmlFor=\"pipeline-schedule\">Schedule (cron)</Label>\n <Input\n id=\"pipeline-schedule\"\n value={form.schedule}\n onChange={(event) => setField('schedule', event.target.value)}\n placeholder=\"0 2 * * *\"\n className=\"font-mono\"\n />\n <p className=\"text-xs text-text-secondary\">\n Standard 5-field cron expression, e.g. <span className=\"font-mono\">0 2 * * *</span>{' '}\n for daily at 02:00.\n </p>\n </div>\n ) : null}\n </div>\n\n <div className=\"space-y-1\">\n <Label htmlFor=\"pipeline-definition\">Definition (JSON)</Label>\n <Textarea\n id=\"pipeline-definition\"\n value={form.definition}\n onChange={(event) => setField('definition', event.target.value)}\n rows={8}\n spellCheck={false}\n className=\"font-mono text-xs\"\n placeholder={\n '{\\n \"steps\": [\\n { \"kind\": \"INGEST\", \"name\": \"Fetch raw frames\" }\\n ]\\n}'\n }\n />\n <p className=\"text-xs text-text-secondary\">\n Optional. A JSON object describing the pipeline steps. Leave blank to configure steps\n later.\n </p>\n </div>\n\n <EntityTagsField value={form.tags} onChange={(tags) => setField('tags', tags)} />\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
|