@burdenoff/microfe-bigconsole 2026.929.2 → 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.
- package/dist/bigconsole/assistant/assistantRunPersistence.js +111 -0
- package/dist/bigconsole/assistant/assistantRunPersistence.js.map +1 -0
- package/dist/bigconsole/assistant/assistantRunStore.js +87 -48
- package/dist/bigconsole/assistant/assistantRunStore.js.map +1 -1
- package/dist/bigconsole/assistant/previewController.js +19 -50
- package/dist/bigconsole/assistant/previewController.js.map +1 -1
- package/dist/bigconsole/assistant/useAssistantRunWorkspace.js +14 -0
- package/dist/bigconsole/assistant/useAssistantRunWorkspace.js.map +1 -0
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js +74 -50
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +9 -4
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
- package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js +68 -40
- package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js.map +1 -1
- package/dist/bigconsole/components/embed/drilldown.js +26 -0
- package/dist/bigconsole/components/embed/drilldown.js.map +1 -0
- package/dist/bigconsole/components/embed/index.js +4 -3
- package/dist/bigconsole/components/embed/types.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +32 -30
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewPanel.js +38 -36
- package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
- package/dist/bigconsole/components/preview/previewShared.js +1 -1
- package/dist/bigconsole/components/preview/previewShared.js.map +1 -1
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +307 -233
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +230 -220
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetWrapper.js +18 -19
- package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
- package/dist/bigconsole/context/BigConsoleContext.js +4 -1
- package/dist/bigconsole/context/BigConsoleContext.js.map +1 -1
- package/dist/bigconsole/hooks/useActivePage.js +25 -0
- package/dist/bigconsole/hooks/useActivePage.js.map +1 -0
- package/dist/bigconsole/hooks/useDrilldown.js +118 -71
- package/dist/bigconsole/hooks/useDrilldown.js.map +1 -1
- package/dist/bigconsole/hooks/useDrilldownOperations.js +32 -26
- package/dist/bigconsole/hooks/useDrilldownOperations.js.map +1 -1
- package/dist/bigconsole/hooks/useFilterUrlSync.js +1 -1
- package/dist/bigconsole/hooks/useFilterUrlSync.js.map +1 -1
- package/dist/bigconsole/pages/DashboardBuilderPage.js +190 -185
- package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardViewPage.js +224 -217
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/generated/wspace-operations.js +132 -86
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/package.json +3 -3
- package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js +0 -25
- package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js.map +0 -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 "
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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
|
|
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
|
-
|
|
51
|
-
let
|
|
52
|
-
|
|
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
|
-
|
|
71
|
+
_(!1);
|
|
55
72
|
return;
|
|
56
73
|
}
|
|
57
74
|
async function n() {
|
|
58
|
-
|
|
75
|
+
_(!0), y(null);
|
|
59
76
|
try {
|
|
60
|
-
let n = (await
|
|
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
|
-
|
|
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
|
-
|
|
90
|
+
x({
|
|
74
91
|
id: n.id,
|
|
75
92
|
name: n.name,
|
|
76
93
|
description: n.description,
|
|
77
94
|
pages: r
|
|
78
|
-
}),
|
|
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) =>
|
|
99
|
+
})).data?.listWidgetsByDashboard || []).map((e, t) => p(e, t)));
|
|
83
100
|
} catch (e) {
|
|
84
|
-
|
|
101
|
+
y(e instanceof Error ? e.message : "Failed to load dashboard");
|
|
85
102
|
} finally {
|
|
86
|
-
|
|
103
|
+
_(!1);
|
|
87
104
|
}
|
|
88
105
|
}
|
|
89
106
|
n();
|
|
90
|
-
}, [i,
|
|
107
|
+
}, [i, s]), h) return /* @__PURE__ */ d("div", {
|
|
91
108
|
className: "flex items-center justify-center h-full",
|
|
92
|
-
children: /* @__PURE__ */
|
|
109
|
+
children: /* @__PURE__ */ f("div", {
|
|
93
110
|
className: "flex flex-col items-center gap-3",
|
|
94
|
-
children: [/* @__PURE__ */
|
|
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 (
|
|
117
|
+
if (v) return /* @__PURE__ */ d("div", {
|
|
101
118
|
className: "flex h-full items-center justify-center",
|
|
102
119
|
role: "alert",
|
|
103
|
-
children: /* @__PURE__ */
|
|
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:
|
|
110
|
-
icon: /* @__PURE__ */
|
|
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__ */
|
|
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 (!
|
|
141
|
+
if (!b || S.length === 0) return /* @__PURE__ */ d("div", {
|
|
125
142
|
className: "flex items-center justify-center h-full",
|
|
126
|
-
children: /* @__PURE__ */
|
|
143
|
+
children: /* @__PURE__ */ f("div", {
|
|
127
144
|
className: "flex flex-col items-center gap-3 text-center",
|
|
128
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
156
|
+
}), /* @__PURE__ */ d("p", {
|
|
140
157
|
className: "text-sm text-text-secondary",
|
|
141
|
-
children:
|
|
158
|
+
children: b ? "This dashboard has no widgets" : "Dashboard not found"
|
|
142
159
|
})]
|
|
143
160
|
})
|
|
144
161
|
});
|
|
145
|
-
let
|
|
146
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
166
|
+
/* @__PURE__ */ d(n, {
|
|
150
167
|
emphasis: "bare",
|
|
151
168
|
padding: "none",
|
|
152
169
|
headingLevel: 3,
|
|
153
|
-
title:
|
|
154
|
-
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
|
-
|
|
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:
|
|
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__ */
|
|
186
|
+
children: [/* @__PURE__ */ f("span", {
|
|
167
187
|
className: "font-medium text-text-primary",
|
|
168
188
|
children: [e, ":"]
|
|
169
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
195
|
+
/* @__PURE__ */ f("div", {
|
|
176
196
|
className: "flex flex-col gap-form-gap",
|
|
177
|
-
children:
|
|
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__ */
|
|
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__ */
|
|
212
|
+
children: /* @__PURE__ */ d(g, {
|
|
189
213
|
widget: e,
|
|
190
|
-
filterValues:
|
|
214
|
+
filterValues: o
|
|
191
215
|
})
|
|
192
216
|
}, e.id);
|
|
193
|
-
})
|
|
217
|
+
})]
|
|
194
218
|
})
|
|
195
219
|
]
|
|
196
220
|
});
|
|
197
|
-
}),
|
|
198
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
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 {
|
|
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 =
|
|
42
|
-
if (e
|
|
43
|
-
|
|
44
|
-
|
|
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 {
|
|
4
|
-
import {
|
|
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
|
|
7
|
-
function
|
|
8
|
-
return
|
|
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__ */
|
|
22
|
+
}) : /* @__PURE__ */ o("div", {
|
|
12
23
|
className: "grid w-full gap-4 p-4",
|
|
13
24
|
style: {
|
|
14
|
-
gridTemplateColumns: `repeat(${
|
|
25
|
+
gridTemplateColumns: `repeat(${c}, minmax(0, 1fr))`,
|
|
15
26
|
gridAutoRows: "minmax(80px, auto)"
|
|
16
27
|
},
|
|
17
|
-
children: e.widgets.map((e) => /* @__PURE__ */
|
|
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),
|
|
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__ */
|
|
34
|
+
children: /* @__PURE__ */ o(d, {
|
|
24
35
|
widget: e,
|
|
25
|
-
capabilities:
|
|
36
|
+
capabilities: t,
|
|
37
|
+
pageIds: n,
|
|
38
|
+
onDrilldown: r
|
|
26
39
|
})
|
|
27
40
|
}, e.id))
|
|
28
41
|
});
|
|
29
42
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
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__ */
|
|
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__ */
|
|
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
|
|
46
|
-
let [
|
|
47
|
-
|
|
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__ */
|
|
75
|
+
children: /* @__PURE__ */ s("div", {
|
|
50
76
|
className: "flex h-full w-full flex-col bg-bg-canvas",
|
|
51
77
|
children: [
|
|
52
|
-
|
|
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__ */
|
|
80
|
+
children: /* @__PURE__ */ o("h1", {
|
|
55
81
|
className: "truncate text-base font-semibold text-text-primary",
|
|
56
|
-
children:
|
|
82
|
+
children: g
|
|
57
83
|
})
|
|
58
84
|
}),
|
|
59
|
-
|
|
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:
|
|
88
|
+
children: p.map((e, t) => /* @__PURE__ */ o("button", {
|
|
63
89
|
role: "tab",
|
|
64
|
-
"aria-selected": t ===
|
|
65
|
-
onClick: () =>
|
|
66
|
-
className: t ===
|
|
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__ */
|
|
96
|
+
/* @__PURE__ */ o("div", {
|
|
71
97
|
className: "min-h-0 flex-1 overflow-auto",
|
|
72
|
-
children: /* @__PURE__ */
|
|
73
|
-
page:
|
|
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__ */
|
|
105
|
+
/* @__PURE__ */ o(f, {})
|
|
78
106
|
]
|
|
79
107
|
})
|
|
80
|
-
}) : /* @__PURE__ */
|
|
108
|
+
}) : /* @__PURE__ */ s("div", {
|
|
81
109
|
className: "flex h-full flex-col bg-bg-canvas",
|
|
82
110
|
children: [
|
|
83
|
-
|
|
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__ */
|
|
113
|
+
children: /* @__PURE__ */ o("h1", {
|
|
86
114
|
className: "truncate text-base font-semibold text-text-primary",
|
|
87
|
-
children:
|
|
115
|
+
children: g
|
|
88
116
|
})
|
|
89
117
|
}),
|
|
90
|
-
/* @__PURE__ */
|
|
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__ */
|
|
122
|
+
/* @__PURE__ */ o(f, {})
|
|
95
123
|
]
|
|
96
124
|
});
|
|
97
125
|
}
|
|
98
126
|
//#endregion
|
|
99
|
-
export {
|
|
127
|
+
export { p as ReadOnlyDashboardRenderer };
|
|
100
128
|
|
|
101
129
|
//# sourceMappingURL=ReadOnlyDashboardRenderer.js.map
|