@burdenoff/microfe-bigconsole 2026.812.2 → 2026.827.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.
Files changed (40) hide show
  1. package/dist/bigconsole/components/builder/inspector/LayoutSection.js +71 -0
  2. package/dist/bigconsole/components/builder/inspector/LayoutSection.js.map +1 -0
  3. package/dist/bigconsole/components/builder/inspector/index.js +1 -0
  4. package/dist/bigconsole/components/dashboard/DashboardCanvas.js +137 -121
  5. package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
  6. package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js +204 -93
  7. package/dist/bigconsole/components/dashboard/ExportDashboardDialog.js.map +1 -1
  8. package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js +259 -133
  9. package/dist/bigconsole/components/dashboard/ShareDialog/ShareDialog.js.map +1 -1
  10. package/dist/bigconsole/components/dashboard/StoreTemplatePicker.js +87 -39
  11. package/dist/bigconsole/components/dashboard/StoreTemplatePicker.js.map +1 -1
  12. package/dist/bigconsole/components/datasink/DataSinkCard.js +88 -68
  13. package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
  14. package/dist/bigconsole/components/widgets/PropertiesPanel.js +213 -199
  15. package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
  16. package/dist/bigconsole/components/workflow/WorkflowCard.js +46 -27
  17. package/dist/bigconsole/components/workflow/WorkflowCard.js.map +1 -1
  18. package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +44 -47
  19. package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
  20. package/dist/bigconsole/customWidget/proposalSource.js +31 -24
  21. package/dist/bigconsole/customWidget/proposalSource.js.map +1 -1
  22. package/dist/bigconsole/hooks/useDashboardOperations.js +74 -64
  23. package/dist/bigconsole/hooks/useDashboardOperations.js.map +1 -1
  24. package/dist/bigconsole/hooks/useDataSinkOperations.js +2 -2
  25. package/dist/bigconsole/hooks/useDataSinkOperations.js.map +1 -1
  26. package/dist/bigconsole/pages/DashboardBuilderPage.js +1 -1
  27. package/dist/bigconsole/pages/DashboardsPage.js +386 -316
  28. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  29. package/dist/bigconsole/pages/DataSinksPage.js +251 -206
  30. package/dist/bigconsole/pages/DataSinksPage.js.map +1 -1
  31. package/dist/bigconsole/pages/InsightsPage.js +646 -378
  32. package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
  33. package/dist/bigconsole/pages/WorkflowsPage.js +153 -108
  34. package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
  35. package/dist/bigconsole/utils/format.js +18 -0
  36. package/dist/bigconsole/utils/format.js.map +1 -0
  37. package/dist/bigconsole/utils/widgetPositioning.js +1 -1
  38. package/dist/generated/wspace-operations.js +1 -0
  39. package/dist/generated/wspace-operations.js.map +1 -1
  40. package/package.json +1 -1
@@ -10,232 +10,277 @@ import { BIGCONSOLE_PERMISSIONS as s } from "../../constants/permissions.js";
10
10
  import { isNative as c, nativeNotify as l } from "../../utils/nativeBridge.js";
11
11
  import u from "../components/workflow/WorkflowDeleteDialog.js";
12
12
  import d from "../components/workflow/WorkflowCard.js";
13
- import { memo as f, useCallback as p, useMemo as m, useState as h } from "react";
14
- import { AccessDenied as g, PermissionButton as _ } from "@burdenoff/fe-libs/shared/components";
15
- import { usePermissions as v } from "@burdenoff/fe-libs/shared/providers/shell";
16
- import { AlertCircle as y, Plus as b, Search as x, Workflow as S } from "lucide-react";
17
- import { useI18n as C } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
18
- import { AuroraBackground as w, Button as T, GradientText as E, IllustratedEmptyState as D, Input as O, PagePurpose as k, Spinner as A } from "@burdenoff/fe-libs/ui";
19
- import { Fragment as j, jsx as M, jsxs as N } from "react/jsx-runtime";
13
+ import { memo as f, useCallback as p, useMemo as ee, useState as m } from "react";
14
+ import { AccessDenied as h, PermissionButton as g } from "@burdenoff/fe-libs/shared/components";
15
+ import { usePermissions as _ } from "@burdenoff/fe-libs/shared/providers/shell";
16
+ import { AlertCircle as v, Plus as y, Search as b, Workflow as x } from "lucide-react";
17
+ import { useI18n as te } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
18
+ import { AuroraBackground as S, Button as C, GradientText as w, IllustratedEmptyState as T, Input as E, PagePurpose as ne, Spinner as re } from "@burdenoff/fe-libs/ui";
19
+ import { Fragment as ie, jsx as D, jsxs as O } from "react/jsx-runtime";
20
20
  //#region src/bigconsole/pages/WorkflowsPage.tsx
