@burdenoff/microfe-cosmicintersection 2026.827.1 → 2026.830.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/components/ActionMenu.d.ts +1 -1
- package/dist/cosmicintersection/components/ActionMenu.d.ts.map +1 -1
- package/dist/cosmicintersection/components/ActionMenu.js +51 -54
- package/dist/cosmicintersection/components/ActionMenu.js.map +1 -1
- package/dist/cosmicintersection/components/EntityFormShell.d.ts +1 -1
- package/dist/cosmicintersection/components/EntityFormShell.d.ts.map +1 -1
- package/dist/cosmicintersection/components/EntityFormShell.js +29 -27
- package/dist/cosmicintersection/components/EntityFormShell.js.map +1 -1
- package/dist/cosmicintersection/components/ErrorState.d.ts +1 -1
- package/dist/cosmicintersection/components/ErrorState.d.ts.map +1 -1
- package/dist/cosmicintersection/components/ErrorState.js +19 -17
- package/dist/cosmicintersection/components/ErrorState.js.map +1 -1
- package/dist/cosmicintersection/components/FileUploadField.d.ts.map +1 -1
- package/dist/cosmicintersection/components/FileUploadField.js +61 -60
- package/dist/cosmicintersection/components/FileUploadField.js.map +1 -1
- package/dist/cosmicintersection/components/LoadingState.d.ts +1 -1
- package/dist/cosmicintersection/components/LoadingState.d.ts.map +1 -1
- package/dist/cosmicintersection/components/LoadingState.js +15 -13
- package/dist/cosmicintersection/components/LoadingState.js.map +1 -1
- package/dist/cosmicintersection/components/PageLayout.d.ts.map +1 -1
- package/dist/cosmicintersection/components/PageLayout.js +28 -27
- package/dist/cosmicintersection/components/PageLayout.js.map +1 -1
- package/dist/cosmicintersection/components/Pagination.d.ts.map +1 -1
- package/dist/cosmicintersection/components/Pagination.js +38 -35
- package/dist/cosmicintersection/components/Pagination.js.map +1 -1
- package/dist/cosmicintersection/components/StatusBadge.d.ts.map +1 -1
- package/dist/cosmicintersection/components/StatusBadge.js +30 -9
- package/dist/cosmicintersection/components/StatusBadge.js.map +1 -1
- package/dist/cosmicintersection/pages/access/AccessPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/access/AccessPage.js +147 -146
- package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -1
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.js +260 -259
- package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionsPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/collections/CollectionsPage.js +63 -62
- package/dist/cosmicintersection/pages/collections/CollectionsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.js +132 -131
- package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.js +178 -177
- package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/discovery/DiscoveryPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js +138 -145
- package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js.map +1 -1
- package/dist/cosmicintersection/pages/lineage/LineagePage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/lineage/LineagePage.js +78 -88
- package/dist/cosmicintersection/pages/lineage/LineagePage.js.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js +125 -124
- package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js +152 -151
- package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/releases/ReleasesPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/releases/ReleasesPage.js +175 -174
- package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -1
- package/dist/cosmicintersection/pages/runs/RunsPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/runs/RunsPage.js +197 -188
- package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourcesPage.d.ts.map +1 -1
- package/dist/cosmicintersection/pages/sources/SourcesPage.js +65 -64
- package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -1
- package/dist/dev/main.d.ts.map +1 -1
- package/dist/shared/hooks/useTr.d.ts +12 -0
- package/dist/shared/hooks/useTr.d.ts.map +1 -0
- package/dist/shared/hooks/useTr.js +16 -0
- package/dist/shared/hooks/useTr.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
import { useCosmicIntersection as e } from "../../context/CosmicIntersectionContext.js";
|
|
2
2
|
import { useCosCollectionsQuery as t, useCosDatasetsQuery as n, useCosSourcesQuery as r } from "../../api/cosmicApi.js";
|
|
3
|
-
import i from "
|
|
4
|
-
import a from "../../components/
|
|
3
|
+
import { useTr as i } from "../../../shared/hooks/useTr.js";
|
|
4
|
+
import a from "../../components/EmptyState.js";
|
|
5
|
+
import o from "../../components/ErrorState.js";
|
|
5
6
|
import ee from "../../components/LoadingState.js";
|
|
6
|
-
import { cn as
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import { cn as s } from "../../utils/cn.js";
|
|
8
|
+
import c from "../../components/PageLayout.js";
|
|
9
|
+
import l, { formatEnumLabel as u } from "../../components/StatusBadge.js";
|
|
9
10
|
import "../../components/index.js";
|
|
10
|
-
import
|
|
11
|
-
import { useEffect as
|
|
12
|
-
import { jsx as
|
|
13
|
-
import { ArrowUpDown as
|
|
14
|
-
import { Badge as
|
|
11
|
+
import te from "../../components/FacetPanel.js";
|
|
12
|
+
import { useEffect as ne, useMemo as d, useState as f } from "react";
|
|
13
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
14
|
+
import { ArrowUpDown as h, Database as g, FileText as _, Rows3 as v, Search as y, SlidersHorizontal as b, Table as x, X as S } from "lucide-react";
|
|
15
|
+
import { Badge as re, Button as C, Input as ie, Select as ae, SelectContent as oe, SelectItem as se, SelectTrigger as ce, SelectValue as le } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/cosmicintersection/pages/discovery/DiscoveryPage.tsx
|
|
16
|
-
var
|
|
17
|
+
var w = 200, ue = 300, de = [
|
|
17
18
|
"PUBLIC",
|
|
18
19
|
"WORKSPACE",
|
|
19
20
|
"RESTRICTED",
|
|
20
21
|
"EMBARGOED"
|
|
21
|
-
],
|
|
22
|
+
], T = {
|
|
22
23
|
relevance: "Best match",
|
|
23
24
|
name: "Name A–Z",
|
|
24
25
|
updated: "Recently updated",
|
|
25
26
|
size: "Largest first"
|
|
26
27
|
};
|
|
27
|
-
function
|
|
28
|
+
function E(e) {
|
|
28
29
|
if (!e) return "—";
|
|
29
30
|
let t = Number(e);
|
|
30
31
|
if (!Number.isFinite(t) || t <= 0) return "—";
|
|
@@ -39,60 +40,60 @@ function ce(e) {
|
|
|
39
40
|
for (; r >= 1024 && i < n.length - 1;) r /= 1024, i += 1;
|
|
40
41
|
return `${r >= 10 || i === 0 ? Math.round(r) : r.toFixed(1)} ${n[i]}`;
|
|
41
42
|
}
|
|
42
|
-
function
|
|
43
|
+
function D(e) {
|
|
43
44
|
let t = Number(e ?? 0);
|
|
44
45
|
return Number.isFinite(t) && t > 0 ? `${t.toLocaleString()} rows` : "no rows";
|
|
45
46
|
}
|
|
46
|
-
function
|
|
47
|
-
let { navigate:
|
|
48
|
-
|
|
49
|
-
let e = window.setTimeout(() =>
|
|
47
|
+
function O() {
|
|
48
|
+
let { navigate: O } = e(), k = i(), [A, j] = f(""), [M, N] = f(""), [P, F] = f({}), [I, L] = f("relevance"), [R, z] = f(!1);
|
|
49
|
+
ne(() => {
|
|
50
|
+
let e = window.setTimeout(() => N(A.trim()), ue);
|
|
50
51
|
return () => window.clearTimeout(e);
|
|
51
|
-
}, [
|
|
52
|
-
let
|
|
53
|
-
q:
|
|
52
|
+
}, [A]);
|
|
53
|
+
let B = n({
|
|
54
|
+
q: M || void 0,
|
|
54
55
|
pagination: {
|
|
55
|
-
limit:
|
|
56
|
+
limit: w,
|
|
56
57
|
offset: 0
|
|
57
58
|
}
|
|
58
|
-
}),
|
|
59
|
-
limit:
|
|
59
|
+
}), V = r({ pagination: {
|
|
60
|
+
limit: w,
|
|
60
61
|
offset: 0
|
|
61
|
-
} }),
|
|
62
|
+
} }), H = t(), U = d(() => B.data?.cosListDatasets?.items ?? [], [B.data]), fe = !!B.data?.cosListDatasets?.pageInfo?.hasMore, W = d(() => V.data?.cosListSources?.items ?? [], [V.data]), G = d(() => H.data?.cosListCollections ?? [], [H.data]), K = d(() => {
|
|
62
63
|
let e = /* @__PURE__ */ new Map();
|
|
63
|
-
for (let t of
|
|
64
|
+
for (let t of W) e.set(t.id, t.type);
|
|
64
65
|
return e;
|
|
65
|
-
}, [
|
|
66
|
+
}, [W]), q = d(() => {
|
|
66
67
|
let e = /* @__PURE__ */ new Map();
|
|
67
|
-
for (let t of
|
|
68
|
+
for (let t of G) e.set(t.id, t.name);
|
|
68
69
|
return e;
|
|
69
|
-
}, [
|
|
70
|
-
let e =
|
|
70
|
+
}, [G]), J = d(() => {
|
|
71
|
+
let e = P.instrument ?? [], t = P.access ?? [], n = P.format ?? [], r = P.mission ?? [];
|
|
71
72
|
return {
|
|
72
73
|
instrument: (t) => {
|
|
73
74
|
if (e.length === 0) return !0;
|
|
74
|
-
let n = t.sourceId ?
|
|
75
|
+
let n = t.sourceId ? K.get(t.sourceId) : void 0;
|
|
75
76
|
return n ? e.includes(n) : !1;
|
|
76
77
|
},
|
|
77
78
|
access: (e) => t.length === 0 || t.includes(e.accessLevel),
|
|
78
79
|
format: (e) => n.length === 0 || (e.format ? n.includes(e.format) : !1),
|
|
79
80
|
mission: (e) => r.length === 0 || (e.collectionId ? r.includes(e.collectionId) : !1)
|
|
80
81
|
};
|
|
81
|
-
}, [
|
|
82
|
+
}, [P, K]), Y = d(() => (e, t) => {
|
|
82
83
|
let n = 0;
|
|
83
|
-
for (let r of
|
|
84
|
+
for (let r of U) {
|
|
84
85
|
let i = !0;
|
|
85
|
-
for (let t of Object.keys(
|
|
86
|
+
for (let t of Object.keys(J)) if (t !== e && !J[t](r)) {
|
|
86
87
|
i = !1;
|
|
87
88
|
break;
|
|
88
89
|
}
|
|
89
90
|
i && t(r) && (n += 1);
|
|
90
91
|
}
|
|
91
92
|
return n;
|
|
92
|
-
}, [
|
|
93
|
+
}, [U, J]), X = d(() => {
|
|
93
94
|
let e = /* @__PURE__ */ new Set(), t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set();
|
|
94
|
-
for (let r of
|
|
95
|
-
let i = r.sourceId ?
|
|
95
|
+
for (let r of U) {
|
|
96
|
+
let i = r.sourceId ? K.get(r.sourceId) : void 0;
|
|
96
97
|
i && e.add(i), r.format && t.add(r.format), r.collectionId && n.add(r.collectionId);
|
|
97
98
|
}
|
|
98
99
|
return [
|
|
@@ -101,17 +102,17 @@ function A() {
|
|
|
101
102
|
label: "Instrument",
|
|
102
103
|
options: [...e].map((e) => ({
|
|
103
104
|
value: e,
|
|
104
|
-
label:
|
|
105
|
-
count:
|
|
105
|
+
label: u(e),
|
|
106
|
+
count: Y("instrument", (t) => t.sourceId ? K.get(t.sourceId) === e : !1)
|
|
106
107
|
})).sort((e, t) => t.count - e.count || e.label.localeCompare(t.label))
|
|
107
108
|
},
|
|
108
109
|
{
|
|
109
110
|
key: "access",
|
|
110
111
|
label: "Access",
|
|
111
|
-
options:
|
|
112
|
+
options: de.map((e) => ({
|
|
112
113
|
value: e,
|
|
113
|
-
label:
|
|
114
|
-
count:
|
|
114
|
+
label: u(e),
|
|
115
|
+
count: Y("access", (t) => t.accessLevel === e)
|
|
115
116
|
}))
|
|
116
117
|
},
|
|
117
118
|
{
|
|
@@ -120,7 +121,7 @@ function A() {
|
|
|
120
121
|
options: [...t].sort((e, t) => e.localeCompare(t)).map((e) => ({
|
|
121
122
|
value: e,
|
|
122
123
|
label: e,
|
|
123
|
-
count:
|
|
124
|
+
count: Y("format", (t) => t.format === e)
|
|
124
125
|
}))
|
|
125
126
|
},
|
|
126
127
|
{
|
|
@@ -128,19 +129,19 @@ function A() {
|
|
|
128
129
|
label: "Mission / Survey",
|
|
129
130
|
options: [...n].map((e) => ({
|
|
130
131
|
value: e,
|
|
131
|
-
label:
|
|
132
|
-
count:
|
|
132
|
+
label: q.get(e) ?? "Unknown collection",
|
|
133
|
+
count: Y("mission", (t) => t.collectionId === e)
|
|
133
134
|
})).sort((e, t) => t.count - e.count || e.label.localeCompare(t.label))
|
|
134
135
|
}
|
|
135
136
|
];
|
|
136
137
|
}, [
|
|
137
|
-
|
|
138
|
+
U,
|
|
139
|
+
K,
|
|
138
140
|
q,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
switch (L) {
|
|
141
|
+
Y
|
|
142
|
+
]), Z = d(() => {
|
|
143
|
+
let e = [...U.filter((e) => J.instrument(e) && J.access(e) && J.format(e) && J.mission(e))];
|
|
144
|
+
switch (I) {
|
|
144
145
|
case "name":
|
|
145
146
|
e.sort((e, t) => e.name.localeCompare(t.name));
|
|
146
147
|
break;
|
|
@@ -154,189 +155,181 @@ function A() {
|
|
|
154
155
|
}
|
|
155
156
|
return e;
|
|
156
157
|
}, [
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
]), Q = Object.values(
|
|
161
|
-
|
|
158
|
+
U,
|
|
159
|
+
J,
|
|
160
|
+
I
|
|
161
|
+
]), Q = Object.values(P).reduce((e, t) => e + t.length, 0), pe = (e, t) => {
|
|
162
|
+
F((n) => {
|
|
162
163
|
let r = n[e] ?? [], i = r.includes(t) ? r.filter((e) => e !== t) : [...r, t];
|
|
163
164
|
return {
|
|
164
165
|
...n,
|
|
165
166
|
[e]: i
|
|
166
167
|
};
|
|
167
168
|
});
|
|
168
|
-
}, $ = () =>
|
|
169
|
-
groups:
|
|
170
|
-
selected:
|
|
171
|
-
onToggle:
|
|
169
|
+
}, $ = () => F({}), me = B.loading && U.length === 0, he = B.error, ge = /* @__PURE__ */ p(te, {
|
|
170
|
+
groups: X,
|
|
171
|
+
selected: P,
|
|
172
|
+
onToggle: pe,
|
|
172
173
|
onClear: $
|
|
173
174
|
});
|
|
174
|
-
return /* @__PURE__ */
|
|
175
|
-
title: "Discovery",
|
|
176
|
-
description: "Search and facet the federated science catalog — full-text across datasets, refined by instrument, access, format and mission.",
|
|
177
|
-
actions: /* @__PURE__ */
|
|
175
|
+
return /* @__PURE__ */ p(c, {
|
|
176
|
+
title: k("cosmicintersection.pages.discovery.title", "Discovery"),
|
|
177
|
+
description: k("cosmicintersection.pages.discovery.description", "Search and facet the federated science catalog — full-text across datasets, refined by instrument, access, format and mission."),
|
|
178
|
+
actions: /* @__PURE__ */ m(C, {
|
|
178
179
|
type: "button",
|
|
179
180
|
variant: "outline",
|
|
180
|
-
onClick: () =>
|
|
181
|
-
children: [/* @__PURE__ */
|
|
181
|
+
onClick: () => O("datasets"),
|
|
182
|
+
children: [/* @__PURE__ */ p(x, { className: "size-4" }), k("cosmicintersection.pages.discovery.tableView", "Table view")]
|
|
182
183
|
}),
|
|
183
|
-
children: /* @__PURE__ */
|
|
184
|
+
children: /* @__PURE__ */ m("div", {
|
|
184
185
|
className: "space-y-5",
|
|
185
186
|
children: [
|
|
186
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ m("div", {
|
|
187
188
|
className: "relative",
|
|
188
189
|
children: [
|
|
189
|
-
/* @__PURE__ */
|
|
190
|
+
/* @__PURE__ */ p(y, {
|
|
190
191
|
className: "pointer-events-none absolute left-4 top-1/2 size-5 -translate-y-1/2 text-text-secondary",
|
|
191
192
|
"aria-hidden": "true"
|
|
192
193
|
}),
|
|
193
|
-
/* @__PURE__ */
|
|
194
|
+
/* @__PURE__ */ p(ie, {
|
|
194
195
|
type: "search",
|
|
195
|
-
value:
|
|
196
|
-
onChange: (e) =>
|
|
197
|
-
placeholder: "Search datasets by name, description or tag…",
|
|
198
|
-
"aria-label": "Search datasets",
|
|
196
|
+
value: A,
|
|
197
|
+
onChange: (e) => j(e.target.value),
|
|
198
|
+
placeholder: k("cosmicintersection.pages.discovery.searchPlaceholder", "Search datasets by name, description or tag…"),
|
|
199
|
+
"aria-label": k("cosmicintersection.pages.discovery.searchAriaLabel", "Search datasets"),
|
|
199
200
|
className: "h-12 rounded-card pl-12 pr-11 text-base"
|
|
200
201
|
}),
|
|
201
|
-
|
|
202
|
+
A ? /* @__PURE__ */ p("button", {
|
|
202
203
|
type: "button",
|
|
203
|
-
onClick: () =>
|
|
204
|
-
"aria-label": "Clear search",
|
|
204
|
+
onClick: () => j(""),
|
|
205
|
+
"aria-label": k("cosmicintersection.pages.discovery.clearSearch", "Clear search"),
|
|
205
206
|
className: "absolute right-3 top-1/2 flex size-7 -translate-y-1/2 items-center justify-center rounded-full text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus",
|
|
206
|
-
children: /* @__PURE__ */
|
|
207
|
+
children: /* @__PURE__ */ p(S, { className: "size-4" })
|
|
207
208
|
}) : null
|
|
208
209
|
]
|
|
209
210
|
}),
|
|
210
|
-
/* @__PURE__ */
|
|
211
|
+
/* @__PURE__ */ p("div", {
|
|
211
212
|
className: "flex items-center justify-between gap-3 lg:hidden",
|
|
212
|
-
children: /* @__PURE__ */
|
|
213
|
+
children: /* @__PURE__ */ m(C, {
|
|
213
214
|
type: "button",
|
|
214
215
|
variant: "outline",
|
|
215
|
-
onClick: () =>
|
|
216
|
-
"aria-expanded":
|
|
217
|
-
children: [
|
|
218
|
-
/* @__PURE__ */ d(ie, { className: "size-4" }),
|
|
219
|
-
"Filters",
|
|
220
|
-
Q > 0 ? ` (${Q})` : ""
|
|
221
|
-
]
|
|
216
|
+
onClick: () => z((e) => !e),
|
|
217
|
+
"aria-expanded": R,
|
|
218
|
+
children: [/* @__PURE__ */ p(b, { className: "size-4" }), Q > 0 ? k("cosmicintersection.pages.discovery.filtersWithCount", "Filters ({{count}})", { count: Q }) : k("cosmicintersection.pages.discovery.filters", "Filters")]
|
|
222
219
|
})
|
|
223
220
|
}),
|
|
224
|
-
/* @__PURE__ */
|
|
221
|
+
/* @__PURE__ */ m("div", {
|
|
225
222
|
className: "flex flex-col gap-6 lg:flex-row",
|
|
226
|
-
children: [/* @__PURE__ */
|
|
227
|
-
className:
|
|
228
|
-
children: /* @__PURE__ */
|
|
223
|
+
children: [/* @__PURE__ */ p("aside", {
|
|
224
|
+
className: s("shrink-0 lg:block lg:w-60 lg:border-r lg:border-border-seam lg:pr-6", R ? "block" : "hidden"),
|
|
225
|
+
children: /* @__PURE__ */ p("div", {
|
|
229
226
|
className: "lg:sticky lg:top-0",
|
|
230
|
-
children:
|
|
227
|
+
children: ge
|
|
231
228
|
})
|
|
232
|
-
}), /* @__PURE__ */
|
|
229
|
+
}), /* @__PURE__ */ m("div", {
|
|
233
230
|
className: "min-w-0 flex-1 space-y-4",
|
|
234
231
|
children: [
|
|
235
|
-
/* @__PURE__ */
|
|
232
|
+
/* @__PURE__ */ m("div", {
|
|
236
233
|
className: "flex flex-wrap items-center justify-between gap-3",
|
|
237
|
-
children: [/* @__PURE__ */
|
|
234
|
+
children: [/* @__PURE__ */ m("div", {
|
|
238
235
|
className: "flex items-baseline gap-2",
|
|
239
|
-
children: [/* @__PURE__ */
|
|
236
|
+
children: [/* @__PURE__ */ p("span", {
|
|
240
237
|
className: "text-lg font-semibold tabular-nums text-text-primary",
|
|
241
238
|
children: Z.length
|
|
242
|
-
}), /* @__PURE__ */
|
|
239
|
+
}), /* @__PURE__ */ m("span", {
|
|
243
240
|
className: "text-sm text-text-secondary",
|
|
244
|
-
children: [Z.length === 1 ? "dataset" : "datasets",
|
|
241
|
+
children: [Z.length === 1 ? k("cosmicintersection.pages.discovery.resultCountSingular", "dataset") : k("cosmicintersection.pages.discovery.resultCountPlural", "datasets"), M ? ` ${k("cosmicintersection.pages.discovery.matching", "matching “{{q}}”", { q: M })}` : ""]
|
|
245
242
|
})]
|
|
246
|
-
}), /* @__PURE__ */
|
|
243
|
+
}), /* @__PURE__ */ m("label", {
|
|
247
244
|
className: "flex items-center gap-2 text-sm",
|
|
248
|
-
children: [/* @__PURE__ */
|
|
245
|
+
children: [/* @__PURE__ */ p(h, {
|
|
249
246
|
className: "size-4 text-text-secondary",
|
|
250
247
|
"aria-hidden": "true"
|
|
251
|
-
}), /* @__PURE__ */
|
|
252
|
-
value:
|
|
253
|
-
onValueChange: (e) =>
|
|
254
|
-
children: [/* @__PURE__ */
|
|
255
|
-
"aria-label": "Sort results",
|
|
248
|
+
}), /* @__PURE__ */ m(ae, {
|
|
249
|
+
value: I,
|
|
250
|
+
onValueChange: (e) => L(e),
|
|
251
|
+
children: [/* @__PURE__ */ p(ce, {
|
|
252
|
+
"aria-label": k("cosmicintersection.pages.discovery.sortResults", "Sort results"),
|
|
256
253
|
className: "w-48",
|
|
257
|
-
children: /* @__PURE__ */
|
|
258
|
-
}), /* @__PURE__ */
|
|
254
|
+
children: /* @__PURE__ */ p(le, {})
|
|
255
|
+
}), /* @__PURE__ */ p(oe, { children: Object.keys(T).map((e) => /* @__PURE__ */ p(se, {
|
|
259
256
|
value: e,
|
|
260
|
-
children:
|
|
257
|
+
children: T[e]
|
|
261
258
|
}, e)) })]
|
|
262
259
|
})]
|
|
263
260
|
})]
|
|
264
261
|
}),
|
|
265
|
-
|
|
262
|
+
fe ? /* @__PURE__ */ p("p", {
|
|
266
263
|
className: "rounded-card border border-border-default bg-bg-sunken px-3 py-2 text-xs text-text-secondary",
|
|
267
|
-
children:
|
|
268
|
-
"Showing facets and results for the first ",
|
|
269
|
-
D,
|
|
270
|
-
" matches. Refine your search to narrow the set."
|
|
271
|
-
]
|
|
264
|
+
children: k("cosmicintersection.pages.discovery.poolLimitNotice", "Showing facets and results for the first {{limit}} matches. Refine your search to narrow the set.", { limit: w })
|
|
272
265
|
}) : null,
|
|
273
|
-
|
|
266
|
+
me ? /* @__PURE__ */ p(ee, {
|
|
274
267
|
variant: "rows",
|
|
275
268
|
rows: 6,
|
|
276
|
-
label: "Searching the catalog…"
|
|
277
|
-
}) :
|
|
278
|
-
message: "We could not run that search. Please try again.",
|
|
279
|
-
onRetry: () => void
|
|
280
|
-
}) : Z.length === 0 ? /* @__PURE__ */
|
|
281
|
-
icon:
|
|
282
|
-
title:
|
|
283
|
-
description:
|
|
284
|
-
action: Q > 0 ? /* @__PURE__ */
|
|
269
|
+
label: k("cosmicintersection.pages.discovery.loading", "Searching the catalog…")
|
|
270
|
+
}) : he ? /* @__PURE__ */ p(o, {
|
|
271
|
+
message: k("cosmicintersection.pages.discovery.error", "We could not run that search. Please try again."),
|
|
272
|
+
onRetry: () => void B.refetch()
|
|
273
|
+
}) : Z.length === 0 ? /* @__PURE__ */ p(a, {
|
|
274
|
+
icon: y,
|
|
275
|
+
title: M || Q > 0 ? k("cosmicintersection.pages.discovery.noDatasetsMatch", "No datasets match") : k("cosmicintersection.pages.discovery.startExploring", "Start exploring"),
|
|
276
|
+
description: M || Q > 0 ? k("cosmicintersection.pages.discovery.noDatasetsMatchDescription", "Try a broader search term or clear some facets.") : k("cosmicintersection.pages.discovery.startExploringDescription", "Search by name, description or tag, or pick a facet to browse the catalog."),
|
|
277
|
+
action: Q > 0 ? /* @__PURE__ */ p(C, {
|
|
285
278
|
type: "button",
|
|
286
279
|
variant: "outline",
|
|
287
280
|
onClick: $,
|
|
288
|
-
children: "Clear filters"
|
|
281
|
+
children: k("cosmicintersection.pages.discovery.clearFilters", "Clear filters")
|
|
289
282
|
}) : void 0
|
|
290
|
-
}) : /* @__PURE__ */
|
|
283
|
+
}) : /* @__PURE__ */ p("div", {
|
|
291
284
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3",
|
|
292
|
-
children: Z.map((e) => /* @__PURE__ */
|
|
285
|
+
children: Z.map((e) => /* @__PURE__ */ m("button", {
|
|
293
286
|
type: "button",
|
|
294
|
-
onClick: () =>
|
|
287
|
+
onClick: () => O(`datasets/${e.id}`),
|
|
295
288
|
className: "group flex flex-col overflow-hidden rounded-card border border-border-default bg-bg-elevated text-left outline-none transition-colors hover:border-border-strong hover:bg-bg-sunken focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
296
|
-
children: [/* @__PURE__ */
|
|
289
|
+
children: [/* @__PURE__ */ p("div", { className: "h-1 w-full bg-gradient-to-r from-accent-blue via-accent-purple to-accent-teal" }), /* @__PURE__ */ m("div", {
|
|
297
290
|
className: "flex flex-1 flex-col gap-3 p-4",
|
|
298
291
|
children: [
|
|
299
|
-
/* @__PURE__ */
|
|
292
|
+
/* @__PURE__ */ p("h3", {
|
|
300
293
|
className: "line-clamp-2 text-sm font-semibold text-text-primary",
|
|
301
294
|
children: e.name
|
|
302
295
|
}),
|
|
303
|
-
/* @__PURE__ */
|
|
296
|
+
/* @__PURE__ */ m("div", {
|
|
304
297
|
className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary",
|
|
305
298
|
children: [
|
|
306
|
-
/* @__PURE__ */
|
|
299
|
+
/* @__PURE__ */ m("span", {
|
|
307
300
|
className: "inline-flex items-center gap-1",
|
|
308
|
-
children: [/* @__PURE__ */
|
|
301
|
+
children: [/* @__PURE__ */ p(_, {
|
|
309
302
|
className: "size-3.5",
|
|
310
303
|
"aria-hidden": "true"
|
|
311
304
|
}), e.format || "No format"]
|
|
312
305
|
}),
|
|
313
|
-
/* @__PURE__ */
|
|
306
|
+
/* @__PURE__ */ m("span", {
|
|
314
307
|
className: "inline-flex items-center gap-1",
|
|
315
|
-
children: [/* @__PURE__ */
|
|
308
|
+
children: [/* @__PURE__ */ p(g, {
|
|
316
309
|
className: "size-3.5",
|
|
317
310
|
"aria-hidden": "true"
|
|
318
|
-
}),
|
|
311
|
+
}), E(e.sizeBytes)]
|
|
319
312
|
}),
|
|
320
|
-
/* @__PURE__ */
|
|
313
|
+
/* @__PURE__ */ m("span", {
|
|
321
314
|
className: "inline-flex items-center gap-1",
|
|
322
|
-
children: [/* @__PURE__ */
|
|
315
|
+
children: [/* @__PURE__ */ p(v, {
|
|
323
316
|
className: "size-3.5",
|
|
324
317
|
"aria-hidden": "true"
|
|
325
|
-
}),
|
|
318
|
+
}), D(e.rowCount)]
|
|
326
319
|
})
|
|
327
320
|
]
|
|
328
321
|
}),
|
|
329
|
-
/* @__PURE__ */
|
|
322
|
+
/* @__PURE__ */ m("div", {
|
|
330
323
|
className: "flex flex-wrap items-center gap-1.5",
|
|
331
|
-
children: [/* @__PURE__ */
|
|
324
|
+
children: [/* @__PURE__ */ p(l, { status: e.status }), /* @__PURE__ */ p(l, { status: e.accessLevel })]
|
|
332
325
|
}),
|
|
333
|
-
e.tags.length > 0 ? /* @__PURE__ */
|
|
326
|
+
e.tags.length > 0 ? /* @__PURE__ */ m("div", {
|
|
334
327
|
className: "mt-auto flex flex-wrap gap-1 pt-1",
|
|
335
|
-
children: [e.tags.slice(0, 3).map((e) => /* @__PURE__ */
|
|
328
|
+
children: [e.tags.slice(0, 3).map((e) => /* @__PURE__ */ p(re, {
|
|
336
329
|
variant: "secondary",
|
|
337
330
|
className: "text-xs",
|
|
338
331
|
children: e
|
|
339
|
-
}, e)), e.tags.length > 3 ? /* @__PURE__ */
|
|
332
|
+
}, e)), e.tags.length > 3 ? /* @__PURE__ */ m("span", {
|
|
340
333
|
className: "text-xs text-text-secondary",
|
|
341
334
|
children: ["+", e.tags.length - 3]
|
|
342
335
|
}) : null]
|
|
@@ -353,6 +346,6 @@ function A() {
|
|
|
353
346
|
});
|
|
354
347
|
}
|
|
355
348
|
//#endregion
|
|
356
|
-
export {
|
|
349
|
+
export { O as default };
|
|
357
350
|
|
|
358
351
|
//# sourceMappingURL=DiscoveryPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DiscoveryPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/discovery/DiscoveryPage.tsx"],"sourcesContent":["/**\n * DiscoveryPage — faceted full-text discovery over the federated dataset catalog\n * (mockup 05).\n *\n * A prominent debounced search bar drives the backend `q` full-text argument on\n * `cosListDatasets`; the matched datasets form a \"pool\" (capped at a generous\n * limit) from which every facet count and the result grid are derived — so counts\n * and results always agree and nothing is fabricated. Facets are multi-select and\n * cross-filtered (each group's counts reflect the other selected groups), matching\n * the standard faceted-search behaviour:\n * - Instrument → distinct source types (via cosListSources), mapped from each\n * dataset's sourceId\n * - Access → CosAccessLevel enum\n * - Format → distinct formats present in the pool\n * - Mission/Survey → collections (via cosListCollections)\n *\n * Everything renders from live backend data with honest loading / empty / error\n * states. Tokens only — no raw palette colours.\n */\nimport { useEffect, useMemo, useState } from 'react';\nimport {\n ArrowUpDown,\n Database,\n FileText,\n Rows3,\n Search,\n SlidersHorizontal,\n Table as TableIcon,\n X,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosCollectionsQuery,\n useCosDatasetsQuery,\n useCosSourcesQuery,\n type CosAccessLevel,\n type CosDataset,\n} from '../../api/cosmicApi';\nimport {\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n StatusBadge,\n formatEnumLabel,\n} from '../../components';\nimport { FacetPanel, type FacetGroup } from '../../components/FacetPanel';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { cn } from '../../utils/cn';\n\n/** Generous ceiling for the search pool — facet counts reflect this window. */\nconst POOL_LIMIT = 200;\nconst SEARCH_DEBOUNCE_MS = 300;\n\nconst ACCESS_LEVELS: CosAccessLevel[] = ['PUBLIC', 'WORKSPACE', 'RESTRICTED', 'EMBARGOED'];\n\ntype SortKey = 'relevance' | 'name' | 'updated' | 'size';\n\nconst SORT_LABELS: Record<SortKey, string> = {\n relevance: 'Best match',\n name: 'Name A–Z',\n updated: 'Recently updated',\n size: 'Largest first',\n};\n\n/** BigInt byte string → human-readable size, or an em dash when absent/zero. */\nfunction formatBytes(raw: string | null | undefined): string {\n if (!raw) {\n return '—';\n }\n const bytes = Number(raw);\n if (!Number.isFinite(bytes) || bytes <= 0) {\n return '—';\n }\n const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];\n let value = bytes;\n let unit = 0;\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024;\n unit += 1;\n }\n return `${value >= 10 || unit === 0 ? Math.round(value) : value.toFixed(1)} ${units[unit]}`;\n}\n\nfunction formatRows(raw: string | null | undefined): string {\n const rows = Number(raw ?? 0);\n return Number.isFinite(rows) && rows > 0 ? `${rows.toLocaleString()} rows` : 'no rows';\n}\n\nexport default function DiscoveryPage() {\n const { navigate } = useCosmicIntersection();\n\n const [searchInput, setSearchInput] = useState('');\n const [q, setQ] = useState('');\n const [selected, setSelected] = useState<Record<string, string[]>>({});\n const [sort, setSort] = useState<SortKey>('relevance');\n const [facetsOpen, setFacetsOpen] = useState(false);\n\n // Debounce the raw input into the backend `q` argument.\n useEffect(() => {\n const handle = window.setTimeout(() => setQ(searchInput.trim()), SEARCH_DEBOUNCE_MS);\n return () => window.clearTimeout(handle);\n }, [searchInput]);\n\n const poolQuery = useCosDatasetsQuery({\n q: q || undefined,\n pagination: { limit: POOL_LIMIT, offset: 0 },\n });\n const sourcesQuery = useCosSourcesQuery({ pagination: { limit: POOL_LIMIT, offset: 0 } });\n const collectionsQuery = useCosCollectionsQuery();\n\n const pool = useMemo(() => poolQuery.data?.cosListDatasets?.items ?? [], [poolQuery.data]);\n const poolHasMore = Boolean(poolQuery.data?.cosListDatasets?.pageInfo?.hasMore);\n const sources = useMemo(\n () => sourcesQuery.data?.cosListSources?.items ?? [],\n [sourcesQuery.data]\n );\n const collections = useMemo(\n () => collectionsQuery.data?.cosListCollections ?? [],\n [collectionsQuery.data]\n );\n\n // sourceId → source type, so a dataset can be bucketed by instrument type.\n const sourceTypeById = useMemo(() => {\n const map = new Map<string, string>();\n for (const source of sources) {\n map.set(source.id, source.type);\n }\n return map;\n }, [sources]);\n\n const collectionNameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const collection of collections) {\n map.set(collection.id, collection.name);\n }\n return map;\n }, [collections]);\n\n // Per-group predicates — a dataset \"matches\" a group if the group has no\n // selection or the dataset's value is among the selected values.\n const matchers = useMemo(() => {\n const instrument = selected.instrument ?? [];\n const access = selected.access ?? [];\n const format = selected.format ?? [];\n const mission = selected.mission ?? [];\n return {\n instrument: (d: CosDataset) => {\n if (instrument.length === 0) return true;\n const type = d.sourceId ? sourceTypeById.get(d.sourceId) : undefined;\n return type ? instrument.includes(type) : false;\n },\n access: (d: CosDataset) => access.length === 0 || access.includes(d.accessLevel),\n format: (d: CosDataset) =>\n format.length === 0 || (d.format ? format.includes(d.format) : false),\n mission: (d: CosDataset) =>\n mission.length === 0 || (d.collectionId ? mission.includes(d.collectionId) : false),\n } as const;\n }, [selected, sourceTypeById]);\n\n // Cross-filtered count: items passing every OTHER group, then the predicate.\n const countFor = useMemo(() => {\n return (groupKey: keyof typeof matchers, predicate: (d: CosDataset) => boolean): number => {\n let total = 0;\n for (const dataset of pool) {\n let ok = true;\n for (const key of Object.keys(matchers) as (keyof typeof matchers)[]) {\n if (key === groupKey) continue;\n if (!matchers[key](dataset)) {\n ok = false;\n break;\n }\n }\n if (ok && predicate(dataset)) {\n total += 1;\n }\n }\n return total;\n };\n }, [pool, matchers]);\n\n const facetGroups = useMemo<FacetGroup[]>(() => {\n // Universe of option values from the unfiltered pool (stable while toggling).\n const instrumentTypes = new Set<string>();\n const formats = new Set<string>();\n const missionIds = new Set<string>();\n for (const dataset of pool) {\n const type = dataset.sourceId ? sourceTypeById.get(dataset.sourceId) : undefined;\n if (type) instrumentTypes.add(type);\n if (dataset.format) formats.add(dataset.format);\n if (dataset.collectionId) missionIds.add(dataset.collectionId);\n }\n\n const instrument: FacetGroup = {\n key: 'instrument',\n label: 'Instrument',\n options: [...instrumentTypes]\n .map((type) => ({\n value: type,\n label: formatEnumLabel(type),\n count: countFor('instrument', (d) =>\n d.sourceId ? sourceTypeById.get(d.sourceId) === type : false\n ),\n }))\n .sort((a, b) => b.count - a.count || a.label.localeCompare(b.label)),\n };\n\n const access: FacetGroup = {\n key: 'access',\n label: 'Access',\n options: ACCESS_LEVELS.map((level) => ({\n value: level,\n label: formatEnumLabel(level),\n count: countFor('access', (d) => d.accessLevel === level),\n })),\n };\n\n const format: FacetGroup = {\n key: 'format',\n label: 'Format',\n options: [...formats]\n .sort((a, b) => a.localeCompare(b))\n .map((fmt) => ({\n value: fmt,\n label: fmt,\n count: countFor('format', (d) => d.format === fmt),\n })),\n };\n\n const mission: FacetGroup = {\n key: 'mission',\n label: 'Mission / Survey',\n options: [...missionIds]\n .map((id) => ({\n value: id,\n label: collectionNameById.get(id) ?? 'Unknown collection',\n count: countFor('mission', (d) => d.collectionId === id),\n }))\n .sort((a, b) => b.count - a.count || a.label.localeCompare(b.label)),\n };\n\n return [instrument, access, format, mission];\n }, [pool, sourceTypeById, collectionNameById, countFor]);\n\n // Displayed results: the pool filtered by all selected facets, then sorted.\n const results = useMemo(() => {\n const filtered = pool.filter(\n (dataset) =>\n matchers.instrument(dataset) &&\n matchers.access(dataset) &&\n matchers.format(dataset) &&\n matchers.mission(dataset)\n );\n const sorted = [...filtered];\n switch (sort) {\n case 'name':\n sorted.sort((a, b) => a.name.localeCompare(b.name));\n break;\n case 'updated':\n sorted.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());\n break;\n case 'size':\n sorted.sort((a, b) => Number(b.sizeBytes) - Number(a.sizeBytes));\n break;\n case 'relevance':\n default:\n break; // preserve backend order (relevance when `q` is present)\n }\n return sorted;\n }, [pool, matchers, sort]);\n\n const totalSelected = Object.values(selected).reduce((sum, values) => sum + values.length, 0);\n\n const handleToggle = (groupKey: string, value: string) => {\n setSelected((prev) => {\n const current = prev[groupKey] ?? [];\n const next = current.includes(value)\n ? current.filter((item) => item !== value)\n : [...current, value];\n return { ...prev, [groupKey]: next };\n });\n };\n\n const handleClear = () => setSelected({});\n\n const loading = poolQuery.loading && pool.length === 0;\n const error = poolQuery.error;\n\n const facetPanel = (\n <FacetPanel\n groups={facetGroups}\n selected={selected}\n onToggle={handleToggle}\n onClear={handleClear}\n />\n );\n\n return (\n <PageLayout\n title=\"Discovery\"\n description=\"Search and facet the federated science catalog — full-text across datasets, refined by instrument, access, format and mission.\"\n actions={\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('datasets')}>\n <TableIcon className=\"size-4\" />\n Table view\n </Button>\n }\n >\n <div className=\"space-y-5\">\n {/* Search bar */}\n <div className=\"relative\">\n <Search\n className=\"pointer-events-none absolute left-4 top-1/2 size-5 -translate-y-1/2 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <Input\n type=\"search\"\n value={searchInput}\n onChange={(event) => setSearchInput(event.target.value)}\n placeholder=\"Search datasets by name, description or tag…\"\n aria-label=\"Search datasets\"\n className=\"h-12 rounded-card pl-12 pr-11 text-base\"\n />\n {searchInput ? (\n <button\n type=\"button\"\n onClick={() => setSearchInput('')}\n aria-label=\"Clear search\"\n className=\"absolute right-3 top-1/2 flex size-7 -translate-y-1/2 items-center justify-center rounded-full text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus\"\n >\n <X className=\"size-4\" />\n </button>\n ) : null}\n </div>\n\n {/* Mobile facet toggle */}\n <div className=\"flex items-center justify-between gap-3 lg:hidden\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setFacetsOpen((open) => !open)}\n aria-expanded={facetsOpen}\n >\n <SlidersHorizontal className=\"size-4\" />\n Filters{totalSelected > 0 ? ` (${totalSelected})` : ''}\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-6 lg:flex-row\">\n {/* Facets — sticky sidebar on desktop, collapsible on mobile */}\n <aside\n className={cn(\n 'shrink-0 lg:block lg:w-60 lg:border-r lg:border-border-seam lg:pr-6',\n facetsOpen ? 'block' : 'hidden'\n )}\n >\n <div className=\"lg:sticky lg:top-0\">{facetPanel}</div>\n </aside>\n\n {/* Results */}\n <div className=\"min-w-0 flex-1 space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-baseline gap-2\">\n <span className=\"text-lg font-semibold tabular-nums text-text-primary\">\n {results.length}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {results.length === 1 ? 'dataset' : 'datasets'}\n {q ? ` matching “${q}”` : ''}\n </span>\n </div>\n <label className=\"flex items-center gap-2 text-sm\">\n <ArrowUpDown className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n <Select value={sort} onValueChange={(value) => setSort(value as SortKey)}>\n <SelectTrigger aria-label=\"Sort results\" className=\"w-48\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {(Object.keys(SORT_LABELS) as SortKey[]).map((key) => (\n <SelectItem key={key} value={key}>\n {SORT_LABELS[key]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n {poolHasMore ? (\n <p className=\"rounded-card border border-border-default bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n Showing facets and results for the first {POOL_LIMIT} matches. Refine your search to\n narrow the set.\n </p>\n ) : null}\n\n {loading ? (\n <LoadingState variant=\"rows\" rows={6} label=\"Searching the catalog…\" />\n ) : error ? (\n <ErrorState\n message=\"We could not run that search. Please try again.\"\n onRetry={() => void poolQuery.refetch()}\n />\n ) : results.length === 0 ? (\n <EmptyState\n icon={Search}\n title={q || totalSelected > 0 ? 'No datasets match' : 'Start exploring'}\n description={\n q || totalSelected > 0\n ? 'Try a broader search term or clear some facets.'\n : 'Search by name, description or tag, or pick a facet to browse the catalog.'\n }\n action={\n totalSelected > 0 ? (\n <Button type=\"button\" variant=\"outline\" onClick={handleClear}>\n Clear filters\n </Button>\n ) : undefined\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3\">\n {results.map((dataset) => (\n <button\n key={dataset.id}\n type=\"button\"\n onClick={() => navigate(`datasets/${dataset.id}`)}\n className=\"group flex flex-col overflow-hidden rounded-card border border-border-default bg-bg-elevated text-left outline-none transition-colors hover:border-border-strong hover:bg-bg-sunken focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"h-1 w-full bg-gradient-to-r from-accent-blue via-accent-purple to-accent-teal\" />\n <div className=\"flex flex-1 flex-col gap-3 p-4\">\n <h3 className=\"line-clamp-2 text-sm font-semibold text-text-primary\">\n {dataset.name}\n </h3>\n\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary\">\n <span className=\"inline-flex items-center gap-1\">\n <FileText className=\"size-3.5\" aria-hidden=\"true\" />\n {dataset.format || 'No format'}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Database className=\"size-3.5\" aria-hidden=\"true\" />\n {formatBytes(dataset.sizeBytes)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Rows3 className=\"size-3.5\" aria-hidden=\"true\" />\n {formatRows(dataset.rowCount)}\n </span>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={dataset.status} />\n <StatusBadge status={dataset.accessLevel} />\n </div>\n\n {dataset.tags.length > 0 ? (\n <div className=\"mt-auto flex flex-wrap gap-1 pt-1\">\n {dataset.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"secondary\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n {dataset.tags.length > 3 ? (\n <span className=\"text-xs text-text-secondary\">\n +{dataset.tags.length - 3}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DiscoveryPage.js","names":[],"sources":["../../../../src/cosmicintersection/pages/discovery/DiscoveryPage.tsx"],"sourcesContent":["/**\n * DiscoveryPage — faceted full-text discovery over the federated dataset catalog\n * (mockup 05).\n *\n * A prominent debounced search bar drives the backend `q` full-text argument on\n * `cosListDatasets`; the matched datasets form a \"pool\" (capped at a generous\n * limit) from which every facet count and the result grid are derived — so counts\n * and results always agree and nothing is fabricated. Facets are multi-select and\n * cross-filtered (each group's counts reflect the other selected groups), matching\n * the standard faceted-search behaviour:\n * - Instrument → distinct source types (via cosListSources), mapped from each\n * dataset's sourceId\n * - Access → CosAccessLevel enum\n * - Format → distinct formats present in the pool\n * - Mission/Survey → collections (via cosListCollections)\n *\n * Everything renders from live backend data with honest loading / empty / error\n * states. Tokens only — no raw palette colours.\n */\nimport { useEffect, useMemo, useState } from 'react';\nimport {\n ArrowUpDown,\n Database,\n FileText,\n Rows3,\n Search,\n SlidersHorizontal,\n Table as TableIcon,\n X,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCosCollectionsQuery,\n useCosDatasetsQuery,\n useCosSourcesQuery,\n type CosAccessLevel,\n type CosDataset,\n} from '../../api/cosmicApi';\nimport {\n EmptyState,\n ErrorState,\n LoadingState,\n PageLayout,\n StatusBadge,\n formatEnumLabel,\n} from '../../components';\nimport { FacetPanel, type FacetGroup } from '../../components/FacetPanel';\nimport { useCosmicIntersection } from '../../context/CosmicIntersectionContext';\nimport { cn } from '../../utils/cn';\nimport { useTr } from '../../../shared/hooks/useTr';\n\n/** Generous ceiling for the search pool — facet counts reflect this window. */\nconst POOL_LIMIT = 200;\nconst SEARCH_DEBOUNCE_MS = 300;\n\nconst ACCESS_LEVELS: CosAccessLevel[] = ['PUBLIC', 'WORKSPACE', 'RESTRICTED', 'EMBARGOED'];\n\ntype SortKey = 'relevance' | 'name' | 'updated' | 'size';\n\nconst SORT_LABELS: Record<SortKey, string> = {\n relevance: 'Best match',\n name: 'Name A–Z',\n updated: 'Recently updated',\n size: 'Largest first',\n};\n\n/** BigInt byte string → human-readable size, or an em dash when absent/zero. */\nfunction formatBytes(raw: string | null | undefined): string {\n if (!raw) {\n return '—';\n }\n const bytes = Number(raw);\n if (!Number.isFinite(bytes) || bytes <= 0) {\n return '—';\n }\n const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];\n let value = bytes;\n let unit = 0;\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024;\n unit += 1;\n }\n return `${value >= 10 || unit === 0 ? Math.round(value) : value.toFixed(1)} ${units[unit]}`;\n}\n\nfunction formatRows(raw: string | null | undefined): string {\n const rows = Number(raw ?? 0);\n return Number.isFinite(rows) && rows > 0 ? `${rows.toLocaleString()} rows` : 'no rows';\n}\n\nexport default function DiscoveryPage() {\n const { navigate } = useCosmicIntersection();\n const tr = useTr();\n\n const [searchInput, setSearchInput] = useState('');\n const [q, setQ] = useState('');\n const [selected, setSelected] = useState<Record<string, string[]>>({});\n const [sort, setSort] = useState<SortKey>('relevance');\n const [facetsOpen, setFacetsOpen] = useState(false);\n\n // Debounce the raw input into the backend `q` argument.\n useEffect(() => {\n const handle = window.setTimeout(() => setQ(searchInput.trim()), SEARCH_DEBOUNCE_MS);\n return () => window.clearTimeout(handle);\n }, [searchInput]);\n\n const poolQuery = useCosDatasetsQuery({\n q: q || undefined,\n pagination: { limit: POOL_LIMIT, offset: 0 },\n });\n const sourcesQuery = useCosSourcesQuery({ pagination: { limit: POOL_LIMIT, offset: 0 } });\n const collectionsQuery = useCosCollectionsQuery();\n\n const pool = useMemo(() => poolQuery.data?.cosListDatasets?.items ?? [], [poolQuery.data]);\n const poolHasMore = Boolean(poolQuery.data?.cosListDatasets?.pageInfo?.hasMore);\n const sources = useMemo(\n () => sourcesQuery.data?.cosListSources?.items ?? [],\n [sourcesQuery.data]\n );\n const collections = useMemo(\n () => collectionsQuery.data?.cosListCollections ?? [],\n [collectionsQuery.data]\n );\n\n // sourceId → source type, so a dataset can be bucketed by instrument type.\n const sourceTypeById = useMemo(() => {\n const map = new Map<string, string>();\n for (const source of sources) {\n map.set(source.id, source.type);\n }\n return map;\n }, [sources]);\n\n const collectionNameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const collection of collections) {\n map.set(collection.id, collection.name);\n }\n return map;\n }, [collections]);\n\n // Per-group predicates — a dataset \"matches\" a group if the group has no\n // selection or the dataset's value is among the selected values.\n const matchers = useMemo(() => {\n const instrument = selected.instrument ?? [];\n const access = selected.access ?? [];\n const format = selected.format ?? [];\n const mission = selected.mission ?? [];\n return {\n instrument: (d: CosDataset) => {\n if (instrument.length === 0) return true;\n const type = d.sourceId ? sourceTypeById.get(d.sourceId) : undefined;\n return type ? instrument.includes(type) : false;\n },\n access: (d: CosDataset) => access.length === 0 || access.includes(d.accessLevel),\n format: (d: CosDataset) =>\n format.length === 0 || (d.format ? format.includes(d.format) : false),\n mission: (d: CosDataset) =>\n mission.length === 0 || (d.collectionId ? mission.includes(d.collectionId) : false),\n } as const;\n }, [selected, sourceTypeById]);\n\n // Cross-filtered count: items passing every OTHER group, then the predicate.\n const countFor = useMemo(() => {\n return (groupKey: keyof typeof matchers, predicate: (d: CosDataset) => boolean): number => {\n let total = 0;\n for (const dataset of pool) {\n let ok = true;\n for (const key of Object.keys(matchers) as (keyof typeof matchers)[]) {\n if (key === groupKey) continue;\n if (!matchers[key](dataset)) {\n ok = false;\n break;\n }\n }\n if (ok && predicate(dataset)) {\n total += 1;\n }\n }\n return total;\n };\n }, [pool, matchers]);\n\n const facetGroups = useMemo<FacetGroup[]>(() => {\n // Universe of option values from the unfiltered pool (stable while toggling).\n const instrumentTypes = new Set<string>();\n const formats = new Set<string>();\n const missionIds = new Set<string>();\n for (const dataset of pool) {\n const type = dataset.sourceId ? sourceTypeById.get(dataset.sourceId) : undefined;\n if (type) instrumentTypes.add(type);\n if (dataset.format) formats.add(dataset.format);\n if (dataset.collectionId) missionIds.add(dataset.collectionId);\n }\n\n const instrument: FacetGroup = {\n key: 'instrument',\n label: 'Instrument',\n options: [...instrumentTypes]\n .map((type) => ({\n value: type,\n label: formatEnumLabel(type),\n count: countFor('instrument', (d) =>\n d.sourceId ? sourceTypeById.get(d.sourceId) === type : false\n ),\n }))\n .sort((a, b) => b.count - a.count || a.label.localeCompare(b.label)),\n };\n\n const access: FacetGroup = {\n key: 'access',\n label: 'Access',\n options: ACCESS_LEVELS.map((level) => ({\n value: level,\n label: formatEnumLabel(level),\n count: countFor('access', (d) => d.accessLevel === level),\n })),\n };\n\n const format: FacetGroup = {\n key: 'format',\n label: 'Format',\n options: [...formats]\n .sort((a, b) => a.localeCompare(b))\n .map((fmt) => ({\n value: fmt,\n label: fmt,\n count: countFor('format', (d) => d.format === fmt),\n })),\n };\n\n const mission: FacetGroup = {\n key: 'mission',\n label: 'Mission / Survey',\n options: [...missionIds]\n .map((id) => ({\n value: id,\n label: collectionNameById.get(id) ?? 'Unknown collection',\n count: countFor('mission', (d) => d.collectionId === id),\n }))\n .sort((a, b) => b.count - a.count || a.label.localeCompare(b.label)),\n };\n\n return [instrument, access, format, mission];\n }, [pool, sourceTypeById, collectionNameById, countFor]);\n\n // Displayed results: the pool filtered by all selected facets, then sorted.\n const results = useMemo(() => {\n const filtered = pool.filter(\n (dataset) =>\n matchers.instrument(dataset) &&\n matchers.access(dataset) &&\n matchers.format(dataset) &&\n matchers.mission(dataset)\n );\n const sorted = [...filtered];\n switch (sort) {\n case 'name':\n sorted.sort((a, b) => a.name.localeCompare(b.name));\n break;\n case 'updated':\n sorted.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());\n break;\n case 'size':\n sorted.sort((a, b) => Number(b.sizeBytes) - Number(a.sizeBytes));\n break;\n case 'relevance':\n default:\n break; // preserve backend order (relevance when `q` is present)\n }\n return sorted;\n }, [pool, matchers, sort]);\n\n const totalSelected = Object.values(selected).reduce((sum, values) => sum + values.length, 0);\n\n const handleToggle = (groupKey: string, value: string) => {\n setSelected((prev) => {\n const current = prev[groupKey] ?? [];\n const next = current.includes(value)\n ? current.filter((item) => item !== value)\n : [...current, value];\n return { ...prev, [groupKey]: next };\n });\n };\n\n const handleClear = () => setSelected({});\n\n const loading = poolQuery.loading && pool.length === 0;\n const error = poolQuery.error;\n\n const facetPanel = (\n <FacetPanel\n groups={facetGroups}\n selected={selected}\n onToggle={handleToggle}\n onClear={handleClear}\n />\n );\n\n return (\n <PageLayout\n title={tr('cosmicintersection.pages.discovery.title', 'Discovery')}\n description={tr(\n 'cosmicintersection.pages.discovery.description',\n 'Search and facet the federated science catalog — full-text across datasets, refined by instrument, access, format and mission.',\n )}\n actions={\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('datasets')}>\n <TableIcon className=\"size-4\" />\n {tr('cosmicintersection.pages.discovery.tableView', 'Table view')}\n </Button>\n }\n >\n <div className=\"space-y-5\">\n {/* Search bar */}\n <div className=\"relative\">\n <Search\n className=\"pointer-events-none absolute left-4 top-1/2 size-5 -translate-y-1/2 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <Input\n type=\"search\"\n value={searchInput}\n onChange={(event) => setSearchInput(event.target.value)}\n placeholder={tr(\n 'cosmicintersection.pages.discovery.searchPlaceholder',\n 'Search datasets by name, description or tag…',\n )}\n aria-label={tr(\n 'cosmicintersection.pages.discovery.searchAriaLabel',\n 'Search datasets',\n )}\n className=\"h-12 rounded-card pl-12 pr-11 text-base\"\n />\n {searchInput ? (\n <button\n type=\"button\"\n onClick={() => setSearchInput('')}\n aria-label={tr(\n 'cosmicintersection.pages.discovery.clearSearch',\n 'Clear search',\n )}\n className=\"absolute right-3 top-1/2 flex size-7 -translate-y-1/2 items-center justify-center rounded-full text-text-secondary transition-colors hover:bg-bg-sunken hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus\"\n >\n <X className=\"size-4\" />\n </button>\n ) : null}\n </div>\n\n {/* Mobile facet toggle */}\n <div className=\"flex items-center justify-between gap-3 lg:hidden\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setFacetsOpen((open) => !open)}\n aria-expanded={facetsOpen}\n >\n <SlidersHorizontal className=\"size-4\" />\n {totalSelected > 0\n ? tr(\n 'cosmicintersection.pages.discovery.filtersWithCount',\n 'Filters ({{count}})',\n { count: totalSelected },\n )\n : tr('cosmicintersection.pages.discovery.filters', 'Filters')}\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-6 lg:flex-row\">\n {/* Facets — sticky sidebar on desktop, collapsible on mobile */}\n <aside\n className={cn(\n 'shrink-0 lg:block lg:w-60 lg:border-r lg:border-border-seam lg:pr-6',\n facetsOpen ? 'block' : 'hidden'\n )}\n >\n <div className=\"lg:sticky lg:top-0\">{facetPanel}</div>\n </aside>\n\n {/* Results */}\n <div className=\"min-w-0 flex-1 space-y-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-baseline gap-2\">\n <span className=\"text-lg font-semibold tabular-nums text-text-primary\">\n {results.length}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {results.length === 1\n ? tr(\n 'cosmicintersection.pages.discovery.resultCountSingular',\n 'dataset',\n )\n : tr(\n 'cosmicintersection.pages.discovery.resultCountPlural',\n 'datasets',\n )}\n {q\n ? ` ${tr(\n 'cosmicintersection.pages.discovery.matching',\n 'matching “{{q}}”',\n { q },\n )}`\n : ''}\n </span>\n </div>\n <label className=\"flex items-center gap-2 text-sm\">\n <ArrowUpDown className=\"size-4 text-text-secondary\" aria-hidden=\"true\" />\n <Select value={sort} onValueChange={(value) => setSort(value as SortKey)}>\n <SelectTrigger\n aria-label={tr(\n 'cosmicintersection.pages.discovery.sortResults',\n 'Sort results',\n )}\n className=\"w-48\"\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {(Object.keys(SORT_LABELS) as SortKey[]).map((key) => (\n <SelectItem key={key} value={key}>\n {SORT_LABELS[key]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n {poolHasMore ? (\n <p className=\"rounded-card border border-border-default bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n {tr(\n 'cosmicintersection.pages.discovery.poolLimitNotice',\n 'Showing facets and results for the first {{limit}} matches. Refine your search to narrow the set.',\n { limit: POOL_LIMIT },\n )}\n </p>\n ) : null}\n\n {loading ? (\n <LoadingState\n variant=\"rows\"\n rows={6}\n label={tr(\n 'cosmicintersection.pages.discovery.loading',\n 'Searching the catalog…',\n )}\n />\n ) : error ? (\n <ErrorState\n message={tr(\n 'cosmicintersection.pages.discovery.error',\n 'We could not run that search. Please try again.',\n )}\n onRetry={() => void poolQuery.refetch()}\n />\n ) : results.length === 0 ? (\n <EmptyState\n icon={Search}\n title={\n q || totalSelected > 0\n ? tr(\n 'cosmicintersection.pages.discovery.noDatasetsMatch',\n 'No datasets match',\n )\n : tr(\n 'cosmicintersection.pages.discovery.startExploring',\n 'Start exploring',\n )\n }\n description={\n q || totalSelected > 0\n ? tr(\n 'cosmicintersection.pages.discovery.noDatasetsMatchDescription',\n 'Try a broader search term or clear some facets.',\n )\n : tr(\n 'cosmicintersection.pages.discovery.startExploringDescription',\n 'Search by name, description or tag, or pick a facet to browse the catalog.',\n )\n }\n action={\n totalSelected > 0 ? (\n <Button type=\"button\" variant=\"outline\" onClick={handleClear}>\n {tr('cosmicintersection.pages.discovery.clearFilters', 'Clear filters')}\n </Button>\n ) : undefined\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3\">\n {results.map((dataset) => (\n <button\n key={dataset.id}\n type=\"button\"\n onClick={() => navigate(`datasets/${dataset.id}`)}\n className=\"group flex flex-col overflow-hidden rounded-card border border-border-default bg-bg-elevated text-left outline-none transition-colors hover:border-border-strong hover:bg-bg-sunken focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <div className=\"h-1 w-full bg-gradient-to-r from-accent-blue via-accent-purple to-accent-teal\" />\n <div className=\"flex flex-1 flex-col gap-3 p-4\">\n <h3 className=\"line-clamp-2 text-sm font-semibold text-text-primary\">\n {dataset.name}\n </h3>\n\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary\">\n <span className=\"inline-flex items-center gap-1\">\n <FileText className=\"size-3.5\" aria-hidden=\"true\" />\n {dataset.format || 'No format'}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Database className=\"size-3.5\" aria-hidden=\"true\" />\n {formatBytes(dataset.sizeBytes)}\n </span>\n <span className=\"inline-flex items-center gap-1\">\n <Rows3 className=\"size-3.5\" aria-hidden=\"true\" />\n {formatRows(dataset.rowCount)}\n </span>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={dataset.status} />\n <StatusBadge status={dataset.accessLevel} />\n </div>\n\n {dataset.tags.length > 0 ? (\n <div className=\"mt-auto flex flex-wrap gap-1 pt-1\">\n {dataset.tags.slice(0, 3).map((tag) => (\n <Badge key={tag} variant=\"secondary\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n {dataset.tags.length > 3 ? (\n <span className=\"text-xs text-text-secondary\">\n +{dataset.tags.length - 3}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LineagePage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/lineage/LineagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LineagePage.d.ts","sourceRoot":"","sources":["../../../../src/cosmicintersection/pages/lineage/LineagePage.tsx"],"names":[],"mappings":"AAmCA,MAAM,CAAC,OAAO,UAAU,WAAW,4CAoPlC"}
|