@burdenoff/microfe-bigconsole 2026.924.2 → 2026.925.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/BigConsoleRoot.js +94 -84
- package/dist/bigconsole/BigConsoleRoot.js.map +1 -1
- package/dist/bigconsole/BigConsoleRoutes.js +137 -138
- package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
- package/dist/bigconsole/assistant/previewController.js +7 -7
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js +17 -17
- package/dist/bigconsole/components/builder/inspector/DataBindingSection.js +32 -32
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js +52 -52
- package/dist/bigconsole/components/dashboard/CreateDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +7 -7
- package/dist/bigconsole/components/dashboard/DashboardStatusPill.js +53 -0
- package/dist/bigconsole/components/dashboard/DashboardStatusPill.js.map +1 -0
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js +60 -60
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +21 -21
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterChip.js +30 -30
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterEditor.js +36 -36
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.js +34 -34
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js +38 -38
- package/dist/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js +121 -121
- package/dist/bigconsole/components/dashboard/ImportDashboardDialog.js.map +1 -1
- package/dist/bigconsole/components/dashboard/PublishToStoreDialog.js +22 -22
- package/dist/bigconsole/components/dashboard/SavedViewsDropdown/SavedViewsDropdown.js +46 -46
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +53 -53
- package/dist/bigconsole/components/dashboard/TemplatePicker/TemplatePicker.js +1 -1
- package/dist/bigconsole/components/dashboard/WidgetPalette.js +47 -47
- package/dist/bigconsole/components/dashboard/index.js +1 -0
- package/dist/bigconsole/components/datasink/CacheTTLSelector.js +35 -35
- package/dist/bigconsole/components/datasink/DataSinkDeleteDialog.js +18 -18
- package/dist/bigconsole/components/datasink/DataSinkRefreshDialog.js +20 -20
- package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js +30 -29
- package/dist/bigconsole/components/datasink/DataSinkStatusBadge.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +48 -48
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js +126 -114
- package/dist/bigconsole/components/datasink/ImportDataSinkDialog.js.map +1 -1
- package/dist/bigconsole/components/datasink/RefreshTypeSelector.js +35 -35
- package/dist/bigconsole/components/overview/DashboardGuide.js +214 -0
- package/dist/bigconsole/components/overview/DashboardGuide.js.map +1 -0
- package/dist/bigconsole/components/overview/DataHealthCard.js +97 -0
- package/dist/bigconsole/components/overview/DataHealthCard.js.map +1 -0
- package/dist/bigconsole/components/overview/OverviewQuickActions.js +32 -0
- package/dist/bigconsole/components/overview/OverviewQuickActions.js.map +1 -0
- package/dist/bigconsole/components/overview/OverviewStatCard.js +57 -0
- package/dist/bigconsole/components/overview/OverviewStatCard.js.map +1 -0
- package/dist/bigconsole/components/overview/RecentDashboardsPanel.js +107 -0
- package/dist/bigconsole/components/overview/RecentDashboardsPanel.js.map +1 -0
- package/dist/bigconsole/components/overview/dashboardGuideSteps.js +119 -0
- package/dist/bigconsole/components/overview/dashboardGuideSteps.js.map +1 -0
- package/dist/bigconsole/components/overview/index.js +8 -0
- package/dist/bigconsole/components/overview/overviewModel.js +119 -0
- package/dist/bigconsole/components/overview/overviewModel.js.map +1 -0
- package/dist/bigconsole/components/overview/useOverviewData.js +48 -0
- package/dist/bigconsole/components/overview/useOverviewData.js.map +1 -0
- package/dist/bigconsole/components/parser/DataSinkSelector.js +40 -40
- package/dist/bigconsole/components/parser/ImportParserDialog.js +127 -127
- package/dist/bigconsole/components/parser/ImportParserDialog.js.map +1 -1
- package/dist/bigconsole/components/parser/ParserSelector.js +51 -51
- package/dist/bigconsole/components/preview/AiPreviewContent.js +5 -5
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +32 -32
- package/dist/bigconsole/components/preview/AiPreviewPanel.js +37 -37
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js +13 -13
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +70 -70
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +28 -28
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +25 -25
- package/dist/bigconsole/components/widgets/chart/ChartConfig.js +48 -48
- package/dist/bigconsole/components/widgets/comments/WidgetComments.js +46 -46
- package/dist/bigconsole/components/widgets/event-logs/EventLogViewer.js +61 -61
- package/dist/bigconsole/components/widgets/form/FormWidget.js +36 -36
- package/dist/bigconsole/components/widgets/gauge/GaugeConfig.js +42 -42
- package/dist/bigconsole/components/widgets/heatmap/HeatmapConfig.js +25 -25
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +21 -21
- package/dist/bigconsole/components/widgets/list/ListConfig.js +30 -30
- package/dist/bigconsole/components/widgets/metric-card/MetricCardConfig.js +38 -38
- package/dist/bigconsole/components/widgets/metric-card/SparklineChart.js +9 -9
- package/dist/bigconsole/components/widgets/metric-card/TrendIndicator.js +9 -9
- package/dist/bigconsole/components/widgets/pivot-table/PivotTableWidget.js +49 -49
- package/dist/bigconsole/components/widgets/progress/ProgressConfig.js +39 -39
- package/dist/bigconsole/components/widgets/table/TableConfig.js +73 -73
- package/dist/bigconsole/components/widgets/webhook-config/WebhookConfigPanel.js +64 -64
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +10 -10
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js +68 -68
- package/dist/bigconsole/customWidget/CustomWidgetProposalPanel.js.map +1 -1
- package/dist/bigconsole/hooks/useDataSinkOperations.js +92 -89
- package/dist/bigconsole/hooks/useDataSinkOperations.js.map +1 -1
- package/dist/bigconsole/hooks/useImportDataSink.js +96 -91
- package/dist/bigconsole/hooks/useImportDataSink.js.map +1 -1
- package/dist/bigconsole/pages/AdminPage.js +136 -136
- package/dist/bigconsole/pages/DashboardBuilderPage.js +10 -10
- package/dist/bigconsole/pages/DashboardViewPage.js +61 -61
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +327 -348
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/DataParsersPage.js +5 -5
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +82 -80
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +83 -83
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinksPage.js +11 -11
- package/dist/bigconsole/pages/DataSourcesPage.js +5 -5
- package/dist/bigconsole/pages/InsightsPage.js +315 -315
- package/dist/bigconsole/pages/InsightsPage.js.map +1 -1
- package/dist/bigconsole/pages/OverviewPage.js +244 -0
- package/dist/bigconsole/pages/OverviewPage.js.map +1 -0
- package/dist/bigconsole/pages/ParserBuilderPage.js +5 -5
- package/dist/bigconsole/pages/ParserViewPage.js +98 -98
- package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineBuilderPage.js +8 -8
- package/dist/bigconsole/pages/PipelineViewPage.js +46 -46
- package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelinesPage.js +80 -80
- package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
- package/dist/bigconsole/pages/SettingsPage.js +70 -70
- package/dist/bigconsole/pages/WidgetDemoPage.js +42 -42
- package/dist/bigconsole/pages/WorkflowViewPage.js +200 -200
- package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowsPage.js +78 -78
- package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
- package/dist/bigconsole/providers/BigConsoleQuotaProvider.js +36 -35
- package/dist/bigconsole/providers/BigConsoleQuotaProvider.js.map +1 -1
- package/dist/bigconsole/renderers/adapters/AdaptiveCardRenderer.js +34 -34
- package/dist/bigconsole/renderers/adapters/BlockKitRenderer.js +79 -79
- package/dist/bigconsole/renderers/adapters/CustomRenderer.js +46 -46
- package/dist/bigconsole/utils/createDialogParam.js +14 -0
- package/dist/bigconsole/utils/createDialogParam.js.map +1 -0
- package/dist/bigconsole/utils/quota.js +51 -23
- package/dist/bigconsole/utils/quota.js.map +1 -1
- package/dist/generated/global-operations.js +1 -1
- package/dist/generated/wspace-operations.js +285 -222
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/shared/hooks/useTr.js +10 -7
- package/dist/shared/hooks/useTr.js.map +1 -1
- package/package.json +1 -1
- package/dist/bigconsole/pages/HomePage.js +0 -318
- package/dist/bigconsole/pages/HomePage.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { SEMANTIC_COLORS as e } from "../../../utils/semanticTokens.js";
|
|
2
2
|
import { useEffect as t, useRef as n, useState as r } from "react";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
4
|
+
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
5
5
|
//#region src/bigconsole/components/dashboard/GlobalFiltersBar/FilterPresetSelector.tsx
|
|
6
6
|
function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, onDeletePreset: d, disabled: f = !1 }) {
|
|
7
|
-
let { t: p } =
|
|
7
|
+
let { t: p } = o(), [m, h] = r(!1), [g, _] = r(!1), [v, y] = r(""), [b, x] = r(""), S = n(null), C = s.find((e) => e.id === c);
|
|
8
8
|
t(() => {
|
|
9
9
|
function e(e) {
|
|
10
10
|
S.current && !S.current.contains(e.target) && (h(!1), _(!1));
|
|
@@ -16,13 +16,13 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
16
16
|
}, T = (e, t) => {
|
|
17
17
|
e.stopPropagation(), window.confirm(p("bigconsole.filters.presets.deleteConfirm", "Are you sure you want to delete this preset?")) && d(t);
|
|
18
18
|
};
|
|
19
|
-
return /* @__PURE__ */
|
|
19
|
+
return /* @__PURE__ */ a("div", {
|
|
20
20
|
ref: S,
|
|
21
21
|
style: {
|
|
22
22
|
position: "relative",
|
|
23
23
|
display: "inline-block"
|
|
24
24
|
},
|
|
25
|
-
children: [/* @__PURE__ */
|
|
25
|
+
children: [/* @__PURE__ */ a("button", {
|
|
26
26
|
onClick: () => !f && h(!m),
|
|
27
27
|
disabled: f,
|
|
28
28
|
style: {
|
|
@@ -42,13 +42,13 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
42
42
|
maxWidth: "100%",
|
|
43
43
|
justifyContent: "space-between"
|
|
44
44
|
},
|
|
45
|
-
children: [/* @__PURE__ */
|
|
45
|
+
children: [/* @__PURE__ */ a("span", {
|
|
46
46
|
style: {
|
|
47
47
|
display: "flex",
|
|
48
48
|
alignItems: "center",
|
|
49
49
|
gap: "6px"
|
|
50
50
|
},
|
|
51
|
-
children: [/* @__PURE__ */
|
|
51
|
+
children: [/* @__PURE__ */ a("svg", {
|
|
52
52
|
width: "14",
|
|
53
53
|
height: "14",
|
|
54
54
|
viewBox: "0 0 24 24",
|
|
@@ -56,12 +56,12 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
56
56
|
stroke: "currentColor",
|
|
57
57
|
strokeWidth: "2",
|
|
58
58
|
children: [
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
-
/* @__PURE__ */
|
|
59
|
+
/* @__PURE__ */ i("path", { d: "M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z" }),
|
|
60
|
+
/* @__PURE__ */ i("polyline", { points: "17,21 17,13 7,13 7,21" }),
|
|
61
|
+
/* @__PURE__ */ i("polyline", { points: "7,3 7,8 15,8" })
|
|
62
62
|
]
|
|
63
63
|
}), C ? C.name : p("bigconsole.filters.presets.buttonLabel", "Presets")]
|
|
64
|
-
}), /* @__PURE__ */
|
|
64
|
+
}), /* @__PURE__ */ i("svg", {
|
|
65
65
|
width: "12",
|
|
66
66
|
height: "12",
|
|
67
67
|
viewBox: "0 0 24 24",
|
|
@@ -72,9 +72,9 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
72
72
|
transform: m ? "rotate(180deg)" : "rotate(0deg)",
|
|
73
73
|
transition: "transform 0.2s ease"
|
|
74
74
|
},
|
|
75
|
-
children: /* @__PURE__ */
|
|
75
|
+
children: /* @__PURE__ */ i("polyline", { points: "6,9 12,15 18,9" })
|
|
76
76
|
})]
|
|
77
|
-
}), m && /* @__PURE__ */
|
|
77
|
+
}), m && /* @__PURE__ */ a("div", {
|
|
78
78
|
style: {
|
|
79
79
|
position: "absolute",
|
|
80
80
|
top: "100%",
|
|
@@ -90,7 +90,7 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
90
90
|
overflow: "hidden"
|
|
91
91
|
},
|
|
92
92
|
children: [
|
|
93
|
-
/* @__PURE__ */
|
|
93
|
+
/* @__PURE__ */ a("div", {
|
|
94
94
|
style: {
|
|
95
95
|
padding: "12px 16px",
|
|
96
96
|
borderBottom: `1px solid ${e.borderDefault}`,
|
|
@@ -99,14 +99,14 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
99
99
|
justifyContent: "space-between",
|
|
100
100
|
alignItems: "center"
|
|
101
101
|
},
|
|
102
|
-
children: [/* @__PURE__ */
|
|
102
|
+
children: [/* @__PURE__ */ i("span", {
|
|
103
103
|
style: {
|
|
104
104
|
fontWeight: 600,
|
|
105
105
|
fontSize: "13px",
|
|
106
106
|
color: e.textPrimary
|
|
107
107
|
},
|
|
108
108
|
children: p("bigconsole.filters.presets.header", "Filter Presets")
|
|
109
|
-
}), /* @__PURE__ */
|
|
109
|
+
}), /* @__PURE__ */ i("button", {
|
|
110
110
|
onClick: () => _(!0),
|
|
111
111
|
style: {
|
|
112
112
|
padding: "4px 10px",
|
|
@@ -121,14 +121,14 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
121
121
|
children: p("bigconsole.filters.presets.saveCurrent", "+ Save Current")
|
|
122
122
|
})]
|
|
123
123
|
}),
|
|
124
|
-
g && /* @__PURE__ */
|
|
124
|
+
g && /* @__PURE__ */ a("div", {
|
|
125
125
|
style: {
|
|
126
126
|
padding: "12px 16px",
|
|
127
127
|
borderBottom: `1px solid ${e.borderDefault}`,
|
|
128
128
|
backgroundColor: e.bgSecondary
|
|
129
129
|
},
|
|
130
130
|
children: [
|
|
131
|
-
/* @__PURE__ */
|
|
131
|
+
/* @__PURE__ */ i("input", {
|
|
132
132
|
type: "text",
|
|
133
133
|
value: v,
|
|
134
134
|
onChange: (e) => y(e.target.value),
|
|
@@ -146,7 +146,7 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
146
146
|
color: e.textPrimary
|
|
147
147
|
}
|
|
148
148
|
}),
|
|
149
|
-
/* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ i("input", {
|
|
150
150
|
type: "text",
|
|
151
151
|
value: b,
|
|
152
152
|
onChange: (e) => x(e.target.value),
|
|
@@ -163,13 +163,13 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
163
163
|
color: e.textPrimary
|
|
164
164
|
}
|
|
165
165
|
}),
|
|
166
|
-
/* @__PURE__ */
|
|
166
|
+
/* @__PURE__ */ a("div", {
|
|
167
167
|
style: {
|
|
168
168
|
display: "flex",
|
|
169
169
|
gap: "8px",
|
|
170
170
|
justifyContent: "flex-end"
|
|
171
171
|
},
|
|
172
|
-
children: [/* @__PURE__ */
|
|
172
|
+
children: [/* @__PURE__ */ i("button", {
|
|
173
173
|
onClick: () => {
|
|
174
174
|
_(!1), y(""), x("");
|
|
175
175
|
},
|
|
@@ -183,7 +183,7 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
183
183
|
color: e.textSecondary
|
|
184
184
|
},
|
|
185
185
|
children: p("bigconsole.filters.presets.cancel", "Cancel")
|
|
186
|
-
}), /* @__PURE__ */
|
|
186
|
+
}), /* @__PURE__ */ i("button", {
|
|
187
187
|
onClick: w,
|
|
188
188
|
disabled: !v.trim(),
|
|
189
189
|
style: {
|
|
@@ -201,12 +201,12 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
201
201
|
})
|
|
202
202
|
]
|
|
203
203
|
}),
|
|
204
|
-
/* @__PURE__ */
|
|
204
|
+
/* @__PURE__ */ a("div", {
|
|
205
205
|
style: {
|
|
206
206
|
maxHeight: "300px",
|
|
207
207
|
overflowY: "auto"
|
|
208
208
|
},
|
|
209
|
-
children: [s.map((t) => /* @__PURE__ */
|
|
209
|
+
children: [s.map((t) => /* @__PURE__ */ a("div", {
|
|
210
210
|
onClick: () => {
|
|
211
211
|
l(t.id), h(!1);
|
|
212
212
|
},
|
|
@@ -227,9 +227,9 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
227
227
|
c !== t.id && (e.currentTarget.style.backgroundColor = "transparent");
|
|
228
228
|
},
|
|
229
229
|
children: [
|
|
230
|
-
/* @__PURE__ */
|
|
230
|
+
/* @__PURE__ */ a("div", {
|
|
231
231
|
style: { flex: 1 },
|
|
232
|
-
children: [/* @__PURE__ */
|
|
232
|
+
children: [/* @__PURE__ */ a("div", {
|
|
233
233
|
style: {
|
|
234
234
|
fontSize: "13px",
|
|
235
235
|
fontWeight: 500,
|
|
@@ -238,7 +238,7 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
238
238
|
alignItems: "center",
|
|
239
239
|
gap: "6px"
|
|
240
240
|
},
|
|
241
|
-
children: [t.name, t.isDefault && /* @__PURE__ */
|
|
241
|
+
children: [t.name, t.isDefault && /* @__PURE__ */ i("span", {
|
|
242
242
|
style: {
|
|
243
243
|
fontSize: "10px",
|
|
244
244
|
padding: "2px 6px",
|
|
@@ -248,7 +248,7 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
248
248
|
},
|
|
249
249
|
children: p("bigconsole.filters.presets.defaultBadge", "Default")
|
|
250
250
|
})]
|
|
251
|
-
}), t.description && /* @__PURE__ */
|
|
251
|
+
}), t.description && /* @__PURE__ */ i("div", {
|
|
252
252
|
style: {
|
|
253
253
|
fontSize: "11px",
|
|
254
254
|
color: e.textTertiary,
|
|
@@ -257,7 +257,7 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
257
257
|
children: t.description
|
|
258
258
|
})]
|
|
259
259
|
}),
|
|
260
|
-
!t.isDefault && !t.id.startsWith("preset-last") && !t.id.startsWith("preset-this") && /* @__PURE__ */
|
|
260
|
+
!t.isDefault && !t.id.startsWith("preset-last") && !t.id.startsWith("preset-this") && /* @__PURE__ */ i("button", {
|
|
261
261
|
onClick: (e) => T(e, t.id),
|
|
262
262
|
style: {
|
|
263
263
|
padding: "4px",
|
|
@@ -277,17 +277,17 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
277
277
|
t.currentTarget.style.backgroundColor = "transparent", t.currentTarget.style.color = e.textTertiary;
|
|
278
278
|
},
|
|
279
279
|
title: p("bigconsole.filters.presets.deletePresetTitle", "Delete preset"),
|
|
280
|
-
children: /* @__PURE__ */
|
|
280
|
+
children: /* @__PURE__ */ a("svg", {
|
|
281
281
|
width: "14",
|
|
282
282
|
height: "14",
|
|
283
283
|
viewBox: "0 0 24 24",
|
|
284
284
|
fill: "none",
|
|
285
285
|
stroke: "currentColor",
|
|
286
286
|
strokeWidth: "2",
|
|
287
|
-
children: [/* @__PURE__ */
|
|
287
|
+
children: [/* @__PURE__ */ i("polyline", { points: "3,6 5,6 21,6" }), /* @__PURE__ */ i("path", { d: "M19,6v14a2,2,0,01-2,2H7a2,2,0,01-2-2V6m3,0V4a2,2,0,012-2h4a2,2,0,012,2v2" })]
|
|
288
288
|
})
|
|
289
289
|
}),
|
|
290
|
-
c === t.id && /* @__PURE__ */
|
|
290
|
+
c === t.id && /* @__PURE__ */ i("svg", {
|
|
291
291
|
width: "16",
|
|
292
292
|
height: "16",
|
|
293
293
|
viewBox: "0 0 24 24",
|
|
@@ -295,10 +295,10 @@ function s({ presets: s, activePresetId: c, onApplyPreset: l, onSavePreset: u, o
|
|
|
295
295
|
stroke: e.actionPrimaryBg,
|
|
296
296
|
strokeWidth: "3",
|
|
297
297
|
style: { marginLeft: "8px" },
|
|
298
|
-
children: /* @__PURE__ */
|
|
298
|
+
children: /* @__PURE__ */ i("polyline", { points: "20,6 9,17 4,12" })
|
|
299
299
|
})
|
|
300
300
|
]
|
|
301
|
-
}, t.id)), s.length === 0 && /* @__PURE__ */
|
|
301
|
+
}, t.id)), s.length === 0 && /* @__PURE__ */ i("div", {
|
|
302
302
|
style: {
|
|
303
303
|
padding: "20px 16px",
|
|
304
304
|
textAlign: "center",
|
|
@@ -2,23 +2,23 @@ import { SECTION_FOCUS_RING as e } from "../../common/sectionStyles.js";
|
|
|
2
2
|
import "../../common/index.js";
|
|
3
3
|
import t from "../../../hooks/useGlobalFilters.js";
|
|
4
4
|
import n from "../../../hooks/useFilterUrlSync.js";
|
|
5
|
-
import
|
|
5
|
+
import ee from "../../../hooks/useFilterPresets.js";
|
|
6
6
|
import "../../../hooks/index.js";
|
|
7
|
-
import { SEMANTIC_COLORS as
|
|
8
|
-
import
|
|
7
|
+
import { SEMANTIC_COLORS as r } from "../../../utils/semanticTokens.js";
|
|
8
|
+
import i from "./FilterChip.js";
|
|
9
9
|
import a from "./FilterEditor.js";
|
|
10
10
|
import o from "./FilterPresetSelector.js";
|
|
11
11
|
import { useCallback as s, useEffect as c, useState as l } from "react";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
13
|
+
import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
14
14
|
//#region src/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.tsx
|
|
15
15
|
var p = `inline-flex min-h-control-sm items-center gap-icon-gap rounded-button border border-[var(--color-border-subtle)] bg-bg-surface px-3 py-1 text-sm font-medium text-text-primary motion-safe:transition-colors hover:bg-bg-accentSubtle ${e}`, m = `inline-flex min-h-control-sm items-center gap-icon-gap rounded-button px-2 py-1 text-sm font-medium text-text-secondary motion-safe:transition-colors hover:bg-bg-accentSubtle ${e}`;
|
|
16
16
|
function h({ dashboardId: e, onFilterChange: h, collapsible: g = !0, className: _ = "", enableUrlSync: v = !0, enablePresets: y = !0 }) {
|
|
17
|
-
let { t: b } =
|
|
17
|
+
let { t: b } = f(), { filters: x, activeValues: S, loading: C, fetchFilters: w, createFilter: T, updateFilter: E, deleteFilter: D, setFilterValue: O, resetFilters: k } = t(), { getShareableUrl: A, clearUrlParams: j } = n({
|
|
18
18
|
enabled: v,
|
|
19
19
|
paramPrefix: "filter_",
|
|
20
20
|
debounceMs: 300
|
|
21
|
-
}), { presets: M, activePresetId: N, loadPresets: P, applyPreset: F, saveCurrentAsPreset: I, deletePreset: L, clearActivePreset: R } =
|
|
21
|
+
}), { presets: M, activePresetId: N, loadPresets: P, applyPreset: F, saveCurrentAsPreset: I, deletePreset: L, clearActivePreset: R } = ee(e), [z, B] = l(!1), [V, H] = l(!1), [U, W] = l(null), [G, K] = l(!1);
|
|
22
22
|
c(() => {
|
|
23
23
|
e && (w(e), y && P(e));
|
|
24
24
|
}, [
|
|
@@ -87,36 +87,36 @@ function h({ dashboardId: e, onFilterChange: h, collapsible: g = !0, className:
|
|
|
87
87
|
}, [L]), ae = s(() => {
|
|
88
88
|
B((e) => !e);
|
|
89
89
|
}, []), $ = Object.keys(S).filter((e) => S[e] !== void 0 && S[e] !== null && S[e] !== "").length;
|
|
90
|
-
return C && x.length === 0 ? /* @__PURE__ */
|
|
90
|
+
return C && x.length === 0 ? /* @__PURE__ */ d("div", {
|
|
91
91
|
className: `flex flex-wrap items-center gap-control-gap border-b border-[var(--color-border-subtle)] bg-bg-sunken px-card-padding py-control-gap ${_}`,
|
|
92
92
|
role: "status",
|
|
93
93
|
"aria-live": "polite",
|
|
94
94
|
"data-testid": "global-filters-loading",
|
|
95
|
-
children: [/* @__PURE__ */
|
|
95
|
+
children: [/* @__PURE__ */ u("span", { className: "h-icon-sm w-icon-sm animate-spin rounded-full border-2 border-action-primary-bg border-t-transparent" }), /* @__PURE__ */ u("span", {
|
|
96
96
|
className: "text-sm text-text-secondary",
|
|
97
97
|
children: b("bigconsole.filters.bar.loading", "Loading filters...")
|
|
98
98
|
})]
|
|
99
|
-
}) : x.length === 0 ? /* @__PURE__ */
|
|
99
|
+
}) : x.length === 0 ? /* @__PURE__ */ d("div", {
|
|
100
100
|
className: `flex flex-wrap items-center justify-between gap-control-gap border-b border-[var(--color-border-subtle)] bg-bg-sunken px-card-padding py-control-gap ${_}`,
|
|
101
101
|
"data-testid": "global-filters-empty",
|
|
102
102
|
children: [
|
|
103
|
-
/* @__PURE__ */
|
|
103
|
+
/* @__PURE__ */ d("div", {
|
|
104
104
|
className: "flex min-w-0 flex-wrap items-center gap-icon-gap",
|
|
105
|
-
children: [/* @__PURE__ */
|
|
105
|
+
children: [/* @__PURE__ */ u("h2", {
|
|
106
106
|
className: "text-sm font-semibold text-text-primary",
|
|
107
107
|
children: b("bigconsole.filters.bar.title", "Filters")
|
|
108
|
-
}), /* @__PURE__ */
|
|
108
|
+
}), /* @__PURE__ */ u("span", {
|
|
109
109
|
className: "text-sm text-text-muted",
|
|
110
110
|
children: b("bigconsole.filters.bar.noFiltersConfigured", "No filters configured")
|
|
111
111
|
})]
|
|
112
112
|
}),
|
|
113
|
-
/* @__PURE__ */
|
|
113
|
+
/* @__PURE__ */ u("button", {
|
|
114
114
|
type: "button",
|
|
115
115
|
onClick: J,
|
|
116
116
|
className: p,
|
|
117
117
|
children: b("bigconsole.filters.bar.addFilter", "+ Add Filter")
|
|
118
118
|
}),
|
|
119
|
-
V && /* @__PURE__ */
|
|
119
|
+
V && /* @__PURE__ */ u(a, {
|
|
120
120
|
filter: U,
|
|
121
121
|
onSave: X,
|
|
122
122
|
onClose: () => {
|
|
@@ -124,48 +124,48 @@ function h({ dashboardId: e, onFilterChange: h, collapsible: g = !0, className:
|
|
|
124
124
|
}
|
|
125
125
|
})
|
|
126
126
|
]
|
|
127
|
-
}) : /* @__PURE__ */
|
|
127
|
+
}) : /* @__PURE__ */ d("section", {
|
|
128
128
|
className: `flex min-w-0 flex-col border-b border-[var(--color-border-subtle)] bg-bg-sunken ${_}`,
|
|
129
129
|
"aria-labelledby": "global-filters-heading",
|
|
130
130
|
"data-testid": "global-filters-bar",
|
|
131
131
|
children: [
|
|
132
|
-
/* @__PURE__ */
|
|
132
|
+
/* @__PURE__ */ d("div", {
|
|
133
133
|
className: `flex flex-wrap items-center justify-between gap-control-gap px-card-padding py-control-gap ${z ? "" : "border-b border-[var(--color-border-subtle)]"}`,
|
|
134
|
-
children: [/* @__PURE__ */
|
|
134
|
+
children: [/* @__PURE__ */ d("div", {
|
|
135
135
|
className: "flex min-w-0 flex-wrap items-center gap-icon-gap",
|
|
136
136
|
children: [
|
|
137
|
-
/* @__PURE__ */
|
|
137
|
+
/* @__PURE__ */ u("h2", {
|
|
138
138
|
id: "global-filters-heading",
|
|
139
139
|
className: "text-sm font-semibold text-text-primary",
|
|
140
140
|
children: b("bigconsole.filters.bar.title", "Filters")
|
|
141
141
|
}),
|
|
142
|
-
/* @__PURE__ */
|
|
142
|
+
/* @__PURE__ */ u("span", {
|
|
143
143
|
className: "rounded-pill bg-bg-surface px-2 py-0.5 text-xs font-medium text-text-secondary",
|
|
144
144
|
children: x.length
|
|
145
145
|
}),
|
|
146
|
-
$ > 0 && /* @__PURE__ */
|
|
146
|
+
$ > 0 && /* @__PURE__ */ u("span", {
|
|
147
147
|
className: "rounded-pill bg-[var(--color-status-success-bgSubtle)] px-2 py-0.5 text-xs font-medium text-status-success-text",
|
|
148
148
|
children: b("bigconsole.filters.bar.activeCount", "{{count}} active", { count: $ ?? "" })
|
|
149
149
|
})
|
|
150
150
|
]
|
|
151
|
-
}), /* @__PURE__ */
|
|
151
|
+
}), /* @__PURE__ */ d("div", {
|
|
152
152
|
className: "flex min-w-0 flex-wrap items-center justify-end gap-control-gap",
|
|
153
153
|
children: [
|
|
154
|
-
y && /* @__PURE__ */
|
|
154
|
+
y && /* @__PURE__ */ u(o, {
|
|
155
155
|
presets: M,
|
|
156
156
|
activePresetId: N,
|
|
157
157
|
onApplyPreset: ne,
|
|
158
158
|
onSavePreset: re,
|
|
159
159
|
onDeletePreset: ie
|
|
160
160
|
}),
|
|
161
|
-
v && $ > 0 && /* @__PURE__ */
|
|
161
|
+
v && $ > 0 && /* @__PURE__ */ d("div", {
|
|
162
162
|
style: { position: "relative" },
|
|
163
|
-
children: [/* @__PURE__ */
|
|
163
|
+
children: [/* @__PURE__ */ d("button", {
|
|
164
164
|
type: "button",
|
|
165
165
|
onClick: te,
|
|
166
166
|
title: b("bigconsole.filters.bar.shareUrlTitle", "Copy shareable URL with filters"),
|
|
167
167
|
className: p,
|
|
168
|
-
children: [/* @__PURE__ */
|
|
168
|
+
children: [/* @__PURE__ */ d("svg", {
|
|
169
169
|
width: "14",
|
|
170
170
|
height: "14",
|
|
171
171
|
viewBox: "0 0 24 24",
|
|
@@ -173,9 +173,9 @@ function h({ dashboardId: e, onFilterChange: h, collapsible: g = !0, className:
|
|
|
173
173
|
stroke: "currentColor",
|
|
174
174
|
strokeWidth: "2",
|
|
175
175
|
children: [
|
|
176
|
-
/* @__PURE__ */
|
|
177
|
-
/* @__PURE__ */
|
|
178
|
-
/* @__PURE__ */
|
|
176
|
+
/* @__PURE__ */ u("path", { d: "M4 12v8a2 2 0 002 2h12a2 2 0 002-2v-8" }),
|
|
177
|
+
/* @__PURE__ */ u("polyline", { points: "16,6 12,2 8,6" }),
|
|
178
|
+
/* @__PURE__ */ u("line", {
|
|
179
179
|
x1: "12",
|
|
180
180
|
y1: "2",
|
|
181
181
|
x2: "12",
|
|
@@ -183,7 +183,7 @@ function h({ dashboardId: e, onFilterChange: h, collapsible: g = !0, className:
|
|
|
183
183
|
})
|
|
184
184
|
]
|
|
185
185
|
}), b("bigconsole.filters.bar.share", "Share")]
|
|
186
|
-
}), G && /* @__PURE__ */
|
|
186
|
+
}), G && /* @__PURE__ */ u("div", {
|
|
187
187
|
style: {
|
|
188
188
|
position: "absolute",
|
|
189
189
|
top: "100%",
|
|
@@ -191,8 +191,8 @@ function h({ dashboardId: e, onFilterChange: h, collapsible: g = !0, className:
|
|
|
191
191
|
transform: "translateX(-50%)",
|
|
192
192
|
marginTop: "4px",
|
|
193
193
|
padding: "6px 12px",
|
|
194
|
-
backgroundColor:
|
|
195
|
-
color:
|
|
194
|
+
backgroundColor: r.textPrimary,
|
|
195
|
+
color: r.bgPrimary,
|
|
196
196
|
fontSize: "12px",
|
|
197
197
|
borderRadius: "4px",
|
|
198
198
|
whiteSpace: "nowrap",
|
|
@@ -201,19 +201,19 @@ function h({ dashboardId: e, onFilterChange: h, collapsible: g = !0, className:
|
|
|
201
201
|
children: b("bigconsole.filters.bar.urlCopied", "URL copied!")
|
|
202
202
|
})]
|
|
203
203
|
}),
|
|
204
|
-
$ > 0 && /* @__PURE__ */
|
|
204
|
+
$ > 0 && /* @__PURE__ */ u("button", {
|
|
205
205
|
type: "button",
|
|
206
206
|
onClick: Q,
|
|
207
207
|
className: `${m} text-status-error-text`,
|
|
208
208
|
children: b("bigconsole.filters.bar.resetAll", "Reset all")
|
|
209
209
|
}),
|
|
210
|
-
/* @__PURE__ */
|
|
210
|
+
/* @__PURE__ */ u("button", {
|
|
211
211
|
type: "button",
|
|
212
212
|
onClick: J,
|
|
213
213
|
className: p,
|
|
214
214
|
children: b("bigconsole.filters.bar.addFilter", "+ Add Filter")
|
|
215
215
|
}),
|
|
216
|
-
g && /* @__PURE__ */
|
|
216
|
+
g && /* @__PURE__ */ u("button", {
|
|
217
217
|
type: "button",
|
|
218
218
|
onClick: ae,
|
|
219
219
|
"aria-expanded": !z,
|
|
@@ -224,11 +224,11 @@ function h({ dashboardId: e, onFilterChange: h, collapsible: g = !0, className:
|
|
|
224
224
|
]
|
|
225
225
|
})]
|
|
226
226
|
}),
|
|
227
|
-
!z && /* @__PURE__ */
|
|
227
|
+
!z && /* @__PURE__ */ u("div", {
|
|
228
228
|
id: "global-filters-chips",
|
|
229
229
|
className: "relative flex flex-wrap gap-control-gap px-card-padding py-control-gap",
|
|
230
230
|
style: { overflow: "visible" },
|
|
231
|
-
children: x.map((e) => /* @__PURE__ */
|
|
231
|
+
children: x.map((e) => /* @__PURE__ */ u(i, {
|
|
232
232
|
filter: e,
|
|
233
233
|
value: S[e.id],
|
|
234
234
|
onChange: (t) => q(e.id, t),
|
|
@@ -236,7 +236,7 @@ function h({ dashboardId: e, onFilterChange: h, collapsible: g = !0, className:
|
|
|
236
236
|
onDelete: () => Z(e.id)
|
|
237
237
|
}, e.id))
|
|
238
238
|
}),
|
|
239
|
-
V && /* @__PURE__ */
|
|
239
|
+
V && /* @__PURE__ */ u(a, {
|
|
240
240
|
filter: U,
|
|
241
241
|
onSave: X,
|
|
242
242
|
onClose: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GlobalFiltersBar.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.tsx"],"sourcesContent":["/**\n * GlobalFiltersBar Component\n *\n * Displays and manages global dashboard filters.\n * v2.0: Added URL persistence and filter presets support.\n */\n\nimport React, { useState, useEffect, useCallback } from 'react';\nimport {\n useGlobalFilters,\n useFilterUrlSync,\n useFilterPresets,\n type GlobalFilter,\n type FilterType,\n} from '../../../hooks';\nimport { FilterChip } from './FilterChip';\nimport { FilterEditor } from './FilterEditor';\nimport { FilterPresetSelector } from './FilterPresetSelector';\nimport { SEMANTIC_COLORS } from '../../../utils/semanticTokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { SECTION_FOCUS_RING } from '../../common';\n\n// ============================================================================\n// Local class vocabulary\n// ============================================================================\n\n/**\n * The filters bar configures the board below it. Before this it shipped a\n * saturated primary \"+ Add Filter\" with an indigo glow and a drop-shadowed\n * container, which made the configuration louder than the data it filtered.\n * Everything here is deliberately secondary weight.\n */\nconst SECONDARY_BUTTON_CLASSES = `inline-flex min-h-control-sm items-center gap-icon-gap rounded-button border border-[var(--color-border-subtle)] bg-bg-surface px-3 py-1 text-sm font-medium text-text-primary motion-safe:transition-colors hover:bg-bg-accentSubtle ${SECTION_FOCUS_RING}`;\n\n/** Text-only affordances (reset, collapse) sit a step below that again. */\nconst QUIET_BUTTON_CLASSES = `inline-flex min-h-control-sm items-center gap-icon-gap rounded-button px-2 py-1 text-sm font-medium text-text-secondary motion-safe:transition-colors hover:bg-bg-accentSubtle ${SECTION_FOCUS_RING}`;\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface GlobalFiltersBarProps {\n dashboardId: string;\n onFilterChange?: (values: Record<string, unknown>) => void;\n collapsible?: boolean;\n className?: string;\n /** Enable URL sync for shareable filtered views */\n enableUrlSync?: boolean;\n /** Enable filter presets */\n enablePresets?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function GlobalFiltersBar({\n dashboardId,\n onFilterChange,\n collapsible = true,\n className = '',\n enableUrlSync = true,\n enablePresets = true,\n}: GlobalFiltersBarProps) {\n const { t } = useI18n();\n const {\n filters,\n activeValues,\n loading,\n fetchFilters,\n createFilter,\n updateFilter,\n deleteFilter,\n setFilterValue,\n resetFilters,\n } = useGlobalFilters();\n\n // URL Sync hook\n const { getShareableUrl, clearUrlParams } = useFilterUrlSync({\n enabled: enableUrlSync,\n paramPrefix: 'filter_',\n debounceMs: 300,\n });\n\n // Filter Presets hook\n const {\n presets,\n activePresetId,\n loadPresets,\n applyPreset,\n saveCurrentAsPreset,\n deletePreset: removePreset,\n clearActivePreset,\n } = useFilterPresets(dashboardId);\n\n const [isCollapsed, setIsCollapsed] = useState(false);\n const [isEditorOpen, setIsEditorOpen] = useState(false);\n const [editingFilter, setEditingFilter] = useState<GlobalFilter | null>(null);\n const [showShareTooltip, setShowShareTooltip] = useState(false);\n\n // Fetch filters on mount\n useEffect(() => {\n if (dashboardId) {\n fetchFilters(dashboardId);\n if (enablePresets) {\n loadPresets(dashboardId);\n }\n }\n }, [dashboardId, fetchFilters, loadPresets, enablePresets]);\n\n // Notify parent of filter changes\n useEffect(() => {\n if (onFilterChange) {\n onFilterChange(activeValues);\n }\n }, [activeValues, onFilterChange]);\n\n // Clear active preset when filter values change manually\n useEffect(() => {\n if (activePresetId && Object.keys(activeValues).length > 0) {\n // Check if current values differ from active preset\n const preset = presets.find((p) => p.id === activePresetId);\n if (preset) {\n const presetStr = JSON.stringify(preset.values);\n const currentStr = JSON.stringify(activeValues);\n if (presetStr !== currentStr) {\n clearActivePreset();\n }\n }\n }\n }, [activeValues, activePresetId, presets, clearActivePreset]);\n\n // Handle filter value change\n const handleFilterChange = useCallback(\n (filterId: string, value: unknown) => {\n setFilterValue(filterId, value);\n },\n [setFilterValue]\n );\n\n // Handle add new filter\n const handleAddFilter = useCallback(() => {\n setEditingFilter(null);\n setIsEditorOpen(true);\n }, []);\n\n // Handle edit filter\n const handleEditFilter = useCallback((filter: GlobalFilter) => {\n setEditingFilter(filter);\n setIsEditorOpen(true);\n }, []);\n\n // Handle save filter\n const handleSaveFilter = useCallback(\n async (data: {\n name: string;\n type: FilterType;\n config: Record<string, unknown>;\n defaultValue?: unknown;\n isRequired?: boolean;\n }) => {\n if (editingFilter) {\n await updateFilter({\n id: editingFilter.id,\n name: data.name,\n config: data.config,\n defaultValue: data.defaultValue,\n isRequired: data.isRequired,\n });\n } else {\n await createFilter({\n dashboardId,\n name: data.name,\n type: data.type,\n config: data.config,\n defaultValue: data.defaultValue,\n isRequired: data.isRequired,\n });\n }\n setIsEditorOpen(false);\n setEditingFilter(null);\n },\n [dashboardId, editingFilter, createFilter, updateFilter]\n );\n\n // Handle delete filter\n const handleDeleteFilter = useCallback(\n async (filterId: string) => {\n await deleteFilter(filterId);\n },\n [deleteFilter]\n );\n\n // Handle reset all filters\n const handleResetAll = useCallback(() => {\n resetFilters();\n clearUrlParams();\n clearActivePreset();\n }, [resetFilters, clearUrlParams, clearActivePreset]);\n\n // Handle copy shareable URL\n const handleCopyShareableUrl = useCallback(async () => {\n const url = getShareableUrl();\n try {\n await navigator.clipboard.writeText(url);\n setShowShareTooltip(true);\n setTimeout(() => setShowShareTooltip(false), 2000);\n } catch {\n // Failed to copy URL to clipboard\n }\n }, [getShareableUrl]);\n\n // Handle apply preset\n const handleApplyPreset = useCallback(\n (presetId: string) => {\n applyPreset(presetId);\n },\n [applyPreset]\n );\n\n // Handle save current as preset\n const handleSavePreset = useCallback(\n (name: string, description?: string) => {\n saveCurrentAsPreset(name, description);\n },\n [saveCurrentAsPreset]\n );\n\n // Handle delete preset\n const handleDeletePreset = useCallback(\n (presetId: string) => {\n removePreset(presetId);\n },\n [removePreset]\n );\n\n // Toggle collapse\n const toggleCollapse = useCallback(() => {\n setIsCollapsed((prev) => !prev);\n }, []);\n\n // Count active filters\n const activeFilterCount = Object.keys(activeValues).filter(\n (key) => activeValues[key] !== undefined && activeValues[key] !== null && activeValues[key] !== ''\n ).length;\n\n if (loading && filters.length === 0) {\n return (\n <div\n className={`flex flex-wrap items-center gap-control-gap border-b border-[var(--color-border-subtle)] bg-bg-sunken px-card-padding py-control-gap ${className}`}\n role=\"status\"\n aria-live=\"polite\"\n data-testid=\"global-filters-loading\"\n >\n <span className=\"h-icon-sm w-icon-sm animate-spin rounded-full border-2 border-action-primary-bg border-t-transparent\" />\n <span className=\"text-sm text-text-secondary\">{t('bigconsole.filters.bar.loading', 'Loading filters...')}</span>\n </div>\n );\n }\n\n // Show empty state with \"Add Filter\" button when no filters exist\n if (filters.length === 0) {\n return (\n <div\n className={`flex flex-wrap items-center justify-between gap-control-gap border-b border-[var(--color-border-subtle)] bg-bg-sunken px-card-padding py-control-gap ${className}`}\n data-testid=\"global-filters-empty\"\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-icon-gap\">\n <h2 className=\"text-sm font-semibold text-text-primary\">{t('bigconsole.filters.bar.title', 'Filters')}</h2>\n <span className=\"text-sm text-text-muted\">\n {t('bigconsole.filters.bar.noFiltersConfigured', 'No filters configured')}\n </span>\n </div>\n <button type=\"button\" onClick={handleAddFilter} className={SECONDARY_BUTTON_CLASSES}>\n {t('bigconsole.filters.bar.addFilter', '+ Add Filter')}\n </button>\n\n {/* Filter Editor Modal */}\n {isEditorOpen && (\n <FilterEditor\n filter={editingFilter}\n onSave={handleSaveFilter}\n onClose={() => {\n setIsEditorOpen(false);\n setEditingFilter(null);\n }}\n />\n )}\n </div>\n );\n }\n\n return (\n <section\n // A configuration band, not a result: recessed surface, no drop shadow,\n // secondary-weight controls. It sits directly above the board and must\n // stay quieter than it.\n className={`flex min-w-0 flex-col border-b border-[var(--color-border-subtle)] bg-bg-sunken ${className}`}\n aria-labelledby=\"global-filters-heading\"\n data-testid=\"global-filters-bar\"\n >\n {/* Header */}\n <div\n className={`flex flex-wrap items-center justify-between gap-control-gap px-card-padding py-control-gap ${\n isCollapsed ? '' : 'border-b border-[var(--color-border-subtle)]'\n }`}\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-icon-gap\">\n <h2 id=\"global-filters-heading\" className=\"text-sm font-semibold text-text-primary\">\n {t('bigconsole.filters.bar.title', 'Filters')}\n </h2>\n <span className=\"rounded-pill bg-bg-surface px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {filters.length}\n </span>\n {activeFilterCount > 0 && (\n <span className=\"rounded-pill bg-[var(--color-status-success-bgSubtle)] px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {t('bigconsole.filters.bar.activeCount', '{{count}} active', {\n count: activeFilterCount ?? '',\n })}\n </span>\n )}\n </div>\n\n <div className=\"flex min-w-0 flex-wrap items-center justify-end gap-control-gap\">\n {/* Filter Presets Selector */}\n {enablePresets && (\n <FilterPresetSelector\n presets={presets}\n activePresetId={activePresetId}\n onApplyPreset={handleApplyPreset}\n onSavePreset={handleSavePreset}\n onDeletePreset={handleDeletePreset}\n />\n )}\n\n {/* Share URL Button */}\n {enableUrlSync && activeFilterCount > 0 && (\n <div style={{ position: 'relative' }}>\n <button\n type=\"button\"\n onClick={handleCopyShareableUrl}\n title={t('bigconsole.filters.bar.shareUrlTitle', 'Copy shareable URL with filters')}\n className={SECONDARY_BUTTON_CLASSES}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M4 12v8a2 2 0 002 2h12a2 2 0 002-2v-8\" />\n <polyline points=\"16,6 12,2 8,6\" />\n <line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"15\" />\n </svg>\n {t('bigconsole.filters.bar.share', 'Share')}\n </button>\n {showShareTooltip && (\n <div\n style={{\n position: 'absolute',\n top: '100%',\n left: '50%',\n transform: 'translateX(-50%)',\n marginTop: '4px',\n padding: '6px 12px',\n backgroundColor: SEMANTIC_COLORS.textPrimary,\n color: SEMANTIC_COLORS.bgPrimary,\n fontSize: '12px',\n borderRadius: '4px',\n whiteSpace: 'nowrap',\n zIndex: 100001,\n }}\n >\n {t('bigconsole.filters.bar.urlCopied', 'URL copied!')}\n </div>\n )}\n </div>\n )}\n\n {/* Reset Button */}\n {activeFilterCount > 0 && (\n <button type=\"button\" onClick={handleResetAll} className={`${QUIET_BUTTON_CLASSES} text-status-error-text`}>\n {t('bigconsole.filters.bar.resetAll', 'Reset all')}\n </button>\n )}\n\n {/* Add Filter Button */}\n <button type=\"button\" onClick={handleAddFilter} className={SECONDARY_BUTTON_CLASSES}>\n {t('bigconsole.filters.bar.addFilter', '+ Add Filter')}\n </button>\n\n {/* Collapse Button */}\n {collapsible && (\n <button\n type=\"button\"\n onClick={toggleCollapse}\n aria-expanded={!isCollapsed}\n // The chips container is unmounted while collapsed; pointing\n // aria-controls at a missing id is an invalid reference, so only\n // advertise it when the target actually exists.\n aria-controls={isCollapsed ? undefined : 'global-filters-chips'}\n className={QUIET_BUTTON_CLASSES}\n >\n {isCollapsed\n ? t('bigconsole.filters.bar.expand', '▼ Expand')\n : t('bigconsole.filters.bar.collapse', '▲ Collapse')}\n </button>\n )}\n </div>\n </div>\n\n {/* Filters List */}\n {!isCollapsed && (\n <div\n id=\"global-filters-chips\"\n className=\"relative flex flex-wrap gap-control-gap px-card-padding py-control-gap\"\n style={{ overflow: 'visible' }}\n >\n {filters.map((filter) => (\n <FilterChip\n key={filter.id}\n filter={filter}\n value={activeValues[filter.id]}\n onChange={(value) => handleFilterChange(filter.id, value)}\n onEdit={() => handleEditFilter(filter)}\n onDelete={() => handleDeleteFilter(filter.id)}\n />\n ))}\n </div>\n )}\n\n {/* Filter Editor Modal */}\n {isEditorOpen && (\n <FilterEditor\n filter={editingFilter}\n onSave={handleSaveFilter}\n onClose={() => {\n setIsEditorOpen(false);\n setEditingFilter(null);\n }}\n />\n )}\n </section>\n );\n}\n\nexport default GlobalFiltersBar;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GlobalFiltersBar.js","names":[],"sources":["../../../../../src/bigconsole/components/dashboard/GlobalFiltersBar/GlobalFiltersBar.tsx"],"sourcesContent":["/**\n * GlobalFiltersBar Component\n *\n * Displays and manages global dashboard filters.\n * v2.0: Added URL persistence and filter presets support.\n */\n\nimport React, { useState, useEffect, useCallback } from 'react';\nimport {\n useGlobalFilters,\n useFilterUrlSync,\n useFilterPresets,\n type GlobalFilter,\n type FilterType,\n} from '../../../hooks';\nimport { FilterChip } from './FilterChip';\nimport { FilterEditor } from './FilterEditor';\nimport { FilterPresetSelector } from './FilterPresetSelector';\nimport { SEMANTIC_COLORS } from '../../../utils/semanticTokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { SECTION_FOCUS_RING } from '../../common';\n\n// ============================================================================\n// Local class vocabulary\n// ============================================================================\n\n/**\n * The filters bar configures the board below it. Before this it shipped a\n * saturated primary \"+ Add Filter\" with an indigo glow and a drop-shadowed\n * container, which made the configuration louder than the data it filtered.\n * Everything here is deliberately secondary weight.\n */\nconst SECONDARY_BUTTON_CLASSES = `inline-flex min-h-control-sm items-center gap-icon-gap rounded-button border border-[var(--color-border-subtle)] bg-bg-surface px-3 py-1 text-sm font-medium text-text-primary motion-safe:transition-colors hover:bg-bg-accentSubtle ${SECTION_FOCUS_RING}`;\n\n/** Text-only affordances (reset, collapse) sit a step below that again. */\nconst QUIET_BUTTON_CLASSES = `inline-flex min-h-control-sm items-center gap-icon-gap rounded-button px-2 py-1 text-sm font-medium text-text-secondary motion-safe:transition-colors hover:bg-bg-accentSubtle ${SECTION_FOCUS_RING}`;\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface GlobalFiltersBarProps {\n dashboardId: string;\n onFilterChange?: (values: Record<string, unknown>) => void;\n collapsible?: boolean;\n className?: string;\n /** Enable URL sync for shareable filtered views */\n enableUrlSync?: boolean;\n /** Enable filter presets */\n enablePresets?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function GlobalFiltersBar({\n dashboardId,\n onFilterChange,\n collapsible = true,\n className = '',\n enableUrlSync = true,\n enablePresets = true,\n}: GlobalFiltersBarProps) {\n const { t } = useI18n();\n const {\n filters,\n activeValues,\n loading,\n fetchFilters,\n createFilter,\n updateFilter,\n deleteFilter,\n setFilterValue,\n resetFilters,\n } = useGlobalFilters();\n\n // URL Sync hook\n const { getShareableUrl, clearUrlParams } = useFilterUrlSync({\n enabled: enableUrlSync,\n paramPrefix: 'filter_',\n debounceMs: 300,\n });\n\n // Filter Presets hook\n const {\n presets,\n activePresetId,\n loadPresets,\n applyPreset,\n saveCurrentAsPreset,\n deletePreset: removePreset,\n clearActivePreset,\n } = useFilterPresets(dashboardId);\n\n const [isCollapsed, setIsCollapsed] = useState(false);\n const [isEditorOpen, setIsEditorOpen] = useState(false);\n const [editingFilter, setEditingFilter] = useState<GlobalFilter | null>(null);\n const [showShareTooltip, setShowShareTooltip] = useState(false);\n\n // Fetch filters on mount\n useEffect(() => {\n if (dashboardId) {\n fetchFilters(dashboardId);\n if (enablePresets) {\n loadPresets(dashboardId);\n }\n }\n }, [dashboardId, fetchFilters, loadPresets, enablePresets]);\n\n // Notify parent of filter changes\n useEffect(() => {\n if (onFilterChange) {\n onFilterChange(activeValues);\n }\n }, [activeValues, onFilterChange]);\n\n // Clear active preset when filter values change manually\n useEffect(() => {\n if (activePresetId && Object.keys(activeValues).length > 0) {\n // Check if current values differ from active preset\n const preset = presets.find((p) => p.id === activePresetId);\n if (preset) {\n const presetStr = JSON.stringify(preset.values);\n const currentStr = JSON.stringify(activeValues);\n if (presetStr !== currentStr) {\n clearActivePreset();\n }\n }\n }\n }, [activeValues, activePresetId, presets, clearActivePreset]);\n\n // Handle filter value change\n const handleFilterChange = useCallback(\n (filterId: string, value: unknown) => {\n setFilterValue(filterId, value);\n },\n [setFilterValue]\n );\n\n // Handle add new filter\n const handleAddFilter = useCallback(() => {\n setEditingFilter(null);\n setIsEditorOpen(true);\n }, []);\n\n // Handle edit filter\n const handleEditFilter = useCallback((filter: GlobalFilter) => {\n setEditingFilter(filter);\n setIsEditorOpen(true);\n }, []);\n\n // Handle save filter\n const handleSaveFilter = useCallback(\n async (data: {\n name: string;\n type: FilterType;\n config: Record<string, unknown>;\n defaultValue?: unknown;\n isRequired?: boolean;\n }) => {\n if (editingFilter) {\n await updateFilter({\n id: editingFilter.id,\n name: data.name,\n config: data.config,\n defaultValue: data.defaultValue,\n isRequired: data.isRequired,\n });\n } else {\n await createFilter({\n dashboardId,\n name: data.name,\n type: data.type,\n config: data.config,\n defaultValue: data.defaultValue,\n isRequired: data.isRequired,\n });\n }\n setIsEditorOpen(false);\n setEditingFilter(null);\n },\n [dashboardId, editingFilter, createFilter, updateFilter]\n );\n\n // Handle delete filter\n const handleDeleteFilter = useCallback(\n async (filterId: string) => {\n await deleteFilter(filterId);\n },\n [deleteFilter]\n );\n\n // Handle reset all filters\n const handleResetAll = useCallback(() => {\n resetFilters();\n clearUrlParams();\n clearActivePreset();\n }, [resetFilters, clearUrlParams, clearActivePreset]);\n\n // Handle copy shareable URL\n const handleCopyShareableUrl = useCallback(async () => {\n const url = getShareableUrl();\n try {\n await navigator.clipboard.writeText(url);\n setShowShareTooltip(true);\n setTimeout(() => setShowShareTooltip(false), 2000);\n } catch {\n // Failed to copy URL to clipboard\n }\n }, [getShareableUrl]);\n\n // Handle apply preset\n const handleApplyPreset = useCallback(\n (presetId: string) => {\n applyPreset(presetId);\n },\n [applyPreset]\n );\n\n // Handle save current as preset\n const handleSavePreset = useCallback(\n (name: string, description?: string) => {\n saveCurrentAsPreset(name, description);\n },\n [saveCurrentAsPreset]\n );\n\n // Handle delete preset\n const handleDeletePreset = useCallback(\n (presetId: string) => {\n removePreset(presetId);\n },\n [removePreset]\n );\n\n // Toggle collapse\n const toggleCollapse = useCallback(() => {\n setIsCollapsed((prev) => !prev);\n }, []);\n\n // Count active filters\n const activeFilterCount = Object.keys(activeValues).filter(\n (key) => activeValues[key] !== undefined && activeValues[key] !== null && activeValues[key] !== ''\n ).length;\n\n if (loading && filters.length === 0) {\n return (\n <div\n className={`flex flex-wrap items-center gap-control-gap border-b border-[var(--color-border-subtle)] bg-bg-sunken px-card-padding py-control-gap ${className}`}\n role=\"status\"\n aria-live=\"polite\"\n data-testid=\"global-filters-loading\"\n >\n <span className=\"h-icon-sm w-icon-sm animate-spin rounded-full border-2 border-action-primary-bg border-t-transparent\" />\n <span className=\"text-sm text-text-secondary\">{t('bigconsole.filters.bar.loading', 'Loading filters...')}</span>\n </div>\n );\n }\n\n // Show empty state with \"Add Filter\" button when no filters exist\n if (filters.length === 0) {\n return (\n <div\n className={`flex flex-wrap items-center justify-between gap-control-gap border-b border-[var(--color-border-subtle)] bg-bg-sunken px-card-padding py-control-gap ${className}`}\n data-testid=\"global-filters-empty\"\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-icon-gap\">\n <h2 className=\"text-sm font-semibold text-text-primary\">{t('bigconsole.filters.bar.title', 'Filters')}</h2>\n <span className=\"text-sm text-text-muted\">\n {t('bigconsole.filters.bar.noFiltersConfigured', 'No filters configured')}\n </span>\n </div>\n <button type=\"button\" onClick={handleAddFilter} className={SECONDARY_BUTTON_CLASSES}>\n {t('bigconsole.filters.bar.addFilter', '+ Add Filter')}\n </button>\n\n {/* Filter Editor Modal */}\n {isEditorOpen && (\n <FilterEditor\n filter={editingFilter}\n onSave={handleSaveFilter}\n onClose={() => {\n setIsEditorOpen(false);\n setEditingFilter(null);\n }}\n />\n )}\n </div>\n );\n }\n\n return (\n <section\n // A configuration band, not a result: recessed surface, no drop shadow,\n // secondary-weight controls. It sits directly above the board and must\n // stay quieter than it.\n className={`flex min-w-0 flex-col border-b border-[var(--color-border-subtle)] bg-bg-sunken ${className}`}\n aria-labelledby=\"global-filters-heading\"\n data-testid=\"global-filters-bar\"\n >\n {/* Header */}\n <div\n className={`flex flex-wrap items-center justify-between gap-control-gap px-card-padding py-control-gap ${\n isCollapsed ? '' : 'border-b border-[var(--color-border-subtle)]'\n }`}\n >\n <div className=\"flex min-w-0 flex-wrap items-center gap-icon-gap\">\n <h2 id=\"global-filters-heading\" className=\"text-sm font-semibold text-text-primary\">\n {t('bigconsole.filters.bar.title', 'Filters')}\n </h2>\n <span className=\"rounded-pill bg-bg-surface px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {filters.length}\n </span>\n {activeFilterCount > 0 && (\n <span className=\"rounded-pill bg-[var(--color-status-success-bgSubtle)] px-2 py-0.5 text-xs font-medium text-status-success-text\">\n {t('bigconsole.filters.bar.activeCount', '{{count}} active', {\n count: activeFilterCount ?? '',\n })}\n </span>\n )}\n </div>\n\n <div className=\"flex min-w-0 flex-wrap items-center justify-end gap-control-gap\">\n {/* Filter Presets Selector */}\n {enablePresets && (\n <FilterPresetSelector\n presets={presets}\n activePresetId={activePresetId}\n onApplyPreset={handleApplyPreset}\n onSavePreset={handleSavePreset}\n onDeletePreset={handleDeletePreset}\n />\n )}\n\n {/* Share URL Button */}\n {enableUrlSync && activeFilterCount > 0 && (\n <div style={{ position: 'relative' }}>\n <button\n type=\"button\"\n onClick={handleCopyShareableUrl}\n title={t('bigconsole.filters.bar.shareUrlTitle', 'Copy shareable URL with filters')}\n className={SECONDARY_BUTTON_CLASSES}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M4 12v8a2 2 0 002 2h12a2 2 0 002-2v-8\" />\n <polyline points=\"16,6 12,2 8,6\" />\n <line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"15\" />\n </svg>\n {t('bigconsole.filters.bar.share', 'Share')}\n </button>\n {showShareTooltip && (\n <div\n style={{\n position: 'absolute',\n top: '100%',\n left: '50%',\n transform: 'translateX(-50%)',\n marginTop: '4px',\n padding: '6px 12px',\n backgroundColor: SEMANTIC_COLORS.textPrimary,\n color: SEMANTIC_COLORS.bgPrimary,\n fontSize: '12px',\n borderRadius: '4px',\n whiteSpace: 'nowrap',\n zIndex: 100001,\n }}\n >\n {t('bigconsole.filters.bar.urlCopied', 'URL copied!')}\n </div>\n )}\n </div>\n )}\n\n {/* Reset Button */}\n {activeFilterCount > 0 && (\n <button type=\"button\" onClick={handleResetAll} className={`${QUIET_BUTTON_CLASSES} text-status-error-text`}>\n {t('bigconsole.filters.bar.resetAll', 'Reset all')}\n </button>\n )}\n\n {/* Add Filter Button */}\n <button type=\"button\" onClick={handleAddFilter} className={SECONDARY_BUTTON_CLASSES}>\n {t('bigconsole.filters.bar.addFilter', '+ Add Filter')}\n </button>\n\n {/* Collapse Button */}\n {collapsible && (\n <button\n type=\"button\"\n onClick={toggleCollapse}\n aria-expanded={!isCollapsed}\n // The chips container is unmounted while collapsed; pointing\n // aria-controls at a missing id is an invalid reference, so only\n // advertise it when the target actually exists.\n aria-controls={isCollapsed ? undefined : 'global-filters-chips'}\n className={QUIET_BUTTON_CLASSES}\n >\n {isCollapsed\n ? t('bigconsole.filters.bar.expand', '▼ Expand')\n : t('bigconsole.filters.bar.collapse', '▲ Collapse')}\n </button>\n )}\n </div>\n </div>\n\n {/* Filters List */}\n {!isCollapsed && (\n <div\n id=\"global-filters-chips\"\n className=\"relative flex flex-wrap gap-control-gap px-card-padding py-control-gap\"\n style={{ overflow: 'visible' }}\n >\n {filters.map((filter) => (\n <FilterChip\n key={filter.id}\n filter={filter}\n value={activeValues[filter.id]}\n onChange={(value) => handleFilterChange(filter.id, value)}\n onEdit={() => handleEditFilter(filter)}\n onDelete={() => handleDeleteFilter(filter.id)}\n />\n ))}\n </div>\n )}\n\n {/* Filter Editor Modal */}\n {isEditorOpen && (\n <FilterEditor\n filter={editingFilter}\n onSave={handleSaveFilter}\n onClose={() => {\n setIsEditorOpen(false);\n setEditingFilter(null);\n }}\n />\n )}\n </section>\n );\n}\n\nexport default GlobalFiltersBar;\n"],"mappings":"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"}
|