@burdenoff/microfe-bigconsole 2026.929.2 → 2026.930.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/bigconsole/assistant/assistantRunPersistence.js +111 -0
  2. package/dist/bigconsole/assistant/assistantRunPersistence.js.map +1 -0
  3. package/dist/bigconsole/assistant/assistantRunStore.js +87 -48
  4. package/dist/bigconsole/assistant/assistantRunStore.js.map +1 -1
  5. package/dist/bigconsole/assistant/previewController.js +19 -50
  6. package/dist/bigconsole/assistant/previewController.js.map +1 -1
  7. package/dist/bigconsole/assistant/useAssistantRunWorkspace.js +14 -0
  8. package/dist/bigconsole/assistant/useAssistantRunWorkspace.js.map +1 -0
  9. package/dist/bigconsole/components/DrilldownDashboardRenderer.js +74 -50
  10. package/dist/bigconsole/components/DrilldownDashboardRenderer.js.map +1 -1
  11. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +9 -4
  12. package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
  13. package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js +68 -40
  14. package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js.map +1 -1
  15. package/dist/bigconsole/components/embed/drilldown.js +26 -0
  16. package/dist/bigconsole/components/embed/drilldown.js.map +1 -0
  17. package/dist/bigconsole/components/embed/index.js +4 -3
  18. package/dist/bigconsole/components/embed/types.js.map +1 -1
  19. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +32 -30
  20. package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
  21. package/dist/bigconsole/components/preview/AiPreviewPanel.js +38 -36
  22. package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
  23. package/dist/bigconsole/components/preview/previewShared.js +1 -1
  24. package/dist/bigconsole/components/preview/previewShared.js.map +1 -1
  25. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +307 -233
  26. package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
  27. package/dist/bigconsole/components/widgets/PropertiesPanel.js +230 -220
  28. package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
  29. package/dist/bigconsole/components/widgets/WidgetWrapper.js +18 -19
  30. package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
  31. package/dist/bigconsole/context/BigConsoleContext.js +4 -1
  32. package/dist/bigconsole/context/BigConsoleContext.js.map +1 -1
  33. package/dist/bigconsole/hooks/useActivePage.js +25 -0
  34. package/dist/bigconsole/hooks/useActivePage.js.map +1 -0
  35. package/dist/bigconsole/hooks/useDrilldown.js +118 -71
  36. package/dist/bigconsole/hooks/useDrilldown.js.map +1 -1
  37. package/dist/bigconsole/hooks/useDrilldownOperations.js +32 -26
  38. package/dist/bigconsole/hooks/useDrilldownOperations.js.map +1 -1
  39. package/dist/bigconsole/hooks/useFilterUrlSync.js +1 -1
  40. package/dist/bigconsole/hooks/useFilterUrlSync.js.map +1 -1
  41. package/dist/bigconsole/pages/DashboardBuilderPage.js +190 -185
  42. package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
  43. package/dist/bigconsole/pages/DashboardViewPage.js +224 -217
  44. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  45. package/dist/generated/wspace-operations.js +132 -86
  46. package/dist/generated/wspace-operations.js.map +1 -1
  47. package/dist/generated/wspace-types.js.map +1 -1
  48. package/package.json +3 -3
  49. package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js +0 -25
  50. package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"useFilterUrlSync.js","names":[],"sources":["../../../src/bigconsole/hooks/useFilterUrlSync.ts"],"sourcesContent":["/**\n * useFilterUrlSync Hook\n *\n * Syncs global filter values to URL query parameters for sharing filtered views.\n * Supports all filter types: DATE_RANGE, CATEGORY, TEXT_SEARCH, NUMERIC_RANGE, CUSTOM\n *\n * Also handles drilldown context parameters:\n * - filter_* : Global filter values\n * - ctx_* : Drilldown context values (clicked item data)\n * - drilldown_* : Drilldown metadata (depth, parent, path)\n */\n\nimport { useEffect, useCallback, useRef, useMemo } from 'react';\nimport { useLocation } from 'react-router';\nimport { useGlobalFilterValues, useDashboardStore } from '../store';\nimport { CONTEXT_PARAM_PREFIX, FILTER_PARAM_PREFIX, DRILLDOWN_PARAM_PREFIX } from './useDrilldown';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface UseFilterUrlSyncOptions {\n /** Whether URL sync is enabled */\n enabled?: boolean;\n /** Prefix for URL params (default: 'filter_') */\n paramPrefix?: string;\n /** Debounce delay in ms for URL updates (default: 300) */\n debounceMs?: number;\n}\n\ninterface DrilldownMetadata {\n /** Current drilldown depth */\n depth: number;\n /** Parent dashboard ID */\n parent: string | null;\n /** Array of dashboard IDs in the drilldown path */\n path: string[];\n}\n\ninterface FilterUrlSyncResult {\n /** Manually sync current filters to URL */\n syncToUrl: () => void;\n /** Get shareable URL with current filters */\n getShareableUrl: () => string;\n /** Clear all filter params from URL */\n clearUrlParams: () => void;\n /** Get context params from URL (ctx_*) */\n getContextFromUrl: () => Record<string, unknown>;\n /** Get drilldown metadata from URL */\n getDrilldownMetadata: () => DrilldownMetadata;\n /** Check if currently in drilldown mode */\n isInDrilldown: boolean;\n /** Current drilldown depth */\n drilldownDepth: number;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Encode filter value for URL\n */\nfunction encodeFilterValue(value: unknown): string {\n if (value === null || value === undefined) {\n return '';\n }\n\n // Array (multi-select): [\"a\", \"b\"] -> \"a,b\"\n if (Array.isArray(value)) {\n return value.map((v) => encodeURIComponent(String(v))).join(',');\n }\n\n // Object types\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n\n // Date range: {start, end} -> \"start,end\"\n if (obj.start || obj.end) {\n return `${obj.start || ''},${obj.end || ''}`;\n }\n\n // Numeric range: {min, max} -> \"min,max\"\n if (obj.min !== undefined || obj.max !== undefined) {\n return `${obj.min ?? ''},${obj.max ?? ''}`;\n }\n\n // Object with value property (category)\n if (obj.value !== undefined) {\n return encodeURIComponent(String(obj.value));\n }\n\n // Generic object - JSON stringify\n return encodeURIComponent(JSON.stringify(obj));\n }\n\n // Simple value\n return encodeURIComponent(String(value));\n}\n\n/**\n * Decode filter value from URL\n */\nfunction decodeFilterValue(encoded: string, filterType?: string): unknown {\n if (!encoded) {\n return undefined;\n }\n\n // Date range\n if (filterType === 'DATE_RANGE' || (encoded.includes('T') && encoded.includes(','))) {\n const parts = encoded.split(',');\n if (parts.length === 2) {\n return {\n start: parts[0] || undefined,\n end: parts[1] || undefined,\n };\n }\n }\n\n // Numeric range (check if both parts are numbers)\n if (filterType === 'NUMERIC_RANGE' || /^-?\\d*\\.?\\d*,-?\\d*\\.?\\d*$/.test(encoded)) {\n const parts = encoded.split(',');\n if (parts.length === 2) {\n const min = parts[0] ? parseFloat(parts[0]) : undefined;\n const max = parts[1] ? parseFloat(parts[1]) : undefined;\n if (!isNaN(min as number) || !isNaN(max as number)) {\n return { min, max };\n }\n }\n }\n\n // Multi-select array\n if (encoded.includes(',') && filterType === 'CATEGORY') {\n return encoded.split(',').map((v) => decodeURIComponent(v));\n }\n\n // Try to parse as JSON\n try {\n const decoded = decodeURIComponent(encoded);\n return JSON.parse(decoded);\n } catch {\n // Simple value\n return decodeURIComponent(encoded);\n }\n}\n\n/**\n * Decode a context param value from URL\n */\nfunction decodeContextValue(encoded: string): unknown {\n if (!encoded) {\n return undefined;\n }\n const decoded = decodeURIComponent(encoded);\n // Try to parse as JSON\n try {\n return JSON.parse(decoded);\n } catch {\n return decoded;\n }\n}\n\n/**\n * Parse URL search params to filter values\n */\nfunction parseUrlToFilters(search: string, paramPrefix: string): Record<string, unknown> {\n const params = new URLSearchParams(search);\n const filters: Record<string, unknown> = {};\n\n params.forEach((value, key) => {\n if (key.startsWith(paramPrefix)) {\n const filterId = key.slice(paramPrefix.length);\n // Try to detect type from value format\n filters[filterId] = decodeFilterValue(value);\n }\n });\n\n return filters;\n}\n\n/**\n * Parse URL search params to context values\n */\nfunction parseUrlToContext(search: string): Record<string, unknown> {\n const params = new URLSearchParams(search);\n const context: Record<string, unknown> = {};\n\n params.forEach((value, key) => {\n if (key.startsWith(CONTEXT_PARAM_PREFIX)) {\n const contextKey = key.slice(CONTEXT_PARAM_PREFIX.length);\n context[contextKey] = decodeContextValue(value);\n }\n });\n\n return context;\n}\n\n/**\n * Parse drilldown metadata from URL\n */\nfunction parseUrlToDrilldownMetadata(search: string): DrilldownMetadata {\n const params = new URLSearchParams(search);\n const depthStr = params.get(`${DRILLDOWN_PARAM_PREFIX}depth`);\n const parent = params.get(`${DRILLDOWN_PARAM_PREFIX}parent`);\n const pathStr = params.get(`${DRILLDOWN_PARAM_PREFIX}path`);\n\n let path: string[] = [];\n if (pathStr) {\n try {\n path = JSON.parse(decodeURIComponent(pathStr));\n } catch {\n path = [];\n }\n }\n\n return {\n depth: depthStr ? parseInt(depthStr, 10) : 0,\n parent: parent || null,\n path,\n };\n}\n\n/**\n * Build URL search params from filter values\n */\nfunction filtersToUrlParams(filters: Record<string, unknown>, paramPrefix: string): URLSearchParams {\n const params = new URLSearchParams();\n\n Object.entries(filters).forEach(([filterId, value]) => {\n if (value !== undefined && value !== null && value !== '') {\n const encoded = encodeFilterValue(value);\n if (encoded) {\n params.set(`${paramPrefix}${filterId}`, encoded);\n }\n }\n });\n\n return params;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useFilterUrlSync(options: UseFilterUrlSyncOptions = {}): FilterUrlSyncResult {\n const { enabled = true, paramPrefix = FILTER_PARAM_PREFIX, debounceMs = 300 } = options;\n\n // Use react-router's useLocation to react to URL changes\n const location = useLocation();\n\n const filterValues = useGlobalFilterValues();\n const setGlobalFilterValue = useDashboardStore((state) => state.setGlobalFilterValue);\n\n // Track if initial load from URL is done\n const initialLoadDone = useRef(false);\n // Track last synced values to avoid loops\n const lastSyncedValues = useRef<string>('');\n // Debounce timer\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n // Track the last pathname to detect same-route navigations (e.g. drilldown)\n const lastPathname = useRef(location.pathname);\n\n // Reset initialLoadDone when pathname changes (e.g. drilldown to different dashboard).\n // Without this, same-route navigation reuses the component and the filter_* URL params\n // from the drilldown URL are never loaded into the Zustand store.\n useEffect(() => {\n if (location.pathname !== lastPathname.current) {\n initialLoadDone.current = false;\n lastSyncedValues.current = '';\n lastPathname.current = location.pathname;\n }\n }, [location.pathname]);\n\n // Compute drilldown state from URL - now reactive to location.search changes\n const drilldownMetadata = useMemo(() => {\n if (typeof window === 'undefined') {\n return { depth: 0, parent: null, path: [] };\n }\n return parseUrlToDrilldownMetadata(location.search);\n }, [location.search]);\n\n const isInDrilldown = drilldownMetadata.depth > 0;\n const drilldownDepth = drilldownMetadata.depth;\n\n // Load filters from URL on initial mount\n useEffect(() => {\n if (!enabled || initialLoadDone.current) return;\n\n // Parse filter params\n const urlFilters = parseUrlToFilters(window.location.search, paramPrefix);\n\n if (Object.keys(urlFilters).length > 0) {\n // Set each filter value\n Object.entries(urlFilters).forEach(([filterId, value]) => {\n setGlobalFilterValue(filterId, value);\n });\n }\n\n // IMPORTANT: Set lastSyncedValues to current filterValues AFTER the sync effect runs\n // This prevents the race condition where sync effect runs with stale (empty) filterValues\n // and strips the URL params that we just loaded.\n // We use the URL filters as the initial synced values since that's what we loaded from.\n lastSyncedValues.current = JSON.stringify(urlFilters);\n\n initialLoadDone.current = true;\n }, [enabled, paramPrefix, setGlobalFilterValue]);\n\n // Sync filter values to URL when they change\n useEffect(() => {\n if (!enabled || !initialLoadDone.current) return;\n\n const currentValues = JSON.stringify(filterValues);\n\n // Skip if values haven't changed\n if (currentValues === lastSyncedValues.current) {\n return;\n }\n\n // Debounce URL updates\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n\n debounceTimer.current = setTimeout(() => {\n const params = filtersToUrlParams(filterValues, paramPrefix);\n\n // Get current URL without filter params (but keep other params like ctx_*, drilldown_*)\n const currentParams = new URLSearchParams(window.location.search);\n // Remove existing filter params only\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n // Merge with new filter params\n params.forEach((value, key) => {\n currentParams.set(key, value);\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n // Update URL without triggering navigation\n window.history.replaceState({ ...window.history.state, filters: filterValues }, '', newUrl);\n\n lastSyncedValues.current = currentValues;\n }, debounceMs);\n\n return () => {\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n };\n }, [enabled, filterValues, paramPrefix, debounceMs]);\n\n // Manual sync to URL\n const syncToUrl = useCallback(() => {\n if (!enabled) return;\n\n const params = filtersToUrlParams(filterValues, paramPrefix);\n const currentParams = new URLSearchParams(window.location.search);\n\n // Remove existing filter params only\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n // Merge with new filter params\n params.forEach((value, key) => {\n currentParams.set(key, value);\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n window.history.replaceState({ ...window.history.state, filters: filterValues }, '', newUrl);\n\n lastSyncedValues.current = JSON.stringify(filterValues);\n }, [enabled, filterValues, paramPrefix]);\n\n // Get shareable URL\n const getShareableUrl = useCallback((): string => {\n const params = filtersToUrlParams(filterValues, paramPrefix);\n const baseUrl = `${window.location.origin}${window.location.pathname}`;\n\n // Preserve non-filter params (ctx_*, drilldown_*)\n const currentParams = new URLSearchParams(window.location.search);\n Array.from(currentParams.keys()).forEach((key) => {\n if (!key.startsWith(paramPrefix)) {\n params.set(key, currentParams.get(key) || '');\n }\n });\n\n const search = params.toString();\n return search ? `${baseUrl}?${search}` : baseUrl;\n }, [filterValues, paramPrefix]);\n\n // Clear all filter params from URL\n const clearUrlParams = useCallback(() => {\n const currentParams = new URLSearchParams(window.location.search);\n\n // Remove filter params only (keep ctx_*, drilldown_*)\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n window.history.replaceState({}, '', newUrl);\n lastSyncedValues.current = '{}';\n }, [paramPrefix]);\n\n // Get context params from URL\n const getContextFromUrl = useCallback((): Record<string, unknown> => {\n return parseUrlToContext(window.location.search);\n }, []);\n\n // Get drilldown metadata from URL\n const getDrilldownMetadata = useCallback((): DrilldownMetadata => {\n return parseUrlToDrilldownMetadata(window.location.search);\n }, []);\n\n return {\n syncToUrl,\n getShareableUrl,\n clearUrlParams,\n getContextFromUrl,\n getDrilldownMetadata,\n isInDrilldown,\n drilldownDepth,\n };\n}\n\nexport default useFilterUrlSync;\n"],"mappings":"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"}
