@burdenoff/microfe-bigconsole 2026.930.1 → 2026.930.2

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 (34) hide show
  1. package/dist/bigconsole/components/DrilldownDashboardRenderer.js +74 -50
  2. package/dist/bigconsole/components/DrilldownDashboardRenderer.js.map +1 -1
  3. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +9 -4
  4. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
  5. package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js +68 -40
  6. package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js.map +1 -1
  7. package/dist/bigconsole/components/embed/drilldown.js +26 -0
  8. package/dist/bigconsole/components/embed/drilldown.js.map +1 -0
  9. package/dist/bigconsole/components/embed/index.js +4 -3
  10. package/dist/bigconsole/components/embed/types.js.map +1 -1
  11. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +307 -233
  12. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
  13. package/dist/bigconsole/components/widgets/PropertiesPanel.js +230 -220
  14. package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
  15. package/dist/bigconsole/components/widgets/WidgetWrapper.js +18 -19
  16. package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
  17. package/dist/bigconsole/context/BigConsoleContext.js +4 -1
  18. package/dist/bigconsole/context/BigConsoleContext.js.map +1 -1
  19. package/dist/bigconsole/hooks/useActivePage.js +25 -0
  20. package/dist/bigconsole/hooks/useActivePage.js.map +1 -0
  21. package/dist/bigconsole/hooks/useDrilldown.js +118 -71
  22. package/dist/bigconsole/hooks/useDrilldown.js.map +1 -1
  23. package/dist/bigconsole/hooks/useDrilldownOperations.js +32 -26
  24. package/dist/bigconsole/hooks/useDrilldownOperations.js.map +1 -1
  25. package/dist/bigconsole/hooks/useFilterUrlSync.js +1 -1
  26. package/dist/bigconsole/hooks/useFilterUrlSync.js.map +1 -1
  27. package/dist/bigconsole/pages/DashboardBuilderPage.js +190 -185
  28. package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
  29. package/dist/bigconsole/pages/DashboardViewPage.js +224 -217
  30. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  31. package/dist/generated/wspace-operations.js +132 -86
  32. package/dist/generated/wspace-operations.js.map +1 -1
  33. package/dist/generated/wspace-types.js.map +1 -1
  34. package/package.json +1 -1
@@ -2,12 +2,13 @@ import { GetDashboardDocument as e, ListWidgetsByDashboardDocument as t } from "
2
2
  import { Section as n, SectionStack as r } from "./common/Section.js";
3
3
  import "./common/index.js";
4
4
  import { mapFromFederationWidgetType as i } from "../utils/widgetTypeMapping.js";
5
- import a from "./widgets/WidgetWrapper.js";
6
- import { memo as o, useEffect as s, useState as c } from "react";
7
- import { useApolloClient as l } from "@apollo/client/react";
8
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
5
+ import { MODAL_TARGET_PAGE_PARAM as a } from "../hooks/useDrilldown.js";
6
+ import o from "./widgets/WidgetWrapper.js";
7
+ import { memo as s, useEffect as c, useState as l } from "react";
8
+ import { useApolloClient as u } from "@apollo/client/react";
9
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
9
10
  //#region src/bigconsole/components/DrilldownDashboardRenderer.tsx
