@burdenoff/microfe-bigconsole 2026.924.1 → 2026.924.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/BigConsoleRoutes.js +137 -138
- package/dist/bigconsole/BigConsoleRoutes.js.map +1 -1
- package/dist/bigconsole/assistant/previewController.js +10 -10
- package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js +25 -0
- package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js.map +1 -0
- 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 +103 -103
- 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 +62 -61
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewPanel.js +82 -81
- package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
- 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/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 +10 -10
- 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/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/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
|
@@ -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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"}
|