@burdenoff/microfe-bigconsole 2026.714.3 → 2026.715.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.
@@ -0,0 +1,48 @@
1
+ import { BigConsoleProvider as e } from "./context/BigConsoleContext.js";
2
+ import "./context/index.js";
3
+ import { AdminRoutes as t } from "./AdminRoutes.js";
4
+ import { useEffect as n } from "react";
5
+ import { ErrorBoundary as r } from "@burdenoff/fe-libs/shared/components";
6
+ import { FeatureFlagProvider as i } from "@burdenoff/fe-libs/shared/feature-flags";
7
+ import { BarChart3 as a } from "lucide-react";
8
+ import { jsx as o } from "react/jsx-runtime";
9
+ //#region src/bigconsole/AdminRoot.tsx
10
+ var s = (s) => {
11
+ let { registerNavItems: c, registerFooterItems: l, basePath: u = "/admin/bigconsole" } = s;
12
+ return n(() => {
13
+ if (c) return c([{
14
+ id: "bigconsole-admin",
15
+ label: "BigConsole",
16
+ path: u,
17
+ icon: /* @__PURE__ */ o(a, { size: 20 }),
18
+ order: 55,
19
+ group: "admin"
20
+ }]);
21
+ }, [c, u]), n(() => {
22
+ if (l) return l([{
23
+ id: "bigconsole-admin-status",
24
+ label: "BigConsole Admin",
25
+ type: "status",
26
+ order: 55,
27
+ section: "left"
28
+ }]);
29
+ }, [l]), /* @__PURE__ */ o(r, {
30
+ fallback: /* @__PURE__ */ o("div", {
31
+ className: "p-6 text-center text-text-secondary",
32
+ children: "Something went wrong loading BigConsole Admin. Please refresh the page."
33
+ }),
34
+ children: /* @__PURE__ */ o(i, {
35
+ workspaceId: null,
36
+ gateway: "global",
37
+ defaultEnabled: !0,
38
+ children: /* @__PURE__ */ o(e, {
39
+ props: s,
40
+ children: /* @__PURE__ */ o(t, {})
41
+ })
42
+ })
43
+ });
44
+ };
45
+ //#endregion
46
+ export { s as AdminRoot };
47
+
48
+ //# sourceMappingURL=AdminRoot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminRoot.js","names":[],"sources":["../../src/bigconsole/AdminRoot.tsx"],"sourcesContent":["/**\n * BigConsole Admin Root\n *\n * Entry point for the BigConsole admin surface mounted inside the platform\n * admin-app. Reuses the same provider stack as the main BigConsoleRoot where\n * appropriate, but keeps the admin UI lightweight and independent of dashboard\n * rendering concerns.\n */\n\nimport type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { BarChart3 } from 'lucide-react';\nimport { ErrorBoundary } from '@burdenoff/fe-libs/shared/components';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport type { AdminRootProps } from './types';\nimport { AdminRoutes } from './AdminRoutes';\nimport { BigConsoleProvider } from './context';\n\n/**\n * Root component for the BigConsole admin microfrontend.\n *\n * This is the admin entry point that host applications import and render.\n */\nexport const AdminRoot: FC<AdminRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = '/admin/bigconsole' } = props;\n\n // Register a single admin nav item with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n const cleanup = registerNavItems([\n {\n id: 'bigconsole-admin',\n label: 'BigConsole',\n path: basePath,\n icon: <BarChart3 size={20} />,\n order: 55,\n group: 'admin',\n },\n ]);\n\n return cleanup;\n }, [registerNavItems, basePath]);\n\n // Register footer status item\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const cleanup = registerFooterItems([\n {\n id: 'bigconsole-admin-status',\n label: 'BigConsole Admin',\n type: 'status' as const,\n order: 55,\n section: 'left' as const,\n },\n ]);\n\n return cleanup;\n }, [registerFooterItems]);\n\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n Something went wrong loading BigConsole Admin. Please refresh the page.\n </div>\n }\n >\n <FeatureFlagProvider workspaceId={null} gateway=\"global\" defaultEnabled={true}>\n <BigConsoleProvider props={props}>\n <AdminRoutes />\n </BigConsoleProvider>\n </FeatureFlagProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;;AAuBA,IAAa,KAAiC,MAAU;CACtD,IAAM,EAAE,qBAAkB,wBAAqB,cAAW,wBAAwB;AAqClF,QAlCA,QAAgB;AACT,QAaL,QAXgB,EAAiB,CAC/B;GACE,IAAI;GACJ,OAAO;GACP,MAAM;GACN,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;GAC7B,OAAO;GACP,OAAO;GACR,CACF,CAAC;IAGD,CAAC,GAAkB,EAAS,CAAC,EAGhC,QAAgB;AACT,QAYL,QAVgB,EAAoB,CAClC;GACE,IAAI;GACJ,OAAO;GACP,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAAC;IAGD,CAAC,EAAoB,CAAC,EAGvB,kBAAC,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;aAAsC;GAE/C,CAAA;YAGR,kBAAC,GAAD;GAAqB,aAAa;GAAM,SAAQ;GAAS,gBAAgB;aACvE,kBAAC,GAAD;IAA2B;cACzB,kBAAC,GAAD,EAAe,CAAA;IACI,CAAA;GACD,CAAA;EACR,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { AdminPage as e } from "./pages/AdminPage.js";
2
+ import { Route as t, Routes as n } from "react-router-dom";
3
+ import { jsx as r } from "react/jsx-runtime";
4
+ //#region src/bigconsole/AdminRoutes.tsx
5
+ function i() {
6
+ return /* @__PURE__ */ r(n, { children: /* @__PURE__ */ r(t, {
7
+ path: "*",
8
+ element: /* @__PURE__ */ r(e, {})
9
+ }) });
10
+ }
11
+ //#endregion
12
+ export { i as AdminRoutes };
13
+
14
+ //# sourceMappingURL=AdminRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminRoutes.js","names":[],"sources":["../../src/bigconsole/AdminRoutes.tsx"],"sourcesContent":["/**\n * BigConsole Admin Routes\n *\n * Internal routing for the admin surface mounted inside admin-app.\n */\n\nimport { Route, Routes } from 'react-router-dom';\nimport { AdminPage } from './pages/AdminPage';\n\nexport function AdminRoutes() {\n return (\n <Routes>\n <Route path=\"*\" element={<AdminPage />} />\n </Routes>\n );\n}\n"],"mappings":";;;;AASA,SAAgB,IAAc;AAC5B,QACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;EAAO,MAAK;EAAI,SAAS,kBAAC,GAAD,EAAa,CAAA;EAAI,CAAA,EACnC,CAAA"}
@@ -3,62 +3,62 @@ import "../../context/index.js";
3
3
  import { PREVIEW_STEPS as t, PREVIEW_STEP_LABEL as n, useAssistantRunStore as r } from "../../assistant/assistantRunStore.js";
4
4
  import i from "../../hooks/useDashboardOperations.js";
5
5
  import a from "../../hooks/useDataSinkOperations.js";
6
- import o from "../../hooks/useParserOperations.js";
7
- import s from "../../hooks/useWidgetOperations.js";
8
- import { useEffect as c, useMemo as l, useRef as u, useState as d } from "react";
9
- import { Check as f, ChevronRight as p, Loader2 as m, Minus as h, Sparkles as g, X as _ } from "lucide-react";
10
- import { Badge as v, Button as y, Spinner as b, Tabs as x, TabsContent as S, TabsList as C, TabsTrigger as w } from "@burdenoff/fe-libs/ui";
11
- import { jsx as T, jsxs as E } from "react/jsx-runtime";
6
+ import ee from "../../hooks/useParserOperations.js";
7
+ import o from "../../hooks/useWidgetOperations.js";
8
+ import { useEffect as s, useMemo as c, useRef as l, useState as u } from "react";
9
+ import { Check as d, ChevronRight as f, Loader2 as p, Minus as m, Sparkles as h, X as g } from "lucide-react";
10
+ import { Badge as _, Button as v, Spinner as y, Tabs as b, TabsContent as x, TabsList as S, TabsTrigger as C } from "@burdenoff/fe-libs/ui";
11
+ import { jsx as w, jsxs as T } from "react/jsx-runtime";
12
12
  //#region src/bigconsole/components/preview/AiPreviewPanel.tsx
