@burdenoff/microfe-bigconsole 2026.929.2 → 2026.930.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/assistant/assistantRunPersistence.js +111 -0
- package/dist/bigconsole/assistant/assistantRunPersistence.js.map +1 -0
- package/dist/bigconsole/assistant/assistantRunStore.js +87 -48
- package/dist/bigconsole/assistant/assistantRunStore.js.map +1 -1
- package/dist/bigconsole/assistant/previewController.js +19 -50
- package/dist/bigconsole/assistant/previewController.js.map +1 -1
- package/dist/bigconsole/assistant/useAssistantRunWorkspace.js +14 -0
- package/dist/bigconsole/assistant/useAssistantRunWorkspace.js.map +1 -0
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js +74 -50
- package/dist/bigconsole/components/DrilldownDashboardRenderer.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js +9 -4
- package/dist/bigconsole/components/dashboard/DrilldownBreadcrumb.js.map +1 -1
- package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js +68 -40
- package/dist/bigconsole/components/embed/ReadOnlyDashboardRenderer.js.map +1 -1
- package/dist/bigconsole/components/embed/drilldown.js +26 -0
- package/dist/bigconsole/components/embed/drilldown.js.map +1 -0
- package/dist/bigconsole/components/embed/index.js +4 -3
- package/dist/bigconsole/components/embed/types.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js +32 -30
- package/dist/bigconsole/components/preview/AiPreviewMiniPanel.js.map +1 -1
- package/dist/bigconsole/components/preview/AiPreviewPanel.js +38 -36
- package/dist/bigconsole/components/preview/AiPreviewPanel.js.map +1 -1
- package/dist/bigconsole/components/preview/previewShared.js +1 -1
- package/dist/bigconsole/components/preview/previewShared.js.map +1 -1
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js +307 -233
- package/dist/bigconsole/components/widgets/DrilldownConfigPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +230 -220
- package/dist/bigconsole/components/widgets/PropertiesPanel.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetWrapper.js +18 -19
- package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
- package/dist/bigconsole/context/BigConsoleContext.js +4 -1
- package/dist/bigconsole/context/BigConsoleContext.js.map +1 -1
- package/dist/bigconsole/hooks/useActivePage.js +25 -0
- package/dist/bigconsole/hooks/useActivePage.js.map +1 -0
- package/dist/bigconsole/hooks/useDrilldown.js +118 -71
- package/dist/bigconsole/hooks/useDrilldown.js.map +1 -1
- package/dist/bigconsole/hooks/useDrilldownOperations.js +32 -26
- package/dist/bigconsole/hooks/useDrilldownOperations.js.map +1 -1
- package/dist/bigconsole/hooks/useFilterUrlSync.js +1 -1
- package/dist/bigconsole/hooks/useFilterUrlSync.js.map +1 -1
- package/dist/bigconsole/pages/DashboardBuilderPage.js +190 -185
- package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardViewPage.js +224 -217
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/generated/wspace-operations.js +132 -86
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js.map +1 -1
- package/package.json +3 -3
- package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js +0 -25
- package/dist/bigconsole/assistant/useAssistantPanelShowsPreview.js.map +0 -1
|
@@ -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/
|
|
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
|
|
14
|
-
import { BuilderCanvas as
|
|
15
|
-
import { BuilderTopbar as
|
|
16
|
-
import { DashboardEditorShell as
|
|
17
|
-
import { findNextAvailablePosition as
|
|
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
|
|
20
|
-
import
|
|
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
|
|
23
|
+
import re from "../customWidget/CustomWidgetBuilderSheet.js";
|
|
23
24
|
import "../customWidget/index.js";
|
|
24
|
-
import { memo as
|
|
25
|
-
import { useParams as
|
|
26
|
-
import { AccessDenied as
|
|
27
|
-
import { usePermissions as
|
|
28
|
-
import { Button as
|
|
29
|
-
import { jsx as
|
|
30
|
-
import { useI18n as
|
|
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
|
|
33
|
+
function y() {
|
|
33
34
|
try {
|
|
34
35
|
return window.__breadcrumbRegistry;
|
|
35
36
|
} catch {
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
|
-
function
|
|
40
|
-
let n =
|
|
40
|
+
function le(e, t) {
|
|
41
|
+
let n = y();
|
|
41
42
|
n?.register && n.register(e, t);
|
|
42
43
|
}
|
|
43
|
-
function
|
|
44
|
-
let t =
|
|
44
|
+
function ue(e) {
|
|
45
|
+
let t = y();
|
|
45
46
|
t?.unregister && t.unregister(e);
|
|
46
47
|
}
|
|
47
|
-
var
|
|
48
|
-
let { dashboardId:
|
|
49
|
-
|
|
50
|
-
if (!
|
|
51
|
-
|
|
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
|
|
55
|
-
e ? (
|
|
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.
|
|
67
|
+
}), e.name && le(p, e.name)) : y("/dashboards");
|
|
67
68
|
}), () => {
|
|
68
|
-
|
|
69
|
+
p && ue(p);
|
|
69
70
|
};
|
|
70
71
|
}, [
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
k
|
|
72
|
+
p,
|
|
73
|
+
y,
|
|
74
|
+
U,
|
|
75
|
+
A
|
|
76
76
|
]);
|
|
77
|
-
let
|
|
78
|
-
let e =
|
|
79
|
-
if (!
|
|
80
|
-
let t =
|
|
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
|
-
|
|
82
|
+
Q();
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
-
let n = Array.from(
|
|
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 } =
|
|
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 =
|
|
94
|
-
c ||= await
|
|
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:
|
|
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 && (
|
|
108
|
+
}), c && (J(c.id), Z === "cut" && await I(X)), Q();
|
|
109
109
|
}, [
|
|
110
|
-
Y,
|
|
111
110
|
X,
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
Z,
|
|
112
|
+
p,
|
|
114
113
|
G,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
F,
|
|
114
|
+
W,
|
|
115
|
+
Y,
|
|
118
116
|
L,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
P,
|
|
118
|
+
I,
|
|
119
|
+
J,
|
|
120
|
+
Q,
|
|
121
|
+
b
|
|
122
122
|
]);
|
|
123
|
-
|
|
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(),
|
|
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(),
|
|
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
|
-
}, [
|
|
139
|
-
let ge =
|
|
140
|
-
|
|
141
|
-
}, [
|
|
142
|
-
if (
|
|
143
|
-
|
|
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) =>
|
|
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
|
|
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
|
-
}))),
|
|
168
|
-
id:
|
|
169
|
-
name:
|
|
170
|
-
description:
|
|
167
|
+
}))), W && await j({
|
|
168
|
+
id: p,
|
|
169
|
+
name: W.title,
|
|
170
|
+
description: W.description
|
|
171
171
|
});
|
|
172
172
|
} catch (e) {
|
|
173
|
-
|
|
173
|
+
E(e instanceof Error ? e.message : b("bigconsole.dashboards.builder.errors.saveFailed", "Failed to save dashboard"));
|
|
174
174
|
} finally {
|
|
175
|
-
|
|
175
|
+
w(!1);
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
}, [
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
]), ve =
|
|
186
|
-
|
|
187
|
-
}, []),
|
|
188
|
-
|
|
189
|
-
}, []),
|
|
190
|
-
|
|
191
|
-
}, []),
|
|
192
|
-
if (!
|
|
193
|
-
let e = await
|
|
194
|
-
dashboardId:
|
|
195
|
-
name:
|
|
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 && (
|
|
198
|
-
...
|
|
199
|
-
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
|
-
}),
|
|
205
|
+
}), K(e.id));
|
|
206
206
|
}, [
|
|
207
|
-
G,
|
|
208
|
-
u,
|
|
209
207
|
W,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
}) &&
|
|
217
|
-
...
|
|
218
|
-
pages:
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
|
232
|
-
dashboardId:
|
|
233
|
-
name:
|
|
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 && (
|
|
236
|
-
...
|
|
237
|
-
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
|
-
}),
|
|
244
|
+
}), K(n.id));
|
|
244
245
|
}, [
|
|
245
|
-
G,
|
|
246
|
-
u,
|
|
247
246
|
W,
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
257
|
-
...
|
|
258
|
+
U({
|
|
259
|
+
...W,
|
|
258
260
|
pages: t
|
|
259
|
-
})
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
|
278
|
+
}), await H(p, e.map((e) => e.id)));
|
|
274
279
|
}, [
|
|
275
|
-
G,
|
|
276
|
-
u,
|
|
277
280
|
W,
|
|
278
|
-
|
|
281
|
+
p,
|
|
282
|
+
U,
|
|
283
|
+
H
|
|
279
284
|
]);
|
|
280
|
-
return
|
|
285
|
+
return S ? /* @__PURE__ */ _("div", {
|
|
281
286
|
className: "flex items-center justify-center h-full",
|
|
282
|
-
children: /* @__PURE__ */
|
|
283
|
-
}) :
|
|
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__ */
|
|
290
|
+
children: /* @__PURE__ */ _(i, {
|
|
286
291
|
headingLevel: 2,
|
|
287
292
|
tone: "danger",
|
|
288
|
-
title:
|
|
289
|
-
description: /* @__PURE__ */
|
|
293
|
+
title: b("bigconsole.dashboards.builder.errors.loadFailed", "Failed to load dashboard"),
|
|
294
|
+
description: /* @__PURE__ */ _("span", {
|
|
290
295
|
className: "break-words",
|
|
291
|
-
children:
|
|
296
|
+
children: N.message
|
|
292
297
|
}),
|
|
293
298
|
className: "w-full max-w-md",
|
|
294
299
|
"data-testid": "dashboard-builder-error",
|
|
295
|
-
actions: /* @__PURE__ */
|
|
300
|
+
actions: /* @__PURE__ */ _(se, {
|
|
296
301
|
size: "sm",
|
|
297
|
-
onClick: () =>
|
|
298
|
-
children:
|
|
302
|
+
onClick: () => y("/dashboards"),
|
|
303
|
+
children: b("bigconsole.dashboards.builder.actions.backToDashboards", "Back to Dashboards")
|
|
299
304
|
})
|
|
300
305
|
})
|
|
301
|
-
}) :
|
|
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__ */
|
|
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__ */
|
|
310
|
-
topbar: /* @__PURE__ */
|
|
311
|
-
title:
|
|
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__ */
|
|
318
|
+
actions: /* @__PURE__ */ v("div", {
|
|
314
319
|
className: "flex items-center gap-2",
|
|
315
|
-
children: [
|
|
320
|
+
children: [T ? /* @__PURE__ */ _("span", {
|
|
316
321
|
className: "text-xs font-medium text-status-error-text",
|
|
317
322
|
role: "alert",
|
|
318
|
-
children:
|
|
319
|
-
}) : null,
|
|
323
|
+
children: T
|
|
324
|
+
}) : null, pe ? /* @__PURE__ */ _("button", {
|
|
320
325
|
type: "button",
|
|
321
326
|
"data-testid": "open-custom-widget-builder",
|
|
322
|
-
onClick: () =>
|
|
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:
|
|
329
|
+
children: b("bigconsole.dashboards.builder.toolbar.aiWidget", "AI widget")
|
|
325
330
|
}) : null]
|
|
326
331
|
})
|
|
327
332
|
}),
|
|
328
333
|
children: [
|
|
329
|
-
/* @__PURE__ */
|
|
330
|
-
dashboardId:
|
|
331
|
-
pageId:
|
|
332
|
-
title:
|
|
333
|
-
onPaste:
|
|
334
|
-
onSave:
|
|
335
|
-
isSaving:
|
|
336
|
-
onExport:
|
|
337
|
-
pages:
|
|
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 ||
|
|
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:
|
|
343
|
-
onPageChange:
|
|
344
|
-
onPageAdd:
|
|
345
|
-
onPageRename:
|
|
346
|
-
onPageDuplicate:
|
|
347
|
-
onPageDelete:
|
|
348
|
-
onPagesReorder:
|
|
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__ */
|
|
352
|
-
isOpen:
|
|
353
|
-
dashboardId:
|
|
354
|
-
dashboardName:
|
|
355
|
-
onClose:
|
|
356
|
+
/* @__PURE__ */ _(ne, {
|
|
357
|
+
isOpen: D,
|
|
358
|
+
dashboardId: W.id,
|
|
359
|
+
dashboardName: W.title,
|
|
360
|
+
onClose: ye
|
|
356
361
|
}),
|
|
357
|
-
/* @__PURE__ */
|
|
362
|
+
/* @__PURE__ */ _(re, {
|
|
358
363
|
isOpen: he,
|
|
359
|
-
onClose: () =>
|
|
360
|
-
dashboardId:
|
|
361
|
-
pageId:
|
|
364
|
+
onClose: () => k(!1),
|
|
365
|
+
dashboardId: W.id,
|
|
366
|
+
pageId: G || W.pages?.[0]?.id || ""
|
|
362
367
|
})
|
|
363
368
|
]
|
|
364
|
-
}) : /* @__PURE__ */
|
|
365
|
-
title:
|
|
366
|
-
description:
|
|
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 {
|
|
375
|
+
export { b as DashboardBuilderPage, b as default };
|
|
371
376
|
|
|
372
377
|
//# sourceMappingURL=DashboardBuilderPage.js.map
|