1
+ {"version":3,"file":"useFilterUrlSync.js","names":[],"sources":["../../../src/bigconsole/hooks/useFilterUrlSync.ts"],"sourcesContent":["/**\n * useFilterUrlSync Hook\n *\n * Syncs global filter values to URL query parameters for sharing filtered views.\n * Supports all filter types: DATE_RANGE, CATEGORY, TEXT_SEARCH, NUMERIC_RANGE, CUSTOM\n *\n * Also handles drilldown context parameters:\n * - filter_* : Global filter values\n * - ctx_* : Drilldown context values (clicked item data)\n * - drilldown_* : Drilldown metadata (depth, parent, path)\n */\n\nimport { useEffect, useCallback, useRef, useMemo } from 'react';\nimport { useLocation } from 'react-router';\nimport { useGlobalFilterValues, useDashboardStore } from '../store';\nimport { CONTEXT_PARAM_PREFIX, FILTER_PARAM_PREFIX, DRILLDOWN_PARAM_PREFIX } from './useDrilldown';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface UseFilterUrlSyncOptions {\n /** Whether URL sync is enabled */\n enabled?: boolean;\n /** Prefix for URL params (default: 'filter_') */\n paramPrefix?: string;\n /** Debounce delay in ms for URL updates (default: 300) */\n debounceMs?: number;\n}\n\ninterface DrilldownMetadata {\n /** Current drilldown depth */\n depth: number;\n /** Parent dashboard ID */\n parent: string | null;\n /** Array of dashboard IDs in the drilldown path */\n path: string[];\n}\n\ninterface FilterUrlSyncResult {\n /** Manually sync current filters to URL */\n syncToUrl: () => void;\n /** Get shareable URL with current filters */\n getShareableUrl: () => string;\n /** Clear all filter params from URL */\n clearUrlParams: () => void;\n /** Get context params from URL (ctx_*) */\n getContextFromUrl: () => Record<string, unknown>;\n /** Get drilldown metadata from URL */\n getDrilldownMetadata: () => DrilldownMetadata;\n /** Check if currently in drilldown mode */\n isInDrilldown: boolean;\n /** Current drilldown depth */\n drilldownDepth: number;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Encode filter value for URL\n */\nfunction encodeFilterValue(value: unknown): string {\n if (value === null || value === undefined) {\n return '';\n }\n\n // Array (multi-select): [\"a\", \"b\"] -> \"a,b\"\n if (Array.isArray(value)) {\n return value.map((v) => encodeURIComponent(String(v))).join(',');\n }\n\n // Object types\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n\n // Date range: {start, end} -> \"start,end\"\n if (obj.start || obj.end) {\n return `${obj.start || ''},${obj.end || ''}`;\n }\n\n // Numeric range: {min, max} -> \"min,max\"\n if (obj.min !== undefined || obj.max !== undefined) {\n return `${obj.min ?? ''},${obj.max ?? ''}`;\n }\n\n // Object with value property (category)\n if (obj.value !== undefined) {\n return encodeURIComponent(String(obj.value));\n }\n\n // Generic object - JSON stringify\n return encodeURIComponent(JSON.stringify(obj));\n }\n\n // Simple value\n return encodeURIComponent(String(value));\n}\n\n/**\n * Decode filter value from URL\n */\nfunction decodeFilterValue(encoded: string, filterType?: string): unknown {\n if (!encoded) {\n return undefined;\n }\n\n // Date range\n if (filterType === 'DATE_RANGE' || (encoded.includes('T') && encoded.includes(','))) {\n const parts = encoded.split(',');\n if (parts.length === 2) {\n return {\n start: parts[0] || undefined,\n end: parts[1] || undefined,\n };\n }\n }\n\n // Numeric range (check if both parts are numbers)\n if (filterType === 'NUMERIC_RANGE' || /^-?\\d*\\.?\\d*,-?\\d*\\.?\\d*$/.test(encoded)) {\n const parts = encoded.split(',');\n if (parts.length === 2) {\n const min = parts[0] ? parseFloat(parts[0]) : undefined;\n const max = parts[1] ? parseFloat(parts[1]) : undefined;\n if (!isNaN(min as number) || !isNaN(max as number)) {\n return { min, max };\n }\n }\n }\n\n // Multi-select array\n if (encoded.includes(',') && filterType === 'CATEGORY') {\n return encoded.split(',').map((v) => decodeURIComponent(v));\n }\n\n // Try to parse as JSON\n try {\n const decoded = decodeURIComponent(encoded);\n return JSON.parse(decoded);\n } catch {\n // Simple value\n return decodeURIComponent(encoded);\n }\n}\n\n/**\n * Decode a context param value from URL\n */\nfunction decodeContextValue(encoded: string): unknown {\n if (!encoded) {\n return undefined;\n }\n const decoded = decodeURIComponent(encoded);\n // Try to parse as JSON\n try {\n return JSON.parse(decoded);\n } catch {\n return decoded;\n }\n}\n\n/**\n * Parse URL search params to filter values\n */\nfunction parseUrlToFilters(search: string, paramPrefix: string): Record<string, unknown> {\n const params = new URLSearchParams(search);\n const filters: Record<string, unknown> = {};\n\n params.forEach((value, key) => {\n if (key.startsWith(paramPrefix)) {\n const filterId = key.slice(paramPrefix.length);\n // Try to detect type from value format\n filters[filterId] = decodeFilterValue(value);\n }\n });\n\n return filters;\n}\n\n/**\n * Parse URL search params to context values\n */\nfunction parseUrlToContext(search: string): Record<string, unknown> {\n const params = new URLSearchParams(search);\n const context: Record<string, unknown> = {};\n\n params.forEach((value, key) => {\n if (key.startsWith(CONTEXT_PARAM_PREFIX)) {\n const contextKey = key.slice(CONTEXT_PARAM_PREFIX.length);\n context[contextKey] = decodeContextValue(value);\n }\n });\n\n return context;\n}\n\n/**\n * Parse drilldown metadata from URL\n */\nfunction parseUrlToDrilldownMetadata(search: string): DrilldownMetadata {\n const params = new URLSearchParams(search);\n const depthStr = params.get(`${DRILLDOWN_PARAM_PREFIX}depth`);\n const parent = params.get(`${DRILLDOWN_PARAM_PREFIX}parent`);\n const pathStr = params.get(`${DRILLDOWN_PARAM_PREFIX}path`);\n\n let path: string[] = [];\n if (pathStr) {\n try {\n path = JSON.parse(decodeURIComponent(pathStr));\n } catch {\n path = [];\n }\n }\n\n return {\n depth: depthStr ? parseInt(depthStr, 10) : 0,\n parent: parent || null,\n path,\n };\n}\n\n/**\n * Build URL search params from filter values\n */\nfunction filtersToUrlParams(filters: Record<string, unknown>, paramPrefix: string): URLSearchParams {\n const params = new URLSearchParams();\n\n Object.entries(filters).forEach(([filterId, value]) => {\n if (value !== undefined && value !== null && value !== '') {\n const encoded = encodeFilterValue(value);\n if (encoded) {\n params.set(`${paramPrefix}${filterId}`, encoded);\n }\n }\n });\n\n return params;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useFilterUrlSync(options: UseFilterUrlSyncOptions = {}): FilterUrlSyncResult {\n const { enabled = true, paramPrefix = FILTER_PARAM_PREFIX, debounceMs = 300 } = options;\n\n // Use react-router's useLocation to react to URL changes\n const location = useLocation();\n\n const filterValues = useGlobalFilterValues();\n const setGlobalFilterValue = useDashboardStore((state) => state.setGlobalFilterValue);\n\n // Track if initial load from URL is done\n const initialLoadDone = useRef(false);\n // Track last synced values to avoid loops\n const lastSyncedValues = useRef<string>('');\n // Debounce timer\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n // Track the last pathname to detect same-route navigations (e.g. drilldown)\n const lastPathname = useRef(location.pathname);\n\n // Reset initialLoadDone when pathname changes (e.g. drilldown to different dashboard).\n // Without this, same-route navigation reuses the component and the filter_* URL params\n // from the drilldown URL are never loaded into the Zustand store.\n useEffect(() => {\n if (location.pathname !== lastPathname.current) {\n initialLoadDone.current = false;\n lastSyncedValues.current = '';\n lastPathname.current = location.pathname;\n }\n }, [location.pathname]);\n\n // Compute drilldown state from URL - now reactive to location.search changes\n const drilldownMetadata = useMemo(() => {\n if (typeof window === 'undefined') {\n return { depth: 0, parent: null, path: [] };\n }\n return parseUrlToDrilldownMetadata(location.search);\n }, [location.search]);\n\n const isInDrilldown = drilldownMetadata.depth > 0;\n const drilldownDepth = drilldownMetadata.depth;\n\n // Load filters from URL on initial mount\n useEffect(() => {\n if (!enabled || initialLoadDone.current) return;\n\n // Parse filter params\n const urlFilters = parseUrlToFilters(window.location.search, paramPrefix);\n\n if (Object.keys(urlFilters).length > 0) {\n // Set each filter value\n Object.entries(urlFilters).forEach(([filterId, value]) => {\n setGlobalFilterValue(filterId, value);\n });\n }\n\n // IMPORTANT: Set lastSyncedValues to current filterValues AFTER the sync effect runs\n // This prevents the race condition where sync effect runs with stale (empty) filterValues\n // and strips the URL params that we just loaded.\n // We use the URL filters as the initial synced values since that's what we loaded from.\n lastSyncedValues.current = JSON.stringify(urlFilters);\n\n initialLoadDone.current = true;\n }, [enabled, paramPrefix, setGlobalFilterValue]);\n\n // Sync filter values to URL when they change\n useEffect(() => {\n if (!enabled || !initialLoadDone.current) return;\n\n const currentValues = JSON.stringify(filterValues);\n\n // Skip if values haven't changed\n if (currentValues === lastSyncedValues.current) {\n return;\n }\n\n // Debounce URL updates\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n\n debounceTimer.current = setTimeout(() => {\n const params = filtersToUrlParams(filterValues, paramPrefix);\n\n // Get current URL without filter params (but keep other params like ctx_*, drilldown_*)\n const currentParams = new URLSearchParams(window.location.search);\n // Remove existing filter params only\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n // Merge with new filter params\n params.forEach((value, key) => {\n currentParams.set(key, value);\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n // Update URL without triggering navigation\n window.history.replaceState({ ...window.history.state, filters: filterValues }, '', newUrl);\n\n lastSyncedValues.current = currentValues;\n }, debounceMs);\n\n return () => {\n if (debounceTimer.current) {\n clearTimeout(debounceTimer.current);\n }\n };\n }, [enabled, filterValues, paramPrefix, debounceMs]);\n\n // Manual sync to URL\n const syncToUrl = useCallback(() => {\n if (!enabled) return;\n\n const params = filtersToUrlParams(filterValues, paramPrefix);\n const currentParams = new URLSearchParams(window.location.search);\n\n // Remove existing filter params only\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n // Merge with new filter params\n params.forEach((value, key) => {\n currentParams.set(key, value);\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n window.history.replaceState({ ...window.history.state, filters: filterValues }, '', newUrl);\n\n lastSyncedValues.current = JSON.stringify(filterValues);\n }, [enabled, filterValues, paramPrefix]);\n\n // Get shareable URL\n const getShareableUrl = useCallback((): string => {\n const params = filtersToUrlParams(filterValues, paramPrefix);\n const baseUrl = `${window.location.origin}${window.location.pathname}`;\n\n // Preserve non-filter params (ctx_*, drilldown_*)\n const currentParams = new URLSearchParams(window.location.search);\n Array.from(currentParams.keys()).forEach((key) => {\n if (!key.startsWith(paramPrefix)) {\n params.set(key, currentParams.get(key) || '');\n }\n });\n\n const search = params.toString();\n return search ? `${baseUrl}?${search}` : baseUrl;\n }, [filterValues, paramPrefix]);\n\n // Clear all filter params from URL\n const clearUrlParams = useCallback(() => {\n const currentParams = new URLSearchParams(window.location.search);\n\n // Remove filter params only (keep ctx_*, drilldown_*)\n Array.from(currentParams.keys()).forEach((key) => {\n if (key.startsWith(paramPrefix)) {\n currentParams.delete(key);\n }\n });\n\n const finalSearch = currentParams.toString();\n const newUrl = finalSearch ? `${window.location.pathname}?${finalSearch}` : window.location.pathname;\n\n // Keep the router's history state (its `key`/`idx`): wiping it breaks Back\n // navigation across drilldowns and page switches (BOFF-7625).\n window.history.replaceState(window.history.state, '', newUrl);\n lastSyncedValues.current = '{}';\n }, [paramPrefix]);\n\n // Get context params from URL\n const getContextFromUrl = useCallback((): Record<string, unknown> => {\n return parseUrlToContext(window.location.search);\n }, []);\n\n // Get drilldown metadata from URL\n const getDrilldownMetadata = useCallback((): DrilldownMetadata => {\n return parseUrlToDrilldownMetadata(window.location.search);\n }, []);\n\n return {\n syncToUrl,\n getShareableUrl,\n clearUrlParams,\n getContextFromUrl,\n getDrilldownMetadata,\n isInDrilldown,\n drilldownDepth,\n };\n}\n\nexport default useFilterUrlSync;\n"],"mappings":"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"}
@@ -8,51 +8,52 @@ import { Section as i } from "../components/common/Section.js";
8
8
  import "../components/common/index.js";
