@burdenoff/microfe-bigconsole 2026.1104.1 → 2026.1104.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,28 +1,29 @@
1
- import { memo as e } from "react";
2
- import { Radio as t } from "lucide-react";
3
- import { jsx as n, jsxs as r } from "react/jsx-runtime";
1
+ import { LIVE_DATA_REFRESH_SECONDS as e } from "../../utils/liveBoardRefresh.js";
2
+ import { memo as t } from "react";
3
+ import { Radio as n } from "lucide-react";
4
+ import { jsx as r, jsxs as i } from "react/jsx-runtime";
4
5
  //#region src/bigconsole/components/dashboard/LiveCanvasToggle.tsx
5
- var i = e(function({ isLive: e, onToggle: i }) {
6
- return /* @__PURE__ */ r("button", {
6
+ var a = t(function({ isLive: t, onToggle: a }) {
7
+ return /* @__PURE__ */ i("button", {
7
8
  type: "button",
8
- onClick: i,
9
- "aria-pressed": e,
9
+ onClick: a,
10
+ "aria-pressed": t,
10
11
  "data-testid": "live-canvas-toggle",
11
- "data-live": e ? "on" : "off",
12
- title: e ? "Live canvas on — new widgets and pages appear automatically, no reload needed" : "Turn on live canvas to watch the board update as the pipeline builds",
13
- className: ["inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium transition-colors", e ? "bg-status-success-bg text-status-success-text border border-status-success-border" : "text-text-secondary border border-border-default hover:bg-bg-sunken"].join(" "),
14
- children: [e ? /* @__PURE__ */ r("span", {
12
+ "data-live": t ? "on" : "off",
13
+ title: t ? `Live canvas on — new widgets and pages appear automatically and widget data refreshes about every ${e} seconds, no reload needed` : `Turn on live canvas to watch the board update as the pipeline builds and refresh widget data about every ${e} seconds`,
14
+ className: ["inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium transition-colors", t ? "bg-status-success-bg text-status-success-text border border-status-success-border" : "text-text-secondary border border-border-default hover:bg-bg-sunken"].join(" "),
15
+ children: [t ? /* @__PURE__ */ i("span", {
15
16
  className: "relative flex h-2.5 w-2.5 flex-shrink-0",
16
17
  "aria-hidden": "true",
17
- children: [/* @__PURE__ */ n("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-status-success-text opacity-60" }), /* @__PURE__ */ n("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-status-success-text" })]
18
- }) : /* @__PURE__ */ n(t, {
18
+ children: [/* @__PURE__ */ r("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-status-success-text opacity-60" }), /* @__PURE__ */ r("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-status-success-text" })]
19
+ }) : /* @__PURE__ */ r(n, {
19
20
  className: "h-3.5 w-3.5 flex-shrink-0",
20
21
  "aria-hidden": "true"
21
- }), /* @__PURE__ */ n("span", {
22
+ }), /* @__PURE__ */ r("span", {
22
23
  className: "uppercase tracking-wide",
23
- children: e ? "Live" : "Go live"
24
+ children: t ? "Live" : "Go live"
24
25
  })]
25
26
  });
26
27
  });
27
28
  //#endregion
28
- export { i as LiveCanvasToggle, i as default };
29
+ export { a as LiveCanvasToggle, a as default };
@@ -4,6 +4,10 @@ interface LinkedWorkflowsPanelProps {
4
4
  workflowLinks?: DataSinkWorkflowLink[];
5
5
  /** Whether loading */
6
6
  isLoading?: boolean;
7
+ /** Key of the sink; with `onLinked`, enables linking from this panel */
8
+ dataSinkKey?: string;
9
+ /** Reload the sink after a workflow is linked here */
10
+ onLinked?: () => Promise<void>;
7
11
  }
8
12
  export declare const LinkedWorkflowsPanel: import('react').NamedExoticComponent<LinkedWorkflowsPanelProps>;
9
13
  export default LinkedWorkflowsPanel;
@@ -3,11 +3,16 @@ import "../../contexts/index.js";
3
3
  import { SECTION_FOCUS_RING as t } from "../common/sectionStyles.js";
4
4
  import { Section as n } from "../common/Section.js";
5
5
  import "../common/index.js";
6
- import { memo as r } from "react";
7
- import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
8
- import { formatDistanceToNow as s } from "date-fns";
6
+ import { BIGCONSOLE_PERMISSIONS as r } from "../../../constants/permissions.js";
7
+ import { SinkWorkflowLinkForm as i } from "./SinkWorkflowLinkForm.js";
8
+ import { memo as a, useState as o } from "react";
9
+ import { PermissionButton as s } from "@burdenoff/fe-libs/shared/components";
10
+ import { Button as c } from "@burdenoff/fe-libs/ui";
11
+ import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
+ import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
13
+ import { formatDistanceToNow as p } from "date-fns";
9
14
  //#region src/bigconsole/components/datasink/LinkedWorkflowsPanel.tsx
10
- function c(e) {
15
+ function m(e) {
11
16
  switch (e) {
12
17
  case "active": return {
13
18
  label: "Active",
@@ -41,156 +46,176 @@ function c(e) {
41
46
  };
42
47
  }
43
48
  }
44
- var l = ({ className: e }) => /* @__PURE__ */ a("svg", {
49
+ var h = ({ className: e }) => /* @__PURE__ */ d("svg", {
45
50
  className: e,
46
51
  fill: "none",
47
52
  stroke: "currentColor",
48
53
  viewBox: "0 0 24 24",
49
- children: /* @__PURE__ */ a("path", {
54
+ children: /* @__PURE__ */ d("path", {
50
55
  strokeLinecap: "round",
51
56
  strokeLinejoin: "round",
52
57
  strokeWidth: 1.5,
53
58
  d: "M13 10V3L4 14h7v7l9-11h-7z"
54
59
  })
55
- }), u = ({ className: e }) => /* @__PURE__ */ a("svg", {
60
+ }), g = ({ className: e }) => /* @__PURE__ */ d("svg", {
56
61
  className: e,
57
62
  fill: "none",
58
63
  stroke: "currentColor",
59
64
  viewBox: "0 0 24 24",
60
- children: /* @__PURE__ */ a("path", {
65
+ children: /* @__PURE__ */ d("path", {
61
66
  strokeLinecap: "round",
62
67
  strokeLinejoin: "round",
63
68
  strokeWidth: 2,
64
69
  d: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
65
70
  })
66
- }), d = ({ className: e }) => /* @__PURE__ */ a("svg", {
71
+ }), _ = ({ className: e }) => /* @__PURE__ */ d("svg", {
67
72
  className: e,
68
73
  fill: "none",
69
74
  stroke: "currentColor",
70
75
  viewBox: "0 0 24 24",
71
- children: /* @__PURE__ */ a("path", {
76
+ children: /* @__PURE__ */ d("path", {
72
77
  strokeLinecap: "round",
73
78
  strokeLinejoin: "round",
74
79
  strokeWidth: 2,
75
80
  d: "M9 5l7 7-7 7"
76
81
  })
77
- }), f = r(function({ workflowLinks: r = [], isLoading: f = !1 }) {
78
- let p = r.filter((e) => e.isEnabled), m = r.filter((e) => !e.isEnabled);
79
- return /* @__PURE__ */ a(n, {
82
+ }), v = a(function({ workflowLinks: a = [], isLoading: v = !1, dataSinkKey: y, onLinked: b }) {
83
+ let { t: x } = l(), [S, C] = o(!1), w = a.filter((e) => e.isEnabled), T = a.filter((e) => !e.isEnabled), E = !!(y && b);
84
+ return /* @__PURE__ */ d(n, {
80
85
  headingLevel: 2,
81
86
  title: "Linked workflows",
82
87
  description: "FluidGrids workflows that push data into this sink.",
83
- icon: /* @__PURE__ */ a(u, { className: "h-icon-sm w-icon-sm" }),
84
- meta: /* @__PURE__ */ o("span", {
85
- className: "text-xs text-text-muted",
86
- children: [
87
- r.length,
88
- " workflow",
89
- r.length === 1 ? "" : "s"
90
- ]
88
+ icon: /* @__PURE__ */ d(g, { className: "h-icon-sm w-icon-sm" }),
89
+ meta: /* @__PURE__ */ f("div", {
90
+ className: "flex flex-wrap items-center gap-2",
91
+ children: [/* @__PURE__ */ f("span", {
92
+ className: "text-xs text-text-muted",
93
+ children: [
94
+ a.length,
95
+ " workflow",
96
+ a.length === 1 ? "" : "s"
97
+ ]
98
+ }), E && !S && /* @__PURE__ */ d(s, {
99
+ permission: r.WORKFLOW_UPDATE,
100
+ deniedBehavior: "hide",
101
+ children: /* @__PURE__ */ d(c, {
102
+ type: "button",
103
+ variant: "secondary",
104
+ size: "sm",
105
+ onClick: () => C(!0),
106
+ children: x("bigconsole.dataSinks.view.linkWorkflow.open", "Link workflow")
107
+ })
108
+ })]
91
109
  }),
92
- children: /* @__PURE__ */ a(i, { children: f ? /* @__PURE__ */ a("div", {
110
+ children: /* @__PURE__ */ f(u, { children: [E && S && y && /* @__PURE__ */ d(i, {
111
+ dataSinkKey: y,
112
+ workflowLinks: a,
113
+ onLinked: async () => {
114
+ await b?.(), C(!1);
115
+ },
116
+ onCancel: () => C(!1)
117
+ }), v ? /* @__PURE__ */ d("div", {
93
118
  className: "flex items-center justify-center py-8",
94
- children: /* @__PURE__ */ o("svg", {
119
+ children: /* @__PURE__ */ f("svg", {
95
120
  className: "w-6 h-6 text-text-secondary animate-spin",
96
121
  fill: "none",
97
122
  viewBox: "0 0 24 24",
98
- children: [/* @__PURE__ */ a("circle", {
123
+ children: [/* @__PURE__ */ d("circle", {
99
124
  className: "opacity-25",
100
125
  cx: "12",
101
126
  cy: "12",
102
127
  r: "10",
103
128
  stroke: "currentColor",
104
129
  strokeWidth: "4"
105
- }), /* @__PURE__ */ a("path", {
130
+ }), /* @__PURE__ */ d("path", {
106
131
  className: "opacity-75",
107
132
  fill: "currentColor",
108
133
  d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
109
134
  })]
110
135
  })
111
- }) : r.length === 0 ? /* @__PURE__ */ o("div", {
136
+ }) : a.length === 0 ? /* @__PURE__ */ f("div", {
112
137
  className: "py-8 text-center",
113
138
  children: [
114
- /* @__PURE__ */ a(l, { className: "mx-auto mb-3 h-icon-lg w-icon-lg text-text-muted" }),
115
- /* @__PURE__ */ a("p", {
139
+ /* @__PURE__ */ d(h, { className: "mx-auto mb-3 h-icon-lg w-icon-lg text-text-muted" }),
140
+ /* @__PURE__ */ d("p", {
116
141
  className: "mb-2 text-sm text-text-secondary",
117
142
  children: "No workflows linked"
118
143
  }),
119
- /* @__PURE__ */ a("p", {
144
+ /* @__PURE__ */ d("p", {
120
145
  className: "text-xs text-text-muted",
121
- children: "Link workflows from the Workflows page to push data to this data sink."
146
+ children: E ? x("bigconsole.dataSinks.view.linkWorkflow.emptyHint", "Use Link workflow to choose a FluidGrids workflow that pushes data into this sink, or manage links from the Workflows page.") : "Link workflows from the Workflows page to push data to this data sink."
122
147
  }),
123
- /* @__PURE__ */ o(e, {
148
+ /* @__PURE__ */ f(e, {
124
149
  to: "/workflows",
125
150
  className: `mt-4 inline-flex min-h-[var(--size-controlHeight-md)] items-center gap-1 text-xs font-medium text-text-link hover:underline ${t}`,
126
- children: ["Go to Workflows", /* @__PURE__ */ a(d, { className: "w-3 h-3" })]
151
+ children: ["Go to Workflows", /* @__PURE__ */ d(_, { className: "w-3 h-3" })]
127
152
  })
128
153
  ]
129
- }) : /* @__PURE__ */ o("div", {
154
+ }) : /* @__PURE__ */ f("div", {
130
155
  className: "space-y-list-gap",
131
- children: [p.map((n) => {
156
+ children: [w.map((n) => {
132
157
  if (!n.workflow) return null;
133
- let r = c(n.workflow.status);
134
- return /* @__PURE__ */ o(e, {
158
+ let r = m(n.workflow.status);
159
+ return /* @__PURE__ */ f(e, {
135
160
  to: `/workflows/${n.workflow.id}`,
136
161
  className: `group flex min-h-[var(--size-controlHeight-md)] items-center justify-between gap-icon-gap rounded-input bg-bg-sunken p-3 motion-safe:transition-colors hover:bg-bg-elevated ${t}`,
137
- children: [/* @__PURE__ */ o("div", {
162
+ children: [/* @__PURE__ */ f("div", {
138
163
  className: "flex min-w-0 items-center gap-icon-gap",
139
- children: [/* @__PURE__ */ a(l, { className: "h-icon-md w-icon-md shrink-0 text-text-secondary" }), /* @__PURE__ */ o("div", {
164
+ children: [/* @__PURE__ */ d(h, { className: "h-icon-md w-icon-md shrink-0 text-text-secondary" }), /* @__PURE__ */ f("div", {
140
165
  className: "min-w-0",
141
- children: [/* @__PURE__ */ a("p", {
166
+ children: [/* @__PURE__ */ d("p", {
142
167
  className: "truncate text-sm font-medium text-text-primary group-hover:text-text-link",
143
168
  children: n.workflow.name
144
- }), /* @__PURE__ */ a("p", {
169
+ }), /* @__PURE__ */ d("p", {
145
170
  className: "truncate font-mono text-xs text-text-secondary",
146
171
  children: n.workflow.key
147
172
  })]
148
173
  })]
149
- }), /* @__PURE__ */ o("div", {
174
+ }), /* @__PURE__ */ f("div", {
150
175
  className: "flex shrink-0 items-center gap-icon-gap",
151
- children: [n.workflow.lastExecutedAt && /* @__PURE__ */ a("span", {
176
+ children: [n.workflow.lastExecutedAt && /* @__PURE__ */ d("span", {
152
177
  className: "hidden text-xs text-text-secondary sm:inline",
153
- children: s(new Date(n.workflow.lastExecutedAt), { addSuffix: !0 })
154
- }), /* @__PURE__ */ o("div", {
178
+ children: p(new Date(n.workflow.lastExecutedAt), { addSuffix: !0 })
179
+ }), /* @__PURE__ */ f("div", {
155
180
  className: `flex items-center gap-1.5 rounded-pill px-2 py-0.5 ${r.bgColor}`,
156
- children: [/* @__PURE__ */ a("span", { className: `h-1.5 w-1.5 rounded-pill ${r.dotColor}` }), /* @__PURE__ */ a("span", {
181
+ children: [/* @__PURE__ */ d("span", { className: `h-1.5 w-1.5 rounded-pill ${r.dotColor}` }), /* @__PURE__ */ d("span", {
157
182
  className: `text-xs font-medium ${r.textColor}`,
158
183
  children: r.label
159
184
  })]
160
185
  })]
161
186
  })]
162
187
  }, n.id);
163
- }), m.length > 0 && /* @__PURE__ */ a(n, {
188
+ }), T.length > 0 && /* @__PURE__ */ d(n, {
164
189
  emphasis: "bare",
165
190
  padding: "none",
166
191
  headingLevel: 3,
167
- title: `Disabled (${m.length})`,
192
+ title: `Disabled (${T.length})`,
168
193
  className: "mt-form-gap border-t border-[var(--color-border-subtle)] pt-form-gap",
169
194
  headerClassName: "mb-list-gap",
170
195
  bodyClassName: "space-y-list-gap",
171
- children: m.map((t) => t.workflow ? /* @__PURE__ */ o(e, {
196
+ children: T.map((t) => t.workflow ? /* @__PURE__ */ f(e, {
172
197
  to: `/workflows/${t.workflow.id}`,
173
198
  className: "group flex min-h-[var(--size-controlHeight-md)] items-center justify-between gap-icon-gap rounded-input bg-bg-sunken p-3 opacity-60 motion-safe:transition-colors hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-bg-surface",
174
- children: [/* @__PURE__ */ o("div", {
199
+ children: [/* @__PURE__ */ f("div", {
175
200
  className: "flex min-w-0 items-center gap-icon-gap",
176
- children: [/* @__PURE__ */ a(l, { className: "h-icon-md w-icon-md shrink-0 text-text-muted" }), /* @__PURE__ */ o("div", {
201
+ children: [/* @__PURE__ */ d(h, { className: "h-icon-md w-icon-md shrink-0 text-text-muted" }), /* @__PURE__ */ f("div", {
177
202
  className: "min-w-0",
178
- children: [/* @__PURE__ */ a("p", {
203
+ children: [/* @__PURE__ */ d("p", {
179
204
  className: "truncate text-sm font-medium text-text-secondary group-hover:text-text-primary",
180
205
  children: t.workflow.name
181
- }), /* @__PURE__ */ a("p", {
206
+ }), /* @__PURE__ */ d("p", {
182
207
  className: "truncate font-mono text-xs text-text-muted",
183
208
  children: t.workflow.key
184
209
  })]
185
210
  })]
186
- }), /* @__PURE__ */ a("span", {
211
+ }), /* @__PURE__ */ d("span", {
187
212
  className: "shrink-0 text-xs text-text-muted",
188
213
  children: "Disabled"
189
214
  })]
190
215
  }, t.id) : null)
191
216
  })]
192
- }) })
217
+ })] })
193
218
  });
194
219
  });
195
220
  //#endregion
196
- export { f as LinkedWorkflowsPanel, f as default };
221
+ export { v as LinkedWorkflowsPanel, v as default };
@@ -0,0 +1,13 @@
1
+ import { DataSinkWorkflowLink } from '../../types';
2
+ export interface SinkWorkflowLinkFormProps {
3
+ /** Key of the sink the workflow will deliver into */
4
+ dataSinkKey: string;
5
+ /** Links the sink already has */
6
+ workflowLinks?: DataSinkWorkflowLink[];
7
+ /** Called after a successful link so the page can reload the sink */
8
+ onLinked: () => Promise<void>;
9
+ /** Called when the user closes the form without linking */
10
+ onCancel: () => void;
11
+ }
12
+ export declare function SinkWorkflowLinkForm({ dataSinkKey, workflowLinks, onLinked, onCancel }: SinkWorkflowLinkFormProps): import('react').JSX.Element;
13
+ export default SinkWorkflowLinkForm;
@@ -0,0 +1,80 @@
1
+ import e from "../../hooks/useWorkflowOperations.js";
2
+ import { SECTION_FOCUS_RING as t } from "../common/sectionStyles.js";
3
+ import "../common/index.js";
4
+ import { linkableWorkflowsForSink as n } from "../../utils/sinkWorkflowLink.js";
5
+ import { useMemo as r, useState as i } from "react";
6
+ import { Button as a } from "@burdenoff/fe-libs/ui";
7
+ import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
8
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
9
+ //#region src/bigconsole/components/datasink/SinkWorkflowLinkForm.tsx
10
+ function l({ dataSinkKey: l, workflowLinks: u, onLinked: d, onCancel: f }) {
11
+ let { t: p } = o(), { workflows: m, loading: h, error: g, linkDataSink: _ } = e(), [v, y] = i(""), [b, x] = i(!1), [S, C] = i(null), w = r(() => n(m, u), [m, u]), T = async (e) => {
12
+ if (e.preventDefault(), !(!w.some((e) => e.id === v) || b)) {
13
+ x(!0), C(null);
14
+ try {
15
+ if (!await _(v, { dataSinkKey: l })) {
16
+ C(p("bigconsole.dataSinks.view.linkWorkflow.saveError", "The workflow could not be linked. Try again."));
17
+ return;
18
+ }
19
+ await d();
20
+ } finally {
21
+ x(!1);
22
+ }
23
+ }
24
+ }, E = `w-full min-w-0 rounded-input border border-[var(--color-border-subtle)] bg-bg-canvas px-3 py-2 text-sm text-text-primary ${t}`;
25
+ return /* @__PURE__ */ c("form", {
26
+ onSubmit: T,
27
+ className: "mb-form-gap flex min-w-0 flex-col gap-form-gap rounded-card border border-[var(--color-border-subtle)] bg-bg-sunken p-3",
28
+ "data-testid": "sink-workflow-link-form",
29
+ children: [
30
+ /* @__PURE__ */ c("label", {
31
+ className: "block text-sm font-medium text-text-primary",
32
+ children: [p("bigconsole.dataSinks.view.linkWorkflow.workflowLabel", "Workflow"), /* @__PURE__ */ c("select", {
33
+ value: v,
34
+ onChange: (e) => y(e.target.value),
35
+ disabled: h || b,
36
+ required: !0,
37
+ className: E,
38
+ children: [/* @__PURE__ */ s("option", {
39
+ value: "",
40
+ children: p("bigconsole.dataSinks.view.linkWorkflow.select", "Select a workflow")
41
+ }), w.map((e) => /* @__PURE__ */ s("option", {
42
+ value: e.id,
43
+ children: e.name || e.key
44
+ }, e.id))]
45
+ })]
46
+ }),
47
+ /* @__PURE__ */ s("p", {
48
+ className: "text-xs text-text-secondary",
49
+ children: p("bigconsole.dataSinks.view.linkWorkflow.help", "The workflow will deliver its output into this sink’s key ({{key}}).", { key: l })
50
+ }),
51
+ !h && !g && w.length === 0 && /* @__PURE__ */ s("p", {
52
+ className: "text-sm text-text-secondary",
53
+ children: p("bigconsole.dataSinks.view.linkWorkflow.noAvailable", "Every workflow in this workspace is already linked to this sink, or there are none yet.")
54
+ }),
55
+ (g || S) && /* @__PURE__ */ s("p", {
56
+ role: "alert",
57
+ className: "text-sm text-status-error-text",
58
+ children: S || p("bigconsole.dataSinks.view.linkWorkflow.loadError", "Workflows could not be loaded. Close and reopen the form to retry.")
59
+ }),
60
+ /* @__PURE__ */ c("div", {
61
+ className: "flex flex-wrap gap-2",
62
+ children: [/* @__PURE__ */ s(a, {
63
+ type: "submit",
64
+ disabled: !v || b || h,
65
+ className: "w-full sm:w-auto",
66
+ children: b ? p("bigconsole.dataSinks.view.linkWorkflow.saving", "Linking…") : p("bigconsole.dataSinks.view.linkWorkflow.submit", "Link workflow")
67
+ }), /* @__PURE__ */ s(a, {
68
+ type: "button",
69
+ variant: "ghost",
70
+ onClick: f,
71
+ disabled: b,
72
+ className: "w-full sm:w-auto",
73
+ children: p("bigconsole.dataSinks.view.linkWorkflow.cancel", "Cancel")
74
+ })]
75
+ })
76
+ ]
77
+ });
78
+ }
79
+ //#endregion
80
+ export { l as SinkWorkflowLinkForm, l as default };