21
- var P = f(function() {
22
- let f = t(), { t: P } = C(), { hasPermission: F, isLoading: I } = v(), L = F(s.WORKFLOW_READ), R = F(s.WORKFLOW_UPDATE), z = F(s.WORKFLOW_DELETE), [B, V] = h("all"), H = e((e) => e.searchQuery), U = e((e) => e.setSearchQuery), ee = e((e) => e.deleteDialogOpen), W = e((e) => e.currentWorkflow), G = e((e) => e.openDeleteDialog), K = e((e) => e.closeDeleteDialog), { loading: q, error: J, workflows: Y, deleteWorkflow: X, refetch: Z } = a(), Q = m(() => {
23
- let e = [...Y];
24
- if (B !== "all" && (e = e.filter((e) => e.status === B)), H.trim()) {
25
- let t = H.toLowerCase();
21
+ var k = f(function() {
22
+ let f = t(), { t: k } = te(), { hasPermission: A, isLoading: j } = _(), M = A(s.WORKFLOW_READ), N = A(s.WORKFLOW_UPDATE), P = A(s.WORKFLOW_DELETE), F = A(s.WORKFLOW_EXECUTE), [I, L] = m("all"), [R, z] = m(null), B = e((e) => e.searchQuery), V = e((e) => e.setSearchQuery), H = e((e) => e.deleteDialogOpen), U = e((e) => e.currentWorkflow), W = e((e) => e.openDeleteDialog), G = e((e) => e.closeDeleteDialog), { loading: K, error: q, workflows: J, deleteWorkflow: Y, refetch: ae } = a(), { executeWorkflow: X } = a({ skipFetch: !0 }), oe = e((e) => e.workflowUIStates), Z = ee(() => {
23
+ let e = [...J];
24
+ if (I !== "all" && (e = e.filter((e) => e.status === I)), B.trim()) {
25
+ let t = B.toLowerCase();
26
26
  e = e.filter((e) => e.name.toLowerCase().includes(t) || e.key.toLowerCase().includes(t) || e.description?.toLowerCase().includes(t));
27
27
  }
28
28
  return e.sort((e, t) => new Date(t.updatedAt).getTime() - new Date(e.updatedAt).getTime());
29
29
  }, [
30
- Y,
31
- B,
32
- H
33
- ]), $ = p(() => {
30
+ J,
31
+ I,
32
+ B
33
+ ]), Q = p(() => {
34
34
  f(o());
35
- }, [f]), te = p((e) => {
35
+ }, [f]), se = p((e) => {
36
36
  f(`/workflows/${e}`);
37
- }, [f]), ne = p((e) => {
37
+ }, [f]), ce = p((e) => {
38
38
  f(`/workflows/${e}/edit`);
39
- }, [f]), re = p((e) => {
40
- let t = Y.find((t) => t.id === e);
41
- t && G(t);
42
- }, [Y, G]), ie = p(async () => {
43
- if (W) {
39
+ }, [f]), le = p((e) => {
40
+ let t = J.find((t) => t.id === e);
41
+ t && W(t);
42
+ }, [J, W]), $ = p(async (e) => {
43
+ let t = J.find((t) => t.id === e);
44
+ if (t) {
45
+ if (z(null), await X(e)) {
46
+ c() && l("success"), z({
47
+ workflowName: t.name,
48
+ ok: !0,
49
+ message: k("bigconsole.workflows.run.triggered", "Run triggered. The execution count and last-run time update as the run progresses.")
50
+ });
51
+ return;
52
+ }
53
+ c() && l("error"), z({
54
+ workflowName: t.name,
55
+ ok: !1,
56
+ message: k("bigconsole.workflows.run.failed", "The workflow could not be started. Nothing has been changed.")
57
+ });
58
+ }
59
+ }, [
60
+ J,
61
+ X,
62
+ k
63
+ ]), ue = p(async () => {
64
+ if (U) {
44
65
  try {
45
- await X(W.id), c() && l("success");
66
+ await Y(U.id), c() && l("success");
46
67
  } catch (e) {
47
68
  throw c() && l("error"), e;
48
69
  }
49
- K();
70
+ G();
50
71
  }
51
72
  }, [
52
- W,
53
- X,
54
- K
55
- ]), ae = [
73
+ U,
74
+ Y,
75
+ G
76
+ ]), de = [
56
77
  {
57
78
  value: "all",
58
- label: P("bigconsole.workflows.filters.all", "All")
79
+ label: k("bigconsole.workflows.filters.all", "All")
59
80
  },
60
81
  {
61
82
  value: "draft",
62
- label: P("bigconsole.workflows.status.draft", "Draft")
83
+ label: k("bigconsole.workflows.status.draft", "Draft")
63
84
  },
64
85
  {
65
86
  value: "active",
66
- label: P("bigconsole.workflows.status.active", "Active")
87
+ label: k("bigconsole.workflows.status.active", "Active")
67
88
  },
68
89
  {
69
90
  value: "paused",
70
- label: P("bigconsole.workflows.status.paused", "Paused")
91
+ label: k("bigconsole.workflows.status.paused", "Paused")
71
92
  },
72
93
  {
73
94
  value: "error",
74
- label: P("bigconsole.workflows.status.error", "Error")
95
+ label: k("bigconsole.workflows.status.error", "Error")
75
96
  },
76
97
  {
77
98
  value: "archived",
78
- label: P("bigconsole.workflows.status.archived", "Archived")
99
+ label: k("bigconsole.workflows.status.archived", "Archived")
79
100
  }
80
101
  ];
81
- if (!I && !L) return /* @__PURE__ */ M(g, {
82
- title: P("bigconsole.workflows.errors.accessDeniedTitle", "Access Denied"),
83
- description: P("bigconsole.workflows.errors.accessDeniedListDescription", "You don't have permission to view workflows.")
102
+ if (!j && !M) return /* @__PURE__ */ D(h, {
103
+ title: k("bigconsole.workflows.errors.accessDeniedTitle", "Access Denied"),
104
+ description: k("bigconsole.workflows.errors.accessDeniedListDescription", "You don't have permission to view workflows.")
84
105
  });
85
- let oe = !q && Y.length === 0, se = !q && Q.length === 0 && Y.length > 0;
86
- return /* @__PURE__ */ N("div", {
106
+ let fe = !K && J.length === 0, pe = !K && Z.length === 0 && J.length > 0;
107
+ return /* @__PURE__ */ O("div", {
87
108
  className: "min-h-dvh bg-bg-canvas",
88
109
  children: [
89
- /* @__PURE__ */ N("header", {
110
+ /* @__PURE__ */ O("header", {
90
111
  "data-section-emphasis": "bare",
91
112
  className: "relative min-w-0 overflow-hidden border-b border-[var(--color-border-seam)] bg-[image:var(--gradient-gloss-card)]",
92
- children: [/* @__PURE__ */ M(w, { intensity: "subtle" }), /* @__PURE__ */ N("div", {
113
+ children: [/* @__PURE__ */ D(S, { intensity: "subtle" }), /* @__PURE__ */ O("div", {
93
114
  className: "relative flex flex-col gap-form-gap p-[var(--space-pagePadding)] sm:flex-row sm:items-center sm:justify-between",
94
- children: [/* @__PURE__ */ N("div", {
115
+ children: [/* @__PURE__ */ O("div", {
95
116
  className: "flex min-w-0 items-center gap-form-gap",
96
- children: [/* @__PURE__ */ M("span", {
117
+ children: [/* @__PURE__ */ D("span", {
97
118
  "aria-hidden": "true",
98
119
  className: "flex h-control-lg w-[var(--size-controlHeight-lg)] shrink-0 items-center justify-center rounded-card bg-action-primary-bg",
99
- children: /* @__PURE__ */ M(S, { className: "h-icon-lg w-icon-lg text-action-primary-text" })
100
- }), /* @__PURE__ */ N("div", {
120
+ children: /* @__PURE__ */ D(x, { className: "h-icon-lg w-icon-lg text-action-primary-text" })
121
+ }), /* @__PURE__ */ O("div", {
101
122
  className: "min-w-0",
102
- children: [/* @__PURE__ */ M("h1", {
123
+ children: [/* @__PURE__ */ D("h1", {
103
124
  className: "text-2xl font-bold text-text-primary",
104
- children: /* @__PURE__ */ M(E, { children: P("bigconsole.workflows.list.title", "Workflows") })
105
- }), /* @__PURE__ */ M("p", {
125
+ children: /* @__PURE__ */ D(w, { children: k("bigconsole.workflows.list.title", "Workflows") })
126
+ }), /* @__PURE__ */ D("p", {
106
127
  className: "text-sm text-text-secondary",
107
- children: P("bigconsole.workflows.list.subtitle", "Design ETL workflows with FluidGrids and link them to DataSinks")
128
+ children: k("bigconsole.workflows.list.subtitle", "Design ETL workflows with FluidGrids and link them to DataSinks")
108
129
  })]
109
130
  })]
110
- }), /* @__PURE__ */ M("div", {
131
+ }), /* @__PURE__ */ D("div", {
111
132
  className: "flex flex-wrap items-center gap-control-gap",
112
- children: /* @__PURE__ */ M(_, {
133
+ children: /* @__PURE__ */ D(g, {
113
134
  permission: s.WORKFLOW_CREATE,
114
135
  deniedBehavior: "disable",
115
- children: /* @__PURE__ */ N(T, {
116
- onClick: $,
117
- children: [/* @__PURE__ */ M(b, { className: "h-icon-sm w-icon-sm" }), P("bigconsole.workflows.actions.designWorkflow", "Design Workflow")]
136
+ children: /* @__PURE__ */ O(C, {
137
+ onClick: Q,
138
+ children: [/* @__PURE__ */ D(y, { className: "h-icon-sm w-icon-sm" }), k("bigconsole.workflows.actions.designWorkflow", "Design Workflow")]
118
139
  })
119
140
  })
120
141
  })]
121
142
  })]
122
143
  }),
123
- /* @__PURE__ */ M("div", {
144
+ /* @__PURE__ */ D("div", {
124
145
  className: "p-[var(--space-pagePadding)]",
125
- children: /* @__PURE__ */ N(i, { children: [/* @__PURE__ */ M(k, { children: P("bigconsole.workflows.list.purpose", "Workflows are visual ETL jobs built with the FluidGrids builder — they fetch data from external systems, transform it, and load it into your BigConsole DataSinks on a schedule or on demand. Use them to keep dashboards fed with fresh, shaped data without writing glue code.") }), J ? /* @__PURE__ */ M(r, {
146
+ children: /* @__PURE__ */ O(i, { children: [/* @__PURE__ */ D(ne, { children: k("bigconsole.workflows.list.purpose", "Workflows are visual ETL jobs built with the FluidGrids builder — they fetch data from external systems, transform it, and load it into your BigConsole DataSinks on a schedule or on demand. Use them to keep dashboards fed with fresh, shaped data without writing glue code.") }), q ? /* @__PURE__ */ D(r, {
126
147
  headingLevel: 2,
127
148
  tone: "danger",
128
- title: P("bigconsole.workflows.errors.loadFailedTitle", "Failed to load Workflows"),
149
+ title: k("bigconsole.workflows.errors.loadFailedTitle", "Failed to load Workflows"),
129
150
  description: "The workflow service did not respond. Nothing has been changed — retry when you are ready.",
130
- icon: /* @__PURE__ */ M(y, { className: "h-icon-md w-icon-md" }),
131
- actions: /* @__PURE__ */ M(T, {
151
+ icon: /* @__PURE__ */ D(v, { className: "h-icon-md w-icon-md" }),
152
+ actions: /* @__PURE__ */ D(C, {
132
153
  size: "sm",
133
- onClick: () => Z(),
134
- children: P("bigconsole.workflows.actions.tryAgain", "Try Again")
154
+ onClick: () => ae(),
155
+ children: k("bigconsole.workflows.actions.tryAgain", "Try Again")
135
156
  }),
136
157
  "data-testid": "workflows-error",
137
- children: /* @__PURE__ */ M("p", {
158
+ children: /* @__PURE__ */ D("p", {
138
159
  className: "break-words text-sm text-[var(--color-status-error-text)]",
139
- children: J.message
160
+ children: q.message
140
161
  })
141
- }) : /* @__PURE__ */ N(j, { children: [/* @__PURE__ */ M(r, {
162
+ }) : /* @__PURE__ */ O(ie, { children: [/* @__PURE__ */ D(r, {
142
163
  headingLevel: 2,
143
164
  title: "Filters",
144
165
  description: "Narrow the list by status, or search by name, key or description.",
145
166
  padding: "compact",
146
167
  "data-testid": "workflows-filters",
147
- children: /* @__PURE__ */ N("div", {
168
+ children: /* @__PURE__ */ O("div", {
148
169
  className: "flex flex-col gap-form-gap lg:flex-row lg:items-center lg:justify-between",
149
- children: [/* @__PURE__ */ M("div", {
170
+ children: [/* @__PURE__ */ D("div", {
150
171
  role: "group",
151
172
  "aria-label": "Filter workflows by status",
152
173
  className: "flex flex-wrap items-center gap-1 rounded-card bg-bg-sunken p-1",
153
- children: ae.map((e) => /* @__PURE__ */ M("button", {
174
+ children: de.map((e) => /* @__PURE__ */ D("button", {
154
175
  type: "button",
155
- "aria-pressed": B === e.value,
156
- onClick: () => V(e.value),
157
- className: `min-h-[var(--size-controlHeight-md)] rounded-button px-3 text-sm font-medium motion-safe:transition-colors ${n} ${B === e.value ? "bg-bg-surface text-text-primary shadow-[var(--shadow-elevation-1)]" : "text-text-secondary hover:text-text-primary"}`,
176
+ "aria-pressed": I === e.value,
177
+ onClick: () => L(e.value),
178
+ className: `min-h-[var(--size-controlHeight-md)] rounded-button px-3 text-sm font-medium motion-safe:transition-colors ${n} ${I === e.value ? "bg-bg-surface text-text-primary shadow-[var(--shadow-elevation-1)]" : "text-text-secondary hover:text-text-primary"}`,
158
179
  children: e.label
159
180
  }, e.value))
160
- }), /* @__PURE__ */ N("div", {
181
+ }), /* @__PURE__ */ O("div", {
161
182
  className: "relative w-full lg:max-w-[360px]",
162
- children: [/* @__PURE__ */ M(x, {
183
+ children: [/* @__PURE__ */ D(b, {
163
184
  "aria-hidden": "true",
164
185
  className: "pointer-events-none absolute left-3 top-1/2 h-icon-sm w-icon-sm -translate-y-1/2 text-text-muted"
165
- }), /* @__PURE__ */ M(O, {
186
+ }), /* @__PURE__ */ D(E, {
166
187
  type: "text",
167
- "aria-label": P("bigconsole.workflows.filters.searchPlaceholder", "Search workflows..."),
168
- placeholder: P("bigconsole.workflows.filters.searchPlaceholder", "Search workflows..."),
169
- value: H,
170
- onChange: (e) => U(e.target.value),
188
+ "aria-label": k("bigconsole.workflows.filters.searchPlaceholder", "Search workflows..."),
189
+ placeholder: k("bigconsole.workflows.filters.searchPlaceholder", "Search workflows..."),
190
+ value: B,
191
+ onChange: (e) => V(e.target.value),
171
192
  className: "h-control-md pl-10"
172
193
  })]
173
194
  })]
174
195
  })
175
- }), /* @__PURE__ */ N(r, {
196
+ }), /* @__PURE__ */ O(r, {
176
197
  headingLevel: 2,
177
198
  emphasis: "primary",
178
- title: P("bigconsole.workflows.list.title", "Workflows"),
199
+ title: k("bigconsole.workflows.list.title", "Workflows"),
179
200
  description: "Every workflow in this workspace, most recently updated first.",
180
- meta: /* @__PURE__ */ N("span", {
201
+ meta: /* @__PURE__ */ O("span", {
181
202
  className: "rounded-pill bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
182
- children: [Q.length, Q.length === Y.length ? "" : ` of ${Y.length}`]
203
+ children: [Z.length, Z.length === J.length ? "" : ` of ${J.length}`]
183
204
  }),
184
205
  "data-testid": "workflows-results",
185
206
  children: [
186
- q && Y.length === 0 && /* @__PURE__ */ M("div", {
207
+ K && J.length === 0 && /* @__PURE__ */ D("div", {
187
208
  className: "flex min-h-[240px] items-center justify-center",
188
- children: /* @__PURE__ */ N("div", {
209
+ children: /* @__PURE__ */ O("div", {
189
210
  className: "text-center",
190
- children: [/* @__PURE__ */ M(A, {
211
+ children: [/* @__PURE__ */ D(re, {
191
212
  size: "lg",
192
213
  className: "mb-form-gap"
193
- }), /* @__PURE__ */ M("p", {
214
+ }), /* @__PURE__ */ D("p", {
194
215
  className: "text-sm text-text-muted",
195
- children: P("bigconsole.workflows.list.loading", "Loading workflows...")
216
+ children: k("bigconsole.workflows.list.loading", "Loading workflows...")
196
217
  })]
197
218
  })
198
219
  }),
199
- oe && /* @__PURE__ */ M(D, {
220
+ fe && /* @__PURE__ */ D(T, {
200
221
  illustration: "empty-data",
201
- title: P("bigconsole.workflows.list.emptyTitle", "No Workflows yet"),
202
- description: P("bigconsole.workflows.list.emptyDescription", "Design ETL workflows with the FluidGrids visual builder to fetch, transform, and deliver data into BigConsole DataSinks."),
203
- action: /* @__PURE__ */ M(_, {
222
+ title: k("bigconsole.workflows.list.emptyTitle", "No Workflows yet"),
223
+ description: k("bigconsole.workflows.list.emptyDescription", "Design ETL workflows with the FluidGrids visual builder to fetch, transform, and deliver data into BigConsole DataSinks."),
224
+ action: /* @__PURE__ */ D(g, {
204
225
  permission: s.WORKFLOW_CREATE,
205
226
  deniedBehavior: "disable",
206
- children: /* @__PURE__ */ N(T, {
207
- onClick: $,
208
- children: [/* @__PURE__ */ M(b, { className: "h-icon-sm w-icon-sm" }), P("bigconsole.workflows.actions.designWorkflow", "Design Workflow")]
227
+ children: /* @__PURE__ */ O(C, {
228
+ onClick: Q,
229
+ children: [/* @__PURE__ */ D(y, { className: "h-icon-sm w-icon-sm" }), k("bigconsole.workflows.actions.designWorkflow", "Design Workflow")]
209
230
  })
210
231
  })
211
232
  }),
212
- se && /* @__PURE__ */ M(D, {
233
+ pe && /* @__PURE__ */ D(T, {
213
234
  illustration: "empty-search",
214
- title: P("bigconsole.workflows.list.noResultsTitle", "No matching workflows"),
215
- description: P("bigconsole.workflows.list.noResultsDescription", "No workflows match your filters")
235
+ title: k("bigconsole.workflows.list.noResultsTitle", "No matching workflows"),
236
+ description: k("bigconsole.workflows.list.noResultsDescription", "No workflows match your filters")
237
+ }),
238
+ R && /* @__PURE__ */ O("div", {
239
+ role: "status",
240
+ "aria-live": "polite",
241
+ className: `mb-form-gap flex flex-wrap items-center justify-between gap-icon-gap rounded-input border p-3 text-sm ${R.ok ? "border-[var(--color-status-success-border)] bg-[var(--color-status-success-bgSubtle)] text-[var(--color-status-success-text)]" : "border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)]"}`,
242
+ "data-testid": "workflows-run-outcome",
243
+ children: [/* @__PURE__ */ O("span", {
244
+ className: "min-w-0",
245
+ children: [
246
+ /* @__PURE__ */ D("strong", {
247
+ className: "font-semibold",
248
+ children: R.workflowName
249
+ }),
250
+ " — ",
251
+ R.message
252
+ ]
253
+ }), /* @__PURE__ */ D("button", {
254
+ type: "button",
255
+ onClick: () => z(null),
256
+ className: "rounded-button px-2 py-1 text-xs font-medium underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface",
257
+ children: k("bigconsole.workflows.run.dismiss", "Dismiss")
258
+ })]
216
259
  }),
217
- Q.length > 0 && /* @__PURE__ */ M("div", {
260
+ Z.length > 0 && /* @__PURE__ */ D("div", {
218
261
  className: "grid grid-cols-1 gap-form-gap sm:grid-cols-2 xl:grid-cols-3",
219
- children: Q.map((e) => /* @__PURE__ */ M(d, {
262
+ children: Z.map((e) => /* @__PURE__ */ D(d, {
220
263
  workflow: e,
221
- onView: te,
222
- onEdit: R ? ne : void 0,
223
- onDelete: z ? re : void 0
264
+ onView: se,
265
+ onEdit: N ? ce : void 0,
266
+ onDelete: P ? le : void 0,
267
+ onRun: F ? $ : void 0,
268
+ isRunning: oe.get(e.id)?.isExecuting ?? !1
224
269
  }, e.id))
225
270
  })
226
271
  ]
227
272
  })] })] })
228
273
  }),
229
- W && /* @__PURE__ */ M(u, {
230
- workflow: W,
231
- isOpen: ee,
232
- onClose: K,
233
- onConfirm: ie
274
+ U && /* @__PURE__ */ D(u, {
275
+ workflow: U,
276
+ isOpen: H,
277
+ onClose: G,
278
+ onConfirm: ue
234
279
  })
235
280
  ]
236
281
  });
237
282
  });
238
283
  //#endregion
239
- export { P as WorkflowsPage };
284
+ export { k as WorkflowsPage };
240
285
 
241
286
  //# sourceMappingURL=WorkflowsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkflowsPage.js","names":[],"sources":["../../../src/bigconsole/pages/WorkflowsPage.tsx"],"sourcesContent":["/**\n * Workflows Page\n *\n * List and manage FluidGrids Workflows.\n *\n * Section hierarchy (BOFF-5634):\n * - page header strip .............. `bare` — identity + the one create action\n * - \"Filters\" ...................... `standard` — status tabs + search, one labelled group\n * - \"Workflows\" .................... `primary` — the result set this route exists for;\n * loading / empty / no-results / grid all\n * live inside it so the page keeps its shape\n * - load failure ................... `danger` — in-page status section, not a page swap\n */\n\nimport { memo, useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AccessDenied, PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeNotify } from '../../utils/nativeBridge';\nimport { newWorkflowPath } from '../BigConsoleRoutes';\nimport { useWorkflowOperations } from '../hooks/useWorkflowOperations';\nimport { useWorkflowStore } from '../store/workflowStore';\nimport { WorkflowCard } from '../components/workflow/WorkflowCard';\nimport { WorkflowDeleteDialog } from '../components/workflow/WorkflowDeleteDialog';\nimport { Section, SectionStack, SECTION_FOCUS_RING } from '../components/common';\nimport type { FluidGridsWorkflowStatus } from '../types';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { AuroraBackground, GradientText, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport { Plus, Search, Workflow, AlertCircle } from 'lucide-react';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ntype StatusFilter = 'all' | FluidGridsWorkflowStatus;\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const WorkflowsPage = memo(function WorkflowsPage() {\n const navigate = useAppNavigate();\n const { t } = useI18n();\n\n // RBAC permissions\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_READ);\n const canUpdate = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_UPDATE);\n const canDelete = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_DELETE);\n\n // Local state\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('all');\n\n // Store state\n const searchQuery = useWorkflowStore((state) => state.searchQuery);\n const setSearchQuery = useWorkflowStore((state) => state.setSearchQuery);\n const deleteDialogOpen = useWorkflowStore((state) => state.deleteDialogOpen);\n const currentWorkflow = useWorkflowStore((state) => state.currentWorkflow);\n const openDeleteDialog = useWorkflowStore((state) => state.openDeleteDialog);\n const closeDeleteDialog = useWorkflowStore((state) => state.closeDeleteDialog);\n\n // Operations\n const { loading, error, workflows, deleteWorkflow, refetch } = useWorkflowOperations();\n\n // Filter workflows\n const filteredWorkflows = useMemo(() => {\n let filtered = [...workflows];\n\n // Apply status filter\n if (statusFilter !== 'all') {\n filtered = filtered.filter((wf) => wf.status === statusFilter);\n }\n\n // Apply search filter\n if (searchQuery.trim()) {\n const query = searchQuery.toLowerCase();\n filtered = filtered.filter(\n (wf) =>\n wf.name.toLowerCase().includes(query) ||\n wf.key.toLowerCase().includes(query) ||\n wf.description?.toLowerCase().includes(query)\n );\n }\n\n // Sort by updatedAt descending\n return filtered.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());\n }, [workflows, statusFilter, searchQuery]);\n\n // Handlers\n const handleCreateWorkflow = useCallback(() => {\n navigate(newWorkflowPath());\n }, [navigate]);\n\n const handleViewWorkflow = useCallback(\n (id: string) => {\n navigate(`/workflows/${id}`);\n },\n [navigate]\n );\n\n const handleEditWorkflow = useCallback(\n (id: string) => {\n navigate(`/workflows/${id}/edit`);\n },\n [navigate]\n );\n\n const handleDeleteClick = useCallback(\n (id: string) => {\n const workflow = workflows.find((wf) => wf.id === id);\n if (workflow) {\n openDeleteDialog(workflow);\n }\n },\n [workflows, openDeleteDialog]\n );\n\n const handleDeleteConfirm = useCallback(async () => {\n if (currentWorkflow) {\n try {\n await deleteWorkflow(currentWorkflow.id);\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n closeDeleteDialog();\n }\n }, [currentWorkflow, deleteWorkflow, closeDeleteDialog]);\n\n // Status filter options\n const statusOptions: { value: StatusFilter; label: string }[] = [\n { value: 'all', label: t('bigconsole.workflows.filters.all', 'All') },\n { value: 'draft', label: t('bigconsole.workflows.status.draft', 'Draft') },\n { value: 'active', label: t('bigconsole.workflows.status.active', 'Active') },\n { value: 'paused', label: t('bigconsole.workflows.status.paused', 'Paused') },\n { value: 'error', label: t('bigconsole.workflows.status.error', 'Error') },\n { value: 'archived', label: t('bigconsole.workflows.status.archived', 'Archived') },\n ];\n\n // Page-level read gate — only after permissions have finished loading so we\n // don't flash Access Denied while the permission set is still being fetched.\n if (!permissionsLoading && !canRead) {\n return (\n <AccessDenied\n title={t('bigconsole.workflows.errors.accessDeniedTitle', 'Access Denied')}\n description={t(\n 'bigconsole.workflows.errors.accessDeniedListDescription',\n \"You don't have permission to view workflows.\"\n )}\n />\n );\n }\n\n const isEmpty = !loading && workflows.length === 0;\n const isNoResults = !loading && filteredWorkflows.length === 0 && workflows.length > 0;\n\n return (\n <div className=\"min-h-dvh bg-bg-canvas\">\n {/* ------------------------------------------------------------------ *\n * Page header strip — `bare` emphasis: structural only. It carries the\n * page identity and the single create action, and must never out-weigh\n * the result set below it. Rendered as <header> (the page banner) so it\n * stays a landmark without competing with the labelled sections below.\n * The band itself was invisible before: `border-border-seam` and\n * `bg-gloss-card` are unregistered utilities that emit no CSS.\n * ------------------------------------------------------------------ */}\n <header\n data-section-emphasis=\"bare\"\n className=\"relative min-w-0 overflow-hidden border-b border-[var(--color-border-seam)] bg-[image:var(--gradient-gloss-card)]\"\n >\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"relative flex flex-col gap-form-gap p-[var(--space-pagePadding)] sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex min-w-0 items-center gap-form-gap\">\n <span\n aria-hidden=\"true\"\n className=\"flex h-control-lg w-[var(--size-controlHeight-lg)] shrink-0 items-center justify-center rounded-card bg-action-primary-bg\"\n >\n <Workflow className=\"h-icon-lg w-icon-lg text-action-primary-text\" />\n </span>\n <div className=\"min-w-0\">\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>{t('bigconsole.workflows.list.title', 'Workflows')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'bigconsole.workflows.list.subtitle',\n 'Design ETL workflows with FluidGrids and link them to DataSinks'\n )}\n </p>\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-control-gap\">\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.WORKFLOW_CREATE} deniedBehavior=\"disable\">\n <Button onClick={handleCreateWorkflow}>\n <Plus className=\"h-icon-sm w-icon-sm\" />\n {t('bigconsole.workflows.actions.designWorkflow', 'Design Workflow')}\n </Button>\n </PermissionButton>\n </div>\n </div>\n </header>\n\n {/* Main Content */}\n <div className=\"p-[var(--space-pagePadding)]\">\n <SectionStack>\n <PagePurpose>\n {t(\n 'bigconsole.workflows.list.purpose',\n 'Workflows are visual ETL jobs built with the FluidGrids builder — they fetch data from external systems, transform it, and load it into your BigConsole DataSinks on a schedule or on demand. Use them to keep dashboards fed with fresh, shaped data without writing glue code.'\n )}\n </PagePurpose>\n\n {error ? (\n /* Load failure reads as a status section inside the page — the\n header band and the create action stay reachable. */\n <Section\n headingLevel={2}\n tone=\"danger\"\n title={t('bigconsole.workflows.errors.loadFailedTitle', 'Failed to load Workflows')}\n description=\"The workflow service did not respond. Nothing has been changed — retry when you are ready.\"\n icon={<AlertCircle className=\"h-icon-md w-icon-md\" />}\n actions={\n <Button size=\"sm\" onClick={() => refetch()}>\n {t('bigconsole.workflows.actions.tryAgain', 'Try Again')}\n </Button>\n }\n data-testid=\"workflows-error\"\n >\n <p className=\"break-words text-sm text-[var(--color-status-error-text)]\">{error.message}</p>\n </Section>\n ) : (\n <>\n {/* Configuration: the two filter controls read as ONE group. */}\n <Section\n headingLevel={2}\n title=\"Filters\"\n description=\"Narrow the list by status, or search by name, key or description.\"\n padding=\"compact\"\n data-testid=\"workflows-filters\"\n >\n <div className=\"flex flex-col gap-form-gap lg:flex-row lg:items-center lg:justify-between\">\n <div\n role=\"group\"\n aria-label=\"Filter workflows by status\"\n className=\"flex flex-wrap items-center gap-1 rounded-card bg-bg-sunken p-1\"\n >\n {statusOptions.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n aria-pressed={statusFilter === option.value}\n onClick={() => setStatusFilter(option.value)}\n className={`min-h-[var(--size-controlHeight-md)] rounded-button px-3 text-sm font-medium motion-safe:transition-colors ${SECTION_FOCUS_RING} ${\n statusFilter === option.value\n ? 'bg-bg-surface text-text-primary shadow-[var(--shadow-elevation-1)]'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n {option.label}\n </button>\n ))}\n </div>\n\n <div className=\"relative w-full lg:max-w-[360px]\">\n <Search\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute left-3 top-1/2 h-icon-sm w-icon-sm -translate-y-1/2 text-text-muted\"\n />\n <Input\n type=\"text\"\n aria-label={t('bigconsole.workflows.filters.searchPlaceholder', 'Search workflows...')}\n placeholder={t('bigconsole.workflows.filters.searchPlaceholder', 'Search workflows...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"h-control-md pl-10\"\n />\n </div>\n </div>\n </Section>\n\n {/* ------------------------------------------------------------ *\n * The result set — the ONE `primary` block on this route.\n * Loading, empty, no-results and the grid all render inside it,\n * so the page keeps the same shape whatever the data does.\n * ------------------------------------------------------------ */}\n <Section\n headingLevel={2}\n emphasis=\"primary\"\n title={t('bigconsole.workflows.list.title', 'Workflows')}\n description=\"Every workflow in this workspace, most recently updated first.\"\n meta={\n <span className=\"rounded-pill bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {filteredWorkflows.length}\n {filteredWorkflows.length !== workflows.length ? ` of ${workflows.length}` : ''}\n </span>\n }\n data-testid=\"workflows-results\"\n >\n {loading && workflows.length === 0 && (\n <div className=\"flex min-h-[240px] items-center justify-center\">\n <div className=\"text-center\">\n <Spinner size=\"lg\" className=\"mb-form-gap\" />\n <p className=\"text-sm text-text-muted\">\n {t('bigconsole.workflows.list.loading', 'Loading workflows...')}\n </p>\n </div>\n </div>\n )}\n\n {isEmpty && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('bigconsole.workflows.list.emptyTitle', 'No Workflows yet')}\n description={t(\n 'bigconsole.workflows.list.emptyDescription',\n 'Design ETL workflows with the FluidGrids visual builder to fetch, transform, and deliver data into BigConsole DataSinks.'\n )}\n action={\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.WORKFLOW_CREATE} deniedBehavior=\"disable\">\n <Button onClick={handleCreateWorkflow}>\n <Plus className=\"h-icon-sm w-icon-sm\" />\n {t('bigconsole.workflows.actions.designWorkflow', 'Design Workflow')}\n </Button>\n </PermissionButton>\n }\n />\n )}\n\n {isNoResults && (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={t('bigconsole.workflows.list.noResultsTitle', 'No matching workflows')}\n description={t('bigconsole.workflows.list.noResultsDescription', 'No workflows match your filters')}\n />\n )}\n\n {filteredWorkflows.length > 0 && (\n <div className=\"grid grid-cols-1 gap-form-gap sm:grid-cols-2 xl:grid-cols-3\">\n {filteredWorkflows.map((workflow) => (\n <WorkflowCard\n key={workflow.id}\n workflow={workflow}\n onView={handleViewWorkflow}\n onEdit={canUpdate ? handleEditWorkflow : undefined}\n onDelete={canDelete ? handleDeleteClick : undefined}\n />\n ))}\n </div>\n )}\n </Section>\n </>\n )}\n </SectionStack>\n </div>\n\n {/* Delete Dialog */}\n {currentWorkflow && (\n <WorkflowDeleteDialog\n workflow={currentWorkflow}\n isOpen={deleteDialogOpen}\n onClose={closeDeleteDialog}\n onConfirm={handleDeleteConfirm}\n />\n )}\n </div>\n );\n});\n\nexport default WorkflowsPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"WorkflowsPage.js","names":[],"sources":["../../../src/bigconsole/pages/WorkflowsPage.tsx"],"sourcesContent":["/**\n * Workflows Page\n *\n * List and manage FluidGrids Workflows.\n *\n * Section hierarchy (BOFF-5634):\n * - page header strip .............. `bare` — identity + the one create action\n * - \"Filters\" ...................... `standard` — status tabs + search, one labelled group\n * - \"Workflows\" .................... `primary` — the result set this route exists for;\n * loading / empty / no-results / grid all\n * live inside it so the page keeps its shape\n * - load failure ................... `danger` — in-page status section, not a page swap\n */\n\nimport { memo, useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AccessDenied, PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeNotify } from '../../utils/nativeBridge';\nimport { newWorkflowPath } from '../BigConsoleRoutes';\nimport { useWorkflowOperations } from '../hooks/useWorkflowOperations';\nimport { useWorkflowStore } from '../store/workflowStore';\nimport { WorkflowCard } from '../components/workflow/WorkflowCard';\nimport { WorkflowDeleteDialog } from '../components/workflow/WorkflowDeleteDialog';\nimport { Section, SectionStack, SECTION_FOCUS_RING } from '../components/common';\nimport type { FluidGridsWorkflowStatus } from '../types';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { AuroraBackground, GradientText, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport { Plus, Search, Workflow, AlertCircle } from 'lucide-react';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ntype StatusFilter = 'all' | FluidGridsWorkflowStatus;\n\n/** Result of the last manual run triggered from a card. */\ninterface RunOutcome {\n workflowName: string;\n ok: boolean;\n message: string;\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const WorkflowsPage = memo(function WorkflowsPage() {\n const navigate = useAppNavigate();\n const { t } = useI18n();\n\n // RBAC permissions\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_READ);\n const canUpdate = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_UPDATE);\n const canDelete = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_DELETE);\n const canExecute = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_EXECUTE);\n\n // Local state\n const [statusFilter, setStatusFilter] = useState<StatusFilter>('all');\n /** Outcome of the most recent run, shown inline above the grid. */\n const [runOutcome, setRunOutcome] = useState<RunOutcome | null>(null);\n\n // Store state\n const searchQuery = useWorkflowStore((state) => state.searchQuery);\n const setSearchQuery = useWorkflowStore((state) => state.setSearchQuery);\n const deleteDialogOpen = useWorkflowStore((state) => state.deleteDialogOpen);\n const currentWorkflow = useWorkflowStore((state) => state.currentWorkflow);\n const openDeleteDialog = useWorkflowStore((state) => state.openDeleteDialog);\n const closeDeleteDialog = useWorkflowStore((state) => state.closeDeleteDialog);\n\n // Operations\n const { loading, error, workflows, deleteWorkflow, refetch } = useWorkflowOperations();\n\n /**\n * Runs go through a SECOND hook instance on purpose. `useWorkflowOperations`\n * keeps one shared `error` for every operation, and this page swaps the whole\n * result set for a \"Failed to load Workflows\" panel whenever that error is\n * set — so a failed run on one card would wipe the list. A separate instance\n * (fetch skipped) keeps run failures local while still writing through to the\n * shared workflow store, so the card's execution count and last-run time\n * update in place.\n */\n const { executeWorkflow } = useWorkflowOperations({ skipFetch: true });\n\n // Per-card execution flags, maintained by the hook's executeWorkflow.\n const workflowUIStates = useWorkflowStore((state) => state.workflowUIStates);\n\n // Filter workflows\n const filteredWorkflows = useMemo(() => {\n let filtered = [...workflows];\n\n // Apply status filter\n if (statusFilter !== 'all') {\n filtered = filtered.filter((wf) => wf.status === statusFilter);\n }\n\n // Apply search filter\n if (searchQuery.trim()) {\n const query = searchQuery.toLowerCase();\n filtered = filtered.filter(\n (wf) =>\n wf.name.toLowerCase().includes(query) ||\n wf.key.toLowerCase().includes(query) ||\n wf.description?.toLowerCase().includes(query)\n );\n }\n\n // Sort by updatedAt descending\n return filtered.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());\n }, [workflows, statusFilter, searchQuery]);\n\n // Handlers\n const handleCreateWorkflow = useCallback(() => {\n navigate(newWorkflowPath());\n }, [navigate]);\n\n const handleViewWorkflow = useCallback(\n (id: string) => {\n navigate(`/workflows/${id}`);\n },\n [navigate]\n );\n\n const handleEditWorkflow = useCallback(\n (id: string) => {\n navigate(`/workflows/${id}/edit`);\n },\n [navigate]\n );\n\n const handleDeleteClick = useCallback(\n (id: string) => {\n const workflow = workflows.find((wf) => wf.id === id);\n if (workflow) {\n openDeleteDialog(workflow);\n }\n },\n [workflows, openDeleteDialog]\n );\n\n const handleRunWorkflow = useCallback(\n async (id: string) => {\n const workflow = workflows.find((wf) => wf.id === id);\n if (!workflow) return;\n\n setRunOutcome(null);\n // `executeWorkflow` swallows the Apollo error and resolves to null, so a\n // null result IS the failure signal — never treat it as a silent success.\n const result = await executeWorkflow(id);\n\n if (result) {\n if (isNative()) void nativeNotify('success');\n setRunOutcome({\n workflowName: workflow.name,\n ok: true,\n message: t(\n 'bigconsole.workflows.run.triggered',\n 'Run triggered. The execution count and last-run time update as the run progresses.'\n ),\n });\n return;\n }\n\n if (isNative()) void nativeNotify('error');\n setRunOutcome({\n workflowName: workflow.name,\n ok: false,\n message: t('bigconsole.workflows.run.failed', 'The workflow could not be started. Nothing has been changed.'),\n });\n },\n [workflows, executeWorkflow, t]\n );\n\n const handleDeleteConfirm = useCallback(async () => {\n if (currentWorkflow) {\n try {\n await deleteWorkflow(currentWorkflow.id);\n if (isNative()) void nativeNotify('success');\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n throw err;\n }\n closeDeleteDialog();\n }\n }, [currentWorkflow, deleteWorkflow, closeDeleteDialog]);\n\n // Status filter options\n const statusOptions: { value: StatusFilter; label: string }[] = [\n { value: 'all', label: t('bigconsole.workflows.filters.all', 'All') },\n { value: 'draft', label: t('bigconsole.workflows.status.draft', 'Draft') },\n { value: 'active', label: t('bigconsole.workflows.status.active', 'Active') },\n { value: 'paused', label: t('bigconsole.workflows.status.paused', 'Paused') },\n { value: 'error', label: t('bigconsole.workflows.status.error', 'Error') },\n { value: 'archived', label: t('bigconsole.workflows.status.archived', 'Archived') },\n ];\n\n // Page-level read gate — only after permissions have finished loading so we\n // don't flash Access Denied while the permission set is still being fetched.\n if (!permissionsLoading && !canRead) {\n return (\n <AccessDenied\n title={t('bigconsole.workflows.errors.accessDeniedTitle', 'Access Denied')}\n description={t(\n 'bigconsole.workflows.errors.accessDeniedListDescription',\n \"You don't have permission to view workflows.\"\n )}\n />\n );\n }\n\n const isEmpty = !loading && workflows.length === 0;\n const isNoResults = !loading && filteredWorkflows.length === 0 && workflows.length > 0;\n\n return (\n <div className=\"min-h-dvh bg-bg-canvas\">\n {/* ------------------------------------------------------------------ *\n * Page header strip — `bare` emphasis: structural only. It carries the\n * page identity and the single create action, and must never out-weigh\n * the result set below it. Rendered as <header> (the page banner) so it\n * stays a landmark without competing with the labelled sections below.\n * The band itself was invisible before: `border-border-seam` and\n * `bg-gloss-card` are unregistered utilities that emit no CSS.\n * ------------------------------------------------------------------ */}\n <header\n data-section-emphasis=\"bare\"\n className=\"relative min-w-0 overflow-hidden border-b border-[var(--color-border-seam)] bg-[image:var(--gradient-gloss-card)]\"\n >\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"relative flex flex-col gap-form-gap p-[var(--space-pagePadding)] sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex min-w-0 items-center gap-form-gap\">\n <span\n aria-hidden=\"true\"\n className=\"flex h-control-lg w-[var(--size-controlHeight-lg)] shrink-0 items-center justify-center rounded-card bg-action-primary-bg\"\n >\n <Workflow className=\"h-icon-lg w-icon-lg text-action-primary-text\" />\n </span>\n <div className=\"min-w-0\">\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>{t('bigconsole.workflows.list.title', 'Workflows')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {t(\n 'bigconsole.workflows.list.subtitle',\n 'Design ETL workflows with FluidGrids and link them to DataSinks'\n )}\n </p>\n </div>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-control-gap\">\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.WORKFLOW_CREATE} deniedBehavior=\"disable\">\n <Button onClick={handleCreateWorkflow}>\n <Plus className=\"h-icon-sm w-icon-sm\" />\n {t('bigconsole.workflows.actions.designWorkflow', 'Design Workflow')}\n </Button>\n </PermissionButton>\n </div>\n </div>\n </header>\n\n {/* Main Content */}\n <div className=\"p-[var(--space-pagePadding)]\">\n <SectionStack>\n <PagePurpose>\n {t(\n 'bigconsole.workflows.list.purpose',\n 'Workflows are visual ETL jobs built with the FluidGrids builder — they fetch data from external systems, transform it, and load it into your BigConsole DataSinks on a schedule or on demand. Use them to keep dashboards fed with fresh, shaped data without writing glue code.'\n )}\n </PagePurpose>\n\n {error ? (\n /* Load failure reads as a status section inside the page — the\n header band and the create action stay reachable. */\n <Section\n headingLevel={2}\n tone=\"danger\"\n title={t('bigconsole.workflows.errors.loadFailedTitle', 'Failed to load Workflows')}\n description=\"The workflow service did not respond. Nothing has been changed — retry when you are ready.\"\n icon={<AlertCircle className=\"h-icon-md w-icon-md\" />}\n actions={\n <Button size=\"sm\" onClick={() => refetch()}>\n {t('bigconsole.workflows.actions.tryAgain', 'Try Again')}\n </Button>\n }\n data-testid=\"workflows-error\"\n >\n <p className=\"break-words text-sm text-[var(--color-status-error-text)]\">{error.message}</p>\n </Section>\n ) : (\n <>\n {/* Configuration: the two filter controls read as ONE group. */}\n <Section\n headingLevel={2}\n title=\"Filters\"\n description=\"Narrow the list by status, or search by name, key or description.\"\n padding=\"compact\"\n data-testid=\"workflows-filters\"\n >\n <div className=\"flex flex-col gap-form-gap lg:flex-row lg:items-center lg:justify-between\">\n <div\n role=\"group\"\n aria-label=\"Filter workflows by status\"\n className=\"flex flex-wrap items-center gap-1 rounded-card bg-bg-sunken p-1\"\n >\n {statusOptions.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n aria-pressed={statusFilter === option.value}\n onClick={() => setStatusFilter(option.value)}\n className={`min-h-[var(--size-controlHeight-md)] rounded-button px-3 text-sm font-medium motion-safe:transition-colors ${SECTION_FOCUS_RING} ${\n statusFilter === option.value\n ? 'bg-bg-surface text-text-primary shadow-[var(--shadow-elevation-1)]'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n {option.label}\n </button>\n ))}\n </div>\n\n <div className=\"relative w-full lg:max-w-[360px]\">\n <Search\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute left-3 top-1/2 h-icon-sm w-icon-sm -translate-y-1/2 text-text-muted\"\n />\n <Input\n type=\"text\"\n aria-label={t('bigconsole.workflows.filters.searchPlaceholder', 'Search workflows...')}\n placeholder={t('bigconsole.workflows.filters.searchPlaceholder', 'Search workflows...')}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"h-control-md pl-10\"\n />\n </div>\n </div>\n </Section>\n\n {/* ------------------------------------------------------------ *\n * The result set — the ONE `primary` block on this route.\n * Loading, empty, no-results and the grid all render inside it,\n * so the page keeps the same shape whatever the data does.\n * ------------------------------------------------------------ */}\n <Section\n headingLevel={2}\n emphasis=\"primary\"\n title={t('bigconsole.workflows.list.title', 'Workflows')}\n description=\"Every workflow in this workspace, most recently updated first.\"\n meta={\n <span className=\"rounded-pill bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {filteredWorkflows.length}\n {filteredWorkflows.length !== workflows.length ? ` of ${workflows.length}` : ''}\n </span>\n }\n data-testid=\"workflows-results\"\n >\n {loading && workflows.length === 0 && (\n <div className=\"flex min-h-[240px] items-center justify-center\">\n <div className=\"text-center\">\n <Spinner size=\"lg\" className=\"mb-form-gap\" />\n <p className=\"text-sm text-text-muted\">\n {t('bigconsole.workflows.list.loading', 'Loading workflows...')}\n </p>\n </div>\n </div>\n )}\n\n {isEmpty && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('bigconsole.workflows.list.emptyTitle', 'No Workflows yet')}\n description={t(\n 'bigconsole.workflows.list.emptyDescription',\n 'Design ETL workflows with the FluidGrids visual builder to fetch, transform, and deliver data into BigConsole DataSinks.'\n )}\n action={\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.WORKFLOW_CREATE} deniedBehavior=\"disable\">\n <Button onClick={handleCreateWorkflow}>\n <Plus className=\"h-icon-sm w-icon-sm\" />\n {t('bigconsole.workflows.actions.designWorkflow', 'Design Workflow')}\n </Button>\n </PermissionButton>\n }\n />\n )}\n\n {isNoResults && (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={t('bigconsole.workflows.list.noResultsTitle', 'No matching workflows')}\n description={t('bigconsole.workflows.list.noResultsDescription', 'No workflows match your filters')}\n />\n )}\n\n {runOutcome && (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={`mb-form-gap flex flex-wrap items-center justify-between gap-icon-gap rounded-input border p-3 text-sm ${\n runOutcome.ok\n ? 'border-[var(--color-status-success-border)] bg-[var(--color-status-success-bgSubtle)] text-[var(--color-status-success-text)]'\n : 'border-[var(--color-status-error-border)] bg-[var(--color-status-error-bgSubtle)] text-[var(--color-status-error-text)]'\n }`}\n data-testid=\"workflows-run-outcome\"\n >\n <span className=\"min-w-0\">\n <strong className=\"font-semibold\">{runOutcome.workflowName}</strong> — {runOutcome.message}\n </span>\n <button\n type=\"button\"\n onClick={() => setRunOutcome(null)}\n className={`rounded-button px-2 py-1 text-xs font-medium underline-offset-2 hover:underline ${SECTION_FOCUS_RING}`}\n >\n {t('bigconsole.workflows.run.dismiss', 'Dismiss')}\n </button>\n </div>\n )}\n\n {filteredWorkflows.length > 0 && (\n <div className=\"grid grid-cols-1 gap-form-gap sm:grid-cols-2 xl:grid-cols-3\">\n {filteredWorkflows.map((workflow) => (\n <WorkflowCard\n key={workflow.id}\n workflow={workflow}\n onView={handleViewWorkflow}\n onEdit={canUpdate ? handleEditWorkflow : undefined}\n onDelete={canDelete ? handleDeleteClick : undefined}\n onRun={canExecute ? handleRunWorkflow : undefined}\n isRunning={workflowUIStates.get(workflow.id)?.isExecuting ?? false}\n />\n ))}\n </div>\n )}\n </Section>\n </>\n )}\n </SectionStack>\n </div>\n\n {/* Delete Dialog */}\n {currentWorkflow && (\n <WorkflowDeleteDialog\n workflow={currentWorkflow}\n isOpen={deleteDialogOpen}\n onClose={closeDeleteDialog}\n onConfirm={handleDeleteConfirm}\n />\n )}\n </div>\n );\n});\n\nexport default WorkflowsPage;\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ //#region src/bigconsole/utils/format.ts
2
+ var e = [
3
+ "B",
4
+ "KB",
5
+ "MB",
6
+ "GB",
7
+ "TB"
8
+ ];
9
+ function t(t) {
10
+ if (t == null || !Number.isFinite(t)) return "—";
11
+ if (t <= 0) return "0 B";
12
+ let n = Math.min(Math.floor(Math.log(t) / Math.log(1024)), e.length - 1), r = t / 1024 ** n, i = n === 0 ? 0 : 1;
13
+ return `${r.toFixed(i)} ${e[n]}`;
14
+ }
15
+ //#endregion
16
+ export { t as formatByteSize };
17
+
18
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","names":[],"sources":["../../../src/bigconsole/utils/format.ts"],"sourcesContent":["/**\n * Shared display formatters.\n *\n * Kept here rather than re-declared per page so the same byte value reads the\n * same everywhere it is shown (list table, card, detail panel).\n *\n * @module bigconsole/utils/format\n */\n\nconst BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'] as const;\n\n/**\n * Render a byte count as a human-readable size.\n *\n * `null`/`undefined` render as an em dash rather than `0 B`: \"we don't know\"\n * and \"it is empty\" are different facts, and a sink whose size has never been\n * computed must not claim to be empty.\n */\nexport function formatByteSize(bytes: number | null | undefined): string {\n if (bytes === null || bytes === undefined || !Number.isFinite(bytes)) return '—';\n if (bytes <= 0) return '0 B';\n const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), BYTE_UNITS.length - 1);\n const value = bytes / Math.pow(1024, exponent);\n // Whole bytes never need a decimal; larger units keep one for readability.\n const decimals = exponent === 0 ? 0 : 1;\n return `${value.toFixed(decimals)} ${BYTE_UNITS[exponent]}`;\n}\n"],"mappings":";AASA,IAAM,IAAa;CAAC;CAAK;CAAM;CAAM;CAAM;CAAK;AAShD,SAAgB,EAAe,GAA0C;AACvE,KAAI,KAAU,QAA+B,CAAC,OAAO,SAAS,EAAM,CAAE,QAAO;AAC7E,KAAI,KAAS,EAAG,QAAO;CACvB,IAAM,IAAW,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,KAAK,CAAC,EAAE,EAAW,SAAS,EAAE,EACxF,IAAQ,IAAiB,QAAM,GAE/B,IAAW,MAAa,IAAI,IAAI;AACtC,QAAO,GAAG,EAAM,QAAQ,EAAS,CAAC,GAAG,EAAW"}
@@ -39,6 +39,6 @@ function i(t, n, r, i, a, o = e) {
39
39
  };
40
40
  }
41
41
  //#endregion
42
- export { i as calculatePositionFromDrop, r as findNextAvailablePosition };
42
+ export { e as DEFAULT_GRID_CONFIG, i as calculatePositionFromDrop, r as findNextAvailablePosition };
43
43
 
44
44
  //# sourceMappingURL=widgetPositioning.js.map
@@ -130,6 +130,7 @@ ${o}`, c = e`
130
130
  category
131
131
  isPublic
132
132
  visibility
133
+ status
133
134
  viewCount
134
135
  widgetCount
135
136
  createdAt