9
9
  import a from "../hooks/useWidgetOperations.js";
10
10
  import o from "../hooks/useDashboardOperations.js";
11
- import s from "../hooks/usePageOperations.js";
11
+ import s from "../hooks/useActivePage.js";
12
+ import c from "../hooks/usePageOperations.js";
12
13
  import "../hooks/index.js";
13
- import { BIGCONSOLE_PERMISSIONS as c } from "../../constants/permissions.js";
14
- import { BuilderCanvas as ee } from "../components/builder/BuilderCanvas.js";
15
- import { BuilderTopbar as te } from "../components/builder/BuilderTopbar.js";
16
- import { DashboardEditorShell as l } from "../components/builder/DashboardEditorShell.js";
17
- import { findNextAvailablePosition as ne } from "../utils/widgetPositioning.js";
14
+ import { BIGCONSOLE_PERMISSIONS as l } from "../../constants/permissions.js";
15
+ import { BuilderCanvas as u } from "../components/builder/BuilderCanvas.js";
16
+ import { BuilderTopbar as d } from "../components/builder/BuilderTopbar.js";
17
+ import { DashboardEditorShell as f } from "../components/builder/DashboardEditorShell.js";
18
+ import { findNextAvailablePosition as ee } from "../utils/widgetPositioning.js";
18
19
  import "../components/builder/index.js";
