@burdenoff/microfe-bigconsole 2026.1008.14 → 2026.1009.10

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.
@@ -1,26 +1,28 @@
1
1
  import { usePipelineStore as e } from "../store/pipelineStore.js";
2
2
  import { useAppNavigate as t } from "../contexts/NavigationContext.js";
3
3
  import "../contexts/index.js";
4
- import { SECTION_FOCUS_RING as n, sectionSurfaceClasses as r } from "../components/common/sectionStyles.js";
5
- import { Section as i, SectionStack as a } from "../components/common/Section.js";
4
+ import { SECTION_FOCUS_RING as ee, sectionSurfaceClasses as te } from "../components/common/sectionStyles.js";
5
+ import { Section as n, SectionStack as ne } from "../components/common/Section.js";
6
6
  import "../components/common/index.js";
7
- import ee from "../hooks/usePipelineOperations.js";
8
- import { BIGCONSOLE_PERMISSIONS as o } from "../../constants/permissions.js";
9
- import { isNative as s, nativeNotify as c } from "../../utils/nativeBridge.js";
10
- import { PipelineScopePicker as l } from "../components/pipeline/PipelineScopePicker.js";
11
- import te from "../components/pipeline/PipelineDeleteDialog.js";
12
- import ne from "../components/pipeline/PipelineCard.js";
13
- import { memo as u, useCallback as d, useMemo as f, useState as p } from "react";
14
- import { useSearchParams as m } from "react-router";
15
- import { AccessDenied as h, PermissionButton as g } from "@burdenoff/fe-libs/shared/components";
16
- import { usePermissions as _ } from "@burdenoff/fe-libs/shared/providers/shell";
17
- import { AlertCircle as v, GitBranch as y, LayoutGrid as b, List as re, Plus as x, Search as ie } from "lucide-react";
18
- import { AuroraBackground as ae, Button as S, GradientText as oe, IllustratedEmptyState as se, Input as ce, PagePurpose as le, Spinner as C } from "@burdenoff/fe-libs/ui";
19
- import { useI18n as ue } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
7
+ import re from "../hooks/usePipelineOperations.js";
8
+ import { BIGCONSOLE_PERMISSIONS as r } from "../../constants/permissions.js";
9
+ import { isNative as i, nativeNotify as a } from "../../utils/nativeBridge.js";
10
+ import { pipelinesListControls as o } from "../assistant/pageControls.js";
11
+ import { PipelineScopePicker as s } from "../components/pipeline/PipelineScopePicker.js";
12
+ import c from "../components/pipeline/PipelineDeleteDialog.js";
13
+ import ie from "../components/pipeline/PipelineCard.js";
14
+ import { memo as l, useCallback as u, useMemo as d, useState as f } from "react";
15
+ import { useSearchParams as p } from "react-router";
16
+ import { AccessDenied as m, PermissionButton as h } from "@burdenoff/fe-libs/shared/components";
17
+ import { usePermissions as g } from "@burdenoff/fe-libs/shared/providers/shell";
18
+ import { AlertCircle as _, GitBranch as ae, LayoutGrid as oe, List as se, Plus as v, Search as ce } from "lucide-react";
19
+ import { AuroraBackground as le, Button as y, GradientText as b, IllustratedEmptyState as ue, Input as de, PagePurpose as x, Spinner as S } from "@burdenoff/fe-libs/ui";
20
+ import { useI18n as C } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
20
21
  import { jsx as w, jsxs as T } from "react/jsx-runtime";
22
+ import { useAssistantPageControls as fe } from "@burdenoff/fe-libs/shared/assistant/appControls/useAssistantControl";
21
23
  //#region src/bigconsole/pages/PipelinesPage.tsx
