@burdenoff/microfe-bigconsole 2026.924.1 → 2026.924.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/bigconsole/BigConsoleRoutes.js +137 -138
  2. package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
  3. package/dist/bigconsole/assistant/previewController.js +10 -10
  4. package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js +25 -0
  5. package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js.map +1 -0
  6. package/dist/bigconsole/components/DrilldownDashboardRenderer.js +17 -17
  7. package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +32 -32
  8. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +52 -52
  9. package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
  10. package/dist/bigconsole/components/dashboard/DashboardCanvas.js +7 -7
  11. package/dist/bigconsole/components/dashboard/DashboardStatusPill.js +53 -0
  12. package/dist/bigconsole/components/dashboard/DashboardStatusPill.js.map +1 -0
  13. package/dist/bigconsole/components/dashboard/DashboardToolbar.js +60 -60
  14. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +21 -21
  15. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +30 -30
  16. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +36 -36
  17. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +34 -34
  18. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +38 -38
  19. package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
  20. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +121 -121
  21. package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
  22. package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js +22 -22
  23. package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +46 -46
  24. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +53 -53
  25. package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -1
  26. package/dist/bigconsole/components/dashboard/WidgetPalette.js +47 -47
  27. package/dist/bigconsole/components/dashboard/index.js +1 -0
  28. package/dist/bigconsole/components/datasink/CacheTTLSelector.js +35 -35
  29. package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +18 -18
  30. package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +20 -20
  31. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js +30 -29
  32. package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js.map +1 -1
  33. package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +48 -48
  34. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +103 -103
  35. package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
  36. package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +35 -35
  37. package/dist/bigconsole/components/overview/DashboardGuide.js +214 -0
  38. package/dist/bigconsole/components/overview/DashboardGuide.js.map +1 -0
  39. package/dist/bigconsole/components/overview/DataHealthCard.js +97 -0
  40. package/dist/bigconsole/components/overview/DataHealthCard.js.map +1 -0
  41. package/dist/bigconsole/components/overview/OverviewQuickActions.js +32 -0
  42. package/dist/bigconsole/components/overview/OverviewQuickActions.js.map +1 -0
  43. package/dist/bigconsole/components/overview/OverviewStatCard.js +57 -0
  44. package/dist/bigconsole/components/overview/OverviewStatCard.js.map +1 -0
  45. package/dist/bigconsole/components/overview/RecentDashboardsPanel.js +107 -0
  46. package/dist/bigconsole/components/overview/RecentDashboardsPanel.js.map +1 -0
  47. package/dist/bigconsole/components/overview/dashboardGuideSteps.js +119 -0
  48. package/dist/bigconsole/components/overview/dashboardGuideSteps.js.map +1 -0
  49. package/dist/bigconsole/components/overview/index.js +8 -0
  50. package/dist/bigconsole/components/overview/overviewModel.js +119 -0
  51. package/dist/bigconsole/components/overview/overviewModel.js.map +1 -0
  52. package/dist/bigconsole/components/overview/useOverviewData.js +48 -0
  53. package/dist/bigconsole/components/overview/useOverviewData.js.map +1 -0
  54. package/dist/bigconsole/components/parser/DataSinkSelector.js +40 -40
  55. package/dist/bigconsole/components/parser/ImportParserDialog.js +127 -127
  56. package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
  57. package/dist/bigconsole/components/parser/ParserSelector.js +51 -51
  58. package/dist/bigconsole/components/preview/AiPreviewContent.js +5 -5
  59. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +62 -61
  60. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
  61. package/dist/bigconsole/components/preview/AiPreviewPanel.js +82 -81
  62. package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
  63. package/dist/bigconsole/components/widgets/ActionConfigDialog.js +13 -13
  64. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +70 -70
  65. package/dist/bigconsole/components/widgets/PropertiesPanel.js +28 -28
  66. package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +25 -25
  67. package/dist/bigconsole/components/widgets/chart/ChartConfig.js +48 -48
  68. package/dist/bigconsole/components/widgets/comments/WidgetComments.js +46 -46
  69. package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +61 -61
  70. package/dist/bigconsole/components/widgets/form/FormWidget.js +36 -36
  71. package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +42 -42
  72. package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +25 -25
  73. package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +21 -21
  74. package/dist/bigconsole/components/widgets/list/ListConfig.js +30 -30
  75. package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +38 -38
  76. package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +9 -9
  77. package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +9 -9
  78. package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +49 -49
  79. package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +39 -39
  80. package/dist/bigconsole/components/widgets/table/TableConfig.js +73 -73
  81. package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +64 -64
  82. package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +10 -10
  83. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +68 -68
  84. package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
  85. package/dist/bigconsole/pages/AdminPage.js +136 -136
  86. package/dist/bigconsole/pages/DashboardBuilderPage.js +10 -10
  87. package/dist/bigconsole/pages/DashboardViewPage.js +61 -61
  88. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  89. package/dist/bigconsole/pages/DashboardsPage.js +327 -348
  90. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  91. package/dist/bigconsole/pages/DataParsersPage.js +5 -5
  92. package/dist/bigconsole/pages/DataSinkBuilderPage.js +10 -10
  93. package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
  94. package/dist/bigconsole/pages/DataSinkViewPage.js +83 -83
  95. package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
  96. package/dist/bigconsole/pages/DataSinksPage.js +11 -11
  97. package/dist/bigconsole/pages/DataSourcesPage.js +5 -5
  98. package/dist/bigconsole/pages/InsightsPage.js +315 -315
  99. package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
  100. package/dist/bigconsole/pages/OverviewPage.js +244 -0
  101. package/dist/bigconsole/pages/OverviewPage.js.map +1 -0
  102. package/dist/bigconsole/pages/ParserBuilderPage.js +5 -5
  103. package/dist/bigconsole/pages/ParserViewPage.js +98 -98
  104. package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
  105. package/dist/bigconsole/pages/PipelineBuilderPage.js +8 -8
  106. package/dist/bigconsole/pages/PipelineViewPage.js +46 -46
  107. package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
  108. package/dist/bigconsole/pages/PipelinesPage.js +80 -80
  109. package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
  110. package/dist/bigconsole/pages/SettingsPage.js +70 -70
  111. package/dist/bigconsole/pages/WidgetDemoPage.js +42 -42
  112. package/dist/bigconsole/pages/WorkflowViewPage.js +200 -200
  113. package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
  114. package/dist/bigconsole/pages/WorkflowsPage.js +78 -78
  115. package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
  116. package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +34 -34
  117. package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +79 -79
  118. package/dist/bigconsole/renderers/adapters/CustomRenderer.js +46 -46
  119. package/dist/bigconsole/utils/createDialogParam.js +14 -0
  120. package/dist/bigconsole/utils/createDialogParam.js.map +1 -0
  121. package/dist/generated/global-operations.js +1 -1
  122. package/dist/generated/wspace-operations.js +285 -222
  123. package/dist/generated/wspace-operations.js.map +1 -1
  124. package/dist/shared/hooks/useTr.js +10 -7
  125. package/dist/shared/hooks/useTr.js.map +1 -1
  126. package/package.json +1 -1
  127. package/dist/bigconsole/pages/HomePage.js +0 -318
  128. package/dist/bigconsole/pages/HomePage.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { useWidgetStore as e } from "../store/widgetStore.js";