19
- import re from "../components/dashboard/DashboardCanvas.js";
20
- import ie from "../components/dashboard/ExportDashboardDialog.js";
20
+ import te from "../components/dashboard/DashboardCanvas.js";
21
+ import ne from "../components/dashboard/ExportDashboardDialog.js";
21
22
  import "../components/dashboard/index.js";
22
- import ae from "../customWidget/CustomWidgetBuilderSheet.js";
23
+ import re from "../customWidget/CustomWidgetBuilderSheet.js";
23
24
  import "../customWidget/index.js";
24
- import { memo as u, useCallback as d, useEffect as f, useState as p } from "react";
25
- import { useParams as oe } from "react-router";
26
- import { AccessDenied as se } from "@burdenoff/fe-libs/shared/components";
27
- import { usePermissions as ce } from "@burdenoff/fe-libs/shared/providers/shell";
28
- import { Button as le } from "@burdenoff/fe-libs/ui";
29
- import { jsx as m, jsxs as h } from "react/jsx-runtime";
30
- import { useI18n as ue } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
25
+ import { memo as p, useCallback as m, useEffect as h, useState as g } from "react";
26
+ import { useParams as ie } from "react-router";
27
+ import { AccessDenied as ae } from "@burdenoff/fe-libs/shared/components";
28
+ import { usePermissions as oe } from "@burdenoff/fe-libs/shared/providers/shell";
29
+ import { Button as se } from "@burdenoff/fe-libs/ui";
30
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
31
+ import { useI18n as ce } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
31
32
  //#region src/bigconsole/pages/DashboardBuilderPage.tsx
