@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
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { useCosmicIntersection as e } from "../../context/CosmicIntersectionContext.js";
|
|
2
|
+
import { notify as t } from "../../lib/toast.js";
|
|
3
|
+
import { useCosArchiveSourceMutation as n, useCosMarkSourceSyncedMutation as r, useCosSourceQuery as i } from "../../api/cosmicApi.js";
|
|
4
|
+
import a from "../../components/ActionMenu.js";
|
|
5
|
+
import o from "../../components/EmptyState.js";
|
|
6
|
+
import s from "../../components/ErrorState.js";
|
|
7
|
+
import c from "../../components/LoadingState.js";
|
|
8
|
+
import l from "../../components/SafeLink.js";
|
|
9
|
+
import u from "../../components/PageLayout.js";
|
|
10
|
+
import d, { formatEnumLabel as f } from "../../components/StatusBadge.js";
|
|
11
|
+
import "../../components/index.js";
|
|
12
|
+
import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
13
|
+
import { useParams as g } from "react-router-dom";
|
|
14
|
+
import { Database as _, Download as v, Pencil as y, Radio as b, RefreshCw as x } from "lucide-react";
|
|
15
|
+
import { Badge as S, Button as C, Table as w, TableBody as T, TableCell as E, TableHead as D, TableHeader as O, TableRow as k } from "@burdenoff/fe-libs/ui";
|
|
16
|
+
//#region src/cosmicintersection/pages/sources/SourceDetailPage.tsx
|
|
17
|
+
function A(e) {
|
|
18
|
+
if (!e) return "—";
|
|
19
|
+
let t = new Date(e);
|
|
20
|
+
return Number.isNaN(t.getTime()) ? "—" : t.toLocaleString();
|
|
21
|
+
}
|
|
22
|
+
function j(e) {
|
|
23
|
+
if (!e) return "—";
|
|
24
|
+
let t = new Date(e);
|
|
25
|
+
return Number.isNaN(t.getTime()) ? "—" : t.toLocaleDateString(void 0, {
|
|
26
|
+
year: "numeric",
|
|
27
|
+
month: "short",
|
|
28
|
+
day: "numeric"
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function M({ label: e, value: t }) {
|
|
32
|
+
return /* @__PURE__ */ h("div", {
|
|
33
|
+
className: "rounded-card border border-border-default bg-bg-elevated p-4",
|
|
34
|
+
children: [/* @__PURE__ */ m("p", {
|
|
35
|
+
className: "text-xs font-medium uppercase tracking-wide text-text-secondary",
|
|
36
|
+
children: e
|
|
37
|
+
}), /* @__PURE__ */ m("div", {
|
|
38
|
+
className: "mt-1 text-sm text-text-primary",
|
|
39
|
+
children: t
|
|
40
|
+
})]
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function N() {
|
|
44
|
+
let { id: N = "" } = g(), { navigate: P } = e(), { data: F, loading: I, error: L, refetch: R } = i(N), z = F?.cosSource ?? null, [B, { loading: V }] = r(), [H] = n(), U = async () => {
|
|
45
|
+
z && (await B({ variables: { id: z.id } })).data?.cosMarkSourceSynced && (t.success("Source marked synced", `“${z.name}” is now up to date.`), await R());
|
|
46
|
+
}, W = async () => {
|
|
47
|
+
if (!z) return;
|
|
48
|
+
let e = await H({ variables: { id: z.id } });
|
|
49
|
+
if (e.error) throw Error(e.error.message);
|
|
50
|
+
if (!e.data?.cosArchiveSource) throw t.error("Could not archive source", "The server did not confirm the change."), Error("Archive not confirmed");
|
|
51
|
+
t.success("Source archived"), P("sources");
|
|
52
|
+
};
|
|
53
|
+
if (I) return /* @__PURE__ */ m(u, {
|
|
54
|
+
title: "Source",
|
|
55
|
+
backPath: "sources",
|
|
56
|
+
children: /* @__PURE__ */ m(c, { label: "Loading source…" })
|
|
57
|
+
});
|
|
58
|
+
if (L) return /* @__PURE__ */ m(u, {
|
|
59
|
+
title: "Source",
|
|
60
|
+
backPath: "sources",
|
|
61
|
+
children: /* @__PURE__ */ m(s, {
|
|
62
|
+
message: "We could not load this source.",
|
|
63
|
+
onRetry: () => void R()
|
|
64
|
+
})
|
|
65
|
+
});
|
|
66
|
+
if (!z) return /* @__PURE__ */ m(u, {
|
|
67
|
+
title: "Source",
|
|
68
|
+
backPath: "sources",
|
|
69
|
+
children: /* @__PURE__ */ m(o, {
|
|
70
|
+
icon: b,
|
|
71
|
+
title: "Source not found.",
|
|
72
|
+
description: "This source may have been removed or is outside your current scope.",
|
|
73
|
+
action: /* @__PURE__ */ m(C, {
|
|
74
|
+
type: "button",
|
|
75
|
+
onClick: () => P("sources"),
|
|
76
|
+
children: "Back to sources"
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
let G = z.datasets ?? [];
|
|
81
|
+
return /* @__PURE__ */ m(u, {
|
|
82
|
+
title: z.name,
|
|
83
|
+
description: z.description ?? void 0,
|
|
84
|
+
backPath: "sources",
|
|
85
|
+
actions: /* @__PURE__ */ h("div", {
|
|
86
|
+
className: "flex items-center gap-2",
|
|
87
|
+
children: [/* @__PURE__ */ h(C, {
|
|
88
|
+
type: "button",
|
|
89
|
+
variant: "outline",
|
|
90
|
+
onClick: () => P(`sources/${z.id}/edit`),
|
|
91
|
+
children: [/* @__PURE__ */ m(y, { className: "size-4" }), "Edit"]
|
|
92
|
+
}), /* @__PURE__ */ m(a, {
|
|
93
|
+
ariaLabel: `Actions for ${z.name}`,
|
|
94
|
+
entityName: z.name,
|
|
95
|
+
onDelete: () => W(),
|
|
96
|
+
deleteLabel: "Archive source",
|
|
97
|
+
extraItems: [{
|
|
98
|
+
key: "mark-synced",
|
|
99
|
+
label: V ? "Marking synced…" : "Mark synced",
|
|
100
|
+
icon: x,
|
|
101
|
+
disabled: V,
|
|
102
|
+
onSelect: () => void U()
|
|
103
|
+
}]
|
|
104
|
+
})]
|
|
105
|
+
}),
|
|
106
|
+
children: /* @__PURE__ */ h("div", {
|
|
107
|
+
className: "space-y-6",
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ h("div", {
|
|
110
|
+
className: "flex flex-wrap items-center gap-2",
|
|
111
|
+
children: [/* @__PURE__ */ m(d, { status: z.status }), /* @__PURE__ */ m(S, {
|
|
112
|
+
variant: "secondary",
|
|
113
|
+
children: f(z.type)
|
|
114
|
+
})]
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ h("div", {
|
|
117
|
+
className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4",
|
|
118
|
+
children: [
|
|
119
|
+
/* @__PURE__ */ m(M, {
|
|
120
|
+
label: "Endpoint",
|
|
121
|
+
value: z.endpoint ? /* @__PURE__ */ h(l, {
|
|
122
|
+
href: z.endpoint,
|
|
123
|
+
className: "inline-flex items-center gap-1.5 truncate text-text-primary underline-offset-2 hover:underline",
|
|
124
|
+
fallbackClassName: "truncate text-text-secondary",
|
|
125
|
+
children: [/* @__PURE__ */ m(v, {
|
|
126
|
+
className: "size-4 shrink-0 text-text-secondary",
|
|
127
|
+
"aria-hidden": "true"
|
|
128
|
+
}), /* @__PURE__ */ m("span", {
|
|
129
|
+
className: "truncate",
|
|
130
|
+
children: z.endpoint
|
|
131
|
+
})]
|
|
132
|
+
}) : /* @__PURE__ */ m("span", {
|
|
133
|
+
className: "text-text-secondary",
|
|
134
|
+
children: "Not set"
|
|
135
|
+
})
|
|
136
|
+
}),
|
|
137
|
+
/* @__PURE__ */ m(M, {
|
|
138
|
+
label: "Last synced",
|
|
139
|
+
value: /* @__PURE__ */ m("span", { children: A(z.lastSyncedAt) })
|
|
140
|
+
}),
|
|
141
|
+
/* @__PURE__ */ m(M, {
|
|
142
|
+
label: "Created",
|
|
143
|
+
value: /* @__PURE__ */ m("span", { children: A(z.createdAt) })
|
|
144
|
+
}),
|
|
145
|
+
/* @__PURE__ */ m(M, {
|
|
146
|
+
label: "Updated",
|
|
147
|
+
value: /* @__PURE__ */ m("span", { children: A(z.updatedAt) })
|
|
148
|
+
})
|
|
149
|
+
]
|
|
150
|
+
}),
|
|
151
|
+
z.tags.length > 0 ? /* @__PURE__ */ m("div", {
|
|
152
|
+
className: "flex flex-wrap gap-1",
|
|
153
|
+
children: z.tags.map((e) => /* @__PURE__ */ m(S, {
|
|
154
|
+
variant: "outline",
|
|
155
|
+
className: "text-xs",
|
|
156
|
+
children: e
|
|
157
|
+
}, e))
|
|
158
|
+
}) : null,
|
|
159
|
+
/* @__PURE__ */ h("section", {
|
|
160
|
+
className: "space-y-3",
|
|
161
|
+
children: [/* @__PURE__ */ m("h2", {
|
|
162
|
+
className: "text-lg font-semibold text-text-primary",
|
|
163
|
+
children: "Datasets from this source"
|
|
164
|
+
}), G.length === 0 ? /* @__PURE__ */ m(o, {
|
|
165
|
+
icon: _,
|
|
166
|
+
title: "No datasets from this source yet",
|
|
167
|
+
description: "Datasets that ingest from this source will be listed here."
|
|
168
|
+
}) : /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("div", {
|
|
169
|
+
className: "hidden overflow-hidden rounded-card border border-border-default bg-bg-elevated md:block",
|
|
170
|
+
children: /* @__PURE__ */ h(w, { children: [/* @__PURE__ */ m(O, { children: /* @__PURE__ */ h(k, { children: [
|
|
171
|
+
/* @__PURE__ */ m(D, { children: "Name" }),
|
|
172
|
+
/* @__PURE__ */ m(D, { children: "Status" }),
|
|
173
|
+
/* @__PURE__ */ m(D, { children: "Access" }),
|
|
174
|
+
/* @__PURE__ */ m(D, { children: "Rows" }),
|
|
175
|
+
/* @__PURE__ */ m(D, { children: "Updated" })
|
|
176
|
+
] }) }), /* @__PURE__ */ m(T, { children: G.map((e) => /* @__PURE__ */ h(k, {
|
|
177
|
+
className: "cursor-pointer",
|
|
178
|
+
onClick: () => P(`datasets/${e.id}`),
|
|
179
|
+
children: [
|
|
180
|
+
/* @__PURE__ */ m(E, {
|
|
181
|
+
className: "font-medium text-text-primary",
|
|
182
|
+
children: e.name
|
|
183
|
+
}),
|
|
184
|
+
/* @__PURE__ */ m(E, { children: /* @__PURE__ */ m(d, { status: e.status }) }),
|
|
185
|
+
/* @__PURE__ */ m(E, { children: /* @__PURE__ */ m(d, { status: e.accessLevel }) }),
|
|
186
|
+
/* @__PURE__ */ m(E, {
|
|
187
|
+
className: "tabular-nums text-sm text-text-secondary",
|
|
188
|
+
children: Number(e.rowCount) > 0 ? Number(e.rowCount).toLocaleString() : "—"
|
|
189
|
+
}),
|
|
190
|
+
/* @__PURE__ */ m(E, {
|
|
191
|
+
className: "whitespace-nowrap text-sm text-text-secondary",
|
|
192
|
+
children: j(e.updatedAt)
|
|
193
|
+
})
|
|
194
|
+
]
|
|
195
|
+
}, e.id)) })] })
|
|
196
|
+
}), /* @__PURE__ */ m("ul", {
|
|
197
|
+
className: "space-y-3 md:hidden",
|
|
198
|
+
children: G.map((e) => /* @__PURE__ */ m("li", { children: /* @__PURE__ */ h("div", {
|
|
199
|
+
role: "button",
|
|
200
|
+
tabIndex: 0,
|
|
201
|
+
"aria-label": `Open ${e.name}`,
|
|
202
|
+
onClick: () => P(`datasets/${e.id}`),
|
|
203
|
+
onKeyDown: (t) => {
|
|
204
|
+
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), P(`datasets/${e.id}`));
|
|
205
|
+
},
|
|
206
|
+
className: "flex flex-col gap-2 rounded-card border border-border-default bg-bg-elevated p-4 outline-none transition-colors hover:bg-bg-sunken focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
207
|
+
children: [
|
|
208
|
+
/* @__PURE__ */ m("p", {
|
|
209
|
+
className: "font-medium text-text-primary",
|
|
210
|
+
children: e.name
|
|
211
|
+
}),
|
|
212
|
+
/* @__PURE__ */ h("div", {
|
|
213
|
+
className: "flex flex-wrap items-center gap-1.5",
|
|
214
|
+
children: [/* @__PURE__ */ m(d, { status: e.status }), /* @__PURE__ */ m(d, { status: e.accessLevel })]
|
|
215
|
+
}),
|
|
216
|
+
/* @__PURE__ */ h("div", {
|
|
217
|
+
className: "flex items-center justify-between gap-2 text-xs text-text-secondary",
|
|
218
|
+
children: [/* @__PURE__ */ m("span", {
|
|
219
|
+
className: "tabular-nums",
|
|
220
|
+
children: Number(e.rowCount) > 0 ? `${Number(e.rowCount).toLocaleString()} rows` : "No rows"
|
|
221
|
+
}), /* @__PURE__ */ m("span", { children: j(e.updatedAt) })]
|
|
222
|
+
})
|
|
223
|
+
]
|
|
224
|
+
}) }, e.id))
|
|
225
|
+
})] })]
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
})
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
export { N as default };
|
|
233
|
+
|
|
234
|
+
//# sourceMappingURL=SourceDetailPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SourceDetailPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/sources/SourceDetailPage.tsx"],"sourcesContent":["/**\n * SourceDetailPage — full record for one data source.\n *\n * Mirrors CollectionDetailPage: a header with the source's metadata (type/status\n * badges, endpoint, last-synced, tags) over a table of the datasets ingested from\n * this source. Header actions edit the source, mark it synced\n * (`cosMarkSourceSynced`) or archive it (`cosArchiveSource`). Everything is read\n * from the backend — no fabricated data.\n */\nimport { Database, Download, Pencil, Radio, RefreshCw } from 'lucide-react';\nimport {\n Badge,\n Button,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@burdenoff/fe-libs/ui';\nimport { useParams } from 'react-router-dom';\n\nimport {\n useCosArchiveSourceMutation,\n useCosMarkSourceSyncedMutation,\n useCosSourceQuery,\n} from '../../api/cosmicApi';\nimport {\n ActionMenu,\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n SafeLink,\n StatusBadge,\n formatEnumLabel,\n} from '../../components';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { notify } from '../../lib/toast';\n\nfunction formatDateTime(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? '—' : date.toLocaleString();\n}\n\nfunction formatDate(value?: string | null): string {\n if (!value) {\n return '—';\n }\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return '—';\n }\n return parsed.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\nfunction MetaTile({ label, value }: { label: string; value: React.ReactNode }) {\n return (\n <div className=\"rounded-card border border-border-default bg-bg-elevated p-4\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">{label}</p>\n <div className=\"mt-1 text-sm text-text-primary\">{value}</div>\n </div>\n );\n}\n\nexport default function SourceDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useCosmicIntersection();\n\n const { data, loading, error, refetch } = useCosSourceQuery(id);\n const source = data?.cosSource ?? null;\n\n const [markSynced, { loading: syncing }] = useCosMarkSourceSyncedMutation();\n const [archiveSource] = useCosArchiveSourceMutation();\n\n const handleMarkSynced = async () => {\n if (!source) {\n return;\n }\n const response = await markSynced({ variables: { id: source.id } });\n if (response.data?.cosMarkSourceSynced) {\n notify.success('Source marked synced', `“${source.name}” is now up to date.`);\n await refetch();\n }\n };\n\n const handleArchive = async () => {\n if (!source) {\n return;\n }\n const response = await archiveSource({ variables: { id: source.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.cosArchiveSource) {\n notify.error('Could not archive source', 'The server did not confirm the change.');\n throw new Error('Archive not confirmed');\n }\n notify.success('Source archived');\n navigate('sources');\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Source\" backPath=\"sources\">\n <LoadingState label=\"Loading source…\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout title=\"Source\" backPath=\"sources\">\n <ErrorState message=\"We could not load this source.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!source) {\n return (\n <PageLayout title=\"Source\" backPath=\"sources\">\n <EmptyState\n icon={Radio}\n title=\"Source not found.\"\n description=\"This source may have been removed or is outside your current scope.\"\n action={\n <Button type=\"button\" onClick={() => navigate('sources')}>\n Back to sources\n </Button>\n }\n />\n </PageLayout>\n );\n }\n\n const datasets = source.datasets ?? [];\n\n const renderDatasets = () => {\n if (datasets.length === 0) {\n return (\n <EmptyState\n icon={Database}\n title=\"No datasets from this source yet\"\n description=\"Datasets that ingest from this source will be listed here.\"\n />\n );\n }\n return (\n <>\n {/* Desktop / tablet: table */}\n <div className=\"hidden overflow-hidden rounded-card border border-border-default bg-bg-elevated md:block\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Status</TableHead>\n <TableHead>Access</TableHead>\n <TableHead>Rows</TableHead>\n <TableHead>Updated</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {datasets.map((dataset) => (\n <TableRow\n key={dataset.id}\n className=\"cursor-pointer\"\n onClick={() => navigate(`datasets/${dataset.id}`)}\n >\n <TableCell className=\"font-medium text-text-primary\">{dataset.name}</TableCell>\n <TableCell>\n <StatusBadge status={dataset.status} />\n </TableCell>\n <TableCell>\n <StatusBadge status={dataset.accessLevel} />\n </TableCell>\n <TableCell className=\"tabular-nums text-sm text-text-secondary\">\n {Number(dataset.rowCount) > 0 ? Number(dataset.rowCount).toLocaleString() : '—'}\n </TableCell>\n <TableCell className=\"whitespace-nowrap text-sm text-text-secondary\">\n {formatDate(dataset.updatedAt)}\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 {datasets.map((dataset) => (\n <li key={dataset.id}>\n <div\n role=\"button\"\n tabIndex={0}\n aria-label={`Open ${dataset.name}`}\n onClick={() => navigate(`datasets/${dataset.id}`)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n navigate(`datasets/${dataset.id}`);\n }\n }}\n className=\"flex flex-col gap-2 rounded-card border border-border-default bg-bg-elevated p-4 outline-none transition-colors hover:bg-bg-sunken focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <p className=\"font-medium text-text-primary\">{dataset.name}</p>\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={dataset.status} />\n <StatusBadge status={dataset.accessLevel} />\n </div>\n <div className=\"flex items-center justify-between gap-2 text-xs text-text-secondary\">\n <span className=\"tabular-nums\">\n {Number(dataset.rowCount) > 0\n ? `${Number(dataset.rowCount).toLocaleString()} rows`\n : 'No rows'}\n </span>\n <span>{formatDate(dataset.updatedAt)}</span>\n </div>\n </div>\n </li>\n ))}\n </ul>\n </>\n );\n };\n\n return (\n <PageLayout\n title={source.name}\n description={source.description ?? undefined}\n backPath=\"sources\"\n actions={\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => navigate(`sources/${source.id}/edit`)}\n >\n <Pencil className=\"size-4\" />\n Edit\n </Button>\n <ActionMenu\n ariaLabel={`Actions for ${source.name}`}\n entityName={source.name}\n onDelete={() => handleArchive()}\n deleteLabel=\"Archive source\"\n extraItems={[\n {\n key: 'mark-synced',\n label: syncing ? 'Marking synced…' : 'Mark synced',\n icon: RefreshCw,\n disabled: syncing,\n onSelect: () => void handleMarkSynced(),\n },\n ]}\n />\n </div>\n }\n >\n <div className=\"space-y-6\">\n {/* Status + key facts */}\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={source.status} />\n <Badge variant=\"secondary\">{formatEnumLabel(source.type)}</Badge>\n </div>\n\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n <MetaTile\n label=\"Endpoint\"\n value={\n source.endpoint ? (\n <SafeLink\n href={source.endpoint}\n className=\"inline-flex items-center gap-1.5 truncate text-text-primary underline-offset-2 hover:underline\"\n fallbackClassName=\"truncate text-text-secondary\"\n >\n <Download className=\"size-4 shrink-0 text-text-secondary\" aria-hidden=\"true\" />\n <span className=\"truncate\">{source.endpoint}</span>\n </SafeLink>\n ) : (\n <span className=\"text-text-secondary\">Not set</span>\n )\n }\n />\n <MetaTile\n label=\"Last synced\"\n value={<span>{formatDateTime(source.lastSyncedAt)}</span>}\n />\n <MetaTile label=\"Created\" value={<span>{formatDateTime(source.createdAt)}</span>} />\n <MetaTile label=\"Updated\" value={<span>{formatDateTime(source.updatedAt)}</span>} />\n </div>\n\n {source.tags.length > 0 ? (\n <div className=\"flex flex-wrap gap-1\">\n {source.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n ) : null}\n\n {/* Datasets */}\n <section className=\"space-y-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Datasets from this source</h2>\n {renderDatasets()}\n </section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|