10
- function f(e, t) {
11
+ function p(e, t) {
11
12
  let n = t * 4, r = {
12
13
  x: 0,
13
14
  y: n,
@@ -47,72 +48,88 @@ function f(e, t) {
47
48
  updatedAt: e.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
48
49
  };
49
50
  }
50
- var p = o(function({ dashboardId: i, params: a }) {
51
- let o = l(), [p, h] = c(!0), [g, _] = c(null), [v, y] = c(null), [b, x] = c([]);
52
- if (s(() => {
51
+ function m(e, t) {
52
+ let n = {
53
+ x: 0,
54
+ y: t * 4,
55
+ width: 12,
56
+ height: 4
57
+ };
58
+ return {
59
+ ...e,
60
+ position: n,
61
+ positionX: 0,
62
+ positionY: n.y,
63
+ positionWidth: 12,
64
+ positionHeight: 4
65
+ };
66
+ }
67
+ var h = s(function({ dashboardId: i, params: o }) {
68
+ let s = u(), [h, _] = l(!0), [v, y] = l(null), [b, x] = l(null), [S, C] = l([]);
69
+ if (c(() => {
53
70
  if (!i) {
54
- h(!1);
71
+ _(!1);
55
72
  return;
56
73
  }
57
74
  async function n() {
58
- h(!0), _(null);
75
+ _(!0), y(null);
59
76
  try {
60
- let n = (await o.query({
77
+ let n = (await s.query({
61
78
  query: e,
62
79
  variables: { id: i },
63
80
  fetchPolicy: "network-only"
64
81
  })).data?.getDashboard;
65
82
  if (!n) {
66
- _("Dashboard not found"), h(!1);
83
+ y("Dashboard not found"), _(!1);
67
84
  return;
68
85
  }
69
86
  let r = n.pages?.edges?.map((e) => ({
70
87
  id: e.node.id,
71
88
  name: e.node.name || "Page 1"
72
89
  })) || [];
73
- y({
90
+ x({
74
91
  id: n.id,
75
92
  name: n.name,
76
93
  description: n.description,
77
94
  pages: r
78
- }), x(((await o.query({
95
+ }), C(((await s.query({
79
96
  query: t,
80
97
  variables: { dashboardId: i },
81
98
  fetchPolicy: "network-only"
82
- })).data?.listWidgetsByDashboard || []).map((e, t) => f(e, t)));
99
+ })).data?.listWidgetsByDashboard || []).map((e, t) => p(e, t)));
83
100
  } catch (e) {
84
- _(e instanceof Error ? e.message : "Failed to load dashboard");
101
+ y(e instanceof Error ? e.message : "Failed to load dashboard");
85
102
  } finally {
86
- h(!1);
103
+ _(!1);
87
104
  }
88
105
  }
89
106
  n();
90
- }, [i, o]), p) return /* @__PURE__ */ u("div", {
107
+ }, [i, s]), h) return /* @__PURE__ */ d("div", {
91
108
  className: "flex items-center justify-center h-full",
92
- children: /* @__PURE__ */ d("div", {
109
+ children: /* @__PURE__ */ f("div", {
93
110
  className: "flex flex-col items-center gap-3",
94
- children: [/* @__PURE__ */ u("div", { className: "w-8 h-8 border-2 border-action-primary-bg border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ u("p", {
111
+ children: [/* @__PURE__ */ d("div", { className: "w-8 h-8 border-2 border-action-primary-bg border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ d("p", {
95
112
  className: "text-sm text-text-secondary",
96
113
  children: "Loading dashboard..."
97
114
  })]
98
115
  })
99
116
  });
100
- if (g) return /* @__PURE__ */ u("div", {
117
+ if (v) return /* @__PURE__ */ d("div", {
101
118
  className: "flex h-full items-center justify-center",
102
119
  role: "alert",
103
- children: /* @__PURE__ */ u(n, {
120
+ children: /* @__PURE__ */ d(n, {
104
121
  headingLevel: 3,
105
122
  tone: "danger",
106
123
  className: "w-full max-w-md",
107
124
  "data-testid": "drilldown-error",
108
125
  title: "Could not load this dashboard",
109
- description: g,
110
- icon: /* @__PURE__ */ u("svg", {
126
+ description: v,
127
+ icon: /* @__PURE__ */ d("svg", {
111
128
  className: "h-icon-md w-icon-md",
112
129
  fill: "none",
113
130
  stroke: "currentColor",
114
131
  viewBox: "0 0 24 24",
115
- children: /* @__PURE__ */ u("path", {
132
+ children: /* @__PURE__ */ d("path", {
116
133
  strokeLinecap: "round",
117
134
  strokeLinejoin: "round",
118
135
  strokeWidth: 1.5,
@@ -121,62 +138,69 @@ var p = o(function({ dashboardId: i, params: a }) {
121
138
  })
122
139
  })
123
140
  });
124
- if (!v || b.length === 0) return /* @__PURE__ */ u("div", {
141
+ if (!b || S.length === 0) return /* @__PURE__ */ d("div", {
125
142
  className: "flex items-center justify-center h-full",
126
- children: /* @__PURE__ */ d("div", {
143
+ children: /* @__PURE__ */ f("div", {
127
144
  className: "flex flex-col items-center gap-3 text-center",
128
- children: [/* @__PURE__ */ u("svg", {
145
+ children: [/* @__PURE__ */ d("svg", {
129
146
  className: "w-12 h-12 text-text-muted",
130
147
  fill: "none",
131
148
  stroke: "currentColor",
132
149
  viewBox: "0 0 24 24",
133
- children: /* @__PURE__ */ u("path", {
150
+ children: /* @__PURE__ */ d("path", {
134
151
  strokeLinecap: "round",
135
152
  strokeLinejoin: "round",
136
153
  strokeWidth: 1.5,
137
154
  d: "M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"
138
155
  })
139
- }), /* @__PURE__ */ u("p", {
156
+ }), /* @__PURE__ */ d("p", {
140
157
  className: "text-sm text-text-secondary",
141
- children: v ? "This dashboard has no widgets" : "Dashboard not found"
158
+ children: b ? "This dashboard has no widgets" : "Dashboard not found"
142
159
  })]
143
160
  })
144
161
  });
145
- let S = Object.entries(a).filter(([e]) => !e.startsWith("__"));
146
- return /* @__PURE__ */ d(r, {
162
+ let w = Object.entries(o).filter(([e]) => !e.startsWith("__")), T = o[a], E = b.pages.find((e) => e.id === T) ?? b.pages[0] ?? null, D = (E ? S.filter((e) => !e.pageId || e.pageId === E.id) : S).map((e, t) => m(e, t));
163
+ return /* @__PURE__ */ f(r, {
147
164
  className: "w-full",
148
165
  children: [
149
- /* @__PURE__ */ u(n, {
166
+ /* @__PURE__ */ d(n, {
150
167
  emphasis: "bare",
151
168
  padding: "none",
152
169
  headingLevel: 3,
153
- title: v.name,
154
- description: v.description,
170
+ title: b.name,
171
+ description: b.pages.length > 1 && E ? /* @__PURE__ */ d("span", {
172
+ "data-testid": "drilldown-dashboard-page",
173
+ children: E.name
174
+ }) : b.description,
155
175
  "data-testid": "drilldown-dashboard-header"
156
176
  }),
157
- S.length > 0 && /* @__PURE__ */ u(n, {
177
+ w.length > 0 && /* @__PURE__ */ d(n, {
158
178
  emphasis: "muted",
159
179
  padding: "compact",
160
180
  headingLevel: 3,
161
181
  title: "Filtered by",
162
182
  "data-testid": "drilldown-params",
163
183
  bodyClassName: "flex flex-wrap gap-control-gap",
164
- children: S.map(([e, t]) => /* @__PURE__ */ d("span", {
184
+ children: w.map(([e, t]) => /* @__PURE__ */ f("span", {
165
185
  className: "inline-flex items-center rounded-pill bg-bg-surface px-2 py-1 text-xs text-text-secondary",
166
- children: [/* @__PURE__ */ d("span", {
186
+ children: [/* @__PURE__ */ f("span", {
167
187
  className: "font-medium text-text-primary",
168
188
  children: [e, ":"]
169
- }), /* @__PURE__ */ u("span", {
189
+ }), /* @__PURE__ */ d("span", {
170
190
  className: "ml-1",
171
191
  children: typeof t == "object" && t ? JSON.stringify(t) : String(t)
172
192
  })]
173
193
  }, e))
174
194
  }),
175
- /* @__PURE__ */ u("div", {
195
+ /* @__PURE__ */ f("div", {
176
196
  className: "flex flex-col gap-form-gap",
177
- children: b.map((e) => {
197
+ children: [D.length === 0 && /* @__PURE__ */ d("p", {
198
+ className: "text-sm text-text-secondary",
199
+ "data-testid": "drilldown-page-empty",
200
+ children: "This page has no widgets"
201
+ }), D.map((e) => {
178
202
  let t = e.config?._internalWidgetType, n = e.type;
179
- return /* @__PURE__ */ u("div", {
203
+ return /* @__PURE__ */ d("div", {
180
204
  className: "min-w-0 overflow-hidden rounded-card border border-[var(--color-border-subtle)] bg-bg-surface",
181
205
  style: n === "LINE_CHART" || n === "chart" || t === "chart" || t === "funnel_chart" ? { height: "clamp(220px, 45vh, 350px)" } : n === "TABLE" || n === "table" || t === "table" || t === "list" || t === "pivot_table" ? {
182
206
  minHeight: "min(250px, 40vh)",
@@ -185,19 +209,19 @@ var p = o(function({ dashboardId: i, params: a }) {
185
209
  minHeight: "min(120px, 25vh)",
186
210
  maxHeight: "min(300px, 50vh)"
187
211
  },
188
- children: /* @__PURE__ */ u(m, {
212
+ children: /* @__PURE__ */ d(g, {
189
213
  widget: e,
190
- filterValues: a
214
+ filterValues: o
191
215
  })
192
216
  }, e.id);
193
- })
217
+ })]
194
218
  })
195
219
  ]
196
220
  });
197
- }), m = o(function({ widget: e, filterValues: t }) {
198
- return /* @__PURE__ */ u("div", {
221
+ }), g = s(function({ widget: e, filterValues: t }) {
222
+ return /* @__PURE__ */ d("div", {
199
223
  className: "h-full [&_[data-widget-id]]:border-transparent [&_[data-widget-id]:hover]:border-transparent",
200
- children: /* @__PURE__ */ u(a, {
224
+ children: /* @__PURE__ */ d(o, {
201
225
  widget: e,
202
226
  isLoading: !1,
203
227
  error: null,
@@ -206,6 +230,6 @@ var p = o(function({ dashboardId: i, params: a }) {
206
230
  });
207
231
  });
208
232
  //#endregion
209
- export { p as default };
233
+ export { h as default };
210
234
 
211
235
  //# sourceMappingURL=DrilldownDashboardRenderer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DrilldownDashboardRenderer.js","names":[],"sources":["../../../src/bigconsole/components/DrilldownDashboardRenderer.tsx"],"sourcesContent":["/**\n * DrilldownDashboardRenderer Component\n *\n * Renders a dashboard inside the DrilldownModal.\n * Fetches dashboard data and displays widgets with passed parameters.\n */\n\nimport { type FC, memo, useEffect, useState } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport type { Widget } from '../types';\nimport { WidgetWrapper } from './widgets/WidgetWrapper';\nimport { Section, SectionStack } from './common';\nimport { ListWidgetsByDashboardDocument, GetDashboardDocument } from '../../generated/wspace-operations';\nimport { mapFromFederationWidgetType } from '../utils/widgetTypeMapping';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DrilldownDashboardRendererProps {\n /** Target dashboard ID to render */\n dashboardId: string;\n /** Parameters passed from drilldown (can be used for filtering) */\n params: Record<string, unknown>;\n}\n\ninterface DashboardData {\n id: string;\n name: string;\n description?: string;\n pages: Array<{\n id: string;\n name: string;\n }>;\n}\n\n// ============================================================================\n// Helper: Normalize widget from GraphQL\n// ============================================================================\n\nfunction normalizeWidget(raw: Record<string, unknown>, index: number): Widget {\n // For drilldown modal, stack widgets vertically with compact sizing\n const posX = 0;\n const posY = index * 4; // Stack vertically, 4 row units per widget\n const posW = 12; // Full width\n const posH = 4; // Better height for widget content\n\n const position = { x: posX, y: posY, width: posW, height: posH };\n\n // Get the config object\n const config = (raw.config as Record<string, unknown>) || {};\n\n // Map widget type correctly: check _internalWidgetType first, then map from federation type\n // This is critical — federation types like 'KPI' must be mapped to internal types like 'metric_card'\n const internalType = config._internalWidgetType as string | undefined;\n const widgetType = internalType || mapFromFederationWidgetType((raw.type as string) || 'KPI');\n\n return {\n id: (raw.id as string) ?? '',\n pageId: (raw.pageId as string) || '',\n dashboardId: raw.dashboardId as string | undefined,\n type: widgetType,\n title: (raw.title as string) || 'Untitled Widget',\n renderer: raw.renderer || 'BIGCONSOLE',\n template: raw.template || null,\n dataSinkId: raw.dataSinkId || null,\n datasetId: raw.datasetId || null,\n parserId: raw.parserId || null,\n parserRules: raw.parserRules || null,\n config: config,\n position: position,\n positionX: posX,\n positionY: posY,\n positionWidth: posW,\n positionHeight: posH,\n positionResponsive: raw.positionResponsive as Widget['positionResponsive'],\n rowOrder: (raw.rowOrder as number | null) ?? null,\n refreshInterval: raw.refreshInterval,\n lastRefreshedAt: raw.lastRefreshedAt,\n cacheTTL: (raw.cacheTTL as number | null) ?? null,\n drilldown: raw.drilldown as Widget['drilldown'],\n actions: (raw.actions as Widget['actions']) || [],\n translations: (raw.translations as Widget['translations']) || [],\n metadata: raw.metadata as Record<string, unknown> | undefined,\n context: raw.context,\n // Include widget data from backend (critical for showing real data in modal)\n data: raw.data || null,\n createdAt: (raw.createdAt as string) || new Date().toISOString(),\n updatedAt: (raw.updatedAt as string) || new Date().toISOString(),\n } as Widget;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DrilldownDashboardRenderer: FC<DrilldownDashboardRendererProps> = memo(\n function DrilldownDashboardRenderer({ dashboardId, params }) {\n const apolloClient = useApolloClient();\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [dashboard, setDashboard] = useState<DashboardData | null>(null);\n const [widgets, setWidgets] = useState<Widget[]>([]);\n\n // Fetch dashboard and widgets — only when dashboardId is provided\n useEffect(() => {\n if (!dashboardId) {\n setLoading(false);\n return;\n }\n\n async function fetchData() {\n setLoading(true);\n setError(null);\n\n try {\n // Fetch dashboard info\n interface DashboardQueryResult {\n getDashboard: {\n id: string;\n name: string;\n description?: string;\n pages?: {\n edges?: Array<{ node: { id: string; name?: string } }>;\n };\n } | null;\n }\n\n const dashboardResult = await apolloClient.query<DashboardQueryResult>({\n query: GetDashboardDocument,\n variables: { id: dashboardId },\n fetchPolicy: 'network-only',\n });\n\n const dashboardData = dashboardResult.data?.getDashboard;\n if (!dashboardData) {\n setError('Dashboard not found');\n setLoading(false);\n return;\n }\n\n // Normalize dashboard\n const pages =\n dashboardData.pages?.edges?.map((edge) => ({\n id: edge.node.id,\n name: edge.node.name || 'Page 1',\n })) || [];\n\n setDashboard({\n id: dashboardData.id,\n name: dashboardData.name,\n description: dashboardData.description,\n pages,\n });\n\n // Fetch widgets for the dashboard\n const widgetsResult = await apolloClient.query<{ listWidgetsByDashboard: Record<string, unknown>[] }>({\n query: ListWidgetsByDashboardDocument,\n variables: { dashboardId },\n fetchPolicy: 'network-only',\n });\n\n const widgetsList = widgetsResult.data?.listWidgetsByDashboard || [];\n setWidgets(widgetsList.map((w, i) => normalizeWidget(w, i)));\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load dashboard');\n } finally {\n setLoading(false);\n }\n }\n\n fetchData();\n }, [dashboardId, apolloClient]);\n\n // Loading state\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"flex flex-col items-center gap-3\">\n <div className=\"w-8 h-8 border-2 border-action-primary-bg border-t-transparent rounded-full animate-spin\" />\n <p className=\"text-sm text-text-secondary\">Loading dashboard...</p>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex h-full items-center justify-center\" role=\"alert\">\n <Section\n headingLevel={3}\n tone=\"danger\"\n className=\"w-full max-w-md\"\n data-testid=\"drilldown-error\"\n title=\"Could not load this dashboard\"\n description={error}\n icon={\n <svg className=\"h-icon-md w-icon-md\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n }\n />\n </div>\n );\n }\n\n // Empty state\n if (!dashboard || widgets.length === 0) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"flex flex-col items-center gap-3 text-center\">\n <svg className=\"w-12 h-12 text-text-muted\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n <p className=\"text-sm text-text-secondary\">\n {dashboard ? 'This dashboard has no widgets' : 'Dashboard not found'}\n </p>\n </div>\n </div>\n );\n }\n\n const visibleParams = Object.entries(params).filter(([key]) => !key.startsWith('__'));\n\n return (\n <SectionStack className=\"w-full\">\n {/* Which board you drilled into — identity, not content. */}\n <Section\n emphasis=\"bare\"\n padding=\"none\"\n headingLevel={3}\n title={dashboard.name}\n description={dashboard.description}\n data-testid=\"drilldown-dashboard-header\"\n />\n\n {/* The filter context you arrived with: supporting detail, recessed. */}\n {visibleParams.length > 0 && (\n <Section\n emphasis=\"muted\"\n padding=\"compact\"\n headingLevel={3}\n title=\"Filtered by\"\n data-testid=\"drilldown-params\"\n bodyClassName=\"flex flex-wrap gap-control-gap\"\n >\n {visibleParams.map(([key, value]) => (\n <span\n key={key}\n className=\"inline-flex items-center rounded-pill bg-bg-surface px-2 py-1 text-xs text-text-secondary\"\n >\n <span className=\"font-medium text-text-primary\">{key}:</span>\n <span className=\"ml-1\">\n {typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value)}\n </span>\n </span>\n ))}\n </Section>\n )}\n\n {/* Widgets List - simple vertical layout for modal */}\n <div className=\"flex flex-col gap-form-gap\">\n {widgets.map((widget) => {\n // Chart widgets (LINE_CHART / chart) need explicit pixel height because\n // Recharts ResponsiveContainer height=\"100%\" requires a parent with\n // a definite height — minHeight alone doesn't work.\n const internalType = (widget.config as Record<string, unknown>)?._internalWidgetType as string | undefined;\n const widgetType = widget.type as string;\n const isChartWidget =\n widgetType === 'LINE_CHART' ||\n widgetType === 'chart' ||\n internalType === 'chart' ||\n internalType === 'funnel_chart';\n const isTableWidget =\n widgetType === 'TABLE' ||\n widgetType === 'table' ||\n internalType === 'table' ||\n internalType === 'list' ||\n internalType === 'pivot_table';\n\n // Charts need an explicit height (Recharts' ResponsiveContainer needs a\n // definite parent). Clamping against the viewport keeps a 350px chart\n // plus a 500px table from filling a phone screen twice over.\n const heightStyle = isChartWidget\n ? { height: 'clamp(220px, 45vh, 350px)' }\n : isTableWidget\n ? { minHeight: 'min(250px, 40vh)', maxHeight: 'min(500px, 65vh)' }\n : { minHeight: 'min(120px, 25vh)', maxHeight: 'min(300px, 50vh)' };\n\n return (\n <div\n key={widget.id}\n className=\"min-w-0 overflow-hidden rounded-card border border-[var(--color-border-subtle)] bg-bg-surface\"\n style={heightStyle}\n >\n <WidgetInModal widget={widget} filterValues={params} />\n </div>\n );\n })}\n </div>\n </SectionStack>\n );\n }\n);\n\n// ============================================================================\n// Widget In Modal Component\n// ============================================================================\n\ninterface WidgetInModalProps {\n widget: Widget;\n filterValues?: Record<string, unknown>;\n}\n\nconst WidgetInModal: FC<WidgetInModalProps> = memo(function WidgetInModal({ widget, filterValues }) {\n return (\n <div className=\"h-full [&_[data-widget-id]]:border-transparent [&_[data-widget-id]:hover]:border-transparent\">\n <WidgetWrapper widget={widget} isLoading={false} error={null} filterValues={filterValues} />\n </div>\n );\n});\n\nexport default DrilldownDashboardRenderer;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DrilldownDashboardRenderer.js","names":[],"sources":["../../../src/bigconsole/components/DrilldownDashboardRenderer.tsx"],"sourcesContent":["/**\n * DrilldownDashboardRenderer Component\n *\n * Renders a dashboard inside the DrilldownModal.\n * Fetches dashboard data and displays the target page's widgets (the page named\n * by the drilldown, else the first page) with passed parameters (BOFF-7625).\n */\n\nimport { type FC, memo, useEffect, useState } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport type { Widget } from '../types';\nimport { WidgetWrapper } from './widgets/WidgetWrapper';\nimport { Section, SectionStack } from './common';\nimport { ListWidgetsByDashboardDocument, GetDashboardDocument } from '../../generated/wspace-operations';\nimport { mapFromFederationWidgetType } from '../utils/widgetTypeMapping';\nimport { MODAL_TARGET_PAGE_PARAM } from '../hooks/useDrilldown';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DrilldownDashboardRendererProps {\n /** Target dashboard ID to render */\n dashboardId: string;\n /** Parameters passed from drilldown (can be used for filtering) */\n params: Record<string, unknown>;\n}\n\ninterface DashboardData {\n id: string;\n name: string;\n description?: string;\n pages: Array<{\n id: string;\n name: string;\n }>;\n}\n\n// ============================================================================\n// Helper: Normalize widget from GraphQL\n// ============================================================================\n\nfunction normalizeWidget(raw: Record<string, unknown>, index: number): Widget {\n // For drilldown modal, stack widgets vertically with compact sizing\n const posX = 0;\n const posY = index * 4; // Stack vertically, 4 row units per widget\n const posW = 12; // Full width\n const posH = 4; // Better height for widget content\n\n const position = { x: posX, y: posY, width: posW, height: posH };\n\n // Get the config object\n const config = (raw.config as Record<string, unknown>) || {};\n\n // Map widget type correctly: check _internalWidgetType first, then map from federation type\n // This is critical — federation types like 'KPI' must be mapped to internal types like 'metric_card'\n const internalType = config._internalWidgetType as string | undefined;\n const widgetType = internalType || mapFromFederationWidgetType((raw.type as string) || 'KPI');\n\n return {\n id: (raw.id as string) ?? '',\n pageId: (raw.pageId as string) || '',\n dashboardId: raw.dashboardId as string | undefined,\n type: widgetType,\n title: (raw.title as string) || 'Untitled Widget',\n renderer: raw.renderer || 'BIGCONSOLE',\n template: raw.template || null,\n dataSinkId: raw.dataSinkId || null,\n datasetId: raw.datasetId || null,\n parserId: raw.parserId || null,\n parserRules: raw.parserRules || null,\n config: config,\n position: position,\n positionX: posX,\n positionY: posY,\n positionWidth: posW,\n positionHeight: posH,\n positionResponsive: raw.positionResponsive as Widget['positionResponsive'],\n rowOrder: (raw.rowOrder as number | null) ?? null,\n refreshInterval: raw.refreshInterval,\n lastRefreshedAt: raw.lastRefreshedAt,\n cacheTTL: (raw.cacheTTL as number | null) ?? null,\n drilldown: raw.drilldown as Widget['drilldown'],\n actions: (raw.actions as Widget['actions']) || [],\n translations: (raw.translations as Widget['translations']) || [],\n metadata: raw.metadata as Record<string, unknown> | undefined,\n context: raw.context,\n // Include widget data from backend (critical for showing real data in modal)\n data: raw.data || null,\n createdAt: (raw.createdAt as string) || new Date().toISOString(),\n updatedAt: (raw.updatedAt as string) || new Date().toISOString(),\n } as Widget;\n}\n\n/** Place a widget at a given slot of the modal's single-column stack. */\nfunction normalizeStacking(widget: Widget, index: number): Widget {\n const position = { x: 0, y: index * 4, width: 12, height: 4 };\n return { ...widget, position, positionX: 0, positionY: position.y, positionWidth: 12, positionHeight: 4 };\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DrilldownDashboardRenderer: FC<DrilldownDashboardRendererProps> = memo(\n function DrilldownDashboardRenderer({ dashboardId, params }) {\n const apolloClient = useApolloClient();\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [dashboard, setDashboard] = useState<DashboardData | null>(null);\n const [widgets, setWidgets] = useState<Widget[]>([]);\n\n // Fetch dashboard and widgets — only when dashboardId is provided\n useEffect(() => {\n if (!dashboardId) {\n setLoading(false);\n return;\n }\n\n async function fetchData() {\n setLoading(true);\n setError(null);\n\n try {\n // Fetch dashboard info\n interface DashboardQueryResult {\n getDashboard: {\n id: string;\n name: string;\n description?: string;\n pages?: {\n edges?: Array<{ node: { id: string; name?: string } }>;\n };\n } | null;\n }\n\n const dashboardResult = await apolloClient.query<DashboardQueryResult>({\n query: GetDashboardDocument,\n variables: { id: dashboardId },\n fetchPolicy: 'network-only',\n });\n\n const dashboardData = dashboardResult.data?.getDashboard;\n if (!dashboardData) {\n setError('Dashboard not found');\n setLoading(false);\n return;\n }\n\n // Normalize dashboard\n const pages =\n dashboardData.pages?.edges?.map((edge) => ({\n id: edge.node.id,\n name: edge.node.name || 'Page 1',\n })) || [];\n\n setDashboard({\n id: dashboardData.id,\n name: dashboardData.name,\n description: dashboardData.description,\n pages,\n });\n\n // Fetch widgets for the dashboard\n const widgetsResult = await apolloClient.query<{ listWidgetsByDashboard: Record<string, unknown>[] }>({\n query: ListWidgetsByDashboardDocument,\n variables: { dashboardId },\n fetchPolicy: 'network-only',\n });\n\n const widgetsList = widgetsResult.data?.listWidgetsByDashboard || [];\n setWidgets(widgetsList.map((w, i) => normalizeWidget(w, i)));\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load dashboard');\n } finally {\n setLoading(false);\n }\n }\n\n fetchData();\n }, [dashboardId, apolloClient]);\n\n // Loading state\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"flex flex-col items-center gap-3\">\n <div className=\"w-8 h-8 border-2 border-action-primary-bg border-t-transparent rounded-full animate-spin\" />\n <p className=\"text-sm text-text-secondary\">Loading dashboard...</p>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex h-full items-center justify-center\" role=\"alert\">\n <Section\n headingLevel={3}\n tone=\"danger\"\n className=\"w-full max-w-md\"\n data-testid=\"drilldown-error\"\n title=\"Could not load this dashboard\"\n description={error}\n icon={\n <svg className=\"h-icon-md w-icon-md\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n }\n />\n </div>\n );\n }\n\n // Empty state\n if (!dashboard || widgets.length === 0) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"flex flex-col items-center gap-3 text-center\">\n <svg className=\"w-12 h-12 text-text-muted\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z\"\n />\n </svg>\n <p className=\"text-sm text-text-secondary\">\n {dashboard ? 'This dashboard has no widgets' : 'Dashboard not found'}\n </p>\n </div>\n </div>\n );\n }\n\n const visibleParams = Object.entries(params).filter(([key]) => !key.startsWith('__'));\n\n // Show one page: the drilldown's target page when it still exists, else the\n // first page. Widgets without a page id (older payloads) are always shown.\n const requestedPageId = params[MODAL_TARGET_PAGE_PARAM];\n const activePage = dashboard.pages.find((page) => page.id === requestedPageId) ?? dashboard.pages[0] ?? null;\n const pageWidgets = activePage\n ? widgets.filter((widget) => !widget.pageId || widget.pageId === activePage.id)\n : widgets;\n // Re-flow the page's widgets top to bottom (the stacking was computed over\n // the whole dashboard).\n const stackedWidgets = pageWidgets.map((widget, index) => normalizeStacking(widget, index));\n\n return (\n <SectionStack className=\"w-full\">\n {/* Which board you drilled into — identity, not content. */}\n <Section\n emphasis=\"bare\"\n padding=\"none\"\n headingLevel={3}\n title={dashboard.name}\n description={\n dashboard.pages.length > 1 && activePage ? (\n <span data-testid=\"drilldown-dashboard-page\">{activePage.name}</span>\n ) : (\n dashboard.description\n )\n }\n data-testid=\"drilldown-dashboard-header\"\n />\n\n {/* The filter context you arrived with: supporting detail, recessed. */}\n {visibleParams.length > 0 && (\n <Section\n emphasis=\"muted\"\n padding=\"compact\"\n headingLevel={3}\n title=\"Filtered by\"\n data-testid=\"drilldown-params\"\n bodyClassName=\"flex flex-wrap gap-control-gap\"\n >\n {visibleParams.map(([key, value]) => (\n <span\n key={key}\n className=\"inline-flex items-center rounded-pill bg-bg-surface px-2 py-1 text-xs text-text-secondary\"\n >\n <span className=\"font-medium text-text-primary\">{key}:</span>\n <span className=\"ml-1\">\n {typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value)}\n </span>\n </span>\n ))}\n </Section>\n )}\n\n {/* Widgets List - simple vertical layout for modal */}\n <div className=\"flex flex-col gap-form-gap\">\n {stackedWidgets.length === 0 && (\n <p className=\"text-sm text-text-secondary\" data-testid=\"drilldown-page-empty\">\n This page has no widgets\n </p>\n )}\n {stackedWidgets.map((widget) => {\n // Chart widgets (LINE_CHART / chart) need explicit pixel height because\n // Recharts ResponsiveContainer height=\"100%\" requires a parent with\n // a definite height — minHeight alone doesn't work.\n const internalType = (widget.config as Record<string, unknown>)?._internalWidgetType as string | undefined;\n const widgetType = widget.type as string;\n const isChartWidget =\n widgetType === 'LINE_CHART' ||\n widgetType === 'chart' ||\n internalType === 'chart' ||\n internalType === 'funnel_chart';\n const isTableWidget =\n widgetType === 'TABLE' ||\n widgetType === 'table' ||\n internalType === 'table' ||\n internalType === 'list' ||\n internalType === 'pivot_table';\n\n // Charts need an explicit height (Recharts' ResponsiveContainer needs a\n // definite parent). Clamping against the viewport keeps a 350px chart\n // plus a 500px table from filling a phone screen twice over.\n const heightStyle = isChartWidget\n ? { height: 'clamp(220px, 45vh, 350px)' }\n : isTableWidget\n ? { minHeight: 'min(250px, 40vh)', maxHeight: 'min(500px, 65vh)' }\n : { minHeight: 'min(120px, 25vh)', maxHeight: 'min(300px, 50vh)' };\n\n return (\n <div\n key={widget.id}\n className=\"min-w-0 overflow-hidden rounded-card border border-[var(--color-border-subtle)] bg-bg-surface\"\n style={heightStyle}\n >\n <WidgetInModal widget={widget} filterValues={params} />\n </div>\n );\n })}\n </div>\n </SectionStack>\n );\n }\n);\n\n// ============================================================================\n// Widget In Modal Component\n// ============================================================================\n\ninterface WidgetInModalProps {\n widget: Widget;\n filterValues?: Record<string, unknown>;\n}\n\nconst WidgetInModal: FC<WidgetInModalProps> = memo(function WidgetInModal({ widget, filterValues }) {\n return (\n <div className=\"h-full [&_[data-widget-id]]:border-transparent [&_[data-widget-id]:hover]:border-transparent\">\n <WidgetWrapper widget={widget} isLoading={false} error={null} filterValues={filterValues} />\n </div>\n );\n});\n\nexport default DrilldownDashboardRenderer;\n"],"mappings":"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"}
@@ -38,10 +38,15 @@ var h = i(function({ className: i = "", currentDashboardId: h, currentDashboardN
38
38
  let n = x().path.slice(0, t), r = t, i = new URL(e.href, window.location.origin);
39
39
  i.searchParams.set("drilldown_depth", String(r)), n.length > 0 && i.searchParams.set("drilldown_path", encodeURIComponent(JSON.stringify(n))), t > 0 && n.length > 0 && i.searchParams.set("drilldown_parent", n[n.length - 1]), b(i.pathname + i.search);
40
40
  }, [x, b]), O = a(() => {
41
- let e = x();
42
- if (e.parent) {
43
- let t = e.path.slice(0, -1), n = e.depth - 1, r = new URL(`${v}/${e.parent}`, window.location.origin);
44
- r.searchParams.set("drilldown_depth", String(n)), t.length > 0 && (r.searchParams.set("drilldown_path", encodeURIComponent(JSON.stringify(t))), r.searchParams.set("drilldown_parent", t[t.length - 1])), b(r.pathname + r.search);
41
+ let e = window.history.state?.idx;
42
+ if (typeof e == "number" && e > 0) {
43
+ window.history.back();
44
+ return;
45
+ }
46
+ let t = x();
47
+ if (t.parent) {
48
+ let e = t.path.slice(0, -1), n = t.depth - 1, r = new URL(`${v}/${t.parent}`, window.location.origin);
49
+ r.searchParams.set("drilldown_depth", String(n)), e.length > 0 && (r.searchParams.set("drilldown_path", encodeURIComponent(JSON.stringify(e))), r.searchParams.set("drilldown_parent", e[e.length - 1])), b(r.pathname + r.search);
45
50
  } else if (T.length > 0) {
46
51
  let e = T[T.length - 1];
47
52
  D(e, T.length - 1);
@@ -1 +1 @@
1
- {"version":3,"file":"DrilldownBreadcrumb.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/DrilldownBreadcrumb.tsx"],"sourcesContent":["/**\n * DrilldownBreadcrumb Component\n *\n * Displays a breadcrumb trail showing the drilldown navigation path.\n * Allows users to navigate back up the drilldown hierarchy.\n *\n * URL Parameters Used:\n * - drilldown_depth: Current depth in drilldown hierarchy\n * - drilldown_parent: Parent dashboard ID\n * - drilldown_path: JSON array of dashboard IDs in the path\n */\n\nimport { memo, useCallback, useEffect, useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useBigConsoleNavigate } from '../../context';\nimport { useFilterUrlSync } from '../../hooks/useFilterUrlSync';\nimport { useDashboardStore } from '../../store';\nimport { ChevronRight, Home, ArrowLeft } from 'lucide-react';\nimport { SECTION_FOCUS_RING } from '../common';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface DrilldownBreadcrumbProps {\n /** Additional CSS classes */\n className?: string;\n /** Current dashboard ID */\n currentDashboardId?: string;\n /** Current dashboard name */\n currentDashboardName?: string;\n /** Whether to show the home/root link */\n showHome?: boolean;\n /** Base path for dashboard URLs */\n basePath?: string;\n}\n\ninterface BreadcrumbItem {\n id: string;\n name: string;\n href: string;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DrilldownBreadcrumb = memo(function DrilldownBreadcrumb({\n className = '',\n currentDashboardId,\n currentDashboardName,\n showHome = true,\n basePath = '/bigconsole/dashboards',\n}: DrilldownBreadcrumbProps) {\n const { t } = useI18n();\n const navigate = useBigConsoleNavigate();\n const { getDrilldownMetadata, isInDrilldown, drilldownDepth } = useFilterUrlSync();\n\n // Get dashboards from store for name lookup\n const dashboards = useDashboardStore((state) => state.dashboards);\n\n const [breadcrumbs, setBreadcrumbs] = useState<BreadcrumbItem[]>([]);\n\n // Build breadcrumb items from path\n useEffect(() => {\n if (!isInDrilldown) {\n setBreadcrumbs([]);\n return;\n }\n\n const metadata = getDrilldownMetadata();\n const { path } = metadata;\n\n if (path.length === 0) {\n setBreadcrumbs([]);\n return;\n }\n\n // Map path IDs to breadcrumb items\n const items: BreadcrumbItem[] = path.map((dashboardId) => {\n // Try to find dashboard name in store\n const dashboard = dashboards.get(dashboardId);\n const name =\n dashboard?.name ||\n t('bigconsole.dashboards.drilldownBreadcrumb.fallbackName', 'Dashboard {{id}}...', {\n id: dashboardId.slice(0, 8),\n });\n\n return {\n id: dashboardId,\n name,\n href: `${basePath}/${dashboardId}`,\n };\n });\n\n setBreadcrumbs(items);\n }, [isInDrilldown, getDrilldownMetadata, dashboards, basePath, t]);\n\n // Handle navigation to a breadcrumb item\n const handleNavigate = useCallback(\n (item: BreadcrumbItem, index: number) => {\n // Build URL preserving context up to this point\n const metadata = getDrilldownMetadata();\n const newPath = metadata.path.slice(0, index);\n const newDepth = index;\n\n // Build URL with updated drilldown params\n const url = new URL(item.href, window.location.origin);\n url.searchParams.set('drilldown_depth', String(newDepth));\n if (newPath.length > 0) {\n url.searchParams.set('drilldown_path', encodeURIComponent(JSON.stringify(newPath)));\n }\n if (index > 0 && newPath.length > 0) {\n url.searchParams.set('drilldown_parent', newPath[newPath.length - 1]);\n }\n\n navigate(url.pathname + url.search);\n },\n [getDrilldownMetadata, navigate]\n );\n\n // Handle back button click\n const handleBack = useCallback(() => {\n const metadata = getDrilldownMetadata();\n\n if (metadata.parent) {\n // Navigate to parent dashboard\n const newPath = metadata.path.slice(0, -1);\n const newDepth = metadata.depth - 1;\n\n const url = new URL(`${basePath}/${metadata.parent}`, window.location.origin);\n url.searchParams.set('drilldown_depth', String(newDepth));\n if (newPath.length > 0) {\n url.searchParams.set('drilldown_path', encodeURIComponent(JSON.stringify(newPath)));\n url.searchParams.set('drilldown_parent', newPath[newPath.length - 1]);\n }\n\n navigate(url.pathname + url.search);\n } else if (breadcrumbs.length > 0) {\n // Navigate to last breadcrumb\n const lastBreadcrumb = breadcrumbs[breadcrumbs.length - 1];\n handleNavigate(lastBreadcrumb, breadcrumbs.length - 1);\n }\n }, [getDrilldownMetadata, basePath, navigate, breadcrumbs, handleNavigate]);\n\n // Handle home click\n const handleHomeClick = useCallback(\n (e: React.MouseEvent) => {\n e.preventDefault();\n navigate(basePath);\n },\n [navigate, basePath]\n );\n\n // Don't render if not in drilldown mode\n if (!isInDrilldown) {\n return null;\n }\n\n return (\n <nav\n // Recessed on purpose: this is navigational CONTEXT, not chrome you act\n // on, so it reads a step below the route header and the toolbar it sits\n // between instead of being a third identical bar. Wraps rather than\n // overflowing once the trail gets deep on a narrow viewport.\n className={`flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 border-b border-[var(--color-border-subtle)] bg-bg-sunken px-4 py-1.5 ${className}`}\n aria-label={t('bigconsole.dashboards.drilldownBreadcrumb.ariaLabel', 'Drilldown breadcrumb')}\n data-testid=\"drilldown-breadcrumb\"\n >\n {/* Back button */}\n <button\n type=\"button\"\n onClick={handleBack}\n className={`flex min-h-control-sm items-center gap-1 rounded-button px-2 py-1 text-sm font-medium text-text-link motion-safe:transition-colors hover:bg-bg-accentSubtle ${SECTION_FOCUS_RING}`}\n aria-label={t('bigconsole.dashboards.drilldownBreadcrumb.back', 'Go back')}\n >\n <ArrowLeft className=\"w-4 h-4\" />\n <span className=\"hidden sm:inline\">{t('bigconsole.dashboards.drilldownBreadcrumb.backLabel', 'Back')}</span>\n </button>\n\n <span className=\"text-text-muted\" aria-hidden=\"true\">\n |\n </span>\n\n {/* Home link */}\n {showHome && (\n <>\n <a\n href={basePath}\n onClick={handleHomeClick}\n className={`flex min-h-control-sm items-center gap-1 rounded-button px-1 text-sm text-text-secondary motion-safe:transition-colors hover:text-text-primary ${SECTION_FOCUS_RING}`}\n aria-label={t('bigconsole.dashboards.drilldownBreadcrumb.home', 'Home')}\n >\n <Home className=\"w-4 h-4\" />\n <span className=\"hidden sm:inline\">\n {t('bigconsole.dashboards.drilldownBreadcrumb.dashboards', 'Dashboards')}\n </span>\n </a>\n <ChevronRight className=\"w-4 h-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n </>\n )}\n\n {/* Breadcrumb items */}\n {breadcrumbs.map((item, index) => (\n <div key={item.id} className=\"flex min-w-0 items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => handleNavigate(item, index)}\n className={`min-h-control-sm max-w-[150px] truncate rounded-button px-1 text-sm text-text-secondary motion-safe:transition-colors hover:text-text-primary hover:underline ${SECTION_FOCUS_RING}`}\n title={item.name}\n >\n {item.name}\n </button>\n <ChevronRight className=\"w-4 h-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n </div>\n ))}\n\n {/* Current dashboard (non-clickable) */}\n {currentDashboardName && (\n <span\n aria-current=\"page\"\n className=\"max-w-[200px] truncate text-sm font-medium text-text-primary\"\n title={currentDashboardName}\n >\n {currentDashboardName}\n </span>\n )}\n\n {/* Depth indicator */}\n <span className=\"ml-auto shrink-0 rounded-pill bg-bg-surface px-2 py-0.5 text-xs text-text-muted\">\n {t('bigconsole.dashboards.drilldownBreadcrumb.level', 'Level {{depth}}', { depth: drilldownDepth ?? '' })}\n </span>\n </nav>\n );\n});\n\nexport default DrilldownBreadcrumb;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DrilldownBreadcrumb.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/DrilldownBreadcrumb.tsx"],"sourcesContent":["/**\n * DrilldownBreadcrumb Component\n *\n * Displays a breadcrumb trail showing the drilldown navigation path.\n * Allows users to navigate back up the drilldown hierarchy.\n *\n * URL Parameters Used:\n * - drilldown_depth: Current depth in drilldown hierarchy\n * - drilldown_parent: Parent dashboard ID\n * - drilldown_path: JSON array of dashboard IDs in the path\n */\n\nimport { memo, useCallback, useEffect, useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useBigConsoleNavigate } from '../../context';\nimport { useFilterUrlSync } from '../../hooks/useFilterUrlSync';\nimport { useDashboardStore } from '../../store';\nimport { ChevronRight, Home, ArrowLeft } from 'lucide-react';\nimport { SECTION_FOCUS_RING } from '../common';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface DrilldownBreadcrumbProps {\n /** Additional CSS classes */\n className?: string;\n /** Current dashboard ID */\n currentDashboardId?: string;\n /** Current dashboard name */\n currentDashboardName?: string;\n /** Whether to show the home/root link */\n showHome?: boolean;\n /** Base path for dashboard URLs */\n basePath?: string;\n}\n\ninterface BreadcrumbItem {\n id: string;\n name: string;\n href: string;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const DrilldownBreadcrumb = memo(function DrilldownBreadcrumb({\n className = '',\n currentDashboardId,\n currentDashboardName,\n showHome = true,\n basePath = '/bigconsole/dashboards',\n}: DrilldownBreadcrumbProps) {\n const { t } = useI18n();\n const navigate = useBigConsoleNavigate();\n const { getDrilldownMetadata, isInDrilldown, drilldownDepth } = useFilterUrlSync();\n\n // Get dashboards from store for name lookup\n const dashboards = useDashboardStore((state) => state.dashboards);\n\n const [breadcrumbs, setBreadcrumbs] = useState<BreadcrumbItem[]>([]);\n\n // Build breadcrumb items from path\n useEffect(() => {\n if (!isInDrilldown) {\n setBreadcrumbs([]);\n return;\n }\n\n const metadata = getDrilldownMetadata();\n const { path } = metadata;\n\n if (path.length === 0) {\n setBreadcrumbs([]);\n return;\n }\n\n // Map path IDs to breadcrumb items\n const items: BreadcrumbItem[] = path.map((dashboardId) => {\n // Try to find dashboard name in store\n const dashboard = dashboards.get(dashboardId);\n const name =\n dashboard?.name ||\n t('bigconsole.dashboards.drilldownBreadcrumb.fallbackName', 'Dashboard {{id}}...', {\n id: dashboardId.slice(0, 8),\n });\n\n return {\n id: dashboardId,\n name,\n href: `${basePath}/${dashboardId}`,\n };\n });\n\n setBreadcrumbs(items);\n }, [isInDrilldown, getDrilldownMetadata, dashboards, basePath, t]);\n\n // Handle navigation to a breadcrumb item\n const handleNavigate = useCallback(\n (item: BreadcrumbItem, index: number) => {\n // Build URL preserving context up to this point\n const metadata = getDrilldownMetadata();\n const newPath = metadata.path.slice(0, index);\n const newDepth = index;\n\n // Build URL with updated drilldown params\n const url = new URL(item.href, window.location.origin);\n url.searchParams.set('drilldown_depth', String(newDepth));\n if (newPath.length > 0) {\n url.searchParams.set('drilldown_path', encodeURIComponent(JSON.stringify(newPath)));\n }\n if (index > 0 && newPath.length > 0) {\n url.searchParams.set('drilldown_parent', newPath[newPath.length - 1]);\n }\n\n navigate(url.pathname + url.search);\n },\n [getDrilldownMetadata, navigate]\n );\n\n // Handle back button click. When the drilldown was reached inside this app\n // session, step back through browser history: that restores the exact\n // origin (its page, filters and scroll) rather than rebuilding a URL that\n // would land on the parent's first page (BOFF-7625). React Router records\n // the history index in `history.state.idx`.\n const handleBack = useCallback(() => {\n const historyIndex = (window.history.state as { idx?: unknown } | null)?.idx;\n if (typeof historyIndex === 'number' && historyIndex > 0) {\n window.history.back();\n return;\n }\n\n const metadata = getDrilldownMetadata();\n\n if (metadata.parent) {\n // Navigate to parent dashboard\n const newPath = metadata.path.slice(0, -1);\n const newDepth = metadata.depth - 1;\n\n const url = new URL(`${basePath}/${metadata.parent}`, window.location.origin);\n url.searchParams.set('drilldown_depth', String(newDepth));\n if (newPath.length > 0) {\n url.searchParams.set('drilldown_path', encodeURIComponent(JSON.stringify(newPath)));\n url.searchParams.set('drilldown_parent', newPath[newPath.length - 1]);\n }\n\n navigate(url.pathname + url.search);\n } else if (breadcrumbs.length > 0) {\n // Navigate to last breadcrumb\n const lastBreadcrumb = breadcrumbs[breadcrumbs.length - 1];\n handleNavigate(lastBreadcrumb, breadcrumbs.length - 1);\n }\n }, [getDrilldownMetadata, basePath, navigate, breadcrumbs, handleNavigate]);\n\n // Handle home click\n const handleHomeClick = useCallback(\n (e: React.MouseEvent) => {\n e.preventDefault();\n navigate(basePath);\n },\n [navigate, basePath]\n );\n\n // Don't render if not in drilldown mode\n if (!isInDrilldown) {\n return null;\n }\n\n return (\n <nav\n // Recessed on purpose: this is navigational CONTEXT, not chrome you act\n // on, so it reads a step below the route header and the toolbar it sits\n // between instead of being a third identical bar. Wraps rather than\n // overflowing once the trail gets deep on a narrow viewport.\n className={`flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 border-b border-[var(--color-border-subtle)] bg-bg-sunken px-4 py-1.5 ${className}`}\n aria-label={t('bigconsole.dashboards.drilldownBreadcrumb.ariaLabel', 'Drilldown breadcrumb')}\n data-testid=\"drilldown-breadcrumb\"\n >\n {/* Back button */}\n <button\n type=\"button\"\n onClick={handleBack}\n className={`flex min-h-control-sm items-center gap-1 rounded-button px-2 py-1 text-sm font-medium text-text-link motion-safe:transition-colors hover:bg-bg-accentSubtle ${SECTION_FOCUS_RING}`}\n aria-label={t('bigconsole.dashboards.drilldownBreadcrumb.back', 'Go back')}\n >\n <ArrowLeft className=\"w-4 h-4\" />\n <span className=\"hidden sm:inline\">{t('bigconsole.dashboards.drilldownBreadcrumb.backLabel', 'Back')}</span>\n </button>\n\n <span className=\"text-text-muted\" aria-hidden=\"true\">\n |\n </span>\n\n {/* Home link */}\n {showHome && (\n <>\n <a\n href={basePath}\n onClick={handleHomeClick}\n className={`flex min-h-control-sm items-center gap-1 rounded-button px-1 text-sm text-text-secondary motion-safe:transition-colors hover:text-text-primary ${SECTION_FOCUS_RING}`}\n aria-label={t('bigconsole.dashboards.drilldownBreadcrumb.home', 'Home')}\n >\n <Home className=\"w-4 h-4\" />\n <span className=\"hidden sm:inline\">\n {t('bigconsole.dashboards.drilldownBreadcrumb.dashboards', 'Dashboards')}\n </span>\n </a>\n <ChevronRight className=\"w-4 h-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n </>\n )}\n\n {/* Breadcrumb items */}\n {breadcrumbs.map((item, index) => (\n <div key={item.id} className=\"flex min-w-0 items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => handleNavigate(item, index)}\n className={`min-h-control-sm max-w-[150px] truncate rounded-button px-1 text-sm text-text-secondary motion-safe:transition-colors hover:text-text-primary hover:underline ${SECTION_FOCUS_RING}`}\n title={item.name}\n >\n {item.name}\n </button>\n <ChevronRight className=\"w-4 h-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n </div>\n ))}\n\n {/* Current dashboard (non-clickable) */}\n {currentDashboardName && (\n <span\n aria-current=\"page\"\n className=\"max-w-[200px] truncate text-sm font-medium text-text-primary\"\n title={currentDashboardName}\n >\n {currentDashboardName}\n </span>\n )}\n\n {/* Depth indicator */}\n <span className=\"ml-auto shrink-0 rounded-pill bg-bg-surface px-2 py-0.5 text-xs text-text-muted\">\n {t('bigconsole.dashboards.drilldownBreadcrumb.level', 'Level {{depth}}', { depth: drilldownDepth ?? '' })}\n </span>\n </nav>\n );\n});\n\nexport default DrilldownBreadcrumb;\n"],"mappings":"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"}
@@ -1,101 +1,129 @@
1
1
  import { EmbedErrorBoundary as e } from "./EmbedErrorBoundary.js";
2
2
  import { EmbedWidgetRenderer as t } from "./EmbedWidgetRenderer.js";
3
- import { useState as n } from "react";
4
- import { jsx as r, jsxs as i } from "react/jsx-runtime";
3
+ import { readDrilldownHint as n } from "./drilldown.js";
4
+ import { useMemo as r, useState as i } from "react";
5
+ import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
5
6
  //#region src/bigconsole/components/embed/ReadOnlyDashboardRenderer.tsx
6
- var a = 12;
7
- function o({ page: e, capabilities: n }) {
8
- return e.widgets.length === 0 ? /* @__PURE__ */ r("div", {
7
+ var c = 12;
8
+ function l({ title: e, onClick: t }) {
9
+ return /* @__PURE__ */ o("button", {
10
+ type: "button",
11
+ onClick: t,
12
+ "aria-label": e ? `View details for ${e}` : "View details",
13
+ className: "absolute right-2 top-2 z-10 rounded-button border border-border-default bg-bg-surface px-2 py-1 text-xs font-medium text-text-link shadow-sm transition-colors hover:bg-bg-sunken focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
14
+ "data-testid": "embed-drilldown",
15
+ children: "View details →"
16
+ });
17
+ }
18
+ function u({ page: e, capabilities: t, pageIds: n, onDrilldown: r }) {
19
+ return e.widgets.length === 0 ? /* @__PURE__ */ o("div", {
9
20
  className: "flex h-40 items-center justify-center text-sm text-text-secondary",
10
21
  children: "This page has no widgets."
11
- }) : /* @__PURE__ */ r("div", {
22
+ }) : /* @__PURE__ */ o("div", {
12
23
  className: "grid w-full gap-4 p-4",
13
24
  style: {
14
- gridTemplateColumns: `repeat(${a}, minmax(0, 1fr))`,
25
+ gridTemplateColumns: `repeat(${c}, minmax(0, 1fr))`,
15
26
  gridAutoRows: "minmax(80px, auto)"
16
27
  },
17
- children: e.widgets.map((e) => /* @__PURE__ */ r("div", {
18
- className: "overflow-hidden rounded-lg border border-border-default bg-bg-surface shadow-sm",
28
+ children: e.widgets.map((e) => /* @__PURE__ */ o("div", {
29
+ className: "relative overflow-hidden rounded-lg border border-border-default bg-bg-surface shadow-sm",
19
30
  style: {
20
- gridColumn: `${Math.min(Math.max(e.layout.x, 0), a - 1) + 1} / span ${Math.min(Math.max(e.layout.w, 1), a)}`,
31
+ gridColumn: `${Math.min(Math.max(e.layout.x, 0), c - 1) + 1} / span ${Math.min(Math.max(e.layout.w, 1), c)}`,
21
32
  gridRow: `${Math.max(e.layout.y, 0) + 1} / span ${Math.max(e.layout.h, 1)}`
22
33
  },
23
- children: /* @__PURE__ */ r(t, {
34
+ children: /* @__PURE__ */ o(d, {
24
35
  widget: e,
25
- capabilities: n
36
+ capabilities: t,
37
+ pageIds: n,
38
+ onDrilldown: r
26
39
  })
27
40
  }, e.id))
28
41
  });
29
42
  }
30
- function s() {
31
- return /* @__PURE__ */ r("div", {
43
+ function d({ widget: e, capabilities: r, pageIds: i, onDrilldown: c }) {
44
+ let u = r.drilldown && c ? n(e, i) : null;
45
+ return /* @__PURE__ */ s(a, { children: [/* @__PURE__ */ o(t, {
46
+ widget: e,
47
+ capabilities: r
48
+ }), u && c && /* @__PURE__ */ o(l, {
49
+ title: e.title,
50
+ onClick: () => c(u)
51
+ })] });
52
+ }
53
+ function f() {
54
+ return /* @__PURE__ */ o("div", {
32
55
  className: "flex shrink-0 items-center justify-end border-t border-border-default bg-bg-surface px-4 py-2",
33
- children: /* @__PURE__ */ i("a", {
56
+ children: /* @__PURE__ */ s("a", {
34
57
  href: "https://bigconsole.com",
35
58
  target: "_blank",
36
59
  rel: "noopener noreferrer",
37
60
  className: "text-xs text-text-tertiary transition-colors hover:text-text-secondary",
38
- children: ["Powered by ", /* @__PURE__ */ r("span", {
61
+ children: ["Powered by ", /* @__PURE__ */ o("span", {
39
62
  className: "font-semibold text-text-secondary",
40
63
  children: "BigConsole"
41
64
  })]
42
65
  })
43
66
  });
44
67
  }
45
- function c({ model: t }) {
46
- let [a, c] = n(0), l = t.pages, u = t.presentation, d = u?.showTitle ? (u.title ?? "").trim() : "", f = l[Math.min(a, l.length - 1)];
47
- return f ? /* @__PURE__ */ r(e, {
68
+ function p({ model: t, activePageId: n, onPageChange: a, onDrilldown: c }) {
69
+ let [l, d] = i(0), p = t.pages, m = t.presentation, h = r(() => new Set(p.map((e) => e.id)), [p]), g = m?.showTitle ? (m.title ?? "").trim() : "", _ = a !== void 0, v = p.findIndex((e) => e.id === n), y = _ ? Math.max(v, 0) : l, b = (e) => {
70
+ let t = p[e];
71
+ t && (_ ? a(t.id) : d(e));
72
+ }, x = p[Math.min(y, p.length - 1)];
73
+ return x ? /* @__PURE__ */ o(e, {
48
74
  label: "dashboard",
49
- children: /* @__PURE__ */ i("div", {
75
+ children: /* @__PURE__ */ s("div", {
50
76
  className: "flex h-full w-full flex-col bg-bg-canvas",
51
77
  children: [
52
- d && /* @__PURE__ */ r("header", {
78
+ g && /* @__PURE__ */ o("header", {
53
79
  className: "shrink-0 border-b border-border-default bg-bg-surface px-4 py-3",
54
- children: /* @__PURE__ */ r("h1", {
80
+ children: /* @__PURE__ */ o("h1", {
55
81
  className: "truncate text-base font-semibold text-text-primary",
56
- children: d
82
+ children: g
57
83
  })
58
84
  }),
59
- l.length > 1 && /* @__PURE__ */ r("div", {
85
+ p.length > 1 && /* @__PURE__ */ o("div", {
60
86
  role: "tablist",
61
87
  className: "flex gap-1 border-b border-border-default bg-bg-surface px-2",
62
- children: l.map((e, t) => /* @__PURE__ */ r("button", {
88
+ children: p.map((e, t) => /* @__PURE__ */ o("button", {
63
89
  role: "tab",
64
- "aria-selected": t === a,
65
- onClick: () => c(t),
66
- className: t === a ? "-mb-px border-b-2 border-action-primary-bg px-3 py-2 text-sm font-medium text-text-primary" : "border-b-2 border-transparent px-3 py-2 text-sm text-text-secondary hover:text-text-primary",
90
+ "aria-selected": t === y,
91
+ onClick: () => b(t),
92
+ className: t === y ? "-mb-px border-b-2 border-action-primary-bg px-3 py-2 text-sm font-medium text-text-primary" : "border-b-2 border-transparent px-3 py-2 text-sm text-text-secondary hover:text-text-primary",
67
93
  children: e.title ?? `Page ${t + 1}`
68
94
  }, e.id))
69
95
  }),
70
- /* @__PURE__ */ r("div", {
96
+ /* @__PURE__ */ o("div", {
71
97
  className: "min-h-0 flex-1 overflow-auto",
72
- children: /* @__PURE__ */ r(o, {
73
- page: f,
74
- capabilities: t.capabilities
98
+ children: /* @__PURE__ */ o(u, {
99
+ page: x,
100
+ capabilities: t.capabilities,
101
+ pageIds: h,
102
+ onDrilldown: c
75
103
  })
76
104
  }),
77
- /* @__PURE__ */ r(s, {})
105
+ /* @__PURE__ */ o(f, {})
78
106
  ]
79
107
  })
80
- }) : /* @__PURE__ */ i("div", {
108
+ }) : /* @__PURE__ */ s("div", {
81
109
  className: "flex h-full flex-col bg-bg-canvas",
82
110
  children: [
83
- d && /* @__PURE__ */ r("header", {
111
+ g && /* @__PURE__ */ o("header", {
84
112
  className: "shrink-0 border-b border-border-default bg-bg-surface px-4 py-3",
85
- children: /* @__PURE__ */ r("h1", {
113
+ children: /* @__PURE__ */ o("h1", {
86
114
  className: "truncate text-base font-semibold text-text-primary",
87
- children: d
115
+ children: g
88
116
  })
89
117
  }),
90
- /* @__PURE__ */ r("div", {
118
+ /* @__PURE__ */ o("div", {
91
119
  className: "flex flex-1 items-center justify-center text-sm text-text-secondary",
92
120
  children: "This dashboard has no pages."
93
121
  }),
94
- /* @__PURE__ */ r(s, {})
122
+ /* @__PURE__ */ o(f, {})
95
123
  ]
96
124
  });
97
125
  }
98
126
  //#endregion
99
- export { c as ReadOnlyDashboardRenderer };
127
+ export { p as ReadOnlyDashboardRenderer };
100
128
 
101
129
  //# sourceMappingURL=ReadOnlyDashboardRenderer.js.map