13
- var D = 3500, O = 3e4, k = "default";
14
- function A({ status: e }) {
15
- return e === "done" ? /* @__PURE__ */ T(f, { className: "size-3.5 text-status-success-text" }) : e === "running" ? /* @__PURE__ */ T(m, { className: "size-3.5 animate-spin text-action-primary-bg" }) : e === "skipped" ? /* @__PURE__ */ T(h, { className: "size-3.5 text-text-muted" }) : /* @__PURE__ */ T("span", { className: "size-1.5 rounded-full bg-border-strong" });
13
+ var E = 3500, D = 3e4, O = "default";
14
+ function k({ status: e }) {
15
+ return e === "done" ? /* @__PURE__ */ w(d, { className: "size-3.5 text-status-success-text" }) : e === "running" ? /* @__PURE__ */ w(p, { className: "size-3.5 animate-spin text-action-primary-bg" }) : e === "skipped" ? /* @__PURE__ */ w(m, { className: "size-3.5 text-text-muted" }) : /* @__PURE__ */ w("span", { className: "size-1.5 rounded-full bg-border-strong" });
16
16
  }
17
- function j({ steps: e }) {
18
- return /* @__PURE__ */ T("div", {
17
+ function A({ steps: e }) {
18
+ return /* @__PURE__ */ w("div", {
19
19
  className: "flex flex-wrap items-center gap-1",
20
20
  "data-testid": "ai-preview-step-rail",
21
- children: t.map((r, i) => /* @__PURE__ */ E("div", {
21
+ children: t.map((r, i) => /* @__PURE__ */ T("div", {
22
22
  className: "flex items-center gap-1",
23
- children: [/* @__PURE__ */ E("span", {
23
+ children: [/* @__PURE__ */ T("span", {
24
24
  "data-testid": `ai-preview-step-${r}`,
25
25
  "data-status": e[r],
26
26
  className: ["inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium", e[r] === "done" ? "border-status-success-border bg-status-success-bg text-status-success-text" : e[r] === "running" ? "border-border-strong bg-bg-surface text-text-primary" : "border-border-default text-text-muted"].join(" "),
27
27
  children: [
28
- /* @__PURE__ */ T(A, { status: e[r] }),
28
+ /* @__PURE__ */ w(k, { status: e[r] }),
29
29
  n[r],
30
- e[r] === "skipped" ? /* @__PURE__ */ T("span", {
30
+ e[r] === "skipped" ? /* @__PURE__ */ w("span", {
31
31
  className: "text-[10px]",
32
32
  children: "skipped"
33
33
  }) : null
34
34
  ]
35
- }), i < t.length - 1 ? /* @__PURE__ */ T(p, { className: "size-3.5 text-text-muted" }) : null]
35
+ }), i < t.length - 1 ? /* @__PURE__ */ w(f, { className: "size-3.5 text-text-muted" }) : null]
36
36
  }, r))
37
37
  });
38
38
  }
39
- function M(e) {
39
+ function j(e) {
40
40
  return e < 5 ? "just now" : e < 60 ? `${String(e)}s ago` : `${String(Math.floor(e / 60))}m ago`;
41
41
  }
42
- function N({ id: e, title: t, meta: n, isNew: r, onOpen: i }) {
43
- return /* @__PURE__ */ E("div", {
42
+ function M({ id: e, title: t, meta: n, isNew: r, onOpen: i }) {
43
+ return /* @__PURE__ */ T("div", {
44
44
  "data-testid": "ai-preview-row",
45
45
  "data-entity-id": e,
46
46
  className: ["flex items-center justify-between gap-3 rounded-md border px-3 py-2", r ? "border-status-success-border bg-status-success-bg/30" : "border-border-subtle bg-bg-surface"].join(" "),
47
- children: [/* @__PURE__ */ E("div", {
47
+ children: [/* @__PURE__ */ T("div", {
48
48
  className: "min-w-0",
49
- children: [/* @__PURE__ */ T("p", {
49
+ children: [/* @__PURE__ */ w("p", {
50
50
  className: "truncate text-sm font-medium text-text-primary",
51
51
  children: t
52
- }), n ? /* @__PURE__ */ T("p", {
52
+ }), n ? /* @__PURE__ */ w("p", {
53
53
  className: "truncate text-xs text-text-muted",
54
54
  children: n
55
55
  }) : null]
56
- }), /* @__PURE__ */ E("div", {
56
+ }), /* @__PURE__ */ T("div", {
57
57
  className: "flex shrink-0 items-center gap-2",
58
- children: [r ? /* @__PURE__ */ E(v, {
58
+ children: [r ? /* @__PURE__ */ T(_, {
59
59
  className: "bg-status-success-bg text-status-success-text",
60
- children: [/* @__PURE__ */ T(g, { className: "mr-1 size-3" }), "just built"]
61
- }) : null, i ? /* @__PURE__ */ T(y, {
60
+ children: [/* @__PURE__ */ w(h, { className: "mr-1 size-3" }), "just built"]
61
+ }) : null, i ? /* @__PURE__ */ w(v, {
62
62
  size: "sm",
63
63
  variant: "ghost",
64
64
  onClick: i,
@@ -67,188 +67,203 @@ function N({ id: e, title: t, meta: n, isNew: r, onOpen: i }) {
67
67
  })]
68
68
  });
69
69
  }
70
+ function N() {
71
+ let [e, t] = u(0);
72
+ return s(() => {
73
+ let e = () => {
74
+ let e = document.querySelector("[data-testid=\"assistant-widget-window\"][data-layout=\"sidebar\"]")?.getBoundingClientRect(), n = e && e.width < window.innerWidth * .75 ? Math.round(e.width) : 0;
75
+ t((e) => e === n ? e : n);
76
+ };
77
+ e();
78
+ let n = window.setInterval(e, 500);
79
+ return window.addEventListener("resize", e), () => {
80
+ window.clearInterval(n), window.removeEventListener("resize", e);
81
+ };
82
+ }, []), e;
83
+ }
70
84
  function P({ loading: e, empty: t }) {
71
- return e ? /* @__PURE__ */ E("div", {
85
+ return e ? /* @__PURE__ */ T("div", {
72
86
  className: "flex items-center gap-2 px-1 py-6 text-sm text-text-muted",
73
- children: [/* @__PURE__ */ T(b, { className: "size-4" }), "Loading…"]
74
- }) : /* @__PURE__ */ T("p", {
87
+ children: [/* @__PURE__ */ w(y, { className: "size-4" }), "Loading…"]
88
+ }) : /* @__PURE__ */ w("p", {
75
89
  className: "px-1 py-6 text-sm text-text-muted",
76
90
  children: t
77
91
  });
78
92
  }
79
93
  function F() {
80
- let n = e(), f = r((e) => e.isRunning), p = r((e) => e.previewOpen), h = r((e) => e.hasRun), v = r((e) => e.steps), b = r((e) => e.entityIds), A = r((e) => e.caption), F = r((e) => e.error), I = r((e) => e.updatedAt), L = r((e) => e.openPreview), R = r((e) => e.closePreview), [z, B] = d("datasink"), [V, H] = d(() => Date.now()), U = b.dashboard, W = a({ skipFetch: !0 }), G = i(), K = o({
81
- consoleId: U ?? k,
94
+ let n = e(), d = r((e) => e.isRunning), f = r((e) => e.previewOpen), m = r((e) => e.hasRun), _ = r((e) => e.steps), y = r((e) => e.entityIds), k = r((e) => e.caption), F = r((e) => e.error), I = r((e) => e.updatedAt), te = r((e) => e.openPreview), L = r((e) => e.closePreview), [R, z] = u("datasink"), [B, V] = u(() => Date.now()), H = y.dashboard, U = a({ skipFetch: !0 }), W = i(), G = ee({
95
+ consoleId: H ?? O,
82
96
  skipFetch: !0
83
- }), q = s(void 0, U, { skipFetch: !U }), J = u({
84
- sinks: W.refetch,
85
- dashboards: G.refetch,
86
- parsers: K.refetch,
87
- widgets: q.refetch
97
+ }), K = o(void 0, H, { skipFetch: !H }), q = l({
98
+ sinks: U.refetch,
99
+ dashboards: W.refetch,
100
+ parsers: G.refetch,
101
+ widgets: K.refetch
88
102
  });
89
- J.current = {
90
- sinks: W.refetch,
91
- dashboards: G.refetch,
92
- parsers: K.refetch,
93
- widgets: q.refetch
94
- }, c(() => {
95
- if (!h) return;
103
+ q.current = {
104
+ sinks: U.refetch,
105
+ dashboards: W.refetch,
106
+ parsers: G.refetch,
107
+ widgets: K.refetch
108
+ }, s(() => {
109
+ if (!m) return;
96
110
  let e = Infinity;
97
- f || (e = Date.now() + O);
111
+ d || (e = Date.now() + D);
98
112
  let t = () => {
99
- typeof document < "u" && document.visibilityState !== "visible" || Date.now() > e || (J.current.sinks(), J.current.dashboards(), U && (J.current.parsers(), J.current.widgets()), H(Date.now()));
113
+ typeof document < "u" && document.visibilityState !== "visible" || Date.now() > e || (q.current.sinks(), q.current.dashboards(), H && (q.current.parsers(), q.current.widgets()), V(Date.now()));
100
114
  };
101
115
  t();
102
- let n = window.setInterval(t, D), r = () => {
116
+ let n = window.setInterval(t, E), r = () => {
103
117
  document.visibilityState === "visible" && t();
104
118
  };
105
119
  return document.addEventListener("visibilitychange", r), () => {
106
120
  window.clearInterval(n), document.removeEventListener("visibilitychange", r);
107
121
  };
108
122
  }, [
109
- h,
110
- f,
111
- U
112
- ]), c(() => {
113
- let e = t.find((e) => v[e] === "running");
114
- e && B(e);
115
- }, [v]);
116
- let Y = I ? Math.max(0, Math.floor((V - I) / 1e3)) : 0, X = l(() => W.dataSinks.slice(0, 25), [W.dataSinks]), Z = l(() => G.dashboards.slice(0, 25), [G.dashboards]), Q = l(() => K.parsers.slice(0, 25), [K.parsers]), $ = l(() => q.widgets.slice(0, 25), [q.widgets]);
117
- return h ? p ? /* @__PURE__ */ E("section", {
123
+ m,
124
+ d,
125
+ H
126
+ ]), s(() => {
127
+ let e = t.find((e) => _[e] === "running");
128
+ e && z(e);
129
+ }, [_]);
130
+ let J = I ? Math.max(0, Math.floor((B - I) / 1e3)) : 0, Y = N(), X = c(() => U.dataSinks.slice(0, 25), [U.dataSinks]), Z = c(() => W.dashboards.slice(0, 25), [W.dashboards]), Q = c(() => G.parsers.slice(0, 25), [G.parsers]), $ = c(() => K.widgets.slice(0, 25), [K.widgets]);
131
+ return m ? f ? /* @__PURE__ */ T("section", {
118
132
  "data-testid": "ai-preview-panel",
119
133
  "aria-label": "AI preview",
134
+ style: { marginRight: Y ? Y + 16 : void 0 },
120
135
  className: "mx-4 mt-3 overflow-hidden rounded-lg border border-border-default bg-bg-sunken shadow-sm md:mx-6",
121
136
  children: [
122
- /* @__PURE__ */ E("div", {
137
+ /* @__PURE__ */ T("div", {
123
138
  className: "flex items-center justify-between gap-3 border-b border-border-subtle bg-bg-surface px-3 py-2",
124
- children: [/* @__PURE__ */ E("div", {
139
+ children: [/* @__PURE__ */ T("div", {
125
140
  className: "flex min-w-0 items-center gap-2",
126
141
  children: [
127
- /* @__PURE__ */ T(g, { className: "size-4 shrink-0 text-action-primary-bg" }),
128
- /* @__PURE__ */ T("h2", {
142
+ /* @__PURE__ */ w(h, { className: "size-4 shrink-0 text-action-primary-bg" }),
143
+ /* @__PURE__ */ w("h2", {
129
144
  className: "truncate text-sm font-semibold text-text-primary",
130
145
  children: "AI Preview"
131
146
  }),
132
- f ? /* @__PURE__ */ E("span", {
147
+ d ? /* @__PURE__ */ T("span", {
133
148
  className: "inline-flex items-center gap-1.5 rounded-full bg-status-success-bg px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-status-success-text",
134
149
  "data-testid": "ai-preview-live",
135
- children: [/* @__PURE__ */ E("span", {
150
+ children: [/* @__PURE__ */ T("span", {
136
151
  className: "relative flex size-2",
137
- children: [/* @__PURE__ */ T("span", { className: "absolute inline-flex size-full animate-ping rounded-full bg-status-success-text opacity-75" }), /* @__PURE__ */ T("span", { className: "relative inline-flex size-2 rounded-full bg-status-success-text" })]
152
+ children: [/* @__PURE__ */ w("span", { className: "absolute inline-flex size-full animate-ping rounded-full bg-status-success-text opacity-75" }), /* @__PURE__ */ w("span", { className: "relative inline-flex size-2 rounded-full bg-status-success-text" })]
138
153
  }), "Live"]
139
154
  }) : null,
140
- /* @__PURE__ */ E("span", {
155
+ /* @__PURE__ */ T("span", {
141
156
  className: "truncate text-xs text-text-muted",
142
157
  "data-testid": "ai-preview-ago",
143
158
  children: [
144
- f ? A || "Working…" : F ? "Stopped" : "Done",
159
+ d ? k || "Working…" : F ? "Stopped" : "Done",
145
160
  " · updated",
146
161
  " ",
147
- M(Y)
162
+ j(J)
148
163
  ]
149
164
  })
150
165
  ]
151
- }), /* @__PURE__ */ T(y, {
166
+ }), /* @__PURE__ */ w(v, {
152
167
  size: "sm",
153
168
  variant: "ghost",
154
- onClick: R,
169
+ onClick: L,
155
170
  "aria-label": "Close preview",
156
171
  "data-testid": "ai-preview-close",
157
- children: /* @__PURE__ */ T(_, { className: "size-4" })
172
+ children: /* @__PURE__ */ w(g, { className: "size-4" })
158
173
  })]
159
174
  }),
160
- /* @__PURE__ */ T("div", {
175
+ /* @__PURE__ */ w("div", {
161
176
  className: "border-b border-border-subtle px-3 py-2",
162
- children: /* @__PURE__ */ T(j, { steps: v })
177
+ children: /* @__PURE__ */ w(A, { steps: _ })
163
178
  }),
164
- /* @__PURE__ */ E(x, {
165
- value: z,
166
- onValueChange: (e) => B(e),
167
- children: [/* @__PURE__ */ E(C, {
179
+ /* @__PURE__ */ T(b, {
180
+ value: R,
181
+ onValueChange: (e) => z(e),
182
+ children: [/* @__PURE__ */ T(S, {
168
183
  className: "mx-3 mt-2",
169
184
  children: [
170
- /* @__PURE__ */ T(w, {
185
+ /* @__PURE__ */ w(C, {
171
186
  value: "datasink",
172
187
  "data-testid": "ai-preview-tab-datasink",
173
188
  children: "Data Sinks"
174
189
  }),
175
- /* @__PURE__ */ T(w, {
190
+ /* @__PURE__ */ w(C, {
176
191
  value: "dashboard",
177
192
  "data-testid": "ai-preview-tab-dashboard",
178
193
  children: "Dashboards"
179
194
  }),
180
- /* @__PURE__ */ T(w, {
195
+ /* @__PURE__ */ w(C, {
181
196
  value: "parser",
182
197
  "data-testid": "ai-preview-tab-parser",
183
198
  children: "Parsers"
184
199
  }),
185
- /* @__PURE__ */ T(w, {
200
+ /* @__PURE__ */ w(C, {
186
201
  value: "widget",
187
202
  "data-testid": "ai-preview-tab-widget",
188
203
  children: "Widgets"
189
204
  })
190
205
  ]
191
- }), /* @__PURE__ */ E("div", {
206
+ }), /* @__PURE__ */ T("div", {
192
207
  className: "max-h-[320px] overflow-y-auto p-3",
193
208
  children: [
194
- /* @__PURE__ */ T(S, {
209
+ /* @__PURE__ */ w(x, {
195
210
  value: "datasink",
196
211
  className: "space-y-2",
197
- children: X.length === 0 ? /* @__PURE__ */ T(P, {
198
- loading: W.loading,
212
+ children: X.length === 0 ? /* @__PURE__ */ w(P, {
213
+ loading: U.loading,
199
214
  empty: "No data sinks yet."
200
- }) : X.map((e) => /* @__PURE__ */ T(N, {
215
+ }) : X.map((e) => /* @__PURE__ */ w(M, {
201
216
  id: e.id,
202
217
  title: e.name ?? e.key,
203
218
  meta: e.status,
204
- isNew: e.id === b.datasink,
219
+ isNew: e.id === y.datasink,
205
220
  onOpen: () => n(`/datasinks/${e.id}`)
206
221
  }, e.id))
207
222
  }),
208
- /* @__PURE__ */ T(S, {
223
+ /* @__PURE__ */ w(x, {
209
224
  value: "dashboard",
210
225
  className: "space-y-2",
211
- children: Z.length === 0 ? /* @__PURE__ */ T(P, {
212
- loading: G.loading,
226
+ children: Z.length === 0 ? /* @__PURE__ */ w(P, {
227
+ loading: W.loading,
213
228
  empty: "No dashboards yet."
214
- }) : Z.map((e) => /* @__PURE__ */ T(N, {
229
+ }) : Z.map((e) => /* @__PURE__ */ w(M, {
215
230
  id: e.id,
216
231
  title: e.name,
217
232
  meta: `${String(e.widgetCount ?? 0)} widget(s)`,
218
- isNew: e.id === b.dashboard,
233
+ isNew: e.id === y.dashboard,
219
234
  onOpen: () => n(`/dashboards/${e.id}`)
220
235
  }, e.id))
221
236
  }),
222
- /* @__PURE__ */ T(S, {
237
+ /* @__PURE__ */ w(x, {
223
238
  value: "parser",
224
239
  className: "space-y-2",
225
- children: U ? Q.length === 0 ? /* @__PURE__ */ T(P, {
226
- loading: K.loading,
240
+ children: H ? Q.length === 0 ? /* @__PURE__ */ w(P, {
241
+ loading: G.loading,
227
242
  empty: "No parsers on this dashboard yet."
228
- }) : Q.map((e) => /* @__PURE__ */ T(N, {
243
+ }) : Q.map((e) => /* @__PURE__ */ w(M, {
229
244
  id: e.id,
230
245
  title: e.name,
231
246
  meta: `${e.type} · ${e.status}`,
232
- isNew: e.id === b.parser,
247
+ isNew: e.id === y.parser,
233
248
  onOpen: () => n(`/parsers/${e.id}`)
234
- }, e.id)) : /* @__PURE__ */ T("p", {
249
+ }, e.id)) : /* @__PURE__ */ w("p", {
235
250
  className: "px-1 py-6 text-sm text-text-muted",
236
251
  children: "Parsers are listed per dashboard — waiting for the dashboard to be created."
237
252
  })
238
253
  }),
239
- /* @__PURE__ */ T(S, {
254
+ /* @__PURE__ */ w(x, {
240
255
  value: "widget",
241
256
  className: "space-y-2",
242
- children: U ? $.length === 0 ? /* @__PURE__ */ T(P, {
243
- loading: q.loading,
257
+ children: H ? $.length === 0 ? /* @__PURE__ */ w(P, {
258
+ loading: K.loading,
244
259
  empty: "No widgets on this dashboard yet."
245
- }) : $.map((e) => /* @__PURE__ */ T(N, {
260
+ }) : $.map((e) => /* @__PURE__ */ w(M, {
246
261
  id: e.id,
247
262
  title: e.title,
248
263
  meta: e.type,
249
- isNew: e.id === $[$.length - 1]?.id && v.widget === "done",
250
- onOpen: () => n(`/dashboards/${U}`)
251
- }, e.id)) : /* @__PURE__ */ T("p", {
264
+ isNew: e.id === $[$.length - 1]?.id && _.widget === "done",
265
+ onOpen: () => n(`/dashboards/${H}`)
266
+ }, e.id)) : /* @__PURE__ */ w("p", {
252
267
  className: "px-1 py-6 text-sm text-text-muted",
253
268
  children: "Widgets live on a dashboard — waiting for the dashboard to be created."
254
269
  })
@@ -256,23 +271,24 @@ function F() {
256
271
  ]
257
272
  })]
258
273
  }),
259
- F ? /* @__PURE__ */ T("p", {
274
+ F ? /* @__PURE__ */ w("p", {
260
275
  className: "border-t border-border-subtle px-3 py-2 text-xs text-status-error-text",
261
276
  "data-testid": "ai-preview-error",
262
277
  children: F
263
278
  }) : null
264
279
  ]
265
- }) : /* @__PURE__ */ T("div", {
280
+ }) : /* @__PURE__ */ w("div", {
266
281
  className: "flex justify-end px-4 pt-3 md:px-6",
267
- children: /* @__PURE__ */ E(y, {
282
+ style: { marginRight: Y },
283
+ children: /* @__PURE__ */ T(v, {
268
284
  size: "sm",
269
285
  variant: "outline",
270
- onClick: L,
286
+ onClick: te,
271
287
  "data-testid": "ai-preview-open",
272
288
  children: [
273
- /* @__PURE__ */ T(g, { className: "mr-1.5 size-4" }),
289
+ /* @__PURE__ */ w(h, { className: "mr-1.5 size-4" }),
274
290
  "Preview",
275
- f ? /* @__PURE__ */ T(m, { className: "ml-1.5 size-3.5 animate-spin" }) : null
291
+ d ? /* @__PURE__ */ w(p, { className: "ml-1.5 size-3.5 animate-spin" }) : null
276
292
  ]
277
293
  })
278
294
  }) : null;
@@ -1 +1 @@
1
- {"version":3,"file":"AiPreviewPanel.js","names":[],"sources":["../../../../src/bigconsole/components/preview/AiPreviewPanel.tsx"],"sourcesContent":["/**\n * AI Preview — watch the assistant build.\n *\n * Opens by itself the moment a prompt starts, and shows the DataSink → Dashboard\n * → Parser → Widget chain filling in live: a step rail driven by the agent's own\n * narration, and four tabs listing the real entities as they land.\n *\n * Why polling and not a subscription: the four entity types have no working\n * GraphQL subscriptions (DataSink's are declared in the SDL but have no\n * resolvers; the others have none at all). The same 3.5s poll is already used by\n * DashboardViewPage for exactly this reason. Note the real freshness floor is\n * fe-libs' ~15s shared read cache, not this interval.\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { Badge, Button, Spinner, Tabs, TabsContent, TabsList, TabsTrigger } from '@burdenoff/fe-libs/ui';\nimport { Check, ChevronRight, Loader2, Minus, Sparkles, X } from 'lucide-react';\n\nimport {\n PREVIEW_STEP_LABEL,\n PREVIEW_STEPS,\n type PreviewStepKey,\n type PreviewStepStatus,\n useAssistantRunStore,\n} from '../../assistant/assistantRunStore';\nimport { useBigConsoleNavigate } from '../../context';\nimport { useDashboardOperations } from '../../hooks/useDashboardOperations';\nimport { useDataSinkOperations } from '../../hooks/useDataSinkOperations';\nimport { useParserOperations } from '../../hooks/useParserOperations';\nimport { useWidgetOperations } from '../../hooks/useWidgetOperations';\n\n/** Matches DashboardViewPage's live cadence. */\nconst LIVE_POLL_MS = 3500;\n/** Keep polling briefly after the turn ends — the last write can land late. */\nconst TRAILING_POLL_MS = 30_000;\n/** Parsers are listed per console; a console IS a dashboard. */\nconst DEFAULT_CONSOLE_ID = 'default';\n\nfunction StepPip({ status }: { status: PreviewStepStatus }) {\n if (status === 'done') return <Check className=\"size-3.5 text-status-success-text\" />;\n if (status === 'running') return <Loader2 className=\"size-3.5 animate-spin text-action-primary-bg\" />;\n if (status === 'skipped') return <Minus className=\"size-3.5 text-text-muted\" />;\n return <span className=\"size-1.5 rounded-full bg-border-strong\" />;\n}\n\nfunction StepRail({ steps }: { steps: Record<PreviewStepKey, PreviewStepStatus> }) {\n return (\n <div className=\"flex flex-wrap items-center gap-1\" data-testid=\"ai-preview-step-rail\">\n {PREVIEW_STEPS.map((key, index) => (\n <div key={key} className=\"flex items-center gap-1\">\n <span\n data-testid={`ai-preview-step-${key}`}\n data-status={steps[key]}\n className={[\n 'inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium',\n steps[key] === 'done'\n ? 'border-status-success-border bg-status-success-bg text-status-success-text'\n : steps[key] === 'running'\n ? 'border-border-strong bg-bg-surface text-text-primary'\n : 'border-border-default text-text-muted',\n ].join(' ')}\n >\n <StepPip status={steps[key]} />\n {PREVIEW_STEP_LABEL[key]}\n {steps[key] === 'skipped' ? <span className=\"text-[10px]\">skipped</span> : null}\n </span>\n {index < PREVIEW_STEPS.length - 1 ? <ChevronRight className=\"size-3.5 text-text-muted\" /> : null}\n </div>\n ))}\n </div>\n );\n}\n\nfunction formatAgo(seconds: number): string {\n if (seconds < 5) return 'just now';\n if (seconds < 60) return `${String(seconds)}s ago`;\n return `${String(Math.floor(seconds / 60))}m ago`;\n}\n\ninterface RowProps {\n id: string;\n title: string;\n meta?: string;\n isNew: boolean;\n onOpen?: () => void;\n}\n\nfunction EntityRow({ id, title, meta, isNew, onOpen }: RowProps) {\n return (\n <div\n data-testid=\"ai-preview-row\"\n data-entity-id={id}\n className={[\n 'flex items-center justify-between gap-3 rounded-md border px-3 py-2',\n isNew ? 'border-status-success-border bg-status-success-bg/30' : 'border-border-subtle bg-bg-surface',\n ].join(' ')}\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{title}</p>\n {meta ? <p className=\"truncate text-xs text-text-muted\">{meta}</p> : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {isNew ? (\n <Badge className=\"bg-status-success-bg text-status-success-text\">\n <Sparkles className=\"mr-1 size-3\" />\n just built\n </Badge>\n ) : null}\n {onOpen ? (\n <Button size=\"sm\" variant=\"ghost\" onClick={onOpen}>\n Open\n </Button>\n ) : null}\n </div>\n </div>\n );\n}\n\nfunction EmptyOrLoading({ loading, empty }: { loading: boolean; empty: string }) {\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 px-1 py-6 text-sm text-text-muted\">\n <Spinner className=\"size-4\" />\n Loading…\n </div>\n );\n }\n return <p className=\"px-1 py-6 text-sm text-text-muted\">{empty}</p>;\n}\n\nexport function AiPreviewPanel() {\n const navigate = useBigConsoleNavigate();\n\n const isRunning = useAssistantRunStore((state) => state.isRunning);\n const previewOpen = useAssistantRunStore((state) => state.previewOpen);\n const hasRun = useAssistantRunStore((state) => state.hasRun);\n const steps = useAssistantRunStore((state) => state.steps);\n const entityIds = useAssistantRunStore((state) => state.entityIds);\n const caption = useAssistantRunStore((state) => state.caption);\n const error = useAssistantRunStore((state) => state.error);\n const updatedAt = useAssistantRunStore((state) => state.updatedAt);\n const openPreview = useAssistantRunStore((state) => state.openPreview);\n const closePreview = useAssistantRunStore((state) => state.closePreview);\n\n const [tab, setTab] = useState<PreviewStepKey>('datasink');\n const [now, setNow] = useState(() => Date.now());\n\n const dashboardId = entityIds.dashboard;\n\n // The panel does its own fetching, so the tabs are live even before the user\n // opens them — the \"just built\" highlight must be right the instant they look.\n const sinks = useDataSinkOperations({ skipFetch: true });\n const dashboards = useDashboardOperations();\n const parsers = useParserOperations({ consoleId: dashboardId ?? DEFAULT_CONSOLE_ID, skipFetch: true });\n const widgets = useWidgetOperations(undefined, dashboardId, { skipFetch: !dashboardId });\n\n const refetchRef = useRef({\n sinks: sinks.refetch,\n dashboards: dashboards.refetch,\n parsers: parsers.refetch,\n widgets: widgets.refetch,\n });\n refetchRef.current = {\n sinks: sinks.refetch,\n dashboards: dashboards.refetch,\n parsers: parsers.refetch,\n widgets: widgets.refetch,\n };\n\n // Follow the build. Keeps polling for a short while after the turn settles so\n // the final widget does not miss the panel by a second.\n useEffect(() => {\n if (!hasRun) return;\n\n let stopAt = Number.POSITIVE_INFINITY;\n if (!isRunning) stopAt = Date.now() + TRAILING_POLL_MS;\n\n const poll = () => {\n if (typeof document !== 'undefined' && document.visibilityState !== 'visible') return;\n if (Date.now() > stopAt) return;\n void refetchRef.current.sinks();\n void refetchRef.current.dashboards();\n if (dashboardId) {\n void refetchRef.current.parsers();\n void refetchRef.current.widgets();\n }\n setNow(Date.now());\n };\n\n poll();\n const intervalId = window.setInterval(poll, LIVE_POLL_MS);\n const onVisible = () => {\n if (document.visibilityState === 'visible') poll();\n };\n document.addEventListener('visibilitychange', onVisible);\n return () => {\n window.clearInterval(intervalId);\n document.removeEventListener('visibilitychange', onVisible);\n };\n }, [hasRun, isRunning, dashboardId]);\n\n // Jump the user to whatever is being worked on, so they are not left staring\n // at the Data Sinks tab while the parser is the thing actually happening.\n useEffect(() => {\n const active = PREVIEW_STEPS.find((key) => steps[key] === 'running');\n if (active) setTab(active);\n }, [steps]);\n\n const agoSeconds = updatedAt ? Math.max(0, Math.floor((now - updatedAt) / 1000)) : 0;\n\n const sinkRows = useMemo(() => sinks.dataSinks.slice(0, 25), [sinks.dataSinks]);\n const dashboardRows = useMemo(() => dashboards.dashboards.slice(0, 25), [dashboards.dashboards]);\n const parserRows = useMemo(() => parsers.parsers.slice(0, 25), [parsers.parsers]);\n const widgetRows = useMemo(() => widgets.widgets.slice(0, 25), [widgets.widgets]);\n\n if (!hasRun) return null;\n\n if (!previewOpen) {\n return (\n <div className=\"flex justify-end px-4 pt-3 md:px-6\">\n <Button size=\"sm\" variant=\"outline\" onClick={openPreview} data-testid=\"ai-preview-open\">\n <Sparkles className=\"mr-1.5 size-4\" />\n Preview\n {isRunning ? <Loader2 className=\"ml-1.5 size-3.5 animate-spin\" /> : null}\n </Button>\n </div>\n );\n }\n\n return (\n <section\n data-testid=\"ai-preview-panel\"\n aria-label=\"AI preview\"\n className=\"mx-4 mt-3 overflow-hidden rounded-lg border border-border-default bg-bg-sunken shadow-sm md:mx-6\"\n >\n {/* Header + live strip */}\n <div className=\"flex items-center justify-between gap-3 border-b border-border-subtle bg-bg-surface px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Sparkles className=\"size-4 shrink-0 text-action-primary-bg\" />\n <h2 className=\"truncate text-sm font-semibold text-text-primary\">AI Preview</h2>\n {isRunning ? (\n <span\n className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-status-success-text\"\n data-testid=\"ai-preview-live\"\n >\n <span className=\"relative flex size-2\">\n <span className=\"absolute inline-flex size-full animate-ping rounded-full bg-status-success-text opacity-75\" />\n <span className=\"relative inline-flex size-2 rounded-full bg-status-success-text\" />\n </span>\n Live\n </span>\n ) : null}\n <span className=\"truncate text-xs text-text-muted\" data-testid=\"ai-preview-ago\">\n {isRunning ? (caption ? caption : 'Working…') : error ? 'Stopped' : 'Done'} · updated{' '}\n {formatAgo(agoSeconds)}\n </span>\n </div>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={closePreview}\n aria-label=\"Close preview\"\n data-testid=\"ai-preview-close\"\n >\n <X className=\"size-4\" />\n </Button>\n </div>\n\n {/* Step rail */}\n <div className=\"border-b border-border-subtle px-3 py-2\">\n <StepRail steps={steps} />\n </div>\n\n {/* Tabs */}\n <Tabs value={tab} onValueChange={(value) => setTab(value as PreviewStepKey)}>\n <TabsList className=\"mx-3 mt-2\">\n <TabsTrigger value=\"datasink\" data-testid=\"ai-preview-tab-datasink\">\n Data Sinks\n </TabsTrigger>\n <TabsTrigger value=\"dashboard\" data-testid=\"ai-preview-tab-dashboard\">\n Dashboards\n </TabsTrigger>\n <TabsTrigger value=\"parser\" data-testid=\"ai-preview-tab-parser\">\n Parsers\n </TabsTrigger>\n <TabsTrigger value=\"widget\" data-testid=\"ai-preview-tab-widget\">\n Widgets\n </TabsTrigger>\n </TabsList>\n\n <div className=\"max-h-[320px] overflow-y-auto p-3\">\n <TabsContent value=\"datasink\" className=\"space-y-2\">\n {sinkRows.length === 0 ? (\n <EmptyOrLoading loading={sinks.loading} empty=\"No data sinks yet.\" />\n ) : (\n sinkRows.map((sink) => (\n <EntityRow\n key={sink.id}\n id={sink.id}\n title={sink.name ?? sink.key}\n meta={sink.status}\n isNew={sink.id === entityIds.datasink}\n onOpen={() => navigate(`/datasinks/${sink.id}`)}\n />\n ))\n )}\n </TabsContent>\n\n <TabsContent value=\"dashboard\" className=\"space-y-2\">\n {dashboardRows.length === 0 ? (\n <EmptyOrLoading loading={dashboards.loading} empty=\"No dashboards yet.\" />\n ) : (\n dashboardRows.map((dashboard) => (\n <EntityRow\n key={dashboard.id}\n id={dashboard.id}\n title={dashboard.name}\n meta={`${String(dashboard.widgetCount ?? 0)} widget(s)`}\n isNew={dashboard.id === entityIds.dashboard}\n onOpen={() => navigate(`/dashboards/${dashboard.id}`)}\n />\n ))\n )}\n </TabsContent>\n\n <TabsContent value=\"parser\" className=\"space-y-2\">\n {!dashboardId ? (\n <p className=\"px-1 py-6 text-sm text-text-muted\">\n Parsers are listed per dashboard — waiting for the dashboard to be created.\n </p>\n ) : parserRows.length === 0 ? (\n <EmptyOrLoading loading={parsers.loading} empty=\"No parsers on this dashboard yet.\" />\n ) : (\n parserRows.map((parser) => (\n <EntityRow\n key={parser.id}\n id={parser.id}\n title={parser.name}\n meta={`${parser.type} · ${parser.status}`}\n isNew={parser.id === entityIds.parser}\n onOpen={() => navigate(`/parsers/${parser.id}`)}\n />\n ))\n )}\n </TabsContent>\n\n <TabsContent value=\"widget\" className=\"space-y-2\">\n {!dashboardId ? (\n <p className=\"px-1 py-6 text-sm text-text-muted\">\n Widgets live on a dashboard — waiting for the dashboard to be created.\n </p>\n ) : widgetRows.length === 0 ? (\n <EmptyOrLoading loading={widgets.loading} empty=\"No widgets on this dashboard yet.\" />\n ) : (\n widgetRows.map((widget) => (\n <EntityRow\n key={widget.id}\n id={widget.id}\n title={widget.title}\n meta={widget.type}\n // A widget has no link of its own in the narration, so the most\n // recent one on the dashboard being built is the new one.\n isNew={widget.id === widgetRows[widgetRows.length - 1]?.id && steps.widget === 'done'}\n onOpen={() => navigate(`/dashboards/${dashboardId}`)}\n />\n ))\n )}\n </TabsContent>\n </div>\n </Tabs>\n\n {error ? (\n <p\n className=\"border-t border-border-subtle px-3 py-2 text-xs text-status-error-text\"\n data-testid=\"ai-preview-error\"\n >\n {error}\n </p>\n ) : null}\n </section>\n );\n}\n\nexport default AiPreviewPanel;\n"],"mappings":"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"}
1
+ {"version":3,"file":"AiPreviewPanel.js","names":[],"sources":["../../../../src/bigconsole/components/preview/AiPreviewPanel.tsx"],"sourcesContent":["/**\n * AI Preview — watch the assistant build.\n *\n * Opens by itself the moment a prompt starts, and shows the DataSink → Dashboard\n * → Parser → Widget chain filling in live: a step rail driven by the agent's own\n * narration, and four tabs listing the real entities as they land.\n *\n * Why polling and not a subscription: the four entity types have no working\n * GraphQL subscriptions (DataSink's are declared in the SDL but have no\n * resolvers; the others have none at all). The same 3.5s poll is already used by\n * DashboardViewPage for exactly this reason. Note the real freshness floor is\n * fe-libs' ~15s shared read cache, not this interval.\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { Badge, Button, Spinner, Tabs, TabsContent, TabsList, TabsTrigger } from '@burdenoff/fe-libs/ui';\nimport { Check, ChevronRight, Loader2, Minus, Sparkles, X } from 'lucide-react';\n\nimport {\n PREVIEW_STEP_LABEL,\n PREVIEW_STEPS,\n type PreviewStepKey,\n type PreviewStepStatus,\n useAssistantRunStore,\n} from '../../assistant/assistantRunStore';\nimport { useBigConsoleNavigate } from '../../context';\nimport { useDashboardOperations } from '../../hooks/useDashboardOperations';\nimport { useDataSinkOperations } from '../../hooks/useDataSinkOperations';\nimport { useParserOperations } from '../../hooks/useParserOperations';\nimport { useWidgetOperations } from '../../hooks/useWidgetOperations';\n\n/** Matches DashboardViewPage's live cadence. */\nconst LIVE_POLL_MS = 3500;\n/** Keep polling briefly after the turn ends — the last write can land late. */\nconst TRAILING_POLL_MS = 30_000;\n/** Parsers are listed per console; a console IS a dashboard. */\nconst DEFAULT_CONSOLE_ID = 'default';\n\nfunction StepPip({ status }: { status: PreviewStepStatus }) {\n if (status === 'done') return <Check className=\"size-3.5 text-status-success-text\" />;\n if (status === 'running') return <Loader2 className=\"size-3.5 animate-spin text-action-primary-bg\" />;\n if (status === 'skipped') return <Minus className=\"size-3.5 text-text-muted\" />;\n return <span className=\"size-1.5 rounded-full bg-border-strong\" />;\n}\n\nfunction StepRail({ steps }: { steps: Record<PreviewStepKey, PreviewStepStatus> }) {\n return (\n <div className=\"flex flex-wrap items-center gap-1\" data-testid=\"ai-preview-step-rail\">\n {PREVIEW_STEPS.map((key, index) => (\n <div key={key} className=\"flex items-center gap-1\">\n <span\n data-testid={`ai-preview-step-${key}`}\n data-status={steps[key]}\n className={[\n 'inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium',\n steps[key] === 'done'\n ? 'border-status-success-border bg-status-success-bg text-status-success-text'\n : steps[key] === 'running'\n ? 'border-border-strong bg-bg-surface text-text-primary'\n : 'border-border-default text-text-muted',\n ].join(' ')}\n >\n <StepPip status={steps[key]} />\n {PREVIEW_STEP_LABEL[key]}\n {steps[key] === 'skipped' ? <span className=\"text-[10px]\">skipped</span> : null}\n </span>\n {index < PREVIEW_STEPS.length - 1 ? <ChevronRight className=\"size-3.5 text-text-muted\" /> : null}\n </div>\n ))}\n </div>\n );\n}\n\nfunction formatAgo(seconds: number): string {\n if (seconds < 5) return 'just now';\n if (seconds < 60) return `${String(seconds)}s ago`;\n return `${String(Math.floor(seconds / 60))}m ago`;\n}\n\ninterface RowProps {\n id: string;\n title: string;\n meta?: string;\n isNew: boolean;\n onOpen?: () => void;\n}\n\nfunction EntityRow({ id, title, meta, isNew, onOpen }: RowProps) {\n return (\n <div\n data-testid=\"ai-preview-row\"\n data-entity-id={id}\n className={[\n 'flex items-center justify-between gap-3 rounded-md border px-3 py-2',\n isNew ? 'border-status-success-border bg-status-success-bg/30' : 'border-border-subtle bg-bg-surface',\n ].join(' ')}\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{title}</p>\n {meta ? <p className=\"truncate text-xs text-text-muted\">{meta}</p> : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {isNew ? (\n <Badge className=\"bg-status-success-bg text-status-success-text\">\n <Sparkles className=\"mr-1 size-3\" />\n just built\n </Badge>\n ) : null}\n {onOpen ? (\n <Button size=\"sm\" variant=\"ghost\" onClick={onOpen}>\n Open\n </Button>\n ) : null}\n </div>\n </div>\n );\n}\n\n/**\n * Width of the assistant sidebar, or 0 when it is closed.\n *\n * Polled rather than subscribed: the widget lives in fe-libs, is mounted by the\n * app shell, and emits nothing when it opens or closes. Reading the DOM is the\n * only honest signal available, and it is cheap.\n */\nfunction useAssistantSidebarWidth(): number {\n const [width, setWidth] = useState(0);\n\n useEffect(() => {\n const measure = () => {\n const el = document.querySelector('[data-testid=\"assistant-widget-window\"][data-layout=\"sidebar\"]');\n const rect = el?.getBoundingClientRect();\n // Below `sm` the sidebar is full-width; reserving that would leave no room\n // for the preview at all, so on small screens let it stack underneath.\n const next = rect && rect.width < window.innerWidth * 0.75 ? Math.round(rect.width) : 0;\n setWidth((current) => (current === next ? current : next));\n };\n measure();\n const intervalId = window.setInterval(measure, 500);\n window.addEventListener('resize', measure);\n return () => {\n window.clearInterval(intervalId);\n window.removeEventListener('resize', measure);\n };\n }, []);\n\n return width;\n}\n\nfunction EmptyOrLoading({ loading, empty }: { loading: boolean; empty: string }) {\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 px-1 py-6 text-sm text-text-muted\">\n <Spinner className=\"size-4\" />\n Loading…\n </div>\n );\n }\n return <p className=\"px-1 py-6 text-sm text-text-muted\">{empty}</p>;\n}\n\nexport function AiPreviewPanel() {\n const navigate = useBigConsoleNavigate();\n\n const isRunning = useAssistantRunStore((state) => state.isRunning);\n const previewOpen = useAssistantRunStore((state) => state.previewOpen);\n const hasRun = useAssistantRunStore((state) => state.hasRun);\n const steps = useAssistantRunStore((state) => state.steps);\n const entityIds = useAssistantRunStore((state) => state.entityIds);\n const caption = useAssistantRunStore((state) => state.caption);\n const error = useAssistantRunStore((state) => state.error);\n const updatedAt = useAssistantRunStore((state) => state.updatedAt);\n const openPreview = useAssistantRunStore((state) => state.openPreview);\n const closePreview = useAssistantRunStore((state) => state.closePreview);\n\n const [tab, setTab] = useState<PreviewStepKey>('datasink');\n const [now, setNow] = useState(() => Date.now());\n\n const dashboardId = entityIds.dashboard;\n\n // The panel does its own fetching, so the tabs are live even before the user\n // opens them — the \"just built\" highlight must be right the instant they look.\n const sinks = useDataSinkOperations({ skipFetch: true });\n const dashboards = useDashboardOperations();\n const parsers = useParserOperations({ consoleId: dashboardId ?? DEFAULT_CONSOLE_ID, skipFetch: true });\n const widgets = useWidgetOperations(undefined, dashboardId, { skipFetch: !dashboardId });\n\n const refetchRef = useRef({\n sinks: sinks.refetch,\n dashboards: dashboards.refetch,\n parsers: parsers.refetch,\n widgets: widgets.refetch,\n });\n refetchRef.current = {\n sinks: sinks.refetch,\n dashboards: dashboards.refetch,\n parsers: parsers.refetch,\n widgets: widgets.refetch,\n };\n\n // Follow the build. Keeps polling for a short while after the turn settles so\n // the final widget does not miss the panel by a second.\n useEffect(() => {\n if (!hasRun) return;\n\n let stopAt = Number.POSITIVE_INFINITY;\n if (!isRunning) stopAt = Date.now() + TRAILING_POLL_MS;\n\n const poll = () => {\n if (typeof document !== 'undefined' && document.visibilityState !== 'visible') return;\n if (Date.now() > stopAt) return;\n void refetchRef.current.sinks();\n void refetchRef.current.dashboards();\n if (dashboardId) {\n void refetchRef.current.parsers();\n void refetchRef.current.widgets();\n }\n setNow(Date.now());\n };\n\n poll();\n const intervalId = window.setInterval(poll, LIVE_POLL_MS);\n const onVisible = () => {\n if (document.visibilityState === 'visible') poll();\n };\n document.addEventListener('visibilitychange', onVisible);\n return () => {\n window.clearInterval(intervalId);\n document.removeEventListener('visibilitychange', onVisible);\n };\n }, [hasRun, isRunning, dashboardId]);\n\n // Jump the user to whatever is being worked on, so they are not left staring\n // at the Data Sinks tab while the parser is the thing actually happening.\n useEffect(() => {\n const active = PREVIEW_STEPS.find((key) => steps[key] === 'running');\n if (active) setTab(active);\n }, [steps]);\n\n const agoSeconds = updatedAt ? Math.max(0, Math.floor((now - updatedAt) / 1000)) : 0;\n\n // Keep clear of the assistant.\n //\n // The assistant sidebar is `position: fixed` — it OVERLAYS the page instead of\n // reflowing it. A panel that spans the full content width therefore runs\n // underneath it, and everything in that overlapped strip (tabs, Open buttons)\n // silently stops taking clicks. Measure the real element rather than hardcoding\n // 560px: the widget is responsive, and it can be closed mid-run.\n const assistantWidth = useAssistantSidebarWidth();\n\n const sinkRows = useMemo(() => sinks.dataSinks.slice(0, 25), [sinks.dataSinks]);\n const dashboardRows = useMemo(() => dashboards.dashboards.slice(0, 25), [dashboards.dashboards]);\n const parserRows = useMemo(() => parsers.parsers.slice(0, 25), [parsers.parsers]);\n const widgetRows = useMemo(() => widgets.widgets.slice(0, 25), [widgets.widgets]);\n\n if (!hasRun) return null;\n\n if (!previewOpen) {\n return (\n <div className=\"flex justify-end px-4 pt-3 md:px-6\" style={{ marginRight: assistantWidth }}>\n <Button size=\"sm\" variant=\"outline\" onClick={openPreview} data-testid=\"ai-preview-open\">\n <Sparkles className=\"mr-1.5 size-4\" />\n Preview\n {isRunning ? <Loader2 className=\"ml-1.5 size-3.5 animate-spin\" /> : null}\n </Button>\n </div>\n );\n }\n\n return (\n <section\n data-testid=\"ai-preview-panel\"\n aria-label=\"AI preview\"\n style={{ marginRight: assistantWidth ? assistantWidth + 16 : undefined }}\n className=\"mx-4 mt-3 overflow-hidden rounded-lg border border-border-default bg-bg-sunken shadow-sm md:mx-6\"\n >\n {/* Header + live strip */}\n <div className=\"flex items-center justify-between gap-3 border-b border-border-subtle bg-bg-surface px-3 py-2\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Sparkles className=\"size-4 shrink-0 text-action-primary-bg\" />\n <h2 className=\"truncate text-sm font-semibold text-text-primary\">AI Preview</h2>\n {isRunning ? (\n <span\n className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-status-success-text\"\n data-testid=\"ai-preview-live\"\n >\n <span className=\"relative flex size-2\">\n <span className=\"absolute inline-flex size-full animate-ping rounded-full bg-status-success-text opacity-75\" />\n <span className=\"relative inline-flex size-2 rounded-full bg-status-success-text\" />\n </span>\n Live\n </span>\n ) : null}\n <span className=\"truncate text-xs text-text-muted\" data-testid=\"ai-preview-ago\">\n {isRunning ? (caption ? caption : 'Working…') : error ? 'Stopped' : 'Done'} · updated{' '}\n {formatAgo(agoSeconds)}\n </span>\n </div>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={closePreview}\n aria-label=\"Close preview\"\n data-testid=\"ai-preview-close\"\n >\n <X className=\"size-4\" />\n </Button>\n </div>\n\n {/* Step rail */}\n <div className=\"border-b border-border-subtle px-3 py-2\">\n <StepRail steps={steps} />\n </div>\n\n {/* Tabs */}\n <Tabs value={tab} onValueChange={(value) => setTab(value as PreviewStepKey)}>\n <TabsList className=\"mx-3 mt-2\">\n <TabsTrigger value=\"datasink\" data-testid=\"ai-preview-tab-datasink\">\n Data Sinks\n </TabsTrigger>\n <TabsTrigger value=\"dashboard\" data-testid=\"ai-preview-tab-dashboard\">\n Dashboards\n </TabsTrigger>\n <TabsTrigger value=\"parser\" data-testid=\"ai-preview-tab-parser\">\n Parsers\n </TabsTrigger>\n <TabsTrigger value=\"widget\" data-testid=\"ai-preview-tab-widget\">\n Widgets\n </TabsTrigger>\n </TabsList>\n\n <div className=\"max-h-[320px] overflow-y-auto p-3\">\n <TabsContent value=\"datasink\" className=\"space-y-2\">\n {sinkRows.length === 0 ? (\n <EmptyOrLoading loading={sinks.loading} empty=\"No data sinks yet.\" />\n ) : (\n sinkRows.map((sink) => (\n <EntityRow\n key={sink.id}\n id={sink.id}\n title={sink.name ?? sink.key}\n meta={sink.status}\n isNew={sink.id === entityIds.datasink}\n onOpen={() => navigate(`/datasinks/${sink.id}`)}\n />\n ))\n )}\n </TabsContent>\n\n <TabsContent value=\"dashboard\" className=\"space-y-2\">\n {dashboardRows.length === 0 ? (\n <EmptyOrLoading loading={dashboards.loading} empty=\"No dashboards yet.\" />\n ) : (\n dashboardRows.map((dashboard) => (\n <EntityRow\n key={dashboard.id}\n id={dashboard.id}\n title={dashboard.name}\n meta={`${String(dashboard.widgetCount ?? 0)} widget(s)`}\n isNew={dashboard.id === entityIds.dashboard}\n onOpen={() => navigate(`/dashboards/${dashboard.id}`)}\n />\n ))\n )}\n </TabsContent>\n\n <TabsContent value=\"parser\" className=\"space-y-2\">\n {!dashboardId ? (\n <p className=\"px-1 py-6 text-sm text-text-muted\">\n Parsers are listed per dashboard — waiting for the dashboard to be created.\n </p>\n ) : parserRows.length === 0 ? (\n <EmptyOrLoading loading={parsers.loading} empty=\"No parsers on this dashboard yet.\" />\n ) : (\n parserRows.map((parser) => (\n <EntityRow\n key={parser.id}\n id={parser.id}\n title={parser.name}\n meta={`${parser.type} · ${parser.status}`}\n isNew={parser.id === entityIds.parser}\n onOpen={() => navigate(`/parsers/${parser.id}`)}\n />\n ))\n )}\n </TabsContent>\n\n <TabsContent value=\"widget\" className=\"space-y-2\">\n {!dashboardId ? (\n <p className=\"px-1 py-6 text-sm text-text-muted\">\n Widgets live on a dashboard — waiting for the dashboard to be created.\n </p>\n ) : widgetRows.length === 0 ? (\n <EmptyOrLoading loading={widgets.loading} empty=\"No widgets on this dashboard yet.\" />\n ) : (\n widgetRows.map((widget) => (\n <EntityRow\n key={widget.id}\n id={widget.id}\n title={widget.title}\n meta={widget.type}\n // A widget has no link of its own in the narration, so the most\n // recent one on the dashboard being built is the new one.\n isNew={widget.id === widgetRows[widgetRows.length - 1]?.id && steps.widget === 'done'}\n onOpen={() => navigate(`/dashboards/${dashboardId}`)}\n />\n ))\n )}\n </TabsContent>\n </div>\n </Tabs>\n\n {error ? (\n <p\n className=\"border-t border-border-subtle px-3 py-2 text-xs text-status-error-text\"\n data-testid=\"ai-preview-error\"\n >\n {error}\n </p>\n ) : null}\n </section>\n );\n}\n\nexport default AiPreviewPanel;\n"],"mappings":"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"}
@@ -6,6 +6,7 @@ import "./components/DrilldownModal.js";
6
6
  import "./hooks/useDrilldown.js";
7
7
  import "./hooks/index.js";
8
8
  import "./BigConsoleRoot.js";
9
+ import "./AdminRoot.js";
9
10
  import "./assistant/pageContext.js";
10
11
  import "./assistant/createSandboxAssistantTransport.js";
11
12
  import "./assistant/index.js";
@@ -0,0 +1,504 @@
1
+ import { ListDataSinksDocument as e, ListEventLogsDocument as t } from "../../generated/wspace-operations.js";
2
+ import { memo as n, useCallback as r, useMemo as i, useState as a } from "react";
3
+ import { AlertCircle as o, Database as s, Flag as c, LayoutDashboard as l, RefreshCw as u, ScrollText as d, Search as f } from "lucide-react";
4
+ import { useQuery as p } from "@apollo/client/react";
5
+ import { useI18n as m } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
6
+ import { Badge as h, Button as g, Card as _, CardContent as v, CardDescription as y, CardHeader as b, CardTitle as x, Input as S, Spinner as C, Tabs as w, TabsContent as T, TabsList as E, TabsTrigger as D } from "@burdenoff/fe-libs/ui";
7
+ import { jsx as O, jsxs as k } from "react/jsx-runtime";
8
+ import { formatDistanceToNow as A } from "date-fns";
9
+ //#region src/bigconsole/pages/AdminPage.tsx
10
+ var j = [
11
+ {
12
+ id: "flag-1",
13
+ key: "bigconsole.ai-insights",
14
+ name: "AI Insights",
15
+ description: "Enable AI-generated insights across dashboards",
16
+ enabled: !0,
17
+ scope: "organization"
18
+ },
19
+ {
20
+ id: "flag-2",
21
+ key: "bigconsole.real-time-collaboration",
22
+ name: "Real-time Collaboration",
23
+ description: "Allow multiple users to edit dashboards simultaneously",
24
+ enabled: !1,
25
+ scope: "workspace"
26
+ },
27
+ {
28
+ id: "flag-3",
29
+ key: "bigconsole.advanced-drilldown",
30
+ name: "Advanced Drilldown",
31
+ description: "Enable cross-dashboard drilldown and parameter passing",
32
+ enabled: !0,
33
+ scope: "organization"
34
+ }
35
+ ];
36
+ function M(e) {
37
+ if (e == null || e === 0) return "0 B";
38
+ let t = [
39
+ "B",
40
+ "KB",
41
+ "MB",
42
+ "GB",
43
+ "TB"
44
+ ], n = Math.floor(Math.log(e) / Math.log(1024));
45
+ return `${(e / 1024 ** n).toFixed(2)} ${t[n]}`;
46
+ }
47
+ function N(e) {
48
+ if (!e) return "—";
49
+ try {
50
+ return A(new Date(e), { addSuffix: !0 });
51
+ } catch {
52
+ return e;
53
+ }
54
+ }
55
+ var P = n(function() {
56
+ let { t } = m(), n = r((e, n) => {
57
+ let r = t(e);
58
+ return r === e ? n : r;
59
+ }, [t]), [c, d] = a(""), { data: w, loading: T, error: E, refetch: D } = p(e, {
60
+ variables: {
61
+ first: 50,
62
+ search: c || void 0
63
+ },
64
+ notifyOnNetworkStatusChange: !0
65
+ }), A = i(() => (w?.listDataSinks?.edges ?? []).map((e) => e.node), [w]);
66
+ return /* @__PURE__ */ k("div", {
67
+ className: "space-y-6",
68
+ children: [
69
+ /* @__PURE__ */ k("div", {
70
+ className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
71
+ children: [/* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("h2", {
72
+ className: "text-lg font-semibold text-text-primary",
73
+ children: n("bigconsole.admin.registry.title", "Widget & Data-sink Registry")
74
+ }), /* @__PURE__ */ O("p", {
75
+ className: "text-sm text-text-secondary",
76
+ children: n("bigconsole.admin.registry.subtitle", "Browse and govern dashboards, widgets, and data sinks across the organization.")
77
+ })] }), /* @__PURE__ */ k("div", {
78
+ className: "flex items-center gap-2",
79
+ children: [/* @__PURE__ */ k("div", {
80
+ className: "relative",
81
+ children: [/* @__PURE__ */ O(f, { className: "absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary" }), /* @__PURE__ */ O(S, {
82
+ type: "text",
83
+ placeholder: n("bigconsole.admin.registry.search", "Search data sinks..."),
84
+ value: c,
85
+ onChange: (e) => d(e.target.value),
86
+ className: "pl-9 w-full sm:w-64"
87
+ })]
88
+ }), /* @__PURE__ */ O(g, {
89
+ variant: "outline",
90
+ size: "icon",
91
+ onClick: () => D(),
92
+ disabled: T,
93
+ "aria-label": n("bigconsole.admin.registry.refresh", "Refresh"),
94
+ children: /* @__PURE__ */ O(u, { className: `h-4 w-4 ${T ? "animate-spin" : ""}` })
95
+ })]
96
+ })]
97
+ }),
98
+ /* @__PURE__ */ k(_, {
99
+ className: "border-dashed border-border-default bg-bg-surface/50",
100
+ children: [/* @__PURE__ */ k(b, {
101
+ className: "pb-3",
102
+ children: [/* @__PURE__ */ k(x, {
103
+ className: "text-base font-medium text-text-primary flex items-center gap-2",
104
+ children: [/* @__PURE__ */ O(l, { className: "h-4 w-4 text-text-secondary" }), n("bigconsole.admin.registry.widgets", "Widget Registry")]
105
+ }), /* @__PURE__ */ O(y, {
106
+ className: "text-sm text-text-secondary",
107
+ children: n("bigconsole.admin.registry.widgets.description", "Cross-workspace widget listing requires a backend query that supports organization-scoped pagination (listWidgets currently requires pageId).")
108
+ })]
109
+ }), /* @__PURE__ */ O(v, { children: /* @__PURE__ */ k("div", {
110
+ className: "flex items-start gap-3 rounded-md bg-warning-bg/10 p-3 text-sm text-warning-fg",
111
+ children: [/* @__PURE__ */ O(o, { className: "mt-0.5 h-4 w-4 shrink-0" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
112
+ className: "font-medium",
113
+ children: n("bigconsole.admin.registry.widgets.gap.title", "Backend gap")
114
+ }), /* @__PURE__ */ O("p", {
115
+ className: "mt-1 text-text-secondary",
116
+ children: n("bigconsole.admin.registry.widgets.gap.body", "Add adminBigConsoleWidgets(organizationId, first, after) to global or workspace schema, then wire it here.")
117
+ })] })]
118
+ }) })]
119
+ }),
120
+ /* @__PURE__ */ k(_, {
121
+ className: "bg-bg-surface border-border-default",
122
+ children: [/* @__PURE__ */ k(b, {
123
+ className: "pb-3",
124
+ children: [/* @__PURE__ */ k(x, {
125
+ className: "text-base font-medium text-text-primary flex items-center gap-2",
126
+ children: [/* @__PURE__ */ O(s, { className: "h-4 w-4 text-text-secondary" }), n("bigconsole.admin.registry.datasinks", "Data-sink Registry")]
127
+ }), /* @__PURE__ */ O(y, {
128
+ className: "text-sm text-text-secondary",
129
+ children: n("bigconsole.admin.registry.datasinks.description", "Live view of data sinks across the organization.")
130
+ })]
131
+ }), /* @__PURE__ */ O(v, { children: E ? /* @__PURE__ */ O("div", {
132
+ className: "rounded-md bg-danger-bg/10 p-4 text-sm text-danger-fg",
133
+ children: E.message
134
+ }) : T && !w ? /* @__PURE__ */ O("div", {
135
+ className: "flex items-center justify-center py-12",
136
+ children: /* @__PURE__ */ O(C, { className: "h-8 w-8" })
137
+ }) : A.length === 0 ? /* @__PURE__ */ O("div", {
138
+ className: "text-center py-12 text-text-secondary text-sm",
139
+ children: n("bigconsole.admin.registry.datasinks.empty", "No data sinks found.")
140
+ }) : /* @__PURE__ */ O("div", {
141
+ className: "overflow-x-auto",
142
+ children: /* @__PURE__ */ k("table", {
143
+ className: "w-full text-sm",
144
+ children: [/* @__PURE__ */ O("thead", { children: /* @__PURE__ */ k("tr", {
145
+ className: "border-b border-border-default text-left text-text-secondary",
146
+ children: [
147
+ /* @__PURE__ */ O("th", {
148
+ className: "pb-3 pr-4 font-medium",
149
+ children: n("bigconsole.admin.registry.datasinks.name", "Name")
150
+ }),
151
+ /* @__PURE__ */ O("th", {
152
+ className: "pb-3 pr-4 font-medium",
153
+ children: n("bigconsole.admin.registry.datasinks.status", "Status")
154
+ }),
155
+ /* @__PURE__ */ O("th", {
156
+ className: "pb-3 pr-4 font-medium",
157
+ children: n("bigconsole.admin.registry.datasinks.size", "Size")
158
+ }),
159
+ /* @__PURE__ */ O("th", {
160
+ className: "pb-3 pr-4 font-medium",
161
+ children: n("bigconsole.admin.registry.datasinks.links", "Links")
162
+ }),
163
+ /* @__PURE__ */ O("th", {
164
+ className: "pb-3 pr-4 font-medium",
165
+ children: n("bigconsole.admin.registry.datasinks.refreshed", "Refreshed")
166
+ }),
167
+ /* @__PURE__ */ O("th", {
168
+ className: "pb-3 font-medium",
169
+ children: n("bigconsole.admin.registry.datasinks.active", "Active")
170
+ })
171
+ ]
172
+ }) }), /* @__PURE__ */ O("tbody", {
173
+ className: "text-text-primary",
174
+ children: A.map((e) => /* @__PURE__ */ k("tr", {
175
+ className: "border-b border-border-seam last:border-0",
176
+ children: [
177
+ /* @__PURE__ */ k("td", {
178
+ className: "py-3 pr-4",
179
+ children: [/* @__PURE__ */ O("div", {
180
+ className: "font-medium",
181
+ children: e.name || e.key
182
+ }), /* @__PURE__ */ O("div", {
183
+ className: "text-xs text-text-tertiary",
184
+ children: e.key
185
+ })]
186
+ }),
187
+ /* @__PURE__ */ O("td", {
188
+ className: "py-3 pr-4",
189
+ children: /* @__PURE__ */ O(h, {
190
+ variant: e.status === "ready" ? "default" : "secondary",
191
+ children: e.status
192
+ })
193
+ }),
194
+ /* @__PURE__ */ O("td", {
195
+ className: "py-3 pr-4 tabular-nums",
196
+ children: M(e.sizeBytes)
197
+ }),
198
+ /* @__PURE__ */ O("td", {
199
+ className: "py-3 pr-4 tabular-nums",
200
+ children: e.linkedWorkflowCount ?? 0
201
+ }),
202
+ /* @__PURE__ */ O("td", {
203
+ className: "py-3 pr-4 text-text-secondary",
204
+ children: N(e.lastRefreshedAt)
205
+ }),
206
+ /* @__PURE__ */ O("td", {
207
+ className: "py-3",
208
+ children: /* @__PURE__ */ O(h, {
209
+ variant: e.isActive ? "default" : "outline",
210
+ children: e.isActive ? "Yes" : "No"
211
+ })
212
+ })
213
+ ]
214
+ }, e.id))
215
+ })]
216
+ })
217
+ }) })]
218
+ })
219
+ ]
220
+ });
221
+ }), F = n(function() {
222
+ let { t: e } = m(), t = r((t, n) => {
223
+ let r = e(t);
224
+ return r === t ? n : r;
225
+ }, [e]), [n, i] = a(j), s = r((e) => {
226
+ i((t) => t.map((t) => t.id === e ? {
227
+ ...t,
228
+ enabled: !t.enabled
229
+ } : t));
230
+ }, []);
231
+ return /* @__PURE__ */ k("div", {
232
+ className: "space-y-6",
233
+ children: [
234
+ /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("h2", {
235
+ className: "text-lg font-semibold text-text-primary",
236
+ children: t("bigconsole.admin.featureFlags.title", "Org-level Feature Flags")
237
+ }), /* @__PURE__ */ O("p", {
238
+ className: "text-sm text-text-secondary",
239
+ children: t("bigconsole.admin.featureFlags.subtitle", "Manage BigConsole features across organizations and workspaces.")
240
+ })] }),
241
+ /* @__PURE__ */ k("div", {
242
+ className: "flex items-start gap-3 rounded-md bg-info-bg/10 p-3 text-sm text-info-fg",
243
+ children: [/* @__PURE__ */ O(o, { className: "mt-0.5 h-4 w-4 shrink-0" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
244
+ className: "font-medium",
245
+ children: t("bigconsole.admin.featureFlags.placeholder.title", "Placeholder data")
246
+ }), /* @__PURE__ */ O("p", {
247
+ className: "mt-1 text-text-secondary",
248
+ children: t("bigconsole.admin.featureFlags.placeholder.body", "Wire this to the feature-flag service admin API (list/upsert organization feature flags) when available.")
249
+ })] })]
250
+ }),
251
+ /* @__PURE__ */ O("div", {
252
+ className: "grid gap-4",
253
+ children: n.map((e) => /* @__PURE__ */ O(_, {
254
+ className: "bg-bg-surface border-border-default",
255
+ children: /* @__PURE__ */ O(v, {
256
+ className: "p-4",
257
+ children: /* @__PURE__ */ k("div", {
258
+ className: "flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between",
259
+ children: [/* @__PURE__ */ k("div", {
260
+ className: "space-y-1",
261
+ children: [
262
+ /* @__PURE__ */ k("div", {
263
+ className: "flex items-center gap-2",
264
+ children: [
265
+ /* @__PURE__ */ O(c, { className: "h-4 w-4 text-text-secondary" }),
266
+ /* @__PURE__ */ O("span", {
267
+ className: "font-medium text-text-primary",
268
+ children: e.name
269
+ }),
270
+ /* @__PURE__ */ O(h, {
271
+ variant: "outline",
272
+ children: e.scope
273
+ })
274
+ ]
275
+ }),
276
+ /* @__PURE__ */ O("p", {
277
+ className: "text-sm text-text-secondary",
278
+ children: e.description
279
+ }),
280
+ /* @__PURE__ */ O("code", {
281
+ className: "text-xs text-text-tertiary bg-bg-sunken px-1.5 py-0.5 rounded",
282
+ children: e.key
283
+ })
284
+ ]
285
+ }), /* @__PURE__ */ O(g, {
286
+ variant: e.enabled ? "default" : "outline",
287
+ size: "sm",
288
+ onClick: () => s(e.id),
289
+ className: "shrink-0",
290
+ children: e.enabled ? t("bigconsole.admin.featureFlags.enabled", "Enabled") : t("bigconsole.admin.featureFlags.disabled", "Disabled")
291
+ })]
292
+ })
293
+ })
294
+ }, e.id))
295
+ })
296
+ ]
297
+ });
298
+ }), I = n(function() {
299
+ let { t: e } = m(), n = r((t, n) => {
300
+ let r = e(t);
301
+ return r === t ? n : r;
302
+ }, [e]), [o, s] = a("all"), { data: c, loading: l, error: d, refetch: f } = p(t, {
303
+ variables: {
304
+ first: 50,
305
+ status: o === "all" ? void 0 : o
306
+ },
307
+ notifyOnNetworkStatusChange: !0
308
+ }), y = i(() => (c?.listEventLogs?.edges ?? []).map((e) => e.node), [c]);
309
+ return /* @__PURE__ */ k("div", {
310
+ className: "space-y-6",
311
+ children: [/* @__PURE__ */ k("div", {
312
+ className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
313
+ children: [/* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("h2", {
314
+ className: "text-lg font-semibold text-text-primary",
315
+ children: n("bigconsole.admin.auditLog.title", "Audit Log Viewer")
316
+ }), /* @__PURE__ */ O("p", {
317
+ className: "text-sm text-text-secondary",
318
+ children: n("bigconsole.admin.auditLog.subtitle", "Widget event delivery log. Organization-level audit trail requires a dedicated admin audit query.")
319
+ })] }), /* @__PURE__ */ k("div", {
320
+ className: "flex items-center gap-2",
321
+ children: [/* @__PURE__ */ k("select", {
322
+ value: o,
323
+ onChange: (e) => s(e.target.value),
324
+ className: "h-9 rounded-md border border-border-default bg-bg-surface px-3 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-focus-ring",
325
+ children: [
326
+ /* @__PURE__ */ O("option", {
327
+ value: "all",
328
+ children: n("bigconsole.admin.auditLog.filter.all", "All statuses")
329
+ }),
330
+ /* @__PURE__ */ O("option", {
331
+ value: "success",
332
+ children: n("bigconsole.admin.auditLog.filter.success", "Success")
333
+ }),
334
+ /* @__PURE__ */ O("option", {
335
+ value: "failed",
336
+ children: n("bigconsole.admin.auditLog.filter.failed", "Failed")
337
+ })
338
+ ]
339
+ }), /* @__PURE__ */ O(g, {
340
+ variant: "outline",
341
+ size: "icon",
342
+ onClick: () => f(),
343
+ disabled: l,
344
+ "aria-label": n("bigconsole.admin.auditLog.refresh", "Refresh"),
345
+ children: /* @__PURE__ */ O(u, { className: `h-4 w-4 ${l ? "animate-spin" : ""}` })
346
+ })]
347
+ })]
348
+ }), /* @__PURE__ */ O(_, {
349
+ className: "bg-bg-surface border-border-default",
350
+ children: /* @__PURE__ */ O(v, {
351
+ className: "p-0",
352
+ children: d ? /* @__PURE__ */ O("div", {
353
+ className: "rounded-md bg-danger-bg/10 p-4 text-sm text-danger-fg",
354
+ children: d.message
355
+ }) : l && !c ? /* @__PURE__ */ O("div", {
356
+ className: "flex items-center justify-center py-12",
357
+ children: /* @__PURE__ */ O(C, { className: "h-8 w-8" })
358
+ }) : y.length === 0 ? /* @__PURE__ */ O("div", {
359
+ className: "text-center py-12 text-text-secondary text-sm",
360
+ children: n("bigconsole.admin.auditLog.empty", "No audit events found.")
361
+ }) : /* @__PURE__ */ O("div", {
362
+ className: "overflow-x-auto",
363
+ children: /* @__PURE__ */ k("table", {
364
+ className: "w-full text-sm",
365
+ children: [/* @__PURE__ */ O("thead", { children: /* @__PURE__ */ k("tr", {
366
+ className: "border-b border-border-default text-left text-text-secondary",
367
+ children: [
368
+ /* @__PURE__ */ O("th", {
369
+ className: "pb-3 pr-4 font-medium p-4",
370
+ children: n("bigconsole.admin.auditLog.event", "Event")
371
+ }),
372
+ /* @__PURE__ */ O("th", {
373
+ className: "pb-3 pr-4 font-medium",
374
+ children: n("bigconsole.admin.auditLog.action", "Action")
375
+ }),
376
+ /* @__PURE__ */ O("th", {
377
+ className: "pb-3 pr-4 font-medium",
378
+ children: n("bigconsole.admin.auditLog.status", "Status")
379
+ }),
380
+ /* @__PURE__ */ O("th", {
381
+ className: "pb-3 pr-4 font-medium",
382
+ children: n("bigconsole.admin.auditLog.widget", "Widget")
383
+ }),
384
+ /* @__PURE__ */ O("th", {
385
+ className: "pb-3 pr-4 font-medium",
386
+ children: n("bigconsole.admin.auditLog.dashboard", "Dashboard")
387
+ }),
388
+ /* @__PURE__ */ O("th", {
389
+ className: "pb-3 font-medium",
390
+ children: n("bigconsole.admin.auditLog.time", "Time")
391
+ })
392
+ ]
393
+ }) }), /* @__PURE__ */ O("tbody", {
394
+ className: "text-text-primary",
395
+ children: y.map((e) => /* @__PURE__ */ k("tr", {
396
+ className: "border-b border-border-seam last:border-0",
397
+ children: [
398
+ /* @__PURE__ */ k("td", {
399
+ className: "py-3 pr-4 p-4",
400
+ children: [/* @__PURE__ */ O("div", {
401
+ className: "font-medium",
402
+ children: e.eventName
403
+ }), e.errorMessage && /* @__PURE__ */ O("div", {
404
+ className: "text-xs text-danger-fg mt-1 max-w-xs truncate",
405
+ children: e.errorMessage
406
+ })]
407
+ }),
408
+ /* @__PURE__ */ O("td", {
409
+ className: "py-3 pr-4",
410
+ children: e.actionName || "—"
411
+ }),
412
+ /* @__PURE__ */ O("td", {
413
+ className: "py-3 pr-4",
414
+ children: /* @__PURE__ */ O(h, {
415
+ variant: e.status === "success" ? "default" : e.status === "failed" ? "destructive" : "secondary",
416
+ children: e.status
417
+ })
418
+ }),
419
+ /* @__PURE__ */ O("td", {
420
+ className: "py-3 pr-4 font-mono text-xs text-text-tertiary",
421
+ children: e.widgetId || "—"
422
+ }),
423
+ /* @__PURE__ */ O("td", {
424
+ className: "py-3 pr-4 font-mono text-xs text-text-tertiary",
425
+ children: e.dashboardId || "—"
426
+ }),
427
+ /* @__PURE__ */ O("td", {
428
+ className: "py-3 text-text-secondary",
429
+ children: N(e.createdAt)
430
+ })
431
+ ]
432
+ }, e.id))
433
+ })]
434
+ })
435
+ })
436
+ })
437
+ })]
438
+ });
439
+ }), L = n(function() {
440
+ let { t: e } = m(), t = r((t, n) => {
441
+ let r = e(t);
442
+ return r === t ? n : r;
443
+ }, [e]), [n, i] = a("registry");
444
+ return /* @__PURE__ */ O("div", {
445
+ className: "min-h-screen bg-bg-base p-6",
446
+ children: /* @__PURE__ */ k("div", {
447
+ className: "mx-auto max-w-7xl space-y-6",
448
+ children: [/* @__PURE__ */ k("div", {
449
+ className: "space-y-1",
450
+ children: [/* @__PURE__ */ O("h1", {
451
+ className: "text-2xl font-bold text-text-primary",
452
+ children: t("bigconsole.admin.title", "BigConsole Admin")
453
+ }), /* @__PURE__ */ O("p", {
454
+ className: "text-sm text-text-secondary",
455
+ children: t("bigconsole.admin.description", "Product-specific administration and governance for BigConsole.")
456
+ })]
457
+ }), /* @__PURE__ */ k(w, {
458
+ value: n,
459
+ onValueChange: (e) => i(e),
460
+ children: [
461
+ /* @__PURE__ */ k(E, {
462
+ className: "bg-bg-surface border border-border-default",
463
+ children: [
464
+ /* @__PURE__ */ k(D, {
465
+ value: "registry",
466
+ className: "flex items-center gap-2",
467
+ children: [/* @__PURE__ */ O(l, { className: "h-4 w-4" }), t("bigconsole.admin.tab.registry", "Registry")]
468
+ }),
469
+ /* @__PURE__ */ k(D, {
470
+ value: "feature-flags",
471
+ className: "flex items-center gap-2",
472
+ children: [/* @__PURE__ */ O(c, { className: "h-4 w-4" }), t("bigconsole.admin.tab.featureFlags", "Feature Flags")]
473
+ }),
474
+ /* @__PURE__ */ k(D, {
475
+ value: "audit-log",
476
+ className: "flex items-center gap-2",
477
+ children: [/* @__PURE__ */ O(d, { className: "h-4 w-4" }), t("bigconsole.admin.tab.auditLog", "Audit Log")]
478
+ })
479
+ ]
480
+ }),
481
+ /* @__PURE__ */ O(T, {
482
+ value: "registry",
483
+ className: "mt-6",
484
+ children: /* @__PURE__ */ O(P, {})
485
+ }),
486
+ /* @__PURE__ */ O(T, {
487
+ value: "feature-flags",
488
+ className: "mt-6",
489
+ children: /* @__PURE__ */ O(F, {})
490
+ }),
491
+ /* @__PURE__ */ O(T, {
492
+ value: "audit-log",
493
+ className: "mt-6",
494
+ children: /* @__PURE__ */ O(I, {})
495
+ })
496
+ ]
497
+ })]
498
+ })
499
+ });
500
+ });
501
+ //#endregion
502
+ export { L as AdminPage };
503
+
504
+ //# sourceMappingURL=AdminPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminPage.js","names":[],"sources":["../../../src/bigconsole/pages/AdminPage.tsx"],"sourcesContent":["/**\n * BigConsole Admin Page\n *\n * Product-specific admin surface mounted inside the platform admin-app.\n * Provides governance views for:\n * - Widget / Data-sink registry\n * - Org-level feature flags\n * - Audit log viewer\n *\n * Uses real GraphQL queries where they exist and documented placeholders\n * where backend support is still needed.\n */\n\nimport { type FC, memo, useCallback, useMemo, useState } from 'react';\nimport { useQuery } from '@apollo/client/react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n CardDescription,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Badge,\n Spinner,\n Input,\n} from '@burdenoff/fe-libs/ui';\nimport { LayoutDashboard, Database, Flag, ScrollText, Search, AlertCircle, RefreshCw } from 'lucide-react';\nimport { formatDistanceToNow } from 'date-fns';\nimport {\n ListDataSinksDocument,\n ListEventLogsDocument,\n type ListDataSinksQuery,\n type ListEventLogsQuery,\n} from '../../generated/wspace-operations';\nimport type { DataSink } from '../types';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ntype AdminTab = 'registry' | 'feature-flags' | 'audit-log';\n\ninterface EventLogNode {\n id: string;\n eventName: string;\n actionName?: string | null;\n status: string;\n widgetId?: string | null;\n dashboardId?: string | null;\n createdAt: string;\n errorMessage?: string | null;\n}\n\n// ============================================================================\n// PLACEHOLDER DATA\n// ============================================================================\n\ninterface FeatureFlag {\n id: string;\n key: string;\n name: string;\n description: string;\n enabled: boolean;\n scope: 'organization' | 'workspace' | 'global';\n}\n\nconst PLACEHOLDER_FEATURE_FLAGS: FeatureFlag[] = [\n {\n id: 'flag-1',\n key: 'bigconsole.ai-insights',\n name: 'AI Insights',\n description: 'Enable AI-generated insights across dashboards',\n enabled: true,\n scope: 'organization',\n },\n {\n id: 'flag-2',\n key: 'bigconsole.real-time-collaboration',\n name: 'Real-time Collaboration',\n description: 'Allow multiple users to edit dashboards simultaneously',\n enabled: false,\n scope: 'workspace',\n },\n {\n id: 'flag-3',\n key: 'bigconsole.advanced-drilldown',\n name: 'Advanced Drilldown',\n description: 'Enable cross-dashboard drilldown and parameter passing',\n enabled: true,\n scope: 'organization',\n },\n];\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\nfunction formatBytes(bytes: number | undefined | null): string {\n if (bytes === undefined || bytes === null || bytes === 0) return '0 B';\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`;\n}\n\nfunction formatDate(iso: string | undefined | null): string {\n if (!iso) return '—';\n try {\n return formatDistanceToNow(new Date(iso), { addSuffix: true });\n } catch {\n return iso;\n }\n}\n\n// ============================================================================\n// SECTIONS\n// ============================================================================\n\nconst RegistrySection: FC = memo(function RegistrySection() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n\n const [search, setSearch] = useState('');\n\n const {\n data: dataSinksData,\n loading: dataSinksLoading,\n error: dataSinksError,\n refetch: refetchDataSinks,\n } = useQuery<ListDataSinksQuery>(ListDataSinksDocument, {\n variables: { first: 50, search: search || undefined },\n notifyOnNetworkStatusChange: true,\n });\n\n const dataSinks = useMemo<DataSink[]>(() => {\n return (dataSinksData?.listDataSinks?.edges ?? []).map((edge) => edge.node as unknown as DataSink);\n }, [dataSinksData]);\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('bigconsole.admin.registry.title', 'Widget & Data-sink Registry')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'bigconsole.admin.registry.subtitle',\n 'Browse and govern dashboards, widgets, and data sinks across the organization.'\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"relative\">\n <Search className=\"absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary\" />\n <Input\n type=\"text\"\n placeholder={tr('bigconsole.admin.registry.search', 'Search data sinks...')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"pl-9 w-full sm:w-64\"\n />\n </div>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => refetchDataSinks()}\n disabled={dataSinksLoading}\n aria-label={tr('bigconsole.admin.registry.refresh', 'Refresh')}\n >\n <RefreshCw className={`h-4 w-4 ${dataSinksLoading ? 'animate-spin' : ''}`} />\n </Button>\n </div>\n </div>\n\n {/* Widget registry placeholder — backend query needs cross-workspace support */}\n <Card className=\"border-dashed border-border-default bg-bg-surface/50\">\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-base font-medium text-text-primary flex items-center gap-2\">\n <LayoutDashboard className=\"h-4 w-4 text-text-secondary\" />\n {tr('bigconsole.admin.registry.widgets', 'Widget Registry')}\n </CardTitle>\n <CardDescription className=\"text-sm text-text-secondary\">\n {tr(\n 'bigconsole.admin.registry.widgets.description',\n 'Cross-workspace widget listing requires a backend query that supports organization-scoped pagination (listWidgets currently requires pageId).'\n )}\n </CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"flex items-start gap-3 rounded-md bg-warning-bg/10 p-3 text-sm text-warning-fg\">\n <AlertCircle className=\"mt-0.5 h-4 w-4 shrink-0\" />\n <div>\n <p className=\"font-medium\">{tr('bigconsole.admin.registry.widgets.gap.title', 'Backend gap')}</p>\n <p className=\"mt-1 text-text-secondary\">\n {tr(\n 'bigconsole.admin.registry.widgets.gap.body',\n 'Add adminBigConsoleWidgets(organizationId, first, after) to global or workspace schema, then wire it here.'\n )}\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Data-sink registry — backed by listDataSinks */}\n <Card className=\"bg-bg-surface border-border-default\">\n <CardHeader className=\"pb-3\">\n <CardTitle className=\"text-base font-medium text-text-primary flex items-center gap-2\">\n <Database className=\"h-4 w-4 text-text-secondary\" />\n {tr('bigconsole.admin.registry.datasinks', 'Data-sink Registry')}\n </CardTitle>\n <CardDescription className=\"text-sm text-text-secondary\">\n {tr('bigconsole.admin.registry.datasinks.description', 'Live view of data sinks across the organization.')}\n </CardDescription>\n </CardHeader>\n <CardContent>\n {dataSinksError ? (\n <div className=\"rounded-md bg-danger-bg/10 p-4 text-sm text-danger-fg\">{dataSinksError.message}</div>\n ) : dataSinksLoading && !dataSinksData ? (\n <div className=\"flex items-center justify-center py-12\">\n <Spinner className=\"h-8 w-8\" />\n </div>\n ) : dataSinks.length === 0 ? (\n <div className=\"text-center py-12 text-text-secondary text-sm\">\n {tr('bigconsole.admin.registry.datasinks.empty', 'No data sinks found.')}\n </div>\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-default text-left text-text-secondary\">\n <th className=\"pb-3 pr-4 font-medium\">{tr('bigconsole.admin.registry.datasinks.name', 'Name')}</th>\n <th className=\"pb-3 pr-4 font-medium\">\n {tr('bigconsole.admin.registry.datasinks.status', 'Status')}\n </th>\n <th className=\"pb-3 pr-4 font-medium\">{tr('bigconsole.admin.registry.datasinks.size', 'Size')}</th>\n <th className=\"pb-3 pr-4 font-medium\">\n {tr('bigconsole.admin.registry.datasinks.links', 'Links')}\n </th>\n <th className=\"pb-3 pr-4 font-medium\">\n {tr('bigconsole.admin.registry.datasinks.refreshed', 'Refreshed')}\n </th>\n <th className=\"pb-3 font-medium\">{tr('bigconsole.admin.registry.datasinks.active', 'Active')}</th>\n </tr>\n </thead>\n <tbody className=\"text-text-primary\">\n {dataSinks.map((sink) => (\n <tr key={sink.id} className=\"border-b border-border-seam last:border-0\">\n <td className=\"py-3 pr-4\">\n <div className=\"font-medium\">{sink.name || sink.key}</div>\n <div className=\"text-xs text-text-tertiary\">{sink.key}</div>\n </td>\n <td className=\"py-3 pr-4\">\n <Badge variant={sink.status === 'ready' ? 'default' : 'secondary'}>{sink.status}</Badge>\n </td>\n <td className=\"py-3 pr-4 tabular-nums\">{formatBytes(sink.sizeBytes)}</td>\n <td className=\"py-3 pr-4 tabular-nums\">{sink.linkedWorkflowCount ?? 0}</td>\n <td className=\"py-3 pr-4 text-text-secondary\">{formatDate(sink.lastRefreshedAt)}</td>\n <td className=\"py-3\">\n <Badge variant={sink.isActive ? 'default' : 'outline'}>{sink.isActive ? 'Yes' : 'No'}</Badge>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n );\n});\n\nconst FeatureFlagsSection: FC = memo(function FeatureFlagsSection() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n\n const [flags, setFlags] = useState<FeatureFlag[]>(PLACEHOLDER_FEATURE_FLAGS);\n\n const toggleFlag = useCallback((id: string) => {\n setFlags((prev) => prev.map((flag) => (flag.id === id ? { ...flag, enabled: !flag.enabled } : flag)));\n }, []);\n\n return (\n <div className=\"space-y-6\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('bigconsole.admin.featureFlags.title', 'Org-level Feature Flags')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'bigconsole.admin.featureFlags.subtitle',\n 'Manage BigConsole features across organizations and workspaces.'\n )}\n </p>\n </div>\n\n <div className=\"flex items-start gap-3 rounded-md bg-info-bg/10 p-3 text-sm text-info-fg\">\n <AlertCircle className=\"mt-0.5 h-4 w-4 shrink-0\" />\n <div>\n <p className=\"font-medium\">{tr('bigconsole.admin.featureFlags.placeholder.title', 'Placeholder data')}</p>\n <p className=\"mt-1 text-text-secondary\">\n {tr(\n 'bigconsole.admin.featureFlags.placeholder.body',\n 'Wire this to the feature-flag service admin API (list/upsert organization feature flags) when available.'\n )}\n </p>\n </div>\n </div>\n\n <div className=\"grid gap-4\">\n {flags.map((flag) => (\n <Card key={flag.id} className=\"bg-bg-surface border-border-default\">\n <CardContent className=\"p-4\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <Flag className=\"h-4 w-4 text-text-secondary\" />\n <span className=\"font-medium text-text-primary\">{flag.name}</span>\n <Badge variant=\"outline\">{flag.scope}</Badge>\n </div>\n <p className=\"text-sm text-text-secondary\">{flag.description}</p>\n <code className=\"text-xs text-text-tertiary bg-bg-sunken px-1.5 py-0.5 rounded\">{flag.key}</code>\n </div>\n <Button\n variant={flag.enabled ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => toggleFlag(flag.id)}\n className=\"shrink-0\"\n >\n {flag.enabled\n ? tr('bigconsole.admin.featureFlags.enabled', 'Enabled')\n : tr('bigconsole.admin.featureFlags.disabled', 'Disabled')}\n </Button>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n </div>\n );\n});\n\nconst AuditLogSection: FC = memo(function AuditLogSection() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n\n const [statusFilter, setStatusFilter] = useState<string>('all');\n\n const {\n data: logsData,\n loading: logsLoading,\n error: logsError,\n refetch: refetchLogs,\n } = useQuery<ListEventLogsQuery>(ListEventLogsDocument, {\n variables: {\n first: 50,\n status: statusFilter === 'all' ? undefined : (statusFilter as 'success' | 'failed'),\n },\n notifyOnNetworkStatusChange: true,\n });\n\n const logs = useMemo<EventLogNode[]>(() => {\n return (logsData?.listEventLogs?.edges ?? []).map((edge) => edge.node as unknown as EventLogNode);\n }, [logsData]);\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('bigconsole.admin.auditLog.title', 'Audit Log Viewer')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'bigconsole.admin.auditLog.subtitle',\n 'Widget event delivery log. Organization-level audit trail requires a dedicated admin audit query.'\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <select\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n className=\"h-9 rounded-md border border-border-default bg-bg-surface px-3 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-focus-ring\"\n >\n <option value=\"all\">{tr('bigconsole.admin.auditLog.filter.all', 'All statuses')}</option>\n <option value=\"success\">{tr('bigconsole.admin.auditLog.filter.success', 'Success')}</option>\n <option value=\"failed\">{tr('bigconsole.admin.auditLog.filter.failed', 'Failed')}</option>\n </select>\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => refetchLogs()}\n disabled={logsLoading}\n aria-label={tr('bigconsole.admin.auditLog.refresh', 'Refresh')}\n >\n <RefreshCw className={`h-4 w-4 ${logsLoading ? 'animate-spin' : ''}`} />\n </Button>\n </div>\n </div>\n\n <Card className=\"bg-bg-surface border-border-default\">\n <CardContent className=\"p-0\">\n {logsError ? (\n <div className=\"rounded-md bg-danger-bg/10 p-4 text-sm text-danger-fg\">{logsError.message}</div>\n ) : logsLoading && !logsData ? (\n <div className=\"flex items-center justify-center py-12\">\n <Spinner className=\"h-8 w-8\" />\n </div>\n ) : logs.length === 0 ? (\n <div className=\"text-center py-12 text-text-secondary text-sm\">\n {tr('bigconsole.admin.auditLog.empty', 'No audit events found.')}\n </div>\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-default text-left text-text-secondary\">\n <th className=\"pb-3 pr-4 font-medium p-4\">{tr('bigconsole.admin.auditLog.event', 'Event')}</th>\n <th className=\"pb-3 pr-4 font-medium\">{tr('bigconsole.admin.auditLog.action', 'Action')}</th>\n <th className=\"pb-3 pr-4 font-medium\">{tr('bigconsole.admin.auditLog.status', 'Status')}</th>\n <th className=\"pb-3 pr-4 font-medium\">{tr('bigconsole.admin.auditLog.widget', 'Widget')}</th>\n <th className=\"pb-3 pr-4 font-medium\">{tr('bigconsole.admin.auditLog.dashboard', 'Dashboard')}</th>\n <th className=\"pb-3 font-medium\">{tr('bigconsole.admin.auditLog.time', 'Time')}</th>\n </tr>\n </thead>\n <tbody className=\"text-text-primary\">\n {logs.map((log) => (\n <tr key={log.id} className=\"border-b border-border-seam last:border-0\">\n <td className=\"py-3 pr-4 p-4\">\n <div className=\"font-medium\">{log.eventName}</div>\n {log.errorMessage && (\n <div className=\"text-xs text-danger-fg mt-1 max-w-xs truncate\">{log.errorMessage}</div>\n )}\n </td>\n <td className=\"py-3 pr-4\">{log.actionName || '—'}</td>\n <td className=\"py-3 pr-4\">\n <Badge\n variant={\n log.status === 'success' ? 'default' : log.status === 'failed' ? 'destructive' : 'secondary'\n }\n >\n {log.status}\n </Badge>\n </td>\n <td className=\"py-3 pr-4 font-mono text-xs text-text-tertiary\">{log.widgetId || '—'}</td>\n <td className=\"py-3 pr-4 font-mono text-xs text-text-tertiary\">{log.dashboardId || '—'}</td>\n <td className=\"py-3 text-text-secondary\">{formatDate(log.createdAt)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n );\n});\n\n// ============================================================================\n// PAGE\n// ============================================================================\n\nexport const AdminPage: FC = memo(function AdminPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n },\n [t]\n );\n\n const [activeTab, setActiveTab] = useState<AdminTab>('registry');\n\n return (\n <div className=\"min-h-screen bg-bg-base p-6\">\n <div className=\"mx-auto max-w-7xl space-y-6\">\n <div className=\"space-y-1\">\n <h1 className=\"text-2xl font-bold text-text-primary\">{tr('bigconsole.admin.title', 'BigConsole Admin')}</h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('bigconsole.admin.description', 'Product-specific administration and governance for BigConsole.')}\n </p>\n </div>\n\n <Tabs value={activeTab} onValueChange={(value) => setActiveTab(value as AdminTab)}>\n <TabsList className=\"bg-bg-surface border border-border-default\">\n <TabsTrigger value=\"registry\" className=\"flex items-center gap-2\">\n <LayoutDashboard className=\"h-4 w-4\" />\n {tr('bigconsole.admin.tab.registry', 'Registry')}\n </TabsTrigger>\n <TabsTrigger value=\"feature-flags\" className=\"flex items-center gap-2\">\n <Flag className=\"h-4 w-4\" />\n {tr('bigconsole.admin.tab.featureFlags', 'Feature Flags')}\n </TabsTrigger>\n <TabsTrigger value=\"audit-log\" className=\"flex items-center gap-2\">\n <ScrollText className=\"h-4 w-4\" />\n {tr('bigconsole.admin.tab.auditLog', 'Audit Log')}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"registry\" className=\"mt-6\">\n <RegistrySection />\n </TabsContent>\n <TabsContent value=\"feature-flags\" className=\"mt-6\">\n <FeatureFlagsSection />\n </TabsContent>\n <TabsContent value=\"audit-log\" className=\"mt-6\">\n <AuditLogSection />\n </TabsContent>\n </Tabs>\n </div>\n </div>\n );\n});\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -3,7 +3,8 @@ import { useBigConsole as t } from "./bigconsole/context/BigConsoleContext.js";
3
3
  import n from "./bigconsole/components/DrilldownModal.js";
4
4
  import r from "./bigconsole/hooks/useDrilldown.js";
5
5
  import { BigConsoleRoot as i } from "./bigconsole/BigConsoleRoot.js";
6
- import { gatherPageContext as a, resolveBigConsoleConversationContext as o } from "./bigconsole/assistant/pageContext.js";
7
- import { useSandboxAssistantTransport as s } from "./bigconsole/assistant/createSandboxAssistantTransport.js";
6
+ import { AdminRoot as a } from "./bigconsole/AdminRoot.js";
7
+ import { gatherPageContext as o, resolveBigConsoleConversationContext as s } from "./bigconsole/assistant/pageContext.js";
8
+ import { useSandboxAssistantTransport as c } from "./bigconsole/assistant/createSandboxAssistantTransport.js";
8
9
  import "./bigconsole/index.js";
9
- export { i as BigConsoleRoot, n as DrilldownModal, a as gatherPageContext, o as resolveBigConsoleConversationContext, t as useBigConsole, r as useDrilldown, e as useDrilldownStore, s as useSandboxAssistantTransport };
10
+ export { a as AdminRoot, i as BigConsoleRoot, n as DrilldownModal, o as gatherPageContext, s as resolveBigConsoleConversationContext, t as useBigConsole, r as useDrilldown, e as useDrilldownStore, c as useSandboxAssistantTransport };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-bigconsole",
3
- "version": "2026.714.3",
3
+ "version": "2026.715.1",
4
4
  "description": "BigConsole - AI-powered analytics and dashboard platform",
5
5
  "type": "module",
6
6
  "files": [