@burdenoff/microfe-bigconsole 2026.615.3 → 2026.616.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/pages/DashboardViewPage.js +186 -168
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +193 -116
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/InsightsPage.js +227 -192
- package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -10,93 +10,97 @@ import o from "../hooks/usePageOperations.js";
|
|
|
10
10
|
import "../hooks/index.js";
|
|
11
11
|
import s from "../components/dashboard/DashboardCanvas.js";
|
|
12
12
|
import c from "../components/dashboard/ExportDashboardDialog.js";
|
|
13
|
-
import
|
|
13
|
+
import ee from "../components/dashboard/DrilldownBreadcrumb.js";
|
|
14
14
|
import "../components/dashboard/index.js";
|
|
15
|
-
import { memo as
|
|
16
|
-
import { useParams as
|
|
17
|
-
import {
|
|
15
|
+
import { memo as l, useCallback as u, useEffect as d, useState as f } from "react";
|
|
16
|
+
import { useParams as p } from "react-router-dom";
|
|
17
|
+
import { ArrowLeft as m } from "lucide-react";
|
|
18
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
19
|
+
import { Button as _ } from "@burdenoff/fe-libs/ui";
|
|
18
20
|
//#region src/bigconsole/pages/DashboardViewPage.tsx
|
|
19
|
-
var
|
|
20
|
-
let { dashboardId:
|
|
21
|
-
|
|
22
|
-
if (!
|
|
23
|
-
|
|
21
|
+
var v = l(function() {
|
|
22
|
+
let { dashboardId: l } = p(), v = n(), [y, b] = f(null), [x, S] = f(!1), [C, w] = f(!1), [T, E] = f(!1), { isInDrilldown: D, getContextFromUrl: O, getDrilldownMetadata: k } = a(), A = t((e) => e.currentDashboard), j = t((e) => e.setCurrentDashboard), M = t((e) => e.setGlobalFilterValue), N = t((e) => e.clearAllGlobalFilters), P = e((e) => e.widgets), F = e((e) => e.clipboardWidgetId), I = e((e) => e.clipboardAction), L = e((e) => e.clearClipboard), R = e((e) => e.selectWidget), { loading: z, error: B, fetchDashboard: V, updateDashboard: H } = i(), { createWidget: U, deleteWidget: W, duplicateWidget: G, batchUpdatePositions: K } = r(y || void 0, l), { createPage: q, updatePage: J, deletePage: Y, reorderPages: X } = o();
|
|
23
|
+
d(() => {
|
|
24
|
+
if (!l) {
|
|
25
|
+
v("/dashboards");
|
|
24
26
|
return;
|
|
25
27
|
}
|
|
26
|
-
return
|
|
27
|
-
e ? e.pages && e.pages.length > 0 &&
|
|
28
|
+
return N(), w(!1), V(l).then((e) => {
|
|
29
|
+
e ? e.pages && e.pages.length > 0 && b(e.pages[0].id) : w(!0);
|
|
28
30
|
}), () => {
|
|
29
|
-
|
|
31
|
+
j(null), N(), w(!1);
|
|
30
32
|
};
|
|
31
33
|
}, [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
]),
|
|
38
|
-
if (!
|
|
39
|
-
let e =
|
|
34
|
+
l,
|
|
35
|
+
v,
|
|
36
|
+
V,
|
|
37
|
+
j,
|
|
38
|
+
N
|
|
39
|
+
]), d(() => {
|
|
40
|
+
if (!D) return;
|
|
41
|
+
let e = O(), t = k();
|
|
40
42
|
Object.entries(e).forEach(([e, t]) => {
|
|
41
|
-
|
|
42
|
-
}), t.depth > 0 && (
|
|
43
|
+
M(`ctx_${e}`, t);
|
|
44
|
+
}), t.depth > 0 && (M("__drilldown_depth", t.depth), M("__drilldown_parent", t.parent), M("__drilldown_path", t.path)), Object.keys(e).length > 0 && console.debug("[DashboardViewPage] Loaded drilldown context:", {
|
|
43
45
|
context: e,
|
|
44
46
|
metadata: t
|
|
45
47
|
});
|
|
46
48
|
}, [
|
|
47
|
-
T,
|
|
48
|
-
E,
|
|
49
49
|
D,
|
|
50
|
-
|
|
50
|
+
O,
|
|
51
|
+
k,
|
|
52
|
+
M
|
|
51
53
|
]);
|
|
52
|
-
let
|
|
53
|
-
if (
|
|
54
|
-
|
|
54
|
+
let Z = u(async () => {
|
|
55
|
+
if (l) {
|
|
56
|
+
S(!0);
|
|
55
57
|
try {
|
|
56
|
-
let e = Array.from(
|
|
57
|
-
e.length > 0 && await
|
|
58
|
+
let e = Array.from(P.values());
|
|
59
|
+
e.length > 0 && await K(e.map((e) => ({
|
|
58
60
|
id: e.id,
|
|
59
61
|
position: e.position
|
|
60
|
-
}))),
|
|
61
|
-
id:
|
|
62
|
-
name:
|
|
63
|
-
description:
|
|
62
|
+
}))), A && await H({
|
|
63
|
+
id: l,
|
|
64
|
+
name: A.name,
|
|
65
|
+
description: A.description
|
|
64
66
|
});
|
|
65
67
|
} catch {} finally {
|
|
66
|
-
|
|
68
|
+
S(!1);
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
}, [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
]),
|
|
76
|
-
v(
|
|
77
|
-
}, []),
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
let
|
|
72
|
+
l,
|
|
73
|
+
P,
|
|
74
|
+
A,
|
|
75
|
+
K,
|
|
76
|
+
H
|
|
77
|
+
]), Q = u(() => {
|
|
78
|
+
v("/dashboards");
|
|
79
|
+
}, [v]), $ = u((e) => {
|
|
80
|
+
b(e);
|
|
81
|
+
}, []), te = u(async () => {
|
|
82
|
+
let e = y || A?.pages?.[0]?.id;
|
|
83
|
+
if (!F || !l || !e) return;
|
|
84
|
+
let t = P.get(F);
|
|
81
85
|
if (!t) {
|
|
82
|
-
|
|
86
|
+
L();
|
|
83
87
|
return;
|
|
84
88
|
}
|
|
85
89
|
let n = {
|
|
86
90
|
x: 0,
|
|
87
|
-
y: Array.from(
|
|
91
|
+
y: Array.from(P.values()).reduce((e, t) => {
|
|
88
92
|
let n = (t.positionY ?? 0) + (t.positionHeight ?? 4);
|
|
89
93
|
return Math.max(e, n);
|
|
90
94
|
}, 0),
|
|
91
95
|
width: t.positionWidth ?? 4,
|
|
92
96
|
height: t.positionHeight ?? 4
|
|
93
|
-
}, r =
|
|
97
|
+
}, r = I === "copy" ? `${t.title} (Copy)` : t.title, i = null;
|
|
94
98
|
try {
|
|
95
|
-
i = await
|
|
99
|
+
i = await G(F, n, r);
|
|
96
100
|
} catch {}
|
|
97
|
-
i ||= await
|
|
101
|
+
i ||= await U({
|
|
98
102
|
pageId: e,
|
|
99
|
-
dashboardId:
|
|
103
|
+
dashboardId: l,
|
|
100
104
|
type: t.type,
|
|
101
105
|
title: r,
|
|
102
106
|
description: t.description,
|
|
@@ -108,220 +112,234 @@ var g = u(function() {
|
|
|
108
112
|
position: n,
|
|
109
113
|
refreshInterval: t.refreshInterval ?? void 0,
|
|
110
114
|
metadata: t.metadata
|
|
111
|
-
}), i && (
|
|
115
|
+
}), i && (R(i.id), I === "cut" && await W(F)), L();
|
|
112
116
|
}, [
|
|
113
|
-
|
|
117
|
+
F,
|
|
118
|
+
I,
|
|
119
|
+
l,
|
|
120
|
+
y,
|
|
121
|
+
A,
|
|
114
122
|
P,
|
|
115
|
-
|
|
116
|
-
_,
|
|
117
|
-
O,
|
|
118
|
-
M,
|
|
123
|
+
G,
|
|
119
124
|
U,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
name: `Page ${(O.pages?.length || 0) + 1}`
|
|
125
|
+
W,
|
|
126
|
+
R,
|
|
127
|
+
L
|
|
128
|
+
]), ne = u(() => {
|
|
129
|
+
E(!0);
|
|
130
|
+
}, []), re = u(() => {
|
|
131
|
+
E(!1);
|
|
132
|
+
}, []), ie = u(async () => {
|
|
133
|
+
if (!A || !l) return;
|
|
134
|
+
let e = await q({
|
|
135
|
+
dashboardId: l,
|
|
136
|
+
name: `Page ${(A.pages?.length || 0) + 1}`
|
|
133
137
|
});
|
|
134
|
-
e && (
|
|
135
|
-
...
|
|
136
|
-
pages: [...
|
|
137
|
-
}),
|
|
138
|
+
e && (j({
|
|
139
|
+
...A,
|
|
140
|
+
pages: [...A.pages || [], e]
|
|
141
|
+
}), b(e.id));
|
|
138
142
|
}, [
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
]),
|
|
144
|
-
|
|
143
|
+
A,
|
|
144
|
+
l,
|
|
145
|
+
j,
|
|
146
|
+
q
|
|
147
|
+
]), ae = u(async (e, t) => {
|
|
148
|
+
A && await J({
|
|
145
149
|
id: e,
|
|
146
150
|
name: t
|
|
147
|
-
}) &&
|
|
148
|
-
...
|
|
149
|
-
pages:
|
|
151
|
+
}) && j({
|
|
152
|
+
...A,
|
|
153
|
+
pages: A.pages?.map((n) => n.id === e ? {
|
|
150
154
|
...n,
|
|
151
155
|
name: t
|
|
152
156
|
} : n)
|
|
153
157
|
});
|
|
154
158
|
}, [
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
]),
|
|
159
|
-
if (!
|
|
160
|
-
let t =
|
|
159
|
+
A,
|
|
160
|
+
j,
|
|
161
|
+
J
|
|
162
|
+
]), oe = u(async (e) => {
|
|
163
|
+
if (!A || !l) return;
|
|
164
|
+
let t = A.pages?.find((t) => t.id === e);
|
|
161
165
|
if (!t) return;
|
|
162
|
-
let n = await
|
|
163
|
-
dashboardId:
|
|
166
|
+
let n = await q({
|
|
167
|
+
dashboardId: l,
|
|
164
168
|
name: `${t.name} (Copy)`
|
|
165
169
|
});
|
|
166
|
-
n && (
|
|
167
|
-
...
|
|
168
|
-
pages: [...
|
|
169
|
-
}),
|
|
170
|
+
n && (j({
|
|
171
|
+
...A,
|
|
172
|
+
pages: [...A.pages || [], n]
|
|
173
|
+
}), b(n.id));
|
|
170
174
|
}, [
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
]),
|
|
176
|
-
if (!(!
|
|
177
|
-
let t =
|
|
175
|
+
A,
|
|
176
|
+
l,
|
|
177
|
+
j,
|
|
178
|
+
q
|
|
179
|
+
]), se = u(async (e) => {
|
|
180
|
+
if (!(!A || (A.pages?.length || 0) <= 1) && await Y(e)) {
|
|
181
|
+
let t = A.pages?.filter((t) => t.id !== e).map((e, t) => ({
|
|
178
182
|
...e,
|
|
179
183
|
order: t
|
|
180
184
|
}));
|
|
181
|
-
|
|
182
|
-
...
|
|
185
|
+
j({
|
|
186
|
+
...A,
|
|
183
187
|
pages: t
|
|
184
|
-
}),
|
|
188
|
+
}), y === e && b(t?.[0]?.id || null);
|
|
185
189
|
}
|
|
186
190
|
}, [
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
]),
|
|
192
|
-
!
|
|
193
|
-
...
|
|
191
|
+
A,
|
|
192
|
+
j,
|
|
193
|
+
y,
|
|
194
|
+
Y
|
|
195
|
+
]), ce = u(async (e) => {
|
|
196
|
+
!A || !l || (j({
|
|
197
|
+
...A,
|
|
194
198
|
pages: e.map((e, t) => ({
|
|
195
199
|
...e,
|
|
196
200
|
order: t
|
|
197
201
|
}))
|
|
198
|
-
}), await
|
|
202
|
+
}), await X(l, e.map((e) => e.id)));
|
|
199
203
|
}, [
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
+
A,
|
|
205
|
+
l,
|
|
206
|
+
j,
|
|
207
|
+
X
|
|
204
208
|
]);
|
|
205
|
-
return
|
|
209
|
+
return B ? /* @__PURE__ */ h("div", {
|
|
206
210
|
className: "flex flex-col items-center justify-center h-full",
|
|
207
211
|
style: { padding: "var(--space-pagePadding)" },
|
|
208
|
-
children: /* @__PURE__ */
|
|
212
|
+
children: /* @__PURE__ */ g("div", {
|
|
209
213
|
className: "bg-status-error-bg border border-status-error-border rounded-lg p-6 w-full max-w-md",
|
|
210
214
|
children: [
|
|
211
|
-
/* @__PURE__ */
|
|
215
|
+
/* @__PURE__ */ h("h3", {
|
|
212
216
|
className: "text-lg font-medium text-status-error-text mb-2",
|
|
213
217
|
children: "Error loading dashboard"
|
|
214
218
|
}),
|
|
215
|
-
/* @__PURE__ */
|
|
219
|
+
/* @__PURE__ */ h("p", {
|
|
216
220
|
className: "text-sm text-text-secondary mb-4 break-words",
|
|
217
|
-
children:
|
|
221
|
+
children: B.message
|
|
218
222
|
}),
|
|
219
|
-
/* @__PURE__ */
|
|
223
|
+
/* @__PURE__ */ g("div", {
|
|
220
224
|
className: "flex flex-wrap gap-3",
|
|
221
|
-
children: [/* @__PURE__ */
|
|
225
|
+
children: [/* @__PURE__ */ h("button", {
|
|
222
226
|
type: "button",
|
|
223
|
-
onClick: () =>
|
|
227
|
+
onClick: () => V(l),
|
|
224
228
|
className: "px-4 py-2 text-sm font-medium text-action-primary-fg bg-action-primary-bg hover:bg-action-primary-bgHover rounded-md",
|
|
225
229
|
children: "Retry"
|
|
226
|
-
}), /* @__PURE__ */
|
|
230
|
+
}), /* @__PURE__ */ h("button", {
|
|
227
231
|
type: "button",
|
|
228
|
-
onClick: () =>
|
|
232
|
+
onClick: () => v("/dashboards"),
|
|
229
233
|
className: "px-4 py-2 text-sm font-medium text-text-secondary bg-bg-muted hover:bg-bg-muted/80 rounded-md",
|
|
230
234
|
children: "Back to Dashboards"
|
|
231
235
|
})]
|
|
232
236
|
})
|
|
233
237
|
]
|
|
234
238
|
})
|
|
235
|
-
}) :
|
|
239
|
+
}) : C ? /* @__PURE__ */ h("div", {
|
|
236
240
|
className: "flex flex-col items-center justify-center h-full",
|
|
237
241
|
style: { padding: "var(--space-pagePadding)" },
|
|
238
|
-
children: /* @__PURE__ */
|
|
242
|
+
children: /* @__PURE__ */ g("div", {
|
|
239
243
|
className: "bg-bg-surface border border-border-default rounded-lg p-6 w-full max-w-md text-center",
|
|
240
244
|
children: [
|
|
241
|
-
/* @__PURE__ */
|
|
245
|
+
/* @__PURE__ */ h("svg", {
|
|
242
246
|
className: "w-16 h-16 mx-auto mb-4 text-text-tertiary",
|
|
243
247
|
fill: "none",
|
|
244
248
|
stroke: "currentColor",
|
|
245
249
|
viewBox: "0 0 24 24",
|
|
246
|
-
children: /* @__PURE__ */
|
|
250
|
+
children: /* @__PURE__ */ h("path", {
|
|
247
251
|
strokeLinecap: "round",
|
|
248
252
|
strokeLinejoin: "round",
|
|
249
253
|
strokeWidth: 1.5,
|
|
250
254
|
d: "M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
251
255
|
})
|
|
252
256
|
}),
|
|
253
|
-
/* @__PURE__ */
|
|
257
|
+
/* @__PURE__ */ h("h3", {
|
|
254
258
|
className: "text-lg font-medium text-text-primary mb-2",
|
|
255
259
|
children: "Dashboard not found"
|
|
256
260
|
}),
|
|
257
|
-
/* @__PURE__ */
|
|
261
|
+
/* @__PURE__ */ h("p", {
|
|
258
262
|
className: "text-sm text-text-secondary mb-4",
|
|
259
263
|
children: "The dashboard you're looking for doesn't exist or has been deleted."
|
|
260
264
|
}),
|
|
261
|
-
/* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ g("p", {
|
|
262
266
|
className: "text-xs text-text-tertiary mb-4 font-mono break-all",
|
|
263
|
-
children: ["ID: ",
|
|
267
|
+
children: ["ID: ", l]
|
|
264
268
|
}),
|
|
265
|
-
/* @__PURE__ */
|
|
269
|
+
/* @__PURE__ */ h("button", {
|
|
266
270
|
type: "button",
|
|
267
|
-
onClick: () =>
|
|
271
|
+
onClick: () => v("/dashboards"),
|
|
268
272
|
className: "px-4 py-2 text-sm font-medium text-action-primary-fg bg-action-primary-bg hover:bg-action-primary-bgHover rounded-md",
|
|
269
273
|
children: "Back to Dashboards"
|
|
270
274
|
})
|
|
271
275
|
]
|
|
272
276
|
})
|
|
273
|
-
}) :
|
|
277
|
+
}) : z || !A ? /* @__PURE__ */ h("div", {
|
|
274
278
|
className: "flex items-center justify-center h-full",
|
|
275
|
-
children: /* @__PURE__ */
|
|
279
|
+
children: /* @__PURE__ */ g("div", {
|
|
276
280
|
className: "flex flex-col items-center gap-4",
|
|
277
|
-
children: [/* @__PURE__ */
|
|
281
|
+
children: [/* @__PURE__ */ h("div", { className: "animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full" }), /* @__PURE__ */ h("p", {
|
|
278
282
|
className: "text-sm text-text-secondary",
|
|
279
283
|
children: "Loading dashboard..."
|
|
280
284
|
})]
|
|
281
285
|
})
|
|
282
|
-
}) : /* @__PURE__ */
|
|
286
|
+
}) : /* @__PURE__ */ g("div", {
|
|
283
287
|
className: "h-full flex flex-col overflow-hidden",
|
|
284
288
|
children: [
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
289
|
+
/* @__PURE__ */ g("div", {
|
|
290
|
+
className: "flex items-center gap-3 px-3 py-2 border-b border-border-default bg-bg-surface flex-shrink-0",
|
|
291
|
+
children: [/* @__PURE__ */ g(_, {
|
|
292
|
+
onClick: Q,
|
|
293
|
+
variant: "ghost",
|
|
294
|
+
size: "sm",
|
|
295
|
+
title: "Back to Dashboards",
|
|
296
|
+
"aria-label": "Back to Dashboards",
|
|
297
|
+
children: [/* @__PURE__ */ h(m, { className: "w-4 h-4 mr-2" }), "Back"]
|
|
298
|
+
}), /* @__PURE__ */ h("h2", {
|
|
299
|
+
className: "text-sm font-semibold text-text-primary truncate",
|
|
300
|
+
children: A.name
|
|
301
|
+
})]
|
|
302
|
+
}),
|
|
303
|
+
D && /* @__PURE__ */ h(ee, {
|
|
304
|
+
currentDashboardId: A.id,
|
|
305
|
+
currentDashboardName: A.name,
|
|
288
306
|
basePath: "/bigconsole/dashboards"
|
|
289
307
|
}),
|
|
290
|
-
/* @__PURE__ */
|
|
308
|
+
/* @__PURE__ */ h("div", {
|
|
291
309
|
className: "flex-1 min-w-0 overflow-hidden",
|
|
292
|
-
children: /* @__PURE__ */
|
|
293
|
-
dashboardId:
|
|
294
|
-
pageId:
|
|
295
|
-
title:
|
|
296
|
-
onPaste:
|
|
297
|
-
onSave:
|
|
298
|
-
isSaving:
|
|
299
|
-
onExport:
|
|
300
|
-
pages: (
|
|
310
|
+
children: /* @__PURE__ */ h(s, {
|
|
311
|
+
dashboardId: A.id,
|
|
312
|
+
pageId: y || A.pages?.[0]?.id,
|
|
313
|
+
title: A.name,
|
|
314
|
+
onPaste: te,
|
|
315
|
+
onSave: Z,
|
|
316
|
+
isSaving: x,
|
|
317
|
+
onExport: ne,
|
|
318
|
+
pages: (A.pages || []).map((e, t) => ({
|
|
301
319
|
id: e.id,
|
|
302
320
|
name: e.name || `Page ${t + 1}`,
|
|
303
321
|
order: e.order ?? 0
|
|
304
322
|
})),
|
|
305
|
-
activePageId:
|
|
306
|
-
onPageChange:
|
|
307
|
-
onPageAdd:
|
|
308
|
-
onPageRename:
|
|
309
|
-
onPageDuplicate:
|
|
310
|
-
onPageDelete:
|
|
311
|
-
onPagesReorder:
|
|
323
|
+
activePageId: y || A.pages?.[0]?.id,
|
|
324
|
+
onPageChange: $,
|
|
325
|
+
onPageAdd: ie,
|
|
326
|
+
onPageRename: ae,
|
|
327
|
+
onPageDuplicate: oe,
|
|
328
|
+
onPageDelete: se,
|
|
329
|
+
onPagesReorder: ce,
|
|
312
330
|
showPageTabs: !0
|
|
313
331
|
})
|
|
314
332
|
}),
|
|
315
|
-
/* @__PURE__ */
|
|
316
|
-
isOpen:
|
|
317
|
-
dashboardId:
|
|
318
|
-
dashboardName:
|
|
319
|
-
onClose:
|
|
333
|
+
/* @__PURE__ */ h(c, {
|
|
334
|
+
isOpen: T,
|
|
335
|
+
dashboardId: A.id,
|
|
336
|
+
dashboardName: A.name,
|
|
337
|
+
onClose: re
|
|
320
338
|
})
|
|
321
339
|
]
|
|
322
340
|
});
|
|
323
341
|
});
|
|
324
342
|
//#endregion
|
|
325
|
-
export {
|
|
343
|
+
export { v as DashboardViewPage, v as default };
|
|
326
344
|
|
|
327
345
|
//# sourceMappingURL=DashboardViewPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/DashboardViewPage.tsx"],"sourcesContent":["/**\n * DashboardViewPage\n *\n * View/edit a single dashboard - fetches data from real backend.\n * Supports drilldown context propagation via URL parameters.\n */\n\nimport { type FC, memo, useEffect, useCallback, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBigConsoleNavigate } from '../context';\nimport { DashboardCanvas, ExportDashboardDialog, DrilldownBreadcrumb } from '../components/dashboard';\nimport { useDashboardStore, useWidgetStore } from '../store';\nimport { useDashboardOperations, useWidgetOperations, usePageOperations } from '../hooks';\nimport { useFilterUrlSync } from '../hooks/useFilterUrlSync';\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardViewPage: FC = memo(function DashboardViewPage() {\n const { dashboardId } = useParams<{ dashboardId: string }>();\n const navigate = useBigConsoleNavigate();\n const [currentPageId, setCurrentPageId] = useState<string | null>(null);\n const [isSaving, setIsSaving] = useState(false);\n const [notFound, setNotFound] = useState(false);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n\n // Drilldown context from URL\n const { isInDrilldown, getContextFromUrl, getDrilldownMetadata } = useFilterUrlSync();\n\n // Store actions\n const currentDashboard = useDashboardStore((state) => state.currentDashboard);\n const setCurrentDashboard = useDashboardStore((state) => state.setCurrentDashboard);\n const setGlobalFilterValue = useDashboardStore((state) => state.setGlobalFilterValue);\n const clearAllGlobalFilters = useDashboardStore((state) => state.clearAllGlobalFilters);\n const widgets = useWidgetStore((state) => state.widgets);\n const clipboardWidgetId = useWidgetStore((state) => state.clipboardWidgetId);\n const clipboardAction = useWidgetStore((state) => state.clipboardAction);\n const clearClipboard = useWidgetStore((state) => state.clearClipboard);\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n\n // Dashboard operations hook\n const { loading, error, fetchDashboard, updateDashboard } = useDashboardOperations();\n\n // Widget operations hook\n const {\n createWidget: createWidgetMutation,\n deleteWidget: deleteWidgetMutation,\n duplicateWidget: duplicateWidgetMutation,\n batchUpdatePositions,\n } = useWidgetOperations(currentPageId || undefined, dashboardId);\n\n // Page operations hook\n const {\n createPage: createPageMutation,\n updatePage: updatePageMutation,\n deletePage: deletePageMutation,\n reorderPages: reorderPagesMutation,\n } = usePageOperations();\n\n // Load dashboard data\n useEffect(() => {\n if (!dashboardId) {\n navigate('/dashboards');\n return;\n }\n\n // Clear stale global filters from previous dashboard before loading new one.\n // This prevents drilldown context filters (ctx_*) from the source dashboard\n // from persisting and incorrectly filtering data on the target dashboard.\n clearAllGlobalFilters();\n\n setNotFound(false);\n\n // Fetch dashboard from backend\n fetchDashboard(dashboardId).then((dashboard) => {\n if (dashboard) {\n // Set the first page as current page\n if (dashboard.pages && dashboard.pages.length > 0) {\n setCurrentPageId(dashboard.pages[0].id);\n }\n } else {\n // Dashboard not found\n setNotFound(true);\n }\n });\n\n // Cleanup\n return () => {\n setCurrentDashboard(null);\n clearAllGlobalFilters();\n setNotFound(false);\n };\n }, [dashboardId, navigate, fetchDashboard, setCurrentDashboard, clearAllGlobalFilters]);\n\n // Load drilldown context from URL and apply to global filters\n useEffect(() => {\n if (!isInDrilldown) return;\n\n // Get context params from URL (ctx_* params)\n const contextParams = getContextFromUrl();\n const drilldownMeta = getDrilldownMetadata();\n\n // Apply context to dashboard state (available for widgets to use)\n // Store as special filter values that widgets can reference\n Object.entries(contextParams).forEach(([key, value]) => {\n // Store context params as global filter values with ctx_ prefix for clarity\n setGlobalFilterValue(`ctx_${key}`, value);\n });\n\n // Store drilldown metadata as special values\n if (drilldownMeta.depth > 0) {\n setGlobalFilterValue('__drilldown_depth', drilldownMeta.depth);\n setGlobalFilterValue('__drilldown_parent', drilldownMeta.parent);\n setGlobalFilterValue('__drilldown_path', drilldownMeta.path);\n }\n\n // Log for debugging\n if (Object.keys(contextParams).length > 0) {\n console.debug('[DashboardViewPage] Loaded drilldown context:', {\n context: contextParams,\n metadata: drilldownMeta,\n });\n }\n }, [isInDrilldown, getContextFromUrl, getDrilldownMetadata, setGlobalFilterValue]);\n\n // Handle save - batch update widget positions and dashboard metadata\n const handleSave = useCallback(async () => {\n if (!dashboardId) return;\n\n setIsSaving(true);\n try {\n // Get all widgets and their positions\n const widgetList = Array.from(widgets.values());\n if (widgetList.length > 0) {\n const positionUpdates = widgetList.map((widget) => ({\n id: widget.id,\n position: widget.position,\n }));\n\n await batchUpdatePositions(positionUpdates);\n }\n\n // Update dashboard metadata if needed\n if (currentDashboard) {\n await updateDashboard({\n id: dashboardId,\n name: currentDashboard.name,\n description: currentDashboard.description,\n });\n }\n } catch (error) {\n } finally {\n setIsSaving(false);\n }\n }, [dashboardId, widgets, currentDashboard, batchUpdatePositions, updateDashboard]);\n\n // Handle page change\n const handlePageChange = useCallback((pageId: string) => {\n setCurrentPageId(pageId);\n }, []);\n\n // Handle paste widget from clipboard\n const handlePaste = useCallback(async () => {\n const pageId = currentPageId || currentDashboard?.pages?.[0]?.id;\n if (!clipboardWidgetId || !dashboardId || !pageId) {\n return;\n }\n\n const sourceWidget = widgets.get(clipboardWidgetId);\n if (!sourceWidget) {\n clearClipboard();\n return;\n }\n\n // Calculate new position (place at bottom of existing widgets)\n const widgetsArray = Array.from(widgets.values());\n const maxY = widgetsArray.reduce((max, w) => {\n const widgetBottom = (w.positionY ?? 0) + (w.positionHeight ?? 4);\n return Math.max(max, widgetBottom);\n }, 0);\n\n const newPosition = {\n x: 0,\n y: maxY,\n width: sourceWidget.positionWidth ?? 4,\n height: sourceWidget.positionHeight ?? 4,\n };\n\n const newTitle = clipboardAction === 'copy' ? `${sourceWidget.title} (Copy)` : sourceWidget.title;\n\n let newWidget = null;\n\n // Try duplicateWidget API first\n try {\n newWidget = await duplicateWidgetMutation(clipboardWidgetId, newPosition, newTitle);\n } catch (err) {}\n\n // Fallback: Create new widget with source widget's data\n if (!newWidget) {\n newWidget = await createWidgetMutation({\n pageId,\n dashboardId,\n type: sourceWidget.type,\n title: newTitle,\n description: sourceWidget.description,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: (sourceWidget as unknown as Record<string, string | undefined>).dataSinkId,\n datasetId: (sourceWidget as unknown as Record<string, string | undefined>).datasetId,\n parserId: (sourceWidget as unknown as Record<string, string | undefined>).parserId,\n parserRules: (sourceWidget as unknown as Record<string, string | undefined>).parserRules,\n config: sourceWidget.config || {},\n position: newPosition,\n refreshInterval: sourceWidget.refreshInterval ?? undefined,\n metadata: sourceWidget.metadata,\n });\n }\n\n if (newWidget) {\n selectWidget(newWidget.id);\n\n // If cut action, delete the original widget from backend\n if (clipboardAction === 'cut') {\n await deleteWidgetMutation(clipboardWidgetId);\n }\n }\n\n clearClipboard();\n }, [\n clipboardWidgetId,\n clipboardAction,\n dashboardId,\n currentPageId,\n currentDashboard,\n widgets,\n duplicateWidgetMutation,\n createWidgetMutation,\n deleteWidgetMutation,\n selectWidget,\n clearClipboard,\n ]);\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // ============================================================================\n // Page Management Callbacks\n // ============================================================================\n\n // Handle adding a new page\n const handlePageAdd = useCallback(async () => {\n if (!currentDashboard || !dashboardId) return;\n\n const newPageCount = (currentDashboard.pages?.length || 0) + 1;\n const newPageName = `Page ${newPageCount}`;\n\n // Call GraphQL mutation to create page\n // Note: 'order' is not available for create - backend auto-assigns order\n const newPage = await createPageMutation({\n dashboardId,\n name: newPageName,\n });\n\n if (newPage) {\n // Update local state with the created page\n setCurrentDashboard({\n ...currentDashboard,\n pages: [...(currentDashboard.pages || []), newPage],\n });\n\n // Switch to the new page\n setCurrentPageId(newPage.id);\n }\n }, [currentDashboard, dashboardId, setCurrentDashboard, createPageMutation]);\n\n // Handle renaming a page\n const handlePageRename = useCallback(\n async (pageId: string, newName: string) => {\n if (!currentDashboard) return;\n\n // Call GraphQL mutation to update page\n const updatedPage = await updatePageMutation({\n id: pageId,\n name: newName,\n });\n\n if (updatedPage) {\n setCurrentDashboard({\n ...currentDashboard,\n pages: currentDashboard.pages?.map((page) => (page.id === pageId ? { ...page, name: newName } : page)),\n });\n }\n },\n [currentDashboard, setCurrentDashboard, updatePageMutation]\n );\n\n // Handle duplicating a page\n const handlePageDuplicate = useCallback(\n async (pageId: string) => {\n if (!currentDashboard || !dashboardId) return;\n\n const sourcePage = currentDashboard.pages?.find((p) => p.id === pageId);\n if (!sourcePage) return;\n\n // Call GraphQL mutation to create a copy of the page\n // Note: 'order' is not available for create - backend auto-assigns order\n const newPage = await createPageMutation({\n dashboardId,\n name: `${sourcePage.name} (Copy)`,\n });\n\n if (newPage) {\n setCurrentDashboard({\n ...currentDashboard,\n pages: [...(currentDashboard.pages || []), newPage],\n });\n\n setCurrentPageId(newPage.id);\n }\n // Note: Widget duplication would require additional backend support\n },\n [currentDashboard, dashboardId, setCurrentDashboard, createPageMutation]\n );\n\n // Handle deleting a page\n const handlePageDelete = useCallback(\n async (pageId: string) => {\n if (!currentDashboard || (currentDashboard.pages?.length || 0) <= 1) return;\n\n // Call GraphQL mutation to delete page\n const success = await deletePageMutation(pageId);\n\n if (success) {\n const updatedPages = currentDashboard.pages\n ?.filter((p) => p.id !== pageId)\n .map((p, index) => ({ ...p, order: index }));\n\n setCurrentDashboard({\n ...currentDashboard,\n pages: updatedPages,\n });\n\n // Switch to first page if current page was deleted\n if (currentPageId === pageId) {\n setCurrentPageId(updatedPages?.[0]?.id || null);\n }\n }\n },\n [currentDashboard, setCurrentDashboard, currentPageId, deletePageMutation]\n );\n\n // Handle reordering pages\n const handlePagesReorder = useCallback(\n async (reorderedPages: Array<{ id: string; name?: string; order?: number }>) => {\n if (!currentDashboard || !dashboardId) return;\n\n // Update local state optimistically\n setCurrentDashboard({\n ...currentDashboard,\n pages: reorderedPages.map((page, index) => ({\n ...page,\n order: index,\n })),\n });\n\n // Call GraphQL mutation to persist the reorder\n const pageIds = reorderedPages.map((p) => p.id);\n await reorderPagesMutation(dashboardId, pageIds);\n },\n [currentDashboard, dashboardId, setCurrentDashboard, reorderPagesMutation]\n );\n\n // Error state\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full\" style={{ padding: 'var(--space-pagePadding)' }}>\n <div className=\"bg-status-error-bg border border-status-error-border rounded-lg p-6 w-full max-w-md\">\n <h3 className=\"text-lg font-medium text-status-error-text mb-2\">Error loading dashboard</h3>\n <p className=\"text-sm text-text-secondary mb-4 break-words\">{error.message}</p>\n <div className=\"flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={() => fetchDashboard(dashboardId!)}\n className=\"px-4 py-2 text-sm font-medium text-action-primary-fg bg-action-primary-bg hover:bg-action-primary-bgHover rounded-md\"\n >\n Retry\n </button>\n <button\n type=\"button\"\n onClick={() => navigate('/dashboards')}\n className=\"px-4 py-2 text-sm font-medium text-text-secondary bg-bg-muted hover:bg-bg-muted/80 rounded-md\"\n >\n Back to Dashboards\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n // Not found state\n if (notFound) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full\" style={{ padding: 'var(--space-pagePadding)' }}>\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-6 w-full max-w-md text-center\">\n <svg\n className=\"w-16 h-16 mx-auto mb-4 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <h3 className=\"text-lg font-medium text-text-primary mb-2\">Dashboard not found</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n The dashboard you're looking for doesn't exist or has been deleted.\n </p>\n <p className=\"text-xs text-text-tertiary mb-4 font-mono break-all\">ID: {dashboardId}</p>\n <button\n type=\"button\"\n onClick={() => navigate('/dashboards')}\n className=\"px-4 py-2 text-sm font-medium text-action-primary-fg bg-action-primary-bg hover:bg-action-primary-bgHover rounded-md\"\n >\n Back to Dashboards\n </button>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (loading || !currentDashboard) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"flex flex-col items-center gap-4\">\n <div className=\"animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full\" />\n <p className=\"text-sm text-text-secondary\">Loading dashboard...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"h-full flex flex-col overflow-hidden\">\n {/* Drilldown Breadcrumb - shown when navigating via drilldown */}\n {isInDrilldown && (\n <DrilldownBreadcrumb\n currentDashboardId={currentDashboard.id}\n currentDashboardName={currentDashboard.name}\n basePath=\"/bigconsole/dashboards\"\n />\n )}\n\n {/* Dashboard Canvas with Page Tabs */}\n <div className=\"flex-1 min-w-0 overflow-hidden\">\n <DashboardCanvas\n dashboardId={currentDashboard.id}\n pageId={currentPageId || currentDashboard.pages?.[0]?.id}\n title={currentDashboard.name}\n onPaste={handlePaste}\n onSave={handleSave}\n isSaving={isSaving}\n onExport={handleExport}\n // Page tabs props\n pages={(currentDashboard.pages || []).map((page, index) => ({\n id: page.id,\n name: page.name || `Page ${index + 1}`,\n order: page.order ?? 0,\n }))}\n activePageId={currentPageId || currentDashboard.pages?.[0]?.id}\n onPageChange={handlePageChange}\n onPageAdd={handlePageAdd}\n onPageRename={handlePageRename}\n onPageDuplicate={handlePageDuplicate}\n onPageDelete={handlePageDelete}\n onPagesReorder={handlePagesReorder}\n showPageTabs={true}\n />\n </div>\n\n {/* Export Dashboard Dialog */}\n <ExportDashboardDialog\n isOpen={isExportDialogOpen}\n dashboardId={currentDashboard.id}\n dashboardName={currentDashboard.name}\n onClose={handleExportClose}\n />\n </div>\n );\n});\n\nexport default DashboardViewPage;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/DashboardViewPage.tsx"],"sourcesContent":["/**\n * DashboardViewPage\n *\n * View/edit a single dashboard - fetches data from real backend.\n * Supports drilldown context propagation via URL parameters.\n */\n\nimport { type FC, memo, useEffect, useCallback, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { useBigConsoleNavigate } from '../context';\nimport { DashboardCanvas, ExportDashboardDialog, DrilldownBreadcrumb } from '../components/dashboard';\nimport { useDashboardStore, useWidgetStore } from '../store';\nimport { useDashboardOperations, useWidgetOperations, usePageOperations } from '../hooks';\nimport { useFilterUrlSync } from '../hooks/useFilterUrlSync';\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DashboardViewPage: FC = memo(function DashboardViewPage() {\n const { dashboardId } = useParams<{ dashboardId: string }>();\n const navigate = useBigConsoleNavigate();\n const [currentPageId, setCurrentPageId] = useState<string | null>(null);\n const [isSaving, setIsSaving] = useState(false);\n const [notFound, setNotFound] = useState(false);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n\n // Drilldown context from URL\n const { isInDrilldown, getContextFromUrl, getDrilldownMetadata } = useFilterUrlSync();\n\n // Store actions\n const currentDashboard = useDashboardStore((state) => state.currentDashboard);\n const setCurrentDashboard = useDashboardStore((state) => state.setCurrentDashboard);\n const setGlobalFilterValue = useDashboardStore((state) => state.setGlobalFilterValue);\n const clearAllGlobalFilters = useDashboardStore((state) => state.clearAllGlobalFilters);\n const widgets = useWidgetStore((state) => state.widgets);\n const clipboardWidgetId = useWidgetStore((state) => state.clipboardWidgetId);\n const clipboardAction = useWidgetStore((state) => state.clipboardAction);\n const clearClipboard = useWidgetStore((state) => state.clearClipboard);\n const selectWidget = useWidgetStore((state) => state.selectWidget);\n\n // Dashboard operations hook\n const { loading, error, fetchDashboard, updateDashboard } = useDashboardOperations();\n\n // Widget operations hook\n const {\n createWidget: createWidgetMutation,\n deleteWidget: deleteWidgetMutation,\n duplicateWidget: duplicateWidgetMutation,\n batchUpdatePositions,\n } = useWidgetOperations(currentPageId || undefined, dashboardId);\n\n // Page operations hook\n const {\n createPage: createPageMutation,\n updatePage: updatePageMutation,\n deletePage: deletePageMutation,\n reorderPages: reorderPagesMutation,\n } = usePageOperations();\n\n // Load dashboard data\n useEffect(() => {\n if (!dashboardId) {\n navigate('/dashboards');\n return;\n }\n\n // Clear stale global filters from previous dashboard before loading new one.\n // This prevents drilldown context filters (ctx_*) from the source dashboard\n // from persisting and incorrectly filtering data on the target dashboard.\n clearAllGlobalFilters();\n\n setNotFound(false);\n\n // Fetch dashboard from backend\n fetchDashboard(dashboardId).then((dashboard) => {\n if (dashboard) {\n // Set the first page as current page\n if (dashboard.pages && dashboard.pages.length > 0) {\n setCurrentPageId(dashboard.pages[0].id);\n }\n } else {\n // Dashboard not found\n setNotFound(true);\n }\n });\n\n // Cleanup\n return () => {\n setCurrentDashboard(null);\n clearAllGlobalFilters();\n setNotFound(false);\n };\n }, [dashboardId, navigate, fetchDashboard, setCurrentDashboard, clearAllGlobalFilters]);\n\n // Load drilldown context from URL and apply to global filters\n useEffect(() => {\n if (!isInDrilldown) return;\n\n // Get context params from URL (ctx_* params)\n const contextParams = getContextFromUrl();\n const drilldownMeta = getDrilldownMetadata();\n\n // Apply context to dashboard state (available for widgets to use)\n // Store as special filter values that widgets can reference\n Object.entries(contextParams).forEach(([key, value]) => {\n // Store context params as global filter values with ctx_ prefix for clarity\n setGlobalFilterValue(`ctx_${key}`, value);\n });\n\n // Store drilldown metadata as special values\n if (drilldownMeta.depth > 0) {\n setGlobalFilterValue('__drilldown_depth', drilldownMeta.depth);\n setGlobalFilterValue('__drilldown_parent', drilldownMeta.parent);\n setGlobalFilterValue('__drilldown_path', drilldownMeta.path);\n }\n\n // Log for debugging\n if (Object.keys(contextParams).length > 0) {\n console.debug('[DashboardViewPage] Loaded drilldown context:', {\n context: contextParams,\n metadata: drilldownMeta,\n });\n }\n }, [isInDrilldown, getContextFromUrl, getDrilldownMetadata, setGlobalFilterValue]);\n\n // Handle save - batch update widget positions and dashboard metadata\n const handleSave = useCallback(async () => {\n if (!dashboardId) return;\n\n setIsSaving(true);\n try {\n // Get all widgets and their positions\n const widgetList = Array.from(widgets.values());\n if (widgetList.length > 0) {\n const positionUpdates = widgetList.map((widget) => ({\n id: widget.id,\n position: widget.position,\n }));\n\n await batchUpdatePositions(positionUpdates);\n }\n\n // Update dashboard metadata if needed\n if (currentDashboard) {\n await updateDashboard({\n id: dashboardId,\n name: currentDashboard.name,\n description: currentDashboard.description,\n });\n }\n } catch (error) {\n } finally {\n setIsSaving(false);\n }\n }, [dashboardId, widgets, currentDashboard, batchUpdatePositions, updateDashboard]);\n\n // Navigate back to the dashboards list\n const handleBack = useCallback(() => {\n navigate('/dashboards');\n }, [navigate]);\n\n // Handle page change\n const handlePageChange = useCallback((pageId: string) => {\n setCurrentPageId(pageId);\n }, []);\n\n // Handle paste widget from clipboard\n const handlePaste = useCallback(async () => {\n const pageId = currentPageId || currentDashboard?.pages?.[0]?.id;\n if (!clipboardWidgetId || !dashboardId || !pageId) {\n return;\n }\n\n const sourceWidget = widgets.get(clipboardWidgetId);\n if (!sourceWidget) {\n clearClipboard();\n return;\n }\n\n // Calculate new position (place at bottom of existing widgets)\n const widgetsArray = Array.from(widgets.values());\n const maxY = widgetsArray.reduce((max, w) => {\n const widgetBottom = (w.positionY ?? 0) + (w.positionHeight ?? 4);\n return Math.max(max, widgetBottom);\n }, 0);\n\n const newPosition = {\n x: 0,\n y: maxY,\n width: sourceWidget.positionWidth ?? 4,\n height: sourceWidget.positionHeight ?? 4,\n };\n\n const newTitle = clipboardAction === 'copy' ? `${sourceWidget.title} (Copy)` : sourceWidget.title;\n\n let newWidget = null;\n\n // Try duplicateWidget API first\n try {\n newWidget = await duplicateWidgetMutation(clipboardWidgetId, newPosition, newTitle);\n } catch (err) {}\n\n // Fallback: Create new widget with source widget's data\n if (!newWidget) {\n newWidget = await createWidgetMutation({\n pageId,\n dashboardId,\n type: sourceWidget.type,\n title: newTitle,\n description: sourceWidget.description,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: (sourceWidget as unknown as Record<string, string | undefined>).dataSinkId,\n datasetId: (sourceWidget as unknown as Record<string, string | undefined>).datasetId,\n parserId: (sourceWidget as unknown as Record<string, string | undefined>).parserId,\n parserRules: (sourceWidget as unknown as Record<string, string | undefined>).parserRules,\n config: sourceWidget.config || {},\n position: newPosition,\n refreshInterval: sourceWidget.refreshInterval ?? undefined,\n metadata: sourceWidget.metadata,\n });\n }\n\n if (newWidget) {\n selectWidget(newWidget.id);\n\n // If cut action, delete the original widget from backend\n if (clipboardAction === 'cut') {\n await deleteWidgetMutation(clipboardWidgetId);\n }\n }\n\n clearClipboard();\n }, [\n clipboardWidgetId,\n clipboardAction,\n dashboardId,\n currentPageId,\n currentDashboard,\n widgets,\n duplicateWidgetMutation,\n createWidgetMutation,\n deleteWidgetMutation,\n selectWidget,\n clearClipboard,\n ]);\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // ============================================================================\n // Page Management Callbacks\n // ============================================================================\n\n // Handle adding a new page\n const handlePageAdd = useCallback(async () => {\n if (!currentDashboard || !dashboardId) return;\n\n const newPageCount = (currentDashboard.pages?.length || 0) + 1;\n const newPageName = `Page ${newPageCount}`;\n\n // Call GraphQL mutation to create page\n // Note: 'order' is not available for create - backend auto-assigns order\n const newPage = await createPageMutation({\n dashboardId,\n name: newPageName,\n });\n\n if (newPage) {\n // Update local state with the created page\n setCurrentDashboard({\n ...currentDashboard,\n pages: [...(currentDashboard.pages || []), newPage],\n });\n\n // Switch to the new page\n setCurrentPageId(newPage.id);\n }\n }, [currentDashboard, dashboardId, setCurrentDashboard, createPageMutation]);\n\n // Handle renaming a page\n const handlePageRename = useCallback(\n async (pageId: string, newName: string) => {\n if (!currentDashboard) return;\n\n // Call GraphQL mutation to update page\n const updatedPage = await updatePageMutation({\n id: pageId,\n name: newName,\n });\n\n if (updatedPage) {\n setCurrentDashboard({\n ...currentDashboard,\n pages: currentDashboard.pages?.map((page) => (page.id === pageId ? { ...page, name: newName } : page)),\n });\n }\n },\n [currentDashboard, setCurrentDashboard, updatePageMutation]\n );\n\n // Handle duplicating a page\n const handlePageDuplicate = useCallback(\n async (pageId: string) => {\n if (!currentDashboard || !dashboardId) return;\n\n const sourcePage = currentDashboard.pages?.find((p) => p.id === pageId);\n if (!sourcePage) return;\n\n // Call GraphQL mutation to create a copy of the page\n // Note: 'order' is not available for create - backend auto-assigns order\n const newPage = await createPageMutation({\n dashboardId,\n name: `${sourcePage.name} (Copy)`,\n });\n\n if (newPage) {\n setCurrentDashboard({\n ...currentDashboard,\n pages: [...(currentDashboard.pages || []), newPage],\n });\n\n setCurrentPageId(newPage.id);\n }\n // Note: Widget duplication would require additional backend support\n },\n [currentDashboard, dashboardId, setCurrentDashboard, createPageMutation]\n );\n\n // Handle deleting a page\n const handlePageDelete = useCallback(\n async (pageId: string) => {\n if (!currentDashboard || (currentDashboard.pages?.length || 0) <= 1) return;\n\n // Call GraphQL mutation to delete page\n const success = await deletePageMutation(pageId);\n\n if (success) {\n const updatedPages = currentDashboard.pages\n ?.filter((p) => p.id !== pageId)\n .map((p, index) => ({ ...p, order: index }));\n\n setCurrentDashboard({\n ...currentDashboard,\n pages: updatedPages,\n });\n\n // Switch to first page if current page was deleted\n if (currentPageId === pageId) {\n setCurrentPageId(updatedPages?.[0]?.id || null);\n }\n }\n },\n [currentDashboard, setCurrentDashboard, currentPageId, deletePageMutation]\n );\n\n // Handle reordering pages\n const handlePagesReorder = useCallback(\n async (reorderedPages: Array<{ id: string; name?: string; order?: number }>) => {\n if (!currentDashboard || !dashboardId) return;\n\n // Update local state optimistically\n setCurrentDashboard({\n ...currentDashboard,\n pages: reorderedPages.map((page, index) => ({\n ...page,\n order: index,\n })),\n });\n\n // Call GraphQL mutation to persist the reorder\n const pageIds = reorderedPages.map((p) => p.id);\n await reorderPagesMutation(dashboardId, pageIds);\n },\n [currentDashboard, dashboardId, setCurrentDashboard, reorderPagesMutation]\n );\n\n // Error state\n if (error) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full\" style={{ padding: 'var(--space-pagePadding)' }}>\n <div className=\"bg-status-error-bg border border-status-error-border rounded-lg p-6 w-full max-w-md\">\n <h3 className=\"text-lg font-medium text-status-error-text mb-2\">Error loading dashboard</h3>\n <p className=\"text-sm text-text-secondary mb-4 break-words\">{error.message}</p>\n <div className=\"flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={() => fetchDashboard(dashboardId!)}\n className=\"px-4 py-2 text-sm font-medium text-action-primary-fg bg-action-primary-bg hover:bg-action-primary-bgHover rounded-md\"\n >\n Retry\n </button>\n <button\n type=\"button\"\n onClick={() => navigate('/dashboards')}\n className=\"px-4 py-2 text-sm font-medium text-text-secondary bg-bg-muted hover:bg-bg-muted/80 rounded-md\"\n >\n Back to Dashboards\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n // Not found state\n if (notFound) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full\" style={{ padding: 'var(--space-pagePadding)' }}>\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-6 w-full max-w-md text-center\">\n <svg\n className=\"w-16 h-16 mx-auto mb-4 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <h3 className=\"text-lg font-medium text-text-primary mb-2\">Dashboard not found</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n The dashboard you're looking for doesn't exist or has been deleted.\n </p>\n <p className=\"text-xs text-text-tertiary mb-4 font-mono break-all\">ID: {dashboardId}</p>\n <button\n type=\"button\"\n onClick={() => navigate('/dashboards')}\n className=\"px-4 py-2 text-sm font-medium text-action-primary-fg bg-action-primary-bg hover:bg-action-primary-bgHover rounded-md\"\n >\n Back to Dashboards\n </button>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (loading || !currentDashboard) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"flex flex-col items-center gap-4\">\n <div className=\"animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full\" />\n <p className=\"text-sm text-text-secondary\">Loading dashboard...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"h-full flex flex-col overflow-hidden\">\n {/* Page header with back navigation - visible in both view and edit modes */}\n <div className=\"flex items-center gap-3 px-3 py-2 border-b border-border-default bg-bg-surface flex-shrink-0\">\n <Button\n onClick={handleBack}\n variant=\"ghost\"\n size=\"sm\"\n title=\"Back to Dashboards\"\n aria-label=\"Back to Dashboards\"\n >\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Back\n </Button>\n <h2 className=\"text-sm font-semibold text-text-primary truncate\">{currentDashboard.name}</h2>\n </div>\n\n {/* Drilldown Breadcrumb - shown when navigating via drilldown */}\n {isInDrilldown && (\n <DrilldownBreadcrumb\n currentDashboardId={currentDashboard.id}\n currentDashboardName={currentDashboard.name}\n basePath=\"/bigconsole/dashboards\"\n />\n )}\n\n {/* Dashboard Canvas with Page Tabs */}\n <div className=\"flex-1 min-w-0 overflow-hidden\">\n <DashboardCanvas\n dashboardId={currentDashboard.id}\n pageId={currentPageId || currentDashboard.pages?.[0]?.id}\n title={currentDashboard.name}\n onPaste={handlePaste}\n onSave={handleSave}\n isSaving={isSaving}\n onExport={handleExport}\n // Page tabs props\n pages={(currentDashboard.pages || []).map((page, index) => ({\n id: page.id,\n name: page.name || `Page ${index + 1}`,\n order: page.order ?? 0,\n }))}\n activePageId={currentPageId || currentDashboard.pages?.[0]?.id}\n onPageChange={handlePageChange}\n onPageAdd={handlePageAdd}\n onPageRename={handlePageRename}\n onPageDuplicate={handlePageDuplicate}\n onPageDelete={handlePageDelete}\n onPagesReorder={handlePagesReorder}\n showPageTabs={true}\n />\n </div>\n\n {/* Export Dashboard Dialog */}\n <ExportDashboardDialog\n isOpen={isExportDialogOpen}\n dashboardId={currentDashboard.id}\n dashboardName={currentDashboard.name}\n onClose={handleExportClose}\n />\n </div>\n );\n});\n\nexport default DashboardViewPage;\n"],"mappings":"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"}
|