2
2
  import { useDashboardStore as t } from "../store/dashboardStore.js";
3
3
  import "../store/index.js";
4
- import { Section as n } from "../components/common/Section.js";
5
- import "../components/common/index.js";
6
- import { useBigConsoleNavigate as r } from "../context/BigConsoleContext.js";
4
+ import { useBigConsoleNavigate as n } from "../context/BigConsoleContext.js";
7
5
  import "../context/index.js";
6
+ import { Section as r } from "../components/common/Section.js";
7
+ import "../components/common/index.js";
8
8
  import i from "../hooks/useWidgetOperations.js";
9
9
  import a from "../hooks/useDashboardOperations.js";
10
10
  import ee from "../hooks/useFilterUrlSync.js";
@@ -13,38 +13,38 @@ import "../hooks/index.js";
13
13
  import { BIGCONSOLE_PERMISSIONS as o } from "../../constants/permissions.js";
14
14
  import ne from "../components/dashboard/DashboardCanvas.js";
15
15
  import s from "../components/dashboard/LiveCanvasToggle.js";
16
- import re from "../components/dashboard/ExportDashboardDialog.js";
17
- import ie from "../components/dashboard/DrilldownBreadcrumb.js";
18
- import ae from "../components/dashboard/PublishToStoreDialog.js";
16
+ import c from "../components/dashboard/ExportDashboardDialog.js";
17
+ import re from "../components/dashboard/DrilldownBreadcrumb.js";
18
+ import ie from "../components/dashboard/PublishToStoreDialog.js";
19
19
  import "../components/dashboard/index.js";
20
- import { memo as c, useCallback as l, useEffect as u, useRef as oe, useState as d } from "react";
21
- import { useParams as se } from "react-router";
22
- import { AccessDenied as ce } from "@burdenoff/fe-libs/shared/components";
23
- import { usePermissions as le } from "@burdenoff/fe-libs/shared/providers/shell";
24
- import { ArrowLeft as ue } from "lucide-react";
25
- import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
20
+ import { memo as l, useCallback as u, useEffect as d, useRef as ae, useState as f } from "react";
21
+ import { useParams as oe } from "react-router";
22
+ import { AccessDenied as se } from "@burdenoff/fe-libs/shared/components";
23
+ import { usePermissions as ce } from "@burdenoff/fe-libs/shared/providers/shell";
24
+ import { ArrowLeft as le } from "lucide-react";
26
25
  import { Button as p, IllustratedEmptyState as m } from "@burdenoff/fe-libs/ui";
27
26
  import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
27
+ import { useI18n as ue } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
28
28
  //#region src/bigconsole/pages/DashboardViewPage.tsx
