drizzle-cube 0.3.21 → 0.3.22
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/adapters/express/index.cjs +1 -1
- package/dist/adapters/express/index.js +1 -1
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/fastify/index.js +1 -1
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/hono/index.js +1 -1
- package/dist/adapters/{mcp-transport-B37JTeww.cjs → mcp-transport-49vIZe0U.cjs} +91 -91
- package/dist/adapters/{mcp-transport-DHBUNdYg.js → mcp-transport-CxemGJYK.js} +2379 -2344
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/client/charts/chartConfigs.d.ts +21 -0
- package/dist/client/charts.js +12 -12
- package/dist/client/chunks/{RetentionCombinedChart-DsWhPI0q.js → RetentionCombinedChart-DpEKFYsd.js} +3 -3
- package/dist/client/chunks/RetentionCombinedChart-DpEKFYsd.js.map +1 -0
- package/dist/client/chunks/{analysis-builder-DcZgXtPK.js → analysis-builder-BCAr4mEO.js} +9 -9
- package/dist/client/chunks/{analysis-builder-DcZgXtPK.js.map → analysis-builder-BCAr4mEO.js.map} +1 -1
- package/dist/client/chunks/{analysis-builder-shared-ZnrPzt_d.js → analysis-builder-shared-D-5OhHaS.js} +5 -5
- package/dist/client/chunks/{analysis-builder-shared-ZnrPzt_d.js.map → analysis-builder-shared-D-5OhHaS.js.map} +1 -1
- package/dist/client/chunks/{chart-activity-grid-BUc21L0U.js → chart-activity-grid-CCGyWo1c.js} +116 -106
- package/dist/client/chunks/chart-activity-grid-CCGyWo1c.js.map +1 -0
- package/dist/client/chunks/chart-area-INsj4GFi.js +233 -0
- package/dist/client/chunks/chart-area-INsj4GFi.js.map +1 -0
- package/dist/client/chunks/chart-bar-BKRPTqiM.js +228 -0
- package/dist/client/chunks/chart-bar-BKRPTqiM.js.map +1 -0
- package/dist/client/chunks/{chart-bubble-CO7qvWR9.js → chart-bubble-BGGAQQUQ.js} +2 -2
- package/dist/client/chunks/{chart-bubble-CO7qvWR9.js.map → chart-bubble-BGGAQQUQ.js.map} +1 -1
- package/dist/client/chunks/{chart-config-activity-grid-BBSNCbkb.js → chart-config-activity-grid-CAlo1cHA.js} +3 -2
- package/dist/client/chunks/{chart-config-activity-grid-BBSNCbkb.js.map → chart-config-activity-grid-CAlo1cHA.js.map} +1 -1
- package/dist/client/chunks/{chart-config-bar-BJKGnfLt.js → chart-config-bar-soxw6m2o.js} +2 -1
- package/dist/client/chunks/chart-config-bar-soxw6m2o.js.map +1 -0
- package/dist/client/chunks/{chart-config-line-DR0ThxZy.js → chart-config-line-D5ME6w0v.js} +2 -1
- package/dist/client/chunks/chart-config-line-D5ME6w0v.js.map +1 -0
- package/dist/client/chunks/{chart-config-pie-BM5lgH-w.js → chart-config-pie-DlHa2jTy.js} +2 -1
- package/dist/client/chunks/chart-config-pie-DlHa2jTy.js.map +1 -0
- package/dist/client/chunks/{chart-config-tree-map-CLmRvvMR.js → chart-config-tree-map-IRAYf9YM.js} +3 -2
- package/dist/client/chunks/{chart-config-tree-map-CLmRvvMR.js.map → chart-config-tree-map-IRAYf9YM.js.map} +1 -1
- package/dist/client/chunks/{chart-data-table-bclSKgkZ.js → chart-data-table-BcH_h6kZ.js} +3 -3
- package/dist/client/chunks/{chart-data-table-bclSKgkZ.js.map → chart-data-table-BcH_h6kZ.js.map} +1 -1
- package/dist/client/chunks/{chart-funnel-FvDvq015.js → chart-funnel-DI8RMacf.js} +3 -3
- package/dist/client/chunks/chart-funnel-DI8RMacf.js.map +1 -0
- package/dist/client/chunks/{chart-heat-map-GpFE-PFB.js → chart-heat-map-D3xNV9ep.js} +2 -2
- package/dist/client/chunks/{chart-heat-map-GpFE-PFB.js.map → chart-heat-map-D3xNV9ep.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-delta-jmz-CK8X.js → chart-kpi-delta-BJMQKPor.js} +3 -3
- package/dist/client/chunks/{chart-kpi-delta-jmz-CK8X.js.map → chart-kpi-delta-BJMQKPor.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-number-DbSmomE8.js → chart-kpi-number-B8u4tWmu.js} +2 -2
- package/dist/client/chunks/{chart-kpi-number-DbSmomE8.js.map → chart-kpi-number-B8u4tWmu.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-text-erI9U7PZ.js → chart-kpi-text--r1d4zAz.js} +3 -3
- package/dist/client/chunks/{chart-kpi-text-erI9U7PZ.js.map → chart-kpi-text--r1d4zAz.js.map} +1 -1
- package/dist/client/chunks/chart-line-DgFmGdWW.js +414 -0
- package/dist/client/chunks/chart-line-DgFmGdWW.js.map +1 -0
- package/dist/client/chunks/chart-pie-DC7axSwd.js +137 -0
- package/dist/client/chunks/chart-pie-DC7axSwd.js.map +1 -0
- package/dist/client/chunks/{chart-radar-BE6xsFiF.js → chart-radar-BDKgpLw5.js} +33 -33
- package/dist/client/chunks/chart-radar-BDKgpLw5.js.map +1 -0
- package/dist/client/chunks/{chart-radial-bar-BEEwtFDc.js → chart-radial-bar-BYNng7Nz.js} +7 -6
- package/dist/client/chunks/chart-radial-bar-BYNng7Nz.js.map +1 -0
- package/dist/client/chunks/{chart-sankey-Dt3KaYrH.js → chart-sankey-CpsKerey.js} +2 -2
- package/dist/client/chunks/{chart-sankey-Dt3KaYrH.js.map → chart-sankey-CpsKerey.js.map} +1 -1
- package/dist/client/chunks/{chart-scatter-gAlYkQcW.js → chart-scatter-CXqFltJg.js} +11 -11
- package/dist/client/chunks/chart-scatter-CXqFltJg.js.map +1 -0
- package/dist/client/chunks/{chart-sunburst-D0Lvdjwu.js → chart-sunburst-DSsO2CzY.js} +2 -2
- package/dist/client/chunks/{chart-sunburst-D0Lvdjwu.js.map → chart-sunburst-DSsO2CzY.js.map} +1 -1
- package/dist/client/chunks/{chart-tree-map-Bv_PYe0c.js → chart-tree-map-D_SeBBD-.js} +77 -57
- package/dist/client/chunks/chart-tree-map-D_SeBBD-.js.map +1 -0
- package/dist/client/chunks/{chartConfigRegistry-BumUIPw4.js → chartConfigRegistry-DNEbwgTc.js} +6 -6
- package/dist/client/chunks/{chartConfigRegistry-BumUIPw4.js.map → chartConfigRegistry-DNEbwgTc.js.map} +1 -1
- package/dist/client/chunks/{charts-DqWRT0TE.js → charts-CsEtJy5f.js} +17 -17
- package/dist/client/chunks/charts-CsEtJy5f.js.map +1 -0
- package/dist/client/chunks/{charts-core-BfxnhMfd.js → charts-core-8jDh3mKC.js} +64 -63
- package/dist/client/chunks/charts-core-8jDh3mKC.js.map +1 -0
- package/dist/client/chunks/{charts-loader-DCGbL50r.js → charts-loader-sNk3q8UX.js} +20 -20
- package/dist/client/chunks/{charts-loader-DCGbL50r.js.map → charts-loader-sNk3q8UX.js.map} +1 -1
- package/dist/client/chunks/{components-NmBmOEqV.js → components-BsV0_0jR.js} +3579 -2880
- package/dist/client/chunks/components-BsV0_0jR.js.map +1 -0
- package/dist/client/chunks/{hooks-D7APQ8uS.js → hooks-CKYzVf_7.js} +3 -3
- package/dist/client/chunks/{hooks-D7APQ8uS.js.map → hooks-CKYzVf_7.js.map} +1 -1
- package/dist/client/chunks/{index-CBvXpG92.js → index-_2PSgbkC.js} +270 -261
- package/dist/client/chunks/index-_2PSgbkC.js.map +1 -0
- package/dist/client/chunks/{providers-Cj7PQfXn.js → providers-BBrUJB2U.js} +2 -2
- package/dist/client/chunks/{providers-Cj7PQfXn.js.map → providers-BBrUJB2U.js.map} +1 -1
- package/dist/client/chunks/{useDirtyStateTracking-ZSi3voVl.js → useDirtyStateTracking-DDQ_Lbki.js} +2 -2
- package/dist/client/chunks/{useDirtyStateTracking-ZSi3voVl.js.map → useDirtyStateTracking-DDQ_Lbki.js.map} +1 -1
- package/dist/client/components/DrillBreadcrumb.d.ts +6 -0
- package/dist/client/components/DrillMenu.d.ts +8 -0
- package/dist/client/components.js +2 -2
- package/dist/client/hooks/useDrillInteraction.d.ts +9 -0
- package/dist/client/hooks.js +3 -3
- package/dist/client/index.js +9 -9
- package/dist/client/providers.js +1 -1
- package/dist/client/styles.css +1 -1
- package/dist/client/types/drill.d.ts +240 -0
- package/dist/client/types.d.ts +53 -2
- package/dist/client/utils/drillQueryBuilder.d.ts +41 -0
- package/dist/client-bundle-stats.html +1 -1
- package/dist/server/index.cjs +25 -25
- package/dist/server/index.d.ts +78 -0
- package/dist/server/index.js +2117 -2082
- package/package.json +1 -1
- package/dist/client/chunks/RetentionCombinedChart-DsWhPI0q.js.map +0 -1
- package/dist/client/chunks/chart-activity-grid-BUc21L0U.js.map +0 -1
- package/dist/client/chunks/chart-area-B_64FScj.js +0 -190
- package/dist/client/chunks/chart-area-B_64FScj.js.map +0 -1
- package/dist/client/chunks/chart-bar-Ctiy2tpQ.js +0 -216
- package/dist/client/chunks/chart-bar-Ctiy2tpQ.js.map +0 -1
- package/dist/client/chunks/chart-config-bar-BJKGnfLt.js.map +0 -1
- package/dist/client/chunks/chart-config-line-DR0ThxZy.js.map +0 -1
- package/dist/client/chunks/chart-config-pie-BM5lgH-w.js.map +0 -1
- package/dist/client/chunks/chart-funnel-FvDvq015.js.map +0 -1
- package/dist/client/chunks/chart-line-B0YOZ88n.js +0 -364
- package/dist/client/chunks/chart-line-B0YOZ88n.js.map +0 -1
- package/dist/client/chunks/chart-pie-CImB6r4F.js +0 -125
- package/dist/client/chunks/chart-pie-CImB6r4F.js.map +0 -1
- package/dist/client/chunks/chart-radar-BE6xsFiF.js.map +0 -1
- package/dist/client/chunks/chart-radial-bar-BEEwtFDc.js.map +0 -1
- package/dist/client/chunks/chart-scatter-gAlYkQcW.js.map +0 -1
- package/dist/client/chunks/chart-tree-map-Bv_PYe0c.js.map +0 -1
- package/dist/client/chunks/charts-DqWRT0TE.js.map +0 -1
- package/dist/client/chunks/charts-core-BfxnhMfd.js.map +0 -1
- package/dist/client/chunks/components-NmBmOEqV.js.map +0 -1
- package/dist/client/chunks/index-CBvXpG92.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"components-NmBmOEqV.js","sources":["../../../src/client/hooks/queries/useFunnelQuery.ts","../../../src/client/hooks/queries/useFlowQuery.ts","../../../src/client/hooks/queries/useRetentionQuery.ts","../../../src/client/components/ChartErrorBoundary.tsx","../../../src/client/types.ts","../../../src/client/types/analysisConfig.ts","../../../src/client/utils/configMigration.ts","../../../src/client/utils/filterUtils.ts","../../../src/client/components/AnalyticsPortlet.tsx","../../../src/client/hooks/useScrollDetection.ts","../../../src/client/hooks/useElementVisibility.ts","../../../src/client/hooks/useDragAutoScroll.ts","../../../src/client/utils/syntaxHighlighting.ts","../../../src/client/components/DebugModal.tsx","../../../src/client/stores/dashboardStore.tsx","../../../src/client/components/DashboardPortletCard.tsx","../../../src/client/components/RowManagedLayout.tsx","../../../src/client/utils/colorPalettes.ts","../../../src/client/components/FloatingEditToolbar.tsx","../../../src/client/components/Modal.tsx","../../../src/client/components/AnalysisBuilderLazy.tsx","../../../src/client/components/AnalysisBuilder/utils/idUtils.ts","../../../src/client/components/AnalysisBuilder/utils/fieldUtils.ts","../../../src/client/components/AnalysisBuilder/utils/recentFieldsUtils.ts","../../../src/client/adapters/funnelModeAdapter.ts","../../../src/client/adapters/flowModeAdapter.ts","../../../src/client/adapters/retentionModeAdapter.ts","../../../src/client/components/PortletAnalysisModal.tsx","../../../src/client/components/PortletFilterConfigModal.tsx","../../../src/client/components/ConfirmModal.tsx","../../../src/client/components/ColorPaletteSelector.tsx","../../../src/client/components/AnalysisBuilder/FieldSearchItem.tsx","../../../src/client/components/AnalysisBuilder/FieldDetailPanel.tsx","../../../src/client/components/AnalysisBuilder/FieldSearchModal.tsx","../../../src/client/components/DashboardFilters/DashboardFilterConfigModal.tsx","../../../src/client/components/DashboardFilters/FilterEditModal.tsx","../../../src/client/components/DashboardFilters/EditModeFilterList.tsx","../../../src/client/components/shared/utils.ts","../../../src/client/components/DashboardFilters/DatePresetChips.tsx","../../../src/client/components/DashboardFilters/CustomDateDropdown.tsx","../../../src/client/components/DashboardFilters/XTDDropdown.tsx","../../../src/client/components/shared/FilterValueSelector.tsx","../../../src/client/components/DashboardFilters/FilterValuePopover.tsx","../../../src/client/components/DashboardFilters/FilterChip.tsx","../../../src/client/components/DashboardFilters/CompactFilterBar.tsx","../../../src/client/components/DashboardFilterPanel.tsx","../../../src/client/components/ScaledGridWrapper.tsx","../../../src/client/components/MobileStackedLayout.tsx","../../../src/client/hooks/useDashboardHook.ts","../../../src/client/components/DashboardGrid.tsx","../../../src/client/components/AnalyticsDashboard.tsx","../../../src/client/components/PortletContainer.tsx","../../../src/client/components/DashboardEditModal.tsx","../../../src/client/components/QueryBuilderShim.tsx"],"sourcesContent":["/**\n * useFunnelQuery - Hook for server-side funnel query execution\n *\n * Executes funnel queries on the server using a single SQL query with\n * CTE-based generation. This provides:\n * - True temporal ordering (step N must occur AFTER step N-1)\n * - Time window enforcement (timeToConvert constraints)\n * - No binding key value limits\n * - Time-to-convert metrics (avg, median, P90)\n *\n * Previously this hook used client-side sequential execution. The server-side\n * approach is strictly better and the data shapes are compatible.\n */\n\nimport { useMemo, useCallback, useState, useEffect } from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useCubeApi } from '../../providers/CubeApiProvider'\nimport { useCubeFeatures } from '../../providers/CubeFeaturesProvider'\nimport { useDebounceQuery } from '../useDebounceQuery'\nimport { stableStringify } from '../../shared/queryKey'\nimport type { CubeQuery } from '../../types'\nimport type {\n FunnelConfig,\n FunnelChartData,\n UseFunnelQueryOptions,\n UseFunnelQueryResult,\n FunnelStepResult,\n FunnelExecutionResult,\n} from '../../types/funnel'\nimport {\n buildServerFunnelQuery,\n transformServerFunnelResult,\n} from '../../utils/funnelExecution'\n\n// Default debounce delay in milliseconds\nconst DEFAULT_DEBOUNCE_MS = 300\n\n/**\n * Check if a FunnelConfig is valid for execution\n */\nfunction isValidFunnelConfig(config: FunnelConfig | null): boolean {\n if (!config) return false\n if (!config.bindingKey) return false\n if (!config.steps || config.steps.length < 2) return false\n\n // Check that binding key dimension is defined\n if (typeof config.bindingKey.dimension === 'string') {\n if (!config.bindingKey.dimension) return false\n } else if (Array.isArray(config.bindingKey.dimension)) {\n if (config.bindingKey.dimension.length === 0) return false\n }\n\n // Check that each step has a valid query\n // For funnels, a step can have:\n // - measures/dimensions/timeDimensions (standard fields), OR\n // - filters only (the binding key dimension is auto-added by buildStepQuery)\n for (const step of config.steps) {\n const query = step.query\n const hasFields =\n (query.measures && query.measures.length > 0) ||\n (query.dimensions && query.dimensions.length > 0) ||\n (query.timeDimensions && query.timeDimensions.length > 0) ||\n (query.filters && query.filters.length > 0)\n if (!hasFields) return false\n }\n\n return true\n}\n\n/**\n * Hook for server-side funnel query execution\n *\n * Usage:\n * ```tsx\n * const { chartData, isExecuting, error } = useFunnelQuery(config, {\n * debounceMs: 300,\n * skip: !hasBindingKey\n * })\n *\n * // Results available after single server request\n * <FunnelChart data={chartData} />\n * ```\n */\nexport function useFunnelQuery(\n config: FunnelConfig | null,\n options: UseFunnelQueryOptions = {}\n): UseFunnelQueryResult {\n const {\n skip = false,\n debounceMs = DEFAULT_DEBOUNCE_MS,\n onComplete,\n onError,\n prebuiltServerQuery,\n } = options\n\n const { cubeApi } = useCubeApi()\n const queryClient = useQueryClient()\n\n // Get manual refresh mode from features\n const { features } = useCubeFeatures()\n const manualRefresh = features.manualRefresh ?? false\n\n // Track the last executed query (for manual refresh mode)\n const [executedQueryKey, setExecutedQueryKey] = useState<string | null>(null)\n\n // Validate config\n const isValidConfig = isValidFunnelConfig(config)\n\n // Use shared debounce hook\n const { debouncedValue: debouncedConfig, isDebouncing } = useDebounceQuery(config, {\n isValid: isValidConfig,\n skip,\n debounceMs,\n })\n\n // Build server query from config (or use prebuilt if provided)\n const serverQuery = useMemo(() => {\n // If prebuiltServerQuery is provided, use it directly\n if (prebuiltServerQuery) {\n return prebuiltServerQuery\n }\n\n // Otherwise build from config (legacy mode)\n if (!debouncedConfig || !isValidConfig) {\n return null\n }\n\n try {\n const result = buildServerFunnelQuery(\n debouncedConfig.steps.map(s => s.query),\n debouncedConfig.bindingKey,\n debouncedConfig.steps.map(s => s.name),\n debouncedConfig.steps.map(s => s.timeToConvert || null),\n true // includeTimeMetrics\n )\n return result\n } catch (error) {\n console.error('Failed to build server funnel query:', error)\n return null\n }\n }, [prebuiltServerQuery, debouncedConfig, isValidConfig])\n\n // Create stable query key\n // Include step count explicitly to ensure cache invalidation when steps change\n const queryKey = useMemo(() => {\n if (!serverQuery) return ['cube', 'funnel', null] as const\n const stepCount = serverQuery.funnel?.steps?.length || 0\n return ['cube', 'funnel', stepCount, JSON.stringify(serverQuery)] as const\n }, [serverQuery])\n\n // Calculate current query key for manual refresh tracking\n const currentQueryKey = serverQuery ? stableStringify(serverQuery) : null\n\n // Calculate if the current query differs from the last executed query\n const needsRefresh = useMemo(() => {\n if (!manualRefresh) return false\n if (!currentQueryKey) return false\n // On first load (executedQueryKey is null), don't show \"needs refresh\" - we'll auto-execute\n if (executedQueryKey === null) return false\n // After initial execution, show \"needs refresh\" when query has changed\n return currentQueryKey !== executedQueryKey\n }, [manualRefresh, currentQueryKey, executedQueryKey])\n\n // In manual refresh mode, only execute when explicitly triggered\n // In auto mode, execute whenever serverQuery is valid and not skipped\n const shouldExecute = useMemo(() => {\n if (!serverQuery || skip) return false\n if (!manualRefresh) return true // Auto mode: always execute\n // Manual mode: auto-execute on first load (executedQueryKey is null),\n // then require explicit trigger for subsequent changes\n if (executedQueryKey === null) return true // First load: auto-execute\n return executedQueryKey === currentQueryKey\n }, [serverQuery, skip, manualRefresh, executedQueryKey, currentQueryKey])\n\n // In auto mode, track executed query for consistency\n // This ensures needsRefresh stays false when query auto-executes\n useEffect(() => {\n if (!manualRefresh && serverQuery && !skip) {\n setExecutedQueryKey(currentQueryKey)\n }\n }, [manualRefresh, serverQuery, skip, currentQueryKey])\n\n // Execute funnel query via TanStack Query\n const queryResult = useQuery({\n queryKey,\n queryFn: async () => {\n if (!serverQuery) {\n throw new Error('No server query available')\n }\n\n const startTime = performance.now()\n\n try {\n // Send funnel query to server (single request)\n const resultSet = await cubeApi.load(serverQuery as unknown as CubeQuery)\n const rawData = resultSet.rawData()\n const executionTime = performance.now() - startTime\n const cacheInfo = resultSet.cacheInfo?.()\n\n return {\n rawData,\n executionTime,\n cacheInfo,\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n onError?.(err, 0)\n throw err\n }\n },\n // Enable when we have a server query (either prebuilt or built from config)\n // In manual refresh mode, only execute when explicitly triggered\n enabled: shouldExecute,\n staleTime: 60000, // 1 minute cache\n gcTime: 5 * 60 * 1000, // 5 minute garbage collection\n })\n\n // Track when query successfully executes in manual refresh mode\n // This ensures executedQueryKey is set after the first auto-execution,\n // preventing subsequent auto-executions until user clicks refresh\n useEffect(() => {\n // Only relevant in manual refresh mode\n if (!manualRefresh) return\n\n // When query successfully completes (and we were executing)\n // update the executed query key\n if (shouldExecute && queryResult.isSuccess && !queryResult.isFetching && serverQuery) {\n setExecutedQueryKey(currentQueryKey)\n }\n }, [manualRefresh, shouldExecute, queryResult.isSuccess, queryResult.isFetching, serverQuery, currentQueryKey])\n\n // Get step names from either config or prebuilt server query\n const stepNames = useMemo(() => {\n if (prebuiltServerQuery?.funnel?.steps) {\n return prebuiltServerQuery.funnel.steps.map(s => s.name)\n }\n return debouncedConfig?.steps?.map(s => s.name)\n }, [prebuiltServerQuery, debouncedConfig])\n\n // Get expected step count from either config or prebuilt server query\n const expectedStepCount = useMemo(() => {\n if (prebuiltServerQuery?.funnel?.steps) {\n return prebuiltServerQuery.funnel.steps.length\n }\n return debouncedConfig?.steps?.length || 0\n }, [prebuiltServerQuery, debouncedConfig])\n\n // Transform server result to chart data\n // Validate step count matches to prevent showing stale data during transitions\n const chartData = useMemo<FunnelChartData[]>(() => {\n if (!queryResult.data?.rawData) return []\n\n // Check if data step count matches expected step count\n const dataStepCount = queryResult.data.rawData.length\n\n if (dataStepCount !== expectedStepCount) {\n // Data is stale (from a different query) - don't return it\n // This prevents showing mismatched step counts while a new query loads\n return []\n }\n\n return transformServerFunnelResult(\n queryResult.data.rawData,\n stepNames\n )\n }, [queryResult.data, expectedStepCount, stepNames])\n\n // Build step results from chart data (for backward compatibility)\n const stepResults = useMemo<FunnelStepResult[]>(() => {\n if (!chartData.length) return []\n\n const firstCount = chartData[0]?.value || 0\n\n return chartData.map((data, index) => ({\n stepIndex: index,\n stepName: data.name,\n // Get step ID from config, or generate one for prebuilt queries\n stepId: debouncedConfig?.steps?.[index]?.id || `step-${index}`,\n data: [], // Raw data not available from server funnel\n bindingKeyValues: [], // Not available from server funnel\n bindingKeyTotalCount: 0,\n count: data.value,\n conversionRate: data.conversionRate !== null ? data.conversionRate / 100 : null,\n cumulativeConversionRate: firstCount > 0 ? data.value / firstCount : 0,\n executionTime: queryResult.data?.executionTime || 0,\n error: null,\n }))\n }, [chartData, debouncedConfig, queryResult.data?.executionTime])\n\n // Build full result for compatibility\n const result = useMemo<FunnelExecutionResult | null>(() => {\n // Need either config or prebuilt query for results\n if (!chartData.length) return null\n if (!debouncedConfig && !prebuiltServerQuery) return null\n\n const firstCount = chartData[0]?.value || 0\n const lastCount = chartData[chartData.length - 1]?.value || 0\n\n // Create a config object (use debouncedConfig if available, else synthesize from prebuilt)\n const effectiveConfig: FunnelConfig = debouncedConfig || {\n id: 'prebuilt-funnel',\n name: 'Funnel Analysis',\n bindingKey: {\n dimension: typeof prebuiltServerQuery?.funnel?.bindingKey === 'string'\n ? prebuiltServerQuery.funnel.bindingKey\n : prebuiltServerQuery?.funnel?.bindingKey?.[0]?.dimension || ''\n },\n steps: (prebuiltServerQuery?.funnel?.steps || []).map((s, i) => ({\n id: `step-${i}`,\n name: s.name,\n query: { filters: s.filter ? [s.filter as unknown as import('../../types').Filter] : [] },\n timeToConvert: s.timeToConvert || undefined,\n })),\n }\n\n const fullResult: FunnelExecutionResult = {\n config: effectiveConfig,\n steps: stepResults,\n summary: {\n totalEntries: firstCount,\n totalCompletions: lastCount,\n overallConversionRate: firstCount > 0 ? lastCount / firstCount : 0,\n totalExecutionTime: queryResult.data?.executionTime || 0,\n },\n chartData,\n status: queryResult.isError\n ? 'error'\n : queryResult.isLoading\n ? 'executing'\n : queryResult.isSuccess\n ? 'success'\n : 'idle',\n error: queryResult.error as Error | null,\n currentStepIndex: null,\n }\n\n // Call completion callback\n if (queryResult.isSuccess && !queryResult.isFetching) {\n onComplete?.(fullResult)\n }\n\n return fullResult\n }, [debouncedConfig, prebuiltServerQuery, chartData, stepResults, queryResult, onComplete])\n\n // Determine current status\n const status: FunnelExecutionResult['status'] = queryResult.isError\n ? 'error'\n : queryResult.isLoading\n ? 'executing'\n : queryResult.isSuccess\n ? 'success'\n : 'idle'\n\n /**\n * Manually execute/refetch the funnel query\n * Pass { bustCache: true } to bypass both client and server caches\n */\n const execute = useCallback(async (options?: { bustCache?: boolean }): Promise<FunnelExecutionResult | null> => {\n // Allow execution if we have a serverQuery (either from prebuiltServerQuery or built from config)\n if (!serverQuery) return null\n\n // Mark this query as executed (for manual refresh mode)\n setExecutedQueryKey(currentQueryKey)\n\n try {\n if (options?.bustCache) {\n // Remove from TanStack Query cache first\n queryClient.removeQueries({ queryKey })\n // Fetch with cache bust header\n await queryClient.fetchQuery({\n queryKey,\n queryFn: async () => {\n const startTime = performance.now()\n const resultSet = await cubeApi.load(\n serverQuery as unknown as CubeQuery,\n { bustCache: true }\n )\n const rawData = resultSet.rawData()\n const executionTime = performance.now() - startTime\n const cacheInfo = resultSet.cacheInfo?.()\n return { rawData, executionTime, cacheInfo }\n },\n })\n } else {\n await queryResult.refetch()\n }\n return result\n } catch {\n return result\n }\n }, [serverQuery, queryResult, result, queryClient, queryKey, cubeApi, currentQueryKey])\n\n /**\n * Cancel is a no-op for TanStack Query (handled automatically)\n */\n const cancel = useCallback(() => {\n // TanStack Query handles cancellation automatically\n }, [])\n\n /**\n * Reset clears the query cache\n */\n const reset = useCallback(() => {\n queryClient.removeQueries({ queryKey })\n }, [queryClient, queryKey])\n\n return {\n result,\n status,\n isExecuting: queryResult.isLoading || queryResult.isFetching,\n isDebouncing,\n currentStepIndex: null, // Not applicable for server-side execution\n stepLoadingStates: [], // Not applicable for server-side execution\n stepResults,\n chartData,\n error: queryResult.error as Error | null,\n execute,\n cancel,\n reset,\n // Not exposing executedQueries - server builds the query internally\n executedQueries: [],\n // Expose the server query for debug panel display\n // This is the actual { funnel: {...} } query sent to the server\n serverQuery,\n cacheInfo: queryResult.data?.cacheInfo ?? null,\n // Manual refresh mode support\n needsRefresh,\n }\n}\n\n/**\n * Create a stable query key for funnel queries\n */\nexport function createFunnelQueryKey(\n config: FunnelConfig | null\n): readonly unknown[] {\n if (!config) return ['cube', 'funnel', null] as const\n // Create a stable key based on config\n return ['cube', 'funnel', JSON.stringify(config)] as const\n}\n","/**\n * useFlowQuery - Hook for server-side flow query execution\n *\n * Executes flow queries on the server for bidirectional Sankey chart data.\n * Flow queries explore paths BEFORE and AFTER a defined starting step.\n *\n * The server returns { nodes: [], links: [] } structure ready for Sankey visualization.\n */\n\nimport { useMemo, useCallback, useState, useEffect } from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useCubeApi } from '../../providers/CubeApiProvider'\nimport { useCubeFeatures } from '../../providers/CubeFeaturesProvider'\nimport { useDebounceQuery } from '../useDebounceQuery'\nimport { stableStringify } from '../../shared/queryKey'\nimport type { CubeQuery } from '../../types'\nimport type {\n ServerFlowQuery,\n FlowChartData,\n} from '../../types/flow'\nimport { isSankeyData } from '../../types/flow'\n\n// Default debounce delay in milliseconds\nconst DEFAULT_DEBOUNCE_MS = 300\n\n/**\n * Options for useFlowQuery hook\n */\nexport interface UseFlowQueryOptions {\n /** Skip query execution */\n skip?: boolean\n /** Debounce delay in milliseconds */\n debounceMs?: number\n /** Callback when query completes successfully */\n onComplete?: (data: FlowChartData) => void\n /** Callback when query fails */\n onError?: (error: Error) => void\n}\n\n/**\n * Result from useFlowQuery hook\n */\nexport interface UseFlowQueryResult {\n /** Transformed flow chart data (nodes and links) */\n data: FlowChartData | null\n /** Raw data from server */\n rawData: unknown[] | null\n /** Cache metadata when served from cache */\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n /** Is initial load in progress */\n isLoading: boolean\n /** Is refetch in progress */\n isFetching: boolean\n /** Is waiting for debounce */\n isDebouncing: boolean\n /** Is executing (loading or fetching) */\n isExecuting: boolean\n /** Error if query failed */\n error: Error | null\n /** Refetch the query. Pass { bustCache: true } to bypass client and server caches. */\n refetch: (options?: { bustCache?: boolean }) => void\n /** Reset the query cache */\n reset: () => void\n /** The server query being executed */\n serverQuery: ServerFlowQuery | null\n /**\n * Whether the query needs to be refreshed (manual refresh mode only).\n * True when the current flow config differs from the last executed query.\n */\n needsRefresh: boolean\n}\n\n/**\n * Check if a ServerFlowQuery is valid for execution\n */\nfunction isValidFlowQuery(query: ServerFlowQuery | null): boolean {\n if (!query?.flow) return false\n\n const { flow } = query\n\n // Must have binding key\n if (!flow.bindingKey) return false\n\n // Must have time dimension\n if (!flow.timeDimension) return false\n\n // Must have event dimension\n if (!flow.eventDimension) return false\n\n // Must have starting step with filter\n if (!flow.startingStep?.filter) return false\n\n // Must have valid depth\n if (flow.stepsBefore < 0 || flow.stepsBefore > 5) return false\n if (flow.stepsAfter < 0 || flow.stepsAfter > 5) return false\n\n return true\n}\n\n/**\n * Transform raw server result to FlowChartData\n */\nfunction transformFlowResult(rawData: unknown[]): FlowChartData | null {\n // Server returns a single row with { nodes: [], links: [] } structure\n if (rawData.length === 1) {\n const row = rawData[0]\n if (row && typeof row === 'object' && 'nodes' in row && 'links' in row) {\n return row as FlowChartData\n }\n }\n\n // Alternative: Server might return nodes and links as separate items\n // or the entire array might be the flow result\n if (rawData.length > 0) {\n const firstItem = rawData[0]\n // Check if it looks like Sankey data using type guard\n if (firstItem && typeof firstItem === 'object' && isSankeyData(firstItem)) {\n return firstItem as FlowChartData\n }\n }\n\n return null\n}\n\n/**\n * Hook for server-side flow query execution\n *\n * Usage:\n * ```tsx\n * const { data, isLoading, error } = useFlowQuery(serverFlowQuery, {\n * debounceMs: 300,\n * skip: !isConfigured\n * })\n *\n * // Results available after single server request\n * <SankeyChart data={data} />\n * ```\n */\nexport function useFlowQuery(\n query: ServerFlowQuery | null,\n options: UseFlowQueryOptions = {}\n): UseFlowQueryResult {\n const {\n skip = false,\n debounceMs = DEFAULT_DEBOUNCE_MS,\n onComplete,\n onError,\n } = options\n\n const { cubeApi } = useCubeApi()\n const queryClient = useQueryClient()\n\n // Get manual refresh mode from features\n const { features } = useCubeFeatures()\n const manualRefresh = features.manualRefresh ?? false\n\n // Track the last executed query (for manual refresh mode)\n const [executedQueryKey, setExecutedQueryKey] = useState<string | null>(null)\n\n // Validate query\n const isValid = isValidFlowQuery(query)\n\n // Use shared debounce hook\n const { debouncedValue: debouncedQuery, isDebouncing } = useDebounceQuery(\n query,\n {\n isValid,\n skip,\n debounceMs,\n }\n )\n\n // Create stable query key string for the debounced query (used for TanStack Query cache key)\n const queryKeyString = useMemo(() => {\n if (!debouncedQuery) return null\n return JSON.stringify(debouncedQuery)\n }, [debouncedQuery])\n\n // Create stable query key string for the RAW input query (used for staleness detection)\n // This detects when the input query has changed but debounce hasn't completed yet\n const rawQueryKeyString = useMemo(() => {\n if (!query) return null\n return JSON.stringify(query)\n }, [query])\n\n // Create stable query key\n const queryKey = useMemo(() => {\n if (!debouncedQuery) return ['cube', 'flow', null] as const\n return ['cube', 'flow', queryKeyString] as const\n }, [debouncedQuery, queryKeyString])\n\n // Calculate current query key for manual refresh tracking (uses raw input query)\n const currentQueryKey = query ? stableStringify(query) : null\n\n // Calculate if the current query differs from the last executed query\n const needsRefresh = useMemo(() => {\n if (!manualRefresh) return false\n if (!currentQueryKey) return false\n // On first load (executedQueryKey is null), don't show \"needs refresh\" - we'll auto-execute\n if (executedQueryKey === null) return false\n // After initial execution, show \"needs refresh\" when query has changed\n return currentQueryKey !== executedQueryKey\n }, [manualRefresh, currentQueryKey, executedQueryKey])\n\n // In manual refresh mode, only execute when explicitly triggered\n // In auto mode, execute whenever query is valid and not skipped\n const shouldExecute = useMemo(() => {\n if (!isValid || !debouncedQuery || skip) return false\n if (!manualRefresh) return true // Auto mode: always execute\n // Manual mode: auto-execute on first load (executedQueryKey is null),\n // then require explicit trigger for subsequent changes\n if (executedQueryKey === null) return true // First load: auto-execute\n return executedQueryKey === currentQueryKey\n }, [isValid, debouncedQuery, skip, manualRefresh, executedQueryKey, currentQueryKey])\n\n // In auto mode, track executed query for consistency\n // This ensures needsRefresh stays false when query auto-executes\n useEffect(() => {\n if (!manualRefresh && query && !skip && isValid) {\n setExecutedQueryKey(currentQueryKey)\n }\n }, [manualRefresh, query, skip, isValid, currentQueryKey])\n\n // Execute flow query via TanStack Query\n const queryResult = useQuery({\n queryKey,\n queryFn: async () => {\n if (!debouncedQuery) {\n throw new Error('No flow query available')\n }\n\n const startTime = performance.now()\n\n try {\n // Send flow query to server (single request)\n const resultSet = await cubeApi.load(\n debouncedQuery as unknown as CubeQuery\n )\n const rawData = resultSet.rawData()\n const executionTime = performance.now() - startTime\n const cacheInfo = resultSet.cacheInfo?.()\n\n return {\n rawData,\n executionTime,\n cacheInfo,\n // Include query key in result so we can detect stale data\n // (data from a different query key, e.g., sankey vs sunburst mode)\n // We store the RAW query key so we can compare against the current raw query\n queryKeyString: rawQueryKeyString,\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n onError?.(err)\n throw err\n }\n },\n // In manual refresh mode, only execute when explicitly triggered\n enabled: shouldExecute,\n staleTime: 60000, // 1 minute cache\n gcTime: 5 * 60 * 1000, // 5 minute garbage collection\n })\n\n // Track when query successfully executes in manual refresh mode\n // This ensures executedQueryKey is set after the first auto-execution,\n // preventing subsequent auto-executions until user clicks refresh\n useEffect(() => {\n // Only relevant in manual refresh mode\n if (!manualRefresh) return\n\n // When query successfully completes (and we were executing)\n // update the executed query key\n if (shouldExecute && queryResult.isSuccess && !queryResult.isFetching && debouncedQuery) {\n setExecutedQueryKey(currentQueryKey)\n }\n }, [manualRefresh, shouldExecute, queryResult.isSuccess, queryResult.isFetching, debouncedQuery, currentQueryKey])\n\n // Check if data is stale (from a different query key)\n // This happens when switching between sankey/sunburst modes\n // We compare the current RAW query key with the one stored in the data\n // This catches staleness even during debounce (when debouncedQuery hasn't updated yet)\n const isDataStale = rawQueryKeyString !== null &&\n queryResult.data?.queryKeyString !== undefined &&\n queryResult.data.queryKeyString !== rawQueryKeyString\n\n // Transform server result to chart data\n // Return null if data is stale (from a different query) to show loading instead\n const chartData = useMemo<FlowChartData | null>(() => {\n // If data is stale (from different query), return null to show loading\n if (isDataStale) return null\n\n if (!queryResult.data?.rawData) return null\n\n const transformed = transformFlowResult(queryResult.data.rawData)\n\n // Call completion callback\n if (transformed && queryResult.isSuccess && !queryResult.isFetching) {\n onComplete?.(transformed)\n }\n\n return transformed\n }, [queryResult.data, queryResult.isSuccess, queryResult.isFetching, onComplete, isDataStale])\n\n /**\n * Refetch the flow query\n * Pass { bustCache: true } to bypass both client and server caches\n */\n const refetch = useCallback((options?: { bustCache?: boolean }) => {\n if (debouncedQuery && isValid) {\n // Mark this query as executed (for manual refresh mode)\n setExecutedQueryKey(currentQueryKey)\n\n if (options?.bustCache) {\n // Remove from TanStack Query cache first\n queryClient.removeQueries({ queryKey })\n // Fetch with cache bust header\n queryClient.fetchQuery({\n queryKey,\n queryFn: async () => {\n const startTime = performance.now()\n const resultSet = await cubeApi.load(\n debouncedQuery as unknown as CubeQuery,\n { bustCache: true }\n )\n const rawData = resultSet.rawData()\n const executionTime = performance.now() - startTime\n const cacheInfo = resultSet.cacheInfo?.()\n return { rawData, executionTime, cacheInfo }\n },\n })\n } else {\n queryResult.refetch()\n }\n }\n }, [debouncedQuery, isValid, queryResult, queryClient, queryKey, cubeApi, currentQueryKey])\n\n /**\n * Reset clears the query cache\n */\n const reset = useCallback(() => {\n queryClient.removeQueries({ queryKey })\n }, [queryClient, queryKey])\n\n return {\n data: chartData,\n rawData: isDataStale ? null : (queryResult.data?.rawData ?? null),\n cacheInfo: queryResult.data?.cacheInfo ?? null,\n isLoading: queryResult.isLoading || isDataStale,\n isFetching: queryResult.isFetching,\n isDebouncing,\n isExecuting: queryResult.isLoading || queryResult.isFetching || isDataStale,\n error: queryResult.error as Error | null,\n refetch,\n reset,\n serverQuery: debouncedQuery,\n // Manual refresh mode support\n needsRefresh,\n }\n}\n\n/**\n * Create a stable query key for flow queries\n */\nexport function createFlowQueryKey(\n query: ServerFlowQuery | null\n): readonly unknown[] {\n if (!query) return ['cube', 'flow', null] as const\n return ['cube', 'flow', JSON.stringify(query)] as const\n}\n","/**\n * useRetentionQuery - Hook for server-side retention query execution\n *\n * Executes retention queries on the server using SQL generation.\n * Returns cohort-based retention data for heatmap visualization.\n */\n\nimport { useMemo, useCallback, useState, useEffect } from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { useCubeApi } from '../../providers/CubeApiProvider'\nimport { useCubeFeatures } from '../../providers/CubeFeaturesProvider'\nimport { useDebounceQuery } from '../useDebounceQuery'\nimport { stableStringify } from '../../shared/queryKey'\nimport type { CubeQuery } from '../../types'\nimport type {\n ServerRetentionQuery,\n RetentionChartData,\n RetentionResultRow,\n RetentionSummary,\n RetentionGranularity,\n} from '../../types/retention'\n\n// Default debounce delay in milliseconds\nconst DEFAULT_DEBOUNCE_MS = 300\n\n/**\n * Options for retention query hook\n */\nexport interface UseRetentionQueryOptions {\n /** Skip execution */\n skip?: boolean\n /** Debounce delay in milliseconds */\n debounceMs?: number\n /** Callback when query completes */\n onComplete?: (result: RetentionChartData) => void\n /** Callback when query fails */\n onError?: (error: Error) => void\n /** Function to resolve field names to human-readable display labels */\n getFieldLabel?: (fieldName: string) => string\n}\n\n/**\n * Result from retention query hook\n */\nexport interface UseRetentionQueryResult {\n /** Retention chart data */\n chartData: RetentionChartData | null\n\n /** Raw data rows from server */\n rawData: RetentionResultRow[] | null\n\n /** Current execution status */\n status: 'idle' | 'loading' | 'success' | 'error'\n\n /** Whether currently loading */\n isLoading: boolean\n\n /** Whether fetching (includes refetch) */\n isFetching: boolean\n\n /** Whether waiting for debounce */\n isDebouncing: boolean\n\n /** Error if execution failed */\n error: Error | null\n\n /** Cache metadata when served from cache */\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n\n /** Execute the query (for manual refresh mode) */\n execute: (options?: { bustCache?: boolean }) => Promise<RetentionChartData | null>\n\n /** Refetch the query */\n refetch: () => void\n\n /**\n * Whether the query needs to be refreshed (manual refresh mode only).\n * True when the current query config differs from the last executed query.\n */\n needsRefresh: boolean\n}\n\n/**\n * Check if a ServerRetentionQuery is valid for execution\n * Uses new simplified Mixpanel-style format with single timeDimension\n */\nfunction isValidRetentionQuery(query: ServerRetentionQuery | null): boolean {\n if (!query) return false\n if (!query.retention) return false\n if (!query.retention.timeDimension) return false\n if (!query.retention.bindingKey) return false\n if (!query.retention.periods || query.retention.periods < 1) return false\n return true\n}\n\n/**\n * Extract the human-readable label from the binding key\n * e.g., \"Users.userId\" → \"userId\", \"Events.customerId\" → \"customerId\"\n */\nfunction extractBindingKeyLabel(\n bindingKey: ServerRetentionQuery['retention']['bindingKey'] | undefined\n): string | undefined {\n if (!bindingKey) return undefined\n\n // String format: \"Cube.dimensionName\" → \"dimensionName\"\n if (typeof bindingKey === 'string') {\n return bindingKey.split('.').pop()\n }\n\n // Array format: [{ cube, dimension }] → extract first dimension's name\n if (Array.isArray(bindingKey) && bindingKey.length > 0) {\n const firstMapping = bindingKey[0]\n if (firstMapping?.dimension) {\n return firstMapping.dimension.split('.').pop()\n }\n }\n\n return undefined\n}\n\n/**\n * Extract breakdown value from server response\n * Server returns breakdownValues as an object like {\"PREvents.eventType\": \"approved\"}\n * We extract the first (and typically only) value from this object\n */\nfunction extractBreakdownValue(\n breakdownValues: unknown\n): string | null {\n // If it's already a string, return it\n if (typeof breakdownValues === 'string') {\n return breakdownValues\n }\n\n // If it's an object, extract the first value\n if (breakdownValues && typeof breakdownValues === 'object' && !Array.isArray(breakdownValues)) {\n const values = Object.values(breakdownValues as Record<string, unknown>)\n if (values.length > 0 && values[0] != null) {\n return String(values[0])\n }\n }\n\n return null\n}\n\n/**\n * Transform raw server data to RetentionChartData format\n * New simplified format: rows with period, cohortSize, retainedUsers, retentionRate, breakdownValue\n */\nfunction transformRetentionResult(\n rawData: unknown[],\n granularity?: RetentionGranularity,\n bindingKeyLabel?: string\n): RetentionChartData {\n if (!rawData || !Array.isArray(rawData) || rawData.length === 0) {\n return { rows: [], periods: [], granularity, bindingKeyLabel }\n }\n\n const rows: RetentionResultRow[] = rawData.map((row: unknown) => {\n const r = row as Record<string, unknown>\n return {\n period: Number(r.period ?? r.period_number ?? 0),\n cohortSize: Number(r.cohortSize ?? r.cohort_size ?? 0),\n retainedUsers: Number(r.retainedUsers ?? r.retained_users ?? 0),\n retentionRate: Number(r.retentionRate ?? r.retention_rate ?? 0),\n // Server returns breakdownValues (object) or breakdownValue (string) or breakdown_value (snake_case)\n breakdownValue: extractBreakdownValue(r.breakdownValues) ?? r.breakdownValue ?? r.breakdown_value ?? null,\n } as RetentionResultRow\n })\n\n // Extract unique periods and breakdown values\n const periodsSet = new Set<number>()\n const breakdownSet = new Set<string>()\n\n rows.forEach((row) => {\n periodsSet.add(row.period)\n if (row.breakdownValue) {\n breakdownSet.add(row.breakdownValue)\n }\n })\n\n const periods = Array.from(periodsSet).sort((a, b) => a - b)\n const breakdownValues = breakdownSet.size > 0 ? Array.from(breakdownSet).sort() : undefined\n\n // Calculate summary statistics\n const summary = calculateSummary(rows, breakdownValues)\n\n return { rows, periods, breakdownValues, summary, granularity, bindingKeyLabel }\n}\n\n/**\n * Calculate summary statistics from retention data\n */\nfunction calculateSummary(\n rows: RetentionResultRow[],\n breakdownValues?: string[]\n): RetentionSummary {\n const period1Rows = rows.filter((r) => r.period === 1)\n const period1Rates = period1Rows.map((r) => r.retentionRate)\n\n const totalUsers = rows\n .filter((r) => r.period === 0)\n .reduce((sum, r) => sum + r.cohortSize, 0)\n\n return {\n totalUsers,\n avgPeriod1Retention:\n period1Rates.length > 0\n ? period1Rates.reduce((a, b) => a + b, 0) / period1Rates.length\n : 0,\n maxPeriod1Retention: period1Rates.length > 0 ? Math.max(...period1Rates) : 0,\n minPeriod1Retention: period1Rates.length > 0 ? Math.min(...period1Rates) : 0,\n segmentCount: breakdownValues?.length || 1,\n }\n}\n\n/**\n * Hook for server-side retention query execution\n *\n * Usage:\n * ```tsx\n * const { chartData, isLoading, error } = useRetentionQuery(serverQuery, {\n * debounceMs: 300,\n * skip: !hasRequiredFields\n * })\n *\n * <RetentionHeatmap data={chartData} />\n * ```\n */\nexport function useRetentionQuery(\n serverQuery: ServerRetentionQuery | null,\n options: UseRetentionQueryOptions = {}\n): UseRetentionQueryResult {\n const { skip = false, debounceMs = DEFAULT_DEBOUNCE_MS, onComplete, onError, getFieldLabel } = options\n\n const { cubeApi } = useCubeApi()\n const queryClient = useQueryClient()\n\n // Get manual refresh mode from features\n const { features } = useCubeFeatures()\n const manualRefresh = features.manualRefresh ?? false\n\n // Track the last executed query (for manual refresh mode)\n const [executedQueryKey, setExecutedQueryKey] = useState<string | null>(null)\n\n // Validate query\n const isValidQuery = isValidRetentionQuery(serverQuery)\n\n // Use shared debounce hook\n const { debouncedValue: debouncedQuery, isDebouncing } = useDebounceQuery(serverQuery, {\n isValid: isValidQuery,\n skip,\n debounceMs,\n })\n\n // Create stable query key\n const queryKey = useMemo(() => {\n if (!debouncedQuery) return ['cube', 'retention', null] as const\n return ['cube', 'retention', JSON.stringify(debouncedQuery)] as const\n }, [debouncedQuery])\n\n // Calculate current query key for manual refresh tracking\n const currentQueryKey = debouncedQuery ? stableStringify(debouncedQuery) : null\n\n // Calculate if the current query differs from the last executed query\n const needsRefresh = useMemo(() => {\n if (!manualRefresh) return false\n if (!currentQueryKey) return false\n if (executedQueryKey === null) return false\n return currentQueryKey !== executedQueryKey\n }, [manualRefresh, currentQueryKey, executedQueryKey])\n\n // In manual refresh mode, only execute when explicitly triggered\n const shouldExecute = useMemo(() => {\n if (!debouncedQuery || skip) return false\n if (!manualRefresh) return true\n if (executedQueryKey === null) return true\n return executedQueryKey === currentQueryKey\n }, [debouncedQuery, skip, manualRefresh, executedQueryKey, currentQueryKey])\n\n // In auto mode, track executed query for consistency\n useEffect(() => {\n if (!manualRefresh && debouncedQuery && !skip) {\n setExecutedQueryKey(currentQueryKey)\n }\n }, [manualRefresh, debouncedQuery, skip, currentQueryKey])\n\n // Execute retention query via TanStack Query\n const queryResult = useQuery({\n queryKey,\n queryFn: async () => {\n if (!debouncedQuery) {\n throw new Error('No retention query available')\n }\n\n const startTime = performance.now()\n\n try {\n // Send retention query to server\n const resultSet = await cubeApi.load(debouncedQuery as unknown as CubeQuery)\n const rawData = resultSet.rawData()\n const executionTime = performance.now() - startTime\n const cacheInfo = resultSet.cacheInfo?.()\n\n return {\n rawData,\n executionTime,\n cacheInfo,\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n onError?.(err)\n throw err\n }\n },\n enabled: shouldExecute,\n staleTime: 60000, // 1 minute cache\n gcTime: 5 * 60 * 1000, // 5 minute garbage collection\n })\n\n // Track when query successfully executes in manual refresh mode\n useEffect(() => {\n if (!manualRefresh) return\n if (shouldExecute && queryResult.isSuccess && !queryResult.isFetching && debouncedQuery) {\n setExecutedQueryKey(currentQueryKey)\n }\n }, [manualRefresh, shouldExecute, queryResult.isSuccess, queryResult.isFetching, debouncedQuery, currentQueryKey])\n\n // Extract granularity and binding key label from server query\n const granularity = serverQuery?.retention?.granularity\n\n // Get the raw binding key field name for label lookup\n const rawBindingKeyField = useMemo(() => {\n const bindingKey = serverQuery?.retention?.bindingKey\n if (!bindingKey) return undefined\n if (typeof bindingKey === 'string') return bindingKey\n if (Array.isArray(bindingKey) && bindingKey.length > 0) {\n return bindingKey[0]?.dimension\n }\n return undefined\n }, [serverQuery?.retention?.bindingKey])\n\n // Use getFieldLabel if provided, fallback to extractBindingKeyLabel\n const bindingKeyLabel = useMemo(() => {\n if (rawBindingKeyField && getFieldLabel) {\n const label = getFieldLabel(rawBindingKeyField)\n // If getFieldLabel returns the same string, it didn't find a better label\n if (label && label !== rawBindingKeyField) {\n return label\n }\n }\n // Fallback to extracting from the field name\n return extractBindingKeyLabel(serverQuery?.retention?.bindingKey)\n }, [rawBindingKeyField, getFieldLabel, serverQuery?.retention?.bindingKey])\n\n // Transform server result to chart data\n const chartData = useMemo<RetentionChartData | null>(() => {\n if (!queryResult.data?.rawData) return null\n const result = transformRetentionResult(\n queryResult.data.rawData,\n granularity,\n bindingKeyLabel\n )\n\n // Call completion callback\n if (queryResult.isSuccess && !queryResult.isFetching) {\n onComplete?.(result)\n }\n\n return result\n }, [queryResult.data, queryResult.isSuccess, queryResult.isFetching, onComplete, granularity, bindingKeyLabel])\n\n // Execute function for manual refresh mode\n const execute = useCallback(\n async (executeOptions?: { bustCache?: boolean }) => {\n if (!debouncedQuery) return null\n\n if (executeOptions?.bustCache) {\n queryClient.removeQueries({ queryKey })\n }\n\n // Update executed query key to trigger execution\n setExecutedQueryKey(currentQueryKey)\n\n // Wait for the query to complete\n const result = await queryClient.fetchQuery({\n queryKey,\n queryFn: async () => {\n const resultSet = await cubeApi.load(debouncedQuery as unknown as CubeQuery)\n const rawData = resultSet.rawData()\n const cacheInfo = resultSet.cacheInfo?.()\n return { rawData, executionTime: 0, cacheInfo }\n },\n })\n\n return transformRetentionResult(result.rawData, granularity, bindingKeyLabel)\n },\n [debouncedQuery, queryClient, queryKey, cubeApi, currentQueryKey, granularity, bindingKeyLabel]\n )\n\n // Refetch function\n const refetch = useCallback(() => {\n queryResult.refetch()\n }, [queryResult])\n\n // Determine status\n const status = useMemo(() => {\n if (queryResult.isError) return 'error' as const\n if (queryResult.isLoading) return 'loading' as const\n if (queryResult.isSuccess) return 'success' as const\n return 'idle' as const\n }, [queryResult.isError, queryResult.isLoading, queryResult.isSuccess])\n\n return {\n chartData,\n rawData: queryResult.data?.rawData as RetentionResultRow[] | null ?? null,\n status,\n isLoading: queryResult.isLoading,\n isFetching: queryResult.isFetching,\n isDebouncing,\n error: queryResult.error as Error | null,\n cacheInfo: queryResult.data?.cacheInfo ?? null,\n execute,\n refetch,\n needsRefresh,\n }\n}\n","import React, { Component, ReactNode } from 'react'\nimport { getIcon } from '../icons'\n\nconst RefreshIcon = getIcon('refresh')\n\ninterface Props {\n children: ReactNode\n fallback?: ReactNode\n portletTitle?: string\n portletConfig?: any\n cubeQuery?: string\n}\n\ninterface State {\n hasError: boolean\n error: Error | null\n errorInfo: string | null\n}\n\nexport default class ChartErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props)\n this.state = {\n hasError: false,\n error: null,\n errorInfo: null\n }\n }\n\n static getDerivedStateFromError(error: Error): State {\n // Update state so the next render will show the fallback UI\n return {\n hasError: true,\n error,\n errorInfo: null\n }\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n // Update state with error details\n this.setState({\n error,\n errorInfo: errorInfo.componentStack || null\n })\n\n // Log the error for debugging\n console.error('Chart Error Boundary caught a rendering error:', error, errorInfo)\n }\n\n handleReset = () => {\n this.setState({\n hasError: false,\n error: null,\n errorInfo: null\n })\n }\n\n render() {\n if (this.state.hasError) {\n // Custom fallback UI\n if (this.props.fallback) {\n return this.props.fallback\n }\n\n // Default error display\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full dc:h-full dc:p-6 text-center dc:border dc:border-dashed dc:rounded-lg\"\n style={{ borderColor: 'var(--dc-border)', backgroundColor: 'var(--dc-surface)' }}>\n <div className=\"dc:h-12 dc:w-12 dc:mb-4 text-dc-text-muted\">⚠️</div>\n <h3 className=\"dc:text-lg dc:font-semibold dc:mb-2 text-dc-text\">\n {this.props.portletTitle ? `Unable to render ${this.props.portletTitle}` : 'Unable to render chart'}\n </h3>\n <p className=\"dc:text-sm text-dc-text-secondary dc:mb-4 dc:max-w-md\">\n There was an error rendering this chart component. The error details are shown below.\n </p>\n\n {/* Error details */}\n <div className=\"dc:w-full dc:max-w-2xl dc:mb-4\">\n <div className=\"bg-dc-surface-secondary dc:rounded-lg dc:p-3 text-left\">\n <div className=\"dc:text-xs font-mono dc:mb-2 text-dc-text\">\n <strong>Error:</strong> {this.state.error?.message}\n </div>\n {this.state.error?.name && (\n <div className=\"dc:text-xs font-mono text-dc-text-secondary dc:mb-2\">\n <strong>Type:</strong> {this.state.error.name}\n </div>\n )}\n\n {/* Portlet Config Debug Info */}\n {this.props.portletConfig && (\n <details className=\"dc:text-xs font-mono text-dc-text-secondary dc:mb-2\">\n <summary className=\"dc:cursor-pointer\">Portlet Configuration</summary>\n <pre className=\"dc:mt-2 dc:whitespace-pre-wrap dc:p-2 dc:rounded-sm dc:overflow-auto dc:max-h-32\"\n style={{ backgroundColor: 'rgba(var(--dc-primary-rgb), 0.1)' }}>\n {JSON.stringify(this.props.portletConfig, null, 2)}\n </pre>\n </details>\n )}\n\n {/* Cube Query Debug Info */}\n {this.props.cubeQuery && (\n <details className=\"dc:text-xs font-mono text-dc-text-secondary dc:mb-2\">\n <summary className=\"dc:cursor-pointer\">Cube Query</summary>\n <pre className=\"dc:mt-2 dc:whitespace-pre-wrap dc:p-2 dc:rounded-sm dc:overflow-auto dc:max-h-32\"\n style={{ backgroundColor: '#d1fae5' }}>\n {typeof this.props.cubeQuery === 'string' \n ? JSON.stringify(JSON.parse(this.props.cubeQuery), null, 2)\n : JSON.stringify(this.props.cubeQuery, null, 2)\n }\n </pre>\n </details>\n )}\n\n {this.state.errorInfo && (\n <details className=\"dc:text-xs font-mono text-dc-text-secondary\">\n <summary className=\"dc:cursor-pointer\">Component Stack</summary>\n <pre className=\"dc:mt-2 dc:whitespace-pre-wrap\">{this.state.errorInfo}</pre>\n </details>\n )}\n </div>\n </div>\n\n {/* Reset button */}\n <button\n onClick={this.handleReset}\n className=\"dc:px-3 dc:py-1 text-white dc:rounded-sm dc:text-sm dc:hover:opacity-90 dc:transition-opacity\"\n style={{\n backgroundColor: 'var(--dc-primary)'\n }}\n >\n <RefreshIcon style={{ width: '16px', height: '16px', display: 'inline', marginRight: '4px' }} />Try Again\n </button>\n </div>\n )\n }\n\n return this.props.children\n }\n}","/**\n * Type definitions for drizzle-cube client components\n */\n\nimport type { ReactNode } from 'react'\nimport type { ColorPalette } from './utils/colorPalettes'\nimport type { FunnelBindingKey } from './types/funnel'\nimport type { FlowChartData } from './types/flow'\nimport type { RetentionChartData } from './types/retention'\n\n// Cube metadata types\nexport interface CubeMetaField {\n name: string\n title: string\n shortTitle: string\n type: string\n}\n\nexport interface CubeMetaRelationship {\n targetCube: string\n relationship: 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany'\n joinFields?: Array<{\n sourceField: string\n targetField: string\n }>\n}\n\nexport interface CubeMetaCube {\n name: string\n title: string\n description?: string\n measures: CubeMetaField[]\n dimensions: CubeMetaField[]\n segments: CubeMetaField[]\n relationships?: CubeMetaRelationship[]\n /** Additional cube metadata (e.g., eventStream configuration for funnel queries) */\n meta?: {\n eventStream?: {\n bindingKey: string\n timeDimension: string\n }\n [key: string]: any\n }\n}\n\nexport interface CubeMeta {\n cubes: CubeMetaCube[]\n}\n\nexport type FieldLabelMap = Record<string, string>\n\n// Re-export color palette types\nexport type { ColorPalette } from './utils/colorPalettes'\n\n// Chart types\nexport type ChartType =\n | 'line'\n | 'bar'\n | 'pie'\n | 'table'\n | 'area'\n | 'scatter'\n | 'radar'\n | 'radialBar'\n | 'treemap'\n | 'bubble'\n | 'activityGrid'\n | 'kpiNumber'\n | 'kpiDelta'\n | 'kpiText'\n | 'markdown'\n | 'funnel'\n | 'sankey'\n | 'sunburst'\n | 'heatmap'\n | 'retentionHeatmap'\n | 'retentionCombined'\n\n// Axis formatting configuration\nexport interface AxisFormatConfig {\n label?: string // Custom axis label (overrides auto-generated)\n unit?: 'currency' | 'percent' | 'number' | 'custom' // Unit type for formatting\n abbreviate?: boolean // Use K, M, B suffixes for large numbers\n decimals?: number // Decimal places (0-4, undefined = auto)\n customPrefix?: string // Prefix for 'custom' unit type\n customSuffix?: string // Suffix for 'custom' unit type\n}\n\n// Chart configuration\nexport interface ChartAxisConfig {\n // New format (for advanced portlet editor)\n xAxis?: string[] // Dimension fields for X axis\n yAxis?: string[] // Measure fields for Y axis \n series?: string[] // Fields to use for series/grouping\n \n // Bubble chart specific fields\n sizeField?: string // Field for bubble size\n colorField?: string // Field for bubble color\n \n // Activity grid chart specific fields\n dateField?: string[] // Time dimension field for activity grid\n valueField?: string[] // Measure field for activity intensity\n \n // Legacy format (for backward compatibility)\n x?: string // Single dimension field for X axis\n y?: string[] // Measure fields for Y axis\n\n // Dual Y-axis support: per-measure axis assignment (left or right)\n // Default: 'left' for all measures (backward compatible)\n yAxisAssignment?: Record<string, 'left' | 'right'>\n}\n\nexport interface ChartDisplayConfig {\n showLegend?: boolean\n showGrid?: boolean\n showTooltip?: boolean\n colors?: string[]\n orientation?: 'horizontal' | 'vertical'\n stacked?: boolean // Deprecated: use stackType instead\n stackType?: 'none' | 'normal' | 'percent' // Stacking mode: none, normal (sum), or percent (100%)\n connectNulls?: boolean // For Area/Line charts: draw continuous lines through missing data\n hideHeader?: boolean // Hide portlet header in non-edit mode\n \n // Bubble chart specific display options\n minBubbleSize?: number\n maxBubbleSize?: number\n bubbleOpacity?: number\n \n // Activity grid specific display options\n showLabels?: boolean\n fitToWidth?: boolean\n\n // DataTable specific display options\n pivotTimeDimension?: boolean // Pivot time dimension as columns (default: true when time dimension present)\n\n // Target functionality\n target?: string // Target values as string (single value or comma-separated for spread)\n \n // KPI specific display options\n template?: string // JavaScript template string for KPI Text\n prefix?: string // Text prefix for KPI Number\n suffix?: string // Text suffix for KPI Number\n decimals?: number // Number of decimal places\n formatValue?: (value: number | null | undefined) => string // Custom value formatter function (takes precedence over prefix/suffix/decimals)\n valueColor?: string // Color for the KPI value (legacy)\n valueColorIndex?: number // Index of color from dashboard palette for KPI value\n \n // KPI Delta specific display options\n positiveColorIndex?: number // Index of color from dashboard palette for positive changes\n negativeColorIndex?: number // Index of color from dashboard palette for negative changes\n showHistogram?: boolean // Whether to show variance histogram\n\n // KPI time period handling\n useLastCompletePeriod?: boolean // Exclude incomplete current period (e.g., partial week/month)\n skipLastPeriod?: boolean // Always exclude the last period regardless of completeness\n \n // Markdown specific display options\n content?: string // Markdown content text\n accentColorIndex?: number // Index of color from dashboard palette for headers, bullets, links\n fontSize?: 'small' | 'medium' | 'large' // Text size for markdown content\n alignment?: 'left' | 'center' | 'right' // Text alignment for markdown content\n\n // Axis formatting options (for Line, Area, Bar, Scatter charts)\n xAxisFormat?: AxisFormatConfig // Formatting for X-axis values\n leftYAxisFormat?: AxisFormatConfig // Formatting for left Y-axis values\n rightYAxisFormat?: AxisFormatConfig // Formatting for right Y-axis values (dual-axis charts)\n\n // Period comparison display options (for compareDateRange queries)\n /**\n * How to display compared periods:\n * - 'separate': Each period as distinct series with different colors (default)\n * - 'overlay': Periods aligned by day-of-period index with ghost styling for prior periods\n */\n comparisonMode?: 'separate' | 'overlay'\n /** Line style for prior periods in overlay mode */\n priorPeriodStyle?: 'solid' | 'dashed' | 'dotted'\n /** Opacity for prior period lines (0-1), default: 0.5 */\n priorPeriodOpacity?: number\n /** Include period labels in legend */\n showPeriodLabels?: boolean\n\n // Funnel chart specific display options\n /** Custom labels for funnel steps (array indexed by step, e.g., [\"Signup\", \"Activation\", \"Purchase\"]) */\n funnelStepLabels?: string[]\n /** Hide the summary footer in funnel charts */\n hideSummaryFooter?: boolean\n /** Funnel orientation: horizontal (bars left to right) or vertical (bars bottom to top) */\n funnelOrientation?: 'horizontal' | 'vertical'\n /** @deprecated Use showFunnelAvgTime, showFunnelMedianTime, showFunnelP90Time instead */\n showFunnelTimeMetrics?: boolean\n /** Funnel visualization style: 'bars' (horizontal bars) or 'funnel' (trapezoid funnel shape) */\n funnelStyle?: 'bars' | 'funnel'\n /** Show step-to-step conversion rate (default: true) */\n showFunnelConversion?: boolean\n /** Show average time-to-convert metric in funnel charts */\n showFunnelAvgTime?: boolean\n /** Show median time-to-convert metric in funnel charts */\n showFunnelMedianTime?: boolean\n /** Show P90 time-to-convert metric in funnel charts */\n showFunnelP90Time?: boolean\n\n // Retention chart specific display options\n /** Retention display mode: line chart, heatmap table, or combined view */\n retentionDisplayMode?: 'line' | 'heatmap' | 'combined'\n}\n\n// Portlet configuration\nexport interface PortletConfig {\n id: string\n title: string\n\n /**\n * Canonical format for analysis configuration.\n * This is the single source of truth for all query/chart config.\n * New portlets only save this field. Legacy portlets are migrated on-the-fly.\n */\n analysisConfig?: import('./types/analysisConfig').AnalysisConfig\n\n // === Legacy fields (deprecated - for backward compatibility only) ===\n // These fields are optional and only used for reading old configurations.\n // New portlets do not write these fields.\n /** @deprecated Use analysisConfig.query instead */\n query?: string // JSON string of cube query (CubeQuery, MultiQueryConfig, or ServerFunnelQuery)\n /** @deprecated Use analysisConfig.charts[mode].chartType instead */\n chartType?: ChartType\n /** @deprecated Use analysisConfig.charts[mode].chartConfig instead */\n chartConfig?: ChartAxisConfig\n /** @deprecated Use analysisConfig.charts[mode].displayConfig instead */\n displayConfig?: ChartDisplayConfig\n dashboardFilterMapping?: string[] // Array of dashboard filter IDs that apply to this portlet\n eagerLoad?: boolean // Force immediate loading (overrides dashboard lazy loading setting)\n /** @deprecated Use analysisConfig.analysisType instead */\n analysisType?: AnalysisType // Optional - defaults to 'query' when undefined (backward compatible)\n\n // Funnel mode state (deprecated - now stored in analysisConfig)\n /** @deprecated Use analysisConfig for funnel mode */\n funnelCube?: string | null\n /** @deprecated Use analysisConfig for funnel mode */\n funnelSteps?: FunnelStepState[]\n /** @deprecated Use analysisConfig for funnel mode */\n funnelTimeDimension?: string | null\n /** @deprecated Use analysisConfig for funnel mode */\n funnelBindingKey?: FunnelBindingKey | null\n /** @deprecated Use analysisConfig for funnel mode */\n funnelChartType?: ChartType\n /** @deprecated Use analysisConfig for funnel mode */\n funnelChartConfig?: ChartAxisConfig\n /** @deprecated Use analysisConfig for funnel mode */\n funnelDisplayConfig?: ChartDisplayConfig\n\n w: number // Grid width\n h: number // Grid height\n x: number // Grid x position\n y: number // Grid y position\n}\n\nexport type DashboardLayoutMode = 'grid' | 'rows'\n\nexport interface DashboardGridSettings {\n cols: number\n rowHeight: number\n minW: number\n minH: number\n}\n\nexport interface RowLayoutColumn {\n portletId: string\n w: number\n}\n\nexport interface RowLayout {\n id: string\n h: number\n columns: RowLayoutColumn[]\n}\n\n// Dashboard configuration\nexport interface DashboardConfig {\n portlets: PortletConfig[]\n layoutMode?: DashboardLayoutMode\n grid?: DashboardGridSettings\n rows?: RowLayout[]\n layouts?: { [key: string]: any } // react-grid-layout layouts\n colorPalette?: string // Name of the color palette to use (defaults to 'default')\n filters?: DashboardFilter[] // Dashboard-level filters that can be applied to portlets\n eagerLoad?: boolean // Force immediate loading for all portlets (default: false, lazy load enabled)\n // Thumbnail fields for dashboard preview/sharing\n thumbnailData?: string // Transient: base64 data URI for upload (cleared by server after processing)\n thumbnailUrl?: string // Permanent: CDN URL after server-side processing\n}\n\n// Analysis type for explicit mode selection in AnalysisBuilder\n// 'query' supports both single and multi-query (add more queries via + button)\n// 'funnel' for funnel analysis with sequential steps\n// 'flow' for bidirectional flow analysis with Sankey visualization\n// 'retention' for cohort-based retention analysis\nexport type AnalysisType = 'query' | 'funnel' | 'flow' | 'retention'\n\n/**\n * State for a single funnel step (dedicated for Funnel mode)\n * Each step represents a stage in the funnel with its own cube and filters\n */\nexport interface FunnelStepState {\n /** Unique step identifier */\n id: string\n /** Display name for the step (e.g., \"Signup\", \"Purchase\") */\n name: string\n /** Which cube this step uses (for multi-cube funnels) */\n cube: string\n /** Filters that define which events qualify for this step */\n filters: Filter[]\n /** Time window from previous step (ISO 8601 duration, e.g., \"P7D\" for 7 days) */\n timeToConvert?: string\n}\n\n// Filter types - hierarchical structure supporting AND/OR logic\nexport type FilterOperator = \n // String operators\n | 'equals' | 'notEquals' | 'contains' | 'notContains' \n | 'startsWith' | 'notStartsWith' | 'endsWith' | 'notEndsWith'\n | 'like' | 'notLike' | 'ilike'\n // Numeric operators \n | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'notBetween'\n // Array operators\n | 'in' | 'notIn'\n // PostgreSQL array operators\n | 'arrayContains' | 'arrayOverlaps' | 'arrayContained'\n // Null/Empty operators\n | 'set' | 'notSet' | 'isEmpty' | 'isNotEmpty'\n // Date operators\n | 'inDateRange' | 'beforeDate' | 'afterDate'\n // Regex operators\n | 'regex' | 'notRegex'\n\nexport interface SimpleFilter {\n member: string\n operator: FilterOperator\n values: any[]\n dateRange?: string | string[]\n}\n\nexport interface GroupFilter {\n type: 'and' | 'or'\n filters: Filter[]\n}\n\nexport type Filter = SimpleFilter | GroupFilter\n\n// Dashboard filter with ID and label for dashboard-level filtering\nexport interface DashboardFilter {\n id: string // Unique identifier for the filter\n label: string // Display label for the filter\n filter: Filter // The actual filter definition\n isUniversalTime?: boolean // When true, applies to all timeDimensions in portlets (ignores member field)\n}\n\n// Cube query types\nexport interface CubeQuery {\n measures?: string[]\n dimensions?: string[]\n timeDimensions?: Array<{\n dimension: string\n granularity?: string\n dateRange?: string[] | string\n fillMissingDates?: boolean\n /**\n * Array of date ranges for period-over-period comparison.\n * When specified, queries are executed for each period and results are merged.\n */\n compareDateRange?: (string | [string, string])[]\n }>\n filters?: Filter[]\n order?: { [key: string]: 'asc' | 'desc' }\n limit?: number\n offset?: number\n segments?: string[]\n}\n\n/**\n * Merge strategy for combining multiple query results\n * - 'concat': Append rows with __queryIndex marker (for separate series per query)\n * - 'merge': Align data by common dimension key (for combined visualization)\n *\n * Note: For funnel analysis, use the dedicated funnel mode (analysisType === 'funnel')\n */\nexport type QueryMergeStrategy = 'concat' | 'merge'\n\n/**\n * Configuration for multi-query portlets\n * Detected by presence of 'queries' array property\n *\n * Note: For funnel analysis, use the dedicated funnel mode (analysisType === 'funnel')\n */\nexport interface MultiQueryConfig {\n queries: CubeQuery[]\n mergeStrategy: QueryMergeStrategy\n mergeKeys?: string[] // Dimensions to align on (for 'merge' strategy) - composite key\n queryLabels?: string[] // User-defined labels per query\n}\n\n/**\n * Type guard to detect multi-query configuration\n */\nexport function isMultiQueryConfig(obj: unknown): obj is MultiQueryConfig {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'queries' in obj &&\n Array.isArray((obj as MultiQueryConfig).queries) &&\n (obj as MultiQueryConfig).queries.length > 0\n )\n}\n\nexport interface CubeQueryOptions {\n skip?: boolean\n resetResultSetOnChange?: boolean\n subscribe?: boolean\n}\n\nexport interface CubeApiOptions {\n apiUrl?: string\n token?: string\n headers?: Record<string, string>\n credentials?: 'include' | 'omit' | 'same-origin'\n}\n\n// Result set types\nexport interface CubeResultSet {\n rawData(): any[]\n tablePivot(): any[]\n series(): any[]\n annotation(): any\n loadResponse?: any\n cacheInfo?(): { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | undefined\n}\n\n// Component props\nexport interface AnalyticsPortletProps {\n query: string\n chartType: ChartType\n chartConfig?: ChartAxisConfig\n displayConfig?: ChartDisplayConfig\n dashboardFilters?: DashboardFilter[] // Dashboard-level filters to merge with portlet query\n dashboardFilterMapping?: string[] // Array of dashboard filter IDs that apply to this portlet\n eagerLoad?: boolean // Force immediate loading (default: false, lazy load enabled)\n isVisible?: boolean // Whether the portlet is visible in the viewport (for lazy loading)\n height?: string | number\n title?: string\n colorPalette?: ColorPalette // Complete palette with both colors and gradient\n loadingComponent?: ReactNode // Custom loading indicator (defaults to LoadingIndicator)\n onDebugDataReady?: (debugData: {\n chartConfig: ChartAxisConfig\n displayConfig: ChartDisplayConfig\n queryObject: any\n data: any[] | FlowChartData | RetentionChartData\n chartType: ChartType\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n }) => void\n}\n\nexport interface AnalyticsDashboardProps {\n config: DashboardConfig\n editable?: boolean\n dashboardFilters?: DashboardFilter[] // Programmatic dashboard filters (merged with config.filters)\n loadingComponent?: ReactNode // Custom loading indicator for all portlets (defaults to LoadingIndicator)\n onConfigChange?: (config: DashboardConfig) => void\n onSave?: (config: DashboardConfig) => Promise<void> | void\n onSaveThumbnail?: (thumbnailData: string) => Promise<string | void> // Callback to save thumbnail separately (called on edit mode exit)\n onDirtyStateChange?: (isDirty: boolean) => void\n}\n\nexport interface ChartProps {\n data: any[]\n chartConfig?: ChartAxisConfig\n displayConfig?: ChartDisplayConfig\n queryObject?: CubeQuery\n height?: string | number\n colorPalette?: ColorPalette // Complete palette with both colors and gradient\n}\n\n// Thumbnail feature configuration\nexport interface ThumbnailFeatureConfig {\n enabled: boolean\n width?: number // default: 1600 (higher resolution for crisp thumbnails)\n height?: number // default: 1200 (4:3 aspect ratio)\n format?: 'png' | 'jpeg' // default: 'png'\n quality?: number // 0-1, mainly for jpeg (PNG ignores this)\n}\n\n// Features configuration\nexport interface FeaturesConfig {\n enableAI?: boolean // Default: true for backward compatibility\n aiEndpoint?: string // Custom AI endpoint (default: '/api/ai/generate')\n showSchemaDiagram?: boolean // Deprecated - schema diagram feature has been removed\n useAnalysisBuilder?: boolean // Deprecated - AnalysisBuilder modal is now always used (PortletEditModal was removed)\n editToolbar?: 'floating' | 'top' | 'both' // Which edit toolbar(s) to show: 'floating' only, 'top' only, or 'both' (default: 'both')\n floatingToolbarPosition?: 'left' | 'right' // Position of floating toolbar when enabled (default: 'right')\n thumbnail?: ThumbnailFeatureConfig // Optional dashboard thumbnail capture on save\n manualRefresh?: boolean // When true, queries don't auto-execute on config changes. User must click Refresh. (default: false)\n}\n\n// Grid layout types (simplified)\nexport interface GridLayout {\n i: string\n x: number\n y: number\n w: number\n h: number\n minW?: number\n minH?: number\n}\n\nexport interface ResponsiveLayout {\n [breakpoint: string]: GridLayout[]\n}\n\n// Dashboard display modes for responsive layout\nexport type DashboardDisplayMode = 'desktop' | 'scaled' | 'mobile'\n\n// Re-export funnel types\nexport type {\n FunnelBindingKey,\n FunnelBindingKeyMapping,\n FunnelStep,\n FunnelConfig,\n FunnelStepResult,\n FunnelExecutionResult,\n FunnelChartData,\n FunnelValidationError,\n FunnelValidationResult,\n UseFunnelQueryOptions,\n UseFunnelQueryResult,\n ServerFunnelQuery,\n} from './types/funnel'\n\n/**\n * Type guard to detect server funnel query format\n * Used to distinguish { funnel: {...} } from CubeQuery or MultiQueryConfig\n */\nexport function isServerFunnelQuery(obj: unknown): obj is import('./types/funnel').ServerFunnelQuery {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'funnel' in obj &&\n typeof (obj as { funnel: unknown }).funnel === 'object'\n )\n}\n\n// Re-export flow types\nexport type {\n FlowStartingStep,\n ServerFlowQuery,\n FlowQueryConfig,\n SankeyNode,\n SankeyLink,\n FlowResultRow,\n FlowChartData,\n FlowSliceState,\n FlowSliceActions,\n} from './types/flow'\n\nexport { isServerFlowQuery, isSankeyData } from './types/flow'\n\n// ============================================================================\n// EXPLAIN PLAN TYPES\n// These types mirror the server-side types in src/server/types/executor.ts\n// ============================================================================\n\n/**\n * Options for EXPLAIN query execution\n */\nexport interface ExplainOptions {\n /** Use EXPLAIN ANALYZE to actually execute the query and get real timing (PostgreSQL, MySQL 8.0.18+) */\n analyze?: boolean\n}\n\n/**\n * A single operation/node in the query execution plan\n * Normalized structure across all databases\n */\nexport interface ExplainOperation {\n /** Operation type (e.g., 'Seq Scan', 'Index Scan', 'Hash Join', 'Sort') */\n type: string\n /** Table name if applicable */\n table?: string\n /** Index name if used */\n index?: string\n /** Estimated row count from planner */\n estimatedRows?: number\n /** Actual row count (if ANALYZE was used) */\n actualRows?: number\n /** Estimated cost (database-specific units) */\n estimatedCost?: number\n /** Filter condition if any */\n filter?: string\n /** Additional details specific to this operation */\n details?: string\n /** Nested/child operations */\n children?: ExplainOperation[]\n}\n\n/**\n * Summary statistics from the execution plan\n */\nexport interface ExplainSummary {\n /** Database engine type */\n database: 'postgres' | 'mysql' | 'sqlite'\n /** Planning time in milliseconds (if available) */\n planningTime?: number\n /** Execution time in milliseconds (if ANALYZE was used) */\n executionTime?: number\n /** Total estimated cost */\n totalCost?: number\n /** Quick flag: true if any sequential scans detected */\n hasSequentialScans: boolean\n /** List of indexes used in the plan */\n usedIndexes: string[]\n}\n\n/**\n * Result of an EXPLAIN query\n * Provides both normalized structure and raw output\n */\nexport interface ExplainResult {\n /** Normalized hierarchical plan as operations */\n operations: ExplainOperation[]\n /** Summary statistics */\n summary: ExplainSummary\n /** Raw EXPLAIN output as text (for display) */\n raw: string\n /** Original SQL query */\n sql: {\n sql: string\n params?: unknown[]\n }\n}\n\n// ============================================================================\n// AI EXPLAIN ANALYSIS TYPES\n// These types mirror the server-side types in src/server/types/executor.ts\n// ============================================================================\n\n/**\n * A recommendation from AI analysis of an execution plan\n */\nexport interface ExplainRecommendation {\n /** Type of recommendation */\n type: 'index' | 'table' | 'cube' | 'general'\n /** Severity/priority of the recommendation */\n severity: 'critical' | 'warning' | 'suggestion'\n /** Short actionable title */\n title: string\n /** Detailed explanation of why this helps */\n description: string\n /** Actionable SQL statement (e.g., CREATE INDEX) - for index/table recommendations */\n sql?: string\n /** TypeScript code snippet to add to cube definition - for cube recommendations */\n cubeCode?: string\n /** Which cube to modify - for cube recommendations */\n cubeName?: string\n /** Affected database table */\n table?: string\n /** Affected columns */\n columns?: string[]\n /** Expected performance improvement */\n estimatedImpact?: string\n}\n\n/**\n * Issue identified in the execution plan\n */\nexport interface ExplainIssue {\n /** Type of issue */\n type: 'sequential_scan' | 'missing_index' | 'high_cost' | 'sort_operation' | string\n /** Description of the issue */\n description: string\n /** Severity level */\n severity: 'high' | 'medium' | 'low'\n}\n\n/**\n * AI-generated analysis of an execution plan\n */\nexport interface AIExplainAnalysis {\n /** One-sentence description of what the query does */\n summary: string\n /** Overall performance assessment */\n assessment: 'good' | 'warning' | 'critical'\n /** Reason for the assessment */\n assessmentReason: string\n /** Detailed explanation of the query's purpose and structure */\n queryUnderstanding: string\n /** Issues identified in the execution plan */\n issues: ExplainIssue[]\n /** Actionable recommendations for improvement */\n recommendations: ExplainRecommendation[]\n /** Metadata from the AI analysis */\n _meta?: {\n model: string\n usingUserKey: boolean\n }\n}\n","/**\n * AnalysisConfig - Versioned, mode-agnostic configuration format\n *\n * This is the canonical format for persisting analysis state to:\n * - localStorage (standalone AnalysisBuilder)\n * - Share URLs\n * - Dashboard portlets (via analysisConfig field)\n *\n * Key design principles:\n * - `query` field IS the executable query (no transformation needed)\n * - Per-mode chart config via `charts[analysisType]` map\n * - No UI state (activeQueryIndex, activeFunnelStepIndex) - those are transient\n */\n\nimport type {\n ChartType,\n ChartAxisConfig,\n ChartDisplayConfig,\n CubeQuery,\n MultiQueryConfig,\n} from '../types'\nimport type { ServerFunnelQuery } from './funnel'\nimport type { ServerFlowQuery } from './flow'\nimport type { ServerRetentionQuery } from './retention'\n\n// ============================================================================\n// Chart Configuration\n// ============================================================================\n\n/**\n * Chart configuration - shared across all analysis types\n */\nexport interface ChartConfig {\n chartType: ChartType\n chartConfig: ChartAxisConfig\n displayConfig: ChartDisplayConfig\n}\n\n// ============================================================================\n// Analysis Type\n// ============================================================================\n\n/**\n * Analysis type discriminator\n * Future modes: 'cohort'\n */\nexport type AnalysisType = 'query' | 'funnel' | 'flow' | 'retention'\n\n// ============================================================================\n// Base Configuration\n// ============================================================================\n\n/**\n * Base config - common to all analysis types\n */\ninterface AnalysisConfigBase {\n /** Version number for migration support */\n version: 1\n\n /** Which analysis mode this config represents */\n analysisType: AnalysisType\n\n /** Whether to show table or chart view */\n activeView: 'table' | 'chart'\n\n /**\n * Per-mode chart configuration map.\n * Each mode owns its own chart settings, allowing users to configure\n * different chart types for query mode vs funnel mode.\n */\n charts: {\n [K in AnalysisType]?: ChartConfig\n }\n}\n\n// ============================================================================\n// Query Mode Configuration\n// ============================================================================\n\n/**\n * Query mode config - for single queries and multi-query analysis\n *\n * The `query` field is the actual executable query:\n * - Single query: CubeQuery object\n * - Multi-query: MultiQueryConfig with queries array and merge strategy\n */\nexport interface QueryAnalysisConfig extends AnalysisConfigBase {\n analysisType: 'query'\n\n /**\n * The executable query.\n * - CubeQuery: Single query with measures, dimensions, filters\n * - MultiQueryConfig: Multiple queries with merge strategy\n */\n query: CubeQuery | MultiQueryConfig\n}\n\n// ============================================================================\n// Funnel Mode Configuration\n// ============================================================================\n\n/**\n * Funnel mode config - for funnel analysis\n *\n * The `query` field is the ServerFunnelQuery which can be sent\n * directly to the server for execution.\n */\nexport interface FunnelAnalysisConfig extends AnalysisConfigBase {\n analysisType: 'funnel'\n\n /**\n * Server funnel query - executable as-is.\n * Contains bindingKey, timeDimension, steps[], and options.\n */\n query: ServerFunnelQuery\n}\n\n// ============================================================================\n// Flow Mode Configuration\n// ============================================================================\n\n/**\n * Flow mode config - for bidirectional flow analysis\n *\n * The `query` field is the ServerFlowQuery which can be sent\n * directly to the server for execution.\n */\nexport interface FlowAnalysisConfig extends AnalysisConfigBase {\n analysisType: 'flow'\n\n /**\n * Server flow query - executable as-is.\n * Contains bindingKey, timeDimension, eventDimension, startingStep, and depth config.\n */\n query: ServerFlowQuery\n}\n\n// ============================================================================\n// Retention Mode Configuration\n// ============================================================================\n\n/**\n * Retention mode config - for cohort-based retention analysis\n *\n * The `query` field is the ServerRetentionQuery which can be sent\n * directly to the server for execution.\n */\nexport interface RetentionAnalysisConfig extends AnalysisConfigBase {\n analysisType: 'retention'\n\n /**\n * Server retention query - executable as-is.\n * Contains cohortTimeDimension, activityTimeDimension, bindingKey,\n * granularity settings, and period configuration.\n */\n query: ServerRetentionQuery\n}\n\n// ============================================================================\n// Union Type\n// ============================================================================\n\n/**\n * AnalysisConfig - union of all analysis mode configurations\n */\nexport type AnalysisConfig = QueryAnalysisConfig | FunnelAnalysisConfig | FlowAnalysisConfig | RetentionAnalysisConfig\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Check if config is for query mode\n */\nexport const isQueryConfig = (c: AnalysisConfig): c is QueryAnalysisConfig =>\n c.analysisType === 'query'\n\n/**\n * Check if config is for funnel mode\n */\nexport const isFunnelConfig = (c: AnalysisConfig): c is FunnelAnalysisConfig =>\n c.analysisType === 'funnel'\n\n/**\n * Check if config is for flow mode\n */\nexport const isFlowConfig = (c: AnalysisConfig): c is FlowAnalysisConfig =>\n c.analysisType === 'flow'\n\n/**\n * Check if config is for retention mode\n */\nexport const isRetentionConfig = (c: AnalysisConfig): c is RetentionAnalysisConfig =>\n c.analysisType === 'retention'\n\n/**\n * Check if a query config contains multiple queries\n */\nexport const isMultiQuery = (config: QueryAnalysisConfig): boolean =>\n 'queries' in config.query &&\n Array.isArray((config.query as MultiQueryConfig).queries)\n\n/**\n * Check if a query config contains a single query\n */\nexport const isSingleQuery = (config: QueryAnalysisConfig): boolean =>\n !isMultiQuery(config)\n\n/**\n * Type guard to validate if an unknown value is a valid AnalysisConfig\n */\nexport const isValidAnalysisConfig = (\n config: unknown\n): config is AnalysisConfig => {\n if (!config || typeof config !== 'object') return false\n\n const c = config as Record<string, unknown>\n\n // Check version\n if (c.version !== 1) return false\n\n // Check analysisType\n if (c.analysisType !== 'query' && c.analysisType !== 'funnel' && c.analysisType !== 'flow' && c.analysisType !== 'retention') return false\n\n // Check query exists\n if (!c.query || typeof c.query !== 'object') return false\n\n // Check activeView\n if (c.activeView !== 'table' && c.activeView !== 'chart') return false\n\n return true\n}\n\n// ============================================================================\n// Default Factories\n// ============================================================================\n\n/**\n * Create a default empty query analysis config\n */\nexport const createDefaultQueryConfig = (): QueryAnalysisConfig => ({\n version: 1,\n analysisType: 'query',\n activeView: 'chart',\n charts: {\n query: {\n chartType: 'bar',\n chartConfig: {},\n displayConfig: {},\n },\n },\n query: {\n measures: [],\n dimensions: [],\n },\n})\n\n/**\n * Create a default empty funnel analysis config\n */\nexport const createDefaultFunnelConfig = (): FunnelAnalysisConfig => ({\n version: 1,\n analysisType: 'funnel',\n activeView: 'chart',\n charts: {\n funnel: {\n chartType: 'funnel',\n chartConfig: {},\n displayConfig: {},\n },\n },\n query: {\n funnel: {\n bindingKey: '',\n timeDimension: '',\n steps: [],\n },\n },\n})\n\n/**\n * Create a default empty flow analysis config\n */\nexport const createDefaultFlowConfig = (): FlowAnalysisConfig => ({\n version: 1,\n analysisType: 'flow',\n activeView: 'chart',\n charts: {\n flow: {\n chartType: 'sankey',\n chartConfig: {},\n displayConfig: {},\n },\n },\n query: {\n flow: {\n bindingKey: '',\n timeDimension: '',\n eventDimension: '',\n startingStep: {\n name: '',\n filter: undefined,\n },\n stepsBefore: 3,\n stepsAfter: 3,\n joinStrategy: 'auto',\n },\n },\n})\n\n/**\n * Create a default empty retention analysis config\n */\nexport const createDefaultRetentionConfig = (): RetentionAnalysisConfig => ({\n version: 1,\n analysisType: 'retention',\n activeView: 'chart',\n charts: {\n retention: {\n chartType: 'heatmap',\n chartConfig: {},\n displayConfig: {},\n },\n },\n query: {\n retention: {\n timeDimension: '',\n bindingKey: '',\n dateRange: { start: '', end: '' },\n granularity: 'week',\n periods: 12,\n retentionType: 'classic',\n },\n },\n})\n\n/**\n * Create a default config for the given analysis type\n */\nexport const createDefaultConfig = (\n type: AnalysisType = 'query'\n): AnalysisConfig => {\n switch (type) {\n case 'funnel':\n return createDefaultFunnelConfig()\n case 'flow':\n return createDefaultFlowConfig()\n case 'retention':\n return createDefaultRetentionConfig()\n case 'query':\n default:\n return createDefaultQueryConfig()\n }\n}\n\n// ============================================================================\n// Workspace Format (localStorage persistence)\n// ============================================================================\n\n/**\n * AnalysisWorkspace - Multi-mode persistence format for localStorage\n *\n * Unlike AnalysisConfig (which represents a single analysis mode),\n * AnalysisWorkspace preserves state for ALL modes. This prevents state\n * loss when switching between query, funnel, flow, and retention modes.\n *\n * Usage:\n * - localStorage persistence → AnalysisWorkspace (preserves all modes)\n * - Share URLs → AnalysisConfig (shares one specific analysis)\n * - Dashboard portlets → AnalysisConfig (embeds one specific analysis)\n */\nexport interface AnalysisWorkspace {\n /** Version number for migration support */\n version: 1\n\n /** Currently active analysis type */\n activeType: AnalysisType\n\n /**\n * Per-mode configurations.\n * Each mode stores its complete AnalysisConfig independently.\n * Charts are duplicated per-mode but merged on load.\n */\n modes: {\n query?: QueryAnalysisConfig\n funnel?: FunnelAnalysisConfig\n flow?: FlowAnalysisConfig\n retention?: RetentionAnalysisConfig\n }\n}\n\n/**\n * Type guard to validate if an unknown value is a valid AnalysisWorkspace\n */\nexport const isValidAnalysisWorkspace = (\n data: unknown\n): data is AnalysisWorkspace => {\n if (!data || typeof data !== 'object') return false\n\n const w = data as Record<string, unknown>\n\n // Check version\n if (w.version !== 1) return false\n\n // Check activeType\n if (w.activeType !== 'query' && w.activeType !== 'funnel' && w.activeType !== 'flow' && w.activeType !== 'retention') return false\n\n // Check modes exists and is an object\n if (!w.modes || typeof w.modes !== 'object') return false\n\n return true\n}\n\n/**\n * Create a default empty workspace with all modes initialized\n */\nexport const createDefaultWorkspace = (): AnalysisWorkspace => ({\n version: 1,\n activeType: 'query',\n modes: {\n query: createDefaultQueryConfig(),\n funnel: createDefaultFunnelConfig(),\n flow: createDefaultFlowConfig(),\n retention: createDefaultRetentionConfig(),\n },\n})\n","/**\n * Config Migration Utilities\n *\n * Handles migration from legacy portlet/share formats to AnalysisConfig.\n * Used for backward compatibility when loading old saved configurations.\n */\n\nimport type {\n AnalysisConfig,\n QueryAnalysisConfig,\n FunnelAnalysisConfig,\n FlowAnalysisConfig,\n RetentionAnalysisConfig,\n ChartConfig,\n} from '../types/analysisConfig'\nimport { createDefaultQueryConfig, isValidAnalysisConfig } from '../types/analysisConfig'\nimport type {\n ChartType,\n ChartAxisConfig,\n ChartDisplayConfig,\n CubeQuery,\n MultiQueryConfig,\n FunnelBindingKey,\n QueryMergeStrategy,\n PortletConfig,\n} from '../types'\nimport type { ServerFunnelQuery, ServerFunnelStep } from '../types/funnel'\nimport type { ServerFlowQuery } from '../types/flow'\nimport { isServerRetentionQuery } from '../types/retention'\n\n// ============================================================================\n// Legacy Portlet Format\n// ============================================================================\n\n/**\n * Legacy portlet format (before AnalysisConfig)\n */\nexport interface LegacyPortlet {\n /** JSON string of CubeQuery or MultiQueryConfig */\n query: string\n chartType?: ChartType\n chartConfig?: ChartAxisConfig\n displayConfig?: ChartDisplayConfig\n analysisType?: 'query' | 'funnel'\n // Funnel-specific legacy fields\n funnelChartType?: ChartType\n funnelChartConfig?: ChartAxisConfig\n funnelDisplayConfig?: ChartDisplayConfig\n}\n\n/**\n * Legacy MultiQueryConfig with funnel merge strategy\n * This is a standalone type (not extending MultiQueryConfig) to handle old data\n * where mergeStrategy was 'funnel' before it was removed from QueryMergeStrategy.\n */\nexport interface LegacyFunnelMultiQuery {\n queries: CubeQuery[]\n mergeStrategy: 'funnel'\n mergeKeys?: string[]\n queryLabels?: string[]\n funnelBindingKey?: FunnelBindingKey | null\n stepTimeToConvert?: (string | null)[]\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Check if a query is a MultiQueryConfig\n */\nfunction isMultiQueryConfig(query: unknown): query is MultiQueryConfig {\n return (\n typeof query === 'object' &&\n query !== null &&\n 'queries' in query &&\n Array.isArray((query as MultiQueryConfig).queries)\n )\n}\n\n/**\n * Check if a query is a legacy funnel multi-query\n * Uses type assertion since 'funnel' is no longer in QueryMergeStrategy\n */\nfunction isLegacyFunnelMultiQuery(\n query: unknown\n): query is LegacyFunnelMultiQuery {\n return (\n typeof query === 'object' &&\n query !== null &&\n 'queries' in query &&\n Array.isArray((query as { queries?: unknown[] }).queries) &&\n 'mergeStrategy' in query &&\n (query as { mergeStrategy?: string }).mergeStrategy === 'funnel'\n )\n}\n\n/**\n * Check if a query is a ServerFunnelQuery\n */\nfunction isServerFunnelQuery(query: unknown): query is ServerFunnelQuery {\n return (\n typeof query === 'object' &&\n query !== null &&\n 'funnel' in query &&\n typeof (query as ServerFunnelQuery).funnel === 'object'\n )\n}\n\n/**\n * Check if a query is a ServerFlowQuery\n */\nfunction isServerFlowQuery(query: unknown): query is ServerFlowQuery {\n return (\n typeof query === 'object' &&\n query !== null &&\n 'flow' in query &&\n typeof (query as ServerFlowQuery).flow === 'object'\n )\n}\n\n// ============================================================================\n// Migration Functions\n// ============================================================================\n\n/**\n * Extract chart config from legacy portlet fields\n */\nfunction extractChartConfig(\n portlet: LegacyPortlet,\n analysisType: 'query' | 'funnel' | 'flow' | 'retention'\n): ChartConfig {\n if (analysisType === 'funnel') {\n return {\n chartType: portlet.funnelChartType || portlet.chartType || 'funnel',\n chartConfig: portlet.funnelChartConfig || portlet.chartConfig || {},\n displayConfig: portlet.funnelDisplayConfig || portlet.displayConfig || {},\n }\n }\n\n if (analysisType === 'flow') {\n // Flow uses sankey chart by default\n return {\n chartType: portlet.chartType || 'sankey',\n chartConfig: portlet.chartConfig || {},\n displayConfig: portlet.displayConfig || {},\n }\n }\n\n if (analysisType === 'retention') {\n // Retention uses retentionCombined chart by default\n return {\n chartType: portlet.chartType || 'retentionCombined',\n chartConfig: portlet.chartConfig || {},\n displayConfig: portlet.displayConfig || {},\n }\n }\n\n return {\n chartType: portlet.chartType || 'bar',\n chartConfig: portlet.chartConfig || {},\n displayConfig: portlet.displayConfig || {},\n }\n}\n\n/**\n * Migrate a legacy portlet to AnalysisConfig format\n *\n * Handles:\n * - Single CubeQuery → QueryAnalysisConfig\n * - MultiQueryConfig → QueryAnalysisConfig\n * - ServerFunnelQuery → FunnelAnalysisConfig\n * - ServerFlowQuery → FlowAnalysisConfig\n * - Legacy mergeStrategy:'funnel' → FunnelAnalysisConfig (via migrateLegacyFunnelMerge)\n *\n * @param portlet - Legacy portlet with query string\n * @returns AnalysisConfig in new format\n */\nexport function migrateLegacyPortlet(portlet: LegacyPortlet): AnalysisConfig {\n try {\n const query = JSON.parse(portlet.query)\n\n // Check if it's a ServerRetentionQuery\n if (isServerRetentionQuery(query)) {\n const chartConfig = extractChartConfig(portlet, 'retention')\n return {\n version: 1,\n analysisType: 'retention',\n activeView: 'chart',\n charts: {\n retention: chartConfig,\n },\n query,\n } as RetentionAnalysisConfig\n }\n\n // Check if it's a ServerFlowQuery\n if (isServerFlowQuery(query)) {\n const chartConfig = extractChartConfig(portlet, 'flow')\n return {\n version: 1,\n analysisType: 'flow',\n activeView: 'chart',\n charts: {\n flow: chartConfig,\n },\n query,\n } as FlowAnalysisConfig\n }\n\n // Check if it's already a ServerFunnelQuery\n if (isServerFunnelQuery(query)) {\n const chartConfig = extractChartConfig(portlet, 'funnel')\n return {\n version: 1,\n analysisType: 'funnel',\n activeView: 'chart',\n charts: {\n funnel: chartConfig,\n },\n query,\n } as FunnelAnalysisConfig\n }\n\n // Check if it's a legacy funnel multi-query\n if (isLegacyFunnelMultiQuery(query)) {\n return migrateLegacyFunnelMerge(query, portlet)\n }\n\n // Check explicit analysisType\n if (portlet.analysisType === 'funnel') {\n // Treat as funnel even if query isn't ServerFunnelQuery format\n // (This handles edge cases where analysisType was set but query wasn't converted)\n const chartConfig = extractChartConfig(portlet, 'funnel')\n return {\n version: 1,\n analysisType: 'funnel',\n activeView: 'chart',\n charts: {\n funnel: chartConfig,\n },\n query: isServerFunnelQuery(query)\n ? query\n : {\n funnel: {\n bindingKey: '',\n timeDimension: '',\n steps: [],\n },\n },\n } as FunnelAnalysisConfig\n }\n\n // Handle regular query or multi-query\n const chartConfig = extractChartConfig(portlet, 'query')\n\n // MultiQueryConfig (non-funnel, since isLegacyFunnelMultiQuery was checked earlier)\n if (isMultiQueryConfig(query)) {\n // Defensive: convert any lingering 'funnel' strategy to 'concat'\n const strategy = (query.mergeStrategy as string) === 'funnel' ? 'concat' : query.mergeStrategy\n return {\n version: 1,\n analysisType: 'query',\n activeView: 'chart',\n charts: {\n query: chartConfig,\n },\n query: {\n queries: query.queries,\n mergeStrategy: strategy as QueryMergeStrategy,\n mergeKeys: query.mergeKeys,\n queryLabels: query.queryLabels,\n },\n } as QueryAnalysisConfig\n }\n\n // Single CubeQuery\n return {\n version: 1,\n analysisType: 'query',\n activeView: 'chart',\n charts: {\n query: chartConfig,\n },\n query: query as CubeQuery,\n } as QueryAnalysisConfig\n } catch (error) {\n // If parsing fails, return default config\n console.warn('[configMigration] Failed to parse legacy portlet:', error)\n return createDefaultQueryConfig()\n }\n}\n\n/**\n * Migrate legacy mergeStrategy:'funnel' to FunnelAnalysisConfig\n *\n * This handles the old pattern where funnels were created using multi-query\n * with mergeStrategy: 'funnel'. Converts to the new dedicated funnel format.\n *\n * @param legacyQuery - MultiQueryConfig with mergeStrategy: 'funnel'\n * @param portlet - Legacy portlet for chart config\n * @returns FunnelAnalysisConfig in new format\n */\nexport function migrateLegacyFunnelMerge(\n legacyQuery: LegacyFunnelMultiQuery,\n portlet?: LegacyPortlet\n): FunnelAnalysisConfig {\n // Extract binding key\n let bindingKey: ServerFunnelQuery['funnel']['bindingKey'] = ''\n if (legacyQuery.funnelBindingKey?.dimension) {\n if (typeof legacyQuery.funnelBindingKey.dimension === 'string') {\n bindingKey = legacyQuery.funnelBindingKey.dimension\n } else if (Array.isArray(legacyQuery.funnelBindingKey.dimension)) {\n bindingKey = legacyQuery.funnelBindingKey.dimension.map((m) => ({\n cube: m.cube,\n dimension: m.dimension,\n }))\n }\n }\n\n // Extract time dimension from first query\n let timeDimension: string = ''\n if (\n legacyQuery.queries.length > 0 &&\n legacyQuery.queries[0].timeDimensions?.length\n ) {\n timeDimension = legacyQuery.queries[0].timeDimensions[0].dimension\n }\n\n // Convert queries to funnel steps\n const steps: ServerFunnelStep[] = legacyQuery.queries.map((query, index) => {\n const step: ServerFunnelStep = {\n name:\n legacyQuery.queryLabels?.[index] ||\n `Step ${index + 1}`,\n }\n\n // Convert filters\n if (query.filters && query.filters.length > 0) {\n step.filter =\n query.filters.length === 1 ? query.filters[0] : { and: query.filters }\n }\n\n // Add time to convert if specified\n if (\n legacyQuery.stepTimeToConvert &&\n legacyQuery.stepTimeToConvert[index]\n ) {\n step.timeToConvert = legacyQuery.stepTimeToConvert[index] as string\n }\n\n return step\n })\n\n // Build chart config\n const chartConfig: ChartConfig = portlet\n ? extractChartConfig(portlet, 'funnel')\n : {\n chartType: 'funnel',\n chartConfig: {},\n displayConfig: {},\n }\n\n return {\n version: 1,\n analysisType: 'funnel',\n activeView: 'chart',\n charts: {\n funnel: chartConfig,\n },\n query: {\n funnel: {\n bindingKey,\n timeDimension,\n steps,\n includeTimeMetrics: true,\n },\n },\n }\n}\n\n/**\n * Migrate any config to the latest version\n *\n * Handles:\n * - Already valid AnalysisConfig (returns as-is)\n * - Legacy portlet format (converts to AnalysisConfig)\n * - Unknown format (returns default config)\n *\n * @param config - Unknown config value\n * @returns Valid AnalysisConfig\n */\nexport function migrateConfig(config: unknown): AnalysisConfig {\n // Already valid?\n if (isValidAnalysisConfig(config)) {\n return config\n }\n\n // Check if it looks like a legacy portlet\n if (\n config &&\n typeof config === 'object' &&\n 'query' in config &&\n typeof (config as { query: unknown }).query === 'string'\n ) {\n return migrateLegacyPortlet(config as LegacyPortlet)\n }\n\n // Check if it's a raw query object (not wrapped in portlet)\n if (config && typeof config === 'object') {\n try {\n // Try to wrap it as a portlet and migrate\n const wrapped: LegacyPortlet = {\n query: JSON.stringify(config),\n }\n return migrateLegacyPortlet(wrapped)\n } catch {\n // Fall through to default\n }\n }\n\n // Return default config\n console.warn('[configMigration] Unknown config format, using defaults')\n return createDefaultQueryConfig()\n}\n\n/**\n * Check if a portlet has the new AnalysisConfig format\n */\nexport function hasAnalysisConfig(\n portlet: unknown\n): portlet is { analysisConfig: AnalysisConfig } {\n return (\n typeof portlet === 'object' &&\n portlet !== null &&\n 'analysisConfig' in portlet &&\n isValidAnalysisConfig((portlet as { analysisConfig: unknown }).analysisConfig)\n )\n}\n\n/**\n * Ensure a portlet has analysisConfig, migrating from legacy format if needed.\n *\n * This is the primary entry point for rendering portlets - it guarantees\n * that analysisConfig exists, either by using the existing one or by\n * converting legacy fields on-the-fly.\n *\n * @param portlet - PortletConfig which may or may not have analysisConfig\n * @returns PortletConfig with analysisConfig guaranteed to exist\n */\nexport function ensureAnalysisConfig(\n portlet: PortletConfig\n): PortletConfig & { analysisConfig: AnalysisConfig } {\n // If already has valid analysisConfig, return as-is\n if (hasAnalysisConfig(portlet)) {\n return portlet as PortletConfig & { analysisConfig: AnalysisConfig }\n }\n\n // Migrate from legacy fields\n // Note: 'flow' and 'retention' types don't have legacy format - filter to only query/funnel\n const legacyAnalysisType = portlet.analysisType === 'flow' || portlet.analysisType === 'retention' ? undefined : portlet.analysisType\n const analysisConfig = migrateLegacyPortlet({\n query: portlet.query ?? '{}',\n chartType: portlet.chartType,\n chartConfig: portlet.chartConfig,\n displayConfig: portlet.displayConfig,\n analysisType: legacyAnalysisType,\n funnelChartType: portlet.funnelChartType,\n funnelChartConfig: portlet.funnelChartConfig,\n funnelDisplayConfig: portlet.funnelDisplayConfig,\n })\n\n return { ...portlet, analysisConfig }\n}\n","/**\n * Filter utility functions for dashboard-level filtering\n *\n * NOTE: These are pure functions without internal caching.\n * Memoization should be handled at the component level using useMemo.\n */\n\nimport type { Filter, DashboardFilter, CubeMeta, GroupFilter, DashboardConfig, SimpleFilter } from '../types'\nimport { ensureAnalysisConfig } from './configMigration'\n\n/**\n * Check if a filter should be included in the query (has valid values or doesn't require values)\n * @param filter - The filter to check\n * @returns true if the filter should be included, false otherwise\n */\nfunction shouldIncludeFilter(filter: Filter): boolean {\n // Handle SimpleFilter\n if ('member' in filter && 'operator' in filter) {\n const simpleFilter = filter as SimpleFilter\n\n // Operators that don't require values\n const noValueOperators = ['set', 'notSet', 'isEmpty', 'isNotEmpty']\n if (noValueOperators.includes(simpleFilter.operator)) {\n return true\n }\n\n // For inDateRange, check if dateRange is provided as alternative to values\n if (simpleFilter.operator === 'inDateRange' && simpleFilter.dateRange) {\n return true\n }\n\n // For other operators, check if values exist and are non-empty\n return !!(simpleFilter.values && simpleFilter.values.length > 0)\n }\n\n // Handle GroupFilter - recursively check nested filters\n if ('type' in filter && 'filters' in filter) {\n const groupFilter = filter as GroupFilter\n // Include group filter if at least one nested filter is valid\n const validFilters = groupFilter.filters.filter(f => shouldIncludeFilter(f))\n return validFilters.length > 0\n }\n\n return false\n}\n\n/**\n * Get dashboard filters that should be applied to a portlet based on its mapping configuration\n *\n * @param dashboardFilters - All available dashboard filters\n * @param filterMapping - Array of filter IDs that apply to this portlet\n * @returns Array of filters that should be applied to the portlet\n */\nexport function getApplicableDashboardFilters(\n dashboardFilters: DashboardFilter[] | undefined,\n filterMapping: string[] | undefined\n): Filter[] {\n if (!dashboardFilters || !dashboardFilters.length) {\n return []\n }\n\n // If no mapping is specified, no dashboard filters apply\n if (!filterMapping || !filterMapping.length) {\n return []\n }\n\n // Compute filters that are in the mapping AND have valid values\n return dashboardFilters\n .filter(df => filterMapping.includes(df.id))\n .filter(df => shouldIncludeFilter(df.filter))\n .map(df => df.filter)\n}\n\n/**\n * Convert GroupFilter format to server format\n * GroupFilter: { type: 'and', filters: [...] }\n * Server format: { and: [...] } or { or: [...] }\n */\nfunction convertToServerFormat(filter: Filter): any {\n // Handle GroupFilter format\n if ('type' in filter && 'filters' in filter) {\n const groupFilter = filter as GroupFilter\n const convertedFilters = groupFilter.filters.map(convertToServerFormat)\n\n if (groupFilter.type === 'and') {\n return { and: convertedFilters }\n } else {\n return { or: convertedFilters }\n }\n }\n\n // Simple filter - return as-is\n return filter\n}\n\n/**\n * Filter format for merge operation:\n * - 'server': Returns {and: [...]} or {or: [...]} format (for API queries)\n * - 'client': Returns {type: 'and', filters: [...]} format (for UI components)\n */\nexport type FilterFormat = 'server' | 'client'\n\n/**\n * Merge dashboard filters with portlet filters using AND logic\n * Dashboard filters are combined with portlet filters so both sets of filters apply\n *\n * @param dashboardFilters - Filters from dashboard-level configuration\n * @param portletFilters - Filters from portlet query\n * @param format - Output format: 'server' for API queries, 'client' for UI (default: 'server')\n * @returns Merged filter array with AND logic in the specified format\n */\nexport function mergeDashboardAndPortletFilters(\n dashboardFilters: Filter[],\n portletFilters: Filter[] | undefined,\n format: FilterFormat = 'server'\n): Filter[] | undefined {\n // If no dashboard filters, return portlet filters as-is\n if (!dashboardFilters || dashboardFilters.length === 0) {\n return portletFilters\n }\n\n // If no portlet filters, return dashboard filters\n if (!portletFilters || portletFilters.length === 0) {\n return [...dashboardFilters]\n }\n\n // Both exist - need to merge with AND logic\n if (format === 'server') {\n // Server format: convert to {and: [...]} structure\n const allFilters = [...dashboardFilters, ...portletFilters].map(convertToServerFormat)\n return [{\n and: allFilters\n } as any]\n } else {\n // Client format: use {type: 'and', filters: [...]} structure\n const allFilters = [...dashboardFilters, ...portletFilters]\n return [{\n type: 'and',\n filters: allFilters\n } as GroupFilter]\n }\n}\n\n/**\n * @deprecated Use mergeDashboardAndPortletFilters(filters, portletFilters, 'client') instead\n */\nexport function mergeDashboardAndPortletFiltersClientFormat(\n dashboardFilters: Filter[],\n portletFilters: Filter[] | undefined\n): Filter[] | undefined {\n return mergeDashboardAndPortletFilters(dashboardFilters, portletFilters, 'client')\n}\n\n/**\n * Check if a filter field exists in the cube metadata\n * This helps identify filters that might not apply to a specific portlet's data\n * @param filter - The filter to validate\n * @param cubeMeta - Cube metadata to validate against\n * @returns true if the filter field exists in any cube's measures or dimensions\n */\nexport function validateFilterForCube(\n filter: Filter,\n cubeMeta: CubeMeta | null\n): boolean {\n if (!cubeMeta || !cubeMeta.cubes) {\n // If no metadata available, assume filter is valid (fail open)\n return true\n }\n\n // Extract member names from filter recursively\n const memberNames = extractMemberNamesFromFilter(filter)\n\n // Check if any of the member names exist in cube metadata\n return memberNames.some(memberName => {\n return cubeMeta.cubes.some(cube => {\n // Check measures\n const inMeasures = cube.measures?.some(m => m.name === memberName) ?? false\n // Check dimensions\n const inDimensions = cube.dimensions?.some(d => d.name === memberName) ?? false\n\n return inMeasures || inDimensions\n })\n })\n}\n\n/**\n * Extract all member names from a filter (handles nested group filters)\n * @param filter - The filter to extract members from\n * @returns Array of member names\n */\nfunction extractMemberNamesFromFilter(filter: Filter): string[] {\n if ('member' in filter) {\n // SimpleFilter\n return [filter.member]\n } else if ('type' in filter && 'filters' in filter) {\n // GroupFilter - recursively extract from nested filters\n return filter.filters.flatMap(f => extractMemberNamesFromFilter(f))\n }\n\n return []\n}\n\n/**\n * Validate that all dashboard filters in a portlet's mapping exist and are valid\n * @param dashboardFilters - All available dashboard filters\n * @param filterMapping - The portlet's filter mapping\n * @param cubeMeta - Cube metadata for validation\n * @returns Object with validation result and list of invalid filter IDs\n */\nexport function validatePortletFilterMapping(\n dashboardFilters: DashboardFilter[] | undefined,\n filterMapping: string[] | undefined,\n cubeMeta: CubeMeta | null\n): { isValid: boolean; invalidFilterIds: string[]; missingFilterIds: string[] } {\n if (!filterMapping || !filterMapping.length) {\n return { isValid: true, invalidFilterIds: [], missingFilterIds: [] }\n }\n\n if (!dashboardFilters || !dashboardFilters.length) {\n // Mapping references filters that don't exist\n return {\n isValid: false,\n invalidFilterIds: [],\n missingFilterIds: filterMapping\n }\n }\n\n const invalidFilterIds: string[] = []\n const missingFilterIds: string[] = []\n\n filterMapping.forEach(filterId => {\n const dashboardFilter = dashboardFilters.find(df => df.id === filterId)\n\n if (!dashboardFilter) {\n // Filter ID in mapping doesn't exist in dashboard filters\n missingFilterIds.push(filterId)\n } else {\n // Check if filter is valid for the cube metadata\n const isValid = validateFilterForCube(dashboardFilter.filter, cubeMeta)\n if (!isValid) {\n invalidFilterIds.push(filterId)\n }\n }\n })\n\n return {\n isValid: invalidFilterIds.length === 0 && missingFilterIds.length === 0,\n invalidFilterIds,\n missingFilterIds\n }\n}\n\n/**\n * Extract all unique measures, dimensions, and timeDimensions used across all portlets in a dashboard\n * This helps create a filtered schema view showing only fields relevant to the dashboard\n * @param dashboardConfig - Dashboard configuration\n * @returns Object with unique measures, dimensions, and timeDimensions\n */\nexport function extractDashboardFields(\n dashboardConfig: DashboardConfig\n): { measures: Set<string>; dimensions: Set<string>; timeDimensions: Set<string> } {\n const measures = new Set<string>()\n const dimensions = new Set<string>()\n const timeDimensions = new Set<string>()\n\n // Iterate through all portlets\n dashboardConfig.portlets.forEach(portlet => {\n try {\n // Get query from analysisConfig (migrating legacy format if needed)\n const normalizedPortlet = ensureAnalysisConfig(portlet)\n const query = normalizedPortlet.analysisConfig.query\n\n // Helper to extract fields from a CubeQuery\n const extractFromCubeQuery = (cubeQuery: any) => {\n if (cubeQuery.measures && Array.isArray(cubeQuery.measures)) {\n cubeQuery.measures.forEach((measure: string) => measures.add(measure))\n }\n if (cubeQuery.dimensions && Array.isArray(cubeQuery.dimensions)) {\n cubeQuery.dimensions.forEach((dimension: string) => dimensions.add(dimension))\n }\n if (cubeQuery.timeDimensions && Array.isArray(cubeQuery.timeDimensions)) {\n cubeQuery.timeDimensions.forEach((td: any) => {\n if (td.dimension) {\n timeDimensions.add(td.dimension)\n }\n })\n }\n if (cubeQuery.filters) {\n extractFieldsFromFilters(cubeQuery.filters).forEach(field => {\n dimensions.add(field)\n })\n }\n }\n\n // Handle different query types\n if ('funnel' in query) {\n // ServerFunnelQuery - extract time dimension from funnel config\n const funnelQuery = query as any\n if (funnelQuery.funnel?.timeDimension) {\n timeDimensions.add(funnelQuery.funnel.timeDimension)\n }\n // Could also extract binding key dimensions if needed\n } else if ('queries' in query) {\n // MultiQueryConfig - extract from all sub-queries\n const multiQuery = query as any\n multiQuery.queries.forEach((subQuery: any) => extractFromCubeQuery(subQuery))\n } else {\n // Single CubeQuery\n extractFromCubeQuery(query)\n }\n } catch (e) {\n // Skip portlets with invalid query\n console.warn('Failed to extract fields from portlet:', portlet.id, e)\n }\n })\n\n return { measures, dimensions, timeDimensions }\n}\n\n/**\n * Extract field names from filters recursively\n * @param filters - Filter array\n * @returns Array of unique field names\n */\nfunction extractFieldsFromFilters(filters: Filter[]): string[] {\n const fields: string[] = []\n\n filters.forEach(filter => {\n if ('member' in filter) {\n // SimpleFilter\n fields.push(filter.member)\n } else if ('type' in filter && 'filters' in filter) {\n // GroupFilter - recurse\n fields.push(...extractFieldsFromFilters(filter.filters))\n }\n })\n\n return [...new Set(fields)] // Return unique fields\n}\n\n/**\n * Time dimension type from CubeQuery\n */\ntype TimeDimension = {\n dimension: string\n granularity?: string\n dateRange?: string[] | string\n}\n\n/**\n * Helper to get date range from a SimpleFilter (backward compatible)\n * Reads from both dateRange and values for compatibility\n * Handles both:\n * - Preset ranges: [\"this quarter\"], [\"last 7 days\"] (single string value)\n * - Custom ranges: [\"2024-01-01\", \"2024-12-31\"] (two date values)\n */\nfunction getDateRangeFromFilter(filter: SimpleFilter): string[] | string | undefined {\n // Prefer dateRange for backward compatibility, fall back to values\n if (filter.dateRange) {\n return filter.dateRange\n }\n if (filter.values && filter.values.length > 0) {\n // Single value = preset like \"this quarter\", return as string\n // Multiple values = custom date range, return as array\n return filter.values.length === 1 ? filter.values[0] : filter.values\n }\n return undefined\n}\n\n/**\n * Apply universal time filters to a portlet's timeDimensions\n * Universal time filters apply their dateRange to ALL time dimensions in the portlet\n *\n * @param dashboardFilters - All dashboard filters\n * @param filterMapping - Filter IDs that apply to this portlet\n * @param portletTimeDimensions - The portlet's existing timeDimensions array\n * @returns Updated timeDimensions array with date ranges applied\n */\nexport function applyUniversalTimeFilters(\n dashboardFilters: DashboardFilter[] | undefined,\n filterMapping: string[] | undefined,\n portletTimeDimensions: TimeDimension[] | undefined\n): TimeDimension[] | undefined {\n // Return as-is if no time dimensions in portlet (skip silently)\n if (!portletTimeDimensions || portletTimeDimensions.length === 0) {\n return portletTimeDimensions\n }\n\n // If no mapping specified, no filters apply\n if (!filterMapping || filterMapping.length === 0) {\n return portletTimeDimensions\n }\n\n // Find applicable universal time filters that have valid date ranges\n const universalTimeFilters = dashboardFilters\n ?.filter(df => df.isUniversalTime && filterMapping.includes(df.id))\n ?.filter(df => {\n // Must be a SimpleFilter with a valid dateRange\n if (!('member' in df.filter)) return false\n const simpleFilter = df.filter as SimpleFilter\n const dateRange = getDateRangeFromFilter(simpleFilter)\n return dateRange !== undefined\n })\n\n if (!universalTimeFilters || universalTimeFilters.length === 0) {\n return portletTimeDimensions\n }\n\n // Use the first universal time filter's dateRange (typically only one)\n const timeFilter = universalTimeFilters[0]\n const simpleFilter = timeFilter.filter as SimpleFilter\n const dateRange = getDateRangeFromFilter(simpleFilter)\n\n // Apply dateRange to ALL time dimensions (dashboard wins - overrides portlet dateRange)\n return portletTimeDimensions.map(td => ({\n ...td,\n dateRange: dateRange\n }))\n}\n","/**\n * Analytics Portlet Component\n * Simplified version with minimal dependencies\n */\n\nimport React, { useMemo, useCallback, forwardRef, useImperativeHandle, useEffect, useRef } from 'react'\nimport { useInView } from 'react-intersection-observer'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useCubeLoadQuery, useMultiCubeLoadQuery, useFunnelQuery, useFlowQuery, useRetentionQuery, createQueryKey, createMultiQueryKey } from '../hooks/queries'\nimport { useScrollContainer } from '../providers/ScrollContainerContext'\nimport ChartErrorBoundary from './ChartErrorBoundary'\nimport LoadingIndicator from './LoadingIndicator'\nimport { LazyChart, isValidChartType } from '../charts/ChartLoader'\nimport { useChartConfig } from '../charts/lazyChartConfigRegistry'\nimport type { AnalyticsPortletProps, MultiQueryConfig, ServerFunnelQuery, CubeQuery } from '../types'\nimport { isMultiQueryConfig, isServerFunnelQuery } from '../types'\nimport type { ServerFlowQuery } from '../types/flow'\nimport { isServerFlowQuery } from '../types/flow'\nimport type { ServerRetentionQuery } from '../types/retention'\nimport { isServerRetentionQuery } from '../types/retention'\nimport { getApplicableDashboardFilters, mergeDashboardAndPortletFilters, applyUniversalTimeFilters } from '../utils/filterUtils'\nimport { cleanQueryForServer } from '../shared/utils'\n\n\ninterface RefreshOptions {\n bustCache?: boolean\n}\n\ninterface AnalyticsPortletRef {\n refresh: (options?: RefreshOptions) => void\n}\n\n// Memoize component to prevent re-renders when props haven't changed\nconst AnalyticsPortlet = React.memo(forwardRef<AnalyticsPortletRef, AnalyticsPortletProps>(({\n query,\n chartType,\n chartConfig,\n displayConfig,\n dashboardFilters,\n dashboardFilterMapping,\n eagerLoad = false,\n isVisible: _isVisible, // Deprecated - visibility now handled internally via useInView\n height = 300,\n title: _title,\n colorPalette,\n loadingComponent,\n onDebugDataReady\n}, ref) => {\n const onDebugDataReadyRef = useRef(onDebugDataReady)\n\n // Lazy loading: Use IntersectionObserver to detect when portlet is visible\n // Get scroll container from context (null = viewport, element = container scroll)\n const scrollContainer = useScrollContainer()\n const { ref: inViewRef, inView } = useInView({\n root: scrollContainer,\n rootMargin: '500px', // Start loading 500px before entering viewport (about half screen)\n triggerOnce: true, // Once visible, stay \"visible\" (don't unload data)\n initialInView: false, // Start as not visible, let observer determine actual state\n skip: eagerLoad // Skip observation entirely if eagerLoad is true\n })\n\n // Effective visibility: eagerLoad forces visible, otherwise use inView from IntersectionObserver\n // Note: Batching is handled by BatchCoordinator which collects queries for 100ms before flushing\n const isVisible = eagerLoad || inView\n\n // Update ref when callback changes\n useEffect(() => {\n onDebugDataReadyRef.current = onDebugDataReady\n }, [onDebugDataReady])\n\n // Check if this chart type skips queries (using lazy-loaded config)\n const { config: chartTypeConfig } = useChartConfig(chartType)\n const shouldSkipQuery = chartTypeConfig.skipQuery === true\n\n // Memoize regular filters to prevent array recreation on every render\n const regularFilters = useMemo(() => {\n return dashboardFilters?.filter(df => !df.isUniversalTime)\n }, [dashboardFilters])\n\n // Parse query from JSON string, merge dashboard filters, and detect query type\n // Supports: CubeQuery, MultiQueryConfig, ServerFunnelQuery, ServerFlowQuery, and ServerRetentionQuery formats\n const { queryObject, multiQueryConfig, serverFunnelQuery, serverFlowQuery, serverRetentionQuery } = useMemo(() => {\n // Skip query parsing for charts that don't need queries\n if (shouldSkipQuery) {\n return { queryObject: null, multiQueryConfig: null, serverFunnelQuery: null, serverFlowQuery: null, serverRetentionQuery: null }\n }\n\n try {\n const parsed = JSON.parse(query)\n\n // Get applicable dashboard filters (excluding universal time filters - they apply to timeDimensions)\n const applicableFilters = getApplicableDashboardFilters(regularFilters, dashboardFilterMapping)\n\n // Check if this is a ServerRetentionQuery format { retention: {...} }\n if (isServerRetentionQuery(parsed)) {\n const retentionQuery = parsed as ServerRetentionQuery\n // Retention queries don't have dashboard filter merging yet (could be added later)\n return {\n queryObject: null,\n multiQueryConfig: null,\n serverFunnelQuery: null,\n serverFlowQuery: null,\n serverRetentionQuery: retentionQuery\n }\n }\n\n // Check if this is a ServerFlowQuery format { flow: {...} }\n if (isServerFlowQuery(parsed)) {\n const flowQuery = parsed as ServerFlowQuery\n // Flow queries don't have dashboard filter merging yet (could be added later)\n return {\n queryObject: null,\n multiQueryConfig: null,\n serverFunnelQuery: null,\n serverFlowQuery: flowQuery,\n serverRetentionQuery: null\n }\n }\n\n // Check if this is a ServerFunnelQuery format { funnel: {...} }\n if (isServerFunnelQuery(parsed)) {\n const funnelQuery = parsed as ServerFunnelQuery\n\n // Apply dashboard filters to funnel query:\n // 1. Regular filters → merge into step 0's filter\n // 2. Universal time filters → apply as inDateRange filter on step 0\n\n // Clone the funnel query to avoid mutating the original\n const modifiedFunnel = { ...funnelQuery, funnel: { ...funnelQuery.funnel, steps: [...funnelQuery.funnel.steps] } }\n\n // Get applicable dashboard filters (non-universal time)\n if (applicableFilters.length > 0 && modifiedFunnel.funnel.steps.length > 0) {\n // Clone step 0\n const step0 = { ...modifiedFunnel.funnel.steps[0] }\n\n // Merge dashboard filters with step 0's existing filter\n const existingFilters = step0.filter ? (Array.isArray(step0.filter) ? step0.filter : [step0.filter]) : []\n const mergedFilters = mergeDashboardAndPortletFilters(applicableFilters, existingFilters as any)\n\n step0.filter = mergedFilters\n modifiedFunnel.funnel.steps[0] = step0\n }\n\n // Apply universal time filters as inDateRange filter on step 0\n const universalTimeFilters = dashboardFilters?.filter(df =>\n df.isUniversalTime && dashboardFilterMapping?.includes(df.id)\n )\n if (universalTimeFilters && universalTimeFilters.length > 0 && modifiedFunnel.funnel.steps.length > 0) {\n const timeFilter = universalTimeFilters[0]\n if ('member' in timeFilter.filter) {\n const simpleFilter = timeFilter.filter as { member: string; operator: string; values?: string[]; dateRange?: string }\n\n // Get the date range value from either dateRange property or values[0]\n const dateRangeValue = simpleFilter.dateRange || (simpleFilter.values?.[0])\n\n if (dateRangeValue) {\n // Clone step 0 and add time filter\n const step0 = { ...modifiedFunnel.funnel.steps[0] }\n\n // Get the time dimension from the funnel config\n let timeDimMember: string | undefined\n if (typeof modifiedFunnel.funnel.timeDimension === 'string') {\n timeDimMember = modifiedFunnel.funnel.timeDimension\n } else if (Array.isArray(modifiedFunnel.funnel.timeDimension) && modifiedFunnel.funnel.timeDimension.length > 0) {\n const td = modifiedFunnel.funnel.timeDimension[0]\n timeDimMember = `${td.cube}.${td.dimension}`\n }\n\n if (timeDimMember) {\n // Create inDateRange filter for the time dimension\n // Format: { member, operator: 'inDateRange', values: [], dateRange: 'this month' }\n const timeRangeFilter = {\n member: timeDimMember,\n operator: 'inDateRange',\n values: [] as string[],\n dateRange: dateRangeValue\n }\n\n // Merge with existing filters on step 0\n const existingFilters = step0.filter ? (Array.isArray(step0.filter) ? step0.filter : [step0.filter]) : []\n step0.filter = [...existingFilters, timeRangeFilter]\n modifiedFunnel.funnel.steps[0] = step0\n }\n }\n }\n }\n\n return { queryObject: null, multiQueryConfig: null, serverFunnelQuery: modifiedFunnel, serverFlowQuery: null, serverRetentionQuery: null }\n }\n\n // Check if this is a multi-query configuration\n if (isMultiQueryConfig(parsed)) {\n // Multi-query: apply filters to each query in the array\n const multiConfig: MultiQueryConfig = {\n ...parsed,\n queries: parsed.queries.map(q => ({\n ...q,\n filters: mergeDashboardAndPortletFilters(applicableFilters, q.filters),\n timeDimensions: applyUniversalTimeFilters(dashboardFilters, dashboardFilterMapping, q.timeDimensions)\n }))\n }\n return { queryObject: null, multiQueryConfig: multiConfig, serverFunnelQuery: null, serverFlowQuery: null, serverRetentionQuery: null }\n }\n\n // Single query: existing behavior\n const mergedFilters = mergeDashboardAndPortletFilters(applicableFilters, parsed.filters)\n const mergedTimeDimensions = applyUniversalTimeFilters(\n dashboardFilters,\n dashboardFilterMapping,\n parsed.timeDimensions\n )\n\n return {\n queryObject: {\n ...parsed,\n filters: mergedFilters,\n timeDimensions: mergedTimeDimensions\n },\n multiQueryConfig: null,\n serverFunnelQuery: null,\n serverFlowQuery: null,\n serverRetentionQuery: null\n }\n } catch (e) {\n console.error('AnalyticsPortlet: Invalid query JSON:', e)\n return { queryObject: null, multiQueryConfig: null, serverFunnelQuery: null, serverFlowQuery: null, serverRetentionQuery: null }\n }\n }, [query, shouldSkipQuery, regularFilters, dashboardFilters, dashboardFilterMapping])\n\n // Determine whether to skip queries based on various conditions\n const isMultiQuery = multiQueryConfig !== null\n // Funnel mode: ServerFunnelQuery format (dedicated funnel mode)\n // Note: Legacy mergeStrategy === 'funnel' is no longer supported\n const isFunnelMode = serverFunnelQuery !== null\n // Flow mode: ServerFlowQuery format (dedicated flow mode for Sankey charts)\n const isFlowMode = serverFlowQuery !== null\n // Retention mode: ServerRetentionQuery format (cohort retention analysis)\n const isRetentionMode = serverRetentionQuery !== null\n const shouldSkipSingle = !queryObject || shouldSkipQuery || (!eagerLoad && !isVisible) || isMultiQuery || isFunnelMode || isFlowMode || isRetentionMode\n const shouldSkipMulti = !multiQueryConfig || shouldSkipQuery || (!eagerLoad && !isVisible) || isFunnelMode || isFlowMode || isRetentionMode\n const shouldSkipFunnel = !isFunnelMode || shouldSkipQuery || (!eagerLoad && !isVisible)\n const shouldSkipFlow = !isFlowMode || shouldSkipQuery || (!eagerLoad && !isVisible)\n const shouldSkipRetention = !isRetentionMode || shouldSkipQuery || (!eagerLoad && !isVisible)\n\n // Query client for cache invalidation\n const queryClient = useQueryClient()\n\n // Note: Legacy funnel config via mergeStrategy === 'funnel' is no longer supported\n // Funnel mode now uses ServerFunnelQuery format exclusively\n const funnelConfig = null\n\n // Use single query hook with TanStack Query (skip if multi-query or other skip conditions)\n const singleQueryResult = useCubeLoadQuery(queryObject, {\n skip: shouldSkipSingle,\n resetResultSetOnChange: true,\n debounceMs: 100, // Lower debounce for portlets (faster response)\n })\n\n // Use multi-query hook with TanStack Query (skip if single query, funnel mode, or other skip conditions)\n const multiQueryResult = useMultiCubeLoadQuery(multiQueryConfig, {\n skip: shouldSkipMulti,\n resetResultSetOnChange: true,\n debounceMs: 100, // Lower debounce for portlets (faster response)\n })\n\n // Use funnel query hook (supports both legacy funnelConfig and new serverFunnelQuery)\n const funnelQueryResult = useFunnelQuery(funnelConfig, {\n skip: shouldSkipFunnel || (!funnelConfig && !serverFunnelQuery),\n debounceMs: 100,\n // Pass prebuilt ServerFunnelQuery directly (new dedicated funnel mode)\n prebuiltServerQuery: serverFunnelQuery,\n })\n\n // Use flow query hook for Sankey chart data\n const flowQueryResult = useFlowQuery(serverFlowQuery, {\n skip: shouldSkipFlow,\n debounceMs: 100,\n })\n\n // Use retention query hook for cohort retention data\n const retentionQueryResult = useRetentionQuery(serverRetentionQuery, {\n skip: shouldSkipRetention,\n debounceMs: 100,\n })\n\n // Combine results from all hooks\n const resultSet = isMultiQuery ? null : singleQueryResult.resultSet\n const isLoading = isRetentionMode\n ? retentionQueryResult.isLoading || retentionQueryResult.isDebouncing\n : isFlowMode\n ? flowQueryResult.isLoading || flowQueryResult.isDebouncing\n : isFunnelMode\n ? funnelQueryResult.isExecuting || funnelQueryResult.isDebouncing\n : isMultiQuery\n ? multiQueryResult.isLoading\n : singleQueryResult.isLoading\n const isFetching = isRetentionMode\n ? retentionQueryResult.isFetching\n : isFlowMode\n ? flowQueryResult.isFetching\n : isFunnelMode\n ? funnelQueryResult.isExecuting\n : isMultiQuery\n ? multiQueryResult.isFetching\n : singleQueryResult.isFetching\n const error = isRetentionMode\n ? retentionQueryResult.error\n : isFlowMode\n ? flowQueryResult.error\n : isFunnelMode\n ? funnelQueryResult.error\n : isMultiQuery\n ? multiQueryResult.error\n : singleQueryResult.error\n const multiQueryData = isRetentionMode\n ? null // Retention returns data in retentionQueryResult.chartData (RetentionChartData structure)\n : isFlowMode\n ? null // Flow returns data in flowQueryResult.data (nodes/links structure)\n : isFunnelMode\n ? (funnelQueryResult.chartData as unknown[] | null)\n : isMultiQuery\n ? multiQueryResult.data\n : null\n // Flow data is separate since it has a different structure (nodes/links vs array)\n const flowChartData = isFlowMode ? flowQueryResult.data : null\n // Retention data is separate since it has a different structure (rows/periods vs array)\n const retentionChartData = isRetentionMode ? retentionQueryResult.chartData : null\n\n // Expose refresh function through ref\n // Invalidates cache and forces a fresh fetch from the server\n // Pass bustCache: true to bypass both client and server caches\n useImperativeHandle(ref, () => ({\n refresh: (options?: RefreshOptions) => {\n const bustCache = options?.bustCache ?? false\n\n if (isRetentionMode && serverRetentionQuery) {\n // For retention mode, invalidate cache first then re-execute\n // Retention query key format: ['cube', 'retention', JSON.stringify(serverQuery)]\n const queryKey = ['cube', 'retention', JSON.stringify(serverRetentionQuery)] as const\n if (bustCache) {\n queryClient.removeQueries({ queryKey })\n } else {\n queryClient.invalidateQueries({ queryKey })\n }\n retentionQueryResult.refetch()\n } else if (isFlowMode && serverFlowQuery) {\n // For flow mode, invalidate cache first then re-execute\n // Flow query key format: ['cube', 'flow', JSON.stringify(serverQuery)]\n const queryKey = ['cube', 'flow', JSON.stringify(serverFlowQuery)] as const\n if (bustCache) {\n queryClient.removeQueries({ queryKey })\n } else {\n queryClient.invalidateQueries({ queryKey })\n }\n flowQueryResult.refetch({ bustCache })\n } else if (isFunnelMode && serverFunnelQuery) {\n // For funnel mode, invalidate cache first then re-execute\n // Funnel query key format: ['cube', 'funnel', stepCount, JSON.stringify(serverQuery)]\n const stepCount = serverFunnelQuery.funnel?.steps?.length || 0\n const queryKey = ['cube', 'funnel', stepCount, JSON.stringify(serverFunnelQuery)] as const\n if (bustCache) {\n queryClient.removeQueries({ queryKey })\n } else {\n queryClient.invalidateQueries({ queryKey })\n }\n funnelQueryResult.execute({ bustCache })\n } else if (isMultiQuery && multiQueryConfig) {\n // Invalidate cache for this specific multi-query, then refetch\n // Clean each query to match the cache key format used by useMultiCubeLoadQuery\n const cleanedConfig = {\n ...multiQueryConfig,\n queries: multiQueryConfig.queries.map((q: CubeQuery) => cleanQueryForServer(q))\n }\n if (bustCache) {\n queryClient.removeQueries({ queryKey: createMultiQueryKey(cleanedConfig) })\n } else {\n queryClient.invalidateQueries({ queryKey: createMultiQueryKey(cleanedConfig) })\n }\n multiQueryResult.refetch({ bustCache })\n } else if (queryObject) {\n // Clean the query to match the cache key format used by useCubeLoadQuery\n // This is important because the cache uses cleanQueryForServer(query) which removes empty arrays\n const cleanedQuery = cleanQueryForServer(queryObject)\n if (bustCache) {\n queryClient.removeQueries({ queryKey: createQueryKey(cleanedQuery) })\n } else {\n queryClient.invalidateQueries({ queryKey: createQueryKey(cleanedQuery) })\n }\n singleQueryResult.refetch({ bustCache })\n }\n }\n }), [isRetentionMode, isFlowMode, isFunnelMode, isMultiQuery, multiQueryConfig, queryObject, queryClient, serverRetentionQuery, serverFlowQuery, serverFunnelQuery, retentionQueryResult, flowQueryResult, funnelQueryResult, multiQueryResult, singleQueryResult])\n\n const handleRetry = useCallback(() => {\n if (isRetentionMode) {\n retentionQueryResult.refetch()\n } else if (isFlowMode) {\n flowQueryResult.refetch()\n } else if (isFunnelMode) {\n funnelQueryResult.execute()\n } else if (isMultiQuery) {\n multiQueryResult.refetch()\n } else {\n singleQueryResult.refetch()\n }\n }, [isRetentionMode, isFlowMode, isFunnelMode, isMultiQuery, retentionQueryResult, flowQueryResult, funnelQueryResult, multiQueryResult, singleQueryResult])\n\n\n // Send debug data to parent when ready (must be before any returns)\n useEffect(() => {\n if (!onDebugDataReadyRef.current || error) return\n\n // Handle funnel mode\n if (isFunnelMode && multiQueryData && multiQueryData.length > 0) {\n onDebugDataReadyRef.current({\n chartConfig: chartConfig || {},\n displayConfig: displayConfig || {},\n queryObject: serverFunnelQuery as unknown as Record<string, unknown>,\n data: multiQueryData,\n chartType,\n cacheInfo: funnelQueryResult.cacheInfo ?? undefined\n })\n return\n }\n\n // Handle flow mode\n if (isFlowMode && serverFlowQuery && flowChartData) {\n onDebugDataReadyRef.current({\n chartConfig: chartConfig || {},\n displayConfig: displayConfig || {},\n queryObject: serverFlowQuery as unknown as Record<string, unknown>,\n data: flowChartData,\n chartType,\n cacheInfo: flowQueryResult.cacheInfo,\n })\n return\n }\n\n // Handle retention mode\n if (isRetentionMode && serverRetentionQuery && retentionChartData) {\n onDebugDataReadyRef.current({\n chartConfig: chartConfig || {},\n displayConfig: displayConfig || {},\n queryObject: serverRetentionQuery as unknown as Record<string, unknown>,\n data: retentionChartData,\n chartType,\n cacheInfo: retentionQueryResult.cacheInfo ?? undefined,\n })\n return\n }\n\n // Handle single query mode\n if (chartConfig && queryObject && resultSet) {\n const getData = () => {\n switch (chartType) {\n case 'pie':\n case 'table':\n return resultSet.tablePivot()\n default:\n return resultSet.rawData()\n }\n }\n const data = getData()\n\n if (data) {\n onDebugDataReadyRef.current({\n chartConfig: chartConfig || {},\n displayConfig: displayConfig || {},\n queryObject,\n data,\n chartType,\n cacheInfo: resultSet.cacheInfo?.()\n })\n }\n }\n }, [chartConfig, displayConfig, queryObject, resultSet, chartType, error, isFunnelMode, isFlowMode, isRetentionMode, multiQueryData, serverFunnelQuery, serverFlowQuery, serverRetentionQuery, flowChartData, retentionChartData, flowQueryResult.cacheInfo, funnelQueryResult.cacheInfo, retentionQueryResult.cacheInfo]) // Use ref for callback to prevent infinite loops\n\n // Validate that chartConfig is provided when required (not required for skipQuery charts)\n // Check if any dropZones are mandatory for this chart type\n const hasMandatoryFields = !shouldSkipQuery && chartTypeConfig.dropZones.some(zone => zone.mandatory === true)\n \n if (!chartConfig && hasMandatoryFields) {\n return (\n <div ref={inViewRef} className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Required</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">Please configure this chart</div>\n </div>\n </div>\n )\n }\n\n // Show placeholder for lazy-loaded portlets that aren't visible yet\n if (!shouldSkipQuery && !eagerLoad && !isVisible) {\n return (\n <div ref={inViewRef} className=\"dc:w-full dc:h-full\" style={{ height }}>\n <div className=\"dc:w-full dc:h-full dc:animate-pulse bg-dc-surface-secondary dc:rounded\" style={{ minHeight: '100px' }} />\n </div>\n )\n }\n\n // Skip loading and error handling for charts that don't need queries\n if (!shouldSkipQuery) {\n // Show loading indicator during initial load OR during refresh (isFetching)\n if (isLoading || isFetching || (queryObject && !resultSet && !error)) {\n return (\n <div ref={inViewRef} className=\"dc:flex dc:items-center dc:justify-center dc:w-full\" style={{ height }}>\n {loadingComponent || <LoadingIndicator size=\"md\" />}\n </div>\n )\n }\n\n if (error) {\n return (\n <div ref={inViewRef} className=\"dc:p-4 dc:border dc:rounded-sm\" style={{ height, borderColor: 'var(--dc-border)', backgroundColor: 'var(--dc-surface)' }}>\n <div className=\"dc:mb-2\">\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:font-medium dc:text-sm\" style={{ color: 'var(--dc-text)' }}>⚠️ Query Error</span>\n <button\n onClick={handleRetry}\n className=\"dc:px-2 dc:py-1 text-white dc:rounded-sm dc:text-xs\"\n style={{ backgroundColor: 'var(--dc-primary)' }}\n >\n Retry\n </button>\n </div>\n </div>\n\n <div className=\"dc:mb-3\">\n <div className=\"dc:text-xs dc:p-2 dc:rounded-sm dc:border\" style={{ color: 'var(--dc-text-secondary)', backgroundColor: 'var(--dc-surface)', borderColor: 'var(--dc-border)' }}>\n {error.message || error.toString()}\n </div>\n </div>\n\n <div className=\"dc:space-y-2 dc:text-xs\">\n <details>\n <summary className=\"dc:cursor-pointer dc:font-medium\" style={{ color: 'var(--dc-text-secondary)' }}>Query (with filters applied)</summary>\n <pre className=\"dc:mt-1 dc:p-2 dc:rounded-sm dc:text-xs dc:overflow-auto dc:max-h-20\" style={{ backgroundColor: 'rgba(var(--dc-primary-rgb), 0.1)' }}>\n {queryObject ? JSON.stringify(queryObject, null, 2) : query}\n </pre>\n </details>\n\n <details>\n <summary className=\"dc:cursor-pointer dc:font-medium\" style={{ color: 'var(--dc-text-secondary)' }}>Chart Config</summary>\n <pre className=\"dc:mt-1 dc:p-2 dc:rounded-sm dc:text-xs dc:overflow-auto dc:max-h-20\" style={{ backgroundColor: 'rgba(var(--dc-primary-rgb), 0.05)' }}>\n {JSON.stringify({\n chartType,\n chartConfig,\n displayConfig: displayConfig\n }, null, 2)}\n </pre>\n </details>\n </div>\n </div>\n )\n }\n\n // Check for valid data based on query type\n // Retention mode uses retentionChartData from retentionQueryResult.chartData\n // Flow mode uses flowChartData from flowQueryResult.data\n // Funnel mode uses multiQueryData from funnelQueryResult.chartData\n // Multi-query mode uses multiQueryData from multiQueryResult.data\n // Single query mode uses resultSet from singleQueryResult\n const hasValidData = isRetentionMode\n ? (retentionChartData !== null && serverRetentionQuery !== null)\n : isFlowMode\n ? (flowChartData !== null && serverFlowQuery !== null)\n : isFunnelMode\n ? (multiQueryData !== null && (funnelConfig !== null || serverFunnelQuery !== null))\n : isMultiQuery\n ? (multiQueryData !== null && multiQueryConfig !== null)\n : (resultSet !== null && queryObject !== null)\n\n if (!hasValidData) {\n return (\n <div ref={inViewRef} className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs\">Invalid query or no results</div>\n </div>\n </div>\n )\n }\n }\n\n // Get data based on chart type needs\n // Returns array data for most charts, FlowChartData for Sankey, or RetentionChartData for retention charts\n // Note: FlowChartData and RetentionChartData are cast to any[] for ChartProps compatibility - specific charts handle internally\n const getData = (): unknown => {\n // Return empty array for charts that don't use query data\n if (shouldSkipQuery) {\n return []\n }\n\n // Retention mode: return retentionChartData (rows/periods structure) from retentionQueryResult\n // Retention charts expect { rows: [], periods: [] } structure\n if (isRetentionMode) {\n return retentionChartData || { rows: [], periods: [] }\n }\n\n // Flow mode: return flowChartData (nodes/links structure) from flowQueryResult\n // Sankey chart expects { nodes: [], links: [] } structure\n if (isFlowMode) {\n return flowChartData || { nodes: [], links: [] }\n }\n\n // Funnel mode: return chartData from funnelQueryResult\n if (isFunnelMode) {\n return multiQueryData || []\n }\n\n // Multi-query: return merged data directly\n if (isMultiQuery) {\n return multiQueryData || []\n }\n\n // Single query: return empty array if no resultSet\n if (!resultSet) {\n return []\n }\n\n switch (chartType) {\n case 'pie':\n case 'table':\n return resultSet.tablePivot()\n default:\n return resultSet.rawData()\n }\n }\n\n // Cast to any[] for ChartProps - specific charts (like Sankey) handle their own data format\n const data = getData() as unknown as unknown[]\n\n // Render appropriate chart component using lazy loading\n // Each chart type is dynamically imported for code splitting\n const renderChart = () => {\n try {\n const chartHeight = height\n\n // Determine effective chart type (handles sankey/sunburst toggle)\n const effectiveChartType = chartType === 'sankey' &&\n (displayConfig as Record<string, unknown>)?.flowVisualization === 'sunburst'\n ? 'sunburst'\n : chartType\n\n // Handle unsupported chart types\n if (!isValidChartType(effectiveChartType)) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full\" style={{ height }}>\n <div className=\"text-center text-dc-text-muted\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Unsupported chart type</div>\n <div className=\"dc:text-xs\">{effectiveChartType}</div>\n </div>\n </div>\n )\n }\n\n // For markdown chart, use empty data array\n const chartData = effectiveChartType === 'markdown' ? [] : data\n\n return (\n <LazyChart\n chartType={effectiveChartType}\n data={chartData}\n chartConfig={chartConfig}\n displayConfig={displayConfig}\n queryObject={queryObject ?? undefined}\n height={chartHeight}\n colorPalette={colorPalette}\n fallback={\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full\"\n style={{ height: typeof chartHeight === 'number' ? `${chartHeight}px` : chartHeight }}\n >\n <div className=\"dc:animate-pulse bg-dc-surface-secondary dc:rounded dc:w-full dc:h-full dc:min-h-[100px]\" />\n </div>\n }\n />\n )\n } catch (error) {\n console.error('Chart rendering error:', error)\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted dc:p-4\" style={{ height }}>\n <div className=\"text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Unable to render chart</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">{error instanceof Error ? error.message : 'Unknown error'}</div>\n </div>\n </div>\n )\n }\n }\n\n return (\n <div ref={inViewRef} className=\"dc:w-full dc:h-full\">\n <ChartErrorBoundary\n portletTitle={_title}\n portletConfig={{\n chartType,\n chartConfig,\n displayConfig,\n height\n }}\n cubeQuery={query}\n >\n <div className=\"dc:w-full dc:h-full dc:flex dc:flex-col dc:flex-1\" style={{ minHeight: '200px' }}>\n {renderChart()}\n </div>\n </ChartErrorBoundary>\n </div>\n )\n}))\n\nAnalyticsPortlet.displayName = 'AnalyticsPortlet'\n\nexport default AnalyticsPortlet\n","/**\n * useScrollDetection - Debounced Scroll Detection Hook\n *\n * Detects when a container has been scrolled past a threshold with debouncing\n * to prevent excessive state updates.\n *\n * This fixes the issue where scroll detection was listening to window.pageYOffset\n * instead of the actual scroll container (overflow-y-auto div in Layout).\n */\n\nimport { useEffect, useState, useRef, type RefObject } from 'react'\n\ninterface UseScrollDetectionOptions {\n /** Scroll threshold in pixels (default: 20) */\n threshold?: number\n /** Debounce delay in milliseconds (default: 150) */\n debounceMs?: number\n /** Optional container state to trigger re-initialization when found */\n container?: HTMLElement | null\n}\n\n/**\n * Hook to detect scroll position in a container\n *\n * @param containerRef - Ref to the scrollable container element\n * @param options - Configuration options for threshold and debounce\n * @returns Boolean indicating if scrolled past threshold\n *\n * @example\n * const scrollContainerRef = useRef<HTMLDivElement>(null)\n * const isScrolled = useScrollDetection(scrollContainerRef, {\n * threshold: 20,\n * debounceMs: 150\n * })\n *\n * <div ref={scrollContainerRef} className=\"overflow-y-auto\">\n * {isScrolled && <div>Shadow visible</div>}\n * </div>\n */\nexport function useScrollDetection(\n containerRef: RefObject<HTMLElement | null>,\n { threshold = 20, debounceMs = 150, container }: UseScrollDetectionOptions = {}\n) {\n const [isScrolled, setIsScrolled] = useState(false)\n const timeoutRef = useRef<number>()\n\n useEffect(() => {\n // Note: containerRef is intentionally not in deps - refs are stable and we access .current inside\n const scrollContainer = containerRef.current\n if (!scrollContainer) return\n\n const handleScroll = () => {\n // Clear existing timeout\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n\n // Debounce scroll updates\n timeoutRef.current = window.setTimeout(() => {\n const scrollTop = scrollContainer.scrollTop\n const shouldBeScrolled = scrollTop > threshold\n\n // Only update state if value actually changed\n setIsScrolled(prev => prev !== shouldBeScrolled ? shouldBeScrolled : prev)\n }, debounceMs)\n }\n\n // Attach scroll listener to actual container (not window!)\n scrollContainer.addEventListener('scroll', handleScroll, { passive: true })\n\n // Initial check\n handleScroll()\n\n // Cleanup\n return () => {\n scrollContainer.removeEventListener('scroll', handleScroll)\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [threshold, debounceMs, container]) // containerRef is stable, container triggers re-init\n\n return isScrolled\n}\n","/**\n * useElementVisibility - Detects when an element scrolls out of view\n *\n * Used to detect when the static edit bar scrolls out of view, triggering\n * the floating toolbar to appear. Works with both viewport and custom\n * scroll containers.\n */\n\nimport { useEffect, useState, useRef, type RefObject } from 'react'\n\ninterface UseElementVisibilityOptions {\n /** Threshold in pixels - element considered out of view when this much scrolls past top */\n threshold?: number\n /** Debounce delay in milliseconds */\n debounceMs?: number\n /** Custom scroll container ref (uses viewport if not provided) */\n containerRef?: RefObject<HTMLElement | null>\n /** Optional state value to trigger re-initialization when container is found */\n container?: HTMLElement | null\n}\n\n/**\n * Hook to detect whether an element is visible in the viewport/container\n *\n * @param elementRef - Ref to the element to track\n * @param options - Configuration options\n * @returns Boolean indicating if the element is visible (true when in view, false when scrolled out)\n *\n * @example\n * const editBarRef = useRef<HTMLDivElement>(null)\n * const isEditBarVisible = useElementVisibility(editBarRef, {\n * threshold: 80,\n * containerRef: scrollContainerRef\n * })\n *\n * // Show floating toolbar when edit bar scrolls out of view\n * {!isEditBarVisible && <FloatingToolbar />}\n */\nexport function useElementVisibility(\n elementRef: RefObject<HTMLElement | null>,\n { threshold = 80, debounceMs = 100, containerRef, container }: UseElementVisibilityOptions = {}\n): boolean {\n // Start with visible=true to prevent flash on initial render\n const [isVisible, setIsVisible] = useState(true)\n const timeoutRef = useRef<number>()\n // Track if we've ever seen the element visible (prevents animation on load)\n const hasBeenVisibleRef = useRef(false)\n\n useEffect(() => {\n const container = containerRef?.current\n\n const checkVisibility = () => {\n const element = elementRef.current\n // If element not yet mounted, stay visible (don't show floating toolbar)\n if (!element) return\n\n // Clear existing timeout for debouncing\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n\n timeoutRef.current = window.setTimeout(() => {\n const elementRect = element.getBoundingClientRect()\n\n if (container) {\n // Check against scroll container\n const containerRect = container.getBoundingClientRect()\n // Element is \"visible\" when its bottom is below the container's top by at least threshold\n const visible = elementRect.bottom > containerRect.top + threshold\n\n // Track that we've seen the element visible at least once\n if (visible) {\n hasBeenVisibleRef.current = true\n }\n\n // Only update state if value changed\n setIsVisible(prev => prev !== visible ? visible : prev)\n } else {\n // Check against viewport (window)\n // Element is \"visible\" when its bottom is in the viewport (plus threshold buffer)\n const visible = elementRect.bottom > threshold\n\n // Track that we've seen the element visible at least once\n if (visible) {\n hasBeenVisibleRef.current = true\n }\n\n // Only update state if value changed\n setIsVisible(prev => prev !== visible ? visible : prev)\n }\n }, debounceMs)\n }\n\n // Attach scroll listener to container or window\n const scrollTarget = container || window\n scrollTarget.addEventListener('scroll', checkVisibility, { passive: true })\n\n // Also listen for resize events\n window.addEventListener('resize', checkVisibility, { passive: true })\n\n // Initial check\n checkVisibility()\n\n // Deferred re-check after React render cycle completes\n // This handles the case where elementRef.current isn't set yet on first render\n const rafId = requestAnimationFrame(() => {\n checkVisibility()\n })\n\n // Cleanup\n return () => {\n scrollTarget.removeEventListener('scroll', checkVisibility)\n window.removeEventListener('resize', checkVisibility)\n cancelAnimationFrame(rafId)\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n }\n }, [elementRef, containerRef, threshold, debounceMs, container])\n\n return isVisible\n}\n","import { useEffect, useRef, useCallback, type RefObject } from 'react'\n\ninterface UseDragAutoScrollOptions {\n /** Distance from edge that triggers scrolling (default: 80px) */\n edgeThreshold?: number\n /** Maximum scroll speed in pixels per frame (default: 15) */\n maxScrollSpeed?: number\n /** Whether auto-scroll is currently enabled */\n enabled?: boolean\n}\n\n/**\n * Hook to enable auto-scrolling when dragging near the edges of a scroll container.\n * Works with HTML5 native drag-and-drop API by listening to dragover events.\n *\n * @param scrollContainerRef - Ref to the scrollable container element\n * @param options - Configuration options\n */\nexport function useDragAutoScroll(\n scrollContainerRef: RefObject<HTMLElement | null>,\n options: UseDragAutoScrollOptions = {}\n) {\n const {\n edgeThreshold = 80,\n maxScrollSpeed = 15,\n enabled = true\n } = options\n\n const animationFrameRef = useRef<number | null>(null)\n const scrollDirectionRef = useRef<'up' | 'down' | null>(null)\n const scrollIntensityRef = useRef<number>(0)\n\n // Calculate scroll speed based on proximity to edge\n const calculateScrollSpeed = useCallback((distanceFromEdge: number): number => {\n // Closer to edge = faster scrolling (exponential curve for smoother feel)\n const normalizedDistance = Math.max(0, Math.min(1, 1 - distanceFromEdge / edgeThreshold))\n return Math.round(normalizedDistance * normalizedDistance * maxScrollSpeed)\n }, [edgeThreshold, maxScrollSpeed])\n\n // Animation frame loop for smooth scrolling\n const scrollLoop = useCallback(() => {\n const container = scrollContainerRef.current\n if (!container || !scrollDirectionRef.current) {\n animationFrameRef.current = null\n return\n }\n\n const speed = scrollIntensityRef.current\n if (speed > 0) {\n const scrollAmount = scrollDirectionRef.current === 'up' ? -speed : speed\n container.scrollTop += scrollAmount\n }\n\n // Continue the loop while dragging\n animationFrameRef.current = requestAnimationFrame(scrollLoop)\n }, [scrollContainerRef])\n\n // Start the scroll animation\n const startScrolling = useCallback((direction: 'up' | 'down', intensity: number) => {\n scrollDirectionRef.current = direction\n scrollIntensityRef.current = intensity\n\n if (animationFrameRef.current === null) {\n animationFrameRef.current = requestAnimationFrame(scrollLoop)\n }\n }, [scrollLoop])\n\n // Stop the scroll animation\n const stopScrolling = useCallback(() => {\n scrollDirectionRef.current = null\n scrollIntensityRef.current = 0\n\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current)\n animationFrameRef.current = null\n }\n }, [])\n\n // Handle dragover events\n const handleDragOver = useCallback((event: DragEvent) => {\n const container = scrollContainerRef.current\n if (!container) return\n\n // Get container bounds\n const containerRect = container.getBoundingClientRect()\n const mouseY = event.clientY\n\n // Check if mouse is within the container's horizontal bounds\n if (event.clientX < containerRect.left || event.clientX > containerRect.right) {\n stopScrolling()\n return\n }\n\n // Calculate distance from edges\n const distanceFromTop = mouseY - containerRect.top\n const distanceFromBottom = containerRect.bottom - mouseY\n\n // Check if we should scroll\n if (distanceFromTop < edgeThreshold && container.scrollTop > 0) {\n // Near top edge - scroll up\n const speed = calculateScrollSpeed(distanceFromTop)\n startScrolling('up', speed)\n } else if (distanceFromBottom < edgeThreshold && container.scrollTop < container.scrollHeight - container.clientHeight) {\n // Near bottom edge - scroll down\n const speed = calculateScrollSpeed(distanceFromBottom)\n startScrolling('down', speed)\n } else {\n // Not near any edge - stop scrolling\n stopScrolling()\n }\n }, [scrollContainerRef, edgeThreshold, calculateScrollSpeed, startScrolling, stopScrolling])\n\n // Handle drag end - stop all scrolling\n const handleDragEnd = useCallback(() => {\n stopScrolling()\n }, [stopScrolling])\n\n // Setup event listeners\n useEffect(() => {\n if (!enabled) {\n stopScrolling()\n return\n }\n\n // Use capture phase to catch drag events before they're handled by other elements\n document.addEventListener('dragover', handleDragOver, { capture: true })\n document.addEventListener('dragend', handleDragEnd)\n document.addEventListener('drop', handleDragEnd)\n\n return () => {\n document.removeEventListener('dragover', handleDragOver, { capture: true })\n document.removeEventListener('dragend', handleDragEnd)\n document.removeEventListener('drop', handleDragEnd)\n stopScrolling()\n }\n }, [enabled, handleDragOver, handleDragEnd, stopScrolling])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current)\n }\n }\n }, [])\n}\n","/**\n * Syntax Highlighting Utility\n *\n * Lazy-loads highlight.js only when needed for syntax highlighting in debug panels.\n * This minimizes bundle size impact since syntax highlighting is not needed on initial load.\n *\n * Usage:\n * await highlightCodeBlocks()\n */\n\nlet highlightJs: any = null\nlet loadingPromise: Promise<void> | null = null\n\n/**\n * Lazy-loads highlight.js and registers supported languages.\n * Only loads once - subsequent calls return immediately.\n */\nexport async function loadSyntaxHighlighter(): Promise<void> {\n // Already loaded\n if (highlightJs) {\n return\n }\n\n // Loading in progress - wait for it\n if (loadingPromise) {\n return loadingPromise\n }\n\n loadingPromise = (async () => {\n try {\n // Dynamic imports to enable code splitting\n const hljs = await import('highlight.js/lib/core')\n const javascript = await import('highlight.js/lib/languages/javascript')\n const sql = await import('highlight.js/lib/languages/sql')\n const json = await import('highlight.js/lib/languages/json')\n\n // Register languages we need\n hljs.default.registerLanguage('javascript', javascript.default)\n hljs.default.registerLanguage('sql', sql.default)\n hljs.default.registerLanguage('json', json.default)\n\n // Store the instance\n highlightJs = hljs.default\n } catch (err) {\n console.error('Failed to load syntax highlighter:', err)\n // Clear loading promise so it can be retried\n loadingPromise = null\n }\n })()\n\n return loadingPromise\n}\n\n/**\n * Highlights all code blocks on the page that haven't been highlighted yet.\n * Gracefully handles cases where highlight.js fails to load.\n */\nexport async function highlightCodeBlocks(): Promise<void> {\n // Load highlighter if not already loaded\n await loadSyntaxHighlighter()\n\n // If loading failed, return silently (code blocks remain unstyled)\n if (!highlightJs) {\n return\n }\n\n // Find all code blocks and highlight them\n document.querySelectorAll('pre code').forEach((block) => {\n // Skip if already highlighted\n if (!block.classList.contains('hljs')) {\n highlightJs.highlightElement(block)\n }\n })\n}\n\n/**\n * Highlights a specific code block element.\n * Useful for dynamically added content.\n *\n * @param element - The code block element to highlight\n */\nexport async function highlightCodeBlock(element: HTMLElement): Promise<void> {\n await loadSyntaxHighlighter()\n\n if (!highlightJs) {\n return\n }\n\n if (!element.classList.contains('hljs')) {\n highlightJs.highlightElement(element)\n }\n}\n\n/**\n * Returns whether syntax highlighting is available.\n * Useful for conditional rendering or feature detection.\n */\nexport function isSyntaxHighlightingAvailable(): boolean {\n return highlightJs !== null\n}\n\n/**\n * Returns the loaded highlight.js instance, if available.\n * Useful for custom highlighting flows that need direct access.\n */\nexport function getSyntaxHighlighter(): any | null {\n return highlightJs\n}\n","import { useState, useEffect } from 'react'\nimport { highlightCodeBlocks } from '../utils/syntaxHighlighting'\nimport type { FlowChartData } from '../types/flow'\nimport type { RetentionChartData } from '../types/retention'\n\ninterface DebugModalProps {\n chartConfig: any\n displayConfig: any\n queryObject: any\n data: any[] | FlowChartData | RetentionChartData\n chartType: string\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n}\n\nexport default function DebugModal({\n chartConfig,\n displayConfig,\n queryObject,\n data,\n chartType,\n cacheInfo\n}: DebugModalProps) {\n const [isOpen, setIsOpen] = useState(false)\n\n // Handle ESC key to close modal\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && isOpen) {\n setIsOpen(false)\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [isOpen])\n\n // Trigger syntax highlighting when modal opens and content is rendered\n useEffect(() => {\n if (isOpen) {\n // Small delay to ensure DOM is updated\n const timer = setTimeout(() => {\n highlightCodeBlocks().catch((err) => {\n console.debug('Syntax highlighting not available:', err)\n })\n }, 10)\n\n return () => clearTimeout(timer)\n }\n }, [isOpen])\n\n\n if (!isOpen) {\n return (\n <button\n onClick={() => setIsOpen(true)}\n className=\"dc:p-1 text-dc-text-muted hover:text-dc-text-secondary dc:transition-colors\"\n title=\"Debug chart configuration\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/>\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>\n </svg>\n </button>\n )\n }\n\n return (\n <div\n className=\"dc:absolute dc:inset-0 bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:z-50 dc:overflow-auto\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"dc:p-4 dc:h-full dc:flex dc:flex-col\">\n <div className=\"dc:flex dc:justify-between dc:items-center dc:mb-4 dc:shrink-0\">\n <h2 className=\"dc:text-lg dc:font-semibold text-dc-text\">Chart Debug Information</h2>\n <button\n onClick={() => setIsOpen(false)}\n className=\"dc:p-2 text-dc-text-muted hover:text-dc-text-secondary hover:bg-dc-surface-secondary dc:rounded-sm\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n\n <div className=\"dc:grid dc:grid-cols-1 dc:lg:grid-cols-2 dc:gap-4 dc:flex-1 dc:overflow-auto\">\n <div>\n <h3 className=\"dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">Chart Type</h3>\n <div className=\"bg-dc-surface-secondary dc:p-2 dc:rounded-sm dc:text-sm font-mono dc:border border-dc-border\">\n {chartType}\n </div>\n </div>\n\n <div>\n <h3 className=\"dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">Field Analysis</h3>\n <div className=\"bg-dc-surface-secondary dc:p-2 dc:rounded-sm dc:text-xs dc:space-y-1 dc:border border-dc-border\">\n <div>\n <strong>xAxis:</strong> {Array.isArray(chartConfig?.xAxis) ? `Array: [${chartConfig.xAxis.join(', ')}]` : `String: \"${chartConfig?.xAxis}\"`}\n </div>\n <div>\n <strong>yAxis:</strong> {Array.isArray(chartConfig?.yAxis) ? `Array: [${chartConfig.yAxis.join(', ')}]` : `String: \"${chartConfig?.yAxis}\"`}\n </div>\n <div>\n <strong>series:</strong> {Array.isArray(chartConfig?.series) ? `Array: [${chartConfig.series.join(', ')}]` : `String: \"${chartConfig?.series}\"`}\n </div>\n {chartConfig?.sizeField && (\n <div>\n <strong>sizeField:</strong> {Array.isArray(chartConfig?.sizeField) ? `Array: [${chartConfig.sizeField.join(', ')}]` : `String: \"${chartConfig?.sizeField}\"`}\n </div>\n )}\n {chartConfig?.colorField && (\n <div>\n <strong>colorField:</strong> {Array.isArray(chartConfig?.colorField) ? `Array: [${chartConfig.colorField.join(', ')}]` : `String: \"${chartConfig?.colorField}\"`}\n </div>\n )}\n </div>\n </div>\n\n <div className=\"dc:lg:col-span-2\">\n <h3 className=\"dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">Chart Config</h3>\n <pre className=\"text-dc-text-secondary dc:overflow-x-auto font-mono dc:p-2 dc:rounded-sm dc:border border-dc-border\" style={{ fontSize: '10px', lineHeight: '1.4' }}>\n <code className=\"language-json\">{JSON.stringify(chartConfig, null, 2)}</code>\n </pre>\n </div>\n\n <div className=\"dc:lg:col-span-2\">\n <h3 className=\"dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">Display Config</h3>\n <pre className=\"text-dc-text-secondary dc:overflow-x-auto font-mono dc:p-2 dc:rounded-sm dc:border border-dc-border\" style={{ fontSize: '10px', lineHeight: '1.4' }}>\n <code className=\"language-json\">{JSON.stringify(displayConfig, null, 2)}</code>\n </pre>\n </div>\n\n <div className=\"dc:lg:col-span-2\">\n <h3 className=\"dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">Query Object</h3>\n <pre className=\"text-dc-text-secondary dc:overflow-x-auto font-mono dc:p-2 dc:rounded-sm dc:border border-dc-border\" style={{ fontSize: '10px', lineHeight: '1.4' }}>\n <code className=\"language-json\">{JSON.stringify(queryObject, null, 2)}</code>\n </pre>\n </div>\n\n <div className=\"dc:lg:col-span-2\">\n <h3 className=\"dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">Data Sample (first 3 rows)</h3>\n <pre className=\"text-dc-text-secondary dc:overflow-x-auto font-mono dc:p-2 dc:rounded-sm dc:border border-dc-border\" style={{ fontSize: '10px', lineHeight: '1.4' }}>\n <code className=\"language-json\">\n {JSON.stringify(Array.isArray(data) ? data.slice(0, 3) : data, null, 2)}\n </code>\n </pre>\n </div>\n\n <div className=\"dc:lg:col-span-2\">\n <h3 className=\"dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">Cache Status</h3>\n <div className=\"bg-dc-surface-secondary dc:p-2 dc:rounded-sm dc:text-xs dc:border border-dc-border\">\n {cacheInfo ? (\n <div className=\"dc:flex dc:items-center dc:gap-4 dc:flex-wrap\">\n <span className=\"dc:inline-flex dc:items-center dc:px-2 dc:py-0.5 dc:rounded-sm dc:text-xs dc:font-medium bg-dc-success-bg text-dc-success\">\n Cache Hit\n </span>\n <span><strong>Cached At:</strong> {new Date(cacheInfo.cachedAt).toLocaleString()}</span>\n <span><strong>TTL:</strong> {Math.round(cacheInfo.ttlMs / 1000)}s</span>\n <span><strong>TTL Remaining:</strong> {Math.round(cacheInfo.ttlRemainingMs / 1000)}s</span>\n </div>\n ) : (\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <span className=\"dc:inline-flex dc:items-center dc:px-2 dc:py-0.5 dc:rounded-sm dc:text-xs dc:font-medium bg-dc-surface text-dc-text-muted dc:border border-dc-border\">\n Fresh Query\n </span>\n <span className=\"text-dc-text-muted\">Result not served from cache</span>\n </div>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"dc:mt-4 dc:pt-2 dc:border-t border-dc-border dc:text-xs text-dc-text-muted dc:shrink-0\">\n Press <kbd className=\"dc:px-1 dc:py-0.5 bg-dc-surface-secondary dc:rounded-sm dc:text-xs\">ESC</kbd> to close\n </div>\n </div>\n </div>\n )\n}\n","/**\n * Dashboard Zustand Store (Instance-based)\n *\n * Centralized state management for Dashboard components, consolidating:\n * - Edit mode state (isEditMode, selectedFilterId)\n * - Modal state (portlet edit, analysis, filter config)\n * - Layout state (draftRows, drag state, initialization)\n * - Debug data (per-portlet debug information)\n *\n * KEY ARCHITECTURE: Instance-based stores\n * - Each Dashboard gets its own store instance via Context\n * - No localStorage persistence (dashboard state is transient)\n * - State is per-dashboard session, not persisted\n *\n * Uses Zustand's createStore (factory) instead of create (singleton).\n * Store is provided via React Context.\n */\n\nimport { createContext, useContext, useRef, type ReactNode } from 'react'\nimport { createStore, useStore, type StoreApi } from 'zustand'\nimport { devtools, subscribeWithSelector } from 'zustand/middleware'\nimport type { LayoutItem } from 'react-grid-layout'\nimport type {\n PortletConfig,\n RowLayout,\n ChartType,\n ChartAxisConfig,\n ChartDisplayConfig,\n CubeQuery,\n} from '../types'\nimport type { FlowChartData } from '../types/flow'\nimport type { RetentionChartData } from '../types/retention'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Debug data entry for a portlet (used by chart inspector)\n */\nexport interface PortletDebugDataEntry {\n chartConfig: ChartAxisConfig\n displayConfig: ChartDisplayConfig\n queryObject: CubeQuery | null\n data: unknown[] | FlowChartData | RetentionChartData\n chartType: ChartType\n cacheInfo?: {\n hit: boolean\n cachedAt: string\n ttlMs: number\n ttlRemainingMs: number\n } | null\n}\n\n/**\n * @deprecated Use PortletDebugDataEntry instead. Kept for backward compatibility.\n */\nexport type DebugDataEntry = PortletDebugDataEntry\n\n/**\n * Drag state for row-based layout\n */\nexport interface DragState {\n rowIndex: number\n colIndex: number\n portletId: string\n}\n\n/**\n * Complete store state interface\n */\nexport interface DashboardStoreState {\n // =========================================================================\n // Edit Mode State\n // =========================================================================\n /** Whether dashboard is in edit mode */\n isEditMode: boolean\n /** Currently selected filter ID for filter-assignment mode */\n selectedFilterId: string | null\n\n // =========================================================================\n // Modal State\n // =========================================================================\n /** Whether portlet edit modal is open */\n isPortletModalOpen: boolean\n /** Portlet being edited (null = adding new) */\n editingPortlet: PortletConfig | null\n /** Whether filter config modal is open */\n isFilterConfigModalOpen: boolean\n /** Portlet for filter configuration */\n filterConfigPortlet: PortletConfig | null\n /** Portlet ID pending delete confirmation (null = no confirmation active) */\n deleteConfirmPortletId: string | null\n\n // =========================================================================\n // Layout State\n // =========================================================================\n /** Draft row layout during drag operations (rows mode) */\n draftRows: RowLayout[] | null\n /** Whether a portlet is currently being dragged */\n isDraggingPortlet: boolean\n /** Last known layout for change detection */\n lastKnownLayout: LayoutItem[]\n /** Whether component has been initialized (prevents saves during load) */\n isInitialized: boolean\n /** Current drag state for row-based layout */\n dragState: DragState | null\n\n // =========================================================================\n // Debug Data\n // =========================================================================\n /** Debug data keyed by portlet ID (for chart inspector) */\n debugData: Record<string, DebugDataEntry>\n\n // =========================================================================\n // Thumbnail State\n // =========================================================================\n /** Whether the dashboard has been modified and needs a new thumbnail */\n thumbnailDirty: boolean\n}\n\n/**\n * Store actions interface\n */\nexport interface DashboardStoreActions {\n // =========================================================================\n // Edit Mode Actions\n // =========================================================================\n /** Set edit mode */\n setEditMode: (isEdit: boolean) => void\n /** Toggle edit mode */\n toggleEditMode: () => void\n /** Set selected filter ID for filter selection mode */\n setSelectedFilterId: (filterId: string | null) => void\n /** Exit filter selection mode */\n exitFilterSelectionMode: () => void\n\n // =========================================================================\n // Modal Actions\n // =========================================================================\n /** Open portlet modal (optionally with a portlet to edit) */\n openPortletModal: (portlet?: PortletConfig | null) => void\n /** Close portlet modal */\n closePortletModal: () => void\n /** Open filter config modal for a portlet */\n openFilterConfigModal: (portlet: PortletConfig) => void\n /** Close filter config modal */\n closeFilterConfigModal: () => void\n /** Open delete confirmation for a portlet */\n openDeleteConfirm: (portletId: string) => void\n /** Close delete confirmation */\n closeDeleteConfirm: () => void\n\n // =========================================================================\n // Layout Actions\n // =========================================================================\n /** Set draft rows during drag operations */\n setDraftRows: (rows: RowLayout[] | null) => void\n /** Set whether a portlet is being dragged */\n setIsDraggingPortlet: (isDragging: boolean) => void\n /** Set last known layout for change detection */\n setLastKnownLayout: (layout: LayoutItem[]) => void\n /** Set whether component is initialized */\n setIsInitialized: (initialized: boolean) => void\n /** Set drag state */\n setDragState: (state: DragState | null) => void\n /** Clear drag state */\n clearDragState: () => void\n\n // =========================================================================\n // Debug Data Actions\n // =========================================================================\n /** Set debug data for a portlet */\n setDebugData: (portletId: string, data: DebugDataEntry) => void\n /** Clear debug data for a portlet (or all if no portletId) */\n clearDebugData: (portletId?: string) => void\n\n // =========================================================================\n // Thumbnail Actions\n // =========================================================================\n /** Set thumbnail dirty state (needs new capture) */\n setThumbnailDirty: (dirty: boolean) => void\n\n // =========================================================================\n // Utility Actions\n // =========================================================================\n /** Reset store to initial state */\n reset: () => void\n}\n\n/**\n * Combined store type\n */\nexport type DashboardStore = DashboardStoreState & DashboardStoreActions\n\n/**\n * Options for creating a store instance\n */\nexport interface CreateDashboardStoreOptions {\n /** Initial edit mode state */\n initialEditMode?: boolean\n}\n\n// ============================================================================\n// Initial State\n// ============================================================================\n\nconst createDefaultState = (): DashboardStoreState => ({\n // Edit mode\n isEditMode: false,\n selectedFilterId: null,\n\n // Modal state\n isPortletModalOpen: false,\n editingPortlet: null,\n isFilterConfigModalOpen: false,\n filterConfigPortlet: null,\n deleteConfirmPortletId: null,\n\n // Layout state\n draftRows: null,\n isDraggingPortlet: false,\n lastKnownLayout: [],\n isInitialized: false,\n dragState: null,\n\n // Debug data\n debugData: {},\n\n // Thumbnail state\n thumbnailDirty: false,\n})\n\n/**\n * Build initial state from options\n */\nfunction buildInitialState(options: CreateDashboardStoreOptions): DashboardStoreState {\n const defaultState = createDefaultState()\n\n return {\n ...defaultState,\n isEditMode: options.initialEditMode ?? false,\n }\n}\n\n// ============================================================================\n// Store Factory\n// ============================================================================\n\n/**\n * Create store actions\n */\nfunction createStoreActions(\n set: (\n partial:\n | Partial<DashboardStore>\n | ((state: DashboardStore) => Partial<DashboardStore>)\n ) => void,\n _get: () => DashboardStore,\n initialState: DashboardStoreState\n): DashboardStoreActions {\n return {\n // =================================================================\n // Edit Mode Actions\n // =================================================================\n\n setEditMode: (isEdit) =>\n set({\n isEditMode: isEdit,\n // Exit filter selection mode when leaving edit mode\n selectedFilterId: isEdit ? null : null,\n }),\n\n toggleEditMode: () =>\n set((state) => ({\n isEditMode: !state.isEditMode,\n // Exit filter selection mode when toggling\n selectedFilterId: null,\n })),\n\n setSelectedFilterId: (filterId) => set({ selectedFilterId: filterId }),\n\n exitFilterSelectionMode: () => set({ selectedFilterId: null }),\n\n // =================================================================\n // Modal Actions\n // =================================================================\n\n openPortletModal: (portlet) =>\n set({\n isPortletModalOpen: true,\n editingPortlet: portlet ?? null,\n }),\n\n closePortletModal: () =>\n set({\n isPortletModalOpen: false,\n editingPortlet: null,\n }),\n\n openFilterConfigModal: (portlet) =>\n set({\n isFilterConfigModalOpen: true,\n filterConfigPortlet: portlet,\n }),\n\n closeFilterConfigModal: () =>\n set({\n isFilterConfigModalOpen: false,\n filterConfigPortlet: null,\n }),\n\n openDeleteConfirm: (portletId: string) =>\n set({ deleteConfirmPortletId: portletId }),\n\n closeDeleteConfirm: () =>\n set({ deleteConfirmPortletId: null }),\n\n // =================================================================\n // Layout Actions\n // =================================================================\n\n setDraftRows: (rows) => set({ draftRows: rows }),\n\n setIsDraggingPortlet: (isDragging) => set({ isDraggingPortlet: isDragging }),\n\n setLastKnownLayout: (layout) => set({ lastKnownLayout: layout }),\n\n setIsInitialized: (initialized) => set({ isInitialized: initialized }),\n\n setDragState: (state) => set({ dragState: state }),\n\n clearDragState: () =>\n set({\n dragState: null,\n isDraggingPortlet: false,\n }),\n\n // =================================================================\n // Debug Data Actions\n // =================================================================\n\n setDebugData: (portletId, data) =>\n set((state) => ({\n debugData: {\n ...state.debugData,\n [portletId]: data,\n },\n })),\n\n clearDebugData: (portletId) =>\n set((state) => {\n if (portletId) {\n const { [portletId]: _, ...rest } = state.debugData\n return { debugData: rest }\n }\n return { debugData: {} }\n }),\n\n // =================================================================\n // Thumbnail Actions\n // =================================================================\n\n setThumbnailDirty: (dirty) => set({ thumbnailDirty: dirty }),\n\n // =================================================================\n // Utility Actions\n // =================================================================\n\n reset: () => set(initialState),\n }\n}\n\n/**\n * Create a new dashboard store instance\n */\nexport function createDashboardStore(options: CreateDashboardStoreOptions = {}) {\n const initialState = buildInitialState(options)\n\n // No localStorage persistence - dashboard state is transient\n return createStore<DashboardStore>()(\n devtools(\n subscribeWithSelector((set, get) => ({\n ...initialState,\n ...createStoreActions(set, get, initialState),\n })),\n { name: 'DashboardStore' }\n )\n )\n}\n\n/**\n * Fallback store for useDashboardStoreOptional - created lazily at module level\n * This allows the hook to always call useStore unconditionally (React hooks rules)\n */\nlet fallbackStore: StoreApi<DashboardStore> | null = null\nfunction getFallbackStore(): StoreApi<DashboardStore> {\n if (!fallbackStore) {\n fallbackStore = createDashboardStore()\n }\n return fallbackStore\n}\n\n// ============================================================================\n// React Context & Provider\n// ============================================================================\n\n/**\n * Context for the store instance\n */\nconst DashboardStoreContext = createContext<StoreApi<DashboardStore> | null>(null)\n\n/**\n * Provider props\n */\nexport interface DashboardStoreProviderProps {\n children: ReactNode\n /** Initial edit mode state */\n initialEditMode?: boolean\n}\n\n/**\n * Provider component that creates a store instance per Dashboard\n */\nexport function DashboardStoreProvider({\n children,\n initialEditMode,\n}: DashboardStoreProviderProps) {\n // Create store instance once per provider mount\n const storeRef = useRef<StoreApi<DashboardStore> | null>(null)\n\n if (!storeRef.current) {\n storeRef.current = createDashboardStore({\n initialEditMode,\n })\n }\n\n return (\n <DashboardStoreContext.Provider value={storeRef.current}>\n {children}\n </DashboardStoreContext.Provider>\n )\n}\n\n/**\n * Hook to access the store from context\n * @throws Error if used outside of provider\n */\nexport function useDashboardStore<T>(selector: (state: DashboardStore) => T): T {\n const store = useContext(DashboardStoreContext)\n if (!store) {\n throw new Error('useDashboardStore must be used within DashboardStoreProvider')\n }\n return useStore(store, selector)\n}\n\n/**\n * Hook to get the raw store API (for actions that need direct access)\n */\nexport function useDashboardStoreApi(): StoreApi<DashboardStore> {\n const store = useContext(DashboardStoreContext)\n if (!store) {\n throw new Error('useDashboardStoreApi must be used within DashboardStoreProvider')\n }\n return store\n}\n\n/**\n * Optional hook that returns null if used outside provider (for optional store access)\n * Uses a fallback store to ensure useStore is always called (React hooks rules compliance)\n */\nexport function useDashboardStoreOptional<T>(\n selector: (state: DashboardStore) => T\n): T | null {\n const contextStore = useContext(DashboardStoreContext)\n // Always call useStore unconditionally (React hooks rules)\n // Use fallback store when context is null to maintain hook order\n const result = useStore(contextStore ?? getFallbackStore(), selector)\n // Return null if there was no real store (using fallback)\n return contextStore ? result : null\n}\n\n// ============================================================================\n// Selectors (for optimized re-renders)\n// ============================================================================\n\n/**\n * Select edit mode state\n */\nexport const selectEditModeState = (state: DashboardStore) => ({\n isEditMode: state.isEditMode,\n selectedFilterId: state.selectedFilterId,\n})\n\n/**\n * Select modal state\n */\nexport const selectModalState = (state: DashboardStore) => ({\n isPortletModalOpen: state.isPortletModalOpen,\n editingPortlet: state.editingPortlet,\n isFilterConfigModalOpen: state.isFilterConfigModalOpen,\n filterConfigPortlet: state.filterConfigPortlet,\n})\n\n/**\n * Select layout state\n */\nexport const selectLayoutState = (state: DashboardStore) => ({\n draftRows: state.draftRows,\n isDraggingPortlet: state.isDraggingPortlet,\n lastKnownLayout: state.lastKnownLayout,\n isInitialized: state.isInitialized,\n dragState: state.dragState,\n})\n\n/**\n * Select debug data\n */\nexport const selectDebugData = (state: DashboardStore) => state.debugData\n\n/**\n * Select debug data for a specific portlet\n */\nexport const selectPortletDebugData = (portletId: string) => (state: DashboardStore) =>\n state.debugData[portletId]\n\n/**\n * Select all edit mode actions\n */\nexport const selectEditModeActions = (state: DashboardStore) => ({\n setEditMode: state.setEditMode,\n toggleEditMode: state.toggleEditMode,\n setSelectedFilterId: state.setSelectedFilterId,\n exitFilterSelectionMode: state.exitFilterSelectionMode,\n})\n\n/**\n * Select all modal actions\n */\nexport const selectModalActions = (state: DashboardStore) => ({\n openPortletModal: state.openPortletModal,\n closePortletModal: state.closePortletModal,\n openFilterConfigModal: state.openFilterConfigModal,\n closeFilterConfigModal: state.closeFilterConfigModal,\n openDeleteConfirm: state.openDeleteConfirm,\n closeDeleteConfirm: state.closeDeleteConfirm,\n})\n\n/**\n * Select all layout actions\n */\nexport const selectLayoutActions = (state: DashboardStore) => ({\n setDraftRows: state.setDraftRows,\n setIsDraggingPortlet: state.setIsDraggingPortlet,\n setLastKnownLayout: state.setLastKnownLayout,\n setIsInitialized: state.setIsInitialized,\n setDragState: state.setDragState,\n clearDragState: state.clearDragState,\n})\n\n/**\n * Select all debug data actions\n */\nexport const selectDebugDataActions = (state: DashboardStore) => ({\n setDebugData: state.setDebugData,\n clearDebugData: state.clearDebugData,\n})\n\n/**\n * Select thumbnail dirty state\n */\nexport const selectThumbnailDirty = (state: DashboardStore) => state.thumbnailDirty\n\n/**\n * Select all actions\n */\nexport const selectAllActions = (state: DashboardStore) => ({\n // Edit mode\n setEditMode: state.setEditMode,\n toggleEditMode: state.toggleEditMode,\n setSelectedFilterId: state.setSelectedFilterId,\n exitFilterSelectionMode: state.exitFilterSelectionMode,\n // Modals\n openPortletModal: state.openPortletModal,\n closePortletModal: state.closePortletModal,\n openFilterConfigModal: state.openFilterConfigModal,\n closeFilterConfigModal: state.closeFilterConfigModal,\n openDeleteConfirm: state.openDeleteConfirm,\n closeDeleteConfirm: state.closeDeleteConfirm,\n // Layout\n setDraftRows: state.setDraftRows,\n setIsDraggingPortlet: state.setIsDraggingPortlet,\n setLastKnownLayout: state.setLastKnownLayout,\n setIsInitialized: state.setIsInitialized,\n setDragState: state.setDragState,\n clearDragState: state.clearDragState,\n // Debug data\n setDebugData: state.setDebugData,\n clearDebugData: state.clearDebugData,\n // Thumbnail\n setThumbnailDirty: state.setThumbnailDirty,\n // Utility\n reset: state.reset,\n})\n","import React, { useCallback, useMemo, useState, useEffect, useRef, type HTMLAttributes, type ReactNode, type CSSProperties, type ComponentType } from 'react'\nimport type { DashboardFilter, PortletConfig } from '../types'\nimport AnalyticsPortlet from './AnalyticsPortlet'\nimport DebugModal from './DebugModal'\nimport type { ColorPalette } from '../utils/colorPalettes'\nimport { useDashboardStore, type PortletDebugDataEntry } from '../stores/dashboardStore'\nimport { ensureAnalysisConfig } from '../utils/configMigration'\nimport { useCubeFeatures } from '../providers/CubeFeaturesProvider'\nimport { getIcon } from '../icons/registry'\nimport { isPortletCopyAvailable, copyPortletToClipboard } from '../utils/thumbnail'\n\n// Constant style object to prevent re-renders from inline object recreation\nconst ICON_STYLE: CSSProperties = { width: '16px', height: '16px', color: 'currentColor' }\n\n/**\n * Simplified props interface after Zustand migration.\n * State (isEditMode, selectedFilterId, debugData) now comes from store.\n * Actions now come from callbacks prop or store.\n */\ninterface DashboardPortletCardProps {\n portlet: PortletConfig\n editable: boolean\n dashboardFilters?: DashboardFilter[]\n configEagerLoad?: boolean\n loadingComponent?: ReactNode\n colorPalette?: ColorPalette\n containerProps?: HTMLAttributes<HTMLDivElement>\n headerProps?: HTMLAttributes<HTMLDivElement>\n // Ref callbacks - must remain as props (parent manages refs)\n setPortletRef: (portletId: string, element: HTMLDivElement | null) => void\n setPortletComponentRef: (portletId: string, element: { refresh: (options?: { bustCache?: boolean }) => void } | null) => void\n // Action callbacks - provided by parent for flexibility\n callbacks: {\n onToggleFilter: (portletId: string, filterId: string) => void\n onRefresh: (portletId: string, options?: { bustCache?: boolean }) => void\n onDuplicate: (portletId: string) => void\n onEdit: (portlet: PortletConfig) => void\n onDelete: (portletId: string) => void\n onOpenFilterConfig: (portlet: PortletConfig) => void\n }\n icons: {\n RefreshIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n EditIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n DeleteIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n CopyIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n FilterIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n }\n}\n\n// Custom comparison function to handle containerProps/headerProps object recreation\nfunction arePropsEqual(\n prevProps: DashboardPortletCardProps,\n nextProps: DashboardPortletCardProps\n): boolean {\n // Fast path: if object references are the same, props are equal\n if (prevProps === nextProps) return true\n\n // Check scalar props\n if (\n prevProps.editable !== nextProps.editable ||\n prevProps.configEagerLoad !== nextProps.configEagerLoad\n ) {\n return false\n }\n\n // Check object/array props by reference\n if (\n prevProps.portlet !== nextProps.portlet ||\n prevProps.dashboardFilters !== nextProps.dashboardFilters ||\n prevProps.colorPalette !== nextProps.colorPalette ||\n prevProps.loadingComponent !== nextProps.loadingComponent ||\n prevProps.callbacks !== nextProps.callbacks ||\n prevProps.icons !== nextProps.icons\n ) {\n return false\n }\n\n // Check function props by reference\n if (\n prevProps.setPortletRef !== nextProps.setPortletRef ||\n prevProps.setPortletComponentRef !== nextProps.setPortletComponentRef\n ) {\n return false\n }\n\n // Special handling for containerProps and headerProps - compare properties shallowly\n const containerPropsEqual = shallowEqualObjects(prevProps.containerProps, nextProps.containerProps)\n const headerPropsEqual = shallowEqualObjects(prevProps.headerProps, nextProps.headerProps)\n\n return containerPropsEqual && headerPropsEqual\n}\n\n// Shallow comparison for objects\nfunction shallowEqualObjects<T extends object>(\n a: T | undefined,\n b: T | undefined\n): boolean {\n if (a === b) return true\n if (!a || !b) return a === b\n\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n\n if (keysA.length !== keysB.length) return false\n\n for (const key of keysA) {\n if ((a as Record<string, unknown>)[key] !== (b as Record<string, unknown>)[key]) return false\n }\n\n return true\n}\n\n// Memoize component - now using store for state, so fewer props to compare\nconst DashboardPortletCard = React.memo(function DashboardPortletCard({\n portlet,\n editable,\n dashboardFilters,\n configEagerLoad,\n loadingComponent,\n colorPalette,\n containerProps,\n headerProps,\n setPortletRef,\n setPortletComponentRef,\n callbacks,\n icons\n}: DashboardPortletCardProps) {\n // Normalize portlet to ensure analysisConfig exists (on-the-fly migration from legacy format)\n const normalizedPortlet = useMemo(() => ensureAnalysisConfig(portlet), [portlet])\n const { analysisConfig } = normalizedPortlet\n\n // Extract rendering props from analysisConfig\n const chartModeConfig = analysisConfig.charts[analysisConfig.analysisType]\n const renderQuery = useMemo(() => JSON.stringify(analysisConfig.query), [analysisConfig.query])\n const renderChartType = chartModeConfig?.chartType || 'line'\n const renderChartConfig = chartModeConfig?.chartConfig\n const renderDisplayConfig = chartModeConfig?.displayConfig\n\n // Get state from Zustand store - automatic memoization via selectors\n const isEditMode = useDashboardStore(state => state.isEditMode)\n const selectedFilterId = useDashboardStore(state => state.selectedFilterId)\n const debugData = useDashboardStore(state => state.debugData[portlet.id])\n\n // Get setDebugData action from store\n const setDebugData = useDashboardStore(state => state.setDebugData)\n\n // Get features for copy-to-clipboard functionality\n const { features } = useCubeFeatures()\n\n // Icons for copy-to-clipboard button\n const CameraIcon = getIcon('camera')\n const CheckIcon = getIcon('check')\n\n // State and ref for copy-to-clipboard functionality\n const [copySuccess, setCopySuccess] = useState(false)\n const [copyAvailable, setCopyAvailable] = useState(false)\n const chartContainerRef = useRef<HTMLDivElement>(null)\n\n // Track shift key + hover state for cache bust visual feedback on refresh button\n const [isShiftHeld, setIsShiftHeld] = useState(false)\n const [isHoveringRefresh, setIsHoveringRefresh] = useState(false)\n\n // Listen for shift key up/down to show visual feedback on refresh button (only when hovering)\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Shift') setIsShiftHeld(true)\n }\n const handleKeyUp = (e: KeyboardEvent) => {\n if (e.key === 'Shift') setIsShiftHeld(false)\n }\n window.addEventListener('keydown', handleKeyDown)\n window.addEventListener('keyup', handleKeyUp)\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n window.removeEventListener('keyup', handleKeyUp)\n }\n }, [])\n\n // Show warning styling only when hovering AND shift is held\n const showCacheBustIndicator = isShiftHeld && isHoveringRefresh\n\n // Check if copy-to-clipboard capability is available on mount\n useEffect(() => {\n if (features.thumbnail?.enabled) {\n isPortletCopyAvailable().then(setCopyAvailable)\n } else {\n setCopyAvailable(false)\n }\n }, [features.thumbnail?.enabled])\n\n // Handler for copy-to-clipboard\n const handleCopyToClipboard = useCallback(async (event: React.MouseEvent | React.TouchEvent) => {\n event.stopPropagation()\n if (!chartContainerRef.current) return\n\n const success = await copyPortletToClipboard(chartContainerRef.current)\n if (success) {\n setCopySuccess(true)\n setTimeout(() => setCopySuccess(false), 2000)\n }\n }, [])\n\n const hasSelectedFilter = selectedFilterId\n ? (portlet.dashboardFilterMapping || []).includes(selectedFilterId)\n : false\n const isInSelectionMode = !!selectedFilterId\n\n const mergedContainerClassName = [\n 'bg-dc-surface border rounded-lg flex flex-col h-full transition-all',\n isInSelectionMode ? 'cursor-pointer' : '',\n containerProps?.className\n ]\n .filter(Boolean)\n .join(' ')\n\n const mergedHeaderClassName = [\n 'flex items-center justify-between px-3 py-1.5 md:px-4 md:py-1 border-b border-dc-border shrink-0 bg-dc-surface-secondary rounded-t-lg portlet-drag-handle',\n isEditMode ? 'cursor-move' : 'cursor-default',\n headerProps?.className\n ]\n .filter(Boolean)\n .join(' ')\n\n const {\n onClick: containerOnClick,\n className: _containerClassName,\n style: containerStyle,\n ...restContainerProps\n } = containerProps ?? {}\n\n const {\n onClick: headerOnClick,\n className: _headerClassName,\n style: headerStyle,\n ...restHeaderProps\n } = headerProps ?? {}\n\n // Memoize debug data callback - now uses store action directly\n const handleDebugDataReady = useCallback((data: PortletDebugDataEntry) => {\n setDebugData(portlet.id, data)\n }, [portlet.id, setDebugData])\n\n return (\n <div\n data-portlet-id={portlet.id}\n ref={el => setPortletRef(portlet.id, el)}\n className={mergedContainerClassName}\n style={{\n boxShadow: 'var(--dc-shadow-sm)',\n borderColor: isInSelectionMode && hasSelectedFilter\n ? 'var(--dc-primary)'\n : 'var(--dc-border)',\n borderWidth: isInSelectionMode && hasSelectedFilter ? '2px' : '1px',\n backgroundColor: isInSelectionMode && hasSelectedFilter\n ? 'rgba(var(--dc-primary-rgb), 0.05)'\n : 'var(--dc-surface)',\n opacity: isInSelectionMode && !hasSelectedFilter ? '0.5' : '1',\n ...containerStyle\n }}\n onClick={(event) => {\n if (isInSelectionMode && selectedFilterId) {\n event.stopPropagation()\n callbacks.onToggleFilter(portlet.id, selectedFilterId)\n }\n containerOnClick?.(event)\n }}\n {...restContainerProps}\n >\n {(!renderDisplayConfig?.hideHeader || isEditMode) && (\n <div\n className={mergedHeaderClassName}\n style={headerStyle}\n onClick={(event) => {\n headerOnClick?.(event)\n }}\n {...restHeaderProps}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:flex-1 dc:min-w-0\">\n <h3 className=\"dc:font-semibold dc:text-sm text-dc-text dc:truncate\">{portlet.title}</h3>\n {editable && isEditMode && debugData && (\n <div\n onMouseDown={(event) => {\n event.stopPropagation()\n event.preventDefault()\n }}\n onClick={(event) => event.stopPropagation()}\n onTouchStart={(event) => {\n event.stopPropagation()\n event.preventDefault()\n }}\n onTouchEnd={(event) => event.stopPropagation()}\n >\n <DebugModal\n chartConfig={debugData.chartConfig}\n displayConfig={debugData.displayConfig}\n queryObject={debugData.queryObject}\n data={debugData.data}\n chartType={debugData.chartType}\n cacheInfo={debugData.cacheInfo as { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | undefined}\n />\n </div>\n )}\n </div>\n <div\n className=\"dc:flex dc:items-center dc:gap-1 dc:shrink-0 dc:ml-4 dc:-mr-2\"\n onMouseDown={(event) => {\n event.stopPropagation()\n event.preventDefault()\n }}\n onClick={(event) => event.stopPropagation()}\n onTouchStart={(event) => {\n event.stopPropagation()\n event.preventDefault()\n }}\n onTouchEnd={(event) => event.stopPropagation()}\n >\n {/* Cache indicator - show when result was served from cache */}\n {debugData?.cacheInfo && (\n <span\n className=\"dc:p-1 text-dc-text-muted dc:opacity-40\"\n title={`Cached ${Math.round((Date.now() - new Date(debugData.cacheInfo.cachedAt).getTime()) / 1000)}s ago`}\n >\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\" />\n <path d=\"M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5\" />\n <path d=\"M3 12c0 1.66 4 3 9 3s9-1.34 9-3\" />\n </svg>\n </span>\n )}\n <button\n onClick={(event) => {\n event.stopPropagation()\n callbacks.onRefresh(portlet.id, { bustCache: event.shiftKey })\n }}\n onTouchEnd={(event) => {\n event.stopPropagation()\n event.preventDefault()\n callbacks.onRefresh(portlet.id)\n }}\n onMouseEnter={() => setIsHoveringRefresh(true)}\n onMouseLeave={() => setIsHoveringRefresh(false)}\n disabled={isInSelectionMode}\n className={`dc:p-1 bg-transparent dc:border-none dc:rounded-sm dc:transition-colors ${\n isInSelectionMode\n ? 'dc:cursor-not-allowed dc:opacity-50 text-dc-text-secondary'\n : showCacheBustIndicator\n ? 'dc:cursor-pointer text-dc-warning bg-dc-warning-bg'\n : 'dc:cursor-pointer text-dc-text-secondary hover:bg-dc-surface-hover'\n }`}\n title={showCacheBustIndicator ? 'Click to refresh and bypass cache' : 'Refresh portlet data (Shift+click to bypass cache)'}\n >\n <icons.RefreshIcon style={ICON_STYLE} />\n </button>\n\n {/* Copy to clipboard button - visible when thumbnail feature is enabled and capability is available */}\n {copyAvailable && !isInSelectionMode && (\n <button\n onClick={handleCopyToClipboard}\n onTouchEnd={(event) => {\n event.preventDefault()\n handleCopyToClipboard(event)\n }}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm text-dc-text-secondary dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors\"\n title={copySuccess ? 'Copied!' : 'Copy chart to clipboard'}\n >\n {copySuccess ? (\n <CheckIcon style={ICON_STYLE} />\n ) : (\n <CameraIcon style={ICON_STYLE} />\n )}\n </button>\n )}\n\n {editable && isEditMode && !isInSelectionMode && (\n <>\n <button\n onClick={(event) => {\n event.stopPropagation()\n callbacks.onOpenFilterConfig(portlet)\n }}\n onTouchEnd={(event) => {\n event.stopPropagation()\n event.preventDefault()\n callbacks.onOpenFilterConfig(portlet)\n }}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors dc:relative\"\n title={`Configure dashboard filters${portlet.dashboardFilterMapping && portlet.dashboardFilterMapping.length > 0 ? ` (${portlet.dashboardFilterMapping.length} active)` : ''}`}\n style={{\n color: portlet.dashboardFilterMapping && portlet.dashboardFilterMapping.length > 0\n ? 'var(--dc-primary)'\n : 'var(--dc-text-secondary)'\n }}\n >\n <icons.FilterIcon style={ICON_STYLE} />\n </button>\n\n <button\n onClick={(event) => {\n event.stopPropagation()\n callbacks.onDuplicate(portlet.id)\n }}\n onTouchEnd={(event) => {\n event.stopPropagation()\n event.preventDefault()\n callbacks.onDuplicate(portlet.id)\n }}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm text-dc-text-secondary dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors\"\n title=\"Duplicate portlet\"\n >\n <icons.CopyIcon style={ICON_STYLE} />\n </button>\n <button\n onClick={(event) => {\n event.stopPropagation()\n callbacks.onEdit(portlet)\n }}\n onTouchEnd={(event) => {\n event.stopPropagation()\n event.preventDefault()\n callbacks.onEdit(portlet)\n }}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm text-dc-text-secondary dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors\"\n title=\"Edit portlet\"\n >\n <icons.EditIcon style={ICON_STYLE} />\n </button>\n <button\n onClick={(event) => {\n event.stopPropagation()\n callbacks.onDelete(portlet.id)\n }}\n onTouchEnd={(event) => {\n event.stopPropagation()\n event.preventDefault()\n callbacks.onDelete(portlet.id)\n }}\n className=\"dc:p-1 dc:mr-0.5 bg-transparent dc:border-none dc:rounded-sm dc:cursor-pointer hover:bg-dc-danger-bg text-dc-danger dc:transition-colors\"\n title=\"Delete portlet\"\n >\n <icons.DeleteIcon style={ICON_STYLE} />\n </button>\n </>\n )}\n </div>\n </div>\n )}\n\n <div\n ref={chartContainerRef}\n className=\"dc:flex-1 dc:px-2 dc:py-3 dc:md:px-4 dc:md:py-4 dc:min-h-0 dc:overflow-visible dc:flex dc:flex-col\"\n >\n <AnalyticsPortlet\n ref={el => setPortletComponentRef(portlet.id, el)}\n query={renderQuery}\n chartType={renderChartType}\n chartConfig={renderChartConfig}\n displayConfig={renderDisplayConfig}\n dashboardFilters={dashboardFilters}\n dashboardFilterMapping={portlet.dashboardFilterMapping}\n eagerLoad={portlet.eagerLoad ?? configEagerLoad ?? false}\n title={portlet.title}\n height=\"100%\"\n colorPalette={colorPalette}\n loadingComponent={loadingComponent}\n onDebugDataReady={handleDebugDataReady}\n />\n </div>\n </div>\n )\n}, arePropsEqual)\n\nexport default DashboardPortletCard\n","import { useState, useCallback, type HTMLAttributes, type ReactNode, type MouseEvent, type DragEvent } from 'react'\nimport type { DashboardGridSettings, PortletConfig, RowLayout } from '../types'\n\ninterface RowManagedLayoutProps {\n rows: RowLayout[]\n portlets: PortletConfig[]\n gridSettings: DashboardGridSettings\n gridWidth: number\n canEdit: boolean\n isDragging: boolean\n onRowResize: (rowIndex: number, event: MouseEvent<HTMLDivElement>) => void\n onColumnResize: (rowIndex: number, columnIndex: number, event: MouseEvent<HTMLDivElement>) => void\n onPortletDragStart: (rowIndex: number, columnIndex: number, portletId: string, event: DragEvent<HTMLDivElement>) => void\n onPortletDragEnd: () => void\n onRowDrop: (rowIndex: number, insertIndex: number | null) => void\n onNewRowDrop: (insertIndex: number) => void\n renderPortlet: (portlet: PortletConfig, containerProps?: HTMLAttributes<HTMLDivElement>, headerProps?: HTMLAttributes<HTMLDivElement>) => ReactNode\n}\n\nconst COLUMN_GAP = 16\n\nexport default function RowManagedLayout({\n rows,\n portlets,\n gridSettings,\n gridWidth,\n canEdit,\n isDragging,\n onRowResize,\n onColumnResize,\n onPortletDragStart,\n onPortletDragEnd,\n onRowDrop,\n onNewRowDrop,\n renderPortlet\n}: RowManagedLayoutProps) {\n const portletMap = new Map(portlets.map(portlet => [portlet.id, portlet]))\n const [activeDropKey, setActiveDropKey] = useState<string | null>(null)\n\n const setDropActive = (key: string | null) => {\n setActiveDropKey(key)\n }\n\n const isDragActive = isDragging || activeDropKey !== null\n\n // Stable drag event handlers using data attributes to prevent containerProps recreation\n const handlePortletDragStart = useCallback((event: DragEvent<HTMLDivElement>) => {\n const rowIndex = parseInt(event.currentTarget.dataset.rowIndex || '0', 10)\n const columnIndex = parseInt(event.currentTarget.dataset.columnIndex || '0', 10)\n const portletId = event.currentTarget.dataset.portletId || ''\n onPortletDragStart(rowIndex, columnIndex, portletId, event)\n }, [onPortletDragStart])\n\n const handlePortletDragEnd = useCallback(() => {\n setDropActive(null)\n onPortletDragEnd()\n }, [onPortletDragEnd])\n\n const topDropActive = activeDropKey === 'row-insert-0'\n const bottomDropActive = activeDropKey === 'row-bottom'\n\n return (\n <div\n className={`dc-row-layout${canEdit ? ' dc-row-layout-editable' : ''}${isDragActive ? ' dc-row-layout-dragging' : ''}`}\n style={{\n ['--dc-row-gap' as string]: '24px',\n ['--dc-column-gap' as string]: `${COLUMN_GAP}px`,\n ['--dc-top-drop-space' as string]: topDropActive ? '24px' : '0px',\n ['--dc-bottom-drop-space' as string]: bottomDropActive ? '24px' : '0px'\n }}\n >\n {canEdit && (\n <div\n className={`dc-row-boundary-drop dc-row-boundary-drop-top dc-split-handle${activeDropKey === 'dc:row-insert-0' ? ' dc-drop-zone-active' : ''}`}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive('row-insert-0')\n }}\n onDragLeave={() => setDropActive(null)}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onNewRowDrop(0)\n }}\n />\n )}\n {rows.map((row, rowIndex) => {\n const rowHeight = row.h * gridSettings.rowHeight\n const safeGridWidth = gridWidth || gridSettings.cols * gridSettings.rowHeight\n const paddingLeft = activeDropKey === `row-${rowIndex}-insert-0` ? COLUMN_GAP : 0\n const paddingRight = activeDropKey === `row-${rowIndex}-insert-${row.columns.length}` ? COLUMN_GAP : 0\n const rowContentWidth = safeGridWidth - (row.columns.length - 1) * COLUMN_GAP - paddingLeft - paddingRight\n const unitWidth = rowContentWidth / gridSettings.cols\n\n return (\n <div key={row.id} className=\"dc-row-layout-row-wrapper\">\n <div\n className=\"dc-row-layout-row\"\n style={{ height: rowHeight, paddingLeft, paddingRight }}\n >\n {row.columns.map((column, columnIndex) => {\n const portlet = portletMap.get(column.portletId)\n if (!portlet) return null\n const width = column.w * unitWidth\n\n const containerProps = {\n draggable: canEdit,\n 'data-row-index': rowIndex.toString(),\n 'data-column-index': columnIndex.toString(),\n 'data-portlet-id': portlet.id,\n onDragStart: handlePortletDragStart,\n onDragEnd: handlePortletDragEnd,\n className: 'dc-row-layout-column'\n } as HTMLAttributes<HTMLDivElement>\n\n return (\n <div\n key={portlet.id}\n className=\"dc-row-layout-column-wrapper\"\n style={{\n flex: `0 0 ${width}px`,\n maxWidth: `${width}px`\n }}\n >\n {renderPortlet(portlet, containerProps)}\n {columnIndex < row.columns.length - 1 && (\n <div\n className={`dc-column-resize-handle dc-split-handle${activeDropKey === `row-${rowIndex}-insert-${columnIndex + 1}` ? ' dc-drop-zone-active' : ''}`}\n onMouseDown={(event) => onColumnResize(rowIndex, columnIndex, event)}\n onDragOver={(event) => {\n if (!canEdit) return\n event.preventDefault()\n setDropActive(`row-${rowIndex}-insert-${columnIndex + 1}`)\n }}\n onDragLeave={() => setDropActive(null)}\n onDrop={(event) => {\n if (!canEdit) return\n event.preventDefault()\n event.stopPropagation()\n setDropActive(null)\n onRowDrop(rowIndex, columnIndex + 1)\n }}\n />\n )}\n </div>\n )\n })}\n </div>\n {canEdit && (\n <>\n <div\n className={`dc-row-edge-drop dc-row-edge-drop-left dc-split-handle${activeDropKey === `row-${rowIndex}-insert-0` ? ' dc-drop-zone-active' : ''}`}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive(`row-${rowIndex}-insert-0`)\n }}\n onDragLeave={() => {\n setDropActive(null)\n }}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onRowDrop(rowIndex, 0)\n }}\n />\n <div\n className={`dc-row-edge-drop dc-row-edge-drop-right dc-split-handle${activeDropKey === `row-${rowIndex}-insert-${row.columns.length}` ? ' dc-drop-zone-active' : ''}`}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive(`row-${rowIndex}-insert-${row.columns.length}`)\n }}\n onDragLeave={() => {\n setDropActive(null)\n }}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onRowDrop(rowIndex, row.columns.length)\n }}\n />\n </>\n )}\n {canEdit && (\n <div\n className={`dc-row-resize-handle dc-split-handle${activeDropKey === `row-insert-${rowIndex + 1}` ? ' dc-drop-zone-active' : ''}`}\n onMouseDown={(event) => onRowResize(rowIndex, event)}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive(`row-insert-${rowIndex + 1}`)\n }}\n onDragLeave={() => setDropActive(null)}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onNewRowDrop(rowIndex + 1)\n }}\n />\n )}\n </div>\n )\n })}\n {canEdit && (\n <div\n className={`dc-row-boundary-drop dc-row-boundary-drop-bottom dc-split-handle${activeDropKey === 'dc:row-bottom' ? ' dc-drop-zone-active' : ''}`}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive('row-bottom')\n }}\n onDragLeave={() => setDropActive(null)}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onNewRowDrop(rows.length)\n }}\n />\n )}\n </div>\n )\n}\n","/**\n * Unified Color Palette System\n * Each palette contains coordinated series and gradient colors that work well together\n */\n\nexport interface ColorPalette {\n name: string\n label: string\n colors: string[] // For series-based charts (bar, line, pie, area, scatter, radar, etc.)\n gradient: string[] // For gradient-based charts (bubble, activity grid)\n}\n\n// Predefined color palettes with visually coordinated series and gradient colors\nexport const COLOR_PALETTES: ColorPalette[] = [\n {\n name: 'default',\n label: 'Default',\n colors: [\n '#3b82f6', // blue\n '#10b981', // green\n '#f59e0b', // yellow\n '#ef4444', // red\n '#8b5cf6', // purple\n '#f97316', // orange\n '#06b6d4', // cyan\n '#84cc16', // lime\n ],\n gradient: [\n '#fde725', // yellow (light - for low values)\n '#7ad151', // green\n '#22a884', // green-teal\n '#2a788e', // teal\n '#414487', // purple-blue\n '#440154', // dark purple (dark - for high values)\n ]\n },\n {\n name: 'ocean',\n label: 'Ocean',\n colors: [\n '#1e3a8a', // deep blue\n '#1e40af', // blue\n '#2563eb', // bright blue\n '#3b82f6', // light blue\n '#06b6d4', // cyan\n '#0891b2', // dark cyan\n '#0e7490', // teal\n '#0f766e', // dark teal\n ],\n gradient: [\n '#38bdf8', // cyan blue (light - for low values)\n '#0ea5e9', // light blue\n '#0284c7', // bright blue\n '#0369a1', // medium blue\n '#075985', // dark blue\n '#0c4a6e', // very dark blue (dark - for high values)\n ]\n },\n {\n name: 'sunset',\n label: 'Sunset',\n colors: [\n '#dc2626', // red\n '#ea580c', // orange-red\n '#f59e0b', // orange\n '#eab308', // yellow-orange\n '#d97706', // amber\n '#b45309', // dark amber\n '#92400e', // brown\n '#7c2d12', // dark brown\n ],\n gradient: [\n '#fbbf24', // light orange (light - for low values)\n '#f59e0b', // orange\n '#d97706', // amber\n '#b45309', // dark amber\n '#92400e', // brown\n '#7c2d12', // dark brown (dark - for high values)\n ]\n },\n {\n name: 'forest',\n label: 'Forest',\n colors: [\n '#166534', // dark green\n '#15803d', // green\n '#16a34a', // bright green\n '#22c55e', // light green\n '#4ade80', // lighter green\n '#65a30d', // lime green\n '#84cc16', // lime\n '#a3e635', // light lime\n ],\n gradient: [\n '#4ade80', // lighter green (light - for low values)\n '#22c55e', // light green\n '#16a34a', // bright green\n '#15803d', // green\n '#166534', // dark green\n '#14532d', // very dark green (dark - for high values)\n ]\n },\n {\n name: 'purple',\n label: 'Purple',\n colors: [\n '#581c87', // dark purple\n '#7c3aed', // purple\n '#8b5cf6', // bright purple\n '#a855f7', // light purple\n '#c084fc', // lighter purple\n '#e879f9', // magenta\n '#f0abfc', // light magenta\n '#fbbf24', // accent yellow\n ],\n gradient: [\n '#a855f7', // light purple (light - for low values)\n '#8b5cf6', // bright purple\n '#7c3aed', // purple\n '#6d28d9', // medium purple\n '#581c87', // dark purple\n '#4c1d95', // very dark purple (dark - for high values)\n ]\n },\n {\n name: 'monochrome',\n label: 'Monochrome',\n colors: [\n '#1f2937', // very dark gray\n '#374151', // dark gray\n '#4b5563', // medium gray\n '#6b7280', // gray\n '#9ca3af', // light gray\n '#d1d5db', // lighter gray\n '#e5e7eb', // very light gray\n '#f3f4f6', // almost white\n ],\n gradient: [\n '#9ca3af', // light gray (light - for low values)\n '#6b7280', // gray\n '#4b5563', // medium gray\n '#374151', // dark gray\n '#1f2937', // very dark gray\n '#111827', // black (dark - for high values)\n ]\n },\n {\n name: 'pastel',\n label: 'Pastel',\n colors: [\n '#93c5fd', // light blue\n '#86efac', // light green\n '#fde047', // light yellow\n '#fca5a5', // light red\n '#c4b5fd', // light purple\n '#fdba74', // light orange\n '#67e8f9', // light cyan\n '#bef264', // light lime\n ],\n gradient: [\n '#fed7aa', // very light orange (light - for low values)\n '#ddd6fe', // very light purple\n '#fecaca', // very light red\n '#fef08a', // very light yellow\n '#a7f3d0', // very light green\n '#bfdbfe', // very light blue (darker - for high values)\n ]\n },\n {\n name: 'vibrant',\n label: 'Vibrant',\n colors: [\n '#0000ff', // pure blue\n '#00ff00', // pure green\n '#ffff00', // pure yellow\n '#ff0000', // pure red\n '#ff00ff', // pure magenta\n '#ff8000', // pure orange\n '#00ffff', // pure cyan\n '#8000ff', // pure violet\n ],\n gradient: [\n '#ffff00', // yellow (light - for low values)\n '#80ff00', // lime\n '#00ff80', // green\n '#00ffff', // cyan\n '#0080ff', // blue\n '#4000ff', // blue-violet (dark - for high values)\n ]\n },\n {\n name: 'd3Category10',\n label: 'D3 Category 10',\n colors: [\n '#1f77b4', // blue\n '#ff7f0e', // orange\n '#2ca02c', // green\n '#d62728', // red\n '#9467bd', // purple\n '#8c564b', // brown\n '#e377c2', // pink\n '#7f7f7f', // gray\n '#bcbd22', // olive\n '#17becf', // cyan\n ],\n gradient: [\n '#9467bd', // purple (light - for low values)\n '#d62728', // red\n '#ff7f0e', // orange\n '#bcbd22', // olive\n '#2ca02c', // green\n '#1f77b4', // blue (dark - for high values)\n ]\n },\n {\n name: 'd3Tableau10',\n label: 'D3 Tableau 10',\n colors: [\n '#4e79a7', // blue\n '#f28e2c', // orange\n '#e15759', // red\n '#76b7b2', // teal\n '#59a14f', // green\n '#edc949', // yellow\n '#af7aa1', // purple\n '#ff9da7', // pink\n '#9c755f', // brown\n '#bab0ab', // gray\n ],\n gradient: [\n '#e15759', // red (light - for low values)\n '#f28e2c', // orange\n '#edc949', // yellow\n '#59a14f', // green\n '#76b7b2', // teal\n '#4e79a7', // blue (dark - for high values)\n ]\n },\n {\n name: 'colorBrewerSet1',\n label: 'ColorBrewer Set 1',\n colors: [\n '#e41a1c', // red\n '#377eb8', // blue\n '#4daf4a', // green\n '#984ea3', // purple\n '#ff7f00', // orange\n '#ffff33', // yellow\n '#a65628', // brown\n '#f781bf', // pink\n '#999999', // gray\n ],\n gradient: [\n '#984ea3', // purple (light - for low values)\n '#e41a1c', // red\n '#ff7f00', // orange\n '#ffff33', // yellow\n '#4daf4a', // green\n '#377eb8', // blue (dark - for high values)\n ]\n },\n {\n name: 'colorBrewerSet2',\n label: 'ColorBrewer Set 2',\n colors: [\n '#66c2a5', // teal\n '#fc8d62', // orange\n '#8da0cb', // blue\n '#e78ac3', // pink\n '#a6d854', // lime\n '#ffd92f', // yellow\n '#e5c494', // tan\n '#b3b3b3', // gray\n ],\n gradient: [\n '#e78ac3', // pink (light - for low values)\n '#fc8d62', // orange\n '#ffd92f', // yellow\n '#a6d854', // lime\n '#66c2a5', // teal\n '#8da0cb', // blue (dark - for high values)\n ]\n },\n {\n name: 'colorBrewerDark2',\n label: 'ColorBrewer Dark 2',\n colors: [\n '#1b9e77', // dark teal\n '#d95f02', // dark orange\n '#7570b3', // dark blue\n '#e7298a', // dark pink\n '#66a61e', // dark green\n '#e6ab02', // dark yellow\n '#a6761d', // dark brown\n '#666666', // dark gray\n ],\n gradient: [\n '#e7298a', // dark pink (light - for low values)\n '#d95f02', // dark orange\n '#e6ab02', // dark yellow\n '#66a61e', // dark green\n '#1b9e77', // dark teal\n '#7570b3', // dark blue (dark - for high values)\n ]\n },\n {\n name: 'colorBrewerPaired',\n label: 'ColorBrewer Paired',\n colors: [\n '#a6cee3', // light blue\n '#1f78b4', // blue\n '#b2df8a', // light green\n '#33a02c', // green\n '#fb9a99', // light red\n '#e31a1c', // red\n '#fdbf6f', // light orange\n '#ff7f00', // orange\n '#cab2d6', // light purple\n '#6a3d9a', // purple\n '#ffff99', // light yellow\n '#b15928', // brown\n ],\n gradient: [\n '#6a3d9a', // purple (light - for low values)\n '#e31a1c', // red\n '#ff7f00', // orange\n '#ffff99', // light yellow\n '#33a02c', // green\n '#1f78b4', // blue (dark - for high values)\n ]\n },\n {\n name: 'viridis',\n label: 'Viridis',\n colors: [\n '#440154', // dark purple\n '#482677', // purple\n '#3f4a8a', // blue-purple\n '#31678e', // blue\n '#26838f', // teal\n '#1f9d8a', // green-teal\n '#6cce5a', // green\n '#b6de2b', // yellow-green\n ],\n gradient: [\n '#b6de2b', // yellow-green (light - for low values)\n '#6cce5a', // green\n '#1f9d8a', // green-teal\n '#26838f', // teal\n '#31678e', // blue\n '#3f4a8a', // blue-purple\n '#482677', // purple\n '#440154', // dark purple (dark - for high values)\n ]\n },\n {\n name: 'plasma',\n label: 'Plasma',\n colors: [\n '#0c0786', // dark blue\n '#5c01a6', // purple\n '#900da4', // magenta\n '#bf3984', // pink\n '#e16462', // coral\n '#f99b45', // orange\n '#fcce25', // yellow\n '#f0f921', // bright yellow\n ],\n gradient: [\n '#f0f921', // bright yellow (light - for low values)\n '#fcce25', // yellow\n '#f99b45', // orange\n '#e16462', // coral\n '#bf3984', // pink\n '#900da4', // magenta\n '#5c01a6', // purple\n '#0c0786', // dark blue (dark - for high values)\n ]\n },\n {\n name: 'inferno',\n label: 'Inferno',\n colors: [\n '#000003', // black\n '#1f0c47', // dark blue\n '#550f6d', // purple\n '#88226a', // magenta\n '#a83655', // red\n '#cc4f39', // orange-red\n '#e6862a', // orange\n '#fec228', // yellow\n ],\n gradient: [\n '#fec228', // yellow (light - for low values)\n '#e6862a', // orange\n '#cc4f39', // orange-red\n '#a83655', // red\n '#88226a', // magenta\n '#550f6d', // purple\n '#1f0c47', // dark blue\n '#000003', // black (dark - for high values)\n ]\n },\n {\n name: 'magma',\n label: 'Magma',\n colors: [\n '#000003', // black\n '#140b34', // dark purple\n '#3b0f6f', // purple\n '#641a80', // magenta\n '#8b2981', // pink\n '#b63679', // coral\n '#de4968', // red\n '#fd9f6c', // orange\n ],\n gradient: [\n '#fd9f6c', // orange (light - for low values)\n '#de4968', // red\n '#b63679', // coral\n '#8b2981', // pink\n '#641a80', // magenta\n '#3b0f6f', // purple\n '#140b34', // dark purple\n '#000003', // black (dark - for high values)\n ]\n },\n {\n name: 'cividis',\n label: 'Cividis',\n colors: [\n '#00204c', // dark blue\n '#003f5c', // blue\n '#2c4b7a', // blue\n '#51576f', // blue-gray\n '#7f6874', // gray\n '#a8786e', // brown\n '#d2906d', // orange\n '#ffb570', // yellow\n ],\n gradient: [\n '#ffb570', // yellow (light - for low values)\n '#d2906d', // orange\n '#a8786e', // brown\n '#7f6874', // gray\n '#51576f', // blue-gray\n '#2c4b7a', // blue\n '#003f5c', // blue\n '#00204c', // dark blue (dark - for high values)\n ]\n },\n {\n name: 'turbo',\n label: 'Turbo',\n colors: [\n '#30123b', // purple\n '#4454c4', // blue\n '#1dd3c0', // cyan\n '#42f465', // green\n '#b2df22', // lime\n '#faba39', // yellow\n '#f66c19', // orange\n '#c42e02', // red\n ],\n gradient: [\n '#c42e02', // red (light - for low values)\n '#f66c19', // orange\n '#faba39', // yellow\n '#b2df22', // lime\n '#42f465', // green\n '#1dd3c0', // cyan\n '#4454c4', // blue\n '#30123b', // purple (dark - for high values)\n ]\n },\n {\n name: 'warm',\n label: 'Warm',\n colors: [\n '#8b0000', // dark red\n '#b22222', // red\n '#cd5c5c', // light red\n '#ff6347', // tomato\n '#ff8c00', // dark orange\n '#ffa500', // orange\n '#ffd700', // gold\n '#ffff00', // yellow\n ],\n gradient: [\n '#ffd700', // gold (light - for low values)\n '#ffa500', // orange\n '#ff8c00', // dark orange\n '#ff6347', // tomato\n '#b22222', // red\n '#8b0000', // dark red (dark - for high values)\n ]\n },\n {\n name: 'cool',\n label: 'Cool',\n colors: [\n '#000080', // navy\n '#0000ff', // blue\n '#4169e1', // royal blue\n '#00bfff', // deep sky blue\n '#00ffff', // cyan\n '#40e0d0', // turquoise\n '#20b2aa', // light sea green\n '#008b8b', // dark cyan\n ],\n gradient: [\n '#40e0d0', // turquoise (light - for low values)\n '#00ffff', // cyan\n '#00bfff', // deep sky blue\n '#4169e1', // royal blue\n '#0000ff', // blue\n '#000080', // navy (dark - for high values)\n ]\n },\n {\n name: 'earth',\n label: 'Earth',\n colors: [\n '#8b4513', // saddle brown\n '#a0522d', // sienna\n '#cd853f', // peru\n '#daa520', // goldenrod\n '#d2691e', // chocolate\n '#bc8f8f', // rosy brown\n '#f4a460', // sandy brown\n '#deb887', // burlywood\n ],\n gradient: [\n '#f4a460', // sandy brown (light - for low values)\n '#d2691e', // chocolate\n '#daa520', // goldenrod\n '#cd853f', // peru\n '#a0522d', // sienna\n '#8b4513', // saddle brown (dark - for high values)\n ]\n },\n {\n name: 'autumn',\n label: 'Autumn',\n colors: [\n '#8b0000', // dark red\n '#a0522d', // sienna\n '#cd853f', // peru\n '#daa520', // goldenrod\n '#ff8c00', // dark orange\n '#ff4500', // orange red\n '#dc143c', // crimson\n '#b22222', // fire brick\n ],\n gradient: [\n '#ff4500', // orange red (light - for low values)\n '#ff8c00', // dark orange\n '#daa520', // goldenrod\n '#cd853f', // peru\n '#a0522d', // sienna\n '#8b0000', // dark red (dark - for high values)\n ]\n },\n {\n name: 'spring',\n label: 'Spring',\n colors: [\n '#32cd32', // lime green\n '#98fb98', // pale green\n '#90ee90', // light green\n '#ffb6c1', // light pink\n '#ffc0cb', // pink\n '#ffffe0', // light yellow\n '#f0fff0', // honeydew\n '#e0ffff', // light cyan\n ],\n gradient: [\n '#e0ffff', // light cyan (light - for low values)\n '#ffc0cb', // pink\n '#ffb6c1', // light pink\n '#ffffe0', // light yellow\n '#98fb98', // pale green\n '#32cd32', // lime green (dark - for high values)\n ]\n },\n {\n name: 'winter',\n label: 'Winter',\n colors: [\n '#191970', // midnight blue\n '#4682b4', // steel blue\n '#87ceeb', // sky blue\n '#b0e0e6', // powder blue\n '#e0ffff', // light cyan\n '#f0f8ff', // alice blue\n '#c0c0c0', // silver\n '#708090', // slate gray\n ],\n gradient: [\n '#f0f8ff', // alice blue (light - for low values)\n '#e0ffff', // light cyan\n '#b0e0e6', // powder blue\n '#87ceeb', // sky blue\n '#4682b4', // steel blue\n '#191970', // midnight blue (dark - for high values)\n ]\n },\n {\n name: 'neon',\n label: 'Neon',\n colors: [\n '#ff0080', // neon pink\n '#00ff80', // neon green\n '#8000ff', // neon purple\n '#ff8000', // neon orange\n '#0080ff', // neon blue\n '#80ff00', // neon lime\n '#ff0040', // neon red\n '#40ff00', // bright green\n ],\n gradient: [\n '#ff0080', // neon pink (light - for low values)\n '#ff8000', // neon orange\n '#80ff00', // neon lime\n '#00ff80', // neon green\n '#0080ff', // neon blue\n '#8000ff', // neon purple (dark - for high values)\n ]\n },\n {\n name: 'retro',\n label: 'Retro',\n colors: [\n '#ff69b4', // hot pink\n '#ffd700', // gold\n '#32cd32', // lime green\n '#00ced1', // dark turquoise\n '#ff6347', // tomato\n '#9370db', // medium purple\n '#ffa500', // orange\n '#20b2aa', // light sea green\n ],\n gradient: [\n '#00ced1', // dark turquoise (light - for low values)\n '#32cd32', // lime green\n '#ffd700', // gold\n '#ff6347', // tomato\n '#ff69b4', // hot pink\n '#9370db', // medium purple (dark - for high values)\n ]\n },\n {\n name: 'corporate',\n label: 'Corporate',\n colors: [\n '#003366', // dark blue\n '#0066cc', // blue\n '#336699', // steel blue\n '#6699cc', // light blue\n '#4d4d4d', // dark gray\n '#808080', // gray\n '#b3b3b3', // light gray\n '#cccccc', // very light gray\n ],\n gradient: [\n '#b3b3b3', // light gray (light - for low values)\n '#808080', // gray\n '#6699cc', // light blue\n '#336699', // steel blue\n '#0066cc', // blue\n '#003366', // dark blue (dark - for high values)\n ]\n },\n {\n name: 'material',\n label: 'Material Design',\n colors: [\n '#f44336', // red\n '#e91e63', // pink\n '#9c27b0', // purple\n '#673ab7', // deep purple\n '#3f51b5', // indigo\n '#2196f3', // blue\n '#03a9f4', // light blue\n '#00bcd4', // cyan\n ],\n gradient: [\n '#e91e63', // pink (light - for low values)\n '#03a9f4', // light blue\n '#00bcd4', // cyan\n '#2196f3', // blue\n '#3f51b5', // indigo\n '#673ab7', // deep purple (dark - for high values)\n ]\n }\n]\n\n/**\n * Get a color palette by name, with fallback to default\n */\nexport function getColorPalette(paletteName?: string): ColorPalette {\n if (!paletteName) {\n return COLOR_PALETTES[0] // default palette\n }\n \n const palette = COLOR_PALETTES.find(p => p.name === paletteName)\n return palette || COLOR_PALETTES[0] // fallback to default\n}\n\n/**\n * Get just the series colors for a palette\n */\nexport function getSeriesColors(paletteName?: string): string[] {\n return getColorPalette(paletteName).colors\n}\n\n/**\n * Get just the gradient colors for a palette\n */\nexport function getGradientColors(paletteName?: string): string[] {\n return getColorPalette(paletteName).gradient\n}\n\n/**\n * Chart types that use series colors (discrete categories)\n */\nexport const SERIES_CHART_TYPES = [\n 'bar', 'line', 'area', 'pie', 'scatter', 'radar', 'radialBar', 'treeMap'\n] as const\n\n/**\n * Chart types that use gradient colors (continuous values)\n */\nexport const GRADIENT_CHART_TYPES = [\n 'bubble', 'activityGrid'\n] as const\n\n/**\n * Determine if a chart type uses gradient colors\n */\nexport function usesGradientColors(chartType: string): boolean {\n return GRADIENT_CHART_TYPES.includes(chartType as any)\n}","/**\n * FloatingEditToolbar - Vertical floating toolbar for dashboard editing\n *\n * Appears when the static edit bar scrolls out of view, providing quick access\n * to edit controls in a compact vertical format with icon-only buttons.\n *\n * Features:\n * - Icon-only buttons with tooltips\n * - Configurable left/right positioning\n * - Smooth slide-in/out animation\n * - Only visible on desktop (≥1200px)\n * - Compact palette dropdown that opens opposite to toolbar position\n */\n\nimport React, { useState, useRef, useEffect } from 'react'\nimport { createPortal } from 'react-dom'\nimport { getIcon } from '../icons'\nimport { COLOR_PALETTES } from '../utils/colorPalettes'\nimport type { DashboardLayoutMode } from '../types'\n\nconst EditIcon = getIcon('edit')\nconst CheckIcon = getIcon('check')\nconst GridIcon = getIcon('segment')\nconst RowsIcon = getIcon('table')\nconst AddIcon = getIcon('add')\nconst SwatchIcon = getIcon('swatch')\n\ninterface FloatingEditToolbarProps {\n /** Whether the static edit bar is visible (toolbar hidden when true) */\n isEditBarVisible: boolean\n /** Position of the floating toolbar */\n position: 'left' | 'right'\n /** Whether currently in edit mode */\n isEditMode: boolean\n /** Toggle edit mode on/off */\n onEditModeToggle: () => void\n /** Current layout mode */\n layoutMode: DashboardLayoutMode\n /** Change layout mode */\n onLayoutModeChange: (mode: DashboardLayoutMode) => void\n /** Available layout modes */\n allowedModes: DashboardLayoutMode[]\n /** Whether layout mode can be changed */\n canChangeLayoutMode: boolean\n /** Current color palette name */\n currentPalette: string\n /** Change color palette */\n onPaletteChange: (palette: string) => void\n /** Add new portlet */\n onAddPortlet: () => void\n}\n\nexport default function FloatingEditToolbar({\n isEditBarVisible,\n position,\n isEditMode,\n onEditModeToggle,\n layoutMode,\n onLayoutModeChange,\n allowedModes,\n canChangeLayoutMode,\n currentPalette,\n onPaletteChange,\n onAddPortlet\n}: FloatingEditToolbarProps) {\n const [isPaletteOpen, setIsPaletteOpen] = useState(false)\n const paletteRef = useRef<HTMLDivElement>(null)\n\n // Close palette dropdown on outside click\n useEffect(() => {\n if (!isPaletteOpen) return\n\n const handleClickOutside = (e: MouseEvent) => {\n if (paletteRef.current && !paletteRef.current.contains(e.target as Node)) {\n setIsPaletteOpen(false)\n }\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [isPaletteOpen])\n\n // Close palette when toolbar hides\n useEffect(() => {\n if (isEditBarVisible) {\n setIsPaletteOpen(false)\n }\n }, [isEditBarVisible])\n\n // Position classes - fixed positioning with vertical centering\n const positionClasses = position === 'left'\n ? 'dc:left-4'\n : 'dc:right-4'\n\n // Animation: slide in from edge when edit bar not visible\n // When edit bar IS visible, slide out and hide\n const isHidden = isEditBarVisible\n const translateClasses = position === 'left'\n ? (isHidden ? 'dc:-translate-x-16 dc:opacity-0 dc:pointer-events-none' : 'dc:translate-x-0 dc:opacity-100')\n : (isHidden ? 'dc:translate-x-16 dc:opacity-0 dc:pointer-events-none' : 'dc:translate-x-0 dc:opacity-100')\n\n // Use portal to render outside the grid container to avoid react-grid-layout issues\n const toolbarContent = (\n <div\n className={`dc:fixed dc:top-1/2 dc:-translate-y-1/2 dc:z-50 dc:flex dc:flex-col dc:gap-1.5 dc:p-2\n bg-dc-surface-tertiary dc:border border-dc-border dc:rounded-lg\n dc:transition-all dc:duration-300 dc:ease-in-out\n ${positionClasses} ${translateClasses}`}\n style={{\n boxShadow: 'var(--dc-shadow-lg)'\n }}\n >\n {/* Edit Toggle */}\n <ToolbarButton\n icon={isEditMode ? CheckIcon : EditIcon}\n tooltip={isEditMode ? 'Finish Editing' : 'Edit Dashboard'}\n isActive={isEditMode}\n onClick={onEditModeToggle}\n />\n\n {/* Layout Mode Switcher - only in edit mode with multiple modes */}\n {isEditMode && allowedModes.length > 1 && (\n <>\n <div className=\"dc:w-full dc:h-px bg-dc-border dc:my-0.5\" />\n <ToolbarButton\n icon={GridIcon}\n tooltip=\"Grid Layout\"\n isActive={layoutMode === 'grid'}\n disabled={!canChangeLayoutMode}\n onClick={() => onLayoutModeChange('grid')}\n />\n <ToolbarButton\n icon={RowsIcon}\n tooltip=\"Rows Layout\"\n isActive={layoutMode === 'rows'}\n disabled={!canChangeLayoutMode}\n onClick={() => onLayoutModeChange('rows')}\n />\n </>\n )}\n\n {/* Color Palette - only in edit mode */}\n {isEditMode && (\n <>\n <div className=\"dc:w-full dc:h-px bg-dc-border dc:my-0.5\" />\n <div ref={paletteRef} className=\"dc:relative\">\n <ToolbarButton\n icon={SwatchIcon}\n tooltip=\"Color Palette\"\n isActive={isPaletteOpen}\n onClick={() => setIsPaletteOpen(!isPaletteOpen)}\n />\n {isPaletteOpen && (\n <CompactPaletteDropdown\n position={position}\n currentPalette={currentPalette}\n onPaletteChange={(palette) => {\n onPaletteChange(palette)\n setIsPaletteOpen(false)\n }}\n />\n )}\n </div>\n </>\n )}\n\n {/* Add Portlet - only in edit mode */}\n {isEditMode && (\n <>\n <div className=\"dc:w-full dc:h-px bg-dc-border dc:my-0.5\" />\n <ToolbarButton\n icon={AddIcon}\n tooltip=\"Add Portlet\"\n onClick={onAddPortlet}\n />\n </>\n )}\n </div>\n )\n\n // Render via portal to document.body to avoid react-grid-layout child iteration issues\n if (typeof document === 'undefined') {\n return null // SSR safety\n }\n\n return createPortal(toolbarContent, document.body)\n}\n\n// Internal ToolbarButton component\ninterface ToolbarButtonProps {\n icon: React.ComponentType<{ className?: string }>\n tooltip: string\n isActive?: boolean\n disabled?: boolean\n onClick: () => void\n}\n\nfunction ToolbarButton({ icon: Icon, tooltip, isActive, disabled, onClick }: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n title={tooltip}\n className={`dc:p-2 dc:rounded-md dc:transition-colors focus:outline-hidden dc:focus:ring-2 focus:ring-dc-accent\n ${disabled\n ? 'dc:opacity-50 dc:cursor-not-allowed bg-dc-surface-secondary text-dc-text-muted'\n : isActive\n ? 'bg-dc-accent-bg text-dc-accent'\n : 'bg-dc-surface text-dc-text-secondary hover:bg-dc-surface-hover'\n }`}\n >\n <Icon className=\"dc:w-5 dc:h-5\" />\n </button>\n )\n}\n\n// Compact palette dropdown for floating toolbar\ninterface CompactPaletteDropdownProps {\n position: 'left' | 'right'\n currentPalette: string\n onPaletteChange: (palette: string) => void\n}\n\nfunction CompactPaletteDropdown({ position, currentPalette, onPaletteChange }: CompactPaletteDropdownProps) {\n // Position dropdown opposite to toolbar position to avoid off-screen\n const positionClasses = position === 'left' ? 'dc:left-full dc:ml-2' : 'dc:right-full dc:mr-2'\n\n return (\n <div\n className={`dc:absolute dc:top-0 ${positionClasses} dc:w-52 bg-dc-surface dc:border border-dc-border dc:rounded-md dc:z-50 dc:max-h-72 dc:overflow-y-auto`}\n style={{\n boxShadow: 'var(--dc-shadow-lg)'\n }}\n >\n <div className=\"dc:py-1\">\n {COLOR_PALETTES.slice().sort((a, b) => a.label.localeCompare(b.label)).map((palette) => (\n <button\n key={palette.name}\n type=\"button\"\n onClick={() => onPaletteChange(palette.name)}\n className={`dc:w-full dc:px-3 dc:py-2 text-left dc:text-sm hover:bg-dc-surface-hover dc:transition-colors ${\n palette.name === currentPalette ? 'bg-dc-surface-secondary text-dc-primary' : 'text-dc-text-secondary'\n }`}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n {/* Compact color preview - 4 series colors */}\n <div className=\"dc:flex dc:gap-0.5 dc:shrink-0\">\n {palette.colors.slice(0, 4).map((color, index) => (\n <div\n key={index}\n className=\"dc:w-2.5 dc:h-2.5 rounded-xs dc:border border-dc-border\"\n style={{ backgroundColor: color }}\n />\n ))}\n </div>\n <span className=\"dc:text-xs dc:font-medium dc:truncate text-dc-text\">{palette.label}</span>\n {/* Current indicator */}\n {palette.name === currentPalette && (\n <div className=\"dc:ml-auto dc:w-1.5 dc:h-1.5 dc:rounded-full dc:shrink-0 bg-dc-primary\" />\n )}\n </div>\n </button>\n ))}\n </div>\n </div>\n )\n}\n","import React, { useEffect, useCallback } from 'react'\n\nexport interface ModalProps {\n isOpen: boolean\n onClose: () => void\n title?: string\n size?: 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'full' | 'fullscreen' | 'fullscreen-mobile'\n closeOnBackdropClick?: boolean\n closeOnEscape?: boolean\n showCloseButton?: boolean\n className?: string\n children: React.ReactNode\n footer?: React.ReactNode\n noPadding?: boolean\n}\n\nconst Modal: React.FC<ModalProps> = ({\n isOpen,\n onClose,\n title,\n size = 'md',\n closeOnBackdropClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n children,\n footer,\n noPadding = false\n}) => {\n // Handle ESC key press\n const handleEscapeKey = useCallback((event: KeyboardEvent) => {\n if (event.key === 'Escape' && closeOnEscape) {\n onClose()\n }\n }, [closeOnEscape, onClose])\n\n\n // Manage ESC key listener and body scroll\n useEffect(() => {\n if (isOpen) {\n // Add ESC key listener\n if (closeOnEscape) {\n document.addEventListener('keydown', handleEscapeKey)\n }\n\n // Prevent body scroll when modal is open\n document.body.style.overflow = 'hidden'\n } else {\n // Restore body scroll\n document.body.style.overflow = 'unset'\n }\n\n // Cleanup\n return () => {\n document.removeEventListener('keydown', handleEscapeKey)\n document.body.style.overflow = 'unset'\n }\n }, [isOpen, closeOnEscape, handleEscapeKey])\n\n\n if (!isOpen) return null\n\n const getSizeClasses = () => {\n switch (size) {\n case 'sm':\n return 'dc:max-w-md'\n case 'md':\n return 'dc:max-w-lg'\n case 'lg':\n return 'dc:max-w-2xl'\n case 'xl':\n return 'dc:max-w-6xl'\n case 'xxl':\n return 'dc:max-w-[1400px]' // Good for retina/mac displays\n case 'full':\n return 'dc:max-w-7xl'\n case 'fullscreen':\n return 'dc:w-[90vw] dc:h-[90vh] dc:max-w-none'\n case 'fullscreen-mobile':\n return 'dc:w-full dc:h-full dc:md:w-[min(90vw,1400px)] dc:md:h-[90vh]'\n default:\n return 'dc:max-w-lg'\n }\n }\n\n return (\n <div\n className={`dc:fixed dc:inset-0 dc:z-50 dc:backdrop-blur-md ${size === 'fullscreen-mobile' ? 'dc:flex dc:md:flex dc:md:items-center dc:md:justify-center' : 'dc:flex dc:items-center dc:justify-center'}`}\n style={{ backgroundColor: 'var(--dc-overlay)' }}\n onClick={closeOnBackdropClick ? onClose : undefined}\n >\n <div\n className={`dc:relative bg-dc-surface dc:border border-dc-border ${size === 'fullscreen-mobile' ? 'dc:rounded-none dc:md:rounded-lg' : 'dc:rounded-lg'} ${size === 'fullscreen' || size === 'fullscreen-mobile' ? '' : 'dc:mx-4'} ${getSizeClasses()} ${size === 'fullscreen' || size === 'fullscreen-mobile' ? '' : 'dc:max-h-[90vh]'} dc:flex dc:flex-col`}\n style={{ boxShadow: 'var(--dc-shadow-2xl)' }}\n onClick={(e) => e.stopPropagation()}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'modal-title' : undefined}\n >\n {/* Header */}\n {(title || showCloseButton) && (\n <div className=\"dc:flex dc:items-center dc:justify-between dc:px-6 dc:py-4 dc:border-b border-dc-border\">\n {title && (\n <h2 id=\"modal-title\" className=\"dc:text-xl dc:font-semibold text-dc-text\">\n {title}\n </h2>\n )}\n {showCloseButton && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"text-dc-text-muted hover:text-dc-text-secondary dc:transition-colors dc:p-2 dc:-mr-2\"\n aria-label=\"Close modal\"\n >\n <svg width=\"24\" height=\"24\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </div>\n )}\n\n {/* Content */}\n <div className={`dc:flex-1 dc:overflow-y-auto ${noPadding ? '' : 'dc:px-6 dc:py-4'}`}>\n {children}\n </div>\n\n {/* Footer */}\n {footer && (\n <div className=\"dc:flex dc:items-center dc:justify-end dc:space-x-3 dc:px-6 dc:py-4 dc:border-t border-dc-border bg-dc-surface-secondary\">\n {React.Children.toArray(footer)}\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default Modal","import { forwardRef, lazy, Suspense } from 'react'\nimport type { ForwardRefExoticComponent, RefAttributes } from 'react'\nimport type { AnalysisBuilderProps, AnalysisBuilderRef } from './AnalysisBuilder/types'\nimport LoadingIndicator from './LoadingIndicator'\n\nconst LazyAnalysisBuilder = lazy(() => import('./AnalysisBuilder')) as ForwardRefExoticComponent<\n AnalysisBuilderProps & RefAttributes<AnalysisBuilderRef>\n>\n\nconst AnalysisBuilder = forwardRef<AnalysisBuilderRef, AnalysisBuilderProps>((props, ref) => (\n <Suspense\n fallback={\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:py-6\">\n <LoadingIndicator />\n </div>\n }\n >\n <LazyAnalysisBuilder {...props} ref={ref} />\n </Suspense>\n))\n\nAnalysisBuilder.displayName = 'AnalysisBuilder'\n\nexport default AnalysisBuilder\n","/**\n * ID Generation Utilities for AnalysisBuilder\n */\n\n/**\n * Generate a unique ID for items\n */\nexport function generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`\n}\n\n/**\n * Generate letter label for metrics (A, B, C, ..., AA, AB, ...)\n */\nexport function generateMetricLabel(index: number): string {\n let label = ''\n let n = index\n do {\n label = String.fromCharCode(65 + (n % 26)) + label\n n = Math.floor(n / 26) - 1\n } while (n >= 0)\n return label\n}\n","/**\n * Field Metadata Utilities for AnalysisBuilder\n *\n * Functions for working with field metadata from the schema.\n */\n\nimport type { FieldOption, FieldType } from '../types'\nimport type { MetaResponse, MetaField } from '../../../shared/types'\n\n/**\n * Get cube name from a field name (e.g., \"Employees.count\" -> \"Employees\")\n */\nexport function getCubeNameFromField(fieldName: string): string {\n return fieldName.split('.')[0]\n}\n\n/**\n * Get field short name from full name (e.g., \"Employees.count\" -> \"count\")\n */\nexport function getFieldShortName(fieldName: string): string {\n const parts = fieldName.split('.')\n return parts.length > 1 ? parts.slice(1).join('.') : fieldName\n}\n\n/**\n * Find field metadata from schema\n */\nexport function findFieldInSchema(\n fieldName: string,\n schema: MetaResponse | null\n): { field: MetaField; cubeName: string; fieldType: FieldType } | null {\n if (!schema) return null\n\n for (const cube of schema.cubes) {\n // Check measures\n const measure = cube.measures.find((m) => m.name === fieldName)\n if (measure) {\n return { field: measure, cubeName: cube.name, fieldType: 'measure' }\n }\n\n // Check dimensions\n const dimension = cube.dimensions.find((d) => d.name === fieldName)\n if (dimension) {\n return {\n field: dimension,\n cubeName: cube.name,\n fieldType: dimension.type === 'time' ? 'timeDimension' : 'dimension'\n }\n }\n }\n\n return null\n}\n\n/**\n * Get display title for a field\n */\nexport function getFieldTitle(fieldName: string, schema: MetaResponse | null): string {\n const found = findFieldInSchema(fieldName, schema)\n if (found) {\n return found.field.title || found.field.shortTitle || fieldName\n }\n return fieldName\n}\n\n/**\n * Determine field type from metadata\n */\nexport function getFieldType(field: MetaField): FieldType {\n if (field.type === 'time') return 'timeDimension'\n // Measures typically have aggregation types\n if (['count', 'countDistinct', 'sum', 'avg', 'min', 'max', 'runningTotal', 'countDistinctApprox'].includes(field.type)) {\n return 'measure'\n }\n return 'dimension'\n}\n\n/**\n * Convert schema to flat list of field options\n */\nexport function schemaToFieldOptions(\n schema: MetaResponse | null,\n mode: 'metrics' | 'breakdown' | 'filter' | 'dimensionFilter'\n): FieldOption[] {\n if (!schema) return []\n\n const options: FieldOption[] = []\n\n for (const cube of schema.cubes) {\n if (mode === 'metrics') {\n // Add measures only\n for (const measure of cube.measures) {\n options.push({\n name: measure.name,\n title: measure.title || measure.shortTitle || measure.name,\n shortTitle: measure.shortTitle || measure.title || measure.name,\n type: measure.type,\n description: measure.description,\n cubeName: cube.name,\n fieldType: 'measure'\n })\n }\n } else if (mode === 'breakdown' || mode === 'dimensionFilter') {\n // Add dimensions only (both regular and time)\n // 'dimensionFilter' is used for funnel step filters where measures don't work\n for (const dimension of cube.dimensions) {\n const isTime = dimension.type === 'time'\n options.push({\n name: dimension.name,\n title: dimension.title || dimension.shortTitle || dimension.name,\n shortTitle: dimension.shortTitle || dimension.title || dimension.name,\n type: dimension.type,\n description: dimension.description,\n cubeName: cube.name,\n fieldType: isTime ? 'timeDimension' : 'dimension'\n })\n }\n } else {\n // 'filter' mode - add BOTH measures AND dimensions\n // Add measures first\n for (const measure of cube.measures) {\n options.push({\n name: measure.name,\n title: measure.title || measure.shortTitle || measure.name,\n shortTitle: measure.shortTitle || measure.title || measure.name,\n type: measure.type,\n description: measure.description,\n cubeName: cube.name,\n fieldType: 'measure'\n })\n }\n // Add dimensions (both regular and time)\n for (const dimension of cube.dimensions) {\n const isTime = dimension.type === 'time'\n options.push({\n name: dimension.name,\n title: dimension.title || dimension.shortTitle || dimension.name,\n shortTitle: dimension.shortTitle || dimension.title || dimension.name,\n type: dimension.type,\n description: dimension.description,\n cubeName: cube.name,\n fieldType: isTime ? 'timeDimension' : 'dimension'\n })\n }\n }\n }\n\n return options\n}\n\n/**\n * Filter field options by search term\n */\nexport function filterFieldOptions(\n options: FieldOption[],\n searchTerm: string,\n selectedCube?: string | null\n): FieldOption[] {\n let filtered = options\n\n // Filter by cube if selected\n if (selectedCube && selectedCube !== 'all') {\n filtered = filtered.filter((opt) => opt.cubeName === selectedCube)\n }\n\n // Filter by search term\n if (searchTerm.trim()) {\n const term = searchTerm.toLowerCase()\n filtered = filtered.filter(\n (opt) =>\n opt.name.toLowerCase().includes(term) ||\n opt.title.toLowerCase().includes(term) ||\n (opt.description?.toLowerCase().includes(term) ?? false)\n )\n }\n\n return filtered\n}\n\n/**\n * Group field options by cube\n */\nexport function groupFieldsByCube(options: FieldOption[]): Map<string, FieldOption[]> {\n const grouped = new Map<string, FieldOption[]>()\n\n for (const option of options) {\n const existing = grouped.get(option.cubeName) || []\n existing.push(option)\n grouped.set(option.cubeName, existing)\n }\n\n return grouped\n}\n\n/**\n * Get list of cube names from schema\n */\nexport function getCubeNames(schema: MetaResponse | null): string[] {\n if (!schema) return []\n return schema.cubes.map((cube) => cube.name)\n}\n\n/**\n * Get cube title by name\n */\nexport function getCubeTitle(cubeName: string, schema: MetaResponse | null): string {\n if (!schema) return cubeName\n const cube = schema.cubes.find((c) => c.name === cubeName)\n return cube?.title || cubeName\n}\n\n/**\n * Get all cubes reachable from a source cube via join relationships\n * Includes the source cube itself plus all cubes it has joins to\n *\n * @param sourceCube - Name of the cube to find related cubes for\n * @param schema - Full schema with all cubes\n * @returns Set of cube names that are reachable from the source\n */\nexport function getRelatedCubeNames(\n sourceCube: string,\n schema: MetaResponse | null\n): Set<string> {\n const related = new Set<string>()\n\n if (!schema) return related\n\n // Always include the source cube\n related.add(sourceCube)\n\n // Find the source cube and get its relationships\n const cube = schema.cubes.find((c) => c.name === sourceCube)\n if (!cube || !cube.relationships) return related\n\n // Add all directly related cubes\n for (const rel of cube.relationships) {\n related.add(rel.targetCube)\n }\n\n return related\n}\n\n/**\n * Filter schema to include only cubes reachable from a source cube\n * This is used for funnel step filters where cross-cube filtering is supported\n *\n * @param sourceCube - Name of the cube to find related cubes for\n * @param schema - Full schema with all cubes\n * @returns Filtered schema containing only reachable cubes\n */\nexport function getRelatedCubesSchema(\n sourceCube: string,\n schema: MetaResponse | null\n): MetaResponse | null {\n if (!schema) return null\n\n const relatedNames = getRelatedCubeNames(sourceCube, schema)\n\n return {\n cubes: schema.cubes\n .filter((c) => relatedNames.has(c.name))\n .map((c) => ({\n ...c,\n description: c.description || '',\n })),\n }\n}\n","/**\n * Recent Fields Storage Utilities for AnalysisBuilder\n *\n * Functions for tracking and retrieving recently used fields.\n */\n\nimport type { FieldOption, RecentFieldsStorage } from '../types'\nimport type { MetaResponse } from '../../../shared/types'\nimport { schemaToFieldOptions } from './fieldUtils'\n\nconst RECENT_FIELDS_KEY = 'drizzle-cube-recent-fields'\nconst MAX_RECENT_FIELDS = 10\n\n/**\n * Get recent fields from localStorage\n */\nexport function getRecentFields(): RecentFieldsStorage {\n try {\n const stored = localStorage.getItem(RECENT_FIELDS_KEY)\n if (stored) {\n return JSON.parse(stored)\n }\n } catch {\n // Ignore errors\n }\n return { metrics: [], breakdowns: [] }\n}\n\n/**\n * Add a field to recent fields\n */\nexport function addRecentField(fieldName: string, mode: 'metrics' | 'breakdowns'): void {\n try {\n const recent = getRecentFields()\n const list = recent[mode]\n\n // Remove if already exists\n const filtered = list.filter((f) => f !== fieldName)\n\n // Add to front\n filtered.unshift(fieldName)\n\n // Limit size\n recent[mode] = filtered.slice(0, MAX_RECENT_FIELDS)\n\n localStorage.setItem(RECENT_FIELDS_KEY, JSON.stringify(recent))\n } catch {\n // Ignore errors\n }\n}\n\n/**\n * Get recent field options from schema\n */\nexport function getRecentFieldOptions(\n schema: MetaResponse | null,\n mode: 'metrics' | 'breakdown' | 'filter' | 'dimensionFilter',\n recentFieldNames: string[]\n): FieldOption[] {\n if (!schema || recentFieldNames.length === 0) return []\n\n const allOptions = schemaToFieldOptions(schema, mode)\n const recentOptions: FieldOption[] = []\n\n for (const fieldName of recentFieldNames) {\n const option = allOptions.find((opt) => opt.name === fieldName)\n if (option) {\n recentOptions.push(option)\n }\n }\n\n return recentOptions\n}\n","/**\n * Funnel Mode Adapter\n *\n * Handles conversion between UI state and AnalysisConfig for funnel mode.\n * Converts funnelSteps UI state to/from ServerFunnelQuery format.\n */\n\nimport type { ModeAdapter, ValidationResult } from './modeAdapter'\nimport { generateId } from '../components/AnalysisBuilder/utils'\nimport type {\n AnalysisConfig,\n FunnelAnalysisConfig,\n AnalysisType,\n ChartConfig,\n} from '../types/analysisConfig'\nimport type { FunnelStepState, FunnelBindingKey, Filter } from '../types'\nimport type { ServerFunnelQuery, ServerFunnelStep } from '../types/funnel'\n\n// ============================================================================\n// Funnel Slice State Type\n// ============================================================================\n\n/**\n * The shape of funnel mode state in the store.\n * This is what the adapter's load() returns and save() receives.\n */\nexport interface FunnelSliceState {\n /** The cube all funnel steps use (single-cube mode) */\n funnelCube: string | null\n /** Funnel step definitions */\n funnelSteps: FunnelStepState[]\n /** Currently selected step index */\n activeFunnelStepIndex: number\n /** Time dimension for temporal ordering */\n funnelTimeDimension: string | null\n /** Binding key that links entities across steps */\n funnelBindingKey: FunnelBindingKey | null\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Convert FunnelSliceState to ServerFunnelQuery\n */\nfunction stateToServerQuery(state: FunnelSliceState): ServerFunnelQuery {\n // Convert binding key to server format\n let bindingKey: ServerFunnelQuery['funnel']['bindingKey'] = ''\n if (state.funnelBindingKey) {\n if (typeof state.funnelBindingKey.dimension === 'string') {\n bindingKey = state.funnelBindingKey.dimension\n } else if (Array.isArray(state.funnelBindingKey.dimension)) {\n bindingKey = state.funnelBindingKey.dimension.map((mapping) => ({\n cube: mapping.cube,\n dimension: mapping.dimension,\n }))\n }\n }\n\n // Convert time dimension to server format\n let timeDimension: ServerFunnelQuery['funnel']['timeDimension'] =\n state.funnelTimeDimension || ''\n\n // Convert steps to server format\n const steps: ServerFunnelStep[] = state.funnelSteps.map((step) => {\n const serverStep: ServerFunnelStep = {\n name: step.name,\n }\n\n // Only include cube if different from default (multi-cube support)\n if (step.cube && step.cube !== state.funnelCube) {\n serverStep.cube = step.cube\n }\n\n // Include filters if present\n if (step.filters && step.filters.length > 0) {\n // Convert to server filter format\n serverStep.filter =\n step.filters.length === 1\n ? step.filters[0]\n : { and: step.filters }\n }\n\n // Include timeToConvert if present\n if (step.timeToConvert) {\n serverStep.timeToConvert = step.timeToConvert\n }\n\n return serverStep\n })\n\n return {\n funnel: {\n bindingKey,\n timeDimension,\n steps,\n includeTimeMetrics: true,\n },\n }\n}\n\n/**\n * Convert ServerFunnelQuery to FunnelSliceState\n */\nfunction serverQueryToState(query: ServerFunnelQuery): FunnelSliceState {\n const { funnel } = query\n\n // Extract cube from first step or binding key\n let funnelCube: string | null = null\n if (funnel.steps.length > 0 && funnel.steps[0].cube) {\n funnelCube = funnel.steps[0].cube\n } else if (typeof funnel.bindingKey === 'string') {\n // Extract cube from binding key (e.g., \"Events.userId\" -> \"Events\")\n const parts = funnel.bindingKey.split('.')\n if (parts.length > 0) {\n funnelCube = parts[0]\n }\n }\n\n // Convert binding key to client format\n let funnelBindingKey: FunnelBindingKey | null = null\n if (funnel.bindingKey) {\n if (typeof funnel.bindingKey === 'string') {\n funnelBindingKey = { dimension: funnel.bindingKey }\n } else if (Array.isArray(funnel.bindingKey)) {\n funnelBindingKey = {\n dimension: funnel.bindingKey.map((mapping) => ({\n cube: mapping.cube,\n dimension: mapping.dimension,\n })),\n }\n }\n }\n\n // Convert time dimension\n let funnelTimeDimension: string | null = null\n if (funnel.timeDimension) {\n if (typeof funnel.timeDimension === 'string') {\n funnelTimeDimension = funnel.timeDimension\n } else if (Array.isArray(funnel.timeDimension) && funnel.timeDimension.length > 0) {\n funnelTimeDimension = `${funnel.timeDimension[0].cube}.${funnel.timeDimension[0].dimension}`\n }\n }\n\n // Convert steps\n const funnelSteps: FunnelStepState[] = funnel.steps.map((step) => {\n // Extract filters\n let filters: Filter[] = []\n if (step.filter) {\n if (Array.isArray(step.filter)) {\n // Already an array of filters\n filters = step.filter as Filter[]\n } else if (\n typeof step.filter === 'object' &&\n 'and' in (step.filter as { and?: unknown })\n ) {\n // { and: [...] } format\n filters = (step.filter as { and: Filter[] }).and\n } else {\n // Single filter object - wrap in array\n filters = [step.filter as Filter]\n }\n }\n\n return {\n id: generateId(),\n name: step.name,\n cube: step.cube || funnelCube || '',\n filters,\n timeToConvert: step.timeToConvert,\n }\n })\n\n return {\n funnelCube,\n funnelSteps,\n activeFunnelStepIndex: 0,\n funnelTimeDimension,\n funnelBindingKey,\n }\n}\n\n/**\n * Check if a config is a valid funnel config\n */\nfunction isValidFunnelConfig(config: unknown): config is FunnelAnalysisConfig {\n if (!config || typeof config !== 'object') return false\n\n const c = config as Record<string, unknown>\n\n if (c.version !== 1) return false\n if (c.analysisType !== 'funnel') return false\n if (!c.query || typeof c.query !== 'object') return false\n\n const query = c.query as Record<string, unknown>\n if (!query.funnel || typeof query.funnel !== 'object') return false\n\n return true\n}\n\n// ============================================================================\n// Funnel Mode Adapter\n// ============================================================================\n\nexport const funnelModeAdapter: ModeAdapter<FunnelSliceState> = {\n type: 'funnel',\n\n createInitial(): FunnelSliceState {\n return {\n funnelCube: null,\n funnelSteps: [],\n activeFunnelStepIndex: 0,\n funnelTimeDimension: null,\n funnelBindingKey: null,\n }\n },\n\n extractState(storeState: Record<string, unknown>): FunnelSliceState {\n return {\n funnelCube: storeState.funnelCube as string | null,\n funnelSteps: storeState.funnelSteps as FunnelStepState[],\n activeFunnelStepIndex: storeState.activeFunnelStepIndex as number,\n funnelTimeDimension: storeState.funnelTimeDimension as string | null,\n funnelBindingKey: storeState.funnelBindingKey as FunnelBindingKey | null,\n }\n },\n\n canLoad(config: unknown): config is AnalysisConfig {\n return isValidFunnelConfig(config)\n },\n\n load(config: AnalysisConfig): FunnelSliceState {\n // Type guard - ensure it's a funnel config\n if (config.analysisType !== 'funnel') {\n throw new Error(\n `Cannot load ${config.analysisType} config with funnel adapter`\n )\n }\n\n const funnelConfig = config as FunnelAnalysisConfig\n return serverQueryToState(funnelConfig.query)\n },\n\n save(\n state: FunnelSliceState,\n charts: Partial<Record<AnalysisType, ChartConfig>>,\n activeView: 'table' | 'chart'\n ): FunnelAnalysisConfig {\n return {\n version: 1,\n analysisType: 'funnel',\n activeView,\n charts: {\n funnel: charts.funnel || this.getDefaultChartConfig(),\n },\n query: stateToServerQuery(state),\n }\n },\n\n validate(state: FunnelSliceState): ValidationResult {\n const errors: string[] = []\n const warnings: string[] = []\n\n // Must have at least 2 steps for a funnel\n if (state.funnelSteps.length < 2) {\n errors.push('A funnel requires at least 2 steps')\n }\n\n // Must have a binding key\n if (!state.funnelBindingKey?.dimension) {\n errors.push('A binding key is required to link funnel steps')\n }\n\n // Must have a time dimension\n if (!state.funnelTimeDimension) {\n errors.push('A time dimension is required for funnel ordering')\n }\n\n // Check each step\n state.funnelSteps.forEach((step, index) => {\n if (!step.name || step.name.trim() === '') {\n warnings.push(`Step ${index + 1} has no name`)\n }\n\n // Warn if step has no distinguishing filter\n if (step.filters.length === 0) {\n warnings.push(\n `Step ${index + 1} \"${step.name}\" has no filter - all events will match`\n )\n }\n })\n\n // Check for duplicate step names\n const names = state.funnelSteps.map((s) => s.name.toLowerCase())\n const duplicates = names.filter(\n (name, index) => names.indexOf(name) !== index\n )\n if (duplicates.length > 0) {\n warnings.push(`Duplicate step names: ${[...new Set(duplicates)].join(', ')}`)\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings,\n }\n },\n\n clear(state: FunnelSliceState): FunnelSliceState {\n // Keep cube selection but clear steps\n return {\n ...this.createInitial(),\n funnelCube: state.funnelCube,\n }\n },\n\n getDefaultChartConfig(): ChartConfig {\n return {\n chartType: 'funnel',\n chartConfig: {},\n displayConfig: { showLegend: true, showGrid: true, showTooltip: true },\n }\n },\n}\n","/**\n * Flow Mode Adapter\n *\n * Handles conversion between UI state and AnalysisConfig for flow mode.\n * Converts FlowSliceState UI state to/from ServerFlowQuery format.\n */\n\nimport type { ModeAdapter, ValidationResult } from './modeAdapter'\nimport type {\n AnalysisConfig,\n FlowAnalysisConfig,\n AnalysisType,\n ChartConfig,\n} from '../types/analysisConfig'\nimport type { Filter, FunnelBindingKey } from '../types'\nimport type {\n FlowSliceState,\n ServerFlowQuery,\n FlowStartingStep,\n} from '../types/flow'\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Convert FlowSliceState to ServerFlowQuery\n */\nfunction stateToServerQuery(state: FlowSliceState): ServerFlowQuery {\n // Convert binding key to server format\n let bindingKey: ServerFlowQuery['flow']['bindingKey'] = ''\n if (state.flowBindingKey) {\n if (typeof state.flowBindingKey.dimension === 'string') {\n bindingKey = state.flowBindingKey.dimension\n } else if (Array.isArray(state.flowBindingKey.dimension)) {\n bindingKey = state.flowBindingKey.dimension.map((mapping) => ({\n cube: mapping.cube,\n dimension: mapping.dimension,\n }))\n }\n }\n\n // Convert time dimension to server format\n const timeDimension: ServerFlowQuery['flow']['timeDimension'] =\n state.flowTimeDimension || ''\n\n // Convert starting step to server format\n // Server accepts Filter | Filter[] for multiple filters\n const startingStep: ServerFlowQuery['flow']['startingStep'] = {\n name: state.startingStep.name || 'Starting Step',\n filter:\n state.startingStep.filters.length === 1\n ? state.startingStep.filters[0]\n : state.startingStep.filters.length > 1\n ? state.startingStep.filters\n : undefined,\n }\n\n return {\n flow: {\n bindingKey,\n timeDimension,\n startingStep,\n stepsBefore: state.stepsBefore,\n stepsAfter: state.stepsAfter,\n eventDimension: state.eventDimension || '',\n joinStrategy: state.joinStrategy,\n },\n }\n}\n\n/**\n * Convert ServerFlowQuery to FlowSliceState\n */\nfunction serverQueryToState(query: ServerFlowQuery): FlowSliceState {\n const { flow } = query\n\n // Extract cube from binding key or event dimension\n let flowCube: string | null = null\n if (typeof flow.bindingKey === 'string') {\n const parts = flow.bindingKey.split('.')\n if (parts.length > 0) {\n flowCube = parts[0]\n }\n } else if (Array.isArray(flow.bindingKey) && flow.bindingKey.length > 0) {\n flowCube = flow.bindingKey[0].cube\n }\n\n // Convert binding key to client format\n let flowBindingKey: FunnelBindingKey | null = null\n if (flow.bindingKey) {\n if (typeof flow.bindingKey === 'string') {\n flowBindingKey = { dimension: flow.bindingKey }\n } else if (Array.isArray(flow.bindingKey)) {\n flowBindingKey = {\n dimension: flow.bindingKey.map((mapping) => ({\n cube: mapping.cube,\n dimension: mapping.dimension,\n })),\n }\n }\n }\n\n // Convert time dimension\n let flowTimeDimension: string | null = null\n if (flow.timeDimension) {\n if (typeof flow.timeDimension === 'string') {\n flowTimeDimension = flow.timeDimension\n } else if (Array.isArray(flow.timeDimension) && flow.timeDimension.length > 0) {\n flowTimeDimension = `${flow.timeDimension[0].cube}.${flow.timeDimension[0].dimension}`\n }\n }\n\n // Convert starting step filters\n let startingStepFilters: Filter[] = []\n if (flow.startingStep.filter) {\n if (Array.isArray(flow.startingStep.filter)) {\n startingStepFilters = flow.startingStep.filter\n } else {\n startingStepFilters = [flow.startingStep.filter]\n }\n }\n\n return {\n flowCube,\n flowBindingKey,\n flowTimeDimension,\n startingStep: {\n name: flow.startingStep.name || '',\n filters: startingStepFilters,\n },\n stepsBefore: flow.stepsBefore || 3,\n stepsAfter: flow.stepsAfter || 3,\n eventDimension: flow.eventDimension || null,\n joinStrategy: flow.joinStrategy || 'auto',\n }\n}\n\n/**\n * Check if a config is a valid flow config\n */\nfunction isValidFlowConfig(config: unknown): config is FlowAnalysisConfig {\n if (!config || typeof config !== 'object') return false\n\n const c = config as Record<string, unknown>\n\n if (c.version !== 1) return false\n if (c.analysisType !== 'flow') return false\n if (!c.query || typeof c.query !== 'object') return false\n\n const query = c.query as Record<string, unknown>\n if (!query.flow || typeof query.flow !== 'object') return false\n\n return true\n}\n\n// ============================================================================\n// Flow Mode Adapter\n// ============================================================================\n\nexport const flowModeAdapter: ModeAdapter<FlowSliceState> = {\n type: 'flow',\n\n createInitial(): FlowSliceState {\n return {\n flowCube: null,\n flowBindingKey: null,\n flowTimeDimension: null,\n startingStep: {\n name: '',\n filters: [],\n },\n stepsBefore: 3,\n stepsAfter: 3,\n eventDimension: null,\n joinStrategy: 'auto',\n }\n },\n\n extractState(storeState: Record<string, unknown>): FlowSliceState {\n return {\n flowCube: storeState.flowCube as string | null,\n flowBindingKey: storeState.flowBindingKey as FunnelBindingKey | null,\n flowTimeDimension: storeState.flowTimeDimension as string | null,\n startingStep: storeState.startingStep as FlowStartingStep,\n stepsBefore: storeState.stepsBefore as number,\n stepsAfter: storeState.stepsAfter as number,\n eventDimension: storeState.eventDimension as string | null,\n joinStrategy: (storeState.joinStrategy as 'auto' | 'lateral' | 'window') || 'auto',\n }\n },\n\n canLoad(config: unknown): config is AnalysisConfig {\n return isValidFlowConfig(config)\n },\n\n load(config: AnalysisConfig): FlowSliceState {\n // Type guard - ensure it's a flow config\n if (config.analysisType !== 'flow') {\n throw new Error(\n `Cannot load ${config.analysisType} config with flow adapter`\n )\n }\n\n const flowConfig = config as FlowAnalysisConfig\n return serverQueryToState(flowConfig.query)\n },\n\n save(\n state: FlowSliceState,\n charts: Partial<Record<AnalysisType, ChartConfig>>,\n activeView: 'table' | 'chart'\n ): FlowAnalysisConfig {\n return {\n version: 1,\n analysisType: 'flow',\n activeView,\n charts: {\n flow: charts.flow || this.getDefaultChartConfig(),\n },\n query: stateToServerQuery(state),\n }\n },\n\n validate(state: FlowSliceState): ValidationResult {\n const errors: string[] = []\n const warnings: string[] = []\n\n // Must have a cube selected\n if (!state.flowCube) {\n errors.push('Select an event stream cube for flow analysis')\n }\n\n // Must have a binding key\n if (!state.flowBindingKey?.dimension) {\n errors.push('A binding key is required to link events to entities')\n }\n\n // Must have a time dimension\n if (!state.flowTimeDimension) {\n errors.push('A time dimension is required for event ordering')\n }\n\n // Must have an event dimension\n if (!state.eventDimension) {\n errors.push('An event dimension is required to categorize events')\n }\n\n // Must have starting step filters\n if (state.startingStep.filters.length === 0) {\n errors.push('The starting step must have at least one filter to identify the anchor event')\n }\n\n // Validate depth bounds\n if (state.stepsBefore < 0 || state.stepsBefore > 5) {\n errors.push(`Steps before must be between 0 and 5`)\n }\n if (state.stepsAfter < 0 || state.stepsAfter > 5) {\n errors.push(`Steps after must be between 0 and 5`)\n }\n\n if (\n state.joinStrategy &&\n !['auto', 'lateral', 'window'].includes(state.joinStrategy)\n ) {\n errors.push('Join strategy must be auto, lateral, or window')\n }\n\n // Warnings\n if (!state.startingStep.name) {\n warnings.push('Starting step has no name - using default')\n }\n\n // Performance warnings for high depth\n if (state.stepsBefore >= 4 || state.stepsAfter >= 4) {\n warnings.push('High step depth (4-5) may impact query performance on large datasets')\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings,\n }\n },\n\n clear(state: FlowSliceState): FlowSliceState {\n // Keep cube selection but clear other settings\n return {\n ...this.createInitial(),\n flowCube: state.flowCube,\n }\n },\n\n getDefaultChartConfig(): ChartConfig {\n return {\n chartType: 'sankey',\n chartConfig: {},\n displayConfig: {\n showLegend: true,\n showGrid: false,\n showTooltip: true,\n },\n }\n },\n}\n","/**\n * Retention Mode Adapter\n *\n * Handles conversion between UI state and AnalysisConfig for retention mode.\n * Converts RetentionSliceState UI state to/from ServerRetentionQuery format.\n *\n * Simplified Mixpanel-style format (Phase 5):\n * - Single cube for all analysis\n * - Single timestamp dimension\n * - Single cohort with breakdown support\n * - Granularity = viewing periods\n */\n\nimport type { ModeAdapter, ValidationResult } from './modeAdapter'\nimport type {\n AnalysisConfig,\n RetentionAnalysisConfig,\n AnalysisType,\n ChartConfig,\n} from '../types/analysisConfig'\nimport type { Filter } from '../types'\nimport type { FunnelBindingKey } from '../types/funnel'\nimport type {\n ServerRetentionQuery,\n RetentionSliceState,\n RetentionGranularity,\n RetentionType,\n DateRange,\n RetentionBreakdownItem,\n} from '../types/retention'\nimport { defaultRetentionSliceState, getDateRangeFromPreset, DEFAULT_DATE_RANGE_PRESET } from '../types/retention'\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Convert RetentionSliceState to ServerRetentionQuery\n * Uses the new simplified format from Phase 1\n */\nfunction stateToServerQuery(state: RetentionSliceState): ServerRetentionQuery {\n // Convert binding key to server format\n let bindingKey: ServerRetentionQuery['retention']['bindingKey'] = ''\n if (state.retentionBindingKey) {\n if (typeof state.retentionBindingKey.dimension === 'string') {\n bindingKey = state.retentionBindingKey.dimension\n } else if (Array.isArray(state.retentionBindingKey.dimension)) {\n bindingKey = state.retentionBindingKey.dimension.map((mapping) => ({\n cube: mapping.cube,\n dimension: mapping.dimension,\n }))\n }\n }\n\n // Build the server query with new simplified format\n const query: ServerRetentionQuery = {\n retention: {\n timeDimension: state.retentionTimeDimension || '',\n bindingKey,\n dateRange: state.retentionDateRange,\n granularity: state.retentionViewGranularity,\n periods: state.retentionPeriods,\n retentionType: state.retentionType,\n },\n }\n\n // Add cohort filters if present\n if (state.retentionCohortFilters.length > 0) {\n query.retention.cohortFilters =\n state.retentionCohortFilters.length === 1\n ? state.retentionCohortFilters[0]\n : state.retentionCohortFilters\n }\n\n // Add activity filters if present\n if (state.retentionActivityFilters.length > 0) {\n query.retention.activityFilters =\n state.retentionActivityFilters.length === 1\n ? state.retentionActivityFilters[0]\n : state.retentionActivityFilters\n }\n\n // Add breakdown dimensions if present\n if (state.retentionBreakdowns && state.retentionBreakdowns.length > 0) {\n query.retention.breakdownDimensions = state.retentionBreakdowns.map((b) => b.field)\n }\n\n return query\n}\n\n/**\n * Convert ServerRetentionQuery to RetentionSliceState\n * Uses the new simplified format from Phase 1\n */\nfunction serverQueryToState(query: ServerRetentionQuery): RetentionSliceState {\n const { retention } = query\n\n // Extract cube from time dimension\n let retentionCube: string | null = null\n if (typeof retention.timeDimension === 'string') {\n const parts = retention.timeDimension.split('.')\n if (parts.length > 0) {\n retentionCube = parts[0]\n }\n } else if (retention.timeDimension?.cube) {\n retentionCube = retention.timeDimension.cube\n }\n\n // Convert binding key to client format\n let retentionBindingKey: FunnelBindingKey | null = null\n if (retention.bindingKey) {\n if (typeof retention.bindingKey === 'string') {\n retentionBindingKey = { dimension: retention.bindingKey }\n } else if (Array.isArray(retention.bindingKey)) {\n retentionBindingKey = {\n dimension: retention.bindingKey.map((mapping) => ({\n cube: mapping.cube,\n dimension: mapping.dimension,\n })),\n }\n }\n }\n\n // Convert time dimension\n let retentionTimeDimension: string | null = null\n if (retention.timeDimension) {\n if (typeof retention.timeDimension === 'string') {\n retentionTimeDimension = retention.timeDimension\n } else {\n retentionTimeDimension = `${retention.timeDimension.cube}.${retention.timeDimension.dimension}`\n }\n }\n\n // Convert filters\n let retentionCohortFilters: Filter[] = []\n if (retention.cohortFilters) {\n if (Array.isArray(retention.cohortFilters)) {\n retentionCohortFilters = retention.cohortFilters as Filter[]\n } else {\n retentionCohortFilters = [retention.cohortFilters as Filter]\n }\n }\n\n let retentionActivityFilters: Filter[] = []\n if (retention.activityFilters) {\n if (Array.isArray(retention.activityFilters)) {\n retentionActivityFilters = retention.activityFilters as Filter[]\n } else {\n retentionActivityFilters = [retention.activityFilters as Filter]\n }\n }\n\n // Convert breakdown dimensions\n let retentionBreakdowns: RetentionBreakdownItem[] = []\n if (retention.breakdownDimensions && Array.isArray(retention.breakdownDimensions)) {\n retentionBreakdowns = retention.breakdownDimensions.map((field) => ({\n field,\n label: field.split('.').pop() || field,\n }))\n }\n\n // Extract or default the date range\n const retentionDateRange: DateRange = retention.dateRange || getDateRangeFromPreset(DEFAULT_DATE_RANGE_PRESET)\n\n return {\n retentionCube,\n retentionBindingKey,\n retentionTimeDimension,\n retentionDateRange,\n retentionViewGranularity: retention.granularity as RetentionGranularity,\n retentionPeriods: retention.periods,\n retentionType: retention.retentionType as RetentionType,\n retentionCohortFilters,\n retentionActivityFilters,\n retentionBreakdowns,\n }\n}\n\n/**\n * Check if a config is a valid retention config\n */\nfunction isValidRetentionConfig(config: unknown): config is RetentionAnalysisConfig {\n if (!config || typeof config !== 'object') return false\n\n const c = config as Record<string, unknown>\n\n if (c.version !== 1) return false\n if (c.analysisType !== 'retention') return false\n if (!c.query || typeof c.query !== 'object') return false\n\n const query = c.query as Record<string, unknown>\n if (!query.retention || typeof query.retention !== 'object') return false\n\n return true\n}\n\n// ============================================================================\n// Retention Mode Adapter\n// ============================================================================\n\nexport const retentionModeAdapter: ModeAdapter<RetentionSliceState> = {\n type: 'retention',\n\n createInitial(): RetentionSliceState {\n return { ...defaultRetentionSliceState }\n },\n\n extractState(storeState: Record<string, unknown>): RetentionSliceState {\n return {\n retentionCube: storeState.retentionCube as string | null,\n retentionBindingKey: storeState.retentionBindingKey as FunnelBindingKey | null,\n retentionTimeDimension: storeState.retentionTimeDimension as string | null,\n retentionDateRange: (storeState.retentionDateRange as DateRange) || getDateRangeFromPreset(DEFAULT_DATE_RANGE_PRESET),\n retentionViewGranularity: (storeState.retentionViewGranularity as RetentionGranularity) || 'week',\n retentionPeriods: (storeState.retentionPeriods as number) || 12,\n retentionType: (storeState.retentionType as RetentionType) || 'classic',\n retentionCohortFilters: (storeState.retentionCohortFilters as Filter[]) || [],\n retentionActivityFilters: (storeState.retentionActivityFilters as Filter[]) || [],\n retentionBreakdowns: (storeState.retentionBreakdowns as RetentionBreakdownItem[]) || [],\n }\n },\n\n canLoad(config: unknown): config is AnalysisConfig {\n return isValidRetentionConfig(config)\n },\n\n load(config: AnalysisConfig): RetentionSliceState {\n // Type guard - ensure it's a retention config\n if (config.analysisType !== 'retention') {\n throw new Error(\n `Cannot load ${config.analysisType} config with retention adapter`\n )\n }\n\n const retentionConfig = config as RetentionAnalysisConfig\n return serverQueryToState(retentionConfig.query)\n },\n\n save(\n state: RetentionSliceState,\n charts: Partial<Record<AnalysisType, ChartConfig>>,\n activeView: 'table' | 'chart'\n ): RetentionAnalysisConfig {\n return {\n version: 1,\n analysisType: 'retention',\n activeView,\n charts: {\n retention: charts.retention || this.getDefaultChartConfig(),\n },\n query: stateToServerQuery(state),\n }\n },\n\n validate(state: RetentionSliceState): ValidationResult {\n const errors: string[] = []\n const warnings: string[] = []\n\n // Must have a cube selected\n if (!state.retentionCube) {\n errors.push('Select a cube for retention analysis')\n }\n\n // Must have a time dimension\n if (!state.retentionTimeDimension) {\n errors.push('Select a timestamp dimension for the analysis')\n }\n\n // Must have a binding key\n if (!state.retentionBindingKey?.dimension) {\n errors.push('Select a user identifier (binding key) to track retention')\n }\n\n // Date range is required\n if (!state.retentionDateRange?.start || !state.retentionDateRange?.end) {\n errors.push('Date range is required for retention analysis')\n } else {\n // Validate date format\n const startDate = new Date(state.retentionDateRange.start)\n const endDate = new Date(state.retentionDateRange.end)\n if (isNaN(startDate.getTime())) {\n errors.push('Invalid start date format')\n }\n if (isNaN(endDate.getTime())) {\n errors.push('Invalid end date format')\n }\n if (startDate > endDate) {\n errors.push('Start date must be before or equal to end date')\n }\n }\n\n // Periods must be valid\n if (state.retentionPeriods < 1) {\n errors.push('At least 1 retention period is required')\n }\n if (state.retentionPeriods > 52) {\n warnings.push('More than 52 periods may impact performance')\n }\n\n // Check time dimension format\n if (state.retentionTimeDimension) {\n const parts = state.retentionTimeDimension.split('.')\n if (parts.length < 2) {\n warnings.push('Time dimension should be in format \"Cube.dimension\"')\n }\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings,\n }\n },\n\n clear(state: RetentionSliceState): RetentionSliceState {\n // Keep cube selection and date range but clear other configuration\n return {\n ...this.createInitial(),\n retentionCube: state.retentionCube,\n retentionDateRange: state.retentionDateRange,\n }\n },\n\n getDefaultChartConfig(): ChartConfig {\n return {\n chartType: 'retentionCombined',\n chartConfig: {\n // RetentionCombinedChart auto-configures from the retention data structure\n // No explicit axis mapping needed\n },\n displayConfig: {\n showLegend: true,\n showTooltip: true,\n showGrid: true,\n retentionDisplayMode: 'combined',\n },\n }\n },\n}\n","import { useState, useEffect, useRef, useCallback, useMemo } from 'react'\nimport Modal from './Modal'\nimport AnalysisBuilder from './AnalysisBuilderLazy'\nimport type { AnalysisBuilderRef, AnalysisBuilderInitialFunnelState, AnalysisBuilderInitialFlowState, AnalysisBuilderInitialRetentionState } from './AnalysisBuilder/types'\nimport type { PortletConfig, ColorPalette, CubeQuery, MultiQueryConfig, DashboardFilter, AnalysisType } from '../types'\nimport type { AnalysisConfig } from '../types/analysisConfig'\nimport { ensureAnalysisConfig } from '../utils/configMigration'\nimport { funnelModeAdapter } from '../adapters/funnelModeAdapter'\nimport { flowModeAdapter } from '../adapters/flowModeAdapter'\nimport { retentionModeAdapter } from '../adapters/retentionModeAdapter'\nimport {\n mergeDashboardAndPortletFilters,\n applyUniversalTimeFilters\n} from '../utils/filterUtils'\n\ninterface PortletAnalysisModalProps {\n isOpen: boolean\n onClose: () => void\n onSave: (portlet: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>) => void\n portlet?: PortletConfig | null\n /** Initial data to display (avoids re-fetching when editing) */\n initialData?: any[]\n title: string\n submitText: string\n colorPalette?: ColorPalette\n /** Dashboard filters to apply to preview (when editing portlet in dashboard context) */\n dashboardFilters?: DashboardFilter[]\n}\n\n/**\n * PortletAnalysisModal - A modal wrapper around AnalysisBuilder for portlet editing\n *\n * This replaces PortletEditModal with the modern AnalysisBuilder interface.\n * Features:\n * - Two-panel layout with results and query builder\n * - Auto-execution of queries\n * - Smart chart defaults\n * - Title input in header\n * - Initial data support (no re-fetch when editing)\n */\nexport default function PortletAnalysisModal({\n isOpen,\n onClose,\n onSave,\n portlet,\n initialData,\n title: modalTitle,\n submitText,\n colorPalette,\n dashboardFilters\n}: PortletAnalysisModalProps) {\n\n // Ref to AnalysisBuilder for getting current query and chart config\n const builderRef = useRef<AnalysisBuilderRef>(null)\n\n // Title state\n const [formTitle, setFormTitle] = useState('')\n\n // Get applicable regular dashboard filters for this portlet\n // Universal time filters are handled separately - they apply to timeDimensions, not the filters array\n const applicableFilters = useMemo(() => {\n if (!dashboardFilters || !portlet?.dashboardFilterMapping) {\n return []\n }\n const mapping = portlet.dashboardFilterMapping\n // Only include regular filters (not universal time filters which use __universal_time__ placeholder)\n return dashboardFilters\n .filter(df => !df.isUniversalTime && mapping.includes(df.id))\n .map(df => df.filter)\n }, [dashboardFilters, portlet?.dashboardFilterMapping])\n\n // =========================================================================\n // Load from analysisConfig (migrating from legacy format if needed)\n // =========================================================================\n const derivedConfig = useMemo<AnalysisConfig | null>(() => {\n if (!portlet) return null\n\n // Use ensureAnalysisConfig which handles both new and legacy formats\n const normalizedPortlet = ensureAnalysisConfig(portlet)\n return normalizedPortlet.analysisConfig\n }, [portlet])\n\n // Parse initial query from derived config and merge dashboard filters\n // AnalysisBuilder handles both single CubeQuery and MultiQueryConfig internally\n const initialQuery = useMemo<CubeQuery | MultiQueryConfig | undefined>(() => {\n if (!derivedConfig) return undefined\n\n // Get query from derived config\n const query = derivedConfig.query\n if (!query) return undefined\n\n // Handle funnel mode - return the ServerFunnelQuery as-is (no filter merging)\n if (derivedConfig.analysisType === 'funnel') {\n // For funnel, query is ServerFunnelQuery - return as-is\n return query as CubeQuery | MultiQueryConfig\n }\n\n // Handle MultiQueryConfig\n if ('queries' in query && Array.isArray(query.queries)) {\n return {\n ...query,\n queries: query.queries.map((q: CubeQuery) => ({\n ...q,\n // Merge regular dashboard filters (not universal time)\n filters: mergeDashboardAndPortletFilters(applicableFilters, q.filters, 'client'),\n // Apply universal time filter dateRange to all time dimensions\n timeDimensions: applyUniversalTimeFilters(\n dashboardFilters,\n portlet?.dashboardFilterMapping,\n q.timeDimensions\n )\n }))\n }\n }\n\n // Handle single CubeQuery\n const cubeQuery = query as CubeQuery\n return {\n ...cubeQuery,\n // Merge regular dashboard filters (not universal time)\n filters: mergeDashboardAndPortletFilters(applicableFilters, cubeQuery.filters, 'client'),\n // Apply universal time filter dateRange to all time dimensions\n timeDimensions: applyUniversalTimeFilters(\n dashboardFilters,\n portlet?.dashboardFilterMapping,\n cubeQuery.timeDimensions\n )\n }\n }, [derivedConfig, applicableFilters, dashboardFilters, portlet?.dashboardFilterMapping])\n\n // Initial chart config from derived config\n const initialChartConfig = useMemo(() => {\n if (!derivedConfig) return undefined\n\n // Get chart config for current mode\n const modeCharts = derivedConfig.charts[derivedConfig.analysisType]\n if (!modeCharts) return undefined\n\n return {\n chartType: modeCharts.chartType,\n chartConfig: modeCharts.chartConfig,\n displayConfig: modeCharts.displayConfig\n }\n }, [derivedConfig])\n\n // Initial analysis type from derived config\n const initialAnalysisType: AnalysisType | undefined = derivedConfig?.analysisType\n\n // Initial funnel state from portlet (when analysisType === 'funnel')\n // Note: The funnel query data is in derivedConfig.query, but we need to pass\n // the store-level funnel state (funnelCube, funnelSteps, etc.) separately\n const initialFunnelState: AnalysisBuilderInitialFunnelState | undefined = useMemo(() => {\n if (derivedConfig?.analysisType !== 'funnel') return undefined\n\n // Option 1: Use legacy fields if present (backward compatibility)\n if (portlet?.funnelSteps && portlet.funnelSteps.length > 0) {\n return {\n funnelCube: portlet.funnelCube,\n funnelSteps: portlet.funnelSteps,\n funnelTimeDimension: portlet.funnelTimeDimension,\n funnelBindingKey: portlet.funnelBindingKey,\n funnelChartType: portlet.funnelChartType,\n funnelChartConfig: portlet.funnelChartConfig,\n funnelDisplayConfig: portlet.funnelDisplayConfig,\n }\n }\n\n // Option 2: Parse from analysisConfig.query (Phase 4 implementation)\n // This handles the case where only analysisConfig exists (no legacy fields)\n if (derivedConfig.query && 'funnel' in derivedConfig.query) {\n // Use adapter's conversion logic - already handles all formats\n const funnelState = funnelModeAdapter.load(derivedConfig)\n const chartConfig = derivedConfig.charts?.funnel\n\n return {\n ...funnelState,\n funnelChartType: chartConfig?.chartType,\n funnelChartConfig: chartConfig?.chartConfig,\n funnelDisplayConfig: chartConfig?.displayConfig,\n }\n }\n\n return undefined\n }, [derivedConfig, portlet])\n\n // Initial flow state from portlet (when analysisType === 'flow')\n const initialFlowState: AnalysisBuilderInitialFlowState | undefined = useMemo(() => {\n if (derivedConfig?.analysisType !== 'flow') return undefined\n\n // Parse from analysisConfig.query\n if (derivedConfig.query && 'flow' in derivedConfig.query) {\n // Use adapter's conversion logic\n const flowState = flowModeAdapter.load(derivedConfig)\n const chartConfig = derivedConfig.charts?.flow\n\n return {\n ...flowState,\n flowChartType: chartConfig?.chartType,\n flowChartConfig: chartConfig?.chartConfig,\n flowDisplayConfig: chartConfig?.displayConfig,\n }\n }\n\n return undefined\n }, [derivedConfig])\n\n // Initial retention state from portlet (when analysisType === 'retention')\n const initialRetentionState: AnalysisBuilderInitialRetentionState | undefined = useMemo(() => {\n if (derivedConfig?.analysisType !== 'retention') return undefined\n\n // Parse from analysisConfig.query\n if (derivedConfig.query && 'retention' in derivedConfig.query) {\n // Use adapter's conversion logic\n const retentionState = retentionModeAdapter.load(derivedConfig)\n const chartConfig = derivedConfig.charts?.retention\n\n return {\n ...retentionState,\n retentionChartType: chartConfig?.chartType,\n retentionChartConfig: chartConfig?.chartConfig,\n retentionDisplayConfig: chartConfig?.displayConfig,\n }\n }\n\n return undefined\n }, [derivedConfig])\n\n // Reset form state when modal opens/closes or portlet changes\n useEffect(() => {\n if (isOpen) {\n setFormTitle(portlet?.title || '')\n }\n }, [isOpen, portlet])\n\n // Handle save - Phase 4: Only save analysisConfig (no more dual-write)\n const handleSave = useCallback(() => {\n if (!formTitle.trim()) {\n alert('Please enter a title for the portlet.')\n return\n }\n\n // Get AnalysisConfig from store - this is the only format we save now\n const analysisConfig = builderRef.current?.getAnalysisConfig()\n\n if (!analysisConfig) {\n alert('Please configure a query before saving.')\n return\n }\n\n // Validate content based on analysis type\n const { query, analysisType } = analysisConfig\n let hasContent = false\n\n // Check for ServerFlowQuery format { flow: {...} }\n if ('flow' in query && query.flow) {\n // Flow mode: check for required configuration\n hasContent = !!(\n query.flow.bindingKey &&\n query.flow.timeDimension &&\n query.flow.eventDimension &&\n query.flow.startingStep?.filter\n )\n } else if ('retention' in query && query.retention) {\n // Retention mode: check for required configuration\n hasContent = !!(\n query.retention.bindingKey &&\n query.retention.timeDimension &&\n query.retention.dateRange?.start &&\n query.retention.dateRange?.end\n )\n } else if ('funnel' in query && query.funnel) {\n // Funnel mode: check for steps\n hasContent = !!(query.funnel.steps && query.funnel.steps.length >= 2)\n } else if ('queries' in query) {\n // Multi-query: check the first query\n const firstQuery = query.queries[0]\n hasContent = !!(\n (firstQuery?.measures && firstQuery.measures.length > 0) ||\n (firstQuery?.dimensions && firstQuery.dimensions.length > 0) ||\n (firstQuery?.timeDimensions && firstQuery.timeDimensions.length > 0)\n )\n } else {\n // Single query: check directly (type narrowed to CubeQuery)\n const cubeQuery = query as CubeQuery\n hasContent = !!(\n (cubeQuery.measures && cubeQuery.measures.length > 0) ||\n (cubeQuery.dimensions && cubeQuery.dimensions.length > 0) ||\n (cubeQuery.timeDimensions && cubeQuery.timeDimensions.length > 0)\n )\n }\n\n if (!hasContent) {\n let message: string\n if (analysisType === 'flow') {\n message = 'Please configure the flow analysis (binding key, time dimension, event dimension, and starting step filter).'\n } else if (analysisType === 'retention') {\n message = 'Please configure the retention analysis (binding key, time dimension, and date range).'\n } else if (analysisType === 'funnel') {\n message = 'Please add at least two funnel steps.'\n } else {\n message = 'Please add at least one metric or breakdown to your query.'\n }\n alert(message)\n return\n }\n\n // Build portlet config with ONLY analysisConfig (no more legacy fields)\n const portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'> = {\n ...(portlet || {}),\n title: formTitle.trim(),\n\n // === Canonical format - single source of truth ===\n analysisConfig,\n\n // Preserve dashboard filter mapping and eager load settings\n dashboardFilterMapping: portlet?.dashboardFilterMapping,\n eagerLoad: portlet?.eagerLoad,\n\n // Preserve existing position or use defaults for new portlets\n w: portlet?.w || 5,\n h: portlet?.h || 4\n } as PortletConfig\n\n onSave(portletData)\n onClose()\n }, [formTitle, portlet, onSave, onClose])\n\n // Handle cancel\n const handleCancel = useCallback(() => {\n onClose()\n }, [onClose])\n\n // Footer with save/cancel buttons\n const footer = (\n <>\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-text-secondary hover:text-dc-text bg-dc-surface dc:border border-dc-border dc:rounded-md hover:bg-dc-surface-hover dc:transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-white bg-dc-accent hover:bg-dc-accent-hover dc:rounded-md dc:transition-colors\"\n >\n {submitText}\n </button>\n </>\n )\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={modalTitle}\n size=\"fullscreen-mobile\"\n showCloseButton={true}\n closeOnBackdropClick={false}\n closeOnEscape={true}\n noPadding={true}\n footer={footer}\n >\n {/* Custom content with title input */}\n <div className=\"dc:flex dc:flex-col dc:h-full\">\n {/* Title input section */}\n <div className=\"dc:shrink-0 dc:px-4 dc:py-3 dc:border-b border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:gap-3\">\n <label htmlFor=\"portlet-title\" className=\"dc:text-sm dc:font-medium text-dc-text-secondary dc:shrink-0\">\n Title\n </label>\n <input\n id=\"portlet-title\"\n type=\"text\"\n value={formTitle}\n onChange={(e) => setFormTitle(e.target.value)}\n placeholder=\"Enter portlet title...\"\n autoComplete=\"off\"\n className=\"dc:flex-1 dc:px-3 dc:py-1.5 dc:text-sm bg-dc-surface dc:border border-dc-border dc:rounded-md text-dc-text placeholder-dc-text-muted dc:focus:outline-none dc:focus:ring-2 focus:ring-dc-accent focus:border-transparent\"\n autoFocus\n />\n </div>\n </div>\n\n {/* AnalysisBuilder content */}\n <div className=\"dc:flex-1 dc:min-h-0\">\n <AnalysisBuilder\n ref={builderRef}\n maxHeight=\"100%\"\n initialQuery={initialQuery}\n initialChartConfig={initialChartConfig}\n initialAnalysisType={initialAnalysisType}\n initialFunnelState={initialFunnelState}\n initialFlowState={initialFlowState}\n initialRetentionState={initialRetentionState}\n initialData={initialData}\n colorPalette={colorPalette}\n disableLocalStorage={true}\n className=\"dc:h-full\"\n />\n </div>\n </div>\n </Modal>\n )\n}\n","/**\n * Portlet Filter Configuration Modal\n * Allows users to configure which dashboard filters apply to a specific portlet\n */\n\nimport { useState, useEffect } from 'react'\nimport type { DashboardFilter } from '../types'\n\ninterface PortletFilterConfigModalProps {\n isOpen: boolean\n onClose: () => void\n dashboardFilters: DashboardFilter[]\n currentMapping: string[]\n onSave: (mapping: string[]) => void\n portletTitle: string\n}\n\nexport default function PortletFilterConfigModal({\n isOpen,\n onClose,\n dashboardFilters = [],\n currentMapping = [],\n onSave,\n portletTitle\n}: PortletFilterConfigModalProps) {\n const [selectedFilters, setSelectedFilters] = useState<string[]>(currentMapping)\n\n // Update local state when props change\n useEffect(() => {\n setSelectedFilters(currentMapping)\n }, [currentMapping, isOpen])\n\n const handleToggleFilter = (filterId: string) => {\n setSelectedFilters(prev => {\n if (prev.includes(filterId)) {\n return prev.filter(id => id !== filterId)\n } else {\n return [...prev, filterId]\n }\n })\n }\n\n const handleSave = () => {\n onSave(selectedFilters)\n onClose()\n }\n\n const handleCancel = () => {\n setSelectedFilters(currentMapping) // Reset to original\n onClose()\n }\n\n // Format filter preview text\n const formatFilterPreview = (filter: DashboardFilter): string => {\n if (!filter.filter) return ''\n\n // Handle simple filters\n if ('member' in filter.filter && filter.filter.member) {\n const values = filter.filter.values || []\n const valuesText = values.length > 0 ? values.join(', ') : 'no value'\n return `${filter.filter.member} ${filter.filter.operator} ${valuesText}`\n }\n\n // Handle group filters (AND/OR)\n if ('type' in filter.filter && filter.filter.type) {\n const filterCount = filter.filter.filters?.length || 0\n return `${filter.filter.type.toUpperCase()} group with ${filterCount} filter${filterCount !== 1 ? 's' : ''}`\n }\n\n return 'Complex filter'\n }\n\n if (!isOpen) return null\n\n return (\n <div className=\"dc:fixed dc:inset-0 dc:z-50 dc:flex dc:items-center dc:justify-center bg-black bg-opacity-50\" onClick={handleCancel}>\n <div\n className=\"bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:max-w-2xl dc:w-full dc:mx-4 dc:max-h-[80vh] dc:flex dc:flex-col\"\n style={{ boxShadow: 'var(--dc-shadow-lg)' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div className=\"dc:px-6 dc:py-4 dc:border-b border-dc-border bg-dc-surface-secondary dc:rounded-t-lg\">\n <h2 className=\"dc:text-lg dc:font-semibold text-dc-text\">Configure Dashboard Filters</h2>\n <p className=\"dc:text-sm text-dc-text-secondary dc:mt-1\">\n Choose which dashboard filters apply to \"{portletTitle}\"\n </p>\n </div>\n\n {/* Content */}\n <div className=\"dc:flex-1 dc:overflow-y-auto dc:px-6 dc:py-4\">\n {dashboardFilters.length === 0 ? (\n <div className=\"text-center dc:py-8 text-dc-text-muted\">\n <svg\n className=\"dc:mx-auto dc:h-12 dc:w-12 dc:mb-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z\"\n />\n </svg>\n <p className=\"dc:text-sm dc:font-medium\">No dashboard filters available</p>\n <p className=\"dc:text-xs dc:mt-1\">Add filters at the dashboard level first</p>\n </div>\n ) : (\n <div className=\"dc:space-y-3\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:mb-4 dc:pb-2 dc:border-b border-dc-border\">\n <span className=\"dc:text-sm dc:font-medium text-dc-text\">Available Filters</span>\n <span className=\"dc:text-xs text-dc-text-secondary\">\n {selectedFilters.length} of {dashboardFilters.length} selected\n </span>\n </div>\n\n {dashboardFilters.map(filter => {\n const isSelected = selectedFilters.includes(filter.id)\n\n return (\n <label\n key={filter.id}\n className={`dc:flex dc:items-start dc:p-3 dc:rounded-md dc:border dc:cursor-pointer dc:transition-colors ${\n isSelected\n ? 'border-dc-primary bg-dc-surface-secondary'\n : 'border-dc-border hover:bg-dc-surface-hover'\n }`}\n >\n <input\n type=\"checkbox\"\n checked={isSelected}\n onChange={() => handleToggleFilter(filter.id)}\n className=\"dc:mt-0.5 dc:mr-3 dc:h-4 dc:w-4 dc:rounded border-dc-border dc:focus:ring-2 focus:ring-dc-primary\"\n style={{\n accentColor: 'var(--dc-primary)'\n }}\n />\n <div className=\"dc:flex-1 dc:min-w-0\">\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <span className=\"dc:font-medium dc:text-sm text-dc-text dc:truncate\">\n {filter.label}\n </span>\n {isSelected && (\n <span\n className=\"dc:px-2 dc:py-0.5 dc:text-xs dc:rounded-full\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n Applied\n </span>\n )}\n </div>\n <div className=\"dc:mt-1 dc:text-xs text-dc-text-secondary dc:break-words\">\n {formatFilterPreview(filter)}\n </div>\n </div>\n </label>\n )\n })}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"dc:px-6 dc:py-4 dc:border-t border-dc-border bg-dc-surface-secondary dc:rounded-b-lg dc:flex dc:justify-end dc:gap-3\">\n <button\n onClick={handleCancel}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md dc:border border-dc-border bg-dc-surface hover:bg-dc-surface-hover dc:transition-colors text-dc-text\"\n >\n Cancel\n </button>\n <button\n onClick={handleSave}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md text-white dc:transition-colors\"\n style={{\n backgroundColor: 'var(--dc-primary)'\n }}\n >\n Apply Filters\n </button>\n </div>\n </div>\n </div>\n )\n}\n","import React from 'react'\nimport Modal from './Modal'\n\nexport interface ConfirmModalProps {\n isOpen: boolean\n onClose: () => void\n onConfirm: () => void | Promise<void>\n title?: string\n message: React.ReactNode\n confirmText?: string\n cancelText?: string\n confirmVariant?: 'danger' | 'primary' | 'warning'\n isLoading?: boolean\n}\n\n/**\n * A reusable confirmation modal component.\n *\n * Usage:\n * ```tsx\n * <ConfirmModal\n * isOpen={showConfirm}\n * onClose={() => setShowConfirm(false)}\n * onConfirm={handleDelete}\n * title=\"Delete Portlet\"\n * message=\"Are you sure you want to delete this portlet? This action cannot be undone.\"\n * confirmText=\"Delete\"\n * confirmVariant=\"danger\"\n * />\n * ```\n */\nconst ConfirmModal: React.FC<ConfirmModalProps> = ({\n isOpen,\n onClose,\n onConfirm,\n title = 'Confirm',\n message,\n confirmText = 'Confirm',\n cancelText = 'Cancel',\n confirmVariant = 'primary',\n isLoading = false,\n}) => {\n const handleConfirm = async () => {\n await onConfirm()\n onClose()\n }\n\n const getConfirmButtonClasses = () => {\n const baseClasses = 'dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md dc:transition-colors dc:focus:outline-none dc:focus:ring-2 dc:focus:ring-offset-2 dc:disabled:opacity-50 dc:disabled:cursor-not-allowed'\n\n switch (confirmVariant) {\n case 'danger':\n return `${baseClasses} bg-dc-danger dc:text-white dc:hover:bg-dc-danger/90 focus:ring-dc-danger`\n case 'warning':\n return `${baseClasses} bg-dc-warning dc:text-white dc:hover:bg-dc-warning/90 focus:ring-dc-warning`\n case 'primary':\n default:\n return `${baseClasses} bg-dc-primary dc:text-white dc:hover:bg-dc-primary/90 focus:ring-dc-primary`\n }\n }\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={title}\n size=\"sm\"\n closeOnBackdropClick={!isLoading}\n closeOnEscape={!isLoading}\n footer={\n <>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isLoading}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-text-secondary bg-dc-surface dc:border border-dc-border dc:rounded-md hover:bg-dc-surface-hover dc:transition-colors dc:focus:outline-none dc:focus:ring-2 dc:focus:ring-offset-2 focus:ring-dc-primary dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\"\n >\n {cancelText}\n </button>\n <button\n type=\"button\"\n onClick={handleConfirm}\n disabled={isLoading}\n className={getConfirmButtonClasses()}\n >\n {isLoading ? (\n <span className=\"dc:flex dc:items-center dc:gap-2\">\n <svg className=\"dc:animate-spin dc:h-4 dc:w-4\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle className=\"dc:opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\"></circle>\n <path className=\"dc:opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n Processing...\n </span>\n ) : (\n confirmText\n )}\n </button>\n </>\n }\n >\n <div className=\"text-dc-text-secondary\">\n {message}\n </div>\n </Modal>\n )\n}\n\nexport default ConfirmModal\n","/**\n * Color Palette Selector Component\n * Allows users to select from predefined color palettes for their dashboard\n */\n\nimport { useState, useRef, useEffect } from 'react'\nimport { COLOR_PALETTES, getColorPalette } from '../utils/colorPalettes'\nimport { getIcon } from '../icons'\n\nconst ChevronDownIcon = getIcon('chevronDown')\n\ninterface ColorPaletteSelectorProps {\n currentPalette?: string\n onPaletteChange: (paletteName: string) => void\n className?: string\n}\n\nexport default function ColorPaletteSelector({\n currentPalette = 'default',\n onPaletteChange,\n className = ''\n}: ColorPaletteSelectorProps) {\n const [isOpen, setIsOpen] = useState(false)\n const dropdownRef = useRef<HTMLDivElement>(null)\n \n const currentPaletteObj = getColorPalette(currentPalette)\n\n // Close dropdown when clicking outside\n useEffect(() => {\n function handleClickOutside(event: MouseEvent) {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false)\n }\n }\n\n if (isOpen) {\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }\n }, [isOpen])\n\n const handlePaletteSelect = (paletteName: string) => {\n onPaletteChange(paletteName)\n setIsOpen(false)\n }\n\n return (\n <div className={`dc:relative ${className}`} ref={dropdownRef}>\n {/* Trigger Button */}\n <button\n type=\"button\"\n onClick={() => setIsOpen(!isOpen)}\n className=\"dc:inline-flex dc:items-center dc:gap-2 dc:px-3 dc:py-1.5 bg-dc-surface dc:border border-dc-border dc:rounded-md shadow-xs dc:text-sm dc:font-medium text-dc-text-secondary hover:bg-dc-surface-hover focus:outline-hidden dc:focus:ring-2 dc:focus:ring-offset-2 focus:ring-dc-accent\"\n >\n {/* Current Palette Preview - Hidden on mobile */}\n <div className=\"dc:hidden dc:md:flex dc:items-center dc:gap-1.5\">\n <div className=\"dc:flex dc:gap-0.5\">\n {currentPaletteObj.colors.slice(0, 4).map((color, index) => (\n <div\n key={index}\n className=\"dc:w-3 dc:h-3 rounded-xs dc:border border-dc-border\"\n style={{ backgroundColor: color }}\n title={`Series Color ${index + 1}`}\n />\n ))}\n </div>\n <span className=\"dc:text-xs text-dc-text-secondary\">|</span>\n <div className=\"dc:flex dc:gap-0.5\">\n {currentPaletteObj.gradient.slice(0, 3).map((color, index) => (\n <div\n key={index}\n className=\"dc:w-2 dc:h-3 dc:border-r dc:first:rounded-l-sm dc:last:rounded-r-sm dc:last:border-r-0\"\n style={{\n backgroundColor: color,\n borderColor: 'var(--dc-border)'\n }}\n title={`Gradient Color ${index + 1}`}\n />\n ))}\n </div>\n </div>\n <span>{currentPaletteObj.label}</span>\n <ChevronDownIcon \n className={`dc:w-4 dc:h-4 dc:transition-transform ${isOpen ? 'dc:rotate-180' : ''}`} \n />\n </button>\n\n {/* Dropdown Menu - Responsive width */}\n {isOpen && (\n <div className=\"dc:absolute dc:top-full dc:left-0 dc:mt-1 dc:w-72 dc:md:w-80 dc:lg:w-96 bg-dc-surface dc:border border-dc-border dc:rounded-md dc:shadow-lg dc:z-50 dc:max-h-80 dc:overflow-y-auto\">\n <div className=\"dc:py-1\">\n {COLOR_PALETTES.slice().sort((a, b) => a.label.localeCompare(b.label)).map((palette) => (\n <button\n key={palette.name}\n type=\"button\"\n onClick={() => handlePaletteSelect(palette.name)}\n className={`dc:w-full dc:px-3 dc:py-2 text-left dc:text-sm hover:bg-dc-surface-hover focus:outline-hidden focus:bg-dc-surface-hover ${\n palette.name === currentPalette ? 'bg-dc-surface-secondary' : 'text-dc-text-secondary'\n }`}\n style={palette.name === currentPalette ? { color: 'var(--dc-primary)' } : undefined}\n >\n <div className=\"dc:flex dc:items-center dc:gap-3\">\n {/* Palette Preview - Hidden on mobile */}\n <div className=\"dc:hidden dc:md:flex dc:items-center dc:gap-2\">\n {/* Series Colors */}\n <div className=\"dc:flex dc:gap-0.5\">\n {palette.colors.slice(0, 6).map((color, index) => (\n <div\n key={`series-${index}`}\n className=\"dc:w-3 dc:h-3 rounded-xs dc:border border-dc-border\"\n style={{ backgroundColor: color }}\n />\n ))}\n </div>\n\n {/* Separator */}\n <div className=\"dc:w-px dc:h-4 bg-dc-border\" />\n \n {/* Gradient Colors */}\n <div className=\"dc:flex\">\n {palette.gradient.map((color, index) => (\n <div\n key={`gradient-${index}`}\n className=\"dc:w-2 dc:h-4 dc:first:rounded-l-sm dc:last:rounded-r-sm\"\n style={{ backgroundColor: color }}\n />\n ))}\n </div>\n </div>\n \n {/* Palette Name */}\n <span className=\"dc:font-medium\">{palette.label}</span>\n \n {/* Current Indicator */}\n {palette.name === currentPalette && (\n <div className=\"dc:ml-auto\">\n <div className=\"dc:w-2 dc:h-2 dc:rounded-full\" style={{ backgroundColor: 'var(--dc-primary)' }}></div>\n </div>\n )}\n </div>\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n )\n}","/**\n * FieldSearchItem Component\n *\n * A single field item in the search results list.\n * Shows field icon, title, type badge, and selection state.\n */\n\nimport { memo } from 'react'\nimport { getIcon, getMeasureTypeIcon, getFieldTypeIcon } from '../../icons'\nimport type { FieldSearchItemProps } from './types'\n\nconst CheckIcon = getIcon('check')\n\nfunction FieldSearchItem({\n field,\n isSelected,\n isFocused,\n onClick,\n onMouseEnter,\n ...props\n}: FieldSearchItemProps & { 'data-field-index'?: number }) {\n // Get appropriate icon based on field type\n const getFieldIcon = () => {\n if (field.fieldType === 'measure') {\n const Icon = getMeasureTypeIcon(field.type)\n return Icon ? <Icon className=\"dc:w-4 dc:h-4\" /> : null\n } else if (field.fieldType === 'timeDimension') {\n const Icon = getFieldTypeIcon('time')\n return Icon ? <Icon className=\"dc:w-4 dc:h-4\" /> : null\n } else {\n const Icon = getFieldTypeIcon('dimension')\n return Icon ? <Icon className=\"dc:w-4 dc:h-4\" /> : null\n }\n }\n\n // Get badge color based on field type\n const getBadgeStyle = () => {\n if (field.fieldType === 'measure') {\n return 'bg-dc-measure text-dc-measure-text'\n } else if (field.fieldType === 'timeDimension') {\n return 'bg-dc-time-dimension text-dc-time-dimension-text'\n } else {\n return 'bg-dc-dimension text-dc-dimension-text'\n }\n }\n\n // Get short type label\n const getTypeLabel = () => {\n if (field.fieldType === 'measure') {\n return field.type.charAt(0).toUpperCase() + field.type.slice(1)\n } else if (field.fieldType === 'timeDimension') {\n return 'Time'\n } else {\n return 'Dim'\n }\n }\n\n return (\n <button\n onClick={onClick}\n onMouseEnter={onMouseEnter}\n className={`dc:w-full text-left dc:px-3 dc:py-2 dc:rounded-lg dc:flex dc:items-center dc:gap-3 dc:transition-colors dc:group ${\n isFocused\n ? 'bg-dc-primary/10 dc:ring-1 ring-dc-primary'\n : isSelected\n ? 'bg-dc-success/10'\n : 'hover:bg-dc-surface-hover'\n }`}\n {...props}\n >\n {/* Icon */}\n <span\n className={`dc:shrink-0 dc:w-8 dc:h-8 dc:flex dc:items-center dc:justify-center dc:rounded-md ${\n field.fieldType === 'measure'\n ? 'bg-dc-measure text-dc-measure-text'\n : field.fieldType === 'timeDimension'\n ? 'bg-dc-time-dimension text-dc-time-dimension-text'\n : 'bg-dc-dimension text-dc-dimension-text'\n }`}\n >\n {getFieldIcon()}\n </span>\n\n {/* Title and name */}\n <div className=\"dc:flex-1 dc:min-w-0\">\n <div className=\"dc:text-sm dc:font-medium text-dc-text dc:truncate\">\n {field.title}\n </div>\n <div className=\"dc:text-xs text-dc-text-muted dc:truncate\">{field.name}</div>\n </div>\n\n {/* Type badge */}\n <span\n className={`dc:shrink-0 dc:px-2 dc:py-0.5 dc:rounded dc:text-xs dc:font-medium ${getBadgeStyle()}`}\n >\n {getTypeLabel()}\n </span>\n\n {/* Selection indicator */}\n {isSelected && (\n <span className=\"dc:shrink-0 dc:w-5 dc:h-5 dc:flex dc:items-center dc:justify-center dc:rounded-full bg-dc-success text-white\">\n <CheckIcon className=\"dc:w-3 dc:h-3\" />\n </span>\n )}\n </button>\n )\n}\n\nexport default memo(FieldSearchItem)\n","/**\n * FieldDetailPanel Component\n *\n * Shows detailed information about the currently focused/hovered field.\n * Displays: icon, title, description, type, cube name, and technical name.\n */\n\nimport { memo } from 'react'\nimport { getMeasureTypeIcon, getFieldTypeIcon } from '../../icons'\nimport type { FieldDetailPanelProps } from './types'\n\nfunction FieldDetailPanel({ field }: FieldDetailPanelProps) {\n if (!field) {\n return (\n <div className=\"dc:p-6 text-center text-dc-text-muted\">\n <p className=\"dc:text-sm\">Hover over a field to see details</p>\n </div>\n )\n }\n\n // Get appropriate icon based on field type\n const getFieldIcon = () => {\n if (field.fieldType === 'measure') {\n const Icon = getMeasureTypeIcon(field.type)\n return Icon ? <Icon className=\"dc:w-6 dc:h-6\" /> : null\n } else if (field.fieldType === 'timeDimension') {\n const Icon = getFieldTypeIcon('time')\n return Icon ? <Icon className=\"dc:w-6 dc:h-6\" /> : null\n } else {\n const Icon = getFieldTypeIcon('dimension')\n return Icon ? <Icon className=\"dc:w-6 dc:h-6\" /> : null\n }\n }\n\n // Get icon background color - use field type specific colors for consistency\n const getIconBgStyle = () => {\n if (field.fieldType === 'measure') {\n return 'bg-dc-measure text-dc-measure-text'\n } else if (field.fieldType === 'timeDimension') {\n return 'bg-dc-time-dimension text-dc-time-dimension-text'\n } else {\n return 'bg-dc-dimension text-dc-dimension-text'\n }\n }\n\n // Get type display name\n const getTypeDisplay = () => {\n if (field.fieldType === 'measure') {\n const typeMap: Record<string, string> = {\n count: 'Count',\n countDistinct: 'Count Distinct',\n countDistinctApprox: 'Count Distinct (Approx)',\n sum: 'Sum',\n avg: 'Average',\n min: 'Minimum',\n max: 'Maximum',\n runningTotal: 'Running Total',\n number: 'Number'\n }\n return typeMap[field.type] || field.type\n } else if (field.fieldType === 'timeDimension') {\n return 'Time Dimension'\n } else {\n const typeMap: Record<string, string> = {\n string: 'Text',\n number: 'Number',\n boolean: 'Boolean',\n geo: 'Geographic'\n }\n return typeMap[field.type] || 'Dimension'\n }\n }\n\n return (\n <div className=\"dc:p-4\">\n {/* Header with icon and title */}\n <div className=\"dc:flex dc:items-start dc:gap-3 dc:mb-4\">\n <span\n className={`dc:shrink-0 dc:w-12 dc:h-12 dc:flex dc:items-center dc:justify-center dc:rounded-lg ${getIconBgStyle()}`}\n >\n {getFieldIcon()}\n </span>\n <div className=\"dc:flex-1 dc:min-w-0\">\n <h3 className=\"dc:text-base dc:font-semibold text-dc-text dc:leading-tight\">\n {field.title}\n </h3>\n <p className=\"dc:text-xs text-dc-text-muted dc:mt-0.5 dc:truncate\">\n {field.name}\n </p>\n </div>\n </div>\n\n {/* Description */}\n {field.description && (\n <div className=\"dc:mb-4\">\n <p className=\"dc:text-sm text-dc-text-secondary dc:leading-relaxed\">\n {field.description}\n </p>\n </div>\n )}\n\n {/* Metadata */}\n <div className=\"dc:space-y-3 dc:pt-4 dc:border-t border-dc-border\">\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:text-xs text-dc-text-muted\">Type</span>\n <span className=\"dc:text-sm text-dc-text dc:font-medium\">{getTypeDisplay()}</span>\n </div>\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:text-xs text-dc-text-muted\">Cube</span>\n <span className=\"dc:text-sm text-dc-text dc:font-medium\">{field.cubeName}</span>\n </div>\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:text-xs text-dc-text-muted\">Category</span>\n <span\n className={`dc:text-xs dc:px-2 dc:py-0.5 dc:rounded dc:font-medium ${\n field.fieldType === 'measure'\n ? 'bg-dc-measure text-dc-measure-text'\n : field.fieldType === 'timeDimension'\n ? 'bg-dc-time-dimension text-dc-time-dimension-text'\n : 'bg-dc-dimension text-dc-dimension-text'\n }`}\n >\n {field.fieldType === 'measure'\n ? 'Measure'\n : field.fieldType === 'timeDimension'\n ? 'Time Dimension'\n : 'Dimension'}\n </span>\n </div>\n </div>\n\n {/* Usage hint */}\n <div className=\"dc:mt-6 dc:p-3 bg-dc-surface dc:rounded-lg\">\n <p className=\"dc:text-xs text-dc-text-muted\">\n Press <kbd className=\"dc:px-1 dc:py-0.5 bg-dc-surface-tertiary dc:rounded dc:text-xs\">Enter</kbd> or click to add this field to your query.\n </p>\n </div>\n </div>\n )\n}\n\nexport default memo(FieldDetailPanel)\n","/**\n * FieldSearchModal Component\n *\n * A full-screen search modal for selecting cube fields (measures/dimensions).\n * Features:\n * - Real-time search filtering\n * - Cube-based category filtering\n * - Three-column layout: Categories | Results | Details\n * - Keyboard navigation support\n * - Recent fields tracking\n */\n\nimport { useState, useMemo, useCallback, useEffect, useRef, KeyboardEvent } from 'react'\nimport { getIcon } from '../../icons'\nimport type { FieldSearchModalProps, FieldOption } from './types'\nimport type { MetaField } from '../../shared/types'\nimport {\n schemaToFieldOptions,\n filterFieldOptions,\n groupFieldsByCube,\n getCubeNames,\n getCubeTitle,\n getRecentFields,\n addRecentField,\n getRecentFieldOptions\n} from './utils'\nimport FieldSearchItem from './FieldSearchItem'\nimport FieldDetailPanel from './FieldDetailPanel'\n\nconst SearchIcon = getIcon('search')\nconst CloseIcon = getIcon('close')\n\nexport default function FieldSearchModal({\n isOpen,\n onClose,\n onSelect,\n mode,\n schema,\n selectedFields,\n recentFields: externalRecentFields\n}: FieldSearchModalProps) {\n // State\n const [searchTerm, setSearchTerm] = useState('')\n const [selectedCube, setSelectedCube] = useState<string | null>(null)\n const [focusedField, setFocusedField] = useState<FieldOption | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const [lastSelectedIndex, setLastSelectedIndex] = useState<number | null>(null)\n\n // Refs\n const searchInputRef = useRef<HTMLInputElement>(null)\n const resultsContainerRef = useRef<HTMLDivElement>(null)\n\n // Get recent fields from localStorage or props\n const recentFieldNames = useMemo(() => {\n if (externalRecentFields) return externalRecentFields\n const stored = getRecentFields()\n return mode === 'metrics' ? stored.metrics : stored.breakdowns\n }, [externalRecentFields, mode])\n\n // Map mode to field options mode\n const fieldOptionsMode = mode\n\n // Get all field options for current mode\n const allFieldOptions = useMemo(() => {\n return schemaToFieldOptions(schema, fieldOptionsMode)\n }, [schema, fieldOptionsMode])\n\n // Get cube names for category filter\n const cubeNames = useMemo(() => {\n return getCubeNames(schema)\n }, [schema])\n\n // Filter fields by search and cube\n const filteredFields = useMemo(() => {\n return filterFieldOptions(allFieldOptions, searchTerm, selectedCube)\n }, [allFieldOptions, searchTerm, selectedCube])\n\n // Group filtered fields by cube\n const groupedFields = useMemo(() => {\n return groupFieldsByCube(filteredFields)\n }, [filteredFields])\n\n // Get recent field options (only when not searching)\n const recentOptions = useMemo(() => {\n if (searchTerm.trim()) return []\n return getRecentFieldOptions(schema, fieldOptionsMode, recentFieldNames).filter(\n (f) => !selectedCube || f.cubeName === selectedCube\n )\n }, [schema, fieldOptionsMode, recentFieldNames, searchTerm, selectedCube])\n\n // Flat list of visible fields for keyboard navigation\n const flatFieldsList = useMemo(() => {\n const list: FieldOption[] = [...recentOptions]\n groupedFields.forEach((fields) => {\n list.push(...fields)\n })\n return list\n }, [recentOptions, groupedFields])\n\n // Focus search input when modal opens\n useEffect(() => {\n if (isOpen && searchInputRef.current) {\n searchInputRef.current.focus()\n }\n }, [isOpen])\n\n // Reset state when modal closes\n useEffect(() => {\n if (!isOpen) {\n setSearchTerm('')\n setSelectedCube(null)\n setFocusedField(null)\n setFocusedIndex(-1)\n setLastSelectedIndex(null)\n }\n }, [isOpen])\n\n // Handle single field selection\n const selectSingleField = useCallback(\n (field: FieldOption, keepOpen: boolean = false) => {\n // Add to recent fields\n addRecentField(field.name, mode === 'metrics' ? 'metrics' : 'breakdowns')\n\n // Create MetaField object for callback\n const metaField: MetaField = {\n name: field.name,\n title: field.title,\n shortTitle: field.shortTitle,\n type: field.type,\n description: field.description\n }\n\n onSelect(metaField, field.fieldType, field.cubeName, keepOpen)\n },\n [mode, onSelect]\n )\n\n // Handle field selection with shift-click support for range selection\n const handleSelectField = useCallback(\n (field: FieldOption, fieldIndex: number, shiftKey: boolean = false) => {\n // Shift-click for range selection - keep modal open\n if (shiftKey && lastSelectedIndex !== null && lastSelectedIndex !== fieldIndex) {\n const startIndex = Math.min(lastSelectedIndex, fieldIndex)\n const endIndex = Math.max(lastSelectedIndex, fieldIndex)\n\n // Select all fields in the range, keep modal open for all\n for (let i = startIndex; i <= endIndex; i++) {\n const rangeField = flatFieldsList[i]\n if (rangeField && !selectedFields.includes(rangeField.name)) {\n selectSingleField(rangeField, true) // Keep modal open\n }\n }\n } else if (shiftKey) {\n // Shift-click on single item - select but keep modal open\n selectSingleField(field, true)\n } else {\n // Normal single selection - close modal after\n selectSingleField(field, false)\n }\n\n // Update last selected index for next shift-click\n setLastSelectedIndex(fieldIndex)\n },\n [flatFieldsList, lastSelectedIndex, selectSingleField, selectedFields]\n )\n\n // Keyboard navigation\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (flatFieldsList.length === 0) return\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n setFocusedIndex((prev) => {\n const next = Math.min(prev + 1, flatFieldsList.length - 1)\n setFocusedField(flatFieldsList[next])\n return next\n })\n break\n\n case 'ArrowUp':\n e.preventDefault()\n setFocusedIndex((prev) => {\n const next = Math.max(prev - 1, 0)\n setFocusedField(flatFieldsList[next])\n return next\n })\n break\n\n case 'Enter':\n e.preventDefault()\n if (focusedIndex >= 0 && flatFieldsList[focusedIndex]) {\n handleSelectField(flatFieldsList[focusedIndex], focusedIndex, e.shiftKey)\n }\n break\n\n case 'Escape':\n e.preventDefault()\n onClose()\n break\n }\n },\n [flatFieldsList, focusedIndex, handleSelectField, onClose]\n )\n\n // Scroll focused item into view\n useEffect(() => {\n if (focusedIndex >= 0 && resultsContainerRef.current) {\n const focusedElement = resultsContainerRef.current.querySelector(\n `[data-field-index=\"${focusedIndex}\"]`\n )\n if (focusedElement) {\n focusedElement.scrollIntoView({ block: 'nearest', behavior: 'smooth' })\n }\n }\n }, [focusedIndex])\n\n if (!isOpen) return null\n\n const searchPlaceholder =\n mode === 'metrics' ? 'Search metrics...' : mode === 'filter' ? 'Search fields to filter...' : 'Search dimensions...'\n\n const modalTitle = mode === 'metrics' ? 'Select a Metric' : mode === 'filter' ? 'Select a Field to Filter' : 'Select a Dimension'\n const focusedFieldId = focusedIndex >= 0 && flatFieldsList[focusedIndex]\n ? `field-option-${flatFieldsList[focusedIndex].name.replace(/\\./g, '-')}`\n : undefined\n\n return (\n <div\n className=\"dc:fixed dc:inset-0 dc:z-50 dc:flex dc:items-center dc:justify-center\"\n style={{ backgroundColor: 'var(--dc-overlay)' }}\n onClick={onClose}\n role=\"presentation\"\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={modalTitle}\n className=\"bg-dc-surface dc:shadow-xl dc:w-full dc:h-full dc:md:rounded-lg dc:md:w-[900px] dc:md:max-w-[900px] dc:md:h-[80vh] dc:md:max-h-[700px] dc:flex dc:flex-col dc:overflow-hidden\"\n onClick={(e) => e.stopPropagation()}\n onKeyDown={handleKeyDown}\n >\n {/* Header with Search */}\n <div className=\"dc:shrink-0 dc:border-b border-dc-border\">\n <div className=\"dc:flex dc:items-center dc:px-4 dc:py-3 dc:gap-3\">\n <SearchIcon className=\"dc:w-5 dc:h-5 text-dc-text-muted\" aria-hidden={true} />\n <input\n ref={searchInputRef}\n type=\"text\"\n value={searchTerm}\n onChange={(e) => {\n setSearchTerm(e.target.value)\n setFocusedIndex(-1)\n }}\n placeholder={searchPlaceholder}\n className=\"dc:flex-1 bg-transparent dc:border-none dc:outline-none text-dc-text placeholder-dc-text-muted dc:text-lg\"\n aria-label={searchPlaceholder}\n aria-controls=\"field-search-results\"\n aria-activedescendant={focusedFieldId}\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-autocomplete=\"list\"\n />\n <button\n onClick={onClose}\n className=\"dc:p-1 text-dc-text-secondary hover:text-dc-text dc:rounded\"\n aria-label=\"Close dialog\"\n >\n <CloseIcon className=\"dc:w-5 dc:h-5\" aria-hidden={true} />\n </button>\n </div>\n {/* Mobile cube filter - shown only on mobile */}\n {cubeNames.length > 1 && (\n <div className=\"dc:md:hidden dc:px-4 dc:pb-3\">\n <select\n value={selectedCube || ''}\n onChange={(e) => setSelectedCube(e.target.value || null)}\n className=\"dc:w-full dc:px-3 dc:py-2 bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:text-sm text-dc-text dc:focus:outline-none dc:focus:ring-1 focus:ring-dc-primary\"\n aria-label=\"Filter by cube\"\n >\n <option value=\"\">All Cubes</option>\n {cubeNames.map((cubeName) => (\n <option key={cubeName} value={cubeName}>\n {getCubeTitle(cubeName, schema)}\n </option>\n ))}\n </select>\n </div>\n )}\n </div>\n\n {/* Three Column Layout - Single column on mobile */}\n <div className=\"dc:flex-1 dc:flex dc:overflow-hidden\">\n {/* Left Column - Categories (hidden on mobile) */}\n <nav\n className=\"dc:hidden dc:md:block dc:w-48 dc:shrink-0 dc:border-r border-dc-border dc:overflow-y-auto bg-dc-surface-secondary\"\n aria-label=\"Filter by cube\"\n >\n <div className=\"dc:p-2\" role=\"group\" aria-label=\"Cube categories\">\n <button\n onClick={() => setSelectedCube(null)}\n className={`dc:w-full text-left dc:px-3 dc:py-2 dc:rounded-md dc:text-sm dc:transition-colors ${\n selectedCube === null\n ? 'bg-dc-primary/10 text-dc-primary dc:font-medium'\n : 'text-dc-text hover:bg-dc-surface-hover'\n }`}\n aria-pressed={selectedCube === null}\n >\n All\n </button>\n {cubeNames.map((cubeName) => (\n <button\n key={cubeName}\n onClick={() => setSelectedCube(cubeName)}\n className={`dc:w-full text-left dc:px-3 dc:py-2 dc:rounded-md dc:text-sm dc:transition-colors dc:truncate ${\n selectedCube === cubeName\n ? 'bg-dc-primary/10 text-dc-primary dc:font-medium'\n : 'text-dc-text hover:bg-dc-surface-hover'\n }`}\n title={getCubeTitle(cubeName, schema)}\n aria-pressed={selectedCube === cubeName}\n >\n {getCubeTitle(cubeName, schema)}\n </button>\n ))}\n </div>\n </nav>\n\n {/* Middle Column - Results */}\n <div\n id=\"field-search-results\"\n ref={resultsContainerRef}\n className=\"dc:flex-1 dc:overflow-y-auto dc:p-4\"\n role=\"listbox\"\n aria-label=\"Available fields\"\n >\n {filteredFields.length === 0 && recentOptions.length === 0 ? (\n <div className=\"text-center dc:py-12 text-dc-text-muted\">\n <p className=\"dc:text-lg dc:mb-2\">No fields found</p>\n <p className=\"dc:text-sm\">\n {searchTerm\n ? `No ${mode === 'metrics' ? 'metrics' : 'dimensions'} match \"${searchTerm}\"`\n : `No ${mode === 'metrics' ? 'metrics' : 'dimensions'} available`}\n </p>\n </div>\n ) : (\n <div className=\"dc:space-y-6\">\n {/* Recent Fields */}\n {recentOptions.length > 0 && (\n <div>\n <h3 className=\"dc:text-xs dc:font-semibold text-dc-text-muted dc:uppercase dc:tracking-wider dc:mb-2\">\n Recents\n </h3>\n <div className=\"dc:space-y-1\">\n {recentOptions.map((field, idx) => (\n <FieldSearchItem\n key={`recent-${field.name}`}\n field={field}\n isSelected={selectedFields.includes(field.name)}\n isFocused={focusedIndex === idx}\n onClick={(e) => handleSelectField(field, idx, e.shiftKey)}\n onMouseEnter={() => {\n setFocusedField(field)\n setFocusedIndex(idx)\n }}\n data-field-index={idx}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Grouped by Cube */}\n {Array.from(groupedFields.entries()).map(([cubeName, fields]) => (\n <div key={cubeName}>\n <h3 className=\"dc:text-xs dc:font-semibold text-dc-text-muted dc:uppercase dc:tracking-wider dc:mb-2\">\n {getCubeTitle(cubeName, schema)}\n </h3>\n <div className=\"dc:space-y-1\">\n {fields.map((field) => {\n const fieldIndex =\n recentOptions.length +\n Array.from(groupedFields.entries())\n .slice(\n 0,\n Array.from(groupedFields.keys()).indexOf(cubeName)\n )\n .reduce((sum, [, f]) => sum + f.length, 0) +\n fields.indexOf(field)\n\n return (\n <FieldSearchItem\n key={field.name}\n field={field}\n isSelected={selectedFields.includes(field.name)}\n isFocused={focusedIndex === fieldIndex}\n onClick={(e) => handleSelectField(field, fieldIndex, e.shiftKey)}\n onMouseEnter={() => {\n setFocusedField(field)\n setFocusedIndex(fieldIndex)\n }}\n data-field-index={fieldIndex}\n />\n )\n })}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Right Column - Field Details (hidden on mobile) */}\n <div className=\"dc:hidden dc:md:block dc:w-72 dc:shrink-0 dc:border-l border-dc-border bg-dc-surface-secondary dc:overflow-y-auto\">\n <FieldDetailPanel field={focusedField} />\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"dc:shrink-0 dc:border-t border-dc-border dc:px-4 dc:py-3 dc:flex dc:items-center dc:justify-between dc:text-sm text-dc-text-muted\">\n <div>\n <span className=\"text-dc-text-secondary\">{filteredFields.length}</span>{' '}\n {mode === 'metrics' ? 'metrics' : mode === 'filter' ? 'fields' : 'dimensions'} available\n </div>\n {/* Keyboard shortcuts - hidden on mobile */}\n <div className=\"dc:hidden dc:md:flex dc:items-center dc:gap-4\">\n <span>\n <kbd className=\"dc:px-1.5 dc:py-0.5 bg-dc-surface-tertiary dc:rounded dc:text-xs\">↑↓</kbd> Navigate\n </span>\n <span>\n <kbd className=\"dc:px-1.5 dc:py-0.5 bg-dc-surface-tertiary dc:rounded dc:text-xs\">Enter</kbd> Select\n </span>\n <span>\n <kbd className=\"dc:px-1.5 dc:py-0.5 bg-dc-surface-tertiary dc:rounded dc:text-xs\">Shift</kbd>+Click Multi-select\n </span>\n <span>\n <kbd className=\"dc:px-1.5 dc:py-0.5 bg-dc-surface-tertiary dc:rounded dc:text-xs\">Esc</kbd> Close\n </span>\n </div>\n </div>\n </div>\n </div>\n )\n}\n","/**\n * DashboardFilterConfigModal Component\n *\n * Modal for editing dashboard filter configuration including:\n * - Filter label\n * - Field selection (via FieldSearchModal)\n * - Operator selection\n * - Value input (adapts to field/operator type)\n * - Date range selection for time dimensions\n *\n * Based on FilterConfigModal but adapted for DashboardFilter with:\n * - Label editing\n * - Clickable field section to change field\n * - \"Dashboard fields only\" toggle\n * - Delete action\n */\n\nimport { useState, useRef, useEffect, useCallback, ChangeEvent } from 'react'\nimport { getIcon } from '../../icons'\nimport type { DashboardFilter, SimpleFilter, FilterOperator } from '../../types'\nimport type { MetaResponse, DateRangeType, MetaField } from '../../shared/types'\nimport type { FieldType } from '../AnalysisBuilder/types'\nimport { FILTER_OPERATORS, DATE_RANGE_OPTIONS } from '../../shared/types'\nimport {\n getAvailableOperators,\n convertDateRangeTypeToValue,\n requiresNumberInput\n} from '../../shared/utils'\nimport { findFieldInSchema, getFieldTitle } from '../AnalysisBuilder/utils'\nimport { useFilterValues } from '../../hooks/useFilterValues'\nimport { useDebounce } from '../../hooks/useDebounce'\nimport FieldSearchModal from '../AnalysisBuilder/FieldSearchModal'\n\nconst CloseIcon = getIcon('close')\nconst ChevronDownIcon = getIcon('chevronDown')\nconst DimensionIcon = getIcon('dimension')\nconst TimeDimensionIcon = getIcon('timeDimension')\nconst MeasureIcon = getIcon('measure')\nconst EditIcon = getIcon('edit')\nconst EyeIcon = getIcon('eye')\nconst EyeOffIcon = getIcon('eyeOff')\n\ninterface DashboardFilterConfigModalProps {\n /** The dashboard filter being edited */\n filter: DashboardFilter\n /** Full schema (unfiltered) */\n fullSchema: MetaResponse | null\n /** Filtered schema (dashboard fields only) */\n filteredSchema: MetaResponse | null\n /** Whether the modal is open */\n isOpen: boolean\n /** Callback when user saves changes */\n onSave: (filter: DashboardFilter) => void\n /** Callback when user deletes the filter */\n onDelete: () => void\n /** Callback when user closes/cancels */\n onClose: () => void\n}\n\nexport default function DashboardFilterConfigModal({\n filter: initialFilter,\n fullSchema,\n filteredSchema,\n isOpen,\n onSave,\n onDelete,\n onClose\n}: DashboardFilterConfigModalProps) {\n // Local state for editing\n const [localLabel, setLocalLabel] = useState(initialFilter.label)\n const [localFilter, setLocalFilter] = useState<SimpleFilter>(initialFilter.filter as SimpleFilter)\n const [showAllFields, setShowAllFields] = useState(false)\n const [showFieldSearch, setShowFieldSearch] = useState(false)\n\n // Dropdown state\n const [isOperatorDropdownOpen, setIsOperatorDropdownOpen] = useState(false)\n const [isValueDropdownOpen, setIsValueDropdownOpen] = useState(false)\n const [isDateRangeDropdownOpen, setIsDateRangeDropdownOpen] = useState(false)\n\n // Date range state\n const [rangeType, setRangeType] = useState<DateRangeType>('this_month')\n const [numberValue, setNumberValue] = useState(1)\n const [searchText, setSearchText] = useState('')\n\n const containerRef = useRef<HTMLDivElement>(null)\n\n // Schema to use based on toggle\n const activeSchema = showAllFields ? fullSchema : filteredSchema\n\n // Sync state when filter changes or modal opens\n useEffect(() => {\n if (isOpen) {\n setLocalLabel(initialFilter.label)\n setLocalFilter(initialFilter.filter as SimpleFilter)\n }\n }, [initialFilter, isOpen])\n\n // Debounce search text for API calls\n const debouncedSearchText = useDebounce(searchText, 300)\n\n // Get field info\n const fieldInfo = findFieldInSchema(localFilter.member, activeSchema)\n const fieldType = fieldInfo?.field.type || 'string'\n const isTimeField = fieldType === 'time'\n const isMeasureField = fieldInfo?.fieldType === 'measure'\n const isDimensionField = fieldInfo?.fieldType === 'dimension'\n\n // Get display title for field\n const fieldTitle = getFieldTitle(localFilter.member, activeSchema)\n\n // Get operator metadata\n const operatorMeta = FILTER_OPERATORS[localFilter.operator as FilterOperator]\n\n // Get available operators for this field type\n const availableOperators = getAvailableOperators(fieldType)\n\n // Should show date range selector\n const shouldShowDateRange = isTimeField && localFilter.operator === 'inDateRange'\n\n // Should use combo box for value selection\n const shouldShowComboBox = (() => {\n const comboOperators = ['equals', 'notEquals', 'in', 'notIn']\n return comboOperators.includes(localFilter.operator) && isDimensionField && !isTimeField\n })()\n\n // Fetch distinct values for combo box\n const {\n values: distinctValues,\n loading: valuesLoading,\n error: valuesError,\n searchValues\n } = useFilterValues(localFilter.member, shouldShowComboBox)\n\n // Close dropdowns when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\n setIsOperatorDropdownOpen(false)\n setIsValueDropdownOpen(false)\n setIsDateRangeDropdownOpen(false)\n }\n }\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [])\n\n // Load values when dropdown opens\n useEffect(() => {\n if (isValueDropdownOpen && shouldShowComboBox && searchValues) {\n searchValues('', true)\n }\n }, [isValueDropdownOpen, shouldShowComboBox, searchValues])\n\n // Search when debounced text changes\n useEffect(() => {\n if (isValueDropdownOpen && shouldShowComboBox && searchValues && debouncedSearchText !== undefined) {\n searchValues(debouncedSearchText)\n }\n }, [debouncedSearchText, isValueDropdownOpen, shouldShowComboBox, searchValues])\n\n // Sync rangeType state with filter.dateRange\n useEffect(() => {\n if (!shouldShowDateRange || !localFilter.dateRange) return\n\n if (Array.isArray(localFilter.dateRange)) {\n setRangeType('custom')\n } else {\n const flexMatch = localFilter.dateRange.match(/^last (\\d+) (days|weeks|months|quarters|years)$/)\n const singularMatch = !flexMatch && localFilter.dateRange.match(/^last (day|week|month|quarter|year)$/)\n\n if (flexMatch) {\n const [, num, unit] = flexMatch\n setRangeType(`last_n_${unit}` as DateRangeType)\n setNumberValue(parseInt(num) || 1)\n } else if (singularMatch) {\n const [, unit] = singularMatch\n const pluralUnit = unit === 'day' ? 'days' :\n unit === 'week' ? 'weeks' :\n unit === 'month' ? 'months' :\n unit === 'quarter' ? 'quarters' : 'years'\n setRangeType(`last_n_${pluralUnit}` as DateRangeType)\n setNumberValue(1)\n } else {\n let found = false\n for (const option of DATE_RANGE_OPTIONS) {\n if (option.value !== 'custom' && !requiresNumberInput(option.value)) {\n if (convertDateRangeTypeToValue(option.value) === localFilter.dateRange) {\n setRangeType(option.value)\n found = true\n break\n }\n }\n }\n if (!found) setRangeType('custom')\n }\n }\n }, [localFilter.dateRange, shouldShowDateRange])\n\n // Handle field selection from FieldSearchModal\n const handleFieldSelected = useCallback((field: MetaField, _fieldType: FieldType) => {\n // Reset operator and values when changing field\n const newFieldType = field.type\n const newOperators = getAvailableOperators(newFieldType)\n const defaultOperator = newOperators[0]?.operator || 'equals'\n\n setLocalFilter({\n member: field.name,\n operator: defaultOperator as FilterOperator,\n values: []\n })\n setShowFieldSearch(false)\n }, [])\n\n // Handle operator change\n const handleOperatorChange = useCallback((operator: FilterOperator) => {\n setLocalFilter({\n member: localFilter.member,\n operator,\n values: []\n })\n setIsOperatorDropdownOpen(false)\n }, [localFilter.member])\n\n // Handle value selection from combo box\n const handleValueSelect = useCallback((value: unknown) => {\n const values = localFilter.values || []\n if (operatorMeta?.supportsMultipleValues) {\n if (!values.includes(value)) {\n setLocalFilter({ ...localFilter, values: [...values, value] })\n }\n } else {\n setLocalFilter({ ...localFilter, values: [value] })\n setIsValueDropdownOpen(false)\n }\n setSearchText('')\n }, [localFilter, operatorMeta?.supportsMultipleValues])\n\n // Handle value removal\n const handleValueRemove = useCallback((valueToRemove: unknown) => {\n const values = (localFilter.values || []).filter((v: unknown) => v !== valueToRemove)\n setLocalFilter({ ...localFilter, values })\n }, [localFilter])\n\n // Handle direct text/number input\n const handleDirectInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n if (operatorMeta?.valueType === 'number') {\n const numValue = parseFloat(value)\n if (!isNaN(numValue)) {\n setLocalFilter({ ...localFilter, values: [numValue] })\n } else if (value === '' || value === '-') {\n setLocalFilter({ ...localFilter, values: [] })\n }\n } else {\n setLocalFilter({ ...localFilter, values: value ? [value] : [] })\n }\n }, [localFilter, operatorMeta?.valueType])\n\n // Handle between range inputs\n const handleBetweenStartInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = parseFloat(e.target.value)\n const currentValues = localFilter.values?.length >= 2 ? localFilter.values : ['', '']\n const newValues = [!isNaN(value) ? value : '', currentValues[1]].filter(v => v !== '')\n setLocalFilter({ ...localFilter, values: newValues })\n }, [localFilter])\n\n const handleBetweenEndInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = parseFloat(e.target.value)\n const currentValues = localFilter.values?.length >= 2 ? localFilter.values : ['', '']\n const newValues = [currentValues[0], !isNaN(value) ? value : ''].filter(v => v !== '')\n setLocalFilter({ ...localFilter, values: newValues })\n }, [localFilter])\n\n // Handle date input\n const handleDateInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n setLocalFilter({ ...localFilter, values: value ? [value] : [] })\n }, [localFilter])\n\n // Handle date range type change\n const handleRangeTypeChange = useCallback((newRangeType: DateRangeType) => {\n setRangeType(newRangeType)\n setIsDateRangeDropdownOpen(false)\n\n let dateRange: string | string[]\n if (newRangeType === 'custom') {\n const today = new Date().toISOString().split('T')[0]\n dateRange = [today, today]\n } else if (requiresNumberInput(newRangeType)) {\n dateRange = convertDateRangeTypeToValue(newRangeType, numberValue)\n } else {\n dateRange = convertDateRangeTypeToValue(newRangeType)\n }\n\n setLocalFilter({ ...localFilter, dateRange } as SimpleFilter)\n }, [localFilter, numberValue])\n\n // Handle number value change for \"last N days/weeks/etc\"\n const handleNumberValueChange = useCallback((value: number) => {\n setNumberValue(value)\n if (requiresNumberInput(rangeType)) {\n const dateRange = convertDateRangeTypeToValue(rangeType, value)\n setLocalFilter({ ...localFilter, dateRange } as SimpleFilter)\n }\n }, [localFilter, rangeType])\n\n // Handle custom date range inputs\n const handleCustomStartDate = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const start = e.target.value\n const currentRange = Array.isArray(localFilter.dateRange) ? localFilter.dateRange : [localFilter.dateRange || '', '']\n const end = currentRange[1] || start\n setLocalFilter({ ...localFilter, dateRange: [start, end] } as SimpleFilter)\n }, [localFilter])\n\n const handleCustomEndDate = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const end = e.target.value\n const currentRange = Array.isArray(localFilter.dateRange) ? localFilter.dateRange : ['', localFilter.dateRange || '']\n const start = currentRange[0] || end\n setLocalFilter({ ...localFilter, dateRange: [start, end] } as SimpleFilter)\n }, [localFilter])\n\n // Handle save\n const handleSave = useCallback(() => {\n if (!localLabel.trim()) {\n alert('Filter label is required')\n return\n }\n\n // Don't require field selection for universal time filters\n if (!initialFilter.isUniversalTime && !localFilter.member) {\n alert('Please select a field for the filter')\n return\n }\n\n const updatedFilter: DashboardFilter = {\n id: initialFilter.id,\n label: localLabel,\n filter: localFilter,\n ...(initialFilter.isUniversalTime && { isUniversalTime: true })\n }\n\n onSave(updatedFilter)\n }, [initialFilter.id, initialFilter.isUniversalTime, localLabel, localFilter, onSave])\n\n // Get current operator label\n const operatorLabel = availableOperators.find(op => op.operator === localFilter.operator)?.label || localFilter.operator\n\n // Get current date range label\n const dateRangeLabel = DATE_RANGE_OPTIONS.find(opt => opt.value === rangeType)?.label || 'Select range'\n\n // Get icon for field type\n const FieldIcon = isTimeField ? TimeDimensionIcon : isMeasureField ? MeasureIcon : DimensionIcon\n const iconBgClass = isTimeField ? 'bg-dc-time-dimension' : isMeasureField ? 'bg-dc-measure' : 'bg-dc-dimension'\n const iconTextClass = isTimeField ? 'text-dc-time-dimension-text' : isMeasureField ? 'text-dc-measure-text' : 'text-dc-dimension-text'\n\n if (!isOpen) return null\n\n // Render value input based on operator type\n const renderValueInput = () => {\n // No value required for set/notSet\n if (!operatorMeta?.requiresValues) {\n return (\n <div className=\"dc:text-sm text-dc-text-muted dc:italic dc:py-2\">\n No value required\n </div>\n )\n }\n\n // Date range selector for inDateRange on time fields\n if (shouldShowDateRange) {\n return (\n <div className=\"dc:space-y-2\">\n {/* Range type dropdown */}\n <div className=\"dc:relative\">\n <button\n onClick={() => {\n setIsOperatorDropdownOpen(false)\n setIsValueDropdownOpen(false)\n setIsDateRangeDropdownOpen(!isDateRangeDropdownOpen)\n }}\n className=\"dc:w-full dc:flex dc:items-center dc:justify-between text-left dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text hover:bg-dc-surface-hover\"\n >\n <span className=\"dc:truncate\">{dateRangeLabel}</span>\n <ChevronDownIcon className={`dc:w-4 dc:h-4 text-dc-text-muted dc:shrink-0 dc:ml-2 dc:transition-transform ${\n isDateRangeDropdownOpen ? 'dc:rotate-180' : ''\n }`} />\n </button>\n\n {isDateRangeDropdownOpen && (\n <div className=\"dc:absolute dc:z-[60] dc:left-0 dc:right-0 dc:mt-1 bg-dc-surface dc:border border-dc-border dc:rounded dc:shadow-lg dc:max-h-48 dc:overflow-y-auto\">\n {DATE_RANGE_OPTIONS.map((option) => (\n <button\n key={option.value}\n onClick={() => handleRangeTypeChange(option.value)}\n className={`dc:w-full text-left dc:px-3 dc:py-2 dc:text-sm hover:bg-dc-surface-hover ${\n option.value === rangeType ? 'bg-dc-primary/10 text-dc-primary' : 'text-dc-text'\n }`}\n >\n {option.label}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Number input for \"last N\" ranges */}\n {requiresNumberInput(rangeType) && (\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <input\n type=\"number\"\n min=\"1\"\n max=\"1000\"\n value={numberValue}\n onChange={(e) => handleNumberValueChange(Math.max(1, parseInt(e.target.value) || 1))}\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text dc:w-20\"\n />\n <span className=\"dc:text-sm text-dc-text-muted\">\n {rangeType.replace('last_n_', '')}\n </span>\n </div>\n )}\n\n {/* Custom date inputs */}\n {rangeType === 'custom' && (\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <input\n type=\"date\"\n value={Array.isArray(localFilter.dateRange) ? localFilter.dateRange[0] : ''}\n onChange={handleCustomStartDate}\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-2 dc:py-2 bg-dc-surface text-dc-text\"\n />\n <span className=\"dc:text-sm text-dc-text-muted\">to</span>\n <input\n type=\"date\"\n value={Array.isArray(localFilter.dateRange) ? localFilter.dateRange[1] : ''}\n onChange={handleCustomEndDate}\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-2 dc:py-2 bg-dc-surface text-dc-text\"\n />\n </div>\n )}\n </div>\n )\n }\n\n // Between/notBetween range inputs\n if (localFilter.operator === 'between' || localFilter.operator === 'notBetween') {\n return (\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <input\n type=\"number\"\n value={localFilter.values?.[0] ?? ''}\n onChange={handleBetweenStartInput}\n placeholder=\"Min\"\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n <span className=\"dc:text-sm text-dc-text-muted\">to</span>\n <input\n type=\"number\"\n value={localFilter.values?.[1] ?? ''}\n onChange={handleBetweenEndInput}\n placeholder=\"Max\"\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n </div>\n )\n }\n\n // Date picker for date operators\n if (operatorMeta?.valueType === 'date') {\n return (\n <input\n type=\"date\"\n value={localFilter.values?.[0] || ''}\n onChange={handleDateInput}\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n )\n }\n\n // Number input\n if (operatorMeta?.valueType === 'number') {\n return (\n <input\n type=\"number\"\n value={localFilter.values?.[0] ?? ''}\n onChange={handleDirectInput}\n placeholder=\"Enter number\"\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n )\n }\n\n // Combo box for equals/notEquals/in/notIn on dimensions\n if (shouldShowComboBox) {\n return (\n <div className=\"dc:space-y-2\">\n {/* Selected values as tags */}\n {localFilter.values && localFilter.values.length > 0 && (\n <div className=\"dc:flex dc:flex-wrap dc:gap-1.5\">\n {localFilter.values.map((value: unknown, index: number) => (\n <span\n key={index}\n className=\"dc:inline-flex dc:items-center dc:gap-1 bg-dc-primary/10 text-dc-primary dc:text-sm dc:px-2 dc:py-1 dc:rounded\"\n >\n <span className=\"dc:max-w-[150px] dc:truncate\">{String(value)}</span>\n <button\n onClick={() => handleValueRemove(value)}\n className=\"hover:text-dc-danger\"\n >\n <CloseIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n </span>\n ))}\n </div>\n )}\n\n {/* Dropdown trigger */}\n <div className=\"dc:relative\">\n <button\n onClick={() => {\n setIsOperatorDropdownOpen(false)\n setIsDateRangeDropdownOpen(false)\n setIsValueDropdownOpen(!isValueDropdownOpen)\n }}\n className=\"dc:w-full dc:flex dc:items-center dc:justify-between text-left dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text hover:bg-dc-surface-hover\"\n >\n <span className=\"text-dc-text-muted dc:truncate\">\n {valuesLoading ? 'Loading...' : 'Select value...'}\n </span>\n <ChevronDownIcon className={`dc:w-4 dc:h-4 text-dc-text-muted dc:shrink-0 dc:ml-2 dc:transition-transform ${\n isValueDropdownOpen ? 'dc:rotate-180' : ''\n }`} />\n </button>\n\n {isValueDropdownOpen && (\n <div className=\"dc:absolute dc:z-[60] dc:left-0 dc:right-0 dc:mt-1 bg-dc-surface dc:border border-dc-border dc:rounded dc:shadow-lg dc:max-h-56 dc:overflow-hidden\">\n {/* Search input */}\n <div className=\"dc:p-2 dc:border-b border-dc-border\">\n <input\n type=\"text\"\n value={searchText}\n onChange={(e) => setSearchText(e.target.value)}\n placeholder=\"Search...\"\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n autoFocus\n />\n </div>\n\n {/* Values list */}\n <div className=\"dc:max-h-40 dc:overflow-y-auto\">\n {valuesLoading ? (\n <div className=\"dc:px-3 dc:py-2 dc:text-sm text-dc-text-muted\">Loading...</div>\n ) : valuesError ? (\n <div className=\"dc:px-3 dc:py-2 dc:text-sm text-dc-error\">Error: {valuesError}</div>\n ) : distinctValues.length === 0 ? (\n <div className=\"dc:px-3 dc:py-2 dc:text-sm text-dc-text-muted\">No values found</div>\n ) : (\n distinctValues.map((value, index) => {\n const isSelected = localFilter.values?.includes(value)\n return (\n <button\n key={`${value}-${index}`}\n onClick={() => handleValueSelect(value)}\n className={`dc:w-full text-left dc:px-3 dc:py-2 dc:text-sm hover:bg-dc-surface-hover ${\n isSelected ? 'bg-dc-primary/10 text-dc-primary' : 'text-dc-text'\n }`}\n >\n {String(value)}\n {isSelected && <span className=\"dc:float-right\">✓</span>}\n </button>\n )\n })\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n )\n }\n\n // Default: text input\n return (\n <input\n type=\"text\"\n value={localFilter.values?.[0] ?? ''}\n onChange={handleDirectInput}\n placeholder=\"Enter value...\"\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text placeholder-dc-text-muted\"\n />\n )\n }\n\n return (\n <>\n {/* Modal overlay */}\n <div\n className=\"dc:fixed dc:inset-0 dc:z-50 dc:flex dc:items-center dc:justify-center dc:p-4\"\n style={{ backgroundColor: 'var(--dc-overlay)' }}\n onClick={onClose}\n >\n <div\n ref={containerRef}\n className=\"bg-dc-surface dc:rounded-lg dc:border border-dc-border dc:max-w-md dc:w-full dc:max-h-[90vh] dc:overflow-auto\"\n style={{ boxShadow: 'var(--dc-shadow-xl)' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div className=\"dc:flex dc:items-center dc:justify-between dc:p-4 dc:border-b border-dc-border\">\n <h2 className=\"dc:text-lg dc:font-semibold text-dc-text\">Edit Filter</h2>\n <button\n onClick={onClose}\n className=\"dc:p-1 text-dc-text-muted hover:text-dc-text dc:transition-colors\"\n >\n <CloseIcon className=\"dc:w-5 dc:h-5\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"dc:p-4 dc:space-y-4\">\n {/* Filter Label */}\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">\n Filter Label\n </label>\n <input\n type=\"text\"\n value={localLabel}\n onChange={(e) => setLocalLabel(e.target.value)}\n placeholder=\"Enter filter label\"\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n </div>\n\n {/* Info box for universal time filters */}\n {initialFilter.isUniversalTime && (\n <div className=\"dc:p-3 dc:rounded-md bg-dc-info-bg dc:border border-dc-info-border\">\n <div className=\"dc:text-sm dc:font-medium text-dc-info dc:mb-1\">\n Universal Time Filter\n </div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n This filter applies to all time dimensions in mapped portlets.\n Users can select the date range when viewing the dashboard.\n </div>\n </div>\n )}\n\n {/* Field selection (not for universal time filters) */}\n {!initialFilter.isUniversalTime && (\n <div>\n <div className=\"dc:flex dc:items-center dc:justify-between dc:mb-2\">\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary\">\n Field\n </label>\n <button\n onClick={() => setShowAllFields(!showAllFields)}\n className=\"dc:flex dc:items-center dc:gap-1 dc:text-xs dc:px-2 dc:py-1 dc:rounded hover:bg-dc-surface-hover text-dc-text-muted\"\n title={showAllFields ? 'Show dashboard fields only' : 'Show all fields'}\n >\n {showAllFields ? (\n <>\n <EyeOffIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span>Dashboard</span>\n </>\n ) : (\n <>\n <EyeIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span>All</span>\n </>\n )}\n </button>\n </div>\n <button\n onClick={() => setShowFieldSearch(true)}\n className=\"dc:w-full dc:flex dc:items-center dc:gap-2 dc:p-3 bg-dc-surface-secondary dc:rounded hover:bg-dc-surface-tertiary dc:transition-colors\"\n >\n {localFilter.member ? (\n <>\n <span className={`dc:w-6 dc:h-6 dc:flex dc:items-center dc:justify-center dc:rounded ${iconBgClass} ${iconTextClass}`}>\n {FieldIcon && <FieldIcon className=\"dc:w-4 dc:h-4\" />}\n </span>\n <span className=\"dc:flex-1 dc:text-sm dc:font-medium text-dc-text text-left\">{fieldTitle}</span>\n </>\n ) : (\n <>\n <span className=\"dc:w-6 dc:h-6 dc:flex dc:items-center dc:justify-center dc:rounded bg-dc-surface-tertiary text-dc-text-muted\">\n <DimensionIcon className=\"dc:w-4 dc:h-4\" />\n </span>\n <span className=\"dc:flex-1 dc:text-sm text-dc-text-muted text-left\">Click to select a field</span>\n </>\n )}\n <EditIcon className=\"dc:w-4 dc:h-4 text-dc-text-muted\" />\n </button>\n </div>\n )}\n\n {/* Operator selector (only if field is selected) */}\n {(localFilter.member || initialFilter.isUniversalTime) && !initialFilter.isUniversalTime && (\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">\n Operator\n </label>\n <div className=\"dc:relative\">\n <button\n onClick={() => {\n setIsValueDropdownOpen(false)\n setIsDateRangeDropdownOpen(false)\n setIsOperatorDropdownOpen(!isOperatorDropdownOpen)\n }}\n className=\"dc:w-full dc:flex dc:items-center dc:justify-between text-left dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text hover:bg-dc-surface-hover\"\n >\n <span className=\"dc:truncate\">{operatorLabel}</span>\n <ChevronDownIcon className={`dc:w-4 dc:h-4 text-dc-text-muted dc:shrink-0 dc:ml-2 dc:transition-transform ${\n isOperatorDropdownOpen ? 'dc:rotate-180' : ''\n }`} />\n </button>\n\n {isOperatorDropdownOpen && (\n <div className=\"dc:absolute dc:z-[60] dc:left-0 dc:right-0 dc:mt-1 bg-dc-surface dc:border border-dc-border dc:rounded dc:shadow-lg dc:max-h-48 dc:overflow-y-auto\">\n {availableOperators.map((op) => (\n <button\n key={op.operator}\n onClick={() => handleOperatorChange(op.operator as FilterOperator)}\n className={`dc:w-full text-left dc:px-3 dc:py-2 dc:text-sm hover:bg-dc-surface-hover ${\n op.operator === localFilter.operator ? 'bg-dc-primary/10 text-dc-primary' : 'text-dc-text'\n }`}\n >\n {op.label}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Value input (only if field is selected, not for universal time filters) */}\n {localFilter.member && !initialFilter.isUniversalTime && (\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">\n Default Value\n </label>\n {renderValueInput()}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"dc:flex dc:items-center dc:justify-between dc:p-4 dc:border-t border-dc-border\">\n <button\n onClick={onDelete}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-danger hover:bg-dc-danger-bg dc:rounded dc:transition-colors\"\n >\n Delete Filter\n </button>\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <button\n onClick={onClose}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-text-secondary hover:text-dc-text dc:transition-colors\"\n >\n Cancel\n </button>\n <button\n onClick={handleSave}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-primary-content bg-dc-primary hover:bg-dc-primary-hover dc:rounded dc:transition-colors\"\n >\n Done\n </button>\n </div>\n </div>\n </div>\n </div>\n\n {/* Field Search Modal */}\n {showFieldSearch && (\n <FieldSearchModal\n isOpen={showFieldSearch}\n onClose={() => setShowFieldSearch(false)}\n onSelect={handleFieldSelected}\n mode=\"filter\"\n schema={activeSchema}\n selectedFields={localFilter.member ? [localFilter.member] : []}\n />\n )}\n </>\n )\n}\n","/**\n * FilterEditModal Component\n *\n * Modal for editing dashboard filter details including label, field, operator, and values.\n * Now delegates to DashboardFilterConfigModal for the modern search-based UX.\n *\n * Pattern: Self-contained modal with local state (matches PortletEditModal pattern)\n * - All editing state is local to the modal\n * - Changes only propagate on \"Done\" button click via onSave callback\n * - Cancel/close resets local state without saving\n */\n\nimport React, { useMemo, useCallback } from 'react'\nimport { extractDashboardFields } from '../../utils/filterUtils'\nimport DashboardFilterConfigModal from './DashboardFilterConfigModal'\nimport type { DashboardFilter, CubeMeta, DashboardConfig } from '../../types'\nimport type { MetaResponse } from '../../shared/types'\n\ninterface FilterEditModalProps {\n filter: DashboardFilter\n schema: CubeMeta | null\n dashboardConfig: DashboardConfig\n isOpen: boolean\n onSave: (filter: DashboardFilter) => void | Promise<void>\n onClose: () => void\n onDelete: () => void\n convertToMetaResponse: (cubeMeta: CubeMeta | null) => MetaResponse | null\n}\n\nconst FilterEditModal: React.FC<FilterEditModalProps> = ({\n filter,\n schema,\n dashboardConfig,\n isOpen,\n onSave,\n onClose,\n onDelete,\n convertToMetaResponse\n}) => {\n // Convert full schema to MetaResponse format\n const fullSchema = useMemo(() => {\n return convertToMetaResponse(schema)\n }, [schema, convertToMetaResponse])\n\n // Extract fields used in dashboard\n const dashboardFields = useMemo(() => {\n return extractDashboardFields(dashboardConfig)\n }, [dashboardConfig])\n\n // Create filtered schema showing only dashboard fields\n const filteredSchema = useMemo<MetaResponse | null>(() => {\n if (!schema) return null\n\n const filteredCubes = schema.cubes\n .map(cube => {\n const cubeName = cube.name\n\n const filteredMeasures = cube.measures.filter(measure => {\n const fullName = measure.name.includes('.')\n ? measure.name\n : `${cubeName}.${measure.name}`\n return dashboardFields.measures.has(fullName)\n })\n\n const filteredDimensions = cube.dimensions.filter(dimension => {\n const fullName = dimension.name.includes('.')\n ? dimension.name\n : `${cubeName}.${dimension.name}`\n return dashboardFields.dimensions.has(fullName) ||\n dashboardFields.timeDimensions.has(fullName)\n })\n\n if (filteredMeasures.length > 0 || filteredDimensions.length > 0) {\n return {\n ...cube,\n measures: filteredMeasures,\n dimensions: filteredDimensions\n }\n }\n\n return null\n })\n .filter((cube): cube is NonNullable<typeof cube> => cube !== null)\n\n const filteredCubeMeta: CubeMeta = {\n ...schema,\n cubes: filteredCubes\n }\n\n return convertToMetaResponse(filteredCubeMeta)\n }, [schema, dashboardFields, convertToMetaResponse])\n\n // Handle save with async support\n const handleSave = useCallback(async (updatedFilter: DashboardFilter) => {\n try {\n await onSave(updatedFilter)\n onClose()\n } catch (error) {\n console.error('Failed to save filter:', error)\n alert('Failed to save filter. Please try again.')\n }\n }, [onSave, onClose])\n\n if (!isOpen) return null\n\n return (\n <DashboardFilterConfigModal\n filter={filter}\n fullSchema={fullSchema}\n filteredSchema={filteredSchema}\n isOpen={isOpen}\n onSave={handleSave}\n onDelete={onDelete}\n onClose={onClose}\n />\n )\n}\n\nexport default FilterEditModal\n","/**\n * EditModeFilterList Component\n *\n * Displays filters in edit mode with chips showing filter labels and edit/delete actions\n */\n\nimport React from 'react'\nimport { getIcon } from '../../icons'\nimport type { DashboardFilter } from '../../types'\n\nconst FilterIcon = getIcon('filter')\nconst AddIcon = getIcon('add')\nconst CloseIcon = getIcon('close')\nconst EditIcon = getIcon('edit')\nconst ChevronDownIcon = getIcon('chevronDown')\nconst ClockIcon = getIcon('timeDimension')\n\ninterface EditModeFilterListProps {\n dashboardFilters: DashboardFilter[]\n onAddFilter: () => void\n onAddTimeFilter: () => void\n onEditFilter: (filterId: string) => void\n onRemoveFilter: (filterId: string) => void\n selectedFilterId?: string | null\n onFilterSelect?: (filterId: string) => void\n}\n\nconst EditModeFilterList: React.FC<EditModeFilterListProps> = ({\n dashboardFilters,\n onAddFilter,\n onAddTimeFilter,\n onEditFilter,\n onRemoveFilter,\n selectedFilterId,\n onFilterSelect\n}) => {\n const [isCollapsed, setIsCollapsed] = React.useState(false)\n\n // Render compact filter chip - just label + edit + delete\n const renderFilterChip = (dashboardFilter: DashboardFilter) => {\n const { id, label, isUniversalTime } = dashboardFilter\n const isSelected = selectedFilterId === id\n\n // Use calendar icon for universal time filters, funnel for regular filters\n const IconComponent = isUniversalTime ? ClockIcon : FilterIcon\n\n return (\n <div\n key={id}\n className={`dc:inline-flex dc:items-center dc:gap-1.5 dc:px-2.5 dc:py-1 dc:rounded-md dc:border dc:text-xs dc:transition-all ${\n 'dc:cursor-pointer dc:hover:shadow-md'\n }`}\n style={{\n backgroundColor: isSelected ? 'var(--dc-primary)' : 'var(--dc-surface)',\n borderColor: isSelected ? 'var(--dc-primary)' : 'var(--dc-border)',\n borderWidth: isSelected ? '2px' : '1px',\n color: isSelected ? 'white' : 'var(--dc-text)',\n boxShadow: isSelected ? '0 0 0 3px rgba(var(--dc-primary-rgb), 0.1)' : 'none'\n }}\n onClick={() => {\n if (onFilterSelect) {\n onFilterSelect(id)\n }\n }}\n >\n <IconComponent\n className=\"dc:w-3.5 dc:h-3.5 dc:shrink-0\"\n style={{ color: isSelected ? 'white' : 'var(--dc-primary)' }}\n />\n <span className=\"dc:font-medium dc:truncate\">{label}</span>\n\n {!isSelected && (\n <div className=\"dc:flex dc:items-center dc:gap-0.5 dc:ml-1\" onClick={(e) => e.stopPropagation()}>\n <button\n onClick={() => onEditFilter(id)}\n className=\"dc:p-0.5 hover:bg-dc-hover dc:rounded dc:transition-colors\"\n title=\"Edit filter\"\n >\n <EditIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n <button\n onClick={() => onRemoveFilter(id)}\n className=\"dc:p-0.5 hover:bg-dc-danger-bg hover:text-dc-danger dc:rounded dc:transition-colors\"\n title=\"Remove filter\"\n >\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n </div>\n )}\n </div>\n )\n }\n\n return (\n <>\n {/* Mobile: Header + collapsible content */}\n <div className=\"dc:md:hidden\">\n {/* Header - clickable to toggle */}\n <div\n className=\"dc:px-4 dc:py-2 dc:flex dc:items-center dc:justify-between dc:cursor-pointer\"\n onClick={() => setIsCollapsed(!isCollapsed)}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <FilterIcon className=\"dc:w-4 dc:h-4 dc:shrink-0\" style={{ color: 'var(--dc-primary)' }} />\n <h3 className=\"dc:text-sm dc:font-semibold\" style={{ color: 'var(--dc-text)' }}>\n Filters\n </h3>\n {dashboardFilters.length > 0 && (\n <span\n className=\"dc:px-1.5 dc:py-0.5 dc:rounded-full dc:text-xs dc:font-medium\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n {dashboardFilters.length}\n </span>\n )}\n <ChevronDownIcon\n className={`dc:w-4 dc:h-4 dc:transition-transform ${isCollapsed ? '' : 'dc:rotate-180'}`}\n style={{ color: 'var(--dc-text-secondary)' }}\n />\n </div>\n\n <div className=\"dc:flex dc:items-center dc:gap-1\">\n {/* Only show Date Range button if no universal time filter exists */}\n {!dashboardFilters.some(f => f.isUniversalTime) && (\n <button\n onClick={(e) => {\n e.stopPropagation()\n onAddTimeFilter()\n }}\n className=\"dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded-md dc:text-xs dc:font-medium dc:transition-colors dc:hover:opacity-80\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n color: 'var(--dc-primary)',\n border: '1px solid var(--dc-border)'\n }}\n title=\"Add date range filter (applies to all time dimensions)\"\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <ClockIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n )}\n <button\n onClick={(e) => {\n e.stopPropagation()\n onAddFilter()\n }}\n className=\"dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded-md dc:text-xs dc:font-medium dc:transition-colors dc:hover:opacity-80\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n </div>\n </div>\n\n {/* Mobile Filter Chips - Collapsible */}\n {dashboardFilters.length > 0 && !isCollapsed && (\n <div className=\"dc:px-4 dc:pb-2 dc:flex dc:flex-col dc:gap-2\">\n {dashboardFilters.map(renderFilterChip)}\n </div>\n )}\n\n {/* Mobile Empty State */}\n {dashboardFilters.length === 0 && !isCollapsed && (\n <div className=\"dc:px-4 dc:pb-2\">\n <div\n className=\"dc:text-xs dc:p-2 dc:rounded-md text-center\"\n style={{\n backgroundColor: 'var(--dc-surface-secondary)',\n color: 'var(--dc-text-secondary)'\n }}\n >\n No filters configured. Click \"Add\" to create one.\n </div>\n </div>\n )}\n </div>\n\n {/* Desktop: Single row layout */}\n <div className=\"dc:hidden dc:md:flex dc:md:items-center dc:md:gap-3 dc:px-4 dc:py-2\">\n {/* Header Section */}\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:shrink-0\">\n <FilterIcon className=\"dc:w-4 dc:h-4 dc:shrink-0\" style={{ color: 'var(--dc-primary)' }} />\n <h3 className=\"dc:text-sm dc:font-semibold dc:whitespace-nowrap\" style={{ color: 'var(--dc-text)' }}>\n Filters\n </h3>\n {dashboardFilters.length > 0 && (\n <span\n className=\"dc:px-1.5 dc:py-0.5 dc:rounded-full dc:text-xs dc:font-medium\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n {dashboardFilters.length}\n </span>\n )}\n </div>\n\n {/* Filter Chips Section - grows to fill space */}\n {dashboardFilters.length > 0 ? (\n <div className=\"dc:flex dc:flex-wrap dc:gap-2 dc:flex-1 dc:min-w-0\">\n {dashboardFilters.map(renderFilterChip)}\n </div>\n ) : (\n <div className=\"dc:flex-1 dc:min-w-0\">\n <div\n className=\"dc:text-xs dc:px-3 dc:py-1 dc:rounded-md dc:inline-block\"\n style={{\n backgroundColor: 'var(--dc-surface-secondary)',\n color: 'var(--dc-text-secondary)'\n }}\n >\n No filters configured. Click \"Add\" to create one.\n </div>\n </div>\n )}\n\n {/* Add Button Section */}\n <div className=\"dc:flex dc:items-center dc:gap-1 dc:shrink-0\">\n {/* Only show Date Range button if no universal time filter exists */}\n {!dashboardFilters.some(f => f.isUniversalTime) && (\n <button\n onClick={onAddTimeFilter}\n className=\"dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded-md dc:text-xs dc:font-medium dc:transition-colors dc:hover:opacity-80\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n color: 'var(--dc-primary)',\n border: '1px solid var(--dc-border)'\n }}\n title=\"Add date range filter (applies to all time dimensions)\"\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span>Date Range</span>\n </button>\n )}\n <button\n onClick={onAddFilter}\n className=\"dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded-md dc:text-xs dc:font-medium dc:transition-colors dc:hover:opacity-80\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span>Filter</span>\n </button>\n </div>\n </div>\n </>\n )\n}\n\nexport default EditModeFilterList\n","/**\n * Utility functions for QueryBuilder components\n *\n * Common filter/query utilities are imported from the shared module\n * to avoid duplication. Only component-specific utilities are defined here.\n */\n\nimport type { CubeQuery, Filter, SimpleFilter } from '../../types'\nimport type { MetaField, MetaResponse } from '../QueryBuilder/types'\nimport { FILTER_OPERATORS } from '../QueryBuilder/types'\n\n// ============================================================================\n// Re-export common utilities from shared module (canonical source)\n// ============================================================================\nexport {\n // Filter type guards\n isSimpleFilter,\n isGroupFilter,\n isAndFilter,\n isOrFilter,\n // Filter manipulation\n flattenFilters,\n countFilters,\n createSimpleFilter,\n createAndFilter,\n createOrFilter,\n cleanupFilters,\n // Filter transformation\n transformFiltersForServer,\n transformFiltersFromServer,\n // Query utilities\n hasQueryContent,\n cleanQuery,\n cleanQueryForServer,\n transformQueryForUI,\n // Schema utilities\n getCubeNameFromField,\n getFieldType,\n getFieldTitle,\n getAvailableOperators,\n getAllFilterableFields,\n // Date utilities\n convertDateRangeTypeToValue,\n requiresNumberInput,\n formatDateForCube,\n} from '../../shared/utils'\n\n// Import for internal use\nimport {\n isSimpleFilter,\n isGroupFilter,\n getCubeNameFromField,\n getAllFilterableFields,\n getFieldType,\n} from '../../shared/utils'\n\n// ============================================================================\n// Component-specific utilities (unique to this module)\n// ============================================================================\n\n/**\n * Check if a field is selected in the current query\n */\nexport function isFieldSelected(\n fieldName: string,\n fieldType: 'measures' | 'dimensions' | 'timeDimensions',\n query: CubeQuery\n): boolean {\n switch (fieldType) {\n case 'measures':\n return query.measures?.includes(fieldName) || false\n case 'dimensions':\n return query.dimensions?.includes(fieldName) || false\n case 'timeDimensions':\n return query.timeDimensions?.some(td => td.dimension === fieldName) || false\n default:\n return false\n }\n}\n\n/**\n * Get all time dimension fields from schema\n */\nexport function getTimeDimensionFields(schema: MetaResponse): MetaField[] {\n const timeDimensions: MetaField[] = []\n\n schema.cubes.forEach(cube => {\n cube.dimensions.forEach(dimension => {\n if (dimension.type === 'time') {\n timeDimensions.push(dimension)\n }\n })\n })\n\n return timeDimensions\n}\n\n/**\n * Get all non-time dimension fields from schema\n */\nexport function getRegularDimensionFields(schema: MetaResponse): MetaField[] {\n const dimensions: MetaField[] = []\n\n schema.cubes.forEach(cube => {\n cube.dimensions.forEach(dimension => {\n if (dimension.type !== 'time') {\n dimensions.push(dimension)\n }\n })\n })\n\n return dimensions\n}\n\n/**\n * Get all measure fields from schema\n */\nexport function getMeasureFields(schema: MetaResponse): MetaField[] {\n const measures: MetaField[] = []\n\n schema.cubes.forEach(cube => {\n cube.measures.forEach(measure => {\n measures.push(measure)\n })\n })\n\n return measures\n}\n\n/**\n * Get count of selected fields across all types\n */\nexport function getSelectedFieldsCount(query: CubeQuery): number {\n let count = 0\n if (query.measures) count += query.measures.length\n if (query.dimensions) count += query.dimensions.length\n if (query.timeDimensions) count += query.timeDimensions.length\n return count\n}\n\n/**\n * Group fields by cube name\n */\nexport function groupFieldsByCube(fields: MetaField[]): Record<string, MetaField[]> {\n return fields.reduce((groups, field) => {\n const cubeName = getCubeNameFromField(field.name)\n if (!groups[cubeName]) {\n groups[cubeName] = []\n }\n groups[cubeName].push(field)\n return groups\n }, {} as Record<string, MetaField[]>)\n}\n\n/**\n * Create an empty query object\n */\nexport function createEmptyQuery(): CubeQuery {\n return {}\n}\n\n/**\n * Get all filterable fields from schema (measures, dimensions, and time dimensions)\n * Returns ALL fields if no query provided, or only query fields if query provided\n */\nexport function getFilterableFields(schema: MetaResponse, query?: CubeQuery): MetaField[] {\n const allFields: MetaField[] = []\n\n schema.cubes.forEach(cube => {\n allFields.push(...cube.measures)\n allFields.push(...cube.dimensions)\n })\n\n // If no query provided, return all fields\n if (!query) {\n return allFields.sort((a, b) => a.name.localeCompare(b.name))\n }\n\n // Get currently selected fields from the query\n const selectedFields = new Set<string>()\n\n // Add measures\n if (query.measures) {\n query.measures.forEach(measure => selectedFields.add(measure))\n }\n\n // Add dimensions\n if (query.dimensions) {\n query.dimensions.forEach(dimension => selectedFields.add(dimension))\n }\n\n // Add time dimensions\n if (query.timeDimensions) {\n query.timeDimensions.forEach(td => selectedFields.add(td.dimension))\n }\n\n // Filter to only include selected fields\n const filterableFields = allFields.filter(field => selectedFields.has(field.name))\n\n return filterableFields.sort((a, b) => a.name.localeCompare(b.name))\n}\n\n/**\n * Get organized filter field options with query fields prioritized at top\n */\nexport function getOrganizedFilterFields(schema: MetaResponse, query?: CubeQuery): {\n queryFields: MetaField[]\n allFields: MetaField[]\n} {\n const allFields = getAllFilterableFields(schema)\n\n if (!query) {\n return {\n queryFields: [],\n allFields\n }\n }\n\n // Get currently selected fields from the query\n const selectedFields = new Set<string>()\n\n // Add measures\n if (query.measures) {\n query.measures.forEach(measure => selectedFields.add(measure))\n }\n\n // Add dimensions\n if (query.dimensions) {\n query.dimensions.forEach(dimension => selectedFields.add(dimension))\n }\n\n // Add time dimensions\n if (query.timeDimensions) {\n query.timeDimensions.forEach(td => selectedFields.add(td.dimension))\n }\n\n // Split fields into query fields and other fields\n const queryFields = allFields.filter(field => selectedFields.has(field.name))\n\n return {\n queryFields,\n allFields\n }\n}\n\n/**\n * Validate a filter\n */\nexport function validateFilter(filter: SimpleFilter, schema: MetaResponse): {\n isValid: boolean\n errors: string[]\n} {\n const errors: string[] = []\n\n // Check if field exists\n const fields = getFilterableFields(schema)\n const fieldExists = fields.some(f => f.name === filter.member)\n\n if (!fieldExists) {\n errors.push(`Field \"${filter.member}\" does not exist`)\n }\n\n // Check if operator is valid for field type\n if (fieldExists) {\n const fieldType = getFieldType(filter.member, schema)\n const operatorMeta = FILTER_OPERATORS[filter.operator]\n\n if (!operatorMeta) {\n errors.push(`Invalid operator \"${filter.operator}\"`)\n } else if (!operatorMeta.fieldTypes.includes(fieldType)) {\n errors.push(`Operator \"${filter.operator}\" is not valid for field type \"${fieldType}\"`)\n } else {\n // Check values\n if (operatorMeta.requiresValues && (!filter.values || filter.values.length === 0)) {\n errors.push(`Operator \"${filter.operator}\" requires values`)\n }\n\n if (!operatorMeta.supportsMultipleValues && filter.values && filter.values.length > 1) {\n errors.push(`Operator \"${filter.operator}\" does not support multiple values`)\n }\n }\n }\n\n return {\n isValid: errors.length === 0,\n errors\n }\n}\n\n/**\n * Clean up filters by removing any that reference fields not in the current query (legacy)\n * Only used for backward compatibility - filters on non-query fields are now supported\n */\nexport function cleanupFiltersLegacy(filters: Filter[], query: CubeQuery): Filter[] {\n if (!filters || filters.length === 0) {\n return []\n }\n\n // Get currently selected fields from the query\n const selectedFields = new Set<string>()\n\n // Add measures\n if (query.measures) {\n query.measures.forEach(measure => selectedFields.add(measure))\n }\n\n // Add dimensions\n if (query.dimensions) {\n query.dimensions.forEach(dimension => selectedFields.add(dimension))\n }\n\n // Add time dimensions\n if (query.timeDimensions) {\n query.timeDimensions.forEach(td => selectedFields.add(td.dimension))\n }\n\n // Recursively clean filters\n const cleanFilter = (filter: Filter): Filter | null => {\n if (isSimpleFilter(filter)) {\n // Remove filter if its field is not selected\n return selectedFields.has(filter.member) ? filter : null\n } else if (isGroupFilter(filter)) {\n // Clean group recursively\n const cleanedFilters = filter.filters.map(cleanFilter).filter(f => f !== null) as Filter[]\n return cleanedFilters.length > 0 ? { type: filter.type, filters: cleanedFilters } : null\n }\n return null\n }\n\n // Clean all filters and remove nulls\n const cleanedFilters = filters.map(cleanFilter).filter(f => f !== null) as Filter[]\n\n return cleanedFilters\n}\n\n/**\n * Get the time dimensions that have date ranges applied\n */\nexport function getTimeDimensionsWithDateRanges(query: CubeQuery): Record<string, string | string[]> {\n const dateRanges: Record<string, string | string[]> = {}\n\n if (query.timeDimensions) {\n query.timeDimensions.forEach(td => {\n if (td.dateRange) {\n dateRanges[td.dimension] = td.dateRange\n }\n })\n }\n\n return dateRanges\n}\n\n/**\n * Check if a query has any time dimensions\n */\nexport function hasTimeDimensions(query: CubeQuery): boolean {\n return Boolean(query.timeDimensions && query.timeDimensions.length > 0)\n}\n\n// ============================================================================\n// Sorting utility functions\n// ============================================================================\n\n/**\n * Clean up order object by removing fields that are no longer in the query\n */\nexport function cleanupOrder(order: Record<string, 'asc' | 'desc'> | undefined, query: CubeQuery): Record<string, 'asc' | 'desc'> | undefined {\n if (!order) return undefined\n\n const allFields = [\n ...(query.measures || []),\n ...(query.dimensions || []),\n ...(query.timeDimensions || []).map(td => td.dimension)\n ]\n\n const cleanedOrder: Record<string, 'asc' | 'desc'> = {}\n for (const [field, direction] of Object.entries(order)) {\n if (allFields.includes(field)) {\n cleanedOrder[field] = direction\n }\n }\n\n return Object.keys(cleanedOrder).length > 0 ? cleanedOrder : undefined\n}\n\n/**\n * Get sort direction for a field from the order object\n */\nexport function getSortDirection(fieldName: string, order: Record<string, 'asc' | 'desc'> | undefined): 'asc' | 'desc' | null {\n return order?.[fieldName] || null\n}\n\n/**\n * Get tooltip text for sort button based on current direction\n */\nexport function getSortTooltip(direction: 'asc' | 'desc' | null): string {\n switch (direction) {\n case 'asc':\n return 'Sorted ascending (click for descending)'\n case 'desc':\n return 'Sorted descending (click to remove)'\n default:\n return 'Click to sort ascending'\n }\n}\n\n/**\n * Get next sort direction in the cycle: null -> asc -> desc -> null\n */\nexport function getNextSortDirection(current: 'asc' | 'desc' | null): 'asc' | 'desc' | null {\n switch (current) {\n case null:\n return 'asc'\n case 'asc':\n return 'desc'\n case 'desc':\n return null\n default:\n return 'asc'\n }\n}\n\n// ============================================================================\n// Compact filter bar date utilities\n// ============================================================================\n\n/**\n * Date preset configuration for compact filter bar\n */\nexport interface DatePreset {\n id: string\n label: string\n value: string\n}\n\nexport const DATE_PRESETS: DatePreset[] = [\n { id: 'today', label: 'Today', value: 'today' },\n { id: 'yesterday', label: 'Yesterday', value: 'yesterday' },\n { id: '7d', label: '7D', value: 'last 7 days' },\n { id: '30d', label: '30D', value: 'last 30 days' },\n { id: '3m', label: '3M', value: 'last 3 months' },\n { id: '6m', label: '6M', value: 'last 6 months' },\n { id: '12m', label: '12M', value: 'last 12 months' }\n]\n\nexport const XTD_OPTIONS: DatePreset[] = [\n { id: 'wtd', label: 'Week to Date', value: 'this week' },\n { id: 'mtd', label: 'Month to Date', value: 'this month' },\n { id: 'qtd', label: 'Quarter to Date', value: 'this quarter' },\n { id: 'ytd', label: 'Year to Date', value: 'this year' }\n]\n\n/**\n * Calculate actual date range from a preset string\n * Returns start and end dates for display purposes\n */\nexport function calculateDateRange(preset: string): { start: Date, end: Date } | null {\n const today = new Date()\n today.setHours(0, 0, 0, 0)\n\n const endOfToday = new Date(today)\n endOfToday.setHours(23, 59, 59, 999)\n\n switch (preset.toLowerCase()) {\n case 'today': {\n return { start: today, end: endOfToday }\n }\n case 'yesterday': {\n const yesterday = new Date(today)\n yesterday.setDate(yesterday.getDate() - 1)\n const endOfYesterday = new Date(yesterday)\n endOfYesterday.setHours(23, 59, 59, 999)\n return { start: yesterday, end: endOfYesterday }\n }\n case 'this week': {\n const startOfWeek = new Date(today)\n const dayOfWeek = startOfWeek.getDay()\n const diff = dayOfWeek === 0 ? 6 : dayOfWeek - 1 // Monday as first day\n startOfWeek.setDate(startOfWeek.getDate() - diff)\n return { start: startOfWeek, end: endOfToday }\n }\n case 'this month': {\n const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1)\n return { start: startOfMonth, end: endOfToday }\n }\n case 'this quarter': {\n const quarter = Math.floor(today.getMonth() / 3)\n const startOfQuarter = new Date(today.getFullYear(), quarter * 3, 1)\n return { start: startOfQuarter, end: endOfToday }\n }\n case 'this year': {\n const startOfYear = new Date(today.getFullYear(), 0, 1)\n return { start: startOfYear, end: endOfToday }\n }\n default: {\n // Handle \"last N units\" patterns\n const lastNMatch = preset.match(/^last\\s+(\\d+)\\s+(day|days|week|weeks|month|months|quarter|quarters|year|years)$/i)\n if (lastNMatch) {\n const num = parseInt(lastNMatch[1], 10)\n const unit = lastNMatch[2].toLowerCase()\n const startDate = new Date(today)\n\n if (unit === 'day' || unit === 'days') {\n startDate.setDate(startDate.getDate() - num + 1)\n } else if (unit === 'week' || unit === 'weeks') {\n startDate.setDate(startDate.getDate() - (num * 7) + 1)\n } else if (unit === 'month' || unit === 'months') {\n startDate.setMonth(startDate.getMonth() - num)\n startDate.setDate(startDate.getDate() + 1)\n } else if (unit === 'quarter' || unit === 'quarters') {\n startDate.setMonth(startDate.getMonth() - (num * 3))\n startDate.setDate(startDate.getDate() + 1)\n } else if (unit === 'year' || unit === 'years') {\n startDate.setFullYear(startDate.getFullYear() - num)\n startDate.setDate(startDate.getDate() + 1)\n }\n\n return { start: startDate, end: endOfToday }\n }\n\n // Handle \"last week\", \"last month\", etc. (without number)\n const lastUnitMatch = preset.match(/^last\\s+(week|month|quarter|year)$/i)\n if (lastUnitMatch) {\n const unit = lastUnitMatch[1].toLowerCase()\n\n if (unit === 'week') {\n const endOfLastWeek = new Date(today)\n const dayOfWeek = endOfLastWeek.getDay()\n const diff = dayOfWeek === 0 ? 6 : dayOfWeek - 1\n endOfLastWeek.setDate(endOfLastWeek.getDate() - diff - 1)\n endOfLastWeek.setHours(23, 59, 59, 999)\n\n const startOfLastWeek = new Date(endOfLastWeek)\n startOfLastWeek.setDate(startOfLastWeek.getDate() - 6)\n startOfLastWeek.setHours(0, 0, 0, 0)\n\n return { start: startOfLastWeek, end: endOfLastWeek }\n } else if (unit === 'month') {\n const startOfLastMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1)\n const endOfLastMonth = new Date(today.getFullYear(), today.getMonth(), 0)\n endOfLastMonth.setHours(23, 59, 59, 999)\n return { start: startOfLastMonth, end: endOfLastMonth }\n } else if (unit === 'quarter') {\n const currentQuarter = Math.floor(today.getMonth() / 3)\n const lastQuarter = currentQuarter === 0 ? 3 : currentQuarter - 1\n const lastQuarterYear = currentQuarter === 0 ? today.getFullYear() - 1 : today.getFullYear()\n const startOfLastQuarter = new Date(lastQuarterYear, lastQuarter * 3, 1)\n const endOfLastQuarter = new Date(lastQuarterYear, lastQuarter * 3 + 3, 0)\n endOfLastQuarter.setHours(23, 59, 59, 999)\n return { start: startOfLastQuarter, end: endOfLastQuarter }\n } else if (unit === 'year') {\n const startOfLastYear = new Date(today.getFullYear() - 1, 0, 1)\n const endOfLastYear = new Date(today.getFullYear() - 1, 11, 31)\n endOfLastYear.setHours(23, 59, 59, 999)\n return { start: startOfLastYear, end: endOfLastYear }\n }\n }\n\n return null\n }\n }\n}\n\n/**\n * Format a date range for display (e.g., \"Jan 1, 2024 - Jan 31, 2024\")\n */\nexport function formatDateRangeDisplay(start: Date, end: Date): string {\n const options: Intl.DateTimeFormatOptions = {\n month: 'short',\n day: 'numeric',\n year: 'numeric'\n }\n\n const startStr = start.toLocaleDateString('en-US', options)\n const endStr = end.toLocaleDateString('en-US', options)\n\n // If same day, just show one date\n if (startStr === endStr) {\n return startStr\n }\n\n // If same year, omit year from start date\n if (start.getFullYear() === end.getFullYear()) {\n const startNoYear: Intl.DateTimeFormatOptions = { month: 'short', day: 'numeric' }\n return `${start.toLocaleDateString('en-US', startNoYear)} - ${endStr}`\n }\n\n return `${startStr} - ${endStr}`\n}\n\n/**\n * Detect preset ID from a date range value\n * Returns the preset ID (e.g., '7d', 'mtd') or 'custom' if not a preset\n */\nexport function detectPresetFromDateRange(dateRange: string | string[] | undefined): string | null {\n if (!dateRange) return null\n\n // Custom date range (array of dates)\n if (Array.isArray(dateRange)) {\n return 'custom'\n }\n\n const normalizedRange = dateRange.toLowerCase().trim()\n\n // Check regular presets\n for (const preset of DATE_PRESETS) {\n if (preset.value.toLowerCase() === normalizedRange) {\n return preset.id\n }\n }\n\n // Check XTD presets\n for (const preset of XTD_OPTIONS) {\n if (preset.value.toLowerCase() === normalizedRange) {\n return preset.id\n }\n }\n\n // Check for \"last N units\" patterns that don't match exact presets\n const lastNMatch = normalizedRange.match(/^last\\s+(\\d+)\\s+(day|days|week|weeks|month|months|quarter|quarters|year|years)$/i)\n if (lastNMatch) {\n return 'custom' // Dynamic last N is treated as custom\n }\n\n return 'custom'\n}\n\n/**\n * Format filter value for display in a compact chip\n */\nexport function formatFilterValueDisplay(values: any[], operator: string): string {\n if (!values || values.length === 0) {\n // Handle operators that don't need values\n if (operator === 'set') return 'is set'\n if (operator === 'notSet') return 'is not set'\n if (operator === 'isEmpty') return 'is empty'\n if (operator === 'isNotEmpty') return 'is not empty'\n return ''\n }\n\n const formattedValues = values.map(v => {\n if (v === true) return 'true'\n if (v === false) return 'false'\n if (v === null || v === undefined) return 'null'\n return String(v)\n })\n\n switch (operator) {\n case 'equals':\n return formattedValues.length === 1 ? `= ${formattedValues[0]}` : `in (${formattedValues.join(', ')})`\n case 'notEquals':\n return formattedValues.length === 1 ? `!= ${formattedValues[0]}` : `not in (${formattedValues.join(', ')})`\n case 'contains':\n return `contains \"${formattedValues[0]}\"`\n case 'notContains':\n return `!contains \"${formattedValues[0]}\"`\n case 'startsWith':\n return `starts with \"${formattedValues[0]}\"`\n case 'endsWith':\n return `ends with \"${formattedValues[0]}\"`\n case 'gt':\n return `> ${formattedValues[0]}`\n case 'gte':\n return `>= ${formattedValues[0]}`\n case 'lt':\n return `< ${formattedValues[0]}`\n case 'lte':\n return `<= ${formattedValues[0]}`\n case 'between':\n return `${formattedValues[0]} - ${formattedValues[1] || '?'}`\n case 'in':\n return `in (${formattedValues.join(', ')})`\n case 'notIn':\n return `not in (${formattedValues.join(', ')})`\n case 'set':\n return 'is set'\n case 'notSet':\n return 'is not set'\n default:\n return formattedValues.join(', ')\n }\n}\n","/**\n * DatePresetChips Component\n *\n * Quick-select date preset chips for the compact filter bar.\n * Displays common date ranges as clickable chips (Today, Yesterday, 7D, 30D, etc.)\n */\n\nimport React, { useMemo } from 'react'\nimport { DATE_PRESETS, calculateDateRange, formatDateRangeDisplay } from '../shared/utils'\n\ninterface DatePresetChipsProps {\n activePreset: string | null\n onPresetSelect: (presetValue: string) => void\n disabled?: boolean\n}\n\nconst DatePresetChips: React.FC<DatePresetChipsProps> = ({\n activePreset,\n onPresetSelect,\n disabled = false\n}) => {\n // Memoize tooltip content for each preset\n const presetTooltips = useMemo(() => {\n const tooltips: Record<string, string> = {}\n for (const preset of DATE_PRESETS) {\n const range = calculateDateRange(preset.value)\n if (range) {\n tooltips[preset.id] = formatDateRangeDisplay(range.start, range.end)\n }\n }\n return tooltips\n }, [])\n\n return (\n <div className=\"dc:flex dc:items-center dc:gap-1\">\n {DATE_PRESETS.map(preset => {\n const isActive = activePreset === preset.id\n const tooltip = presetTooltips[preset.id]\n\n return (\n <button\n key={preset.id}\n type=\"button\"\n onClick={() => onPresetSelect(preset.value)}\n disabled={disabled}\n title={tooltip}\n className={`\n dc:px-2.5 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:transition-colors\n dc:focus:outline-none dc:focus:ring-2 dc:focus:ring-offset-1\n dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\n ${isActive ? 'dc:shadow-sm' : 'dc:border'}\n `}\n style={{\n backgroundColor: isActive ? 'var(--dc-primary)' : 'var(--dc-surface)',\n color: isActive ? 'white' : 'var(--dc-text)',\n borderColor: isActive ? 'transparent' : 'var(--dc-border)',\n ...(disabled ? {} : {\n cursor: 'pointer'\n })\n }}\n onMouseEnter={(e) => {\n if (!isActive && !disabled) {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive && !disabled) {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface)'\n }\n }}\n >\n {preset.label}\n </button>\n )\n })}\n </div>\n )\n}\n\nexport default DatePresetChips\n","/**\n * CustomDateDropdown Component\n *\n * Tabbed dropdown for custom date selection with Fixed, Since, and Last tabs.\n */\n\nimport React, { useState, useEffect, useRef, useCallback } from 'react'\nimport { formatDateForCube, convertDateRangeTypeToValue } from '../shared/utils'\n\ntype TabType = 'fixed' | 'since' | 'last'\ntype LastUnit = 'days' | 'weeks' | 'months' | 'quarters' | 'years'\n\ninterface CustomDateDropdownProps {\n isOpen: boolean\n onClose: () => void\n onDateRangeChange: (dateRange: string | string[]) => void\n currentDateRange?: string | string[]\n anchorRef: React.RefObject<HTMLElement>\n}\n\nconst LAST_UNITS: { value: LastUnit; label: string }[] = [\n { value: 'days', label: 'Days' },\n { value: 'weeks', label: 'Weeks' },\n { value: 'months', label: 'Months' },\n { value: 'quarters', label: 'Quarters' },\n { value: 'years', label: 'Years' }\n]\n\nconst CustomDateDropdown: React.FC<CustomDateDropdownProps> = ({\n isOpen,\n onClose,\n onDateRangeChange,\n currentDateRange\n}) => {\n const dropdownRef = useRef<HTMLDivElement>(null)\n\n // Tab state\n const [activeTab, setActiveTab] = useState<TabType>('fixed')\n\n // Fixed tab state\n const [fixedStartDate, setFixedStartDate] = useState('')\n const [fixedEndDate, setFixedEndDate] = useState('')\n\n // Since tab state\n const [sinceDate, setSinceDate] = useState('')\n\n // Last tab state\n const [lastNumber, setLastNumber] = useState(7)\n const [lastUnit, setLastUnit] = useState<LastUnit>('days')\n\n // Initialize state from current date range\n useEffect(() => {\n if (!currentDateRange) return\n\n if (Array.isArray(currentDateRange)) {\n // Custom date range - set fixed tab\n setActiveTab('fixed')\n setFixedStartDate(currentDateRange[0] || '')\n setFixedEndDate(currentDateRange[1] || currentDateRange[0] || '')\n } else {\n // Check for \"last N units\" pattern\n const lastNMatch = currentDateRange.match(/^last\\s+(\\d+)\\s+(day|days|week|weeks|month|months|quarter|quarters|year|years)$/i)\n if (lastNMatch) {\n setActiveTab('last')\n setLastNumber(parseInt(lastNMatch[1], 10))\n const unit = lastNMatch[2].toLowerCase()\n if (unit === 'day') setLastUnit('days')\n else if (unit === 'week') setLastUnit('weeks')\n else if (unit === 'month') setLastUnit('months')\n else if (unit === 'quarter') setLastUnit('quarters')\n else if (unit === 'year') setLastUnit('years')\n else setLastUnit(unit.endsWith('s') ? unit as LastUnit : `${unit}s` as LastUnit)\n }\n }\n }, [currentDateRange])\n\n // Handle click outside to close\n useEffect(() => {\n if (!isOpen) return\n\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n onClose()\n }\n }\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n\n // Delay adding listener to prevent immediate close\n const timeoutId = setTimeout(() => {\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n }, 0)\n\n return () => {\n clearTimeout(timeoutId)\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n }\n }, [isOpen, onClose])\n\n // Handle apply for Fixed tab\n const handleApplyFixed = useCallback(() => {\n if (fixedStartDate && fixedEndDate) {\n onDateRangeChange([fixedStartDate, fixedEndDate])\n } else if (fixedStartDate) {\n onDateRangeChange([fixedStartDate, fixedStartDate])\n }\n }, [fixedStartDate, fixedEndDate, onDateRangeChange])\n\n // Handle apply for Since tab\n const handleApplySince = useCallback(() => {\n if (sinceDate) {\n const today = formatDateForCube(new Date())\n onDateRangeChange([sinceDate, today])\n }\n }, [sinceDate, onDateRangeChange])\n\n // Handle apply for Last tab\n const handleApplyLast = useCallback(() => {\n if (lastNumber > 0) {\n // Convert to the internal DateRangeType format then to value\n const rangeType = `last_n_${lastUnit}`\n const value = convertDateRangeTypeToValue(rangeType, lastNumber)\n onDateRangeChange(value)\n }\n }, [lastNumber, lastUnit, onDateRangeChange])\n\n if (!isOpen) return null\n\n const tabButtonStyle = (isActive: boolean) => ({\n backgroundColor: isActive ? 'var(--dc-primary)' : 'transparent',\n color: isActive ? 'white' : 'var(--dc-text)',\n borderBottom: isActive ? 'none' : `2px solid var(--dc-border)`\n })\n\n // Stop propagation to prevent parent handlers from interfering\n const handleDropdownClick = (e: React.MouseEvent) => {\n e.stopPropagation()\n }\n\n return (\n <div\n ref={dropdownRef}\n className=\"dc:absolute dc:top-full dc:left-0 dc:mt-1 dc:z-50 dc:border dc:rounded-lg dc:shadow-lg dc:min-w-[280px]\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: 'var(--dc-border)',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15)'\n }}\n onClick={handleDropdownClick}\n onMouseDown={handleDropdownClick}\n >\n {/* Tab Headers */}\n <div\n className=\"dc:flex dc:border-b\"\n style={{ borderColor: 'var(--dc-border)' }}\n >\n {(['fixed', 'since', 'last'] as TabType[]).map(tab => (\n <button\n key={tab}\n type=\"button\"\n onClick={() => setActiveTab(tab)}\n className=\"dc:flex-1 dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:capitalize dc:transition-colors\"\n style={tabButtonStyle(activeTab === tab)}\n >\n {tab}\n </button>\n ))}\n </div>\n\n {/* Tab Content */}\n <div className=\"dc:p-4\">\n {/* Fixed Tab */}\n {activeTab === 'fixed' && (\n <div className=\"dc:space-y-3\">\n <div>\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n Start Date\n </label>\n <input\n type=\"date\"\n value={fixedStartDate}\n onChange={(e) => setFixedStartDate(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n />\n </div>\n <div>\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n End Date\n </label>\n <input\n type=\"date\"\n value={fixedEndDate}\n onChange={(e) => setFixedEndDate(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n />\n </div>\n <button\n type=\"button\"\n onClick={handleApplyFixed}\n disabled={!fixedStartDate}\n className=\"dc:w-full dc:py-2 dc:text-sm dc:font-medium dc:rounded dc:transition-colors dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n Apply\n </button>\n </div>\n )}\n\n {/* Since Tab */}\n {activeTab === 'since' && (\n <div className=\"dc:space-y-3\">\n <div>\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n Since Date\n </label>\n <input\n type=\"date\"\n value={sinceDate}\n onChange={(e) => setSinceDate(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n />\n </div>\n <p\n className=\"dc:text-xs\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n From selected date to today\n </p>\n <button\n type=\"button\"\n onClick={handleApplySince}\n disabled={!sinceDate}\n className=\"dc:w-full dc:py-2 dc:text-sm dc:font-medium dc:rounded dc:transition-colors dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n Apply\n </button>\n </div>\n )}\n\n {/* Last Tab */}\n {activeTab === 'last' && (\n <div className=\"dc:space-y-3\">\n <div className=\"dc:flex dc:gap-2\">\n <div className=\"dc:flex-1\">\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n Number\n </label>\n <input\n type=\"number\"\n min=\"1\"\n max=\"999\"\n value={lastNumber}\n onChange={(e) => setLastNumber(Math.max(1, parseInt(e.target.value, 10) || 1))}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n />\n </div>\n <div className=\"dc:flex-1\">\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n Unit\n </label>\n <select\n value={lastUnit}\n onChange={(e) => setLastUnit(e.target.value as LastUnit)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n >\n {LAST_UNITS.map(unit => (\n <option key={unit.value} value={unit.value}>\n {unit.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n <p\n className=\"dc:text-xs\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n Last {lastNumber} {lastNumber === 1 ? lastUnit.slice(0, -1) : lastUnit}\n </p>\n <button\n type=\"button\"\n onClick={handleApplyLast}\n disabled={lastNumber < 1}\n className=\"dc:w-full dc:py-2 dc:text-sm dc:font-medium dc:rounded dc:transition-colors dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n Apply\n </button>\n </div>\n )}\n </div>\n\n {/* Cancel Button */}\n <div\n className=\"dc:px-4 dc:pb-4\"\n >\n <button\n type=\"button\"\n onClick={onClose}\n className=\"dc:w-full dc:py-2 dc:text-sm dc:font-medium dc:rounded dc:border dc:transition-colors\"\n style={{\n borderColor: 'var(--dc-border)',\n color: 'var(--dc-text-secondary)',\n backgroundColor: 'transparent'\n }}\n >\n Cancel\n </button>\n </div>\n </div>\n )\n}\n\nexport default CustomDateDropdown\n","/**\n * XTDDropdown Component\n *\n * Dropdown for X-to-Date period selections (Week, Month, Quarter, Year to Date).\n */\n\nimport React, { useEffect, useRef } from 'react'\nimport { getIcon } from '../../icons'\nimport { XTD_OPTIONS, calculateDateRange, formatDateRangeDisplay } from '../shared/utils'\n\nconst CheckIcon = getIcon('check')\n\ninterface XTDDropdownProps {\n isOpen: boolean\n onClose: () => void\n onSelect: (xtdValue: string) => void\n currentXTD?: string | null\n anchorRef: React.RefObject<HTMLElement>\n}\n\nconst XTDDropdown: React.FC<XTDDropdownProps> = ({\n isOpen,\n onClose,\n onSelect,\n currentXTD\n}) => {\n const dropdownRef = useRef<HTMLDivElement>(null)\n\n // Handle click outside to close\n useEffect(() => {\n if (!isOpen) return\n\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n onClose()\n }\n }\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n\n // Delay adding listener to prevent immediate close\n const timeoutId = setTimeout(() => {\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n }, 0)\n\n return () => {\n clearTimeout(timeoutId)\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n }\n }, [isOpen, onClose])\n\n if (!isOpen) return null\n\n // Stop propagation to prevent parent handlers from interfering\n const handleDropdownClick = (e: React.MouseEvent) => {\n e.stopPropagation()\n }\n\n return (\n <div\n ref={dropdownRef}\n className=\"dc:absolute dc:top-full dc:left-0 dc:mt-1 dc:z-50 dc:border dc:rounded-lg dc:shadow-lg dc:min-w-[180px] dc:py-1\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: 'var(--dc-border)',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15)'\n }}\n onClick={handleDropdownClick}\n onMouseDown={handleDropdownClick}\n >\n {XTD_OPTIONS.map(option => {\n const isActive = currentXTD === option.id\n const dateRange = calculateDateRange(option.value)\n const dateRangeText = dateRange\n ? formatDateRangeDisplay(dateRange.start, dateRange.end)\n : ''\n\n return (\n <button\n key={option.id}\n type=\"button\"\n onClick={() => onSelect(option.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 text-left dc:text-sm dc:transition-colors dc:flex dc:items-center dc:justify-between dc:gap-2\"\n style={{\n backgroundColor: isActive ? 'var(--dc-primary-bg)' : 'transparent',\n color: 'var(--dc-text)'\n }}\n onMouseEnter={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent'\n }\n }}\n >\n <div className=\"dc:flex dc:flex-col\">\n <span className=\"dc:font-medium\">{option.label}</span>\n {dateRangeText && (\n <span\n className=\"dc:text-xs dc:mt-0.5\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {dateRangeText}\n </span>\n )}\n </div>\n {isActive && (\n <CheckIcon\n className=\"dc:w-4 dc:h-4 dc:shrink-0\"\n style={{ color: 'var(--dc-primary)' }}\n />\n )}\n </button>\n )\n })}\n </div>\n )\n}\n\nexport default XTDDropdown\n","/**\n * FilterValueSelector Component\n * \n * Smart input component that adapts to operator type:\n * - Combo box for equals/notEquals with API-fetched values\n * - Number input for numeric operators\n * - Date picker for date operators\n * - No input for set/notSet operators\n */\n\nimport React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'\nimport { getIcon } from '../../icons'\nimport { useFilterValues } from '../../hooks/useFilterValues'\nimport { useDebounce } from '../../hooks/useDebounce'\nimport type { FilterValueSelectorProps } from '../QueryBuilder/types'\nimport { FILTER_OPERATORS } from '../QueryBuilder/types'\n\nconst ChevronDownIcon = getIcon('chevronDown')\nconst CloseIcon = getIcon('close')\n\nconst FilterValueSelector: React.FC<FilterValueSelectorProps> = ({\n fieldName,\n operator,\n values,\n onValuesChange,\n schema\n}) => {\n const operatorMeta = FILTER_OPERATORS[operator]\n const [isOpen, setIsOpen] = useState(false)\n const [searchText, setSearchText] = useState('')\n const [hasLoadedInitial, setHasLoadedInitial] = useState(false)\n const dropdownRef = useRef<HTMLDivElement>(null)\n const lastSearchedTerm = useRef<string>('')\n \n // Debounce the search text\n const debouncedSearchText = useDebounce(searchText, 300)\n \n // Check if the field is a dimension (not a measure)\n const isDimension = useMemo(() => schema ? schema.cubes.some(cube => \n cube.dimensions.some(dim => dim.name === fieldName)\n ) : false, [schema, fieldName])\n \n // Check if the field is a time dimension\n const isTimeDimension = useMemo(() => schema ? schema.cubes.some(cube => \n cube.dimensions.some(dim => dim.name === fieldName && dim.type === 'time')\n ) : false, [schema, fieldName])\n \n // Fetch distinct values for combo box (only for equals/notEquals/in/notIn on non-time dimensions)\n const shouldFetchValues = useMemo(() => \n (['equals', 'notEquals', 'in', 'notIn'].includes(operator)) && isDimension && !isTimeDimension,\n [operator, isDimension, isTimeDimension]\n )\n const shouldShowComboBox = shouldFetchValues\n \n const { \n values: distinctValues, \n loading: valuesLoading, \n error: valuesError,\n searchValues\n } = useFilterValues(fieldName, shouldFetchValues)\n \n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false)\n }\n }\n \n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [])\n \n // Load initial values when dropdown opens\n useEffect(() => {\n if (isOpen && shouldFetchValues && searchValues) {\n searchValues('', true) // Force load with empty search\n setHasLoadedInitial(true)\n lastSearchedTerm.current = ''\n }\n }, [isOpen, shouldFetchValues, searchValues])\n \n // Trigger search when debounced search text changes\n useEffect(() => {\n if (hasLoadedInitial && shouldFetchValues && searchValues && debouncedSearchText !== lastSearchedTerm.current) {\n lastSearchedTerm.current = debouncedSearchText\n searchValues(debouncedSearchText)\n }\n }, [debouncedSearchText, hasLoadedInitial, shouldFetchValues, searchValues])\n \n // Handle dropdown toggle\n const handleDropdownToggle = useCallback(() => {\n const newIsOpen = !isOpen\n setIsOpen(newIsOpen)\n \n // Reset search when closing dropdown\n if (!newIsOpen) {\n setSearchText('')\n lastSearchedTerm.current = ''\n }\n }, [isOpen])\n \n // Handle search input change\n const handleSearchChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const newSearchText = e.target.value\n setSearchText(newSearchText)\n }, [])\n \n // Handle value selection for combo box\n const handleValueSelect = useCallback((value: any) => {\n if (operatorMeta.supportsMultipleValues) {\n // Add to selection if not already selected\n if (!values.includes(value)) {\n onValuesChange([...values, value])\n }\n } else {\n // Replace current value\n onValuesChange([value])\n setIsOpen(false)\n }\n // Clear search after selection\n setSearchText('')\n }, [operatorMeta.supportsMultipleValues, values, onValuesChange])\n \n // Handle value removal for multi-select\n const handleValueRemove = useCallback((valueToRemove: any) => {\n onValuesChange(values.filter(v => v !== valueToRemove))\n }, [values, onValuesChange])\n \n // Handle direct text input for non-combo operators\n const handleDirectInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n if (operatorMeta.valueType === 'number') {\n const numValue = parseFloat(value)\n // Accept valid numbers including zero\n if (!isNaN(numValue)) {\n onValuesChange([numValue])\n } else if (value === '' || value === '-') {\n // Allow empty string or just a minus sign for negative numbers being typed\n onValuesChange([])\n }\n } else {\n onValuesChange(value ? [value] : [])\n }\n }, [operatorMeta.valueType, onValuesChange])\n \n // Handle date input\n const handleDateInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n if (operator === 'inDateRange') {\n // For date range, we need two values\n const currentValues = values.length >= 2 ? values : ['', '']\n onValuesChange([value, currentValues[1]])\n } else {\n // Single date value\n onValuesChange(value ? [value] : [])\n }\n }, [operator, values, onValuesChange])\n \n const handleDateRangeEndInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n const currentValues = values.length >= 2 ? values : ['', '']\n onValuesChange([currentValues[0], value])\n }, [values, onValuesChange])\n\n // Handle between/notBetween range inputs (must be defined at top level, not inside conditionals)\n const handleBetweenStartInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const value = parseFloat(e.target.value)\n const currentValues = values.length >= 2 ? values : ['', '']\n const newValues = [!isNaN(value) ? value : e.target.value === '' ? '' : currentValues[0], currentValues[1]]\n onValuesChange(newValues.filter(v => v !== ''))\n }, [values, onValuesChange])\n\n const handleBetweenEndInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const value = parseFloat(e.target.value)\n const currentValues = values.length >= 2 ? values : ['', '']\n const newValues = [currentValues[0], !isNaN(value) ? value : e.target.value === '' ? '' : currentValues[1]]\n onValuesChange(newValues.filter(v => v !== ''))\n }, [values, onValuesChange])\n\n // Render based on operator type\n if (!operatorMeta.requiresValues) {\n // No input needed for set/notSet\n return (\n <div className=\"dc:text-sm text-dc-text-muted dc:italic\">\n No value required\n </div>\n )\n }\n \n if (operator === 'inDateRange') {\n // Date range picker\n return (\n <div className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"date\"\n value={values[0] || ''}\n onChange={handleDateInput}\n className=\"dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n />\n <span className=\"dc:text-sm text-dc-text-muted\">to</span>\n <input\n type=\"date\"\n value={values[1] || ''}\n onChange={handleDateRangeEndInput}\n className=\"dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n />\n </div>\n )\n }\n \n if (operator === 'between' || operator === 'notBetween') {\n // Between range picker (for numbers)\n return (\n <div className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"number\"\n value={values[0] !== undefined && values[0] !== null ? values[0] : ''}\n onChange={handleBetweenStartInput}\n placeholder=\"Min\"\n className=\"dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n />\n <span className=\"dc:text-sm text-dc-text-muted\">to</span>\n <input\n type=\"number\"\n value={values[1] !== undefined && values[1] !== null ? values[1] : ''}\n onChange={handleBetweenEndInput}\n placeholder=\"Max\"\n className=\"dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n />\n </div>\n )\n }\n \n if (operatorMeta.valueType === 'date') {\n // Single date picker\n return (\n <input\n type=\"date\"\n value={values[0] || ''}\n onChange={handleDateInput}\n className=\"dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n />\n )\n }\n \n if (operatorMeta.valueType === 'number') {\n // Number input\n return (\n <input\n type=\"number\"\n value={values[0] !== undefined && values[0] !== null ? values[0] : ''}\n onChange={handleDirectInput}\n placeholder=\"Enter number\"\n className=\"dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n />\n )\n }\n \n // Time dimension with equals/notEquals/in/notIn - use date picker\n if (isTimeDimension && (['equals', 'notEquals', 'in', 'notIn'].includes(operator))) {\n if (operatorMeta.supportsMultipleValues) {\n // Multi-select date picker (for notEquals that supports multiple values)\n return (\n <div className=\"dc:space-y-2 dc:min-w-0 dc:max-w-full\">\n {/* Selected dates display */}\n {values.length > 0 && (\n <div className=\"dc:flex dc:flex-wrap dc:gap-1 dc:max-w-full\">\n {values.map((value, index) => (\n <div\n key={index}\n className=\"dc:inline-flex dc:items-center bg-dc-time-dimension text-dc-time-dimension dc:text-xs dc:px-2 dc:py-1 dc:rounded-sm dc:border border-dc-time-dimension\"\n >\n <span className=\"dc:mr-1\">{String(value)}</span>\n <button\n onClick={() => handleValueRemove(value)}\n className=\"text-dc-accent hover:text-dc-accent focus:outline-hidden\"\n >\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n </div>\n ))}\n </div>\n )}\n \n {/* Add new date */}\n <input\n type=\"date\"\n onChange={(e) => {\n if (e.target.value && !values.includes(e.target.value)) {\n onValuesChange([...values, e.target.value])\n e.target.value = '' // Clear the input\n }\n }}\n className=\"dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n placeholder=\"Add date...\"\n />\n </div>\n )\n } else {\n // Single date picker\n return (\n <input\n type=\"date\"\n value={values[0] || ''}\n onChange={handleDateInput}\n className=\"dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n />\n )\n }\n }\n \n if (shouldShowComboBox) {\n // Combo box with API-fetched values\n return (\n <div className=\"dc:relative dc:min-w-0 dc:max-w-full\" ref={dropdownRef}>\n {/* Selected values display (for multi-select) */}\n {operatorMeta.supportsMultipleValues && values.length > 0 && (\n <div className=\"dc:flex dc:flex-wrap dc:gap-1 dc:mb-2 dc:max-w-full\">\n {values.map((value, index) => (\n <div\n key={index}\n className=\"dc:inline-flex dc:items-center bg-dc-time-dimension text-dc-time-dimension dc:text-xs dc:px-2 dc:py-1 dc:rounded-sm dc:border border-dc-time-dimension\"\n >\n <span className=\"dc:mr-1\">{String(value)}</span>\n <button\n onClick={() => handleValueRemove(value)}\n className=\"text-dc-accent hover:text-dc-accent focus:outline-hidden\"\n >\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n </div>\n ))}\n </div>\n )}\n \n {/* Single value display (for single-select) */}\n {!operatorMeta.supportsMultipleValues && values.length > 0 && (\n <div className=\"dc:mb-2\">\n <div className=\"dc:inline-flex dc:items-center bg-dc-time-dimension text-dc-time-dimension dc:text-xs dc:px-2 dc:py-1 dc:rounded-sm dc:border border-dc-time-dimension\">\n <span className=\"dc:mr-1\">{String(values[0])}</span>\n <button\n onClick={() => onValuesChange([])}\n className=\"text-dc-accent hover:text-dc-accent focus:outline-hidden\"\n >\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n </div>\n </div>\n )}\n \n {/* Dropdown trigger */}\n <button\n onClick={handleDropdownToggle}\n className=\"dc:w-full text-left dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface hover:bg-dc-surface-hover dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent dc:flex dc:items-center dc:justify-between dc:min-w-0\"\n >\n <span className=\"text-dc-text-muted dc:truncate\">\n {valuesLoading && !hasLoadedInitial ? 'Loading values...' : 'Select value...'}\n </span>\n <ChevronDownIcon className=\"dc:w-4 dc:h-4 text-dc-text-muted\" />\n </button>\n\n {/* Dropdown menu */}\n {isOpen && (\n <div className=\"dc:absolute dc:z-30 dc:left-0 dc:right-0 dc:mt-1 bg-dc-surface dc:border border-dc-border dc:rounded-md dc:shadow-lg dc:max-h-60 dc:overflow-y-auto\">\n {/* Search input */}\n <div className=\"dc:p-2 dc:border-b border-dc-border\">\n <input\n type=\"text\"\n value={searchText}\n onChange={handleSearchChange}\n placeholder=\"Search values...\"\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n autoFocus\n />\n </div>\n\n {/* Values list */}\n <div className=\"dc:max-h-48 dc:overflow-y-auto\">\n {valuesLoading ? (\n <div className=\"dc:p-2 dc:text-sm text-dc-text-muted\">\n {searchText ? 'Searching...' : 'Loading values...'}\n </div>\n ) : valuesError ? (\n <div className=\"dc:p-2 dc:text-sm text-dc-error\">\n Error loading values: {valuesError}\n </div>\n ) : distinctValues.length === 0 ? (\n <div className=\"dc:p-2 dc:text-sm text-dc-text-muted\">\n {searchText ? 'No matching values' : 'No values available'}\n </div>\n ) : (\n distinctValues.map((value, index) => {\n const isSelected = values.includes(value)\n\n return (\n <button\n key={`${value}-${index}`}\n onClick={() => handleValueSelect(value)}\n className={`dc:w-full text-left dc:px-3 dc:py-2 dc:text-sm hover:bg-dc-surface-hover focus:outline-hidden focus:bg-dc-surface-hover ${\n isSelected ? 'bg-dc-accent-bg text-dc-accent' : 'text-dc-text-secondary'\n }`}\n >\n {String(value)}\n {isSelected && (\n <span className=\"dc:float-right text-dc-accent\">✓</span>\n )}\n </button>\n )\n })\n )}\n </div>\n </div>\n )}\n </div>\n )\n }\n \n // Fallback to text input\n return (\n <input\n type=\"text\"\n value={values[0] !== undefined && values[0] !== null ? values[0] : ''}\n onChange={handleDirectInput}\n placeholder={`Enter ${operatorMeta.valueType} value`}\n className=\"dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n />\n )\n}\n\nexport default FilterValueSelector","/**\n * FilterValuePopover Component\n *\n * Inline popover for editing filter values.\n * Uses FilterValueSelector for the actual value input.\n */\n\nimport React, { useEffect, useRef, useCallback } from 'react'\nimport FilterValueSelector from '../shared/FilterValueSelector'\nimport type { SimpleFilter, CubeMeta } from '../../types'\nimport type { MetaResponse } from '../../shared/types'\n\ninterface FilterValuePopoverProps {\n filter: SimpleFilter\n schema: CubeMeta | null\n onValuesChange: (values: any[]) => void\n onClose: () => void\n anchorRef: React.RefObject<HTMLElement>\n}\n\n// Convert CubeMeta to MetaResponse format\nfunction convertToMetaResponse(cubeMeta: CubeMeta | null): MetaResponse | null {\n if (!cubeMeta) return null\n\n return {\n cubes: cubeMeta.cubes.map(cube => ({\n name: cube.name,\n title: cube.title || cube.name,\n description: cube.description || '',\n measures: cube.measures.map(m => ({\n name: m.name,\n title: m.title || m.name,\n type: m.type,\n description: '',\n shortTitle: m.shortTitle || m.title || m.name\n })),\n dimensions: cube.dimensions.map(d => ({\n name: d.name,\n title: d.title || d.name,\n type: d.type,\n description: '',\n shortTitle: d.shortTitle || d.title || d.name\n })),\n segments: cube.segments?.map(s => ({\n name: s.name,\n title: s.title || s.name,\n type: s.type,\n description: '',\n shortTitle: s.shortTitle || s.title || s.name\n })) || []\n }))\n }\n}\n\nconst FilterValuePopover: React.FC<FilterValuePopoverProps> = ({\n filter,\n schema,\n onValuesChange,\n onClose,\n anchorRef\n}) => {\n const popoverRef = useRef<HTMLDivElement>(null)\n\n // Handle click outside to close\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n popoverRef.current &&\n !popoverRef.current.contains(event.target as Node) &&\n anchorRef.current &&\n !anchorRef.current.contains(event.target as Node)\n ) {\n onClose()\n }\n }\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n\n // Delay adding listener to prevent immediate close\n const timeoutId = setTimeout(() => {\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n }, 0)\n\n return () => {\n clearTimeout(timeoutId)\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n }\n }, [onClose, anchorRef])\n\n // Handle value change\n const handleValuesChange = useCallback((newValues: any[]) => {\n onValuesChange(newValues)\n }, [onValuesChange])\n\n // Convert schema to MetaResponse format\n const metaResponse = convertToMetaResponse(schema)\n\n return (\n <div\n ref={popoverRef}\n className=\"dc:absolute dc:top-full dc:left-0 dc:mt-1 dc:z-50 dc:border dc:rounded-lg dc:shadow-lg dc:p-3 dc:min-w-[220px]\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: 'var(--dc-border)',\n boxShadow: 'var(--dc-shadow-lg)'\n }}\n >\n {/* Filter label */}\n <div\n className=\"dc:text-xs dc:font-medium dc:mb-2\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n Edit value\n </div>\n\n {/* Value selector */}\n <div className=\"dc:min-w-[180px]\">\n <FilterValueSelector\n fieldName={filter.member}\n operator={filter.operator}\n values={filter.values || []}\n onValuesChange={handleValuesChange}\n schema={metaResponse}\n />\n </div>\n\n {/* Action buttons */}\n <div className=\"dc:flex dc:justify-end dc:gap-2 dc:mt-3 dc:pt-2 dc:border-t\" style={{ borderColor: 'var(--dc-border)' }}>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"dc:px-3 dc:py-1 dc:text-xs dc:font-medium dc:rounded dc:border dc:transition-colors\"\n style={{\n borderColor: 'var(--dc-border)',\n color: 'var(--dc-text-secondary)',\n backgroundColor: 'transparent'\n }}\n >\n Close\n </button>\n </div>\n </div>\n )\n}\n\nexport default FilterValuePopover\n","/**\n * FilterChip Component\n *\n * Compact display of non-date filters as clickable chips.\n * Clicking opens a popover for inline value editing.\n */\n\nimport React, { useState, useRef, useCallback } from 'react'\nimport { getIcon } from '../../icons'\nimport FilterValuePopover from './FilterValuePopover'\nimport { formatFilterValueDisplay } from '../shared/utils'\nimport type { DashboardFilter, CubeMeta, SimpleFilter } from '../../types'\n\nconst CloseIcon = getIcon('close')\nconst EditIcon = getIcon('edit')\n\ninterface FilterChipProps {\n filter: DashboardFilter\n schema: CubeMeta | null\n isEditMode: boolean\n onChange: (updatedFilter: DashboardFilter) => void\n onEdit?: () => void\n onRemove?: () => void\n}\n\nconst FilterChip: React.FC<FilterChipProps> = ({\n filter,\n schema,\n isEditMode,\n onChange,\n onEdit,\n onRemove\n}) => {\n const [showPopover, setShowPopover] = useState(false)\n const chipRef = useRef<HTMLDivElement>(null)\n\n // Get filter details\n const simpleFilter = filter.filter as SimpleFilter\n const { label } = filter\n const { operator, values } = simpleFilter\n\n // Format value display\n const valueDisplay = formatFilterValueDisplay(values || [], operator)\n\n // Handle value change from popover\n const handleValueChange = useCallback((newValues: any[]) => {\n onChange({\n ...filter,\n filter: {\n ...simpleFilter,\n values: newValues\n }\n })\n }, [filter, simpleFilter, onChange])\n\n // Handle chip click - open popover in view mode, or edit in edit mode\n const handleChipClick = useCallback(() => {\n if (isEditMode) {\n // In edit mode, clicking opens the full modal\n onEdit?.()\n } else {\n // In view mode, show inline popover for value editing\n setShowPopover(true)\n }\n }, [isEditMode, onEdit])\n\n // Don't show chips for group filters\n if (!('member' in filter.filter)) {\n return null\n }\n\n return (\n <div ref={chipRef} className=\"dc:relative dc:inline-flex\">\n <div\n className={`\n dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded dc:text-xs\n dc:border dc:transition-colors dc:cursor-pointer\n ${isEditMode ? 'dc:pr-1' : ''}\n `}\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: 'var(--dc-border)',\n color: 'var(--dc-text)'\n }}\n onClick={handleChipClick}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface)'\n }}\n title={`${label} ${valueDisplay}`}\n >\n <span className=\"dc:font-medium dc:truncate dc:max-w-[100px]\">{label}</span>\n {valueDisplay && (\n <>\n <span style={{ color: 'var(--dc-text-secondary)' }}>{valueDisplay}</span>\n </>\n )}\n\n {/* Edit mode: show edit and remove buttons */}\n {isEditMode && (\n <>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onEdit?.()\n }}\n className=\"dc:p-0.5 dc:rounded dc:transition-colors\"\n style={{ color: 'var(--dc-text-secondary)' }}\n onMouseEnter={(e) => {\n e.currentTarget.style.color = 'var(--dc-text)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.color = 'var(--dc-text-secondary)'\n }}\n >\n <EditIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove?.()\n }}\n className=\"dc:p-0.5 dc:rounded dc:transition-colors\"\n style={{ color: 'var(--dc-text-secondary)' }}\n onMouseEnter={(e) => {\n e.currentTarget.style.color = 'var(--dc-error)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.color = 'var(--dc-text-secondary)'\n }}\n >\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n </>\n )}\n </div>\n\n {/* Value editing popover (view mode only) */}\n {showPopover && !isEditMode && (\n <FilterValuePopover\n filter={simpleFilter}\n schema={schema}\n onValuesChange={handleValueChange}\n onClose={() => setShowPopover(false)}\n anchorRef={chipRef}\n />\n )}\n </div>\n )\n}\n\nexport default FilterChip\n","/**\n * CompactFilterBar Component\n *\n * A Mixpanel-inspired compact horizontal filter bar for dashboards.\n * Provides quick preset date selection, custom date options, XTD options,\n * and compact non-date filter display.\n */\n\nimport React, { useState, useCallback, useMemo, useRef } from 'react'\nimport { getIcon } from '../../icons'\nimport DatePresetChips from './DatePresetChips'\nimport CustomDateDropdown from './CustomDateDropdown'\nimport XTDDropdown from './XTDDropdown'\nimport FilterChip from './FilterChip'\nimport type { DashboardFilter, CubeMeta, SimpleFilter } from '../../types'\nimport {\n detectPresetFromDateRange,\n calculateDateRange,\n formatDateRangeDisplay,\n XTD_OPTIONS\n} from '../shared/utils'\n\nconst AddIcon = getIcon('add')\nconst CalendarIcon = getIcon('timeDimension')\nconst ChevronDownIcon = getIcon('chevronDown')\nconst FilterIcon = getIcon('filter')\n\ninterface CompactFilterBarProps {\n dashboardFilters: DashboardFilter[]\n schema: CubeMeta | null\n isEditMode: boolean\n onDashboardFiltersChange: (filters: DashboardFilter[]) => void\n onAddFilter?: () => void\n onEditFilter?: (filterId: string) => void\n onRemoveFilter?: (filterId: string) => void\n}\n\nconst CompactFilterBar: React.FC<CompactFilterBarProps> = ({\n dashboardFilters,\n schema,\n isEditMode,\n onDashboardFiltersChange,\n onAddFilter,\n onEditFilter,\n onRemoveFilter\n}) => {\n // Dropdown state\n const [showCustomDropdown, setShowCustomDropdown] = useState(false)\n const [showXTDDropdown, setShowXTDDropdown] = useState(false)\n\n // Refs for dropdown positioning\n const customButtonRef = useRef<HTMLButtonElement>(null)\n const xtdButtonRef = useRef<HTMLButtonElement>(null)\n\n // Find universal time filter\n const universalTimeFilter = useMemo(() => {\n return dashboardFilters.find(df => df.isUniversalTime)\n }, [dashboardFilters])\n\n // Get current date range from universal time filter\n const currentDateRange = useMemo(() => {\n if (!universalTimeFilter) return null\n const filter = universalTimeFilter.filter as SimpleFilter\n // Handle both dateRange property and values array\n if (filter.dateRange) return filter.dateRange\n if (filter.values && filter.values.length > 0) {\n // Single string value (preset) - return as string\n if (filter.values.length === 1 && typeof filter.values[0] === 'string') {\n return filter.values[0]\n }\n // Array of dates for custom range\n return filter.values\n }\n return null\n }, [universalTimeFilter])\n\n // Detect active preset from current date range\n const activePresetId = useMemo(() => {\n return detectPresetFromDateRange(currentDateRange as string | string[] | undefined)\n }, [currentDateRange])\n\n // Check if XTD is active\n const activeXTDId = useMemo(() => {\n if (!currentDateRange || Array.isArray(currentDateRange)) return null\n const preset = detectPresetFromDateRange(currentDateRange)\n return XTD_OPTIONS.find(opt => opt.id === preset)?.id || null\n }, [currentDateRange])\n\n // Get non-date filters (exclude universal time filter)\n const nonDateFilters = useMemo(() => {\n return dashboardFilters.filter(df => !df.isUniversalTime)\n }, [dashboardFilters])\n\n // Generate unique ID for new filters\n const generateFilterId = useCallback(() => {\n return `df_${Date.now()}_${Math.random().toString(36).substring(7)}`\n }, [])\n\n // Handle date range change (preset, custom, or XTD)\n const handleDateRangeChange = useCallback((newDateRange: string | string[]) => {\n if (universalTimeFilter) {\n // Update existing filter\n const updatedFilters = dashboardFilters.map(df => {\n if (df.id === universalTimeFilter.id) {\n return {\n ...df,\n filter: {\n ...(df.filter as SimpleFilter),\n values: Array.isArray(newDateRange) ? newDateRange : [newDateRange],\n dateRange: newDateRange\n }\n }\n }\n return df\n })\n onDashboardFiltersChange(updatedFilters)\n } else {\n // Create new universal time filter\n const newFilter: DashboardFilter = {\n id: generateFilterId(),\n label: 'Date Range',\n isUniversalTime: true,\n filter: {\n member: '__universal_time__',\n operator: 'inDateRange',\n values: Array.isArray(newDateRange) ? newDateRange : [newDateRange],\n dateRange: newDateRange\n }\n }\n onDashboardFiltersChange([...dashboardFilters, newFilter])\n }\n }, [dashboardFilters, universalTimeFilter, onDashboardFiltersChange, generateFilterId])\n\n // Handle preset selection\n const handlePresetSelect = useCallback((presetValue: string) => {\n handleDateRangeChange(presetValue)\n }, [handleDateRangeChange])\n\n // Handle XTD selection\n const handleXTDSelect = useCallback((xtdValue: string) => {\n handleDateRangeChange(xtdValue)\n setShowXTDDropdown(false)\n }, [handleDateRangeChange])\n\n // Handle custom date selection\n const handleCustomDateSelect = useCallback((dateRange: string | string[]) => {\n handleDateRangeChange(dateRange)\n setShowCustomDropdown(false)\n }, [handleDateRangeChange])\n\n // Handle filter value change (for non-date filters)\n const handleFilterChange = useCallback((filterId: string, updatedFilter: DashboardFilter) => {\n const updatedFilters = dashboardFilters.map(df =>\n df.id === filterId ? updatedFilter : df\n )\n onDashboardFiltersChange(updatedFilters)\n }, [dashboardFilters, onDashboardFiltersChange])\n\n // Calculate tooltip for active date range\n const dateRangeTooltip = useMemo(() => {\n if (!currentDateRange) return null\n\n if (Array.isArray(currentDateRange)) {\n // Custom date range - format the dates\n const start = new Date(currentDateRange[0])\n const end = new Date(currentDateRange[1] || currentDateRange[0])\n return formatDateRangeDisplay(start, end)\n }\n\n // Preset - calculate the actual range\n const range = calculateDateRange(currentDateRange)\n if (range) {\n return formatDateRangeDisplay(range.start, range.end)\n }\n\n return currentDateRange\n }, [currentDateRange])\n\n // If no filters and not in edit mode, don't show anything\n if (!isEditMode && dashboardFilters.length === 0) {\n return null\n }\n\n return (\n <div\n className=\"dc:border dc:rounded-lg\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-surface)'\n }}\n >\n {/* Desktop Layout */}\n <div className=\"dc:hidden dc:md:flex dc:items-center dc:gap-2 dc:px-3 dc:py-2\">\n {/* Filter Icon */}\n <FilterIcon\n className=\"dc:w-4 dc:h-4 dc:shrink-0\"\n style={{ color: 'var(--dc-text-secondary)' }}\n />\n\n {/* Date Preset Chips */}\n <DatePresetChips\n activePreset={activePresetId !== 'custom' && !activeXTDId ? activePresetId : null}\n onPresetSelect={handlePresetSelect}\n />\n\n {/* Custom Date Button */}\n <div className=\"dc:relative\">\n <button\n ref={customButtonRef}\n type=\"button\"\n onClick={() => {\n setShowCustomDropdown(!showCustomDropdown)\n setShowXTDDropdown(false)\n }}\n title={activePresetId === 'custom' && dateRangeTooltip ? dateRangeTooltip : 'Custom date range'}\n className={`\n dc:flex dc:items-center dc:gap-1 dc:px-2.5 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:border\n dc:transition-colors dc:focus:outline-none dc:focus:ring-2 dc:focus:ring-offset-1\n `}\n style={{\n backgroundColor: activePresetId === 'custom' ? 'var(--dc-primary)' : 'var(--dc-surface)',\n color: activePresetId === 'custom' ? 'white' : 'var(--dc-text)',\n borderColor: activePresetId === 'custom' ? 'transparent' : 'var(--dc-border)'\n }}\n >\n <CalendarIcon className=\"dc:w-3 dc:h-3\" />\n <span>Custom</span>\n <ChevronDownIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n\n {showCustomDropdown && (\n <CustomDateDropdown\n isOpen={showCustomDropdown}\n onClose={() => setShowCustomDropdown(false)}\n onDateRangeChange={handleCustomDateSelect}\n currentDateRange={currentDateRange as string | string[] | undefined}\n anchorRef={customButtonRef}\n />\n )}\n </div>\n\n {/* XTD Button */}\n <div className=\"dc:relative\">\n <button\n ref={xtdButtonRef}\n type=\"button\"\n onClick={() => {\n setShowXTDDropdown(!showXTDDropdown)\n setShowCustomDropdown(false)\n }}\n title={activeXTDId && dateRangeTooltip ? dateRangeTooltip : 'X to Date options'}\n className={`\n dc:flex dc:items-center dc:gap-1 dc:px-2.5 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:border\n dc:transition-colors dc:focus:outline-none dc:focus:ring-2 dc:focus:ring-offset-1\n `}\n style={{\n backgroundColor: activeXTDId ? 'var(--dc-primary)' : 'var(--dc-surface)',\n color: activeXTDId ? 'white' : 'var(--dc-text)',\n borderColor: activeXTDId ? 'transparent' : 'var(--dc-border)'\n }}\n >\n <span>XTD</span>\n <ChevronDownIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n\n {showXTDDropdown && (\n <XTDDropdown\n isOpen={showXTDDropdown}\n onClose={() => setShowXTDDropdown(false)}\n onSelect={handleXTDSelect}\n currentXTD={activeXTDId}\n anchorRef={xtdButtonRef}\n />\n )}\n </div>\n\n {/* Separator */}\n {nonDateFilters.length > 0 && (\n <div\n className=\"dc:h-5 dc:w-px dc:mx-1\"\n style={{ backgroundColor: 'var(--dc-border)' }}\n />\n )}\n\n {/* Non-date Filter Chips */}\n <div className=\"dc:flex dc:items-center dc:gap-1.5 dc:flex-wrap\">\n {nonDateFilters.map(filter => (\n <FilterChip\n key={filter.id}\n filter={filter}\n schema={schema}\n isEditMode={isEditMode}\n onChange={(updatedFilter) => handleFilterChange(filter.id, updatedFilter)}\n onEdit={() => onEditFilter?.(filter.id)}\n onRemove={() => onRemoveFilter?.(filter.id)}\n />\n ))}\n </div>\n\n {/* Add Filter Button (Edit Mode) */}\n {isEditMode && onAddFilter && (\n <button\n type=\"button\"\n onClick={onAddFilter}\n className=\"dc:flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:border dc:transition-colors\"\n style={{\n borderColor: 'var(--dc-border)',\n color: 'var(--dc-text-secondary)',\n backgroundColor: 'transparent'\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n )}\n </div>\n\n {/* Mobile Layout */}\n <div className=\"dc:md:hidden\">\n {/* Presets row with horizontal scroll */}\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:overflow-x-auto dc:px-3 dc:py-2 scrollbar-thin\">\n {/* Filter Icon */}\n <FilterIcon\n className=\"dc:w-4 dc:h-4 dc:shrink-0\"\n style={{ color: 'var(--dc-text-secondary)' }}\n />\n <DatePresetChips\n activePreset={activePresetId !== 'custom' && !activeXTDId ? activePresetId : null}\n onPresetSelect={handlePresetSelect}\n />\n </div>\n\n {/* Custom, XTD, and Add buttons */}\n <div\n className=\"dc:flex dc:items-center dc:justify-between dc:px-3 dc:py-2 dc:border-t\"\n style={{ borderColor: 'var(--dc-border)' }}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n {/* Custom Button */}\n <div className=\"dc:relative\">\n <button\n ref={customButtonRef}\n type=\"button\"\n onClick={() => {\n setShowCustomDropdown(!showCustomDropdown)\n setShowXTDDropdown(false)\n }}\n className={`\n dc:flex dc:items-center dc:gap-1 dc:px-2.5 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:border\n dc:transition-colors\n `}\n style={{\n backgroundColor: activePresetId === 'custom' ? 'var(--dc-primary)' : 'var(--dc-surface)',\n color: activePresetId === 'custom' ? 'white' : 'var(--dc-text)',\n borderColor: activePresetId === 'custom' ? 'transparent' : 'var(--dc-border)'\n }}\n >\n <CalendarIcon className=\"dc:w-3 dc:h-3\" />\n <span>Custom</span>\n </button>\n\n {showCustomDropdown && (\n <CustomDateDropdown\n isOpen={showCustomDropdown}\n onClose={() => setShowCustomDropdown(false)}\n onDateRangeChange={handleCustomDateSelect}\n currentDateRange={currentDateRange as string | string[] | undefined}\n anchorRef={customButtonRef}\n />\n )}\n </div>\n\n {/* XTD Button */}\n <div className=\"dc:relative\">\n <button\n ref={xtdButtonRef}\n type=\"button\"\n onClick={() => {\n setShowXTDDropdown(!showXTDDropdown)\n setShowCustomDropdown(false)\n }}\n className={`\n dc:flex dc:items-center dc:gap-1 dc:px-2.5 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:border\n dc:transition-colors\n `}\n style={{\n backgroundColor: activeXTDId ? 'var(--dc-primary)' : 'var(--dc-surface)',\n color: activeXTDId ? 'white' : 'var(--dc-text)',\n borderColor: activeXTDId ? 'transparent' : 'var(--dc-border)'\n }}\n >\n <span>XTD</span>\n <ChevronDownIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n\n {showXTDDropdown && (\n <XTDDropdown\n isOpen={showXTDDropdown}\n onClose={() => setShowXTDDropdown(false)}\n onSelect={handleXTDSelect}\n currentXTD={activeXTDId}\n anchorRef={xtdButtonRef}\n />\n )}\n </div>\n </div>\n\n {/* Add Filter Button (Edit Mode) */}\n {isEditMode && onAddFilter && (\n <button\n type=\"button\"\n onClick={onAddFilter}\n className=\"dc:flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:border dc:transition-colors\"\n style={{\n borderColor: 'var(--dc-border)',\n color: 'var(--dc-text-secondary)',\n backgroundColor: 'transparent'\n }}\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n )}\n </div>\n\n {/* Non-date Filter Chips (Mobile) */}\n {nonDateFilters.length > 0 && (\n <div\n className=\"dc:px-3 dc:py-2 dc:border-t\"\n style={{ borderColor: 'var(--dc-border)' }}\n >\n <div className=\"dc:flex dc:items-center dc:gap-1.5 dc:flex-wrap\">\n {nonDateFilters.map(filter => (\n <FilterChip\n key={filter.id}\n filter={filter}\n schema={schema}\n isEditMode={isEditMode}\n onChange={(updatedFilter) => handleFilterChange(filter.id, updatedFilter)}\n onEdit={() => onEditFilter?.(filter.id)}\n onRemove={() => onRemoveFilter?.(filter.id)}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default CompactFilterBar\n","/**\n * DashboardFilterPanel Component\n *\n * Orchestrates dashboard-level filtering UI\n * - Edit mode: Shows filter chips with edit/delete actions\n * - View mode: Shows interactive read-only filters\n *\n * Pattern: Simplified coordination layer (matches portlet editing pattern)\n * - No local editing state (modal owns all edit state)\n * - Just tracks which filter is being edited and modal visibility\n * - Delegates to FilterEditModal for all editing logic\n */\n\nimport React, { useState, useCallback } from 'react'\nimport FilterEditModal from './DashboardFilters/FilterEditModal'\nimport EditModeFilterList from './DashboardFilters/EditModeFilterList'\nimport CompactFilterBar from './DashboardFilters/CompactFilterBar'\nimport type { DashboardFilter, CubeMeta, DashboardConfig } from '../types'\nimport type { MetaResponse } from '../shared/types'\n\ninterface DashboardFilterPanelProps {\n dashboardFilters: DashboardFilter[]\n editable: boolean\n schema: CubeMeta | null\n dashboardConfig: DashboardConfig\n onDashboardFiltersChange: (filters: DashboardFilter[]) => void\n onSaveFilters?: (filters: DashboardFilter[]) => void | Promise<void>\n selectedFilterId?: string | null\n onFilterSelect?: (filterId: string) => void\n isEditMode?: boolean\n}\n\nconst DashboardFilterPanel: React.FC<DashboardFilterPanelProps> = ({\n dashboardFilters,\n editable,\n schema,\n dashboardConfig,\n onDashboardFiltersChange,\n onSaveFilters,\n selectedFilterId,\n onFilterSelect,\n isEditMode = false\n}) => {\n // Track which filter is being edited and modal visibility (no local editing state)\n const [editingFilter, setEditingFilter] = useState<DashboardFilter | null>(null)\n const [showFilterBuilder, setShowFilterBuilder] = useState(false)\n\n // Convert CubeMeta to MetaResponse (QueryBuilder type)\n const convertToMetaResponse = useCallback((cubeMeta: CubeMeta | null): MetaResponse | null => {\n if (!cubeMeta) return null\n\n return {\n cubes: cubeMeta.cubes.map(cube => ({\n name: cube.name,\n title: cube.title || cube.name,\n description: cube.description || '',\n measures: cube.measures.map(m => ({\n name: m.name,\n title: m.title,\n type: m.type,\n description: '',\n shortTitle: m.shortTitle\n })),\n dimensions: cube.dimensions.map(d => ({\n name: d.name,\n title: d.title,\n type: d.type,\n description: '',\n shortTitle: d.shortTitle\n })),\n segments: cube.segments?.map(s => ({\n name: s.name,\n title: s.title,\n type: s.type,\n description: '',\n shortTitle: s.shortTitle\n })) || []\n }))\n }\n }, [])\n\n // Generate unique ID for new filters\n const generateFilterId = useCallback(() => {\n return `df_${Date.now()}_${Math.random().toString(36).substring(7)}`\n }, [])\n\n // Handle adding a new filter - create temporary filter for modal\n const handleAddFilter = useCallback(() => {\n const newFilter: DashboardFilter = {\n id: generateFilterId(),\n label: `Filter ${dashboardFilters.length + 1}`,\n filter: {\n member: '',\n operator: 'equals',\n values: []\n }\n }\n setEditingFilter(newFilter)\n setShowFilterBuilder(true)\n }, [dashboardFilters.length, generateFilterId])\n\n // Handle adding a universal time filter - applies to all time dimensions\n // Creates filter directly without opening modal (fixed name, user just sets date range in view mode)\n const handleAddTimeFilter = useCallback(() => {\n const newFilter: DashboardFilter = {\n id: generateFilterId(),\n label: 'Date Range Filter',\n isUniversalTime: true,\n filter: {\n member: '__universal_time__', // Placeholder, not used in merge logic\n operator: 'inDateRange',\n values: ['last 30 days']\n }\n }\n // Add directly to filters without opening modal\n const updatedFilters = [...dashboardFilters, newFilter]\n onDashboardFiltersChange(updatedFilters)\n }, [generateFilterId, dashboardFilters, onDashboardFiltersChange])\n\n // Handle editing an existing filter - just open modal with filter\n const handleEditFilter = useCallback((filterId: string) => {\n const filterToEdit = dashboardFilters.find(df => df.id === filterId)\n if (filterToEdit) {\n setEditingFilter(filterToEdit)\n setShowFilterBuilder(true)\n }\n }, [dashboardFilters])\n\n // Handle removing a filter - simple filter list update\n const handleRemoveFilter = useCallback((filterId: string) => {\n const updatedFilters = dashboardFilters.filter(df => df.id !== filterId)\n onDashboardFiltersChange(updatedFilters)\n\n // Close modal if we're deleting the filter being edited\n if (editingFilter?.id === filterId) {\n setEditingFilter(null)\n setShowFilterBuilder(false)\n }\n }, [dashboardFilters, editingFilter, onDashboardFiltersChange])\n\n // Handle save from modal - update or add filter and save\n const handleSaveFilter = useCallback(async (filterData: DashboardFilter) => {\n // Check if this is a new filter (not in current list) or an update\n const existingFilterIndex = dashboardFilters.findIndex(f => f.id === filterData.id)\n\n let updatedFilters: DashboardFilter[]\n if (existingFilterIndex >= 0) {\n // Update existing filter\n updatedFilters = dashboardFilters.map(f =>\n f.id === filterData.id ? filterData : f\n )\n } else {\n // Add new filter\n updatedFilters = [...dashboardFilters, filterData]\n }\n\n // Update dashboard state\n onDashboardFiltersChange(updatedFilters)\n\n // Trigger save if callback provided\n if (onSaveFilters) {\n try {\n await onSaveFilters(updatedFilters)\n } catch (error) {\n console.error('Failed to save filters:', error)\n throw error // Re-throw so modal can handle it\n }\n }\n }, [dashboardFilters, onDashboardFiltersChange, onSaveFilters])\n\n // Handle modal close - just clean up state\n const handleCloseFilterBuilder = useCallback(() => {\n setEditingFilter(null)\n setShowFilterBuilder(false)\n }, [])\n\n // Hide filter panel completely when not editable (fully embedded mode without filter support)\n if (!editable) {\n return null\n }\n\n // Hide if no filters exist and not in edit mode (nothing to show)\n if (!isEditMode && dashboardFilters.length === 0) {\n return null\n }\n\n return (\n <div className=\"dc:mb-4\">\n {/* Edit Mode - Full filter management with chips and actions */}\n {isEditMode ? (\n <div\n className=\"dc:border dc:rounded-lg\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-surface)',\n boxShadow: 'var(--dc-shadow-sm)'\n }}\n >\n <EditModeFilterList\n dashboardFilters={dashboardFilters}\n onAddFilter={handleAddFilter}\n onAddTimeFilter={handleAddTimeFilter}\n onEditFilter={handleEditFilter}\n onRemoveFilter={handleRemoveFilter}\n selectedFilterId={selectedFilterId}\n onFilterSelect={onFilterSelect}\n />\n </div>\n ) : (\n /* View Mode - Compact Mixpanel-style filter bar */\n <CompactFilterBar\n dashboardFilters={dashboardFilters}\n schema={schema}\n isEditMode={false}\n onDashboardFiltersChange={onDashboardFiltersChange}\n onAddFilter={handleAddFilter}\n onEditFilter={handleEditFilter}\n onRemoveFilter={handleRemoveFilter}\n />\n )}\n\n {/* Filter Edit Modal */}\n {editable && showFilterBuilder && editingFilter && (\n <FilterEditModal\n filter={editingFilter}\n schema={schema}\n dashboardConfig={dashboardConfig}\n isOpen={showFilterBuilder}\n onSave={handleSaveFilter}\n onClose={handleCloseFilterBuilder}\n onDelete={() => handleRemoveFilter(editingFilter.id)}\n convertToMetaResponse={convertToMetaResponse}\n />\n )}\n </div>\n )\n}\n\nexport default DashboardFilterPanel\n","/**\n * ScaledGridWrapper component\n * Applies CSS transform scaling to the dashboard grid for intermediate screen sizes\n * Maintains the exact desktop layout appearance, just proportionally smaller\n */\n\nimport React, { useState, useEffect, useRef } from 'react'\n\ninterface ScaledGridWrapperProps {\n scaleFactor: number\n designWidth: number\n children: React.ReactNode\n}\n\n/**\n * Wrapper component that scales the grid using CSS transform\n * Handles height compensation to prevent overflow/whitespace issues\n */\nexport default function ScaledGridWrapper({\n scaleFactor,\n designWidth,\n children\n}: ScaledGridWrapperProps) {\n const [actualHeight, setActualHeight] = useState(0)\n const innerRef = useRef<HTMLDivElement>(null)\n\n // Measure actual grid height to calculate visible height\n useEffect(() => {\n if (!innerRef.current) return\n\n const observer = new ResizeObserver((entries) => {\n setActualHeight(entries[0]?.contentRect.height ?? 0)\n })\n\n observer.observe(innerRef.current)\n\n // Set initial height\n setActualHeight(innerRef.current.offsetHeight || 0)\n\n return () => observer.disconnect()\n }, [])\n\n // The scaled visual height\n const visualHeight = actualHeight * scaleFactor\n\n return (\n <div\n className=\"scaled-grid-container\"\n style={{\n height: visualHeight > 0 ? visualHeight : 'auto',\n overflow: 'hidden',\n width: '100%'\n }}\n >\n <div\n ref={innerRef}\n className=\"scaled-grid-inner\"\n style={{\n transform: `scale(${scaleFactor})`,\n transformOrigin: 'top left',\n width: designWidth\n }}\n >\n {children}\n </div>\n </div>\n )\n}\n","/**\n * MobileStackedLayout component\n * Simple vertical stack layout for mobile screens (<768px)\n * Read-only view with portlets sorted by grid position\n */\n\nimport { useMemo, useRef, useState, useCallback } from 'react'\nimport { getIcon } from '../icons'\nimport AnalyticsPortlet from './AnalyticsPortlet'\n\nconst RefreshIcon = getIcon('refresh')\nimport { ScrollContainerProvider } from '../providers/ScrollContainerContext'\nimport type { DashboardFilter, DashboardConfig } from '../types'\nimport type { ColorPalette } from '../utils/colorPalettes'\nimport { ensureAnalysisConfig } from '../utils/configMigration'\n\n/**\n * Finds the nearest scrollable ancestor of an element.\n */\nfunction findScrollableAncestor(element: HTMLElement | null): HTMLElement | null {\n if (!element) return null\n\n let current = element.parentElement\n\n while (current) {\n const style = window.getComputedStyle(current)\n const overflowY = style.overflowY\n const overflowX = style.overflowX\n\n const hasScrollableOverflow =\n overflowY === 'auto' || overflowY === 'scroll' ||\n overflowX === 'auto' || overflowX === 'scroll'\n\n const hasScrollContent =\n current.scrollHeight > current.clientHeight ||\n current.scrollWidth > current.clientWidth\n\n if (hasScrollableOverflow && hasScrollContent) {\n return current\n }\n\n if (current === document.body) break\n current = current.parentElement\n }\n\n return null\n}\n\ninterface MobileStackedLayoutProps {\n config: DashboardConfig\n colorPalette?: ColorPalette\n dashboardFilters?: DashboardFilter[]\n onPortletRefresh?: (portletId: string) => void\n}\n\n/**\n * Mobile-optimized stacked layout for dashboard portlets\n * Renders portlets in a single column, sorted by grid position\n */\nexport default function MobileStackedLayout({\n config,\n colorPalette,\n dashboardFilters,\n onPortletRefresh\n}: MobileStackedLayoutProps) {\n const portletComponentRefs = useRef<{ [key: string]: { refresh: () => void } | null }>({})\n\n // Scroll container detection for lazy loading\n const [scrollContainer, setScrollContainer] = useState<HTMLElement | null>(null)\n const containerRef = useRef<HTMLDivElement | null>(null)\n\n const setContainerRef = useCallback((node: HTMLDivElement | null) => {\n containerRef.current = node\n if (node) {\n setScrollContainer(findScrollableAncestor(node))\n }\n }, [])\n\n // Sort portlets by y position, then x position (top-to-bottom, left-to-right)\n const sortedPortlets = useMemo(() => {\n return [...config.portlets].sort((a, b) => {\n if (a.y !== b.y) return a.y - b.y\n return a.x - b.x\n })\n }, [config.portlets])\n\n const handlePortletRefresh = (portletId: string) => {\n // Refresh the specific portlet component\n portletComponentRefs.current[portletId]?.refresh()\n // Also call external handler if provided\n onPortletRefresh?.(portletId)\n }\n\n return (\n <ScrollContainerProvider value={scrollContainer}>\n <div ref={setContainerRef} className=\"mobile-stacked-layout dc:space-y-4 dc:px-2\">\n {sortedPortlets.map(portlet => {\n // Normalize portlet to ensure analysisConfig exists (on-the-fly migration)\n const normalizedPortlet = ensureAnalysisConfig(portlet)\n const { analysisConfig } = normalizedPortlet\n const chartModeConfig = analysisConfig.charts[analysisConfig.analysisType]\n const renderQuery = JSON.stringify(analysisConfig.query)\n const renderChartType = chartModeConfig?.chartType || 'line'\n const renderChartConfig = chartModeConfig?.chartConfig\n const renderDisplayConfig = chartModeConfig?.displayConfig\n\n // Calculate height: use stored h * rowHeight (80px), with minimum\n const portletHeight = Math.max(300, portlet.h * 80)\n // Header is approximately 40px when shown\n const headerHeight = renderDisplayConfig?.hideHeader ? 0 : 40\n // Content height = total - header - padding (py-3 = 24px)\n const contentHeight = portletHeight - headerHeight - 24\n\n return (\n <div\n key={portlet.id}\n data-portlet-id={portlet.id}\n className=\"bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:flex dc:flex-col\"\n style={{\n height: portletHeight,\n boxShadow: 'var(--dc-shadow-sm)'\n }}\n >\n {/* Portlet Header - Simplified for mobile (no edit controls) */}\n {!renderDisplayConfig?.hideHeader && (\n <div className=\"dc:flex dc:items-center dc:justify-between dc:px-3 dc:py-2 dc:border-b border-dc-border dc:shrink-0 bg-dc-surface-secondary dc:rounded-t-lg\">\n <h3 className=\"dc:font-semibold dc:text-sm text-dc-text dc:truncate dc:flex-1\">\n {portlet.title}\n </h3>\n <div className=\"dc:flex dc:items-center dc:gap-1 dc:shrink-0 dc:ml-2\">\n <button\n onClick={() => handlePortletRefresh(portlet.id)}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm text-dc-text-secondary dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors\"\n title=\"Refresh portlet data\"\n >\n <RefreshIcon style={{ width: '16px', height: '16px', color: 'currentColor' }} />\n </button>\n </div>\n </div>\n )}\n\n {/* Portlet Content - explicit height for charts to render */}\n <div\n className=\"dc:px-2 dc:py-3 dc:overflow-visible dc:flex dc:flex-col\"\n style={{ height: contentHeight }}\n >\n <AnalyticsPortlet\n ref={el => { portletComponentRefs.current[portlet.id] = el }}\n query={renderQuery}\n chartType={renderChartType}\n chartConfig={renderChartConfig}\n displayConfig={renderDisplayConfig}\n dashboardFilters={dashboardFilters}\n dashboardFilterMapping={portlet.dashboardFilterMapping}\n eagerLoad={portlet.eagerLoad ?? config.eagerLoad ?? false}\n title={portlet.title}\n height={contentHeight}\n colorPalette={colorPalette}\n />\n </div>\n </div>\n )\n })}\n </div>\n </ScrollContainerProvider>\n )\n}\n","/**\n * useDashboard - Master Coordination Hook\n *\n * The single hook that provides everything DashboardGrid needs:\n * - Zustand store state and actions (from Context)\n * - Computed values (canEdit, resolvedRows, etc.)\n * - Config-modifying actions (that call onConfigChange/onSave)\n *\n * This hook replaces 14+ useState calls and 25+ useCallback handlers,\n * providing a clean interface for the DashboardGrid component.\n *\n * IMPORTANT: This hook must be used within DashboardStoreProvider\n *\n * Usage:\n * ```tsx\n * const dashboard = useDashboard({\n * config,\n * editable,\n * gridSettings,\n * onConfigChange,\n * onSave,\n * })\n *\n * // Access state\n * const { isEditMode, selectedFilterId } = dashboard\n *\n * // Access computed values\n * const { canEdit, resolvedRows } = dashboard\n *\n * // Access actions\n * dashboard.actions.openAddPortlet()\n * ```\n */\n\nimport React, { useMemo, useCallback, useRef } from 'react'\nimport { useShallow } from 'zustand/react/shallow'\nimport {\n useDashboardStore,\n type DashboardStore,\n type PortletDebugDataEntry,\n} from '../stores/dashboardStore'\nimport { useCubeFeatures } from '../providers/CubeProvider'\nimport { captureThumbnail } from '../utils/thumbnail'\nimport type { LayoutItem } from 'react-grid-layout'\nimport type {\n DashboardConfig,\n PortletConfig,\n RowLayout,\n RowLayoutColumn,\n DashboardFilter,\n DashboardGridSettings,\n DashboardLayoutMode,\n} from '../types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseDashboardOptions {\n /** Dashboard configuration */\n config: DashboardConfig\n /** Whether dashboard is editable */\n editable?: boolean\n /** Dashboard filters */\n dashboardFilters?: DashboardFilter[]\n /** Grid settings */\n gridSettings: DashboardGridSettings\n /** Allowed layout modes */\n allowedModes?: DashboardLayoutMode[]\n /** Whether responsive mode allows editing (desktop only) */\n isResponsiveEditable?: boolean\n /** Config change handler */\n onConfigChange?: (config: DashboardConfig) => void\n /** Save handler */\n onSave?: (config: DashboardConfig) => Promise<void> | void\n /** Callback to save thumbnail separately - called on edit mode exit when thumbnail feature is enabled */\n onSaveThumbnail?: (thumbnailData: string) => Promise<string | void>\n /** Grid width for row calculations */\n gridWidth?: number\n /** Portlet component refs for refresh functionality */\n portletComponentRefs?: React.MutableRefObject<Record<string, { refresh: (options?: { bustCache?: boolean }) => void } | null>>\n /** Portlet refresh handler (external) */\n onPortletRefresh?: (portletId: string, options?: { bustCache?: boolean }) => void\n /** Ref to the dashboard container element for thumbnail capture */\n dashboardRef?: React.RefObject<HTMLElement | null>\n}\n\nexport interface UseDashboardResult {\n // =========================================================================\n // Store State\n // =========================================================================\n /** Whether dashboard is in edit mode */\n isEditMode: boolean\n /** Selected filter ID for filter assignment mode */\n selectedFilterId: string | null\n /** Whether portlet modal is open */\n isPortletModalOpen: boolean\n /** Portlet being edited */\n editingPortlet: PortletConfig | null\n /** Whether filter config modal is open */\n isFilterConfigModalOpen: boolean\n /** Portlet for filter configuration */\n filterConfigPortlet: PortletConfig | null\n /** Portlet ID pending delete confirmation */\n deleteConfirmPortletId: string | null\n /** Draft rows during drag operations */\n draftRows: RowLayout[] | null\n /** Whether a portlet is being dragged */\n isDraggingPortlet: boolean\n /** Last known layout for change detection */\n lastKnownLayout: LayoutItem[]\n /** Whether component is initialized */\n isInitialized: boolean\n /** Debug data per portlet */\n debugData: Record<string, PortletDebugDataEntry>\n\n // =========================================================================\n // Computed Values\n // =========================================================================\n /** Whether editing is allowed (editable && isEditMode && desktop && !filterMode) */\n canEdit: boolean\n /** Whether layout mode can be changed */\n canChangeLayoutMode: boolean\n /** Currently selected filter object */\n selectedFilter: DashboardFilter | null\n /** Resolved rows for row-based layout */\n resolvedRows: RowLayout[]\n /** Current layout mode */\n layoutMode: DashboardLayoutMode\n /** Allowed layout modes */\n allowedModes: DashboardLayoutMode[]\n\n // =========================================================================\n // Actions\n // =========================================================================\n actions: UseDashboardActions\n}\n\nexport interface UseDashboardActions {\n // Edit Mode\n enterEditMode: () => void\n exitEditMode: () => void\n toggleEditMode: () => void\n selectFilter: (filterId: string | null) => void\n exitFilterSelectionMode: () => void\n\n // Modals\n openAddPortlet: () => void\n openEditPortlet: (portlet: PortletConfig) => void\n closePortletModal: () => void\n openFilterConfig: (portlet: PortletConfig) => void\n closeFilterConfig: () => void\n\n // Layout State (store-only)\n setDraftRows: (rows: RowLayout[] | null) => void\n setIsDraggingPortlet: (isDragging: boolean) => void\n setLastKnownLayout: (layout: LayoutItem[]) => void\n setIsInitialized: (initialized: boolean) => void\n setDragState: (state: { rowIndex: number; colIndex: number; portletId: string } | null) => void\n clearDragState: () => void\n\n // Layout Operations (config-modifying)\n hasLayoutActuallyChanged: (newLayout: LayoutItem[]) => boolean\n updateRowLayout: (rows: RowLayout[], save?: boolean, portletsOverride?: PortletConfig[]) => Promise<void>\n handleLayoutModeChange: (mode: DashboardLayoutMode) => Promise<void>\n\n // Portlet Operations\n savePortlet: (portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>) => Promise<string | null>\n deletePortlet: (portletId: string) => Promise<void>\n duplicatePortlet: (portletId: string) => Promise<string | undefined>\n refreshPortlet: (portletId: string, options?: { bustCache?: boolean }) => void\n\n // Filter Operations\n toggleFilterForPortlet: (portletId: string, filterId: string) => Promise<void>\n selectAllForFilter: (filterId: string) => Promise<void>\n saveFilterConfig: (mapping: string[]) => Promise<void>\n\n // Config Operations\n handlePaletteChange: (paletteName: string) => Promise<void>\n\n // Delete Confirmation\n openDeleteConfirm: (portletId: string) => void\n closeDeleteConfirm: () => void\n confirmDelete: () => Promise<void>\n\n // Debug\n setDebugData: (portletId: string, data: PortletDebugDataEntry) => void\n clearDebugData: (portletId?: string) => void\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nconst createRowId = () => `row-${Date.now()}`\n\nconst equalizeRowColumns = (\n portletIds: string[],\n gridSettings: DashboardGridSettings\n): RowLayoutColumn[] => {\n const count = portletIds.length\n if (count === 0) return []\n\n const { cols, minW } = gridSettings\n const minTotal = minW * count\n\n if (minTotal > cols) {\n const base = Math.floor(cols / count)\n const remainder = cols % count\n return portletIds.map((id, index) => ({\n portletId: id,\n w: base + (index < remainder ? 1 : 0),\n }))\n }\n\n const remaining = cols - minTotal\n const extra = Math.floor(remaining / count)\n const remainder = remaining % count\n\n return portletIds.map((id, index) => ({\n portletId: id,\n w: minW + extra + (index < remainder ? 1 : 0),\n }))\n}\n\nconst adjustRowWidths = (\n columns: RowLayoutColumn[],\n gridSettings: DashboardGridSettings\n): RowLayoutColumn[] => {\n if (columns.length === 0) return []\n\n const { cols, minW } = gridSettings\n const adjusted = columns.map((column) => ({\n ...column,\n w: Math.max(minW, column.w),\n }))\n\n let total = adjusted.reduce((sum, column) => sum + column.w, 0)\n if (total === cols) return adjusted\n\n if (total < cols) {\n let remaining = cols - total\n let index = 0\n while (remaining > 0) {\n adjusted[index % adjusted.length].w += 1\n remaining -= 1\n index += 1\n }\n return adjusted\n }\n\n let overflow = total - cols\n for (let index = adjusted.length - 1; index >= 0 && overflow > 0; index -= 1) {\n const column = adjusted[index]\n const reducible = Math.max(0, column.w - minW)\n if (reducible === 0) continue\n const delta = Math.min(reducible, overflow)\n column.w -= delta\n overflow -= delta\n }\n\n return adjusted\n}\n\nconst convertPortletsToRows = (\n portlets: PortletConfig[],\n gridSettings: DashboardGridSettings\n): RowLayout[] => {\n if (portlets.length === 0) return []\n\n const sorted = [...portlets].sort((a, b) => {\n if (a.y !== b.y) return a.y - b.y\n return a.x - b.x\n })\n\n const rowsByY = new Map<number, PortletConfig[]>()\n sorted.forEach((portlet) => {\n const row = rowsByY.get(portlet.y) ?? []\n row.push(portlet)\n rowsByY.set(portlet.y, row)\n })\n\n return Array.from(rowsByY.entries())\n .sort(([a], [b]) => a - b)\n .map(([rowY, rowPortlets]) => {\n const rowHeight = Math.max(gridSettings.minH, ...rowPortlets.map((p) => p.h))\n const portletIds = rowPortlets.map((p) => p.id)\n return {\n id: `row-${rowY}`,\n h: rowHeight,\n columns: equalizeRowColumns(portletIds, gridSettings),\n }\n })\n}\n\nconst normalizeRows = (\n rows: RowLayout[],\n portlets: PortletConfig[],\n gridSettings: DashboardGridSettings\n): RowLayout[] => {\n const portletIds = new Set(portlets.map((p) => p.id))\n return rows\n .map((row) => ({\n ...row,\n h: Math.max(gridSettings.minH, row.h),\n columns: adjustRowWidths(\n row.columns.filter((col) => portletIds.has(col.portletId)),\n gridSettings\n ),\n }))\n .filter((row) => row.columns.length > 0)\n}\n\nconst convertRowsToPortlets = (\n rows: RowLayout[],\n portlets: PortletConfig[]\n): PortletConfig[] => {\n const portletMap = new Map(portlets.map((p) => [p.id, p]))\n let currentY = 0\n\n const updated: PortletConfig[] = []\n rows.forEach((row) => {\n let currentX = 0\n row.columns.forEach((column) => {\n const portlet = portletMap.get(column.portletId)\n if (!portlet) return\n updated.push({\n ...portlet,\n x: currentX,\n y: currentY,\n w: column.w,\n h: row.h,\n })\n currentX += column.w\n })\n currentY += row.h\n })\n\n const updatedIds = new Set(updated.map((p) => p.id))\n portlets.forEach((portlet) => {\n if (!updatedIds.has(portlet.id)) {\n updated.push(portlet)\n }\n })\n\n return updated\n}\n\n// ============================================================================\n// Selectors\n// ============================================================================\n\nconst selectStoreState = (state: DashboardStore) => ({\n isEditMode: state.isEditMode,\n selectedFilterId: state.selectedFilterId,\n isPortletModalOpen: state.isPortletModalOpen,\n editingPortlet: state.editingPortlet,\n isFilterConfigModalOpen: state.isFilterConfigModalOpen,\n filterConfigPortlet: state.filterConfigPortlet,\n deleteConfirmPortletId: state.deleteConfirmPortletId,\n draftRows: state.draftRows,\n isDraggingPortlet: state.isDraggingPortlet,\n lastKnownLayout: state.lastKnownLayout,\n isInitialized: state.isInitialized,\n debugData: state.debugData,\n thumbnailDirty: state.thumbnailDirty,\n})\n\nconst selectStoreActions = (state: DashboardStore) => ({\n setEditMode: state.setEditMode,\n toggleEditMode: state.toggleEditMode,\n setSelectedFilterId: state.setSelectedFilterId,\n exitFilterSelectionMode: state.exitFilterSelectionMode,\n openPortletModal: state.openPortletModal,\n closePortletModal: state.closePortletModal,\n openFilterConfigModal: state.openFilterConfigModal,\n closeFilterConfigModal: state.closeFilterConfigModal,\n openDeleteConfirm: state.openDeleteConfirm,\n closeDeleteConfirm: state.closeDeleteConfirm,\n setDraftRows: state.setDraftRows,\n setIsDraggingPortlet: state.setIsDraggingPortlet,\n setLastKnownLayout: state.setLastKnownLayout,\n setIsInitialized: state.setIsInitialized,\n setDragState: state.setDragState,\n clearDragState: state.clearDragState,\n setDebugData: state.setDebugData,\n clearDebugData: state.clearDebugData,\n setThumbnailDirty: state.setThumbnailDirty,\n})\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useDashboard(options: UseDashboardOptions): UseDashboardResult {\n const {\n config,\n editable = false,\n dashboardFilters,\n gridSettings,\n allowedModes: propAllowedModes,\n isResponsiveEditable = true,\n onConfigChange,\n onSave,\n onSaveThumbnail,\n portletComponentRefs,\n onPortletRefresh,\n dashboardRef,\n } = options\n\n // =========================================================================\n // Store Access\n // =========================================================================\n\n const storeState = useDashboardStore(useShallow(selectStoreState))\n const storeActions = useDashboardStore(useShallow(selectStoreActions))\n\n // Get thumbnail feature config from context\n const { features } = useCubeFeatures()\n const thumbnailConfig = features.thumbnail\n\n // Keep refs for draft rows (needed for mouse event handlers)\n const draftRowsRef = useRef<RowLayout[] | null>(null)\n draftRowsRef.current = storeState.draftRows\n\n // =========================================================================\n // Computed Values\n // =========================================================================\n\n const allowedModes: DashboardLayoutMode[] = useMemo(() => {\n return propAllowedModes && propAllowedModes.length > 0\n ? propAllowedModes\n : ['rows', 'grid']\n }, [propAllowedModes])\n\n const layoutMode: DashboardLayoutMode = useMemo(() => {\n const fallbackMode: DashboardLayoutMode = allowedModes.includes('rows')\n ? 'rows'\n : allowedModes[0] ?? 'grid'\n const configMode = config.layoutMode ?? 'grid'\n return allowedModes.includes(configMode) ? configMode : fallbackMode\n }, [config.layoutMode, allowedModes])\n\n const canEdit = useMemo(() => {\n return (\n editable &&\n storeState.isEditMode &&\n isResponsiveEditable &&\n !storeState.selectedFilterId\n )\n }, [editable, storeState.isEditMode, isResponsiveEditable, storeState.selectedFilterId])\n\n const canChangeLayoutMode = useMemo(() => {\n return (\n editable &&\n storeState.isEditMode &&\n isResponsiveEditable &&\n !storeState.selectedFilterId &&\n allowedModes.length > 1\n )\n }, [\n editable,\n storeState.isEditMode,\n isResponsiveEditable,\n storeState.selectedFilterId,\n allowedModes.length,\n ])\n\n const selectedFilter = useMemo(() => {\n if (!storeState.selectedFilterId || !dashboardFilters) return null\n return dashboardFilters.find((f) => f.id === storeState.selectedFilterId) ?? null\n }, [storeState.selectedFilterId, dashboardFilters])\n\n const resolvedRows = useMemo(() => {\n if (layoutMode !== 'rows') return []\n const baseRows =\n storeState.draftRows ??\n config.rows ??\n convertPortletsToRows(config.portlets, gridSettings)\n return normalizeRows(baseRows, config.portlets, gridSettings)\n }, [layoutMode, storeState.draftRows, config.rows, config.portlets, gridSettings])\n\n // =========================================================================\n // Actions\n // =========================================================================\n\n // Edit mode actions\n const enterEditMode = useCallback(() => {\n storeActions.setEditMode(true)\n }, [storeActions])\n\n const exitEditMode = useCallback(() => {\n storeActions.setEditMode(false)\n\n // Capture thumbnail in background after UI has fully re-rendered\n // Use longer delay to ensure edit mode UI elements are removed and charts settle\n if (storeState.thumbnailDirty && thumbnailConfig?.enabled && dashboardRef) {\n setTimeout(async () => {\n const thumbnailData = await captureThumbnail(dashboardRef, thumbnailConfig)\n if (thumbnailData && onSaveThumbnail) {\n try {\n const thumbnailUrl = await onSaveThumbnail(thumbnailData)\n // Optionally update config with URL\n if (thumbnailUrl && onConfigChange) {\n onConfigChange({ ...config, thumbnailUrl, thumbnailData: undefined })\n }\n } catch (error) {\n console.error('Failed to save thumbnail:', error)\n }\n }\n storeActions.setThumbnailDirty(false)\n }, 500) // 500ms delay for re-render (edit mode UI removal + chart settling)\n }\n }, [storeActions, storeState.thumbnailDirty, thumbnailConfig, dashboardRef, onSaveThumbnail, config, onConfigChange])\n\n const toggleEditMode = useCallback(() => {\n if (isResponsiveEditable) {\n if (storeState.isEditMode) {\n // Use exitEditMode to handle thumbnail capture\n exitEditMode()\n } else {\n storeActions.setEditMode(true)\n }\n }\n }, [isResponsiveEditable, storeActions, storeState.isEditMode, exitEditMode])\n\n const selectFilter = useCallback(\n (filterId: string | null) => {\n // Toggle selection: if already selected, deselect\n storeActions.setSelectedFilterId(\n filterId === storeState.selectedFilterId ? null : filterId\n )\n },\n [storeActions, storeState.selectedFilterId]\n )\n\n // Modal actions\n const openAddPortlet = useCallback(() => {\n storeActions.openPortletModal(null)\n }, [storeActions])\n\n const openEditPortlet = useCallback(\n (portlet: PortletConfig) => {\n storeActions.openPortletModal(portlet)\n },\n [storeActions]\n )\n\n const openFilterConfig = useCallback(\n (portlet: PortletConfig) => {\n storeActions.openFilterConfigModal(portlet)\n },\n [storeActions]\n )\n\n // Layout change detection\n const hasLayoutActuallyChanged = useCallback(\n (newLayout: LayoutItem[]) => {\n if (!storeState.isInitialized || storeState.lastKnownLayout.length === 0) {\n return false\n }\n\n for (const newItem of newLayout) {\n const oldItem = storeState.lastKnownLayout.find((item) => item.i === newItem.i)\n if (!oldItem) continue\n\n if (\n oldItem.x !== newItem.x ||\n oldItem.y !== newItem.y ||\n oldItem.w !== newItem.w ||\n oldItem.h !== newItem.h\n ) {\n return true\n }\n }\n return false\n },\n [storeState.isInitialized, storeState.lastKnownLayout]\n )\n\n // Row layout update\n const updateRowLayout = useCallback(\n async (\n rows: RowLayout[],\n save = true,\n portletsOverride?: PortletConfig[]\n ) => {\n if (!onConfigChange) return\n\n const portlets = portletsOverride ?? config.portlets\n const normalizedRows = normalizeRows(rows, portlets, gridSettings)\n const updatedPortlets = convertRowsToPortlets(normalizedRows, portlets)\n const updatedConfig: DashboardConfig = {\n ...config,\n layoutMode: 'rows',\n rows: normalizedRows,\n portlets: updatedPortlets,\n }\n\n storeActions.setDraftRows(null)\n onConfigChange(updatedConfig)\n\n if (save && onSave) {\n try {\n await onSave(updatedConfig)\n storeActions.setThumbnailDirty(true)\n } catch (error) {\n console.error('Auto-save failed after row layout change:', error)\n }\n }\n },\n [config, gridSettings, onConfigChange, onSave, storeActions]\n )\n\n // Layout mode change\n const handleLayoutModeChange = useCallback(\n async (mode: DashboardLayoutMode) => {\n if (\n !onConfigChange ||\n mode === layoutMode ||\n !canChangeLayoutMode ||\n !allowedModes.includes(mode)\n ) {\n return\n }\n\n const baseRows = normalizeRows(\n config.rows && config.rows.length > 0\n ? config.rows\n : convertPortletsToRows(config.portlets, gridSettings),\n config.portlets,\n gridSettings\n )\n\n const updatedPortlets = convertRowsToPortlets(baseRows, config.portlets)\n const updatedConfig: DashboardConfig = {\n ...config,\n layoutMode: mode,\n rows: baseRows,\n portlets: updatedPortlets,\n }\n\n storeActions.setDraftRows(null)\n onConfigChange(updatedConfig)\n\n if (onSave) {\n try {\n await onSave(updatedConfig)\n storeActions.setThumbnailDirty(true)\n } catch (error) {\n console.error('Auto-save failed after layout mode switch:', error)\n }\n }\n },\n [\n allowedModes,\n canChangeLayoutMode,\n config,\n gridSettings,\n layoutMode,\n onConfigChange,\n onSave,\n storeActions,\n ]\n )\n\n // Portlet operations\n const savePortlet = useCallback(\n async (\n portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>\n ): Promise<string | null> => {\n if (!onConfigChange) return null\n\n let updatedPortlets = [...config.portlets]\n let isNewPortlet = false\n let newPortletId: string | null = null\n\n if (storeState.editingPortlet) {\n // Editing existing portlet\n const index = updatedPortlets.findIndex(\n (p) => p.id === storeState.editingPortlet!.id\n )\n if (index !== -1) {\n updatedPortlets[index] = portletData as PortletConfig\n }\n } else {\n // Adding new portlet\n isNewPortlet = true\n const newPortlet: PortletConfig = {\n ...portletData,\n id: `portlet-${Date.now()}`,\n x: 0,\n y: 0,\n } as PortletConfig\n\n newPortletId = newPortlet.id\n\n // Find best position for new portlet\n let maxY = 0\n config.portlets.forEach((p) => {\n if (p.y + p.h > maxY) {\n maxY = p.y + p.h\n }\n })\n newPortlet.y = maxY\n\n updatedPortlets.push(newPortlet)\n }\n\n if (layoutMode === 'rows') {\n const baseRows =\n resolvedRows.length > 0\n ? resolvedRows.map((row) => ({\n ...row,\n columns: row.columns.map((col) => ({ ...col })),\n }))\n : normalizeRows(\n config.rows ?? convertPortletsToRows(config.portlets, gridSettings),\n updatedPortlets,\n gridSettings\n )\n\n const nextRows =\n isNewPortlet && newPortletId\n ? [\n ...baseRows,\n {\n id: createRowId(),\n h: Math.max(gridSettings.minH, 5),\n columns: equalizeRowColumns([newPortletId], gridSettings),\n },\n ]\n : baseRows\n\n await updateRowLayout(nextRows, true, updatedPortlets)\n } else {\n const updatedConfig: DashboardConfig = {\n ...config,\n portlets: updatedPortlets,\n }\n\n onConfigChange(updatedConfig)\n\n if (onSave) {\n try {\n await onSave(updatedConfig)\n storeActions.setThumbnailDirty(true)\n } catch (error) {\n console.error('Auto-save failed:', error)\n }\n }\n }\n\n storeActions.closePortletModal()\n return newPortletId\n },\n [\n config,\n gridSettings,\n layoutMode,\n onConfigChange,\n onSave,\n resolvedRows,\n storeActions,\n storeState.editingPortlet,\n updateRowLayout,\n ]\n )\n\n // Internal delete logic (called after confirmation)\n const executeDeletePortlet = useCallback(\n async (portletId: string) => {\n if (!onConfigChange) return\n\n const updatedPortlets = config.portlets.filter((p) => p.id !== portletId)\n\n if (layoutMode === 'rows') {\n const nextRows = resolvedRows\n .map((row) => ({\n ...row,\n columns: row.columns.filter((col) => col.portletId !== portletId),\n }))\n .filter((row) => row.columns.length > 0)\n .map((row) => ({\n ...row,\n columns: equalizeRowColumns(\n row.columns.map((col) => col.portletId),\n gridSettings\n ),\n }))\n\n await updateRowLayout(nextRows, true, updatedPortlets)\n } else {\n const updatedConfig: DashboardConfig = {\n ...config,\n portlets: updatedPortlets,\n }\n\n onConfigChange(updatedConfig)\n\n if (onSave) {\n try {\n await onSave(updatedConfig)\n storeActions.setThumbnailDirty(true)\n } catch (error) {\n console.error('Auto-save failed:', error)\n }\n }\n }\n },\n [config, gridSettings, layoutMode, onConfigChange, onSave, resolvedRows, storeActions, updateRowLayout]\n )\n\n // Public delete action - opens confirmation modal\n const deletePortlet = useCallback(\n async (portletId: string) => {\n storeActions.openDeleteConfirm(portletId)\n },\n [storeActions]\n )\n\n // Confirm delete action - called when user confirms in modal\n const confirmDelete = useCallback(async () => {\n const portletId = storeState.deleteConfirmPortletId\n if (!portletId) return\n\n await executeDeletePortlet(portletId)\n storeActions.closeDeleteConfirm()\n }, [executeDeletePortlet, storeState.deleteConfirmPortletId, storeActions])\n\n const duplicatePortlet = useCallback(\n async (portletId: string): Promise<string | undefined> => {\n if (!onConfigChange) return undefined\n\n const originalPortlet = config.portlets.find((p) => p.id === portletId)\n if (!originalPortlet) return undefined\n\n const duplicatedPortlet: PortletConfig = {\n ...originalPortlet,\n id: `portlet-${Date.now()}`,\n title: `${originalPortlet.title} Duplicated`,\n x: 0,\n y: 0,\n }\n\n let maxY = 0\n config.portlets.forEach((p) => {\n if (p.y + p.h > maxY) {\n maxY = p.y + p.h\n }\n })\n duplicatedPortlet.y = maxY\n\n const updatedPortlets = [...config.portlets, duplicatedPortlet]\n\n if (layoutMode === 'rows') {\n const baseRows = resolvedRows.map((row) => ({\n ...row,\n columns: row.columns.map((col) => ({ ...col })),\n }))\n const nextRows = [\n ...baseRows,\n {\n id: createRowId(),\n h: Math.max(gridSettings.minH, 5),\n columns: equalizeRowColumns([duplicatedPortlet.id], gridSettings),\n },\n ]\n await updateRowLayout(nextRows, true, updatedPortlets)\n } else {\n const updatedConfig: DashboardConfig = {\n ...config,\n portlets: updatedPortlets,\n }\n\n onConfigChange(updatedConfig)\n\n if (onSave) {\n try {\n await onSave(updatedConfig)\n storeActions.setThumbnailDirty(true)\n } catch (error) {\n console.error('Auto-save failed:', error)\n }\n }\n }\n\n return duplicatedPortlet.id\n },\n [config, gridSettings, layoutMode, onConfigChange, onSave, resolvedRows, storeActions, updateRowLayout]\n )\n\n const refreshPortlet = useCallback(\n (portletId: string, options?: { bustCache?: boolean }) => {\n const portletComponent = portletComponentRefs?.current?.[portletId]\n if (portletComponent?.refresh) {\n portletComponent.refresh(options)\n }\n onPortletRefresh?.(portletId, options)\n },\n [portletComponentRefs, onPortletRefresh]\n )\n\n // Filter operations\n const toggleFilterForPortlet = useCallback(\n async (portletId: string, filterId: string) => {\n if (!onConfigChange) return\n\n const updatedPortlets = config.portlets.map((p) => {\n if (p.id === portletId) {\n const currentMapping = p.dashboardFilterMapping || []\n const hasFilter = currentMapping.includes(filterId)\n\n return {\n ...p,\n dashboardFilterMapping: hasFilter\n ? currentMapping.filter((id) => id !== filterId)\n : [...currentMapping, filterId],\n }\n }\n return p\n })\n\n const updatedConfig: DashboardConfig = {\n ...config,\n portlets: updatedPortlets,\n }\n\n onConfigChange(updatedConfig)\n\n if (onSave) {\n try {\n await onSave(updatedConfig)\n storeActions.setThumbnailDirty(true)\n } catch (error) {\n console.error('Auto-save failed:', error)\n }\n }\n },\n [config, onConfigChange, onSave, storeActions]\n )\n\n const selectAllForFilter = useCallback(\n async (filterId: string) => {\n if (!onConfigChange) return\n\n const updatedPortlets = config.portlets.map((p) => {\n const currentMapping = p.dashboardFilterMapping || []\n if (!currentMapping.includes(filterId)) {\n return {\n ...p,\n dashboardFilterMapping: [...currentMapping, filterId],\n }\n }\n return p\n })\n\n const updatedConfig: DashboardConfig = {\n ...config,\n portlets: updatedPortlets,\n }\n\n onConfigChange(updatedConfig)\n\n if (onSave) {\n try {\n await onSave(updatedConfig)\n storeActions.setThumbnailDirty(true)\n } catch (error) {\n console.error('Auto-save failed:', error)\n }\n }\n },\n [config, onConfigChange, onSave, storeActions]\n )\n\n const saveFilterConfig = useCallback(\n async (mapping: string[]) => {\n if (!onConfigChange || !storeState.filterConfigPortlet) return\n\n const updatedPortlets = config.portlets.map((p) => {\n if (p.id === storeState.filterConfigPortlet!.id) {\n return {\n ...p,\n dashboardFilterMapping: mapping,\n }\n }\n return p\n })\n\n const updatedConfig: DashboardConfig = {\n ...config,\n portlets: updatedPortlets,\n }\n\n onConfigChange(updatedConfig)\n\n if (onSave) {\n try {\n await onSave(updatedConfig)\n storeActions.setThumbnailDirty(true)\n } catch (error) {\n console.error('Auto-save failed:', error)\n }\n }\n },\n [config, onConfigChange, onSave, storeActions, storeState.filterConfigPortlet]\n )\n\n // Config operations\n const handlePaletteChange = useCallback(\n async (paletteName: string) => {\n if (!onConfigChange) return\n\n const updatedConfig: DashboardConfig = {\n ...config,\n colorPalette: paletteName,\n }\n\n onConfigChange(updatedConfig)\n\n if (onSave) {\n try {\n await onSave(updatedConfig)\n storeActions.setThumbnailDirty(true)\n } catch (error) {\n console.error('Auto-save failed:', error)\n }\n }\n },\n [config, onConfigChange, onSave, storeActions]\n )\n\n // =========================================================================\n // Assemble Result\n // =========================================================================\n\n const actions: UseDashboardActions = useMemo(\n () => ({\n // Edit mode\n enterEditMode,\n exitEditMode,\n toggleEditMode,\n selectFilter,\n exitFilterSelectionMode: storeActions.exitFilterSelectionMode,\n\n // Modals\n openAddPortlet,\n openEditPortlet,\n closePortletModal: storeActions.closePortletModal,\n openFilterConfig,\n closeFilterConfig: storeActions.closeFilterConfigModal,\n\n // Layout state\n setDraftRows: storeActions.setDraftRows,\n setIsDraggingPortlet: storeActions.setIsDraggingPortlet,\n setLastKnownLayout: storeActions.setLastKnownLayout,\n setIsInitialized: storeActions.setIsInitialized,\n setDragState: storeActions.setDragState,\n clearDragState: storeActions.clearDragState,\n\n // Layout operations\n hasLayoutActuallyChanged,\n updateRowLayout,\n handleLayoutModeChange,\n\n // Portlet operations\n savePortlet,\n deletePortlet,\n duplicatePortlet,\n refreshPortlet,\n\n // Filter operations\n toggleFilterForPortlet,\n selectAllForFilter,\n saveFilterConfig,\n\n // Config operations\n handlePaletteChange,\n\n // Delete confirmation\n openDeleteConfirm: storeActions.openDeleteConfirm,\n closeDeleteConfirm: storeActions.closeDeleteConfirm,\n confirmDelete,\n\n // Debug\n setDebugData: storeActions.setDebugData,\n clearDebugData: storeActions.clearDebugData,\n }),\n [\n enterEditMode,\n exitEditMode,\n toggleEditMode,\n selectFilter,\n storeActions,\n openAddPortlet,\n openEditPortlet,\n openFilterConfig,\n hasLayoutActuallyChanged,\n updateRowLayout,\n handleLayoutModeChange,\n savePortlet,\n deletePortlet,\n duplicatePortlet,\n refreshPortlet,\n toggleFilterForPortlet,\n selectAllForFilter,\n saveFilterConfig,\n handlePaletteChange,\n confirmDelete,\n ]\n )\n\n return {\n // Store state\n ...storeState,\n\n // Computed values\n canEdit,\n canChangeLayoutMode,\n selectedFilter,\n resolvedRows,\n layoutMode,\n allowedModes,\n\n // Actions\n actions,\n }\n}\n","/**\n * Dashboard Grid Component\n * Uses react-grid-layout for responsive grid layout\n * Simplified version without app-specific dependencies\n */\n\nimport {\n useCallback,\n useRef,\n useState,\n useEffect,\n useMemo,\n type ReactNode,\n type HTMLAttributes,\n type DragEvent,\n type MouseEvent,\n type Ref\n} from 'react'\nimport ReactGridLayout, { verticalCompactor, type LayoutItem, type Layout } from 'react-grid-layout'\nimport { getIcon } from '../icons'\nimport { useScrollDetection } from '../hooks/useScrollDetection'\nimport { useElementVisibility } from '../hooks/useElementVisibility'\nimport { useDragAutoScroll } from '../hooks/useDragAutoScroll'\nimport DashboardPortletCard from './DashboardPortletCard'\nimport RowManagedLayout from './RowManagedLayout'\nimport FloatingEditToolbar from './FloatingEditToolbar'\n\nconst ChartBarIcon = getIcon('measure')\nconst RefreshIcon = getIcon('refresh')\nconst EditIcon = getIcon('edit')\nconst CheckIcon = getIcon('check')\nconst DeleteIcon = getIcon('delete')\nconst AddIcon = getIcon('add')\nconst CopyIcon = getIcon('copy')\nconst FilterIcon = getIcon('filter')\nconst DesktopIcon = getIcon('desktop')\nconst GridIcon = getIcon('segment')\nconst RowsIcon = getIcon('table')\nimport PortletAnalysisModal from './PortletAnalysisModal'\nimport PortletFilterConfigModal from './PortletFilterConfigModal'\nimport ConfirmModal from './ConfirmModal'\nimport { useCubeFeatures } from '../providers/CubeProvider'\nimport ColorPaletteSelector from './ColorPaletteSelector'\nimport DashboardFilterPanel from './DashboardFilterPanel'\nimport ScaledGridWrapper from './ScaledGridWrapper'\nimport MobileStackedLayout from './MobileStackedLayout'\nimport { useResponsiveDashboard } from '../hooks/useResponsiveDashboard'\nimport { ScrollContainerProvider } from '../providers/ScrollContainerContext'\nimport { useDashboard } from '../hooks/useDashboardHook'\nimport type { ColorPalette } from '../utils/colorPalettes'\n\n/**\n * Finds the nearest scrollable ancestor of an element.\n * Used to detect scroll container for lazy loading IntersectionObserver.\n */\nfunction findScrollableAncestor(element: HTMLElement | null): HTMLElement | null {\n if (!element) return null\n\n let current = element.parentElement\n\n while (current) {\n const style = window.getComputedStyle(current)\n const overflowY = style.overflowY\n const overflowX = style.overflowX\n\n const hasScrollableOverflow =\n overflowY === 'auto' || overflowY === 'scroll' ||\n overflowX === 'auto' || overflowX === 'scroll'\n\n const hasScrollContent =\n current.scrollHeight > current.clientHeight ||\n current.scrollWidth > current.clientWidth\n\n if (hasScrollableOverflow && hasScrollContent) {\n return current\n }\n\n if (current === document.body) break\n current = current.parentElement\n }\n\n return null // Use viewport\n}\nimport type {\n DashboardConfig,\n PortletConfig,\n DashboardFilter,\n CubeMeta,\n DashboardLayoutMode,\n DashboardGridSettings,\n RowLayout,\n RowLayoutColumn\n} from '../types'\n\n// CSS for react-grid-layout should be imported by the consuming app\n// import 'react-grid-layout/css/styles.css'\n\ninterface DashboardGridProps {\n config: DashboardConfig\n editable?: boolean\n dashboardFilters?: DashboardFilter[] // Dashboard-level filters to apply to portlets\n loadingComponent?: ReactNode // Custom loading indicator for all portlets\n onConfigChange?: (config: DashboardConfig) => void\n onPortletRefresh?: (portletId: string) => void\n onSave?: (config: DashboardConfig) => Promise<void> | void\n /** Callback to save thumbnail separately - called on edit mode exit when thumbnail feature is enabled */\n onSaveThumbnail?: (thumbnailData: string) => Promise<string | void>\n colorPalette?: ColorPalette // Complete palette with both colors and gradient\n schema?: CubeMeta | null // Cube metadata for filter panel\n onDashboardFiltersChange?: (filters: DashboardFilter[]) => void // Handler for filter changes\n dashboardModes?: DashboardLayoutMode[]\n}\n\nconst DEFAULT_GRID_SETTINGS: DashboardGridSettings = {\n cols: 12,\n rowHeight: 80,\n minW: 2,\n minH: 2\n}\n\nconst createRowId = () => `row-${Date.now()}`\n\nconst getGridSettings = (config: DashboardConfig): DashboardGridSettings => ({\n cols: config.grid?.cols ?? DEFAULT_GRID_SETTINGS.cols,\n rowHeight: config.grid?.rowHeight ?? DEFAULT_GRID_SETTINGS.rowHeight,\n minW: config.grid?.minW ?? DEFAULT_GRID_SETTINGS.minW,\n minH: config.grid?.minH ?? DEFAULT_GRID_SETTINGS.minH\n})\n\nconst equalizeRowColumns = (\n portletIds: string[],\n gridSettings: DashboardGridSettings\n): RowLayoutColumn[] => {\n const count = portletIds.length\n if (count === 0) return []\n\n const { cols, minW } = gridSettings\n const minTotal = minW * count\n\n if (minTotal > cols) {\n const base = Math.floor(cols / count)\n const remainder = cols % count\n return portletIds.map((id, index) => ({\n portletId: id,\n w: base + (index < remainder ? 1 : 0)\n }))\n }\n\n const remaining = cols - minTotal\n const extra = Math.floor(remaining / count)\n const remainder = remaining % count\n\n return portletIds.map((id, index) => ({\n portletId: id,\n w: minW + extra + (index < remainder ? 1 : 0)\n }))\n}\n\nconst adjustRowWidths = (\n columns: RowLayoutColumn[],\n gridSettings: DashboardGridSettings\n): RowLayoutColumn[] => {\n if (columns.length === 0) return []\n\n const { cols, minW } = gridSettings\n const adjusted = columns.map(column => ({\n ...column,\n w: Math.max(minW, column.w)\n }))\n\n let total = adjusted.reduce((sum, column) => sum + column.w, 0)\n if (total === cols) return adjusted\n\n if (total < cols) {\n let remaining = cols - total\n let index = 0\n while (remaining > 0) {\n adjusted[index % adjusted.length].w += 1\n remaining -= 1\n index += 1\n }\n return adjusted\n }\n\n let overflow = total - cols\n for (let index = adjusted.length - 1; index >= 0 && overflow > 0; index -= 1) {\n const column = adjusted[index]\n const reducible = Math.max(0, column.w - minW)\n if (reducible === 0) continue\n const delta = Math.min(reducible, overflow)\n column.w -= delta\n overflow -= delta\n }\n\n return adjusted\n}\n\n// Helper functions moved to useDashboardHook.ts:\n// - convertPortletsToRows\n// - normalizeRows\n// - convertRowsToPortlets\n\nexport default function DashboardGrid({\n config,\n editable = false,\n dashboardFilters,\n loadingComponent,\n onConfigChange,\n onPortletRefresh,\n onSave,\n onSaveThumbnail,\n colorPalette,\n schema,\n onDashboardFiltersChange,\n dashboardModes\n}: DashboardGridProps) {\n // Get features from context for conditional modal rendering\n const { features } = useCubeFeatures()\n\n // Responsive dashboard hook for three-tier layout strategy\n const {\n containerRef,\n containerWidth,\n displayMode,\n scaleFactor,\n isEditable: isResponsiveEditable,\n designWidth\n } = useResponsiveDashboard()\n\n // allowedModes is passed to useDashboard hook which computes layoutMode\n const allowedModes: DashboardLayoutMode[] = dashboardModes && dashboardModes.length > 0\n ? dashboardModes\n : ['rows', 'grid']\n const gridSettings = useMemo(() => getGridSettings(config), [config])\n\n // Scroll container detection for lazy loading\n // Null = viewport, element = scrolling container\n // Detection happens once in the ref callback to avoid double state updates\n const [scrollContainer, setScrollContainer] = useState<HTMLElement | null>(null)\n const containerElementRef = useRef<HTMLDivElement | null>(null)\n const scrollContainerRef = useRef<HTMLElement | null>(null)\n const editBarRef = useRef<HTMLDivElement | null>(null)\n // Separate ref for grid content area (used for thumbnail capture - excludes toolbar/filters)\n const gridContentRef = useRef<HTMLDivElement | null>(null)\n\n // Combined ref for container\n const combinedContainerRef = useCallback((node: HTMLDivElement | null) => {\n containerElementRef.current = node\n containerRef(node)\n if (node) {\n const foundScrollContainer = findScrollableAncestor(node)\n setScrollContainer(foundScrollContainer)\n scrollContainerRef.current = foundScrollContainer\n }\n }, [containerRef])\n\n // Calculate grid width based on display mode\n // Desktop: use actual container width (allows wider than 1200px)\n // Scaled: use design width (1200px) and apply CSS scaling\n const gridWidth = displayMode === 'desktop' ? containerWidth : designWidth\n\n // Refs to store portlet refs for refresh functionality (kept local - DOM-specific)\n const portletRefs = useRef<{ [key: string]: HTMLDivElement | null }>({})\n const portletComponentRefs = useRef<{ [key: string]: { refresh: () => void } | null }>({})\n const draftRowsRef = useRef<RowLayout[] | null>(null)\n const dragStateRef = useRef<{ rowIndex: number; colIndex: number; portletId: string } | null>(null)\n\n // =========================================================================\n // Dashboard State from Zustand Store via useDashboard hook\n // Replaces 11 useState calls and provides computed values + actions\n // =========================================================================\n const dashboard = useDashboard({\n config,\n editable,\n dashboardFilters,\n gridSettings,\n allowedModes,\n isResponsiveEditable,\n onConfigChange,\n onSave,\n onSaveThumbnail,\n gridWidth,\n portletComponentRefs,\n onPortletRefresh,\n dashboardRef: gridContentRef, // For thumbnail capture on exit edit mode (grid content only, excludes toolbar/filters)\n })\n\n // Destructure for easier access (maintains existing variable names)\n // Note: lastKnownLayout is managed internally by the hook and accessed via actions\n const {\n isEditMode,\n selectedFilterId,\n isPortletModalOpen,\n editingPortlet,\n isFilterConfigModalOpen,\n filterConfigPortlet,\n deleteConfirmPortletId,\n draftRows,\n isDraggingPortlet,\n isInitialized,\n // debugData removed - now read from store directly in DashboardPortletCard\n canEdit,\n canChangeLayoutMode,\n selectedFilter,\n resolvedRows,\n layoutMode,\n actions,\n } = dashboard\n\n // Sync draftRowsRef with store state (for mouse event handlers)\n useEffect(() => {\n draftRowsRef.current = draftRows\n }, [draftRows])\n\n // Exit filter selection mode when leaving edit mode or when switching to non-desktop mode\n useEffect(() => {\n if ((!isEditMode || !isResponsiveEditable) && selectedFilterId) {\n actions.exitFilterSelectionMode()\n }\n }, [isEditMode, isResponsiveEditable, selectedFilterId, actions])\n\n // Exit edit mode when switching to non-desktop view\n useEffect(() => {\n if (!isResponsiveEditable && isEditMode) {\n actions.exitEditMode()\n }\n }, [isResponsiveEditable, isEditMode, actions])\n\n // Track scroll state for sticky header using debounced scroll detection\n // Uses the actual scroll container (found via findScrollableAncestor) instead of window\n const isScrolled = useScrollDetection(scrollContainerRef, {\n threshold: 20,\n debounceMs: 150,\n container: scrollContainer // State dependency to trigger re-init when container found\n })\n\n // Track edit bar visibility for floating toolbar\n // When edit bar scrolls out of view, show floating toolbar\n const isEditBarVisible = useElementVisibility(editBarRef, {\n threshold: 80,\n debounceMs: 100,\n containerRef: scrollContainerRef,\n container: scrollContainer // State dependency to trigger re-init when container found\n })\n\n // Auto-scroll when dragging portlets near edges in row mode\n // Grid mode (react-grid-layout) has built-in auto-scroll, row mode needs this\n useDragAutoScroll(scrollContainerRef, {\n enabled: layoutMode === 'rows' && isDraggingPortlet,\n edgeThreshold: 80,\n maxScrollSpeed: 15\n })\n\n // Set up initialization tracking\n useEffect(() => {\n // Mark as initialized after first render to prevent saves during load/resize\n const timer = setTimeout(() => {\n actions.setIsInitialized(true)\n // Store initial layout for comparison\n const initialLayout = config.portlets.map(portlet => ({\n i: portlet.id,\n x: portlet.x,\n y: portlet.y,\n w: portlet.w,\n h: portlet.h\n }))\n actions.setLastKnownLayout(initialLayout)\n }, 200) // Slightly longer delay to ensure responsive grid is fully settled\n\n return () => clearTimeout(timer)\n }, [config.portlets, actions])\n\n // Scroll detection now handled by useScrollDetection hook above (lines 373-378)\n // This eliminates the old window.addEventListener('scroll') listener that was\n // listening to the wrong element and causing performance issues\n\n // Set up ESC key listener for filter selection mode\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && selectedFilterId) {\n actions.exitFilterSelectionMode()\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n }\n }, [selectedFilterId, actions])\n\n const handleLayoutChange = useCallback((_layout: Layout) => {\n // This function is called for ALL layout changes\n // We should NOT save here - only update internal state if needed\n // Actual saving only happens in handleDragStop and handleResizeStop for explicit user actions\n\n // Note: We don't call onConfigChange here to prevent saving intermediate layout changes\n // The layout changes are handled by react-grid-layout internally\n }, []) // No dependencies since we're not doing anything\n\n // Handle drag stop - save when user finishes dragging (only if layout actually changed)\n const handleDragStop = useCallback(async (layout: Layout, _oldItem: LayoutItem | null, _newItem: LayoutItem | null, _placeholder: LayoutItem | null, _e: Event, _element: HTMLElement | undefined) => {\n if (!editable || !isEditMode || !onSave || !isInitialized) return\n\n // Only save if the layout actually changed from user interaction\n // Convert readonly Layout to mutable array for comparison\n const mutableLayout = [...layout]\n if (!actions.hasLayoutActuallyChanged(mutableLayout)) {\n return // No actual change, don't save\n }\n\n // Get the current updated config (only called in desktop mode)\n const updatedPortlets = config.portlets.map(portlet => {\n const layoutItem = mutableLayout.find(item => item.i === portlet.id)\n if (layoutItem) {\n return {\n ...portlet,\n x: layoutItem.x,\n y: layoutItem.y,\n w: layoutItem.w,\n h: layoutItem.h\n }\n }\n return portlet\n })\n\n // Preserve existing responsive layouts, only update with new lg layout\n const updatedConfig = {\n ...config,\n portlets: updatedPortlets,\n layouts: {\n ...config.layouts,\n lg: mutableLayout // Only save the large layout, let RGL handle responsive adjustments\n }\n }\n\n // Update our tracking of the last known layout\n actions.setLastKnownLayout(mutableLayout)\n\n // Update config state first\n onConfigChange?.(updatedConfig)\n\n // Auto-save after drag operation\n try {\n await onSave(updatedConfig)\n } catch (error) {\n console.error('Auto-save failed after drag:', error)\n }\n }, [config, editable, isEditMode, onConfigChange, onSave, isInitialized, actions])\n\n // Handle resize stop - update config and save (resize is user interaction)\n const handleResizeStop = useCallback(async (layout: Layout, _oldItem: LayoutItem | null, _newItem: LayoutItem | null, _placeholder: LayoutItem | null, _e: Event, _element: HTMLElement | undefined) => {\n if (!editable || !isEditMode || !onConfigChange || !isInitialized) return\n\n // Only proceed if the layout actually changed from user interaction\n // Convert readonly Layout to mutable array for comparison\n const mutableLayout = [...layout]\n if (!actions.hasLayoutActuallyChanged(mutableLayout)) {\n return // No actual change, don't save\n }\n\n // Get the current updated config (only called in desktop mode)\n const updatedPortlets = config.portlets.map(portlet => {\n const layoutItem = mutableLayout.find(item => item.i === portlet.id)\n if (layoutItem) {\n return {\n ...portlet,\n x: layoutItem.x,\n y: layoutItem.y,\n w: layoutItem.w,\n h: layoutItem.h\n }\n }\n return portlet\n })\n\n // Preserve existing responsive layouts, only update with new lg layout\n const updatedConfig = {\n ...config,\n portlets: updatedPortlets,\n layouts: {\n ...config.layouts,\n lg: mutableLayout // Only save the large layout, let RGL handle responsive adjustments\n }\n }\n\n // Update our tracking of the last known layout\n actions.setLastKnownLayout(mutableLayout)\n\n // Update config state\n onConfigChange(updatedConfig)\n\n // Auto-save after resize operation (user deliberately resized)\n if (onSave) {\n try {\n await onSave(updatedConfig)\n } catch (error) {\n console.error('Auto-save failed after resize:', error)\n }\n }\n }, [config, editable, isEditMode, onConfigChange, onSave, isInitialized, actions])\n\n // handleLayoutModeChange now uses actions.handleLayoutModeChange from hook\n\n const startRowResize = useCallback((rowIndex: number, event: MouseEvent<HTMLDivElement>) => {\n if (!canEdit) return\n event.preventDefault()\n\n const startY = event.clientY\n const startRows = resolvedRows.map(row => ({\n ...row,\n columns: row.columns.map(column => ({ ...column }))\n }))\n\n const handleMouseMove = (moveEvent: globalThis.MouseEvent) => {\n const delta = moveEvent.clientY - startY\n const deltaUnits = Math.round(delta / gridSettings.rowHeight)\n const nextRows = startRows.map((row, index) => {\n if (index !== rowIndex) return row\n return {\n ...row,\n h: Math.max(gridSettings.minH, row.h + deltaUnits)\n }\n })\n actions.setDraftRows(nextRows)\n }\n\n const handleMouseUp = () => {\n document.removeEventListener('mousemove', handleMouseMove)\n document.removeEventListener('mouseup', handleMouseUp)\n const finalRows = draftRowsRef.current ?? startRows\n actions.updateRowLayout(finalRows)\n }\n\n document.addEventListener('mousemove', handleMouseMove)\n document.addEventListener('mouseup', handleMouseUp)\n }, [canEdit, gridSettings, resolvedRows, actions])\n\n const startColumnResize = useCallback((rowIndex: number, columnIndex: number, event: MouseEvent<HTMLDivElement>) => {\n if (!canEdit) return\n event.preventDefault()\n\n const startX = event.clientX\n const startRows = resolvedRows.map(row => ({\n ...row,\n columns: row.columns.map(column => ({ ...column }))\n }))\n\n const row = startRows[rowIndex]\n const leftColumn = row?.columns[columnIndex]\n const rightColumn = row?.columns[columnIndex + 1]\n if (!row || !leftColumn || !rightColumn) return\n\n const columnGap = 16\n const rowContentWidth = gridWidth - (row.columns.length - 1) * columnGap\n const unitWidth = rowContentWidth / gridSettings.cols\n\n const handleMouseMove = (moveEvent: globalThis.MouseEvent) => {\n const delta = moveEvent.clientX - startX\n const deltaUnits = Math.round(delta / unitWidth)\n if (deltaUnits === 0) {\n actions.setDraftRows(startRows)\n return\n }\n\n let nextLeft = leftColumn.w + deltaUnits\n let nextRight = rightColumn.w - deltaUnits\n\n if (nextLeft < gridSettings.minW) {\n const diff = gridSettings.minW - nextLeft\n nextLeft = gridSettings.minW\n nextRight -= diff\n }\n\n if (nextRight < gridSettings.minW) {\n const diff = gridSettings.minW - nextRight\n nextRight = gridSettings.minW\n nextLeft -= diff\n }\n\n if (nextLeft < gridSettings.minW || nextRight < gridSettings.minW) return\n\n const nextRows = startRows.map((rowItem, index) => {\n if (index !== rowIndex) return rowItem\n const nextColumns = rowItem.columns.map((column, colIndex) => {\n if (colIndex === columnIndex) {\n return { ...column, w: nextLeft }\n }\n if (colIndex === columnIndex + 1) {\n return { ...column, w: nextRight }\n }\n return column\n })\n return {\n ...rowItem,\n columns: adjustRowWidths(nextColumns, gridSettings)\n }\n })\n actions.setDraftRows(nextRows)\n }\n\n const handleMouseUp = () => {\n document.removeEventListener('mousemove', handleMouseMove)\n document.removeEventListener('mouseup', handleMouseUp)\n const finalRows = draftRowsRef.current ?? startRows\n actions.updateRowLayout(finalRows)\n }\n\n document.addEventListener('mousemove', handleMouseMove)\n document.addEventListener('mouseup', handleMouseUp)\n }, [canEdit, gridSettings, gridWidth, resolvedRows, actions])\n\n const handlePortletDragStart = useCallback((rowIndex: number, colIndex: number, portletId: string, event: DragEvent<HTMLDivElement>) => {\n if (!canEdit) return\n dragStateRef.current = { rowIndex, colIndex, portletId }\n actions.setIsDraggingPortlet(true)\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', portletId)\n }, [canEdit, actions])\n\n const handlePortletDragEnd = useCallback(() => {\n dragStateRef.current = null\n actions.setIsDraggingPortlet(false)\n }, [actions])\n\n const handleRowDrop = useCallback((rowIndex: number, insertIndex: number | null) => {\n const dragState = dragStateRef.current\n if (!dragState) return\n\n const nextRows = resolvedRows.map(row => ({\n ...row,\n columns: row.columns.map(column => ({ ...column }))\n }))\n\n const sourceRowIndex = dragState.rowIndex\n const sourceRow = nextRows[sourceRowIndex]\n if (!sourceRow) return\n\n const [movedColumn] = sourceRow.columns.splice(dragState.colIndex, 1)\n let sourceRowRemoved = false\n if (sourceRow.columns.length === 0) {\n nextRows.splice(sourceRowIndex, 1)\n sourceRowRemoved = true\n }\n\n let targetRowIndex = rowIndex\n if (sourceRowRemoved && sourceRowIndex < rowIndex) {\n targetRowIndex -= 1\n }\n\n const targetRow = nextRows[targetRowIndex]\n if (!targetRow) return\n\n let targetIndex = insertIndex ?? targetRow.columns.length\n if (!sourceRowRemoved && sourceRowIndex === targetRowIndex && insertIndex !== null) {\n if (insertIndex > dragState.colIndex) {\n targetIndex -= 1\n }\n }\n targetRow.columns.splice(targetIndex, 0, movedColumn)\n\n const movedBetweenRows = sourceRowIndex !== targetRowIndex || sourceRowRemoved\n if (movedBetweenRows) {\n if (!sourceRowRemoved) {\n nextRows[sourceRowIndex] = {\n ...nextRows[sourceRowIndex],\n columns: equalizeRowColumns(\n nextRows[sourceRowIndex].columns.map(column => column.portletId),\n gridSettings\n )\n }\n }\n nextRows[targetRowIndex] = {\n ...nextRows[targetRowIndex],\n columns: equalizeRowColumns(\n nextRows[targetRowIndex].columns.map(column => column.portletId),\n gridSettings\n )\n }\n }\n\n actions.updateRowLayout(nextRows)\n }, [gridSettings, resolvedRows, actions])\n\n const handleNewRowDrop = useCallback((insertIndex: number) => {\n const dragState = dragStateRef.current\n if (!dragState) return\n\n const nextRows = resolvedRows.map(row => ({\n ...row,\n columns: row.columns.map(column => ({ ...column }))\n }))\n\n const sourceRow = nextRows[dragState.rowIndex]\n if (!sourceRow) return\n\n const [movedColumn] = sourceRow.columns.splice(dragState.colIndex, 1)\n if (sourceRow.columns.length === 0) {\n nextRows.splice(dragState.rowIndex, 1)\n } else {\n sourceRow.columns = equalizeRowColumns(\n sourceRow.columns.map(column => column.portletId),\n gridSettings\n )\n }\n\n const newRow: RowLayout = {\n id: createRowId(),\n h: Math.max(gridSettings.minH, 5),\n columns: equalizeRowColumns([movedColumn.portletId], gridSettings)\n }\n nextRows.splice(insertIndex, 0, newRow)\n\n actions.updateRowLayout(nextRows)\n }, [gridSettings, resolvedRows, actions])\n\n // Handle portlet refresh - use action from hook\n // Pass { bustCache: true } to bypass client and server caches (shift+click)\n const handlePortletRefresh = useCallback((portletId: string, options?: { bustCache?: boolean }) => {\n actions.refreshPortlet(portletId, options)\n }, [actions])\n\n // Portlet CRUD operations - now use actions from useDashboard hook\n // The hook handles all logic including row layout updates, saving, and modal state\n\n // Handle portlet save with scroll-to-new behavior\n const handlePortletSave = useCallback(async (portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>) => {\n const newPortletId = await actions.savePortlet(portletData)\n actions.closePortletModal()\n\n // Scroll to the new portlet after DOM update\n if (newPortletId) {\n setTimeout(() => {\n const scrollToPortlet = () => {\n let portletElement: HTMLElement | null = portletRefs.current[newPortletId]\n if (!portletElement) {\n portletElement = document.querySelector(`[data-portlet-id=\"${newPortletId}\"]`)\n }\n\n if (portletElement) {\n portletElement.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n inline: 'nearest'\n })\n return true\n }\n return false\n }\n\n // Try multiple times with increasing delays\n if (!scrollToPortlet()) {\n setTimeout(() => {\n if (!scrollToPortlet()) {\n setTimeout(() => {\n scrollToPortlet()\n }, 300)\n }\n }, 200)\n }\n }, 200)\n }\n }, [actions])\n\n // Handle deleting portlet - delegate to hook action\n const handleDeletePortlet = useCallback(async (portletId: string) => {\n await actions.deletePortlet(portletId)\n }, [actions])\n\n // Handle duplicating portlet - delegate to hook action with scroll to new portlet\n const handleDuplicatePortlet = useCallback(async (portletId: string) => {\n const newPortletId = await actions.duplicatePortlet(portletId)\n\n // Scroll to the duplicated portlet after DOM update\n if (newPortletId) {\n setTimeout(() => {\n const scrollToPortlet = () => {\n let portletElement: HTMLElement | null = portletRefs.current[newPortletId]\n if (!portletElement) {\n portletElement = document.querySelector(`[data-portlet-id=\"${newPortletId}\"]`)\n }\n\n if (portletElement) {\n portletElement.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n inline: 'nearest'\n })\n return true\n }\n return false\n }\n\n // Try multiple times with increasing delays\n if (!scrollToPortlet()) {\n setTimeout(() => {\n if (!scrollToPortlet()) {\n setTimeout(() => {\n scrollToPortlet()\n }, 300)\n }\n }, 200)\n }\n }, 200)\n }\n }, [actions])\n\n // Handle adding new portlet - delegate to hook action\n const handleAddPortlet = useCallback(() => {\n actions.openAddPortlet()\n }, [actions])\n\n // Handle editing existing portlet - delegate to hook action\n const handleEditPortlet = useCallback((portlet: PortletConfig) => {\n actions.openEditPortlet(portlet)\n }, [actions])\n\n // Handle palette change - delegate to hook action\n const handlePaletteChange = useCallback(async (paletteName: string) => {\n await actions.handlePaletteChange(paletteName)\n }, [actions])\n\n // Handle opening filter config modal - delegate to hook action\n const handleOpenFilterConfig = useCallback((portlet: PortletConfig) => {\n actions.openFilterConfig(portlet)\n }, [actions])\n\n // Handle saving filter configuration - delegate to hook action\n const handleSaveFilterConfig = useCallback(async (mapping: string[]) => {\n await actions.saveFilterConfig(mapping)\n }, [actions])\n\n // Memoized ref callbacks for DashboardPortletCard\n const handleSetPortletRef = useCallback((portletId: string, element: HTMLDivElement | null) => {\n portletRefs.current[portletId] = element\n }, [])\n\n const handleSetPortletComponentRef = useCallback((portletId: string, element: { refresh: () => void } | null) => {\n portletComponentRefs.current[portletId] = element\n }, [])\n\n // Memoized icons object to prevent re-renders\n const portletIcons = useMemo(() => ({\n RefreshIcon, EditIcon, DeleteIcon, CopyIcon, FilterIcon\n }), [])\n\n // Handle toggling filter for a portlet - delegate to hook action\n const handleToggleFilterForPortlet = useCallback(async (portletId: string, filterId: string) => {\n await actions.toggleFilterForPortlet(portletId, filterId)\n }, [actions])\n\n // Handle filter selection (click on filter chip) - delegate to hook action\n const handleFilterSelect = useCallback((filterId: string) => {\n actions.selectFilter(filterId)\n }, [actions])\n\n // Handle select all - delegate to hook action\n const handleSelectAllForFilter = useCallback(async (filterId: string) => {\n await actions.selectAllForFilter(filterId)\n }, [actions])\n\n // selectedFilter is now computed by the hook\n\n // Memoized callbacks object for DashboardPortletCard\n // This groups action callbacks into a stable object to reduce prop drilling\n const portletCallbacks = useMemo(() => ({\n onToggleFilter: handleToggleFilterForPortlet,\n onRefresh: handlePortletRefresh,\n onDuplicate: handleDuplicatePortlet,\n onEdit: handleEditPortlet,\n onDelete: handleDeletePortlet,\n onOpenFilterConfig: handleOpenFilterConfig,\n }), [\n handleToggleFilterForPortlet,\n handlePortletRefresh,\n handleDuplicatePortlet,\n handleEditPortlet,\n handleDeletePortlet,\n handleOpenFilterConfig,\n ])\n\n // Memoize renderPortletCard to prevent unnecessary re-renders of DashboardPortletCard\n // IMPORTANT: Must be defined before any early returns to follow Rules of Hooks\n // State props (isEditMode, selectedFilterId, debugData) now read from store in component\n const renderPortletCard = useCallback((\n portlet: PortletConfig,\n containerProps?: HTMLAttributes<HTMLDivElement>,\n headerProps?: HTMLAttributes<HTMLDivElement>\n ) => (\n <DashboardPortletCard\n portlet={portlet}\n editable={editable}\n dashboardFilters={dashboardFilters}\n configEagerLoad={config.eagerLoad}\n loadingComponent={loadingComponent}\n colorPalette={colorPalette}\n containerProps={containerProps}\n headerProps={headerProps}\n callbacks={portletCallbacks}\n setPortletRef={handleSetPortletRef}\n setPortletComponentRef={handleSetPortletComponentRef}\n icons={portletIcons}\n />\n ), [\n editable,\n dashboardFilters,\n config.eagerLoad,\n loadingComponent,\n colorPalette,\n portletCallbacks,\n handleSetPortletRef,\n handleSetPortletComponentRef,\n portletIcons\n ])\n\n if (!config.portlets || config.portlets.length === 0) {\n return (\n <>\n <div className=\"dc:flex dc:justify-center dc:items-center dc:min-h-[50vh]\">\n <div className=\"text-center\">\n <ChartBarIcon style={{ width: '64px', height: '64px', color: 'var(--dc-text-muted)', margin: '0 auto 16px auto' }} />\n <h3 className=\"dc:text-lg dc:font-semibold dc:mb-2 text-dc-text\">No Portlets</h3>\n <p className=\"dc:text-sm text-dc-text-secondary dc:mb-4\">Add your first portlet to start visualizing your data</p>\n {editable && (\n <button\n onClick={handleAddPortlet}\n className=\"dc:inline-flex dc:items-center dc:px-4 dc:py-2 dc:border border-dc-border bg-dc-surface dc:rounded-md focus:outline-hidden dc:focus:ring-2\"\n style={{\n color: 'var(--dc-primary)',\n borderColor: 'var(--dc-primary)'\n }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-surface)'}\n >\n <AddIcon className=\"dc:w-5 dc:h-5 dc:mr-2\" />\n Add Portlet\n </button>\n )}\n </div>\n </div>\n \n {/* Portlet Modal */}\n <PortletAnalysisModal\n isOpen={isPortletModalOpen}\n onClose={actions.closePortletModal}\n onSave={handlePortletSave}\n portlet={editingPortlet}\n title={editingPortlet ? 'Edit Portlet' : 'Add New Portlet'}\n submitText={editingPortlet ? 'Update Portlet' : 'Add Portlet'}\n colorPalette={colorPalette}\n dashboardFilters={dashboardFilters}\n />\n </>\n )\n }\n\n // Generate grid layout from portlets - only use for lg (desktop) breakpoint\n // Let react-grid-layout handle responsive adjustments automatically\n const baseLayout: LayoutItem[] = config.portlets.map(portlet => ({\n i: portlet.id,\n x: portlet.x,\n y: portlet.y,\n w: portlet.w,\n h: portlet.h,\n minW: gridSettings.minW,\n minH: gridSettings.minH, // 2 rows at 80px = 160px minimum\n // Only enable drag/resize in edit mode\n isDraggable: canEdit,\n isResizable: canEdit,\n ...(canEdit ? { resizeHandles: ['s', 'w', 'e', 'n', 'se', 'sw', 'ne', 'nw'] as const } : {})\n }))\n\n // Render the portlet grid content (shared between desktop and scaled modes)\n const renderGridContent = () => (\n <ReactGridLayout\n className=\"layout\"\n layout={baseLayout}\n onLayoutChange={handleLayoutChange}\n onDragStop={handleDragStop}\n onResizeStop={handleResizeStop}\n width={gridWidth}\n gridConfig={{\n cols: gridSettings.cols,\n rowHeight: gridSettings.rowHeight,\n margin: [16, 16],\n containerPadding: [0, 0]\n }}\n dragConfig={{\n enabled: canEdit,\n handle: '.portlet-drag-handle'\n }}\n resizeConfig={{\n enabled: canEdit,\n handles: ['s', 'w', 'e', 'n', 'se', 'sw', 'ne', 'nw'],\n // Invisible but functional resize handles\n handleComponent: (axis, ref) => (\n <div\n ref={ref as Ref<HTMLDivElement>}\n className={`react-resizable-handle react-resizable-handle-${axis}`}\n style={{ opacity: 0 }}\n />\n )\n }}\n compactor={verticalCompactor}\n >\n {config.portlets\n .filter(portlet => portlet && portlet.id) // Guard against malformed portlets\n .map(portlet => (\n <div key={portlet.id}>\n {renderPortletCard(portlet)}\n </div>\n ))}\n </ReactGridLayout>\n )\n\n const renderRowContent = () => (\n <RowManagedLayout\n rows={resolvedRows}\n portlets={config.portlets}\n gridSettings={gridSettings}\n gridWidth={gridWidth}\n canEdit={canEdit}\n isDragging={isDraggingPortlet}\n onRowResize={startRowResize}\n onColumnResize={startColumnResize}\n onPortletDragStart={handlePortletDragStart}\n onPortletDragEnd={handlePortletDragEnd}\n onRowDrop={handleRowDrop}\n onNewRowDrop={handleNewRowDrop}\n renderPortlet={renderPortletCard}\n />\n )\n\n const renderActiveLayout = layoutMode === 'rows' ? renderRowContent() : renderGridContent()\n const editModeHint = 'Drag • Resize • Auto-save'\n\n return (\n <ScrollContainerProvider value={scrollContainer}>\n <div ref={combinedContainerRef} className=\"dashboard-grid-container dc:w-full\" style={{ maxWidth: '100%', overflow: 'hidden' }}>\n {editable && features.editToolbar !== 'floating' && (\n <div\n ref={editBarRef}\n className={`dc:mb-4 dc:flex dc:justify-between dc:items-center dc:sticky dc:top-0 dc:z-10 dc:px-4 dc:py-4 bg-dc-surface-tertiary dc:border border-dc-border dc:rounded-lg dc:transition-all dc:duration-200 ${\n isScrolled ? 'dc:border-b' : ''\n }`}\n style={{\n boxShadow: isScrolled ? 'var(--dc-shadow-md)' : 'var(--dc-shadow-sm)'\n }}\n >\n <div className=\"dc:flex dc:items-center dc:gap-4\">\n <button\n onClick={() => isResponsiveEditable && actions.toggleEditMode()}\n disabled={!isResponsiveEditable}\n className={`dc:inline-flex dc:items-center dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md dc:transition-colors focus:outline-hidden dc:focus:ring-2 dc:focus:ring-offset-2 ${\n !isResponsiveEditable\n ? 'dc:opacity-50 dc:cursor-not-allowed bg-dc-surface-secondary dc:border border-dc-border'\n : isEditMode\n ? 'bg-dc-surface-secondary dc:border border-dc-border hover:bg-dc-surface-hover'\n : 'bg-dc-surface dc:border border-dc-border hover:bg-dc-surface-hover'\n }`}\n style={{\n color: !isResponsiveEditable ? 'var(--dc-text-muted)' : 'var(--dc-primary)',\n borderColor: !isResponsiveEditable ? 'var(--dc-border)' : isEditMode ? 'var(--dc-border)' : 'var(--dc-primary)'\n }}\n >\n {isEditMode ? <CheckIcon className=\"dc:w-4 dc:h-4 dc:mr-1.5\" /> : <EditIcon className=\"dc:w-4 dc:h-4 dc:mr-1.5\" />}\n {isEditMode ? 'Finish Editing' : 'Edit'}\n </button>\n {isEditMode && allowedModes.length > 1 && (\n <div className=\"dc:inline-flex dc:rounded-md dc:border border-dc-border dc:overflow-hidden dc:whitespace-nowrap\">\n <button\n onClick={() => actions.handleLayoutModeChange('grid')}\n disabled={!canChangeLayoutMode}\n className={`dc:inline-flex dc:items-center dc:gap-2 dc:whitespace-nowrap dc:px-3 dc:py-1.5 dc:text-sm dc:font-medium dc:transition-colors dc:border-b-2 ${\n layoutMode === 'grid'\n ? 'bg-dc-accent-bg text-dc-accent border-b-dc-accent'\n : 'bg-dc-surface text-dc-text-secondary hover:bg-dc-surface-hover border-b-transparent'\n } ${!canChangeLayoutMode ? 'dc:cursor-not-allowed dc:opacity-50' : ''}`}\n >\n <GridIcon className=\"dc:w-4 dc:h-4 dc:shrink-0\" />\n Grid\n </button>\n <button\n onClick={() => actions.handleLayoutModeChange('rows')}\n disabled={!canChangeLayoutMode}\n className={`dc:inline-flex dc:items-center dc:gap-2 dc:whitespace-nowrap dc:px-3 dc:py-1.5 dc:text-sm dc:font-medium dc:transition-colors dc:border-b-2 ${\n layoutMode === 'rows'\n ? 'bg-dc-accent-bg text-dc-accent border-b-dc-accent'\n : 'bg-dc-surface text-dc-text-secondary hover:bg-dc-surface-hover border-b-transparent'\n } ${!canChangeLayoutMode ? 'dc:cursor-not-allowed dc:opacity-50' : ''}`}\n >\n <RowsIcon className=\"dc:w-4 dc:h-4 dc:shrink-0\" />\n Rows\n </button>\n </div>\n )}\n {!isResponsiveEditable && (\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:text-sm text-dc-text-secondary\">\n <DesktopIcon className=\"dc:w-4 dc:h-4\" />\n <span>Desktop view required for editing</span>\n </div>\n )}\n {isEditMode && isResponsiveEditable && (\n <p className=\"dc:hidden dc:md:block dc:text-sm text-dc-text-secondary\">\n {editModeHint}\n </p>\n )}\n </div>\n\n {/* Color Palette Selector and Add Portlet - Only show in edit mode */}\n {isEditMode && (\n <div className=\"dc:flex dc:items-center dc:gap-3\">\n <ColorPaletteSelector\n currentPalette={config.colorPalette}\n onPaletteChange={handlePaletteChange}\n className=\"dc:shrink-0\"\n />\n\n <button\n onClick={handleAddPortlet}\n className=\"dc:inline-flex dc:items-center dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:border dc:rounded-md focus:outline-hidden dc:focus:ring-2 dc:focus:ring-offset-2 border-dc-border bg-dc-surface hover:bg-dc-surface-hover\"\n style={{\n color: 'var(--dc-primary)',\n borderColor: 'var(--dc-primary)'\n }}\n >\n <AddIcon className=\"dc:w-5 dc:h-5 dc:mr-2\" />\n Add Portlet\n </button>\n </div>\n )}\n </div>\n )}\n\n {/* Floating Edit Toolbar - appears when top edit bar scrolls out of view (or always if editToolbar='floating') */}\n {editable && features.editToolbar !== 'top' && displayMode === 'desktop' && (\n <FloatingEditToolbar\n isEditBarVisible={features.editToolbar === 'floating' ? false : isEditBarVisible}\n position={features.floatingToolbarPosition || 'right'}\n isEditMode={isEditMode}\n onEditModeToggle={() => isResponsiveEditable && actions.toggleEditMode()}\n layoutMode={layoutMode}\n onLayoutModeChange={actions.handleLayoutModeChange}\n allowedModes={allowedModes}\n canChangeLayoutMode={canChangeLayoutMode}\n currentPalette={config.colorPalette || 'default'}\n onPaletteChange={actions.handlePaletteChange}\n onAddPortlet={actions.openAddPortlet}\n />\n )}\n\n {/* Dashboard Filter Panel - Always visible below toolbar */}\n <DashboardFilterPanel\n dashboardFilters={dashboardFilters || []}\n editable={editable}\n schema={schema || null}\n dashboardConfig={config}\n onDashboardFiltersChange={onDashboardFiltersChange || (() => {})}\n onSaveFilters={onSave ? async (filters: DashboardFilter[]) => {\n const updatedConfig = {\n ...config,\n filters\n }\n await onSave(updatedConfig)\n } : undefined}\n selectedFilterId={selectedFilterId}\n onFilterSelect={handleFilterSelect}\n isEditMode={isEditMode}\n />\n\n {/* Filter Selection Mode Banner */}\n {selectedFilterId && selectedFilter && (\n <div\n className=\"dc:mb-4 dc:px-4 dc:py-3 dc:rounded-md dc:border-2 dc:transition-all\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n borderColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n <div className=\"dc:flex dc:items-center dc:justify-between dc:flex-wrap dc:gap-2\">\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:flex-wrap\">\n <FilterIcon className=\"dc:w-5 dc:h-5 dc:shrink-0\" />\n <span className=\"dc:font-medium\">\n Filter Selection Mode - Click portlets to toggle '{selectedFilter.label}'\n </span>\n <span className=\"dc:text-sm dc:opacity-90 dc:hidden dc:sm:inline\">• Press ESC to exit</span>\n </div>\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <button\n onClick={() => handleSelectAllForFilter(selectedFilterId)}\n className=\"dc:px-3 dc:py-1 dc:rounded-md dc:transition-colors dc:text-sm dc:font-medium\"\n style={{\n backgroundColor: 'rgba(255, 255, 255, 0.2)',\n color: 'white'\n }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.3)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.2)'}\n >\n Select All\n </button>\n <button\n onClick={() => actions.exitFilterSelectionMode()}\n className=\"dc:px-3 dc:py-1 dc:rounded-md dc:transition-colors dc:text-sm dc:font-medium\"\n style={{\n backgroundColor: 'rgba(255, 255, 255, 0.2)',\n color: 'white'\n }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.3)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.2)'}\n >\n Exit\n </button>\n </div>\n </div>\n </div>\n )}\n \n {/* Render layout based on display mode */}\n {/* Grid content ref wrapper for thumbnail capture (excludes toolbar/filters) */}\n <div ref={gridContentRef}>\n {displayMode === 'mobile' ? (\n <MobileStackedLayout\n config={config}\n colorPalette={colorPalette}\n dashboardFilters={dashboardFilters}\n onPortletRefresh={handlePortletRefresh}\n />\n ) : displayMode === 'scaled' ? (\n <ScaledGridWrapper scaleFactor={scaleFactor} designWidth={designWidth}>\n {renderActiveLayout}\n </ScaledGridWrapper>\n ) : (\n renderActiveLayout\n )}\n </div>\n \n {/* Portlet Modal */}\n <PortletAnalysisModal\n isOpen={isPortletModalOpen}\n onClose={actions.closePortletModal}\n onSave={handlePortletSave}\n portlet={editingPortlet}\n title={editingPortlet ? 'Edit Portlet' : 'Add New Portlet'}\n submitText={editingPortlet ? 'Update Portlet' : 'Add Portlet'}\n colorPalette={colorPalette}\n dashboardFilters={dashboardFilters}\n />\n\n {/* Filter Configuration Modal */}\n <PortletFilterConfigModal\n isOpen={isFilterConfigModalOpen}\n onClose={actions.closeFilterConfig}\n dashboardFilters={dashboardFilters || []}\n currentMapping={filterConfigPortlet?.dashboardFilterMapping || []}\n onSave={handleSaveFilterConfig}\n portletTitle={filterConfigPortlet?.title || ''}\n />\n\n {/* Delete Confirmation Modal */}\n <ConfirmModal\n isOpen={!!deleteConfirmPortletId}\n onClose={actions.closeDeleteConfirm}\n onConfirm={actions.confirmDelete}\n title=\"Delete Portlet\"\n message={\n <>\n Are you sure you want to delete{' '}\n <strong>\n {config.portlets.find(p => p.id === deleteConfirmPortletId)?.title || 'this portlet'}\n </strong>\n ? This action cannot be undone.\n </>\n }\n confirmText=\"Delete\"\n confirmVariant=\"danger\"\n />\n </div>\n </ScrollContainerProvider>\n )\n}\n","/**\n * Analytics Dashboard Component\n * Main dashboard container that uses CubeProvider context\n * Minimal dependencies, designed to be embedded in existing apps\n */\n\nimport { useCallback, useMemo } from 'react'\nimport DashboardGrid from './DashboardGrid'\nimport { useCubeFeatures, useCubeMeta } from '../providers/CubeProvider'\nimport { DashboardStoreProvider } from '../stores/dashboardStore'\nimport { getColorPalette } from '../utils/colorPalettes'\nimport { useDirtyStateTracking } from '../hooks/useDirtyStateTracking'\nimport type { AnalyticsDashboardProps, DashboardConfig, DashboardFilter } from '../types'\n\nexport default function AnalyticsDashboard({\n config,\n editable = false,\n dashboardFilters: propDashboardFilters,\n loadingComponent,\n onConfigChange,\n onSave,\n onSaveThumbnail,\n onDirtyStateChange\n}: AnalyticsDashboardProps) {\n // Get cube metadata for filter building\n const { meta } = useCubeMeta()\n const { dashboardModes } = useCubeFeatures()\n\n // Track dirty state and wrap save/change handlers\n const {\n handleConfigChange: handleConfigChangeWithDirtyTracking,\n handleSave: handleSaveWithDirtyTracking,\n } = useDirtyStateTracking<DashboardConfig>({\n initialConfig: config,\n onConfigChange,\n onSave,\n onDirtyStateChange,\n })\n\n // Merge programmatic filters (props) with config filters by ID\n // Config provides structure/metadata, props provide value overrides\n const mergedDashboardFilters = useMemo<DashboardFilter[]>(() => {\n const configFilters = config.filters || []\n const propFilters = propDashboardFilters || []\n\n // If no prop filters, use config filters as-is\n if (propFilters.length === 0) {\n return configFilters\n }\n\n // If no config filters, use prop filters as-is\n if (configFilters.length === 0) {\n return propFilters\n }\n\n // Merge by ID: config filters provide structure, prop filters provide values\n const mergedFilters: DashboardFilter[] = configFilters.map(configFilter => {\n // Find matching prop filter by ID\n const propFilter = propFilters.find(pf => pf.id === configFilter.id)\n\n if (propFilter) {\n // Merge: preserve config metadata, use prop filter values\n return {\n ...configFilter, // Preserve id, label, isUniversalTime from config\n filter: propFilter.filter // Use filter values from prop override\n }\n }\n\n // No override for this filter, use config as-is\n return configFilter\n })\n\n // Add any prop filters that don't exist in config (new filters)\n const configIds = new Set(configFilters.map(cf => cf.id))\n const newFilters = propFilters.filter(pf => !configIds.has(pf.id))\n\n return [...mergedFilters, ...newFilters]\n }, [config.filters, propDashboardFilters])\n\n // Handle dashboard filter changes\n const handleDashboardFiltersChange = useCallback((filters: DashboardFilter[]) => {\n // Only update config if we're not using programmatic filters\n if (!propDashboardFilters || propDashboardFilters.length === 0) {\n const updatedConfig = {\n ...config,\n filters\n }\n handleConfigChangeWithDirtyTracking(updatedConfig)\n } else {\n console.warn('Dashboard filters are controlled via props - config changes ignored')\n }\n }, [config, propDashboardFilters, handleConfigChangeWithDirtyTracking])\n\n // Resolve complete palette object based on config\n const colorPalette = useMemo(() => {\n const paletteName = config.colorPalette\n return getColorPalette(paletteName)\n }, [config.colorPalette])\n\n return (\n <DashboardStoreProvider>\n <div className=\"dc:w-full\">\n {/* Dashboard Grid (now includes filter panel) */}\n <DashboardGrid\n config={config}\n editable={editable}\n dashboardFilters={mergedDashboardFilters}\n loadingComponent={loadingComponent}\n onConfigChange={handleConfigChangeWithDirtyTracking}\n onSave={handleSaveWithDirtyTracking}\n onSaveThumbnail={onSaveThumbnail}\n colorPalette={colorPalette}\n schema={meta}\n dashboardModes={dashboardModes}\n onDashboardFiltersChange={handleDashboardFiltersChange}\n />\n </div>\n </DashboardStoreProvider>\n )\n}\n","/**\n * Portlet Container Component\n * Simple wrapper for individual portlets\n */\n\nimport { useState, useCallback, useMemo } from 'react'\nimport AnalyticsPortlet from './AnalyticsPortlet'\nimport DebugModal from './DebugModal'\nimport type { PortletConfig } from '../types'\nimport type { FlowChartData } from '../types/flow'\nimport type { RetentionChartData } from '../types/retention'\nimport { ensureAnalysisConfig } from '../utils/configMigration'\n\ninterface PortletContainerProps {\n portlet: PortletConfig\n editable?: boolean\n onEdit?: (portlet: PortletConfig) => void\n onDelete?: (portletId: string) => void\n onRefresh?: (portletId: string) => void\n}\n\nexport default function PortletContainer({ \n portlet, \n editable = false,\n onEdit,\n onDelete,\n onRefresh\n}: PortletContainerProps) {\n // Normalize portlet to ensure analysisConfig exists (on-the-fly migration)\n const normalizedPortlet = useMemo(() => ensureAnalysisConfig(portlet), [portlet])\n const { analysisConfig } = normalizedPortlet\n\n // Extract rendering props from analysisConfig\n const chartModeConfig = analysisConfig.charts[analysisConfig.analysisType]\n const renderQuery = useMemo(() => JSON.stringify(analysisConfig.query), [analysisConfig.query])\n const renderChartType = chartModeConfig?.chartType || 'line'\n const renderChartConfig = chartModeConfig?.chartConfig\n const renderDisplayConfig = chartModeConfig?.displayConfig\n\n const [debugData, setDebugData] = useState<{\n chartConfig: any\n displayConfig: any\n queryObject: any\n data: any[] | FlowChartData | RetentionChartData\n chartType: string\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n } | null>(null)\n\n // Memoize debug data callback to prevent AnalyticsPortlet re-renders\n const handleDebugDataReady = useCallback((data: {\n chartConfig: any\n displayConfig: any\n queryObject: any\n data: any[] | FlowChartData | RetentionChartData\n chartType: string\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n }) => {\n setDebugData(data)\n }, [])\n\n return (\n <div className=\"bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:flex dc:flex-col dc:h-full\" style={{ boxShadow: 'var(--dc-shadow-sm)' }}>\n {/* Header */}\n <div className=\"dc:flex dc:items-center dc:justify-between dc:border-b border-dc-border dc:shrink-0 bg-dc-surface-secondary dc:rounded-t-lg dc:px-3 dc:py-2 dc:md:px-6 dc:md:py-3\">\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:flex-1 dc:min-w-0\">\n <h3 className=\"dc:font-semibold dc:text-sm dc:truncate text-dc-text\">{portlet.title}</h3>\n {/* Debug button - right next to title */}\n {debugData && (\n <DebugModal\n chartConfig={debugData.chartConfig}\n displayConfig={debugData.displayConfig}\n queryObject={debugData.queryObject}\n data={debugData.data}\n chartType={debugData.chartType}\n cacheInfo={debugData.cacheInfo}\n />\n )}\n </div>\n\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:ml-4\">\n\n {editable && (\n <>\n <button\n onClick={() => onRefresh?.(portlet.id)}\n className=\"dc:p-1.5 hover:bg-dc-surface-hover dc:rounded-sm text-dc-text-secondary\"\n title=\"Refresh\"\n >\n <svg className=\"dc:h-4 dc:w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n </button>\n <button\n onClick={() => onEdit?.(portlet)}\n className=\"dc:p-1.5 hover:bg-dc-surface-hover dc:rounded-sm text-dc-text-secondary\"\n title=\"Edit\"\n >\n <svg className=\"dc:h-4 dc:w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\" />\n </svg>\n </button>\n <button\n onClick={() => onDelete?.(portlet.id)}\n className=\"dc:p-1.5 dc:rounded-sm text-dc-danger\"\n style={{ backgroundColor: 'transparent' }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-danger-bg)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'transparent'}\n title=\"Delete\"\n >\n <svg className=\"dc:h-4 dc:w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\" />\n </svg>\n </button>\n </>\n )}\n </div>\n </div>\n\n {/* Content */}\n <div className=\"dc:px-2 dc:py-3 dc:md:px-4 dc:md:pt-6 dc:md:pb-4 dc:flex-1 dc:min-h-0\">\n <AnalyticsPortlet\n query={renderQuery}\n chartType={renderChartType}\n chartConfig={renderChartConfig}\n displayConfig={renderDisplayConfig}\n title={portlet.title}\n height=\"100%\"\n onDebugDataReady={handleDebugDataReady}\n />\n </div>\n </div>\n )\n}","import React, { useState, useEffect } from 'react'\nimport Modal from './Modal'\n\ninterface DashboardEditModalProps {\n isOpen: boolean\n onClose: () => void\n onSave: (data: { name: string; description?: string }) => Promise<void> | void\n title: string\n submitText: string\n initialName?: string\n initialDescription?: string\n}\n\nexport default function DashboardEditModal({\n isOpen,\n onClose,\n onSave,\n title,\n submitText,\n initialName = '',\n initialDescription = ''\n}: DashboardEditModalProps) {\n const [name, setName] = useState('')\n const [description, setDescription] = useState('')\n const [isSaving, setIsSaving] = useState(false)\n\n // Initialize form values when modal opens\n useEffect(() => {\n if (isOpen) {\n setName(initialName)\n setDescription(initialDescription)\n }\n }, [isOpen, initialName, initialDescription])\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n \n if (!name.trim()) {\n return\n }\n\n setIsSaving(true)\n \n try {\n await onSave({\n name: name.trim(),\n description: description.trim() || undefined\n })\n handleClose()\n } catch {\n // Failed to save dashboard\n // Don't close modal on error so user can retry\n } finally {\n setIsSaving(false)\n }\n }\n\n const handleClose = () => {\n setName('')\n setDescription('')\n setIsSaving(false)\n onClose()\n }\n\n const footer = (\n <>\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={isSaving}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-text-secondary bg-dc-surface dc:border border-dc-border dc:rounded-md hover:bg-dc-surface-hover dc:disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n form=\"dashboard-form\"\n disabled={isSaving || !name.trim()}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-white bg-dc-primary dc:border border-transparent dc:rounded-md hover:bg-dc-primary-hover dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\"\n >\n {isSaving ? 'Saving...' : submitText}\n </button>\n </>\n )\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={handleClose}\n title={title}\n size=\"fullscreen-mobile\"\n footer={footer}\n >\n <form id=\"dashboard-form\" onSubmit={handleSubmit} className=\"dc:space-y-4 dc:w-full\">\n <div>\n <label htmlFor=\"dashboard-name\" className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-1\">\n Dashboard Name\n </label>\n <input\n type=\"text\"\n id=\"dashboard-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:border border-dc-border dc:rounded-md bg-dc-surface text-dc-text dc:focus:outline-none dc:focus:ring-2 focus:ring-dc-primary focus:border-dc-primary\"\n placeholder=\"Enter dashboard name...\"\n required\n autoFocus\n />\n </div>\n\n <div>\n <label htmlFor=\"dashboard-description\" className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-1\">\n Description (optional)\n </label>\n <textarea\n id=\"dashboard-description\"\n rows={3}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:border border-dc-border dc:rounded-md bg-dc-surface text-dc-text dc:focus:outline-none dc:focus:ring-2 focus:ring-dc-primary focus:border-dc-primary\"\n placeholder=\"Enter description...\"\n />\n </div>\n </form>\n </Modal>\n )\n}","/**\n * QueryBuilder Shim\n *\n * Backward compatibility wrapper that provides the QueryBuilder interface\n * while using AnalysisBuilder internally.\n *\n * This allows existing code importing QueryBuilder to seamlessly use the new\n * AnalysisBuilder component without any code changes.\n */\nimport { forwardRef, useImperativeHandle, useRef } from 'react'\nimport AnalysisBuilder from './AnalysisBuilderLazy'\nimport type { AnalysisBuilderRef } from './AnalysisBuilder/types'\nimport type { QueryBuilderProps, QueryBuilderRef } from './QueryBuilder/types'\n\nconst QueryBuilderShim = forwardRef<QueryBuilderRef, QueryBuilderProps>(\n (props, ref) => {\n const analysisBuilderRef = useRef<AnalysisBuilderRef>(null)\n\n // Map QueryBuilder ref interface to AnalysisBuilder ref\n useImperativeHandle(ref, () => ({\n getCurrentQuery: () => {\n const config = analysisBuilderRef.current?.getQueryConfig()\n if (!config) {\n return { measures: [], dimensions: [] }\n }\n // If funnel query, return empty query for backward compatibility\n // (QueryBuilder interface doesn't support funnel mode)\n if ('funnel' in config) {\n return { measures: [], dimensions: [] }\n }\n // If multi-query, return first query for backward compatibility\n if ('queries' in config) {\n return config.queries[0] || { measures: [], dimensions: [] }\n }\n return config\n },\n\n getValidationState: () => {\n // AnalysisBuilder doesn't expose validation state\n // Return a basic \"valid\" state since AnalysisBuilder auto-validates\n return {\n status: 'valid' as const\n }\n },\n\n getValidationResult: () => {\n // AnalysisBuilder doesn't expose validation results\n // Return null as it's an optional field\n return null\n }\n }), [])\n\n // Map QueryBuilder props to AnalysisBuilder props\n return (\n <AnalysisBuilder\n ref={analysisBuilderRef}\n className={props.className}\n initialQuery={props.initialQuery}\n disableLocalStorage={props.disableLocalStorage}\n hideSettings={props.hideSettings}\n // Note: enableSharing and onShare props are not supported by AnalysisBuilder\n // The AnalysisBuilder has its own sharing mechanism\n />\n )\n }\n)\n\nQueryBuilderShim.displayName = 'QueryBuilder'\n\nexport default QueryBuilderShim\n"],"names":["DEFAULT_DEBOUNCE_MS","isValidFunnelConfig","config","step","query","useFunnelQuery","options","skip","debounceMs","onComplete","onError","prebuiltServerQuery","cubeApi","useCubeApi","queryClient","useQueryClient","features","useCubeFeatures","manualRefresh","executedQueryKey","setExecutedQueryKey","useState","isValidConfig","debouncedConfig","isDebouncing","useDebounceQuery","serverQuery","useMemo","buildServerFunnelQuery","s","error","queryKey","currentQueryKey","stableStringify","needsRefresh","shouldExecute","useEffect","queryResult","useQuery","startTime","resultSet","rawData","executionTime","cacheInfo","err","stepNames","expectedStepCount","chartData","transformServerFunnelResult","stepResults","firstCount","data","index","result","lastCount","fullResult","i","status","execute","useCallback","cancel","reset","createFunnelQueryKey","isValidFlowQuery","flow","transformFlowResult","row","firstItem","isSankeyData","useFlowQuery","isValid","debouncedQuery","queryKeyString","rawQueryKeyString","isDataStale","transformed","refetch","createFlowQueryKey","isValidRetentionQuery","extractBindingKeyLabel","bindingKey","firstMapping","extractBreakdownValue","breakdownValues","values","transformRetentionResult","granularity","bindingKeyLabel","rows","r","periodsSet","breakdownSet","periods","a","b","summary","calculateSummary","period1Rates","sum","useRetentionQuery","getFieldLabel","isValidQuery","rawBindingKeyField","label","executeOptions","RefreshIcon","getIcon","ChartErrorBoundary","Component","props","errorInfo","jsxs","jsx","isMultiQueryConfig","obj","isServerFunnelQuery","isValidAnalysisConfig","c","createDefaultQueryConfig","isValidAnalysisWorkspace","w","isLegacyFunnelMultiQuery","isServerFlowQuery","extractChartConfig","portlet","analysisType","migrateLegacyPortlet","isServerRetentionQuery","migrateLegacyFunnelMerge","chartConfig","strategy","legacyQuery","m","timeDimension","steps","hasAnalysisConfig","ensureAnalysisConfig","legacyAnalysisType","analysisConfig","shouldIncludeFilter","filter","simpleFilter","f","getApplicableDashboardFilters","dashboardFilters","filterMapping","df","convertToServerFormat","groupFilter","convertedFilters","mergeDashboardAndPortletFilters","portletFilters","format","extractDashboardFields","dashboardConfig","measures","dimensions","timeDimensions","extractFromCubeQuery","cubeQuery","measure","dimension","td","extractFieldsFromFilters","field","funnelQuery","subQuery","e","filters","fields","getDateRangeFromFilter","applyUniversalTimeFilters","portletTimeDimensions","universalTimeFilters","dateRange","AnalyticsPortlet","React","forwardRef","chartType","displayConfig","dashboardFilterMapping","eagerLoad","_isVisible","height","_title","colorPalette","loadingComponent","onDebugDataReady","ref","onDebugDataReadyRef","useRef","scrollContainer","useScrollContainer","inViewRef","inView","useInView","isVisible","chartTypeConfig","useChartConfig","shouldSkipQuery","regularFilters","queryObject","multiQueryConfig","serverFunnelQuery","serverFlowQuery","serverRetentionQuery","parsed","applicableFilters","modifiedFunnel","step0","existingFilters","mergedFilters","timeFilter","dateRangeValue","timeDimMember","timeRangeFilter","q","mergedTimeDimensions","isMultiQuery","isFunnelMode","isFlowMode","isRetentionMode","shouldSkipSingle","shouldSkipMulti","shouldSkipFunnel","shouldSkipFlow","shouldSkipRetention","funnelConfig","singleQueryResult","useCubeLoadQuery","multiQueryResult","useMultiCubeLoadQuery","funnelQueryResult","flowQueryResult","retentionQueryResult","isLoading","isFetching","multiQueryData","flowChartData","retentionChartData","useImperativeHandle","bustCache","cleanedConfig","cleanQueryForServer","createMultiQueryKey","cleanedQuery","createQueryKey","handleRetry","getData","hasMandatoryFields","zone","LoadingIndicator","chartHeight","effectiveChartType","isValidChartType","LazyChart","useScrollDetection","containerRef","threshold","container","isScrolled","setIsScrolled","timeoutRef","handleScroll","shouldBeScrolled","prev","useElementVisibility","elementRef","setIsVisible","hasBeenVisibleRef","checkVisibility","element","elementRect","containerRect","visible","scrollTarget","rafId","useDragAutoScroll","scrollContainerRef","edgeThreshold","maxScrollSpeed","enabled","animationFrameRef","scrollDirectionRef","scrollIntensityRef","calculateScrollSpeed","distanceFromEdge","normalizedDistance","scrollLoop","speed","scrollAmount","startScrolling","direction","intensity","stopScrolling","handleDragOver","event","mouseY","distanceFromTop","distanceFromBottom","handleDragEnd","highlightJs","loadingPromise","loadSyntaxHighlighter","hljs","javascript","sql","json","highlightCodeBlocks","block","getSyntaxHighlighter","DebugModal","isOpen","setIsOpen","handleKeyDown","timer","createDefaultState","buildInitialState","createStoreActions","set","_get","initialState","isEdit","state","filterId","portletId","isDragging","layout","initialized","_","rest","dirty","createDashboardStore","createStore","devtools","subscribeWithSelector","get","fallbackStore","getFallbackStore","DashboardStoreContext","createContext","DashboardStoreProvider","children","initialEditMode","storeRef","useDashboardStore","selector","store","useContext","useStore","useDashboardStoreApi","useDashboardStoreOptional","contextStore","selectEditModeState","selectModalState","selectLayoutState","selectDebugData","selectPortletDebugData","selectEditModeActions","selectModalActions","selectLayoutActions","selectDebugDataActions","selectThumbnailDirty","selectAllActions","ICON_STYLE","arePropsEqual","prevProps","nextProps","containerPropsEqual","shallowEqualObjects","headerPropsEqual","keysA","keysB","key","DashboardPortletCard","editable","configEagerLoad","containerProps","headerProps","setPortletRef","setPortletComponentRef","callbacks","icons","normalizedPortlet","chartModeConfig","renderQuery","renderChartType","renderChartConfig","renderDisplayConfig","isEditMode","selectedFilterId","debugData","setDebugData","CameraIcon","CheckIcon","copySuccess","setCopySuccess","copyAvailable","setCopyAvailable","chartContainerRef","isShiftHeld","setIsShiftHeld","isHoveringRefresh","setIsHoveringRefresh","handleKeyUp","showCacheBustIndicator","isPortletCopyAvailable","handleCopyToClipboard","copyPortletToClipboard","hasSelectedFilter","isInSelectionMode","mergedContainerClassName","mergedHeaderClassName","containerOnClick","_containerClassName","containerStyle","restContainerProps","headerOnClick","_headerClassName","headerStyle","restHeaderProps","handleDebugDataReady","el","Fragment","COLUMN_GAP","RowManagedLayout","portlets","gridSettings","gridWidth","canEdit","onRowResize","onColumnResize","onPortletDragStart","onPortletDragEnd","onRowDrop","onNewRowDrop","renderPortlet","portletMap","activeDropKey","setActiveDropKey","setDropActive","isDragActive","handlePortletDragStart","rowIndex","columnIndex","handlePortletDragEnd","topDropActive","bottomDropActive","rowHeight","safeGridWidth","paddingLeft","paddingRight","unitWidth","column","width","COLOR_PALETTES","getColorPalette","paletteName","p","EditIcon","GridIcon","RowsIcon","AddIcon","SwatchIcon","FloatingEditToolbar","isEditBarVisible","position","onEditModeToggle","layoutMode","onLayoutModeChange","allowedModes","canChangeLayoutMode","currentPalette","onPaletteChange","onAddPortlet","isPaletteOpen","setIsPaletteOpen","paletteRef","handleClickOutside","positionClasses","isHidden","toolbarContent","ToolbarButton","CompactPaletteDropdown","palette","createPortal","Icon","tooltip","isActive","disabled","onClick","color","Modal","onClose","title","size","closeOnBackdropClick","closeOnEscape","showCloseButton","footer","noPadding","handleEscapeKey","LazyAnalysisBuilder","lazy","AnalysisBuilder","Suspense","generateId","generateMetricLabel","n","findFieldInSchema","fieldName","schema","cube","d","getFieldTitle","found","schemaToFieldOptions","mode","isTime","filterFieldOptions","searchTerm","selectedCube","filtered","opt","term","groupFieldsByCube","grouped","option","existing","getCubeNames","getCubeTitle","cubeName","getRelatedCubeNames","sourceCube","related","rel","getRelatedCubesSchema","relatedNames","RECENT_FIELDS_KEY","MAX_RECENT_FIELDS","getRecentFields","stored","addRecentField","recent","getRecentFieldOptions","recentFieldNames","allOptions","recentOptions","stateToServerQuery","mapping","serverStep","serverQueryToState","funnel","funnelCube","parts","funnelBindingKey","funnelTimeDimension","funnelSteps","funnelModeAdapter","storeState","charts","activeView","errors","warnings","names","duplicates","name","startingStep","flowCube","flowBindingKey","flowTimeDimension","startingStepFilters","isValidFlowConfig","flowModeAdapter","retention","retentionCube","retentionBindingKey","retentionTimeDimension","retentionCohortFilters","retentionActivityFilters","retentionBreakdowns","retentionDateRange","getDateRangeFromPreset","DEFAULT_DATE_RANGE_PRESET","isValidRetentionConfig","retentionModeAdapter","defaultRetentionSliceState","startDate","endDate","PortletAnalysisModal","onSave","initialData","modalTitle","submitText","builderRef","formTitle","setFormTitle","derivedConfig","initialQuery","initialChartConfig","modeCharts","initialAnalysisType","initialFunnelState","funnelState","initialFlowState","flowState","initialRetentionState","retentionState","handleSave","hasContent","firstQuery","message","portletData","handleCancel","PortletFilterConfigModal","currentMapping","portletTitle","selectedFilters","setSelectedFilters","handleToggleFilter","id","formatFilterPreview","valuesText","filterCount","isSelected","ConfirmModal","onConfirm","confirmText","cancelText","confirmVariant","baseClasses","ChevronDownIcon","ColorPaletteSelector","className","dropdownRef","currentPaletteObj","handlePaletteSelect","FieldSearchItem","isFocused","onMouseEnter","getFieldIcon","getMeasureTypeIcon","getFieldTypeIcon","getBadgeStyle","getTypeLabel","FieldSearchItem$1","memo","FieldDetailPanel","getIconBgStyle","getTypeDisplay","FieldDetailPanel$1","SearchIcon","CloseIcon","FieldSearchModal","onSelect","selectedFields","externalRecentFields","setSearchTerm","setSelectedCube","focusedField","setFocusedField","focusedIndex","setFocusedIndex","lastSelectedIndex","setLastSelectedIndex","searchInputRef","resultsContainerRef","fieldOptionsMode","allFieldOptions","cubeNames","filteredFields","groupedFields","flatFieldsList","list","selectSingleField","keepOpen","metaField","handleSelectField","fieldIndex","shiftKey","startIndex","endIndex","rangeField","next","focusedElement","searchPlaceholder","focusedFieldId","idx","DimensionIcon","TimeDimensionIcon","MeasureIcon","EyeIcon","EyeOffIcon","DashboardFilterConfigModal","initialFilter","fullSchema","filteredSchema","onDelete","localLabel","setLocalLabel","localFilter","setLocalFilter","showAllFields","setShowAllFields","showFieldSearch","setShowFieldSearch","isOperatorDropdownOpen","setIsOperatorDropdownOpen","isValueDropdownOpen","setIsValueDropdownOpen","isDateRangeDropdownOpen","setIsDateRangeDropdownOpen","rangeType","setRangeType","numberValue","setNumberValue","searchText","setSearchText","activeSchema","debouncedSearchText","useDebounce","fieldInfo","fieldType","isTimeField","isMeasureField","isDimensionField","fieldTitle","operatorMeta","FILTER_OPERATORS","availableOperators","getAvailableOperators","shouldShowDateRange","shouldShowComboBox","distinctValues","valuesLoading","valuesError","searchValues","useFilterValues","flexMatch","singularMatch","num","unit","DATE_RANGE_OPTIONS","requiresNumberInput","convertDateRangeTypeToValue","handleFieldSelected","_fieldType","newFieldType","defaultOperator","handleOperatorChange","operator","handleValueSelect","value","handleValueRemove","valueToRemove","v","handleDirectInput","numValue","handleBetweenStartInput","currentValues","newValues","handleBetweenEndInput","handleDateInput","handleRangeTypeChange","newRangeType","today","handleNumberValueChange","handleCustomStartDate","start","end","handleCustomEndDate","updatedFilter","operatorLabel","op","dateRangeLabel","FieldIcon","iconBgClass","iconTextClass","renderValueInput","FilterEditModal","convertToMetaResponse","dashboardFields","filteredCubes","filteredMeasures","fullName","filteredDimensions","filteredCubeMeta","FilterIcon","ClockIcon","EditModeFilterList","onAddFilter","onAddTimeFilter","onEditFilter","onRemoveFilter","onFilterSelect","isCollapsed","setIsCollapsed","renderFilterChip","dashboardFilter","isUniversalTime","DATE_PRESETS","XTD_OPTIONS","calculateDateRange","preset","endOfToday","yesterday","endOfYesterday","startOfWeek","dayOfWeek","diff","quarter","lastNMatch","lastUnitMatch","endOfLastWeek","startOfLastWeek","startOfLastMonth","endOfLastMonth","currentQuarter","lastQuarter","lastQuarterYear","startOfLastQuarter","endOfLastQuarter","startOfLastYear","endOfLastYear","formatDateRangeDisplay","startStr","endStr","startNoYear","detectPresetFromDateRange","normalizedRange","formatFilterValueDisplay","formattedValues","DatePresetChips","activePreset","onPresetSelect","presetTooltips","tooltips","range","LAST_UNITS","CustomDateDropdown","onDateRangeChange","currentDateRange","activeTab","setActiveTab","fixedStartDate","setFixedStartDate","fixedEndDate","setFixedEndDate","sinceDate","setSinceDate","lastNumber","setLastNumber","lastUnit","setLastUnit","handleEscape","timeoutId","handleApplyFixed","handleApplySince","formatDateForCube","handleApplyLast","tabButtonStyle","handleDropdownClick","tab","XTDDropdown","currentXTD","dateRangeText","FilterValueSelector","onValuesChange","hasLoadedInitial","setHasLoadedInitial","lastSearchedTerm","isDimension","dim","isTimeDimension","shouldFetchValues","handleDropdownToggle","newIsOpen","handleSearchChange","newSearchText","handleDateRangeEndInput","cubeMeta","FilterValuePopover","anchorRef","popoverRef","handleValuesChange","metaResponse","FilterChip","onChange","onEdit","onRemove","showPopover","setShowPopover","chipRef","valueDisplay","handleValueChange","handleChipClick","CalendarIcon","CompactFilterBar","onDashboardFiltersChange","showCustomDropdown","setShowCustomDropdown","showXTDDropdown","setShowXTDDropdown","customButtonRef","xtdButtonRef","universalTimeFilter","activePresetId","activeXTDId","nonDateFilters","generateFilterId","handleDateRangeChange","newDateRange","updatedFilters","newFilter","handlePresetSelect","presetValue","handleXTDSelect","xtdValue","handleCustomDateSelect","handleFilterChange","dateRangeTooltip","DashboardFilterPanel","onSaveFilters","editingFilter","setEditingFilter","showFilterBuilder","setShowFilterBuilder","handleAddFilter","handleAddTimeFilter","handleEditFilter","filterToEdit","handleRemoveFilter","handleSaveFilter","filterData","existingFilterIndex","handleCloseFilterBuilder","ScaledGridWrapper","scaleFactor","designWidth","actualHeight","setActualHeight","innerRef","observer","entries","visualHeight","findScrollableAncestor","current","style","overflowY","overflowX","hasScrollableOverflow","hasScrollContent","MobileStackedLayout","onPortletRefresh","portletComponentRefs","setScrollContainer","setContainerRef","node","sortedPortlets","handlePortletRefresh","ScrollContainerProvider","portletHeight","headerHeight","contentHeight","createRowId","equalizeRowColumns","portletIds","count","cols","minW","minTotal","base","remainder","remaining","extra","adjustRowWidths","columns","adjusted","total","overflow","reducible","delta","convertPortletsToRows","sorted","rowsByY","rowY","rowPortlets","normalizeRows","col","convertRowsToPortlets","currentY","updated","currentX","updatedIds","selectStoreState","selectStoreActions","useDashboard","propAllowedModes","isResponsiveEditable","onConfigChange","onSaveThumbnail","dashboardRef","useShallow","storeActions","thumbnailConfig","draftRowsRef","fallbackMode","configMode","selectedFilter","resolvedRows","baseRows","enterEditMode","exitEditMode","thumbnailData","captureThumbnail","thumbnailUrl","toggleEditMode","selectFilter","openAddPortlet","openEditPortlet","openFilterConfig","hasLayoutActuallyChanged","newLayout","newItem","oldItem","item","updateRowLayout","save","portletsOverride","normalizedRows","updatedPortlets","updatedConfig","handleLayoutModeChange","savePortlet","isNewPortlet","newPortletId","newPortlet","maxY","nextRows","executeDeletePortlet","deletePortlet","confirmDelete","duplicatePortlet","originalPortlet","duplicatedPortlet","refreshPortlet","portletComponent","toggleFilterForPortlet","hasFilter","selectAllForFilter","saveFilterConfig","handlePaletteChange","actions","ChartBarIcon","DeleteIcon","CopyIcon","DesktopIcon","DEFAULT_GRID_SETTINGS","getGridSettings","DashboardGrid","dashboardModes","containerWidth","displayMode","useResponsiveDashboard","containerElementRef","editBarRef","gridContentRef","combinedContainerRef","foundScrollContainer","portletRefs","dragStateRef","dashboard","isPortletModalOpen","editingPortlet","isFilterConfigModalOpen","filterConfigPortlet","deleteConfirmPortletId","draftRows","isDraggingPortlet","isInitialized","initialLayout","handleLayoutChange","_layout","handleDragStop","_oldItem","_newItem","_placeholder","_e","_element","mutableLayout","layoutItem","handleResizeStop","startRowResize","startY","startRows","handleMouseMove","moveEvent","deltaUnits","handleMouseUp","finalRows","startColumnResize","startX","leftColumn","rightColumn","nextLeft","nextRight","rowItem","nextColumns","colIndex","handleRowDrop","insertIndex","dragState","sourceRowIndex","sourceRow","movedColumn","sourceRowRemoved","targetRowIndex","targetRow","targetIndex","handleNewRowDrop","newRow","handlePortletSave","scrollToPortlet","portletElement","handleDeletePortlet","handleDuplicatePortlet","handleAddPortlet","handleEditPortlet","handleOpenFilterConfig","handleSaveFilterConfig","handleSetPortletRef","handleSetPortletComponentRef","portletIcons","handleToggleFilterForPortlet","handleFilterSelect","handleSelectAllForFilter","portletCallbacks","renderPortletCard","baseLayout","renderActiveLayout","ReactGridLayout","axis","verticalCompactor","AnalyticsDashboard","propDashboardFilters","onDirtyStateChange","meta","useCubeMeta","handleConfigChangeWithDirtyTracking","handleSaveWithDirtyTracking","useDirtyStateTracking","mergedDashboardFilters","configFilters","propFilters","configFilter","propFilter","pf","configIds","cf","newFilters","handleDashboardFiltersChange","PortletContainer","onRefresh","DashboardEditModal","initialName","initialDescription","setName","description","setDescription","isSaving","setIsSaving","handleSubmit","handleClose","QueryBuilderShim","analysisBuilderRef"],"mappings":"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"}
|