32
- function g() {
33
+ function y() {
33
34
  try {
34
35
  return window.__breadcrumbRegistry;
35
36
  } catch {
36
37
  return;
37
38
  }
38
39
  }
39
- function de(e, t) {
40
- let n = g();
40
+ function le(e, t) {
41
+ let n = y();
41
42
  n?.register && n.register(e, t);
42
43
  }
43
- function fe(e) {
44
- let t = g();
44
+ function ue(e) {
45
+ let t = y();
45
46
  t?.unregister && t.unregister(e);
46
47
  }
47
- var _ = u(function() {
48
- let { dashboardId: u } = oe(), g = n(), { t: _ } = ue(), { hasPermission: v, isLoading: y } = ce(), b = v(c.DASHBOARD_READ), x = v(c.DASHBOARD_UPDATE), pe = v(c.DASHBOARD_EXPORT), me = v(c.WIDGET_CREATE), [S, C] = p(!1), [w, T] = p(null), [E, D] = p(!1), [he, O] = p(!1), [k, A] = p(null), { fetchDashboard: j, updateDashboard: M, loading: N, error: P } = o(), { createWidget: F, updateWidget: I, deleteWidget: L, duplicateWidget: R, batchUpdatePositions: z } = a(void 0, u), { createPage: B, updatePage: V, deletePage: H, reorderPages: U } = s(), W = t((e) => e.setCurrentDashboard), G = t((e) => e.currentDashboard), K = t((e) => e.setViewMode), q = e((e) => e.selectWidget), J = e((e) => e.widgets), Y = e((e) => e.clipboardWidgetId), X = e((e) => e.clipboardAction), Z = e((e) => e.clearClipboard);
49
- f(() => (K("edit"), () => K("view")), [K]), f(() => {
50
- if (!u) {
51
- g("/dashboards");
48
+ var b = p(function() {
49
+ let { dashboardId: p } = ie(), y = n(), { t: b } = ce(), { hasPermission: x, isLoading: S } = oe(), C = x(l.DASHBOARD_READ), de = x(l.DASHBOARD_UPDATE), fe = x(l.DASHBOARD_EXPORT), pe = x(l.WIDGET_CREATE), [me, w] = g(!1), [T, E] = g(null), [D, O] = g(!1), [he, k] = g(!1), { fetchDashboard: A, updateDashboard: j, loading: M, error: N } = o(), { createWidget: P, updateWidget: F, deleteWidget: I, duplicateWidget: L, batchUpdatePositions: R } = a(void 0, p), { createPage: z, updatePage: B, deletePage: V, reorderPages: H } = c(), U = t((e) => e.setCurrentDashboard), W = t((e) => e.currentDashboard), { activePageId: G, setActivePage: K } = s(W?.id === p ? W?.pages : void 0), q = t((e) => e.setViewMode), J = e((e) => e.selectWidget), Y = e((e) => e.widgets), X = e((e) => e.clipboardWidgetId), Z = e((e) => e.clipboardAction), Q = e((e) => e.clearClipboard);
50
+ h(() => (q("edit"), () => q("view")), [q]), h(() => {
51
+ if (!p) {
52
+ y("/dashboards");
52
53
  return;
53
54
  }
54
- return j(u).then((e) => {
55
- e ? (W({
55
+ return A(p).then((e) => {
56
+ e ? (U({
56
57
  ...e,
57
58
  title: e.name,
58
59
  description: e.description ?? "",
@@ -63,37 +64,36 @@ var _ = u(function() {
63
64
  index: t,
64
65
  layoutConfig: e.layoutConfig ?? {}
65
66
  }))
66
- }), e.pages.length > 0 && !k && A(e.pages[0].id), e.name && de(u, e.name)) : g("/dashboards");
67
+ }), e.name && le(p, e.name)) : y("/dashboards");
67
68
  }), () => {
68
- u && fe(u);
69
+ p && ue(p);
69
70
  };
70
71
  }, [
71
- u,
72
- g,
73
- W,
74
- j,
75
- k
72
+ p,
73
+ y,
74
+ U,
75
+ A
76
76
  ]);
77
- let Q = d(async () => {
78
- let e = k ?? G?.pages?.[0]?.id;
79
- if (!Y || !u || !e) return;
80
- let t = J.get(Y);
77
+ let $ = m(async () => {
78
+ let e = G ?? W?.pages?.[0]?.id;
79
+ if (!X || !p || !e) return;
80
+ let t = Y.get(X);
81
81
  if (!t) {
82
- Z();
82
+ Q();
83
83
  return;
84
84
  }
85
- let n = Array.from(J.values()).filter((t) => t.pageId === e), r = {
85
+ let n = Array.from(Y.values()).filter((t) => t.pageId === e), r = {
86
86
  width: t.positionWidth ?? 4,
87
87
  height: t.positionHeight ?? 4
88
- }, { x: i, y: a } = ne(n, r, { cols: 12 }), o = {
88
+ }, { x: i, y: a } = ee(n, r, { cols: 12 }), o = {
89
89
  x: i,
90
90
  y: a,
91
91
  width: r.width,
92
92
  height: r.height
93
- }, s = X === "copy" ? _("bigconsole.dashboards.builder.actions.copySuffix", "{{title}} (Copy)", { title: t.title }) : t.title, c = await R(Y, o, s).catch(() => null);
94
- c ||= await F({
93
+ }, s = Z === "copy" ? b("bigconsole.dashboards.builder.actions.copySuffix", "{{title}} (Copy)", { title: t.title }) : t.title, c = await L(X, o, s).catch(() => null);
94
+ c ||= await P({
95
95
  pageId: e,
96
- dashboardId: u,
96
+ dashboardId: p,
97
97
  type: t.type,
98
98
  title: s,
99
99
  description: t.description,
@@ -105,22 +105,22 @@ var _ = u(function() {
105
105
  position: o,
106
106
  refreshInterval: t.refreshInterval ?? void 0,
107
107
  metadata: t.metadata
108
- }), c && (q(c.id), X === "cut" && await L(Y)), Z();
108
+ }), c && (J(c.id), Z === "cut" && await I(X)), Q();
109
109
  }, [
110
- Y,
111
110
  X,
112
- u,
113
- k,
111
+ Z,
112
+ p,
114
113
  G,
115
- J,
116
- R,
117
- F,
114
+ W,
115
+ Y,
118
116
  L,
119
- q,
120
- Z,
121
- _
117
+ P,
118
+ I,
119
+ J,
120
+ Q,
121
+ b
122
122
  ]);
123
- f(() => {
123
+ h(() => {
124
124
  let n = (n) => {
125
125
  if (t.getState().viewMode !== "edit" || n.target?.closest("input, textarea, select, [contenteditable=\"true\"]") || document.querySelector("[data-slot=\"dialog-overlay\"]")) return;
126
126
  let r = e.getState(), i = r.selectedWidgetId;
@@ -129,22 +129,22 @@ var _ = u(function() {
129
129
  return;
130
130
  }
131
131
  if ((n.key === "Delete" || n.key === "Backspace") && i) {
132
- n.preventDefault(), L(i), r.selectWidget(null);
132
+ n.preventDefault(), I(i), r.selectWidget(null);
133
133
  return;
134
134
  }
135
- (n.metaKey || n.ctrlKey) && (n.key === "c" && i ? r.copyWidget(i) : n.key === "x" && i ? r.cutWidget(i) : n.key === "v" && (n.preventDefault(), Q()));
135
+ (n.metaKey || n.ctrlKey) && (n.key === "c" && i ? r.copyWidget(i) : n.key === "x" && i ? r.cutWidget(i) : n.key === "v" && (n.preventDefault(), $()));
136
136
  };
137
137
  return document.addEventListener("keydown", n, !0), () => document.removeEventListener("keydown", n, !0);
138
- }, [L, Q]);
139
- let ge = d(() => {
140
- g("/dashboards");
141
- }, [g]), _e = d(async () => {
142
- if (u) {
143
- C(!0), T(null);
138
+ }, [I, $]);
139
+ let ge = m(() => {
140
+ y("/dashboards");
141
+ }, [y]), _e = m(async () => {
142
+ if (p) {
143
+ w(!0), E(null);
144
144
  try {
145
145
  let t = e.getState(), n = Array.from(t.widgets.values()), r = t.dirtyWidgetIds, i = n.filter((e) => r.has(e.id));
146
146
  if (i.length > 0) {
147
- let e = i.map((e) => I({
147
+ let e = i.map((e) => F({
148
148
  id: e.id,
149
149
  title: e.title,
150
150
  config: e.config,
@@ -156,7 +156,7 @@ var _ = u(function() {
156
156
  }));
157
157
  await Promise.all(e), t.clearAllDirty();
158
158
  }
159
- n.length > 0 && await z(n.map((e) => ({
159
+ n.length > 0 && await R(n.map((e) => ({
160
160
  id: e.id,
161
161
  position: e.position || {
162
162
  x: e.positionX ?? 0,
@@ -164,209 +164,214 @@ var _ = u(function() {
164
164
  width: e.positionWidth ?? 4,
165
165
  height: e.positionHeight ?? 4
166
166
  }
167
- }))), G && await M({
168
- id: u,
169
- name: G.title,
170
- description: G.description
167
+ }))), W && await j({
168
+ id: p,
169
+ name: W.title,
170
+ description: W.description
171
171
  });
172
172
  } catch (e) {
173
- T(e instanceof Error ? e.message : _("bigconsole.dashboards.builder.errors.saveFailed", "Failed to save dashboard"));
173
+ E(e instanceof Error ? e.message : b("bigconsole.dashboards.builder.errors.saveFailed", "Failed to save dashboard"));
174
174
  } finally {
175
- C(!1);
175
+ w(!1);
176
176
  }
177
177
  }
178
178
  }, [
179
- u,
180
- G,
181
- I,
182
- z,
183
- M,
184
- _
185
- ]), ve = d(() => {
186
- D(!0);
187
- }, []), $ = d(() => {
188
- D(!1);
189
- }, []), ye = d((e) => {
190
- A(e);
191
- }, []), be = d(async () => {
192
- if (!G || !u) return;
193
- let e = await B({
194
- dashboardId: u,
195
- name: _("bigconsole.dashboards.builder.actions.newPageName", "Page {{count}}", { count: G.pages.length + 1 })
179
+ p,
180
+ W,
181
+ F,
182
+ R,
183
+ j,
184
+ b
185
+ ]), ve = m(() => {
186
+ O(!0);
187
+ }, []), ye = m(() => {
188
+ O(!1);
189
+ }, []), be = m((e) => {
190
+ K(e);
191
+ }, [K]), xe = m(async () => {
192
+ if (!W || !p) return;
193
+ let e = await z({
194
+ dashboardId: p,
195
+ name: b("bigconsole.dashboards.builder.actions.newPageName", "Page {{count}}", { count: W.pages.length + 1 })
196
196
  });
197
- e && (W({
198
- ...G,
199
- pages: [...G.pages, {
197
+ e && (U({
198
+ ...W,
199
+ pages: [...W.pages, {
200
200
  id: e.id,
201
201
  title: e.name,
202
202
  index: e.order,
203
203
  layoutConfig: e.layoutConfig || {}
204
204
  }]
205
- }), A(e.id));
205
+ }), K(e.id));
206
206
  }, [
207
- G,
208
- u,
209
207
  W,
210
- B,
211
- _
212
- ]), xe = d(async (e, t) => {
213
- G && await V({
208
+ p,
209
+ U,
210
+ z,
211
+ K,
212
+ b
213
+ ]), Se = m(async (e, t) => {
214
+ W && await B({
214
215
  id: e,
215
216
  name: t
216
- }) && W({
217
- ...G,
218
- pages: G.pages.map((n) => n.id === e ? {
217
+ }) && U({
218
+ ...W,
219
+ pages: W.pages.map((n) => n.id === e ? {
219
220
  ...n,
220
221
  title: t
221
222
  } : n)
222
223
  });
223
224
  }, [
224
- G,
225
225
  W,
226
- V
227
- ]), Se = d(async (e) => {
228
- if (!G || !u) return;
229
- let t = G.pages.find((t) => t.id === e);
226
+ U,
227
+ B
228
+ ]), Ce = m(async (e) => {
229
+ if (!W || !p) return;
230
+ let t = W.pages.find((t) => t.id === e);
230
231
  if (!t) return;
231
- let n = await B({
232
- dashboardId: u,
233
- name: _("bigconsole.dashboards.builder.actions.copySuffix", "{{title}} (Copy)", { title: t.title ?? "" })
232
+ let n = await z({
233
+ dashboardId: p,
234
+ name: b("bigconsole.dashboards.builder.actions.copySuffix", "{{title}} (Copy)", { title: t.title ?? "" })
234
235
  });
235
- n && (W({
236
- ...G,
237
- pages: [...G.pages, {
236
+ n && (U({
237
+ ...W,
238
+ pages: [...W.pages, {
238
239
  id: n.id,
239
240
  title: n.name,
240
241
  index: n.order,
241
242
  layoutConfig: n.layoutConfig || {}
242
243
  }]
243
- }), A(n.id));
244
+ }), K(n.id));
244
245
  }, [
245
- G,
246
- u,
247
246
  W,
248
- B,
249
- _
250
- ]), Ce = d(async (e) => {
251
- if (!(!G || G.pages.length <= 1) && await H(e)) {
252
- let t = G.pages.filter((t) => t.id !== e).map((e, t) => ({
247
+ p,
248
+ U,
249
+ z,
250
+ K,
251
+ b
252
+ ]), we = m(async (e) => {
253
+ if (!(!W || W.pages.length <= 1) && await V(e)) {
254
+ let t = W.pages.filter((t) => t.id !== e).map((e, t) => ({
253
255
  ...e,
254
256
  index: t
255
257
  }));
256
- W({
257
- ...G,
258
+ U({
259
+ ...W,
258
260
  pages: t
259
- }), k === e && A(t[0]?.id || null);
261
+ });
262
+ let n = t[0]?.id;
263
+ G === e && n && K(n, { replace: !0 });
260
264
  }
261
265
  }, [
262
- G,
263
266
  W,
264
- k,
265
- H
266
- ]), we = d(async (e) => {
267
- !G || !u || (W({
268
- ...G,
267
+ U,
268
+ G,
269
+ V,
270
+ K
271
+ ]), Te = m(async (e) => {
272
+ !W || !p || (U({
273
+ ...W,
269
274
  pages: e.map((e, t) => ({
270
275
  ...e,
271
276
  index: t
272
277
  }))
273
- }), await U(u, e.map((e) => e.id)));
278
+ }), await H(p, e.map((e) => e.id)));
274
279
  }, [
275
- G,
276
- u,
277
280
  W,
278
- U
281
+ p,
282
+ U,
283
+ H
279
284
  ]);
280
- return y ? /* @__PURE__ */ m("div", {
285
+ return S ? /* @__PURE__ */ _("div", {
281
286
  className: "flex items-center justify-center h-full",
282
- children: /* @__PURE__ */ m("div", { className: "animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full" })
283
- }) : b ? P ? /* @__PURE__ */ m("div", {
287
+ children: /* @__PURE__ */ _("div", { className: "animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full" })
288
+ }) : C ? N ? /* @__PURE__ */ _("div", {
284
289
  className: "flex h-full flex-col items-center justify-center p-6",
285
- children: /* @__PURE__ */ m(i, {
290
+ children: /* @__PURE__ */ _(i, {
286
291
  headingLevel: 2,
287
292
  tone: "danger",
288
- title: _("bigconsole.dashboards.builder.errors.loadFailed", "Failed to load dashboard"),
289
- description: /* @__PURE__ */ m("span", {
293
+ title: b("bigconsole.dashboards.builder.errors.loadFailed", "Failed to load dashboard"),
294
+ description: /* @__PURE__ */ _("span", {
290
295
  className: "break-words",
291
- children: P.message
296
+ children: N.message
292
297
  }),
293
298
  className: "w-full max-w-md",
294
299
  "data-testid": "dashboard-builder-error",
295
- actions: /* @__PURE__ */ m(le, {
300
+ actions: /* @__PURE__ */ _(se, {
296
301
  size: "sm",
297
- onClick: () => g("/dashboards"),
298
- children: _("bigconsole.dashboards.builder.actions.backToDashboards", "Back to Dashboards")
302
+ onClick: () => y("/dashboards"),
303
+ children: b("bigconsole.dashboards.builder.actions.backToDashboards", "Back to Dashboards")
299
304
  })
300
305
  })
301
- }) : N || !G ? /* @__PURE__ */ h("div", {
306
+ }) : M || !W ? /* @__PURE__ */ v("div", {
302
307
  className: "flex items-center justify-center h-full",
303
308
  role: "status",
304
309
  "aria-live": "polite",
305
- children: [/* @__PURE__ */ m("div", { className: "animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full" }), /* @__PURE__ */ m("span", {
310
+ children: [/* @__PURE__ */ _("div", { className: "animate-spin w-8 h-8 border-4 border-action-primary-bg border-t-transparent rounded-full" }), /* @__PURE__ */ _("span", {
306
311
  className: "sr-only",
307
312
  children: "Loading dashboard…"
308
313
  })]
309
- }) : /* @__PURE__ */ h(l, {
310
- topbar: /* @__PURE__ */ m(te, {
311
- title: G.title || _("bigconsole.dashboards.builder.header.untitledDashboard", "Untitled dashboard"),
314
+ }) : /* @__PURE__ */ v(f, {
315
+ topbar: /* @__PURE__ */ _(d, {
316
+ title: W.title || b("bigconsole.dashboards.builder.header.untitledDashboard", "Untitled dashboard"),
312
317
  onBack: ge,
313
- actions: /* @__PURE__ */ h("div", {
318
+ actions: /* @__PURE__ */ v("div", {
314
319
  className: "flex items-center gap-2",
315
- children: [w ? /* @__PURE__ */ m("span", {
320
+ children: [T ? /* @__PURE__ */ _("span", {
316
321
  className: "text-xs font-medium text-status-error-text",
317
322
  role: "alert",
318
- children: w
319
- }) : null, me ? /* @__PURE__ */ m("button", {
323
+ children: T
324
+ }) : null, pe ? /* @__PURE__ */ _("button", {
320
325
  type: "button",
321
326
  "data-testid": "open-custom-widget-builder",
322
- onClick: () => O(!0),
327
+ onClick: () => k(!0),
323
328
  className: `inline-flex min-h-control-md items-center rounded-button border border-[var(--color-border-subtle)] px-2 py-1 text-xs font-medium text-text-primary motion-safe:transition-colors hover:bg-bg-sunken ${r}`,
324
- children: _("bigconsole.dashboards.builder.toolbar.aiWidget", "AI widget")
329
+ children: b("bigconsole.dashboards.builder.toolbar.aiWidget", "AI widget")
325
330
  }) : null]
326
331
  })
327
332
  }),
328
333
  children: [
329
- /* @__PURE__ */ m(ee, { canvas: /* @__PURE__ */ m(re, {
330
- dashboardId: G.id,
331
- pageId: k || G.pages?.[0]?.id,
332
- title: G.title,
333
- onPaste: Q,
334
- onSave: x ? _e : void 0,
335
- isSaving: S,
336
- onExport: pe ? ve : void 0,
337
- pages: G.pages.map((e) => ({
334
+ /* @__PURE__ */ _(u, { canvas: /* @__PURE__ */ _(te, {
335
+ dashboardId: W.id,
336
+ pageId: G || W.pages?.[0]?.id,
337
+ title: W.title,
338
+ onPaste: $,
339
+ onSave: de ? _e : void 0,
340
+ isSaving: me,
341
+ onExport: fe ? ve : void 0,
342
+ pages: W.pages.map((e) => ({
338
343
  id: e.id,
339
- name: e.title || _("bigconsole.dashboards.builder.actions.newPageName", "Page {{count}}", { count: (e.index ?? 0) + 1 }),
344
+ name: e.title || b("bigconsole.dashboards.builder.actions.newPageName", "Page {{count}}", { count: (e.index ?? 0) + 1 }),
340
345
  order: e.index ?? 0
341
346
  })),
342
- activePageId: k || G.pages?.[0]?.id,
343
- onPageChange: ye,
344
- onPageAdd: be,
345
- onPageRename: xe,
346
- onPageDuplicate: Se,
347
- onPageDelete: Ce,
348
- onPagesReorder: we,
347
+ activePageId: G || W.pages?.[0]?.id,
348
+ onPageChange: be,
349
+ onPageAdd: xe,
350
+ onPageRename: Se,
351
+ onPageDuplicate: Ce,
352
+ onPageDelete: we,
353
+ onPagesReorder: Te,
349
354
  showPageTabs: !0
350
355
  }) }),
351
- /* @__PURE__ */ m(ie, {
352
- isOpen: E,
353
- dashboardId: G.id,
354
- dashboardName: G.title,
355
- onClose: $
356
+ /* @__PURE__ */ _(ne, {
357
+ isOpen: D,
358
+ dashboardId: W.id,
359
+ dashboardName: W.title,
360
+ onClose: ye
356
361
  }),
357
- /* @__PURE__ */ m(ae, {
362
+ /* @__PURE__ */ _(re, {
358
363
  isOpen: he,
359
- onClose: () => O(!1),
360
- dashboardId: G.id,
361
- pageId: k || G.pages?.[0]?.id || ""
364
+ onClose: () => k(!1),
365
+ dashboardId: W.id,
366
+ pageId: G || W.pages?.[0]?.id || ""
362
367
  })
363
368
  ]
364
- }) : /* @__PURE__ */ m(se, {
365
- title: _("bigconsole.dashboards.builder.errors.accessDeniedTitle", "Access Denied"),
366
- description: _("bigconsole.dashboards.builder.errors.accessDeniedDescription", "You don't have permission to view this dashboard.")
369
+ }) : /* @__PURE__ */ _(ae, {
370
+ title: b("bigconsole.dashboards.builder.errors.accessDeniedTitle", "Access Denied"),
371
+ description: b("bigconsole.dashboards.builder.errors.accessDeniedDescription", "You don't have permission to view this dashboard.")
367
372
  });
368
373
  });
369
374
  //#endregion
370
- export { _ as DashboardBuilderPage, _ as default };
375
+ export { b as DashboardBuilderPage, b as default };
371
376
 
372
377
  //# sourceMappingURL=DashboardBuilderPage.js.map