29
- var v = c(function() {
30
- let { dashboardId: c } = se(), v = r(), { t: y } = f(), { hasPermission: b, isLoading: de } = le(), fe = b(o.DASHBOARD_READ), pe = b(o.DASHBOARD_UPDATE), me = b(o.DASHBOARD_EXPORT), he = b(o.DASHBOARD_PUBLISH), [x, S] = d(null), [ge, C] = d(!1), [_e, w] = d(!1), [ve, T] = d(!1), [E, ye] = d(!1), { isInDrilldown: D, getContextFromUrl: O, getDrilldownMetadata: k } = ee(), 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: be, error: z, fetchDashboard: B, updateDashboard: V } = a(), { createWidget: H, deleteWidget: U, duplicateWidget: W, batchUpdatePositions: G, refetch: K } = i(x || void 0, c), { createPage: q, updatePage: J, deletePage: Y, reorderPages: X } = te();
31
- u(() => {
32
- if (!c) {
29
+ var v = l(function() {
30
+ let { dashboardId: l } = oe(), v = n(), { t: y } = ue(), { hasPermission: b, isLoading: de } = ce(), fe = b(o.DASHBOARD_READ), pe = b(o.DASHBOARD_UPDATE), me = b(o.DASHBOARD_EXPORT), he = b(o.DASHBOARD_PUBLISH), [x, S] = f(null), [ge, C] = f(!1), [_e, w] = f(!1), [ve, T] = f(!1), [E, ye] = f(!1), { isInDrilldown: D, getContextFromUrl: O, getDrilldownMetadata: k } = ee(), 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: be, error: z, fetchDashboard: B, updateDashboard: V } = a(), { createWidget: H, deleteWidget: U, duplicateWidget: W, batchUpdatePositions: G, refetch: K } = i(x || void 0, l), { createPage: q, updatePage: J, deletePage: Y, reorderPages: X } = te();
31
+ d(() => {
32
+ if (!l) {
33
33
  v("/dashboards");
34
34
  return;
35
35
  }
36
- return N(), w(!1), B(c).then((e) => {
36
+ return N(), w(!1), B(l).then((e) => {
37
37
  e ? e.pages && e.pages.length > 0 && S(e.pages[0].id) : w(!0);
38
38
  }), () => {
39
39
  j(null), N(), w(!1);
40
40
  };
41
41
  }, [
42
- c,
42
+ l,
43
43
  v,
44
44
  B,
45
45
  j,
46
46
  N
47
- ]), u(() => {
47
+ ]), d(() => {
48
48
  if (!D) return;
49
49
  let e = O(), t = k();
50
50
  Object.entries(e).forEach(([e, t]) => {
@@ -59,11 +59,11 @@ var v = c(function() {
59
59
  k,
60
60
  M
61
61
  ]);
62
- let Z = oe(K);
63
- Z.current = K, u(() => {
64
- if (!E || !c) return;
62
+ let Z = ae(K);
63
+ Z.current = K, d(() => {
64
+ if (!E || !l) return;
65
65
  let e = () => {
66
- typeof document < "u" && document.visibilityState !== "visible" || t.getState().viewMode !== "edit" && (B(c, { silent: !0 }), Z.current());
66
+ typeof document < "u" && document.visibilityState !== "visible" || t.getState().viewMode !== "edit" && (B(l, { silent: !0 }), Z.current());
67
67
  };
68
68
  e();
69
69
  let n = window.setInterval(e, 3500), r = () => {
@@ -74,11 +74,11 @@ var v = c(function() {
74
74
  };
75
75
  }, [
76
76
  E,
77
- c,
77
+ l,
78
78
  B
79
79
  ]);
80
- let xe = l(async () => {
81
- if (c) {
80
+ let xe = u(async () => {
81
+ if (l) {
82
82
  C(!0);
83
83
  try {
84
84
  let e = Array.from(P.values());
@@ -86,7 +86,7 @@ var v = c(function() {
86
86
  id: e.id,
87
87
  position: e.position
88
88
  }))), A && await V({
89
- id: c,
89
+ id: l,
90
90
  name: A.name,
91
91
  description: A.description
92
92
  });
@@ -95,18 +95,18 @@ var v = c(function() {
95
95
  }
96
96
  }
97
97
  }, [
98
- c,
98
+ l,
99
99
  P,
100
100
  A,
101
101
  G,
102
102
  V
103
- ]), Se = l(() => {
103
+ ]), Se = u(() => {
104
104
  v("/dashboards");
105
- }, [v]), Ce = l((e) => {
105
+ }, [v]), Ce = u((e) => {
106
106
  S(e);
107
- }, []), we = l(async () => {
107
+ }, []), we = u(async () => {
108
108
  let e = x || A?.pages?.[0]?.id;
109
- if (!F || !c || !e) return;
109
+ if (!F || !l || !e) return;
110
110
  let t = P.get(F);
111
111
  if (!t) {
112
112
  L();
@@ -126,7 +126,7 @@ var v = c(function() {
126
126
  } catch {}
127
127
  i ||= await H({
128
128
  pageId: e,
129
- dashboardId: c,
129
+ dashboardId: l,
130
130
  type: t.type,
131
131
  title: r,
132
132
  description: t.description,
@@ -142,7 +142,7 @@ var v = c(function() {
142
142
  }, [
143
143
  F,
144
144
  I,
145
- c,
145
+ l,
146
146
  x,
147
147
  A,
148
148
  P,
@@ -152,18 +152,18 @@ var v = c(function() {
152
152
  R,
153
153
  L,
154
154
  y
155
- ]), Te = l(() => {
155
+ ]), Te = u(() => {
156
156
  T(!0);
157
- }, []), [Ee, Q] = d(!1), De = l(() => {
157
+ }, []), [Ee, Q] = f(!1), De = u(() => {
158
158
  Q(!0);
159
- }, []), $ = l(() => {
159
+ }, []), $ = u(() => {
160
160
  Q(!1);
161
- }, []), Oe = l(() => {
161
+ }, []), Oe = u(() => {
162
162
  T(!1);
163
- }, []), ke = l(async () => {
164
- if (!A || !c) return;
163
+ }, []), ke = u(async () => {
164
+ if (!A || !l) return;
165
165
  let e = await q({
166
- dashboardId: c,
166
+ dashboardId: l,
167
167
  name: y("bigconsole.dashboards.view.pageName", "Page {{count}}", { count: (A.pages?.length || 0) + 1 })
168
168
  });
169
169
  e && (j({
@@ -172,11 +172,11 @@ var v = c(function() {
172
172
  }), S(e.id));
173
173
  }, [
174
174
  A,
175
- c,
175
+ l,
176
176
  j,
177
177
  q,
178
178
  y
179
- ]), Ae = l(async (e, t) => {
179
+ ]), Ae = u(async (e, t) => {
180
180
  A && await J({
181
181
  id: e,
182
182
  name: t
@@ -191,12 +191,12 @@ var v = c(function() {
191
191
  A,
192
192
  j,
193
193
  J
194
- ]), je = l(async (e) => {
195
- if (!A || !c) return;
194
+ ]), je = u(async (e) => {
195
+ if (!A || !l) return;
196
196
  let t = A.pages?.find((t) => t.id === e);
197
197
  if (!t) return;
198
198
  let n = await q({
199
- dashboardId: c,
199
+ dashboardId: l,
200
200
  name: y("bigconsole.dashboards.view.pageCopyName", "{{name}} (Copy)", { name: t.name ?? "" })
201
201
  });
202
202
  n && (j({
@@ -205,11 +205,11 @@ var v = c(function() {
205
205
  }), S(n.id));
206
206
  }, [
207
207
  A,
208
- c,
208
+ l,
209
209
  j,
210
210
  q,
211
211
  y
212
- ]), Me = l(async (e) => {
212
+ ]), Me = u(async (e) => {
213
213
  if (!(!A || (A.pages?.length || 0) <= 1) && await Y(e)) {
214
214
  let t = A.pages?.filter((t) => t.id !== e).map((e, t) => ({
215
215
  ...e,
@@ -225,17 +225,17 @@ var v = c(function() {
225
225
  j,
226
226
  x,
227
227
  Y
228
- ]), Ne = l(async (e) => {
229
- !A || !c || (j({
228
+ ]), Ne = u(async (e) => {
229
+ !A || !l || (j({
230
230
  ...A,
231
231
  pages: e.map((e, t) => ({
232
232
  ...e,
233
233
  order: t
234
234
  }))
235
- }), await X(c, e.map((e) => e.id)));
235
+ }), await X(l, e.map((e) => e.id)));
236
236
  }, [
237
237
  A,
238
- c,
238
+ l,
239
239
  j,
240
240
  X
241
241
  ]);
@@ -245,7 +245,7 @@ var v = c(function() {
245
245
  }) : fe ? z ? /* @__PURE__ */ g("div", {
246
246
  className: "flex h-full flex-col items-center justify-center",
247
247
  style: { padding: "var(--space-pagePadding)" },
248
- children: /* @__PURE__ */ g(n, {
248
+ children: /* @__PURE__ */ g(r, {
249
249
  headingLevel: 2,
250
250
  tone: "danger",
251
251
  title: y("bigconsole.dashboards.view.errorTitle", "Error loading dashboard"),
@@ -257,7 +257,7 @@ var v = c(function() {
257
257
  "data-testid": "dashboard-view-error",
258
258
  actions: /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g(p, {
259
259
  size: "sm",
260
- onClick: () => B(c),
260
+ onClick: () => B(l),
261
261
  children: y("bigconsole.dashboards.view.retry", "Retry")
262
262
  }), /* @__PURE__ */ g(p, {
263
263
  size: "sm",
@@ -274,7 +274,7 @@ var v = c(function() {
274
274
  title: y("bigconsole.dashboards.view.notFoundTitle", "Dashboard not found"),
275
275
  description: /* @__PURE__ */ _(h, { children: [y("bigconsole.dashboards.view.notFoundDescription", "The dashboard you're looking for doesn't exist or has been deleted."), /* @__PURE__ */ g("span", {
276
276
  className: "mt-2 block font-mono text-xs text-text-muted break-all",
277
- children: y("bigconsole.dashboards.view.idLabel", "ID: {{id}}", { id: c ?? "" })
277
+ children: y("bigconsole.dashboards.view.idLabel", "ID: {{id}}", { id: l ?? "" })
278
278
  })] }),
279
279
  action: /* @__PURE__ */ g(p, {
280
280
  onClick: () => v("/dashboards"),
@@ -295,7 +295,7 @@ var v = c(function() {
295
295
  }) : /* @__PURE__ */ _("div", {
296
296
  className: "h-full flex flex-col overflow-hidden",
297
297
  children: [
298
- /* @__PURE__ */ g(n, {
298
+ /* @__PURE__ */ g(r, {
299
299
  emphasis: "bare",
300
300
  padding: "none",
301
301
  "data-testid": "dashboard-route-header",
@@ -312,7 +312,7 @@ var v = c(function() {
312
312
  title: y("bigconsole.dashboards.view.backToDashboards", "Back to Dashboards"),
313
313
  "aria-label": y("bigconsole.dashboards.view.backToDashboards", "Back to Dashboards"),
314
314
  className: "flex-shrink-0",
315
- children: [/* @__PURE__ */ g(ue, { className: "w-4 h-4 mr-2" }), y("bigconsole.dashboards.view.back", "Back")]
315
+ children: [/* @__PURE__ */ g(le, { className: "w-4 h-4 mr-2" }), y("bigconsole.dashboards.view.back", "Back")]
316
316
  }),
317
317
  /* @__PURE__ */ g("h1", {
318
318
  className: "min-w-0 flex-1 truncate text-sm font-semibold text-text-primary",
@@ -329,7 +329,7 @@ var v = c(function() {
329
329
  ]
330
330
  })
331
331
  }),
332
- D && /* @__PURE__ */ g(ie, {
332
+ D && /* @__PURE__ */ g(re, {
333
333
  currentDashboardId: A.id,
334
334
  currentDashboardName: A.name,
335
335
  basePath: "/bigconsole/dashboards"
@@ -360,13 +360,13 @@ var v = c(function() {
360
360
  showPageTabs: !0
361
361
  })
362
362
  }),
363
- /* @__PURE__ */ g(re, {
363
+ /* @__PURE__ */ g(c, {
364
364
  isOpen: ve,
365
365
  dashboardId: A.id,
366
366
  dashboardName: A.name,
367
367
  onClose: Oe
368
368
  }),
369
- /* @__PURE__ */ g(ae, {
369
+ /* @__PURE__ */ g(ie, {
370
370
  isOpen: Ee,
371
371
  dashboardId: A.id,
372
372
  dashboardName: A.name,
@@ -374,7 +374,7 @@ var v = c(function() {
374
374
  onClose: $
375
375
  })
376
376
  ]
377
- }) : /* @__PURE__ */ g(ce, {
377
+ }) : /* @__PURE__ */ g(se, {
378
378
  title: y("bigconsole.dashboards.view.accessDeniedTitle", "Access Denied"),
379
379
  description: y("bigconsole.dashboards.view.accessDeniedDescription", "You don't have permission to view this dashboard.")
380
380
  });
@@ -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, useRef } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft } from 'lucide-react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\nimport { useBigConsoleNavigate } from '../context';\nimport {\n DashboardCanvas,\n ExportDashboardDialog,\n PublishToStoreDialog,\n DrilldownBreadcrumb,\n LiveCanvasToggle,\n} from '../components/dashboard';\nimport { Section } from '../components/common';\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 { t } = useI18n();\n\n // RBAC gating. Read gate blocks the whole board; action gates hide the\n // corresponding toolbar controls (the toolbar renders each control only when\n // its handler is passed, so an undefined handler cleanly removes it).\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_READ);\n const canUpdate = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_UPDATE);\n const canExport = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_EXPORT);\n const canPublish = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_PUBLISH);\n\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 // Live canvas: when on, poll the dashboard + widget STRUCTURE so server-side\n // pipeline builds (AI assistant agent or any other writer) appear without a reload.\n const [isLive, setIsLive] = 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 refetch: refetchWidgets,\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 // ---------------------------------------------------------------------------\n // Live canvas: poll dashboard + widget STRUCTURE while live mode is on.\n //\n // The per-widget DATA poll (WidgetWrapper → refreshInterval) already keeps\n // numbers fresh, but nothing refetches the board's STRUCTURE, so a widget the\n // AI assistant's api-calls agent (DataSink → Parser → Widget) — or any other\n // server-side writer — adds would never appear without a manual reload. This\n // interval closes that gap: it silently refetches the dashboard (pages) and\n // the widget list, so new widgets/pages materialise on the canvas live.\n //\n // Efficiency: it runs ONLY while live mode is on AND the tab is visible, and\n // pauses while the board is in edit mode so a poll cannot clobber an in-flight\n // drag/resize. Polling, not subscriptions — the backend GraphQL server is\n // HTTP-only with no graphql-ws/PubSub transport (see LiveBoardBanner).\n //\n // Latency note: the floor on how quickly a change shows up is NOT this poll\n // interval but fe-libs' shared GraphQL read cache (RECENT_QUERY_CACHE_TTL_MS,\n // ~15s) — `network-only` is served from that short-lived client cache, so a\n // newly-created widget surfaces on the next poll AFTER its cache entry\n // expires. No manual reload is ever needed; worst-case freshness is ~15s.\n // ---------------------------------------------------------------------------\n const refetchWidgetsRef = useRef(refetchWidgets);\n refetchWidgetsRef.current = refetchWidgets;\n\n useEffect(() => {\n if (!isLive || !dashboardId) return;\n\n const LIVE_POLL_MS = 3500;\n\n const pollStructure = () => {\n // Skip work when the tab is backgrounded or the board is being edited.\n if (typeof document !== 'undefined' && document.visibilityState !== 'visible') return;\n if (useDashboardStore.getState().viewMode === 'edit') return;\n void fetchDashboard(dashboardId, { silent: true });\n void refetchWidgetsRef.current();\n };\n\n // Refetch immediately on enable so newly-built widgets show up at once,\n // then settle into the cadence.\n pollStructure();\n const intervalId = window.setInterval(pollStructure, LIVE_POLL_MS);\n\n // Catch up the moment the operator returns to the tab.\n const handleVisibility = () => {\n if (document.visibilityState === 'visible') pollStructure();\n };\n document.addEventListener('visibilitychange', handleVisibility);\n\n return () => {\n window.clearInterval(intervalId);\n document.removeEventListener('visibilitychange', handleVisibility);\n };\n }, [isLive, dashboardId, fetchDashboard]);\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 =\n clipboardAction === 'copy'\n ? t('bigconsole.dashboards.view.widgetCopyTitle', '{{title}} (Copy)', { title: sourceWidget.title })\n : 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 t,\n ]);\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n // Publish this dashboard as a reusable store template\n const [isPublishDialogOpen, setIsPublishDialogOpen] = useState(false);\n const handlePublishTemplate = useCallback(() => {\n setIsPublishDialogOpen(true);\n }, []);\n const handlePublishClose = useCallback(() => {\n setIsPublishDialogOpen(false);\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 = t('bigconsole.dashboards.view.pageName', 'Page {{count}}', { count: 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, t]);\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: t('bigconsole.dashboards.view.pageCopyName', '{{name}} (Copy)', {\n name: sourcePage.name ?? '',\n }),\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, t]\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 // Permission read gate — wait for the provider to resolve before deciding,\n // then block the board for callers without dashboard read access. Placed\n // ahead of error/not-found/loading returns so an unauthorized caller never\n // sees dashboard contents; those returns remain reachable once read is allowed.\n if (permissionsLoading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (!canRead) {\n return (\n <AccessDenied\n title={t('bigconsole.dashboards.view.accessDeniedTitle', 'Access Denied')}\n description={t(\n 'bigconsole.dashboards.view.accessDeniedDescription',\n \"You don't have permission to view this dashboard.\"\n )}\n />\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex h-full flex-col items-center justify-center\" style={{ padding: 'var(--space-pagePadding)' }}>\n <Section\n headingLevel={2}\n tone=\"danger\"\n title={t('bigconsole.dashboards.view.errorTitle', 'Error loading dashboard')}\n description={<span className=\"break-words\">{error.message}</span>}\n className=\"w-full max-w-md\"\n data-testid=\"dashboard-view-error\"\n actions={\n <>\n <Button size=\"sm\" onClick={() => fetchDashboard(dashboardId!)}>\n {t('bigconsole.dashboards.view.retry', 'Retry')}\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => navigate('/dashboards')}>\n {t('bigconsole.dashboards.view.backToDashboards', 'Back to Dashboards')}\n </Button>\n </>\n }\n />\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 <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('bigconsole.dashboards.view.notFoundTitle', 'Dashboard not found')}\n description={\n <>\n {t(\n 'bigconsole.dashboards.view.notFoundDescription',\n \"The dashboard you're looking for doesn't exist or has been deleted.\"\n )}\n <span className=\"mt-2 block font-mono text-xs text-text-muted break-all\">\n {t('bigconsole.dashboards.view.idLabel', 'ID: {{id}}', { id: dashboardId ?? '' })}\n </span>\n </>\n }\n action={\n <Button onClick={() => navigate('/dashboards')}>\n {t('bigconsole.dashboards.view.backToDashboards', 'Back to Dashboards')}\n </Button>\n }\n />\n </div>\n );\n }\n\n // Loading state\n if (loading || !currentDashboard) {\n return (\n <div className=\"flex items-center justify-center h-full\" role=\"status\" aria-live=\"polite\">\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\">\n {t('bigconsole.dashboards.view.loading', 'Loading dashboard...')}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"h-full flex flex-col overflow-hidden\">\n {/*\n Route identity strip. This is the ONLY place the board is named on this\n route — the toolbar below it deliberately carries no title, so the user\n never sees the same name twice in two stacked bands.\n */}\n <Section\n emphasis=\"bare\"\n padding=\"none\"\n data-testid=\"dashboard-route-header\"\n // Landmark name only; no catalog key exists for it yet.\n aria-label=\"Dashboard\"\n className=\"flex-shrink-0 border-b border-[var(--color-border-subtle)] bg-bg-surface px-3 py-2\"\n headerClassName=\"\"\n header={\n <div className=\"flex flex-wrap items-center gap-control-gap\">\n <Button\n onClick={handleBack}\n variant=\"ghost\"\n size=\"sm\"\n title={t('bigconsole.dashboards.view.backToDashboards', 'Back to Dashboards')}\n aria-label={t('bigconsole.dashboards.view.backToDashboards', 'Back to Dashboards')}\n className=\"flex-shrink-0\"\n >\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dashboards.view.back', 'Back')}\n </Button>\n <h1\n className=\"min-w-0 flex-1 truncate text-sm font-semibold text-text-primary\"\n title={currentDashboard.name}\n >\n {currentDashboard.name}\n </h1>\n {/* Live canvas toggle — watch server-side pipeline builds appear without a reload */}\n <div className=\"flex-shrink-0\">\n <LiveCanvasToggle isLive={isLive} onToggle={() => setIsLive((prev) => !prev)} />\n </div>\n </div>\n }\n />\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 // Gate editor/action controls: DashboardToolbar renders each button\n // only when its handler is defined, so passing undefined hides the\n // control for callers lacking the permission.\n onSave={canUpdate ? handleSave : undefined}\n isSaving={isSaving}\n onExport={canExport ? handleExport : undefined}\n onPublishTemplate={canPublish ? handlePublishTemplate : undefined}\n // Page tabs props\n pages={(currentDashboard.pages || []).map((page, index) => ({\n id: page.id,\n name: page.name || t('bigconsole.dashboards.view.pageName', 'Page {{count}}', { count: 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\n {/* Publish as Template */}\n <PublishToStoreDialog\n isOpen={isPublishDialogOpen}\n dashboardId={currentDashboard.id}\n dashboardName={currentDashboard.name}\n dashboardDescription={currentDashboard.description}\n onClose={handlePublishClose}\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, useRef } from 'react';\nimport { useParams } from 'react-router';\nimport { ArrowLeft } from 'lucide-react';\nimport { Button, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { AccessDenied } from '@burdenoff/fe-libs/shared/components';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\nimport { useBigConsoleNavigate } from '../context';\nimport {\n DashboardCanvas,\n ExportDashboardDialog,\n PublishToStoreDialog,\n DrilldownBreadcrumb,\n LiveCanvasToggle,\n} from '../components/dashboard';\nimport { Section } from '../components/common';\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 { t } = useI18n();\n\n // RBAC gating. Read gate blocks the whole board; action gates hide the\n // corresponding toolbar controls (the toolbar renders each control only when\n // its handler is passed, so an undefined handler cleanly removes it).\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_READ);\n const canUpdate = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_UPDATE);\n const canExport = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_EXPORT);\n const canPublish = hasPermission(BIGCONSOLE_PERMISSIONS.DASHBOARD_PUBLISH);\n\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 // Live canvas: when on, poll the dashboard + widget STRUCTURE so server-side\n // pipeline builds (AI assistant agent or any other writer) appear without a reload.\n const [isLive, setIsLive] = 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 refetch: refetchWidgets,\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 // ---------------------------------------------------------------------------\n // Live canvas: poll dashboard + widget STRUCTURE while live mode is on.\n //\n // The per-widget DATA poll (WidgetWrapper → refreshInterval) already keeps\n // numbers fresh, but nothing refetches the board's STRUCTURE, so a widget the\n // AI assistant's api-calls agent (DataSink → Parser → Widget) — or any other\n // server-side writer — adds would never appear without a manual reload. This\n // interval closes that gap: it silently refetches the dashboard (pages) and\n // the widget list, so new widgets/pages materialise on the canvas live.\n //\n // Efficiency: it runs ONLY while live mode is on AND the tab is visible, and\n // pauses while the board is in edit mode so a poll cannot clobber an in-flight\n // drag/resize. Polling, not subscriptions — the backend GraphQL server is\n // HTTP-only with no graphql-ws/PubSub transport (see LiveBoardBanner).\n //\n // Latency note: the floor on how quickly a change shows up is NOT this poll\n // interval but fe-libs' shared GraphQL read cache (RECENT_QUERY_CACHE_TTL_MS,\n // ~15s) — `network-only` is served from that short-lived client cache, so a\n // newly-created widget surfaces on the next poll AFTER its cache entry\n // expires. No manual reload is ever needed; worst-case freshness is ~15s.\n // ---------------------------------------------------------------------------\n const refetchWidgetsRef = useRef(refetchWidgets);\n refetchWidgetsRef.current = refetchWidgets;\n\n useEffect(() => {\n if (!isLive || !dashboardId) return;\n\n const LIVE_POLL_MS = 3500;\n\n const pollStructure = () => {\n // Skip work when the tab is backgrounded or the board is being edited.\n if (typeof document !== 'undefined' && document.visibilityState !== 'visible') return;\n if (useDashboardStore.getState().viewMode === 'edit') return;\n void fetchDashboard(dashboardId, { silent: true });\n void refetchWidgetsRef.current();\n };\n\n // Refetch immediately on enable so newly-built widgets show up at once,\n // then settle into the cadence.\n pollStructure();\n const intervalId = window.setInterval(pollStructure, LIVE_POLL_MS);\n\n // Catch up the moment the operator returns to the tab.\n const handleVisibility = () => {\n if (document.visibilityState === 'visible') pollStructure();\n };\n document.addEventListener('visibilitychange', handleVisibility);\n\n return () => {\n window.clearInterval(intervalId);\n document.removeEventListener('visibilitychange', handleVisibility);\n };\n }, [isLive, dashboardId, fetchDashboard]);\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 =\n clipboardAction === 'copy'\n ? t('bigconsole.dashboards.view.widgetCopyTitle', '{{title}} (Copy)', { title: sourceWidget.title })\n : 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 t,\n ]);\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n // Publish this dashboard as a reusable store template\n const [isPublishDialogOpen, setIsPublishDialogOpen] = useState(false);\n const handlePublishTemplate = useCallback(() => {\n setIsPublishDialogOpen(true);\n }, []);\n const handlePublishClose = useCallback(() => {\n setIsPublishDialogOpen(false);\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 = t('bigconsole.dashboards.view.pageName', 'Page {{count}}', { count: 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, t]);\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: t('bigconsole.dashboards.view.pageCopyName', '{{name}} (Copy)', {\n name: sourcePage.name ?? '',\n }),\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, t]\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 // Permission read gate — wait for the provider to resolve before deciding,\n // then block the board for callers without dashboard read access. Placed\n // ahead of error/not-found/loading returns so an unauthorized caller never\n // sees dashboard contents; those returns remain reachable once read is allowed.\n if (permissionsLoading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (!canRead) {\n return (\n <AccessDenied\n title={t('bigconsole.dashboards.view.accessDeniedTitle', 'Access Denied')}\n description={t(\n 'bigconsole.dashboards.view.accessDeniedDescription',\n \"You don't have permission to view this dashboard.\"\n )}\n />\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex h-full flex-col items-center justify-center\" style={{ padding: 'var(--space-pagePadding)' }}>\n <Section\n headingLevel={2}\n tone=\"danger\"\n title={t('bigconsole.dashboards.view.errorTitle', 'Error loading dashboard')}\n description={<span className=\"break-words\">{error.message}</span>}\n className=\"w-full max-w-md\"\n data-testid=\"dashboard-view-error\"\n actions={\n <>\n <Button size=\"sm\" onClick={() => fetchDashboard(dashboardId!)}>\n {t('bigconsole.dashboards.view.retry', 'Retry')}\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => navigate('/dashboards')}>\n {t('bigconsole.dashboards.view.backToDashboards', 'Back to Dashboards')}\n </Button>\n </>\n }\n />\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 <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('bigconsole.dashboards.view.notFoundTitle', 'Dashboard not found')}\n description={\n <>\n {t(\n 'bigconsole.dashboards.view.notFoundDescription',\n \"The dashboard you're looking for doesn't exist or has been deleted.\"\n )}\n <span className=\"mt-2 block font-mono text-xs text-text-muted break-all\">\n {t('bigconsole.dashboards.view.idLabel', 'ID: {{id}}', { id: dashboardId ?? '' })}\n </span>\n </>\n }\n action={\n <Button onClick={() => navigate('/dashboards')}>\n {t('bigconsole.dashboards.view.backToDashboards', 'Back to Dashboards')}\n </Button>\n }\n />\n </div>\n );\n }\n\n // Loading state\n if (loading || !currentDashboard) {\n return (\n <div className=\"flex items-center justify-center h-full\" role=\"status\" aria-live=\"polite\">\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\">\n {t('bigconsole.dashboards.view.loading', 'Loading dashboard...')}\n </p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"h-full flex flex-col overflow-hidden\">\n {/*\n Route identity strip. This is the ONLY place the board is named on this\n route — the toolbar below it deliberately carries no title, so the user\n never sees the same name twice in two stacked bands.\n */}\n <Section\n emphasis=\"bare\"\n padding=\"none\"\n data-testid=\"dashboard-route-header\"\n // Landmark name only; no catalog key exists for it yet.\n aria-label=\"Dashboard\"\n className=\"flex-shrink-0 border-b border-[var(--color-border-subtle)] bg-bg-surface px-3 py-2\"\n headerClassName=\"\"\n header={\n <div className=\"flex flex-wrap items-center gap-control-gap\">\n <Button\n onClick={handleBack}\n variant=\"ghost\"\n size=\"sm\"\n title={t('bigconsole.dashboards.view.backToDashboards', 'Back to Dashboards')}\n aria-label={t('bigconsole.dashboards.view.backToDashboards', 'Back to Dashboards')}\n className=\"flex-shrink-0\"\n >\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n {t('bigconsole.dashboards.view.back', 'Back')}\n </Button>\n <h1\n className=\"min-w-0 flex-1 truncate text-sm font-semibold text-text-primary\"\n title={currentDashboard.name}\n >\n {currentDashboard.name}\n </h1>\n {/* Live canvas toggle — watch server-side pipeline builds appear without a reload */}\n <div className=\"flex-shrink-0\">\n <LiveCanvasToggle isLive={isLive} onToggle={() => setIsLive((prev) => !prev)} />\n </div>\n </div>\n }\n />\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 // Gate editor/action controls: DashboardToolbar renders each button\n // only when its handler is defined, so passing undefined hides the\n // control for callers lacking the permission.\n onSave={canUpdate ? handleSave : undefined}\n isSaving={isSaving}\n onExport={canExport ? handleExport : undefined}\n onPublishTemplate={canPublish ? handlePublishTemplate : undefined}\n // Page tabs props\n pages={(currentDashboard.pages || []).map((page, index) => ({\n id: page.id,\n name: page.name || t('bigconsole.dashboards.view.pageName', 'Page {{count}}', { count: 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\n {/* Publish as Template */}\n <PublishToStoreDialog\n isOpen={isPublishDialogOpen}\n dashboardId={currentDashboard.id}\n dashboardName={currentDashboard.name}\n dashboardDescription={currentDashboard.description}\n onClose={handlePublishClose}\n />\n </div>\n );\n});\n\nexport default DashboardViewPage;\n"],"mappings":"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"}