22
- var E = u(function() {
23
- let u = t(), { t: E } = ue(), { hasPermission: D, isLoading: O } = _(), k = D(o.PIPELINE_READ), A = D(o.PIPELINE_UPDATE), j = D(o.PIPELINE_DELETE), M = D(o.PIPELINE_TEST), [N, P] = p("all"), [F, I] = m(), L = F.get("consoleId") || "default", [R, z] = p("grid"), B = e((e) => e.searchQuery), V = e((e) => e.setSearchQuery), H = e((e) => e.deleteDialogOpen), U = e((e) => e.currentPipeline), W = e((e) => e.openDeleteDialog), G = e((e) => e.closeDeleteDialog), { loading: K, error: q, pipelines: J, deletePipeline: Y, refetch: de } = ee({ consoleId: L }), X = f(() => {
24
+ var E = l(function() {
25
+ let l = t(), { t: E } = C(), { hasPermission: D, isLoading: O } = g(), k = D(r.PIPELINE_READ), A = D(r.PIPELINE_UPDATE), j = D(r.PIPELINE_DELETE), M = D(r.PIPELINE_TEST), [N, P] = f("all"), [F, I] = p(), L = F.get("consoleId") || "default", [R, z] = f("grid"), B = e((e) => e.searchQuery), V = e((e) => e.setSearchQuery), H = e((e) => e.deleteDialogOpen), U = e((e) => e.currentPipeline), W = e((e) => e.openDeleteDialog), G = e((e) => e.closeDeleteDialog), { loading: K, error: q, pipelines: J, deletePipeline: Y, refetch: pe } = re({ consoleId: L }), X = d(() => {
24
26
  let e = [...J];
25
27
  if (N !== "all" && (e = e.filter((e) => e.status === N)), B.trim()) {
26
28
  let t = B.toLowerCase();
@@ -31,23 +33,23 @@ var E = u(function() {
31
33
  J,
32
34
  N,
33
35
  B
34
- ]), Z = d(() => {
35
- u(`/pipelines/new?consoleId=${encodeURIComponent(L)}`);
36
- }, [u, L]), fe = d((e) => {
37
- u(`/pipelines/${e}`);
38
- }, [u]), pe = d((e) => {
39
- u(`/pipelines/${e}/edit`);
40
- }, [u]), me = d((e) => {
41
- u(`/pipelines/${e}?test=true`);
42
- }, [u]), he = d((e) => {
36
+ ]), Z = u(() => {
37
+ l(`/pipelines/new?consoleId=${encodeURIComponent(L)}`);
38
+ }, [l, L]), me = u((e) => {
39
+ l(`/pipelines/${e}`);
40
+ }, [l]), he = u((e) => {
41
+ l(`/pipelines/${e}/edit`);
42
+ }, [l]), ge = u((e) => {
43
+ l(`/pipelines/${e}?test=true`);
44
+ }, [l]), _e = u((e) => {
43
45
  let t = J.find((t) => t.id === e);
44
46
  t && W(t);
45
- }, [J, W]), ge = d(async () => {
47
+ }, [J, W]), ve = u(async () => {
46
48
  if (U) {
47
49
  try {
48
- await Y(U.id), s() && c("success");
50
+ await Y(U.id), i() && a("success");
49
51
  } catch (e) {
50
- throw s() && c("error"), e;
52
+ throw i() && a("error"), e;
51
53
  }
52
54
  G();
53
55
  }
@@ -55,9 +57,9 @@ var E = u(function() {
55
57
  U,
56
58
  Y,
57
59
  G
58
- ]), _e = d(() => {
60
+ ]), ye = u(() => {
59
61
  P("all"), V("");
60
- }, [V]), Q = [
62
+ }, [V]), Q = d(() => [
61
63
  {
62
64
  value: "all",
63
65
  label: E("bigconsole.pipelines.filters.all", "All")
@@ -78,26 +80,43 @@ var E = u(function() {
78
80
  value: "error",
79
81
  label: E("bigconsole.pipelines.filters.error", "Error")
80
82
  }
81
- ];
82
- if (O) return /* @__PURE__ */ w("div", {
83
+ ], [E]);
84
+ if (fe(d(() => !O && k && !q ? o({
85
+ tr: E,
86
+ statusOptions: Q,
87
+ status: N,
88
+ setStatus: (e) => {
89
+ let t = Q.find((t) => t.value === e);
90
+ t && P(t.value);
91
+ },
92
+ setSearch: V
93
+ }) : null, [
94
+ E,
95
+ O,
96
+ k,
97
+ q,
98
+ Q,
99
+ N,
100
+ V
101
+ ])), O) return /* @__PURE__ */ w("div", {
83
102
  style: {
84
103
  display: "flex",
85
104
  alignItems: "center",
86
105
  justifyContent: "center",
87
106
  padding: "80px 0"
88
107
  },
89
- children: /* @__PURE__ */ w(C, { style: {
108
+ children: /* @__PURE__ */ w(S, { style: {
90
109
  height: "var(--size-icon-xl)",
91
110
  width: "var(--size-icon-xl)"
92
111
  } })
93
112
  });
94
- if (!k) return /* @__PURE__ */ w(h, {
113
+ if (!k) return /* @__PURE__ */ w(m, {
95
114
  title: E("bigconsole.pipelines.errors.accessDeniedTitle", "Access Denied"),
96
115
  description: E("bigconsole.pipelines.errors.accessDeniedListDescription", "You don't have permission to view pipelines.")
97
116
  });
98
117
  let $ = /* @__PURE__ */ T("header", {
99
118
  className: "relative overflow-hidden border-b border-[var(--color-border-seam)] bg-[image:var(--gradient-gloss-card)]",
100
- children: [/* @__PURE__ */ w(ae, { intensity: "subtle" }), /* @__PURE__ */ T("div", {
119
+ children: [/* @__PURE__ */ w(le, { intensity: "subtle" }), /* @__PURE__ */ T("div", {
101
120
  className: "flex flex-col gap-form-gap sm:flex-row sm:items-center sm:justify-between",
102
121
  style: { padding: "var(--space-pagePadding)" },
103
122
  children: [/* @__PURE__ */ T("div", {
@@ -110,7 +129,7 @@ var E = u(function() {
110
129
  width: "var(--size-controlHeight-lg)",
111
130
  backgroundColor: "var(--color-action-primary-bg)"
112
131
  },
113
- children: /* @__PURE__ */ w(y, { style: {
132
+ children: /* @__PURE__ */ w(ae, { style: {
114
133
  height: "var(--size-icon-lg)",
115
134
  width: "var(--size-icon-lg)",
116
135
  color: "var(--color-action-primary-text)"
@@ -119,19 +138,19 @@ var E = u(function() {
119
138
  className: "min-w-0",
120
139
  children: [/* @__PURE__ */ w("h1", {
121
140
  className: "text-2xl font-bold leading-tight text-text-primary",
122
- children: /* @__PURE__ */ w(oe, { children: E("bigconsole.pipelines.list.heading", "Pipelines") })
141
+ children: /* @__PURE__ */ w(b, { children: E("bigconsole.pipelines.list.heading", "Pipelines") })
123
142
  }), /* @__PURE__ */ w("p", {
124
143
  className: "text-sm text-text-secondary",
125
144
  children: E("bigconsole.pipelines.list.subtitle", "Create and manage sequential parser chains for data transformation")
126
145
  })]
127
146
  })]
128
- }), /* @__PURE__ */ w(g, {
129
- permission: o.PIPELINE_CREATE,
147
+ }), /* @__PURE__ */ w(h, {
148
+ permission: r.PIPELINE_CREATE,
130
149
  deniedBehavior: "disable",
131
- children: /* @__PURE__ */ T(S, {
150
+ children: /* @__PURE__ */ T(y, {
132
151
  onClick: Z,
133
152
  className: "w-full shrink-0 min-h-[var(--size-controlHeight-md)] sm:w-auto",
134
- children: [/* @__PURE__ */ w(x, { className: "h-icon-sm w-icon-sm" }), E("bigconsole.pipelines.actions.createPipeline", "Create Pipeline")]
153
+ children: [/* @__PURE__ */ w(v, { className: "h-icon-sm w-icon-sm" }), E("bigconsole.pipelines.actions.createPipeline", "Create Pipeline")]
135
154
  })
136
155
  })]
137
156
  })]
@@ -140,15 +159,15 @@ var E = u(function() {
140
159
  className: "min-h-screen bg-bg-canvas",
141
160
  children: [$, /* @__PURE__ */ w("div", {
142
161
  style: { padding: "var(--space-pagePadding)" },
143
- children: /* @__PURE__ */ w(i, {
162
+ children: /* @__PURE__ */ w(n, {
144
163
  tone: "danger",
145
164
  headingLevel: 2,
146
165
  title: E("bigconsole.pipelines.errors.loadListTitle", "Failed to load Pipelines"),
147
166
  description: q.message,
148
- icon: /* @__PURE__ */ w(v, { className: "h-icon-sm w-icon-sm" }),
149
- actions: /* @__PURE__ */ w(S, {
167
+ icon: /* @__PURE__ */ w(_, { className: "h-icon-sm w-icon-sm" }),
168
+ actions: /* @__PURE__ */ w(y, {
150
169
  size: "sm",
151
- onClick: () => de(),
170
+ onClick: () => pe(),
152
171
  children: E("bigconsole.pipelines.actions.tryAgain", "Try Again")
153
172
  }),
154
173
  "data-testid": "pipelines-error",
@@ -164,8 +183,8 @@ var E = u(function() {
164
183
  $,
165
184
  /* @__PURE__ */ w("div", {
166
185
  style: { padding: "var(--space-pagePadding)" },
167
- children: /* @__PURE__ */ T(a, { children: [
168
- /* @__PURE__ */ w(le, {
186
+ children: /* @__PURE__ */ T(ne, { children: [
187
+ /* @__PURE__ */ w(x, {
169
188
  className: "max-w-[75ch]",
170
189
  children: E("bigconsole.pipelines.list.purpose", "Pipelines chain parsers and data sinks into a repeatable flow — they take raw input, run it through your parsers, and route the structured result into a sink. Use them to turn messy logs, files, or API payloads into clean, queryable data your dashboards can use.")
171
190
  }),
@@ -173,8 +192,8 @@ var E = u(function() {
173
192
  "aria-label": "Filter and search pipelines",
174
193
  "data-testid": "pipelines-toolbar",
175
194
  "data-section-emphasis": "bare",
176
- className: r("bare", "neutral", "none"),
177
- children: [/* @__PURE__ */ w(l, {
195
+ className: te("bare", "neutral", "none"),
196
+ children: [/* @__PURE__ */ w(s, {
178
197
  value: L,
179
198
  onChange: (e) => {
180
199
  let t = new URLSearchParams(F);
@@ -191,7 +210,7 @@ var E = u(function() {
191
210
  type: "button",
192
211
  "aria-pressed": N === e.value,
193
212
  onClick: () => P(e.value),
194
- className: `shrink-0 whitespace-nowrap rounded-button text-sm font-medium motion-safe:transition-colors ${n}`,
213
+ className: `shrink-0 whitespace-nowrap rounded-button text-sm font-medium motion-safe:transition-colors ${ee}`,
195
214
  style: {
196
215
  minHeight: "var(--size-controlHeight-md)",
197
216
  padding: "0 var(--space-formGap)",
@@ -208,23 +227,23 @@ var E = u(function() {
208
227
  role: "group",
209
228
  "aria-label": "Pipeline layout",
210
229
  className: "flex shrink-0 gap-1",
211
- children: [/* @__PURE__ */ w(S, {
230
+ children: [/* @__PURE__ */ w(y, {
212
231
  variant: R === "grid" ? "secondary" : "ghost",
213
232
  "aria-label": "Grid view",
214
233
  "aria-pressed": R === "grid",
215
234
  onClick: () => z("grid"),
216
- children: /* @__PURE__ */ w(b, { className: "h-icon-sm w-icon-sm" })
217
- }), /* @__PURE__ */ w(S, {
235
+ children: /* @__PURE__ */ w(oe, { className: "h-icon-sm w-icon-sm" })
236
+ }), /* @__PURE__ */ w(y, {
218
237
  variant: R === "list" ? "secondary" : "ghost",
219
238
  "aria-label": "List view",
220
239
  "aria-pressed": R === "list",
221
240
  onClick: () => z("list"),
222
- children: /* @__PURE__ */ w(re, { className: "h-icon-sm w-icon-sm" })
241
+ children: /* @__PURE__ */ w(se, { className: "h-icon-sm w-icon-sm" })
223
242
  })]
224
243
  }),
225
244
  /* @__PURE__ */ T("div", {
226
245
  className: "relative w-full md:max-w-[320px]",
227
- children: [/* @__PURE__ */ w(ie, {
246
+ children: [/* @__PURE__ */ w(ce, {
228
247
  "aria-hidden": "true",
229
248
  style: {
230
249
  position: "absolute",
@@ -235,7 +254,7 @@ var E = u(function() {
235
254
  width: "var(--size-icon-sm)",
236
255
  color: "var(--color-text-muted)"
237
256
  }
238
- }), /* @__PURE__ */ w(ce, {
257
+ }), /* @__PURE__ */ w(de, {
239
258
  type: "text",
240
259
  "aria-label": E("bigconsole.pipelines.list.searchPlaceholder", "Search pipelines..."),
241
260
  placeholder: E("bigconsole.pipelines.list.searchPlaceholder", "Search pipelines..."),
@@ -250,7 +269,7 @@ var E = u(function() {
250
269
  ]
251
270
  })]
252
271
  }),
253
- /* @__PURE__ */ T(i, {
272
+ /* @__PURE__ */ T(n, {
254
273
  emphasis: "primary",
255
274
  headingLevel: 2,
256
275
  title: "Your pipelines",
@@ -266,7 +285,7 @@ var E = u(function() {
266
285
  role: "status",
267
286
  children: /* @__PURE__ */ T("div", {
268
287
  className: "text-center",
269
- children: [/* @__PURE__ */ w(C, { style: {
288
+ children: [/* @__PURE__ */ w(S, { style: {
270
289
  height: "var(--size-icon-xl)",
271
290
  width: "var(--size-icon-xl)",
272
291
  margin: "0 auto var(--space-formGap)"
@@ -278,32 +297,32 @@ var E = u(function() {
278
297
  }),
279
298
  !K && J.length === 0 && /* @__PURE__ */ w("div", {
280
299
  "data-testid": "pipelines-empty",
281
- children: /* @__PURE__ */ w(se, {
300
+ children: /* @__PURE__ */ w(ue, {
282
301
  illustration: "empty-data",
283
302
  title: E("bigconsole.pipelines.list.emptyTitle", "No Pipelines yet"),
284
303
  description: E("bigconsole.pipelines.list.emptyDescription", "Create a pipeline to chain parsers together for sequential data transformation. Each stage processes the output of the previous stage."),
285
- action: /* @__PURE__ */ w(g, {
286
- permission: o.PIPELINE_CREATE,
304
+ action: /* @__PURE__ */ w(h, {
305
+ permission: r.PIPELINE_CREATE,
287
306
  deniedBehavior: "disable",
288
- children: /* @__PURE__ */ T(S, {
307
+ children: /* @__PURE__ */ T(y, {
289
308
  onClick: Z,
290
309
  className: "min-h-[var(--size-controlHeight-md)]",
291
- children: [/* @__PURE__ */ w(x, { className: "h-icon-sm w-icon-sm" }), E("bigconsole.pipelines.actions.createPipeline", "Create Pipeline")]
310
+ children: [/* @__PURE__ */ w(v, { className: "h-icon-sm w-icon-sm" }), E("bigconsole.pipelines.actions.createPipeline", "Create Pipeline")]
292
311
  })
293
312
  })
294
313
  })
295
314
  }),
296
- !K && X.length === 0 && J.length > 0 && /* @__PURE__ */ w(i, {
315
+ !K && X.length === 0 && J.length > 0 && /* @__PURE__ */ w(n, {
297
316
  emphasis: "muted",
298
317
  tone: "info",
299
318
  padding: "compact",
300
319
  headingLevel: 3,
301
320
  title: E("bigconsole.pipelines.list.noResultsTitle", "No matching pipelines"),
302
321
  description: E("bigconsole.pipelines.list.noResultsDescription", "No pipelines match your filters"),
303
- actions: /* @__PURE__ */ w(S, {
322
+ actions: /* @__PURE__ */ w(y, {
304
323
  size: "sm",
305
324
  variant: "secondary",
306
- onClick: _e,
325
+ onClick: ye,
307
326
  children: "Clear filters"
308
327
  }),
309
328
  "data-testid": "pipelines-no-results"
@@ -311,23 +330,23 @@ var E = u(function() {
311
330
  X.length > 0 && /* @__PURE__ */ w("div", {
312
331
  className: R === "grid" ? "grid grid-cols-1 gap-form-gap sm:grid-cols-2 xl:grid-cols-3" : "grid grid-cols-1 gap-form-gap",
313
332
  "data-layout": R,
314
- children: X.map((e) => /* @__PURE__ */ w(ne, {
333
+ children: X.map((e) => /* @__PURE__ */ w(ie, {
315
334
  pipeline: e,
316
- onView: fe,
317
- onEdit: A ? pe : void 0,
318
- onTest: M ? me : void 0,
319
- onDelete: j ? he : void 0
335
+ onView: me,
336
+ onEdit: A ? he : void 0,
337
+ onTest: M ? ge : void 0,
338
+ onDelete: j ? _e : void 0
320
339
  }, e.id))
321
340
  })
322
341
  ]
323
342
  })
324
343
  ] })
325
344
  }),
326
- U && /* @__PURE__ */ w(te, {
345
+ U && /* @__PURE__ */ w(c, {
327
346
  pipeline: U,
328
347
  isOpen: H,
329
348
  onClose: G,
330
- onConfirm: ge
349
+ onConfirm: ve
331
350
  })
332
351
  ]
333
352
  });
@@ -0,0 +1,28 @@
1
+ import { ListSortValue } from './listSort';
2
+
3
+ export interface DashboardRow {
4
+ name: string;
5
+ description?: string | null;
6
+ category?: string | null;
7
+ status?: string;
8
+ updatedAt: string;
9
+ }
10
+ export interface DashboardListQuery {
11
+ search: string;
12
+ /** '' = all categories. */
13
+ category: string;
14
+ /** '' = all statuses. */
15
+ status: string;
16
+ sort: ListSortValue;
17
+ /** 1-based. */
18
+ page: number;
19
+ pageSize: number;
20
+ }
21
+ export interface DashboardListResult<T> {
22
+ /** Every row that passes search and filters, in the chosen order. */
23
+ filtered: T[];
24
+ totalPages: number;
25
+ /** The rows on `page` (clamped to the available pages). */
26
+ pageItems: T[];
27
+ }
28
+ export declare function selectDashboards<T extends DashboardRow>(dashboards: readonly T[], query: DashboardListQuery): DashboardListResult<T>;
@@ -0,0 +1,15 @@
1
+ import { sortList as e } from "./listSort.js";
2
+ //#region src/bigconsole/utils/dashboardList.ts
3
+ function t(t, n) {
4
+ let r = n.search.toLowerCase(), i = e(t.filter((e) => (!n.category || e.category === n.category) && (!n.status || e.status === n.status) && (e.name.toLowerCase().includes(r) || e.description?.toLowerCase().includes(r))), n.sort, {
5
+ name: (e) => e.name,
6
+ updatedAt: (e) => e.updatedAt
7
+ }), a = Math.max(1, Math.ceil(i.length / n.pageSize)), o = (Math.min(Math.max(1, n.page), a) - 1) * n.pageSize;
8
+ return {
9
+ filtered: i,
10
+ totalPages: a,
11
+ pageItems: i.slice(o, o + n.pageSize)
12
+ };
13
+ }
14
+ //#endregion
15
+ export { t as selectDashboards };
@@ -0,0 +1,15 @@
1
+ import { DataSink, DataSinkSetupState, DataSinkStatus } from '../types';
2
+ import { ListSortValue } from './listSort';
3
+
4
+ export interface DataSinkRow {
5
+ dataSink: DataSink;
6
+ setupState: DataSinkSetupState;
7
+ }
8
+ export interface DataSinkListQuery {
9
+ setupState: 'all' | 'in_progress' | 'completed';
10
+ status: DataSinkStatus | null;
11
+ active: boolean | null;
12
+ search: string;
13
+ sort: ListSortValue;
14
+ }
15
+ export declare function selectDataSinks<T extends DataSinkRow>(rows: readonly T[], query: DataSinkListQuery): T[];
@@ -0,0 +1,15 @@
1
+ import { sortList as e } from "./listSort.js";
2
+ //#region src/bigconsole/utils/dataSinkList.ts
3
+ function t(t, n) {
4
+ let r = [...t];
5
+ if (n.setupState === "in_progress" ? r = r.filter((e) => e.setupState !== "completed") : n.setupState === "completed" && (r = r.filter((e) => e.setupState === "completed")), n.status !== null && (r = r.filter((e) => e.dataSink.status === n.status)), n.active !== null && (r = r.filter((e) => e.dataSink.isActive === n.active)), n.search.trim()) {
6
+ let e = n.search.toLowerCase();
7
+ r = r.filter((t) => t.dataSink.name?.toLowerCase().includes(e) || t.dataSink.key.toLowerCase().includes(e) || t.dataSink.description?.toLowerCase().includes(e));
8
+ }
9
+ return e(r, n.sort, {
10
+ name: (e) => e.dataSink.name || e.dataSink.key,
11
+ updatedAt: (e) => e.dataSink.updatedAt
12
+ });
13
+ }
14
+ //#endregion
15
+ export { t as selectDataSinks };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Shared "Sort by" model for BigConsole list pages (BOFF-8679).
3
+ *
4
+ * ★ One definition of the sort values, the comparator and the option labels, so
5
+ * the visible `SortBySelect`, the page's own list pipeline and the assistant's
6
+ * page-control adapter (`assistant/pageControls.ts`) cannot disagree: a person
7
+ * who picks "Name Z–A" from the menu and a person who says "sort by name
8
+ * descending" set the SAME state and get the SAME list.
9
+ *
10
+ * Pure: no React, no DOM, no i18n provider (labels come in through `Tr`).
11
+ *
12
+ * Only fields the list data really carries are offered:
13
+ * - Name: every dashboard has `name`; a data sink may have a null `name`, in
14
+ * which case its `key` is what the card and the table show, so that is what
15
+ * sorts.
16
+ * - Last updated: `updatedAt` on both. A row with no parseable date sorts last
17
+ * in both directions (it has no "age" to rank).
18
+ */
19
+ export type ListSortValue = 'default' | 'name-asc' | 'name-desc' | 'updated-desc' | 'updated-asc';
20
+ export type ListSortField = 'name' | 'updated';
21
+ export type SortTr = (key: string, fallback: string) => string;
22
+ export interface ListSortOption {
23
+ value: ListSortValue;
24
+ label: string;
25
+ }
26
+ /** How a list item exposes the two sortable fields. */
27
+ export interface ListSortKeys<T> {
28
+ /** The text the row shows as its name. */
29
+ name: (item: T) => string;
30
+ /** ISO timestamp of the last update, if any. */
31
+ updatedAt: (item: T) => string | null | undefined;
32
+ }
33
+ /** The i18n key and English source of every label used by the control. */
34
+ export declare const LIST_SORT_COPY: {
35
+ readonly label: {
36
+ readonly key: "bigconsole.listSort.label";
37
+ readonly en: "Sort by";
38
+ };
39
+ readonly default: {
40
+ readonly key: "bigconsole.listSort.default";
41
+ readonly en: "Default order";
42
+ };
43
+ readonly nameAsc: {
44
+ readonly key: "bigconsole.listSort.nameAsc";
45
+ readonly en: "Name A–Z";
46
+ };
47
+ readonly nameDesc: {
48
+ readonly key: "bigconsole.listSort.nameDesc";
49
+ readonly en: "Name Z–A";
50
+ };
51
+ readonly updatedDesc: {
52
+ readonly key: "bigconsole.listSort.updatedDesc";
53
+ readonly en: "Last updated: newest";
54
+ };
55
+ readonly updatedAsc: {
56
+ readonly key: "bigconsole.listSort.updatedAsc";
57
+ readonly en: "Last updated: oldest";
58
+ };
59
+ readonly fieldName: {
60
+ readonly key: "bigconsole.listSort.field.name";
61
+ readonly en: "Name";
62
+ };
63
+ readonly fieldUpdated: {
64
+ readonly key: "bigconsole.listSort.field.updated";
65
+ readonly en: "Last updated";
66
+ };
67
+ };
68
+ /**
69
+ * The list ordered by `value`. `default` returns the items in the order given
70
+ * (the page's own default order, untouched). The sort is stable: rows that tie
71
+ * keep their incoming order, so a re-sort never shuffles equal rows.
72
+ */
73
+ export declare function sortList<T>(items: readonly T[], value: ListSortValue, keys: ListSortKeys<T>): T[];
74
+ /**
75
+ * The options the visible control lists, in its words. `withDefault` adds the
76
+ * "Default order" entry for a page whose natural order is not one of the four
77
+ * (Dashboards: the server's order). A page whose default IS one of them
78
+ * (Data Sinks: newest first) omits it and starts on that value.
79
+ */
80
+ export declare function listSortOptions(tr: SortTr, withDefault: boolean): ListSortOption[];
@@ -0,0 +1,85 @@
1
+ //#region src/bigconsole/utils/listSort.ts
2
+ var e = {
3
+ label: {
4
+ key: "bigconsole.listSort.label",
5
+ en: "Sort by"
6
+ },
7
+ default: {
8
+ key: "bigconsole.listSort.default",
9
+ en: "Default order"
10
+ },
11
+ nameAsc: {
12
+ key: "bigconsole.listSort.nameAsc",
13
+ en: "Name A–Z"
14
+ },
15
+ nameDesc: {
16
+ key: "bigconsole.listSort.nameDesc",
17
+ en: "Name Z–A"
18
+ },
19
+ updatedDesc: {
20
+ key: "bigconsole.listSort.updatedDesc",
21
+ en: "Last updated: newest"
22
+ },
23
+ updatedAsc: {
24
+ key: "bigconsole.listSort.updatedAsc",
25
+ en: "Last updated: oldest"
26
+ },
27
+ fieldName: {
28
+ key: "bigconsole.listSort.field.name",
29
+ en: "Name"
30
+ },
31
+ fieldUpdated: {
32
+ key: "bigconsole.listSort.field.updated",
33
+ en: "Last updated"
34
+ }
35
+ }, t = new Intl.Collator(void 0, {
36
+ sensitivity: "base",
37
+ numeric: !0
38
+ });
39
+ function n(e) {
40
+ if (!e) return null;
41
+ let t = new Date(e).getTime();
42
+ return Number.isNaN(t) ? null : t;
43
+ }
44
+ function r(e, r, i) {
45
+ let a = [...e];
46
+ switch (r) {
47
+ case "default": return a;
48
+ case "name-asc": return a.sort((e, n) => t.compare(i.name(e), i.name(n)));
49
+ case "name-desc": return a.sort((e, n) => t.compare(i.name(n), i.name(e)));
50
+ case "updated-desc":
51
+ case "updated-asc": {
52
+ let e = r === "updated-desc" ? -1 : 1;
53
+ return a.sort((t, r) => {
54
+ let a = n(i.updatedAt(t)), o = n(i.updatedAt(r));
55
+ return a === null && o === null ? 0 : a === null ? 1 : o === null ? -1 : (a - o) * e;
56
+ });
57
+ }
58
+ }
59
+ }
60
+ function i(t, n) {
61
+ let r = [
62
+ {
63
+ value: "name-asc",
64
+ label: t(e.nameAsc.key, e.nameAsc.en)
65
+ },
66
+ {
67
+ value: "name-desc",
68
+ label: t(e.nameDesc.key, e.nameDesc.en)
69
+ },
70
+ {
71
+ value: "updated-desc",
72
+ label: t(e.updatedDesc.key, e.updatedDesc.en)
73
+ },
74
+ {
75
+ value: "updated-asc",
76
+ label: t(e.updatedAsc.key, e.updatedAsc.en)
77
+ }
78
+ ];
79
+ return n ? [{
80
+ value: "default",
81
+ label: t(e.default.key, e.default.en)
82
+ }, ...r] : r;
83
+ }
84
+ //#endregion
85
+ export { e as LIST_SORT_COPY, i as listSortOptions, r as sortList };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-bigconsole",
3
- "version": "2026.1008.14",
3
+ "version": "2026.1009.10",
4
4
  "description": "BigConsole - AI-powered analytics and dashboard platform",
5
5
  "type": "module",
6
6
  "files": [
@@ -51,7 +51,7 @@
51
51
  },
52
52
  "peerDependencies": {
53
53
  "@apollo/client": "^4.0.11",
54
- "@burdenoff/fe-libs": "2026.1009.1",
54
+ "@burdenoff/fe-libs": "2026.1009.2",
55
55
  "@burdenoff/fluidgrids-fe-sdk": "2026.731.4",
56
56
  "@iconify/react": "^6.0.2",
57
57
  "@monaco-editor/react": "^4.7.0",
@@ -86,7 +86,7 @@
86
86
  "zustand": "^5.0.3"
87
87
  },
88
88
  "devDependencies": {
89
- "@burdenoff/fe-libs": "2026.1009.1",
89
+ "@burdenoff/fe-libs": "2026.1009.2",
90
90
  "@burdenoff/fluidgrids-fe-sdk": "2026.731.4",
91
91
  "@eslint/js": "^10.0.1",
92
92
  "@graphql-codegen/add": "^5.0.0",