drizzle-cube 0.4.13 → 0.4.14
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/README.md +1 -0
- package/dist/adapters/express/index.cjs +6 -6
- package/dist/adapters/express/index.js +73 -72
- package/dist/adapters/fastify/index.cjs +5 -5
- package/dist/adapters/fastify/index.js +110 -109
- package/dist/adapters/{handler-CQkIwtxp.js → handler-DZnCbydH.js} +719 -272
- package/dist/adapters/handler-ZDYlokiM.cjs +25 -0
- package/dist/adapters/hono/index.cjs +6 -6
- package/dist/adapters/hono/index.js +121 -120
- package/dist/adapters/nextjs/index.cjs +5 -5
- package/dist/adapters/nextjs/index.js +92 -91
- package/dist/client/charts.js +67 -59
- package/dist/client/charts.js.map +1 -1
- package/dist/client/chunks/{RetentionCombinedChart-CEI8KQ3t.js → RetentionCombinedChart-CLq89aOJ.js} +2 -2
- package/dist/client/chunks/{RetentionCombinedChart-CEI8KQ3t.js.map → RetentionCombinedChart-CLq89aOJ.js.map} +1 -1
- package/dist/client/chunks/{analysis-builder-BMmWeFPr.js → analysis-builder-C5e52Z3p.js} +419 -411
- package/dist/client/chunks/analysis-builder-C5e52Z3p.js.map +1 -0
- package/dist/client/chunks/{analysis-builder-shared-D56zYeV0.js → analysis-builder-shared-EnM-8plh.js} +2 -2
- package/dist/client/chunks/{analysis-builder-shared-D56zYeV0.js.map → analysis-builder-shared-EnM-8plh.js.map} +1 -1
- package/dist/client/chunks/{chart-activity-grid-CE7xGFQo.js → chart-activity-grid-CPGcTSuh.js} +2 -2
- package/dist/client/chunks/{chart-activity-grid-CE7xGFQo.js.map → chart-activity-grid-CPGcTSuh.js.map} +1 -1
- package/dist/client/chunks/{chart-area-BJAgusst.js → chart-area-ByJQ7NZd.js} +3 -3
- package/dist/client/chunks/{chart-area-BJAgusst.js.map → chart-area-ByJQ7NZd.js.map} +1 -1
- package/dist/client/chunks/{chart-bar-Blypx8O4.js → chart-bar-dj14frMt.js} +2 -2
- package/dist/client/chunks/{chart-bar-Blypx8O4.js.map → chart-bar-dj14frMt.js.map} +1 -1
- package/dist/client/chunks/chart-box-plot-ZatBpatq.js +322 -0
- package/dist/client/chunks/chart-box-plot-ZatBpatq.js.map +1 -0
- package/dist/client/chunks/{chart-bubble-Bf42A1-B.js → chart-bubble-CemotLx-.js} +2 -2
- package/dist/client/chunks/{chart-bubble-Bf42A1-B.js.map → chart-bubble-CemotLx-.js.map} +1 -1
- package/dist/client/chunks/chart-candlestick-BIR4uGGt.js +269 -0
- package/dist/client/chunks/chart-candlestick-BIR4uGGt.js.map +1 -0
- package/dist/client/chunks/chart-config-box-plot-D_E_SSc2.js +38 -0
- package/dist/client/chunks/chart-config-box-plot-D_E_SSc2.js.map +1 -0
- package/dist/client/chunks/chart-config-candlestick-CRCpD43-.js +70 -0
- package/dist/client/chunks/chart-config-candlestick-CRCpD43-.js.map +1 -0
- package/dist/client/chunks/chart-config-gauge-CQx9w3d4.js +64 -0
- package/dist/client/chunks/chart-config-gauge-CQx9w3d4.js.map +1 -0
- package/dist/client/chunks/chart-config-measure-profile-ZYaMrtws.js +70 -0
- package/dist/client/chunks/chart-config-measure-profile-ZYaMrtws.js.map +1 -0
- package/dist/client/chunks/chart-config-waterfall-DTyXV_fo.js +60 -0
- package/dist/client/chunks/chart-config-waterfall-DTyXV_fo.js.map +1 -0
- package/dist/client/chunks/{chart-data-table-Ba_6tuJw.js → chart-data-table-D5G8nMnb.js} +2 -2
- package/dist/client/chunks/{chart-data-table-Ba_6tuJw.js.map → chart-data-table-D5G8nMnb.js.map} +1 -1
- package/dist/client/chunks/{chart-funnel-C9kenCpp.js → chart-funnel-dofnhD24.js} +2 -2
- package/dist/client/chunks/{chart-funnel-C9kenCpp.js.map → chart-funnel-dofnhD24.js.map} +1 -1
- package/dist/client/chunks/chart-gauge-CKJJ8m3b.js +374 -0
- package/dist/client/chunks/chart-gauge-CKJJ8m3b.js.map +1 -0
- package/dist/client/chunks/{chart-heat-map-CYGemyPB.js → chart-heat-map-BVuPUKHT.js} +2 -2
- package/dist/client/chunks/{chart-heat-map-CYGemyPB.js.map → chart-heat-map-BVuPUKHT.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-delta-D9XJoKuA.js → chart-kpi-delta-Dgg2eYRl.js} +3 -3
- package/dist/client/chunks/{chart-kpi-delta-D9XJoKuA.js.map → chart-kpi-delta-Dgg2eYRl.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-number-C29Vj2g8.js → chart-kpi-number-DkoO99c1.js} +2 -2
- package/dist/client/chunks/{chart-kpi-number-C29Vj2g8.js.map → chart-kpi-number-DkoO99c1.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-text-CgjjrurK.js → chart-kpi-text-1O6_lmz7.js} +3 -3
- package/dist/client/chunks/{chart-kpi-text-CgjjrurK.js.map → chart-kpi-text-1O6_lmz7.js.map} +1 -1
- package/dist/client/chunks/{chart-line-zi6olZet.js → chart-line-DzyZkugh.js} +3 -3
- package/dist/client/chunks/{chart-line-zi6olZet.js.map → chart-line-DzyZkugh.js.map} +1 -1
- package/dist/client/chunks/chart-measure-profile-C2IkBG3V.js +114 -0
- package/dist/client/chunks/chart-measure-profile-C2IkBG3V.js.map +1 -0
- package/dist/client/chunks/{chart-pie-C4SuxKSN.js → chart-pie-akbfRfb9.js} +2 -2
- package/dist/client/chunks/{chart-pie-C4SuxKSN.js.map → chart-pie-akbfRfb9.js.map} +1 -1
- package/dist/client/chunks/{chart-radar-BW3Z_-Ly.js → chart-radar-BaN-Kjww.js} +2 -2
- package/dist/client/chunks/{chart-radar-BW3Z_-Ly.js.map → chart-radar-BaN-Kjww.js.map} +1 -1
- package/dist/client/chunks/{chart-radial-bar-0Fa3aeP5.js → chart-radial-bar-DpptEL3s.js} +2 -2
- package/dist/client/chunks/{chart-radial-bar-0Fa3aeP5.js.map → chart-radial-bar-DpptEL3s.js.map} +1 -1
- package/dist/client/chunks/{chart-sankey-DBghfbg1.js → chart-sankey-CG-3hHmX.js} +2 -2
- package/dist/client/chunks/{chart-sankey-DBghfbg1.js.map → chart-sankey-CG-3hHmX.js.map} +1 -1
- package/dist/client/chunks/{chart-scatter-DOVu1TNq.js → chart-scatter-l_yTVxF3.js} +2 -2
- package/dist/client/chunks/{chart-scatter-DOVu1TNq.js.map → chart-scatter-l_yTVxF3.js.map} +1 -1
- package/dist/client/chunks/{chart-sunburst-LfNthFlZ.js → chart-sunburst-KhDcKhmZ.js} +2 -2
- package/dist/client/chunks/{chart-sunburst-LfNthFlZ.js.map → chart-sunburst-KhDcKhmZ.js.map} +1 -1
- package/dist/client/chunks/{chart-tree-map-DZtQPyWX.js → chart-tree-map-CBbiaBXV.js} +2 -2
- package/dist/client/chunks/{chart-tree-map-DZtQPyWX.js.map → chart-tree-map-CBbiaBXV.js.map} +1 -1
- package/dist/client/chunks/chart-waterfall-CX3vx_lI.js +191 -0
- package/dist/client/chunks/chart-waterfall-CX3vx_lI.js.map +1 -0
- package/dist/client/chunks/{charts-core-DmGfleFz.js → charts-core-CU9u_HtL.js} +2 -1
- package/dist/client/chunks/charts-core-CU9u_HtL.js.map +1 -0
- package/dist/client/chunks/{charts-loader-CH0_S06T.js → charts-loader-AW3T1nv5.js} +58 -42
- package/dist/client/chunks/charts-loader-AW3T1nv5.js.map +1 -0
- package/dist/client/chunks/{components-ClQziOcT.js → components-BkeSy9xv.js} +4 -4
- package/dist/client/chunks/components-BkeSy9xv.js.map +1 -0
- package/dist/client/components/AgenticNotebook/AgentChatPanel.d.ts +10 -0
- package/dist/client/components/AgenticNotebook/ChatMessage.d.ts +2 -0
- package/dist/client/components/AgenticNotebook/index.d.ts +11 -1
- package/dist/client/components/LoadingIndicator.d.ts +2 -2
- package/dist/client/components/charts/BoxPlotChart.config.d.ts +1 -7
- package/dist/client/components/charts/CandlestickChart.config.d.ts +5 -0
- package/dist/client/components/charts/CandlestickChart.d.ts +4 -0
- package/dist/client/components/charts/GaugeChart.config.d.ts +5 -0
- package/dist/client/components/charts/GaugeChart.d.ts +4 -0
- package/dist/client/components/charts/MeasureProfileChart.config.d.ts +5 -0
- package/dist/client/components/charts/MeasureProfileChart.d.ts +4 -0
- package/dist/client/components/charts/WaterfallChart.config.d.ts +5 -0
- package/dist/client/components/charts/WaterfallChart.d.ts +4 -0
- package/dist/client/components/charts/index.d.ts +4 -0
- package/dist/client/components.js +1 -1
- package/dist/client/hooks/useAgentChat.d.ts +20 -2
- package/dist/client/index.js +583 -522
- package/dist/client/index.js.map +1 -1
- package/dist/client/styles.css +1 -1
- package/dist/client/types.d.ts +19 -1
- package/dist/client/utils.js +4 -4
- package/dist/client-bundle-stats.html +1 -1
- package/dist/server/index.cjs +47 -45
- package/dist/server/index.d.ts +37 -0
- package/dist/server/index.js +1745 -1298
- package/package.json +4 -1
- package/dist/adapters/handler-dnkqpznh.cjs +0 -23
- package/dist/client/chunks/analysis-builder-BMmWeFPr.js.map +0 -1
- package/dist/client/chunks/chart-box-plot-Dja4LS3O.js +0 -313
- package/dist/client/chunks/chart-box-plot-Dja4LS3O.js.map +0 -1
- package/dist/client/chunks/chart-config-box-plot-D3DA7_pr.js +0 -85
- package/dist/client/chunks/chart-config-box-plot-D3DA7_pr.js.map +0 -1
- package/dist/client/chunks/charts-core-DmGfleFz.js.map +0 -1
- package/dist/client/chunks/charts-loader-CH0_S06T.js.map +0 -1
- package/dist/client/chunks/components-ClQziOcT.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components-BkeSy9xv.js","sources":["../../../src/client/components/ChartErrorBoundary.tsx","../../../src/client/components/DrillMenu.tsx","../../../src/client/components/DrillBreadcrumb.tsx","../../../src/client/utils/drillQueryBuilder.ts","../../../src/client/hooks/useDrillInteraction.ts","../../../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/shared/components/CodeBlock.tsx","../../../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/AnalysisBuilder/AnalysisDisplayConfigPanel.tsx","../../../src/client/components/TextPortletModal.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/dashboard/useGridLayoutEngine.ts","../../../src/client/hooks/dashboard/layoutUtils.ts","../../../src/client/hooks/dashboard/useRowLayoutEngine.ts","../../../src/client/hooks/dashboard/useDashboardController.ts","../../../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"],"sourcesContent":["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 dc: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 dc:text-left\">\n <div className=\"dc:text-xs dc: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 dc: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 dc: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 dc: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 dc: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 * DrillMenu - Popover menu for drill-down options\n * Shows available drill directions (down/up) grouped by type (time, hierarchy, details)\n * Rendered in a portal to avoid stacking context issues with react-grid-layout\n */\n\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport type { DrillMenuProps, DrillOption } from '../types/drill'\n\n/**\n * Icon components for drill options\n */\nfunction TimeIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"8\" cy=\"8\" r=\"6\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M8 5V8L10 10\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nfunction HierarchyIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"6\" y=\"2\" width=\"4\" height=\"3\" rx=\"0.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <rect x=\"2\" y=\"11\" width=\"4\" height=\"3\" rx=\"0.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <rect x=\"10\" y=\"11\" width=\"4\" height=\"3\" rx=\"0.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M8 5V8M8 8L4 11M8 8L12 11\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n </svg>\n )\n}\n\nfunction TableIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"2\" y=\"2\" width=\"12\" height=\"12\" rx=\"1\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M2 6H14M6 6V14\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n </svg>\n )\n}\n\nfunction DrillDownIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 2V10M6 10L3 7M6 10L9 7\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\nfunction DrillUpIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 10V2M6 2L3 5M6 2L9 5\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n/**\n * Get the direction indicator for a drill option\n */\nfunction getDirectionIndicator(option: DrillOption) {\n if (option.type === 'drillDown') {\n return <DrillDownIcon className=\"dc:w-3 dc:h-3 text-dc-success\" />\n }\n if (option.type === 'drillUp') {\n return <DrillUpIcon className=\"dc:w-3 dc:h-3 text-dc-warning\" />\n }\n return null\n}\n\n/**\n * Group options by category (time, hierarchy, details)\n */\nfunction groupOptions(options: DrillOption[]): Map<string, DrillOption[]> {\n const groups = new Map<string, DrillOption[]>()\n\n for (const option of options) {\n const category = option.icon || 'other'\n const existing = groups.get(category) || []\n existing.push(option)\n groups.set(category, existing)\n }\n\n return groups\n}\n\n/**\n * Get display name for a category\n */\nfunction getCategoryLabel(category: string): string {\n switch (category) {\n case 'time':\n return 'Time'\n case 'hierarchy':\n return 'Hierarchy'\n case 'table':\n return 'Details'\n default:\n return 'Options'\n }\n}\n\n/**\n * DrillMenu component\n * Uses createPortal to render directly to document.body, avoiding stacking context issues\n */\nexport function DrillMenu({ options, position, onSelect, onClose }: DrillMenuProps) {\n const menuRef = useRef<HTMLDivElement>(null)\n const [mounted, setMounted] = useState(false)\n\n // Track if we're mounted (for SSR safety)\n useEffect(() => {\n setMounted(true)\n return () => setMounted(false)\n }, [])\n\n // Close on click outside\n useEffect(() => {\n function handleClickOutside(event: MouseEvent) {\n if (menuRef.current && !menuRef.current.contains(event.target as Node)) {\n onClose()\n }\n }\n\n function handleEscape(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n\n // Close on scroll to avoid menu getting out of sync with clicked element\n function handleScroll() {\n onClose()\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n // Listen to scroll on capture phase to catch scrolling containers\n window.addEventListener('scroll', handleScroll, true)\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n window.removeEventListener('scroll', handleScroll, true)\n }\n }, [onClose])\n\n if (options.length === 0 || !mounted) {\n return null\n }\n\n // Position menu near clicked point but within viewport\n // Use very high z-index to ensure menu appears above everything\n const menuStyle: React.CSSProperties = {\n position: 'fixed',\n left: Math.min(position.x, window.innerWidth - 250),\n top: Math.min(position.y, window.innerHeight - 300),\n zIndex: 99999\n }\n\n const groupedOptions = groupOptions(options)\n\n const menuContent = (\n <div\n ref={menuRef}\n className=\"dc:min-w-[200px] dc:max-w-[280px] bg-dc-surface dc:rounded-lg dc:shadow-lg border border-dc-border dc:overflow-hidden\"\n style={menuStyle}\n >\n {Array.from(groupedOptions.entries()).map(([category, categoryOptions], categoryIndex) => (\n <div key={category}>\n {/* Category header */}\n <div className=\"dc:px-3 dc:py-2 dc:flex dc:items-center dc:gap-2 bg-dc-surface-secondary text-dc-text-secondary dc:text-xs dc:font-medium dc:uppercase dc:tracking-wide\">\n {category === 'time' && <TimeIcon className=\"dc:w-3 dc:h-3\" />}\n {category === 'hierarchy' && <HierarchyIcon className=\"dc:w-3 dc:h-3\" />}\n {category === 'table' && <TableIcon className=\"dc:w-3 dc:h-3\" />}\n {getCategoryLabel(category)}\n </div>\n\n {/* Options in this category */}\n <div className=\"dc:py-1\">\n {categoryOptions.map((option) => (\n <button\n key={option.id}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:flex dc:items-center dc:gap-2 dc:cursor-pointer text-dc-text dc:text-sm dc:text-left hover:bg-dc-surface-hover hover:text-dc-accent dc:transition-colors dc:rounded-sm\"\n onClick={() => {\n onSelect(option)\n onClose()\n }}\n >\n {/* Direction indicator */}\n <span className=\"dc:w-4 dc:flex dc:justify-center\">\n {getDirectionIndicator(option)}\n </span>\n\n {/* Label */}\n <span className=\"dc:flex-1\">{option.label}</span>\n </button>\n ))}\n </div>\n\n {/* Separator between categories */}\n {categoryIndex < groupedOptions.size - 1 && (\n <div className=\"border-t border-dc-border\" />\n )}\n </div>\n ))}\n </div>\n )\n\n // Render in a portal to avoid stacking context issues with react-grid-layout\n return createPortal(menuContent, document.body)\n}\n\nexport default DrillMenu\n","/**\n * DrillBreadcrumb - Navigation breadcrumb for drill path\n * Shows the current drill history and allows navigating back\n */\n\nimport React from 'react'\nimport type { DrillBreadcrumbProps } from '../types/drill'\n\n/**\n * Home icon for the root level\n */\nfunction HomeIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 7L7 2L12 7\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M3 6V11.5C3 11.7761 3.22386 12 3.5 12H5.5V9H8.5V12H10.5C10.7761 12 11 11.7761 11 11.5V6\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n/**\n * Chevron separator between breadcrumb items\n */\nfunction ChevronIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 2L8 6L4 10\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n/**\n * Back arrow icon\n */\nfunction BackIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12 7H2M2 7L6 3M2 7L6 11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n/**\n * Get a safe display label, handling edge cases\n */\nfunction getSafeLabel(label: string | undefined | null): string {\n if (!label || label === 'undefined' || label === 'null' || label === '') {\n return '(empty)'\n }\n // Handle labels that are just whitespace\n if (label.trim() === '') {\n return '(empty)'\n }\n return label\n}\n\n/**\n * DrillBreadcrumb component\n */\nexport function DrillBreadcrumb({ path, onNavigate, onLevelClick }: DrillBreadcrumbProps) {\n if (path.length === 0) {\n return null\n }\n\n return (\n <div className=\"dc:flex dc:items-center dc:gap-1 dc:px-2 dc:py-1.5 bg-dc-surface-secondary dc:rounded-md dc:text-xs\">\n {/* Back button */}\n <button\n className=\"dc:flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded dc:cursor-pointer dc:hover:bg-dc-surface-hover text-dc-text-secondary dc:hover:text-dc-text dc:transition-colors\"\n onClick={onNavigate}\n title=\"Go back one level\"\n >\n <BackIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span className=\"dc:sr-only\">Back</span>\n </button>\n\n <span className=\"text-dc-text-muted\">|</span>\n\n {/* Home / root level */}\n <button\n className=\"dc:flex dc:items-center dc:gap-1 dc:px-1.5 dc:py-1 dc:rounded dc:cursor-pointer dc:hover:bg-dc-surface-hover text-dc-text-secondary dc:hover:text-dc-text dc:transition-colors\"\n onClick={() => onLevelClick?.(0)}\n title=\"Return to top level\"\n >\n <HomeIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n\n {/* Path items */}\n {path.map((entry, index) => {\n const safeLabel = getSafeLabel(entry.label)\n return (\n <React.Fragment key={entry.id}>\n <ChevronIcon className=\"dc:w-3 dc:h-3 text-dc-text-muted\" />\n\n {index === path.length - 1 ? (\n // Current level (not clickable)\n <span className=\"dc:px-1.5 dc:py-1 text-dc-text dc:font-medium\" title={safeLabel}>\n {safeLabel}\n </span>\n ) : (\n // Previous levels (clickable)\n <button\n className=\"dc:px-1.5 dc:py-1 dc:rounded dc:cursor-pointer dc:hover:bg-dc-surface-hover text-dc-text-secondary dc:hover:text-dc-text dc:transition-colors\"\n onClick={() => onLevelClick?.(index + 1)}\n title={`Navigate to ${safeLabel}`}\n >\n {safeLabel}\n </button>\n )}\n </React.Fragment>\n )\n })}\n </div>\n )\n}\n\nexport default DrillBreadcrumb\n","/**\n * Utilities for building drill queries from drill options\n */\n\nimport type {\n CubeQuery,\n Filter,\n CubeMeta,\n CubeMetaHierarchy,\n TimeGranularity,\n DashboardFilter\n} from '../types'\nimport type {\n DrillOption,\n DrillResult,\n ChartDataPointClickEvent\n} from '../types/drill'\n\n/**\n * Generate a simple unique ID (no external dependency)\n */\nfunction generateId(): string {\n return `drill-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n}\n\n/**\n * Time granularity order from least to most granular\n * Used for determining drill direction\n */\nconst TIME_GRANULARITY_ORDER: TimeGranularity[] = [\n 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second'\n]\n\n/**\n * Check if a dimension is a time dimension based on metadata\n */\nexport function isTimeDimension(\n dimensionName: string,\n metadata: CubeMeta\n): boolean {\n for (const cube of metadata.cubes) {\n const dimension = cube.dimensions.find(d => d.name === dimensionName)\n if (dimension && dimension.type === 'time') {\n return true\n }\n }\n return false\n}\n\n/**\n * Get the granularities available for a time dimension\n */\nexport function getTimeDimensionGranularities(\n dimensionName: string,\n metadata: CubeMeta\n): TimeGranularity[] {\n for (const cube of metadata.cubes) {\n const dimension = cube.dimensions.find(d => d.name === dimensionName)\n if (dimension && dimension.type === 'time' && dimension.granularities) {\n return dimension.granularities\n }\n }\n // Default granularities if not specified\n return ['year', 'quarter', 'month', 'week', 'day', 'hour']\n}\n\n/**\n * Get the current granularity from a query's time dimensions\n */\nexport function getCurrentGranularity(query: CubeQuery): TimeGranularity | null {\n if (!query.timeDimensions || query.timeDimensions.length === 0) {\n return null\n }\n const granularity = query.timeDimensions[0].granularity\n if (granularity && TIME_GRANULARITY_ORDER.includes(granularity as TimeGranularity)) {\n return granularity as TimeGranularity\n }\n return null\n}\n\n/**\n * Get drillMembers for a measure from metadata\n */\nexport function getMeasureDrillMembers(\n measureName: string,\n metadata: CubeMeta\n): string[] | null {\n for (const cube of metadata.cubes) {\n const measure = cube.measures.find(m => m.name === measureName)\n if (measure && measure.drillMembers && measure.drillMembers.length > 0) {\n return measure.drillMembers\n }\n }\n return null\n}\n\n/**\n * Get hierarchy by name from metadata\n */\nexport function getHierarchy(\n hierarchyName: string,\n cubeName: string,\n metadata: CubeMeta\n): CubeMetaHierarchy | null {\n const cube = metadata.cubes.find(c => c.name === cubeName)\n if (cube && cube.hierarchies) {\n return cube.hierarchies.find(h => h.name === hierarchyName) || null\n }\n return null\n}\n\n/**\n * Get all hierarchies for a cube\n */\nexport function getCubeHierarchies(\n cubeName: string,\n metadata: CubeMeta\n): CubeMetaHierarchy[] {\n const cube = metadata.cubes.find(c => c.name === cubeName)\n return cube?.hierarchies || []\n}\n\n/**\n * Find which hierarchy (if any) contains a dimension\n */\nexport function findHierarchyForDimension(\n dimensionName: string,\n metadata: CubeMeta\n): { hierarchy: CubeMetaHierarchy; levelIndex: number } | null {\n const [cubeName] = dimensionName.split('.')\n const cube = metadata.cubes.find(c => c.name === cubeName)\n\n if (cube && cube.hierarchies) {\n for (const hierarchy of cube.hierarchies) {\n const levelIndex = hierarchy.levels.indexOf(dimensionName)\n if (levelIndex !== -1) {\n return { hierarchy, levelIndex }\n }\n }\n }\n return null\n}\n\n/**\n * Build drill options for a clicked data point\n */\nexport function buildDrillOptions(\n event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta | null,\n dashboardFilters?: DashboardFilter[],\n dashboardFilterMapping?: string[]\n): DrillOption[] {\n if (!metadata) {\n return []\n }\n\n const options: DrillOption[] = []\n const { clickedField } = event\n\n // Find the measure being drilled (if clicking on a measure)\n const measureName = clickedField\n const drillMembers = getMeasureDrillMembers(measureName, metadata)\n\n // 1. Time dimension drill options (if query has time dimensions)\n const timeDimOptions = buildTimeDrillOptions(query, metadata, dashboardFilters, dashboardFilterMapping)\n options.push(...timeDimOptions)\n\n // 2. Hierarchy drill options (for any dimension hierarchies in the query)\n const hierarchyOptions = buildHierarchyDrillOptions(query, metadata, dashboardFilters, dashboardFilterMapping)\n options.push(...hierarchyOptions)\n\n // 3. Detail drill options (if measure has drillMembers)\n // Show each drillMember as a separate option so user can choose what to drill into\n if (drillMembers && drillMembers.length > 0) {\n for (const drillMember of drillMembers) {\n const label = getDimensionLabel(drillMember, metadata)\n options.push({\n id: `details-${measureName}-${drillMember}`,\n label: `Show by ${label}`,\n type: 'details',\n icon: 'table',\n scope: 'portlet',\n measure: measureName,\n targetDimension: drillMember // Which drillMember to use\n })\n }\n }\n return options\n}\n\n/**\n * Build time-based drill options\n */\nfunction buildTimeDrillOptions(\n query: CubeQuery,\n metadata: CubeMeta,\n _dashboardFilters?: DashboardFilter[],\n _dashboardFilterMapping?: string[]\n): DrillOption[] {\n const options: DrillOption[] = []\n\n if (!query.timeDimensions || query.timeDimensions.length === 0) {\n return options\n }\n\n const timeDim = query.timeDimensions[0]\n const currentGranularity = timeDim.granularity as TimeGranularity | undefined\n const availableGranularities = getTimeDimensionGranularities(timeDim.dimension, metadata)\n\n if (availableGranularities.length === 0) {\n return options\n }\n\n // When no granularity is set, offer \"View by X\" options\n if (!currentGranularity) {\n for (const granularity of availableGranularities) {\n options.push({\n id: `time-set-${granularity}-portlet`,\n label: `View by ${capitalizeGranularity(granularity)}`,\n type: 'drillDown',\n icon: 'time',\n targetGranularity: granularity,\n scope: 'portlet'\n })\n }\n return options\n }\n\n const currentIndex = availableGranularities.indexOf(currentGranularity)\n\n // Drill down options (more granular)\n for (let i = currentIndex + 1; i < availableGranularities.length; i++) {\n const targetGranularity = availableGranularities[i]\n\n options.push({\n id: `time-down-${targetGranularity}-portlet`,\n label: `Drill to ${capitalizeGranularity(targetGranularity)}`,\n type: 'drillDown',\n icon: 'time',\n targetGranularity,\n scope: 'portlet'\n })\n }\n\n // Drill up options (less granular)\n for (let i = currentIndex - 1; i >= 0; i--) {\n const targetGranularity = availableGranularities[i]\n\n options.push({\n id: `time-up-${targetGranularity}-portlet`,\n label: `Roll up to ${capitalizeGranularity(targetGranularity)}`,\n type: 'drillUp',\n icon: 'time',\n targetGranularity,\n scope: 'portlet'\n })\n }\n\n return options\n}\n\n/**\n * Build hierarchy-based drill options\n */\nfunction buildHierarchyDrillOptions(\n query: CubeQuery,\n metadata: CubeMeta,\n _dashboardFilters?: DashboardFilter[],\n _dashboardFilterMapping?: string[]\n): DrillOption[] {\n const options: DrillOption[] = []\n\n if (!query.dimensions || query.dimensions.length === 0) {\n return options\n }\n\n // Check each dimension for hierarchy membership\n for (const dimension of query.dimensions) {\n const hierarchyInfo = findHierarchyForDimension(dimension, metadata)\n\n if (!hierarchyInfo) {\n continue\n }\n\n const { hierarchy, levelIndex } = hierarchyInfo\n\n // Drill down (more granular - next level in hierarchy)\n if (levelIndex < hierarchy.levels.length - 1) {\n const nextDimension = hierarchy.levels[levelIndex + 1]\n options.push({\n id: `hierarchy-down-${hierarchy.name}-${nextDimension}`,\n label: `Drill to ${getDimensionLabel(nextDimension, metadata)}`,\n type: 'drillDown',\n icon: 'hierarchy',\n hierarchy: hierarchy.name,\n targetDimension: nextDimension,\n scope: 'portlet'\n })\n }\n\n // Drill up (less granular - previous level in hierarchy)\n if (levelIndex > 0) {\n const prevDimension = hierarchy.levels[levelIndex - 1]\n options.push({\n id: `hierarchy-up-${hierarchy.name}-${prevDimension}`,\n label: `Roll up to ${getDimensionLabel(prevDimension, metadata)}`,\n type: 'drillUp',\n icon: 'hierarchy',\n hierarchy: hierarchy.name,\n targetDimension: prevDimension,\n scope: 'portlet'\n })\n }\n }\n\n return options\n}\n\n/**\n * Get display label for a dimension\n */\nfunction getDimensionLabel(dimensionName: string, metadata: CubeMeta): string {\n for (const cube of metadata.cubes) {\n const dimension = cube.dimensions.find(d => d.name === dimensionName)\n if (dimension) {\n return dimension.title || dimension.shortTitle || dimensionName.split('.')[1]\n }\n }\n return dimensionName.split('.')[1]\n}\n\n/**\n * Capitalize a granularity name\n */\nfunction capitalizeGranularity(granularity: string): string {\n return granularity.charAt(0).toUpperCase() + granularity.slice(1)\n}\n\n/**\n * Build a drill query based on the selected option\n */\nexport function buildDrillQuery(\n option: DrillOption,\n event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta\n): DrillResult {\n switch (option.type) {\n case 'drillDown':\n return buildDrillDownQuery(option, event, query, metadata)\n case 'drillUp':\n return buildDrillUpQuery(option, event, query, metadata)\n case 'details':\n return buildDetailsQuery(option, event, query, metadata)\n default:\n throw new Error(`Unknown drill type: ${option.type}`)\n }\n}\n\n/**\n * Build a drill-down query (more granular)\n */\nfunction buildDrillDownQuery(\n option: DrillOption,\n event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta\n): DrillResult {\n const { xValue } = event\n const newQuery = { ...query }\n const filters: Filter[] = []\n\n if (option.targetGranularity && query.timeDimensions) {\n // Time-based drill down\n const timeDim = query.timeDimensions[0]\n const currentGranularity = timeDim.granularity\n\n // Update granularity\n newQuery.timeDimensions = [{\n ...timeDim,\n granularity: option.targetGranularity,\n // Update date range to filter to the clicked period\n dateRange: getDateRangeForPeriod(String(xValue), currentGranularity || 'month')\n }]\n\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: String(xValue),\n query: newQuery,\n filters,\n granularity: option.targetGranularity,\n clickedValue: xValue\n }\n }\n } else if (option.targetDimension) {\n // Hierarchy-based drill down\n const currentDimensions = query.dimensions || []\n const hierarchyInfo = option.hierarchy ? findHierarchyForDimension(option.targetDimension, metadata) : null\n\n // Replace the current hierarchy dimension with the target\n const newDimensions = currentDimensions.map(dim => {\n if (hierarchyInfo && hierarchyInfo.hierarchy.levels.includes(dim)) {\n return option.targetDimension!\n }\n return dim\n })\n\n // If not replacing, add the new dimension\n if (!newDimensions.includes(option.targetDimension)) {\n newDimensions.push(option.targetDimension)\n }\n\n newQuery.dimensions = newDimensions\n\n // Add filter for the clicked value\n const currentDim = currentDimensions.find(d => {\n const info = findHierarchyForDimension(d, metadata)\n return info && info.hierarchy.name === option.hierarchy\n })\n\n if (currentDim && xValue !== undefined) {\n const newFilter: Filter = {\n member: currentDim,\n operator: 'equals',\n values: [String(xValue)]\n }\n filters.push(newFilter)\n newQuery.filters = [...(query.filters || []), newFilter]\n }\n\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: String(xValue),\n query: newQuery,\n filters,\n dimension: option.targetDimension,\n hierarchy: option.hierarchy,\n clickedValue: xValue\n }\n }\n }\n\n // Fallback - return query unchanged\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: 'Drill',\n query: newQuery,\n filters,\n clickedValue: xValue\n }\n }\n}\n\n/**\n * Build a drill-up query (less granular / roll-up)\n */\nfunction buildDrillUpQuery(\n option: DrillOption,\n _event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta\n): DrillResult {\n const newQuery = { ...query }\n\n if (option.targetGranularity && query.timeDimensions) {\n // Time-based drill up\n const timeDim = query.timeDimensions[0]\n\n newQuery.timeDimensions = [{\n ...timeDim,\n granularity: option.targetGranularity,\n // Clear date range to show all data at new granularity\n dateRange: timeDim.dateRange\n }]\n\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: `By ${capitalizeGranularity(option.targetGranularity)}`,\n query: newQuery,\n granularity: option.targetGranularity\n }\n }\n } else if (option.targetDimension) {\n // Hierarchy-based drill up\n const currentDimensions = query.dimensions || []\n const hierarchyInfo = option.hierarchy ? findHierarchyForDimension(option.targetDimension, metadata) : null\n\n // Replace the current hierarchy dimension with the target\n const newDimensions = currentDimensions.map(dim => {\n if (hierarchyInfo && hierarchyInfo.hierarchy.levels.includes(dim)) {\n return option.targetDimension!\n }\n return dim\n })\n\n newQuery.dimensions = newDimensions\n\n // Remove filters for dimensions below the target level\n if (query.filters && hierarchyInfo) {\n const targetIndex = hierarchyInfo.hierarchy.levels.indexOf(option.targetDimension)\n const lowerLevels = hierarchyInfo.hierarchy.levels.slice(targetIndex + 1)\n\n newQuery.filters = query.filters.filter(f => {\n if ('member' in f) {\n return !lowerLevels.includes(f.member)\n }\n return true\n })\n }\n\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: `By ${getDimensionLabel(option.targetDimension, metadata)}`,\n query: newQuery,\n dimension: option.targetDimension,\n hierarchy: option.hierarchy\n }\n }\n }\n\n // Fallback\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: 'Roll Up',\n query: newQuery\n }\n }\n}\n\n/**\n * Build a details query using the selected drillMember\n */\nfunction buildDetailsQuery(\n option: DrillOption,\n event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta\n): DrillResult {\n const { xValue } = event\n const measureName = option.measure || event.clickedField\n\n // Use the specific drillMember selected by the user\n const targetDimension = option.targetDimension\n if (!targetDimension) {\n throw new Error(`No targetDimension specified for details drill on measure ${measureName}`)\n }\n\n // Get labels for breadcrumb\n const targetDimensionLabel = getDimensionLabel(targetDimension, metadata)\n\n // Get the current x-axis dimension label for context\n const xAxisDimension = query.dimensions?.[0] || query.timeDimensions?.[0]?.dimension\n const xAxisLabel = xAxisDimension ? getDimensionLabel(xAxisDimension, metadata) : null\n\n // Check if the target dimension is a time dimension\n const isTargetTimeDimension = isTimeDimension(targetDimension, metadata)\n\n // Build a new query with the selected drillMember\n // If it's a time dimension, put it in timeDimensions; otherwise in dimensions\n const newQuery: CubeQuery = {\n measures: [measureName], // Keep the measure to show its value\n dimensions: isTargetTimeDimension ? [] : [targetDimension],\n timeDimensions: isTargetTimeDimension\n ? [{\n dimension: targetDimension,\n // Use granularity from original query or default to 'day'\n granularity: query.timeDimensions?.[0]?.granularity || 'day',\n // Preserve dateRange from original query (from dashboard filter)\n dateRange: query.timeDimensions?.[0]?.dateRange\n }]\n : query.timeDimensions, // Preserve existing time context for non-time drills\n filters: [...(query.filters || [])],\n limit: 100 // Reasonable default limit for detail view\n }\n\n // Add filter for the clicked data point\n if (xAxisDimension && xValue !== undefined && xValue !== null && xValue !== '') {\n const xFilter: Filter = {\n member: xAxisDimension,\n operator: 'equals',\n values: [String(xValue)]\n }\n newQuery.filters = [...(newQuery.filters || []), xFilter]\n }\n\n // Generate chart config that maps the selected dimension to xAxis\n // For time dimensions, use the timeDimension format in xAxis\n const chartConfig = {\n xAxis: [targetDimension], // Selected drillMember as x-axis\n yAxis: [measureName] // The measure being drilled\n }\n\n // Build a meaningful breadcrumb label\n // Format: \"By {dimension} ({clicked value context})\" or just \"By {dimension}\"\n const breadcrumbLabel = xValue !== undefined && xValue !== null && xValue !== ''\n ? `By ${targetDimensionLabel} (${xAxisLabel}: ${xValue})`\n : `By ${targetDimensionLabel}`\n\n return {\n query: newQuery,\n chartConfig,\n pathEntry: {\n id: generateId(),\n label: breadcrumbLabel,\n query: newQuery,\n filters: newQuery.filters,\n clickedValue: xValue,\n chartConfig\n }\n }\n}\n\n/**\n * Get a date range for a specific period value\n * @param periodValue - The period value (e.g., '2024-01', 'Q1 2024', '2024')\n * @param granularity - The current granularity\n * @returns Date range as [startDate, endDate] strings\n */\nfunction getDateRangeForPeriod(\n periodValue: string,\n granularity: string\n): [string, string] {\n // This is a simplified implementation\n // In production, you'd want more robust date parsing\n const date = new Date(periodValue)\n\n if (isNaN(date.getTime())) {\n // If we can't parse, return the value as-is (let the server handle it)\n return [periodValue, periodValue]\n }\n\n switch (granularity) {\n case 'year': {\n const year = date.getFullYear()\n return [`${year}-01-01`, `${year}-12-31`]\n }\n case 'quarter': {\n const year = date.getFullYear()\n const quarter = Math.floor(date.getMonth() / 3)\n const startMonth = quarter * 3\n const endMonth = startMonth + 2\n return [\n `${year}-${String(startMonth + 1).padStart(2, '0')}-01`,\n `${year}-${String(endMonth + 1).padStart(2, '0')}-${new Date(year, endMonth + 1, 0).getDate()}`\n ]\n }\n case 'month': {\n const year = date.getFullYear()\n const month = date.getMonth()\n const lastDay = new Date(year, month + 1, 0).getDate()\n return [\n `${year}-${String(month + 1).padStart(2, '0')}-01`,\n `${year}-${String(month + 1).padStart(2, '0')}-${lastDay}`\n ]\n }\n case 'week': {\n // Get the start of the week (Monday)\n const dayOfWeek = date.getDay()\n const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek\n const weekStart = new Date(date)\n weekStart.setDate(date.getDate() + diff)\n const weekEnd = new Date(weekStart)\n weekEnd.setDate(weekStart.getDate() + 6)\n return [\n weekStart.toISOString().split('T')[0],\n weekEnd.toISOString().split('T')[0]\n ]\n }\n case 'day':\n default: {\n const dateStr = date.toISOString().split('T')[0]\n return [dateStr, dateStr]\n }\n }\n}\n","/**\n * useDrillInteraction - Hook for managing drill-down interaction state\n * Coordinates between chart clicks, drill menu, and query updates\n */\n\nimport { useState, useCallback, useMemo } from 'react'\nimport type {\n ChartDataPointClickEvent,\n DrillOption,\n DrillPathEntry,\n DrillInteraction,\n UseDrillInteractionOptions\n} from '../types/drill'\nimport type { ChartAxisConfig, CubeQuery } from '../types'\nimport {\n buildDrillOptions,\n buildDrillQuery,\n getMeasureDrillMembers\n} from '../utils/drillQueryBuilder'\n\n/**\n * Hook for managing drill-down interaction\n *\n * @param options - Configuration options\n * @returns DrillInteraction object with handlers and state\n */\nexport function useDrillInteraction(options: UseDrillInteractionOptions): DrillInteraction {\n const {\n query,\n metadata,\n onQueryChange,\n chartConfig,\n dashboardFilters,\n dashboardFilterMapping,\n // onDashboardFilterChange is no longer used - dashboard-scope options removed\n enabled = true\n } = options\n\n // Menu state\n const [menuOpen, setMenuOpen] = useState(false)\n const [menuPosition, setMenuPosition] = useState<{ x: number; y: number } | null>(null)\n const [menuOptions, setMenuOptions] = useState<DrillOption[]>([])\n const [currentClickEvent, setCurrentClickEvent] = useState<ChartDataPointClickEvent | null>(null)\n\n // Drill path (breadcrumb navigation)\n const [drillPath, setDrillPath] = useState<DrillPathEntry[]>([])\n\n // Track the original granularity before any drilling (to detect when we should go back to root)\n const [originalGranularity, setOriginalGranularity] = useState<string | null>(null)\n\n // Track the original chart config to restore on drill up to root\n const [originalChartConfig, setOriginalChartConfig] = useState<ChartAxisConfig | null>(null)\n\n // Current chart config override (from drill details)\n const [currentChartConfig, setCurrentChartConfig] = useState<ChartAxisConfig | null>(null)\n\n // Track the complete original query to restore on drill up to root\n const [originalQuery, setOriginalQuery] = useState<CubeQuery | null>(null)\n\n /**\n * Check if any drill options are available for the current query/metadata\n */\n const drillEnabled = useMemo(() => {\n if (!enabled || !metadata) {\n return false\n }\n\n // Check if query has time dimensions (can drill time)\n const hasTimeDimensions = (query.timeDimensions?.length ?? 0) > 0\n\n // Check if query has dimensions that might be in hierarchies\n const hasDimensions = (query.dimensions?.length ?? 0) > 0\n\n // Check if any measures have drillMembers\n const hasDrillMembers = query.measures?.some(measure =>\n getMeasureDrillMembers(measure, metadata) !== null\n ) ?? false\n\n return hasTimeDimensions || hasDimensions || hasDrillMembers\n }, [enabled, metadata, query])\n\n /**\n * Check if any dimension in the query matches a dashboard filter\n */\n const hasDashboardFilterMatch = useMemo(() => {\n if (!dashboardFilters || !dashboardFilterMapping) {\n return false\n }\n\n // Check for universal time filter\n return dashboardFilters.some(f =>\n f.isUniversalTime && dashboardFilterMapping.includes(f.id)\n )\n }, [dashboardFilters, dashboardFilterMapping])\n\n /**\n * Handle data point click from chart\n */\n const handleDataPointClick = useCallback((event: ChartDataPointClickEvent) => {\n if (!enabled || !metadata) {\n return\n }\n\n // Build drill options for this click\n const options = buildDrillOptions(\n event,\n query,\n metadata,\n dashboardFilters,\n dashboardFilterMapping\n )\n\n if (options.length === 0) {\n return\n }\n\n // Store the click event and show menu\n setCurrentClickEvent(event)\n setMenuOptions(options)\n setMenuPosition(event.position)\n setMenuOpen(true)\n }, [enabled, metadata, query, dashboardFilters, dashboardFilterMapping])\n\n /**\n * Handle drill option selection\n */\n const handleOptionSelect = useCallback((option: DrillOption) => {\n if (!currentClickEvent || !metadata) {\n return\n }\n\n try {\n // For time dimension drilling, check if we should navigate back instead of creating new entries\n if (option.targetGranularity && drillPath.length > 0) {\n // Check if target granularity exists in drill path (navigate back to that level)\n const existingLevelIndex = drillPath.findIndex(entry =>\n entry.granularity === option.targetGranularity\n )\n\n if (existingLevelIndex !== -1) {\n // Navigate back to the existing level\n const targetIndex = existingLevelIndex + 1\n if (targetIndex < drillPath.length) {\n const newPath = drillPath.slice(0, targetIndex)\n const targetEntry = newPath[newPath.length - 1]\n setDrillPath(newPath)\n onQueryChange(targetEntry.query)\n setCurrentChartConfig(targetEntry.chartConfig || null)\n }\n setMenuOpen(false)\n setCurrentClickEvent(null)\n return\n }\n\n // Check if drilling back to the original granularity (return to root)\n if (originalGranularity && option.targetGranularity === originalGranularity) {\n // Restore the complete original query\n const restoredQuery = originalQuery || query\n setDrillPath([])\n // Restore original chart config (or clear if none was stored)\n setCurrentChartConfig(originalChartConfig)\n setOriginalChartConfig(null)\n setOriginalGranularity(null)\n setOriginalQuery(null)\n onQueryChange(restoredQuery)\n setMenuOpen(false)\n setCurrentClickEvent(null)\n return\n }\n }\n\n // For hierarchy dimension drilling, check if we should navigate back\n if (option.targetDimension && drillPath.length > 0) {\n const existingLevelIndex = drillPath.findIndex(entry =>\n entry.dimension === option.targetDimension\n )\n\n if (existingLevelIndex !== -1) {\n // Navigate back to the existing level\n const targetIndex = existingLevelIndex + 1\n if (targetIndex < drillPath.length) {\n const newPath = drillPath.slice(0, targetIndex)\n const targetEntry = newPath[newPath.length - 1]\n setDrillPath(newPath)\n onQueryChange(targetEntry.query)\n setCurrentChartConfig(targetEntry.chartConfig || null)\n }\n setMenuOpen(false)\n setCurrentClickEvent(null)\n return\n }\n }\n\n // Build the drill query\n const result = buildDrillQuery(option, currentClickEvent, query, metadata)\n\n // Track original state when first drilling\n if (drillPath.length === 0) {\n // Store the complete original query for restoration on drill back to root\n setOriginalQuery(query)\n\n // Store original chart config for restoration on drill back to root\n if (chartConfig) {\n setOriginalChartConfig(chartConfig)\n }\n\n // Track original granularity for detecting drill-back-to-root via time granularity\n if (option.targetGranularity && query.timeDimensions?.[0]) {\n const currentGran = query.timeDimensions[0].granularity\n if (currentGran) {\n setOriginalGranularity(currentGran)\n }\n }\n }\n\n // Add to drill path and update query\n setDrillPath(prev => [...prev, result.pathEntry])\n onQueryChange(result.query)\n\n // Update chart config if provided (e.g., when drilling to details)\n if (result.chartConfig) {\n setCurrentChartConfig(result.chartConfig)\n }\n } catch (error) {\n console.error('Error building drill query:', error)\n }\n\n // Close menu\n setMenuOpen(false)\n setCurrentClickEvent(null)\n }, [currentClickEvent, metadata, query, drillPath, originalGranularity, originalQuery, onQueryChange, chartConfig, originalChartConfig])\n\n /**\n * Close the drill menu\n */\n const closeMenu = useCallback(() => {\n setMenuOpen(false)\n setMenuPosition(null)\n setMenuOptions([])\n setCurrentClickEvent(null)\n }, [])\n\n /**\n * Navigate back one level in the drill path\n */\n const navigateBack = useCallback(() => {\n if (drillPath.length === 0) {\n return\n }\n\n if (drillPath.length === 1) {\n // Going back to root - restore the complete original query\n // Use stored originalQuery to fully restore dimensions, measures, filters, etc.\n const restoredQuery = originalQuery || query\n\n setDrillPath([])\n // Restore original chart config (or clear if none was stored)\n setCurrentChartConfig(originalChartConfig)\n setOriginalChartConfig(null)\n setOriginalGranularity(null)\n setOriginalQuery(null)\n onQueryChange(restoredQuery)\n } else {\n // Go back to previous level\n const newPath = drillPath.slice(0, -1)\n const previousEntry = newPath[newPath.length - 1]\n setDrillPath(newPath)\n onQueryChange(previousEntry.query)\n // Restore chart config from previous entry\n setCurrentChartConfig(previousEntry.chartConfig || null)\n }\n }, [drillPath, onQueryChange, originalChartConfig, originalQuery, query])\n\n /**\n * Navigate to a specific level in the drill path\n */\n const navigateToLevel = useCallback((index: number) => {\n if (index <= 0) {\n // Navigate to root - restore the complete original query\n // Use stored originalQuery to fully restore dimensions, measures, filters, etc.\n const restoredQuery = originalQuery || query\n\n setDrillPath([])\n // Restore original chart config (or clear if none was stored)\n setCurrentChartConfig(originalChartConfig)\n setOriginalChartConfig(null)\n setOriginalGranularity(null)\n setOriginalQuery(null)\n onQueryChange(restoredQuery)\n } else if (index < drillPath.length) {\n // Navigate to a specific level\n const newPath = drillPath.slice(0, index)\n const targetEntry = newPath[newPath.length - 1]\n setDrillPath(newPath)\n onQueryChange(targetEntry.query)\n // Restore chart config from target entry\n setCurrentChartConfig(targetEntry.chartConfig || null)\n }\n }, [drillPath, onQueryChange, originalChartConfig, originalQuery, query])\n\n return {\n handleDataPointClick,\n menuOpen,\n menuPosition,\n menuOptions,\n handleOptionSelect,\n closeMenu,\n drillPath,\n navigateBack,\n navigateToLevel,\n drillEnabled,\n hasDashboardFilterMatch,\n currentChartConfig\n }\n}\n\nexport default useDrillInteraction\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// Time granularity type for drill-down support\nexport type TimeGranularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'\n\n// Cube metadata types\nexport interface CubeMetaMeasure {\n name: string\n title: string\n shortTitle: string\n type: string\n /** Dimension names shown when drilling into this measure */\n drillMembers?: string[]\n}\n\nexport interface CubeMetaDimension {\n name: string\n title: string\n shortTitle: string\n type: string\n /** Supported granularities for time dimensions (for time-based drill-down) */\n granularities?: TimeGranularity[]\n}\n\n// Legacy type alias for backward compatibility\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\n/**\n * Hierarchy metadata for structured drill-down paths\n */\nexport interface CubeMetaHierarchy {\n /** Unique identifier for the hierarchy */\n name: string\n /** Display title for the hierarchy */\n title: string\n /** Cube name this hierarchy belongs to */\n cubeName: string\n /** Full dimension names in order from least to most granular */\n levels: string[]\n}\n\nexport interface CubeMetaCube {\n name: string\n title: string\n description?: string\n measures: CubeMetaMeasure[]\n dimensions: CubeMetaDimension[]\n segments: CubeMetaField[]\n relationships?: CubeMetaRelationship[]\n /** Hierarchies for structured drill-down paths */\n hierarchies?: CubeMetaHierarchy[]\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 | 'boxPlot'\n | 'waterfall'\n | 'candlestick'\n | 'measureProfile'\n | 'gauge'\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 ThresholdBand {\n value: number\n color: string\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 transparentBackground?: boolean // Remove card background, border, shadow, padding (for section headers)\n autoHeight?: boolean // Card shrinks to fit content instead of filling grid cell (default: true for markdown)\n accentBorder?: 'none' | 'left' | 'top' | 'bottom' // Accent-colored border on specified side\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 // Waterfall chart specific display options\n showTotal?: boolean\n showConnectorLine?: boolean\n showDataLabels?: boolean\n\n // Candlestick chart specific display options\n bullColor?: string\n bearColor?: string\n showWicks?: boolean\n rangeMode?: 'ohlc' | 'range'\n\n // Measure profile chart specific display options\n showReferenceLineAtZero?: boolean\n lineType?: 'monotone' | 'linear' | 'step'\n\n // Gauge chart specific display options\n minValue?: number\n maxValue?: number\n thresholds?: string | ThresholdBand[]\n showCenterLabel?: boolean\n showPercentage?: boolean\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 drillState?: {\n isDrilling: boolean\n drillPath: Array<{\n id: string\n label: string\n clickedValue?: unknown\n dimension?: string\n granularity?: string\n hierarchy?: string\n }>\n currentDrillDepth: number\n originalQuery: any\n activeQuery: any\n }\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 // Drill-down support\n /** Handler for data point clicks - fires ChartDataPointClickEvent */\n onDataPointClick?: (event: import('./types/drill').ChartDataPointClickEvent) => void\n /** Whether drill-down is enabled (shows pointer cursor on clickable elements) */\n drillEnabled?: boolean\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// Re-export drill types\nexport type {\n ChartDataPointClickEvent,\n DrillOptionType,\n DrillScope,\n DrillOption,\n DrillPathEntry,\n DrillResult,\n UseDrillInteractionOptions,\n DrillInteraction,\n DrillMenuProps,\n DrillBreadcrumbProps,\n} from './types/drill'\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 */\nexport function 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, useState } 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 { useCubeMeta } from '../providers/CubeMetaContext'\nimport ChartErrorBoundary from './ChartErrorBoundary'\nimport LoadingIndicator from './LoadingIndicator'\nimport { DrillMenu } from './DrillMenu'\nimport { DrillBreadcrumb } from './DrillBreadcrumb'\nimport { useDrillInteraction } from '../hooks/useDrillInteraction'\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\n // ========== Drill-Down State ==========\n // Track the current drilled query (null means using the original/parsed query)\n const [drilledQuery, setDrilledQuery] = useState<CubeQuery | null>(null)\n\n // Reset drilled query when the base query changes (e.g., dashboard filters change)\n const queryObjectJson = queryObject ? JSON.stringify(queryObject) : null\n const previousQueryObjectJson = useRef<string | null>(null)\n useEffect(() => {\n if (queryObjectJson !== previousQueryObjectJson.current) {\n previousQueryObjectJson.current = queryObjectJson\n // Reset drill state when base query changes\n if (drilledQuery) {\n setDrilledQuery(null)\n }\n }\n }, [queryObjectJson, drilledQuery])\n\n // The active query is either the drilled query or the original parsed query\n const activeQuery = drilledQuery || queryObject\n\n // Get metadata for drill options (only for single query mode)\n const { meta } = useCubeMeta()\n\n // Drill interaction hook - only enabled for single query mode\n const drill = useDrillInteraction({\n query: activeQuery || { measures: [], dimensions: [] },\n metadata: meta,\n onQueryChange: (newQuery) => {\n setDrilledQuery(newQuery)\n },\n chartConfig,\n dashboardFilters,\n dashboardFilterMapping,\n enabled: !isMultiQuery && !isFunnelMode && !isFlowMode && !isRetentionMode && !!activeQuery\n })\n\n // Handle navigating back to root - restore the original query\n const handleNavigateBack = useCallback(() => {\n if (drill.drillPath.length === 1) {\n // Going back to root - restore original query\n setDrilledQuery(null)\n // Clear the drill path by calling the hook's navigateBack\n }\n drill.navigateBack()\n }, [drill])\n\n // Handle navigating to a specific level\n const handleNavigateToLevel = useCallback((index: number) => {\n if (index === 0) {\n // Navigate to root - restore original query\n setDrilledQuery(null)\n }\n drill.navigateToLevel(index)\n }, [drill])\n\n const shouldSkipSingle = !activeQuery || 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 // Use activeQuery to support drill-down (which replaces the query temporarily)\n const singleQueryResult = useCubeLoadQuery(activeQuery, {\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 (activeQuery) {\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(activeQuery)\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, activeQuery, 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 // Include drill state in debug info if drilling is active\n const drillDebugInfo = drill.drillPath.length > 0 ? {\n isDrilling: true,\n drillPath: drill.drillPath.map(entry => ({\n id: entry.id,\n label: entry.label,\n clickedValue: entry.clickedValue,\n dimension: entry.dimension,\n granularity: entry.granularity,\n hierarchy: entry.hierarchy\n })),\n currentDrillDepth: drill.drillPath.length,\n originalQuery: queryObject,\n activeQuery: activeQuery\n } : undefined\n\n onDebugDataReadyRef.current({\n chartConfig: drill.currentChartConfig || chartConfig || {},\n displayConfig: displayConfig || {},\n queryObject: activeQuery || queryObject,\n data,\n chartType,\n cacheInfo: resultSet.cacheInfo?.(),\n drillState: drillDebugInfo\n })\n }\n }\n }, [chartConfig, displayConfig, queryObject, activeQuery, resultSet, chartType, error, isFunnelMode, isFlowMode, isRetentionMode, multiQueryData, serverFunnelQuery, serverFlowQuery, serverRetentionQuery, flowChartData, retentionChartData, flowQueryResult.cacheInfo, funnelQueryResult.cacheInfo, retentionQueryResult.cacheInfo, drill.drillPath, drill.currentChartConfig]) // 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=\"dc: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 {activeQuery ? JSON.stringify(activeQuery, 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: drill.currentChartConfig || 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 // Check if we're in a drilled state - show more helpful message with back option\n const isDrilledState = drill.drillPath.length > 0\n\n return (\n <div ref={inViewRef} className=\"dc:flex dc:flex-col dc:w-full\" style={{ height }}>\n {/* Show breadcrumb when drilling even if no data */}\n {isDrilledState && (\n <div className=\"dc:mb-2 dc:flex-shrink-0\">\n <DrillBreadcrumb\n path={drill.drillPath}\n onNavigate={handleNavigateBack}\n onLevelClick={handleNavigateToLevel}\n />\n </div>\n )}\n <div className=\"dc:flex dc:items-center dc:justify-center dc:flex-1 text-dc-text-muted\">\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {isDrilledState\n ? 'No data points to display for the current filter'\n : 'Invalid query or no results'\n }\n </div>\n </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=\"dc: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 // Determine if drill is enabled for this chart (only single query mode)\n const isDrillEnabledForChart = !isMultiQuery && !isFunnelMode && !isFlowMode && !isRetentionMode && drill.drillEnabled\n\n // Use drill chart config if available, otherwise fall back to original\n const effectiveChartConfig = (isDrillEnabledForChart && drill.currentChartConfig)\n ? drill.currentChartConfig\n : chartConfig\n\n return (\n <LazyChart\n chartType={effectiveChartType}\n data={chartData}\n chartConfig={effectiveChartConfig}\n displayConfig={displayConfig}\n queryObject={activeQuery ?? undefined}\n height={chartHeight}\n colorPalette={colorPalette}\n onDataPointClick={isDrillEnabledForChart ? drill.handleDataPointClick : undefined}\n drillEnabled={isDrillEnabledForChart}\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=\"dc: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 // Check if drill is enabled for this portlet\n const isDrillEnabled = !isMultiQuery && !isFunnelMode && !isFlowMode && !isRetentionMode && drill.drillEnabled\n\n return (\n <div ref={inViewRef} className=\"dc:w-full dc:h-full dc:relative\">\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: chartType === 'markdown' ? undefined : '200px' }}>\n {/* Drill breadcrumb - shows when drilling into data */}\n {isDrillEnabled && drill.drillPath.length > 0 && (\n <div className=\"dc:mb-2 dc:flex-shrink-0\">\n <DrillBreadcrumb\n path={drill.drillPath}\n onNavigate={handleNavigateBack}\n onLevelClick={handleNavigateToLevel}\n />\n </div>\n )}\n\n {/* Chart content */}\n <div className=\"dc:flex-1 dc:min-h-0\">\n {renderChart()}\n </div>\n </div>\n </ChartErrorBoundary>\n\n {/* Drill menu - positioned absolutely near clicked point */}\n {isDrillEnabled && drill.menuOpen && drill.menuPosition && (\n <DrillMenu\n options={drill.menuOptions}\n position={drill.menuPosition}\n onSelect={drill.handleOptionSelect}\n onClose={drill.closeMenu}\n />\n )}\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 * CodeBlock Component\n * Displays syntax-highlighted code with copy-to-clipboard functionality\n */\n\nimport React, { useState, useEffect, useRef } from 'react'\nimport { getIcon } from '../../icons'\nimport { getSyntaxHighlighter, loadSyntaxHighlighter } from '../../utils/syntaxHighlighting'\nimport './CodeBlock.css'\n\ninterface CodeBlockProps {\n code: string\n language: 'json' | 'sql'\n title?: string\n maxHeight?: string\n height?: string\n className?: string\n /** Additional content to render on the right side of the header (before Copy button) */\n headerRight?: React.ReactNode\n}\n\nexport const CodeBlock: React.FC<CodeBlockProps> = ({\n code,\n language,\n title,\n maxHeight = '16rem',\n height,\n className = '',\n headerRight\n}) => {\n const [copied, setCopied] = useState(false)\n const codeRef = useRef<HTMLElement>(null)\n const CopyIcon = getIcon('copy')\n const CheckIcon = getIcon('check')\n\n // Apply syntax highlighting\n useEffect(() => {\n if (!codeRef.current) return\n const element = codeRef.current\n let isActive = true\n\n element.textContent = code\n\n loadSyntaxHighlighter()\n .then(() => {\n if (!isActive) return\n const hljs = getSyntaxHighlighter()\n if (!hljs) return\n element.innerHTML = hljs.highlight(code, { language }).value\n })\n .catch(() => {\n if (isActive) {\n element.textContent = code\n }\n })\n\n return () => {\n isActive = false\n }\n }, [code, language])\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(code)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch {\n // Fallback for older browsers\n const textArea = document.createElement('textarea')\n textArea.value = code\n textArea.style.position = 'fixed'\n textArea.style.left = '-999999px'\n document.body.appendChild(textArea)\n textArea.select()\n document.execCommand('copy')\n document.body.removeChild(textArea)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n }\n }\n\n return (\n <div className={`dc:relative ${className}`}>\n {/* Header with title, optional extra controls, and copy button */}\n <div className=\"dc:flex dc:items-center dc:justify-between dc:mb-2 dc:gap-2\">\n {title && (\n <h4 className=\"dc:text-sm dc:font-semibold text-dc-text\">{title}</h4>\n )}\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:ml-auto\">\n {headerRight}\n <button\n onClick={handleCopy}\n className=\"dc:px-2 dc:py-1 dc:text-xs dc:rounded hover:bg-dc-surface-secondary dc:border border-dc-border dc:transition-colors dc:flex dc:items-center dc:gap-1.5\"\n title={copied ? 'Copied!' : 'Copy to clipboard'}\n >\n {copied ? (\n <>\n <CheckIcon className=\"dc:w-3.5 dc:h-3.5 text-dc-success\" />\n <span className=\"text-dc-success\">Copied</span>\n </>\n ) : (\n <>\n <CopyIcon className=\"dc:w-3.5 dc:h-3.5 text-dc-text-secondary\" />\n <span className=\"text-dc-text-secondary\">Copy</span>\n </>\n )}\n </button>\n </div>\n </div>\n\n {/* Code block with syntax highlighting */}\n <div\n className=\"bg-dc-surface-secondary dc:border border-dc-border dc:rounded dc:overflow-auto\"\n style={height ? { height, minHeight: height, maxHeight: height } : { maxHeight }}\n >\n <pre className=\"dc:p-3 dc:text-xs dc:m-0\">\n <code\n ref={codeRef}\n className={`hljs language-${language}`}\n >\n {code}\n </code>\n </pre>\n </div>\n </div>\n )\n}\n\nexport default CodeBlock\n","import { useState, useEffect } from 'react'\nimport type { FlowChartData } from '../types/flow'\nimport type { RetentionChartData } from '../types/retention'\nimport { CodeBlock } from '../shared/components/CodeBlock'\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\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 const fieldAnalysis = [\n `xAxis: ${Array.isArray(chartConfig?.xAxis) ? `[${chartConfig.xAxis.join(', ')}]` : JSON.stringify(chartConfig?.xAxis ?? null)}`,\n `yAxis: ${Array.isArray(chartConfig?.yAxis) ? `[${chartConfig.yAxis.join(', ')}]` : JSON.stringify(chartConfig?.yAxis ?? null)}`,\n `series: ${Array.isArray(chartConfig?.series) ? `[${chartConfig.series.join(', ')}]` : JSON.stringify(chartConfig?.series ?? null)}`,\n ...(chartConfig?.sizeField ? [`sizeField: ${JSON.stringify(chartConfig.sizeField)}`] : []),\n ...(chartConfig?.colorField ? [`colorField: ${JSON.stringify(chartConfig.colorField)}`] : []),\n ].join('\\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 <CodeBlock\n code={chartType}\n language=\"json\"\n title=\"Chart Type\"\n maxHeight=\"3rem\"\n />\n\n <CodeBlock\n code={fieldAnalysis}\n language=\"json\"\n title=\"Field Analysis\"\n maxHeight=\"8rem\"\n />\n\n <CodeBlock\n code={JSON.stringify(chartConfig, null, 2)}\n language=\"json\"\n title=\"Chart Config\"\n className=\"dc:lg:col-span-2\"\n />\n\n <CodeBlock\n code={JSON.stringify(displayConfig, null, 2)}\n language=\"json\"\n title=\"Display Config\"\n className=\"dc:lg:col-span-2\"\n />\n\n <CodeBlock\n code={JSON.stringify(queryObject, null, 2)}\n language=\"json\"\n title=\"Query Object\"\n className=\"dc:lg:col-span-2\"\n />\n\n <CodeBlock\n code={JSON.stringify(Array.isArray(data) ? data.slice(0, 3) : data, null, 2)}\n language=\"json\"\n title=\"Data Sample (first 3 rows)\"\n className=\"dc:lg:col-span-2\"\n />\n\n <div className=\"dc:lg:col-span-2\">\n <h4 className=\"dc:text-sm dc:font-semibold text-dc-text dc:mb-2\">Cache Status</h4>\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 /** Whether text portlet modal is open */\n isTextModalOpen: boolean\n /** Portlet being edited in text modal (null = adding new) */\n editingTextPortlet: PortletConfig | 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 text portlet modal (optionally with a portlet to edit) */\n openTextModal: (portlet?: PortletConfig | null) => void\n /** Close text portlet modal */\n closeTextModal: () => 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 isTextModalOpen: false,\n editingTextPortlet: 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 openTextModal: (portlet) =>\n set({\n isTextModalOpen: true,\n editingTextPortlet: portlet ?? null,\n }),\n\n closeTextModal: () =>\n set({\n isTextModalOpen: false,\n editingTextPortlet: 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 isTextModalOpen: state.isTextModalOpen,\n editingTextPortlet: state.editingTextPortlet,\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 openTextModal: state.openTextModal,\n closeTextModal: state.closeTextModal,\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 openTextModal: state.openTextModal,\n closeTextModal: state.closeTextModal,\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 { ChartType, DashboardFilter, DashboardLayoutMode, 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 (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 layoutMode?: DashboardLayoutMode\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.layoutMode !== nextProps.layoutMode ||\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\ninterface PortletChartBodyProps {\n isTransparent: boolean\n setChartContainerRef: (el: HTMLDivElement | null) => void\n setPortletComponentRef: (el: { refresh: (options?: { bustCache?: boolean }) => void } | null) => void\n renderQuery: string\n renderChartType: ChartType\n renderChartConfig: unknown\n renderDisplayConfig: unknown\n dashboardFilters?: DashboardFilter[]\n dashboardFilterMapping?: string[]\n eagerLoad: boolean\n title: string\n isMarkdownAutoHeight: boolean\n colorPalette?: ColorPalette\n loadingComponent?: ReactNode\n onDebugDataReady: (data: PortletDebugDataEntry) => void\n}\n\nconst PortletChartBody = React.memo(function PortletChartBody({\n isTransparent,\n setChartContainerRef,\n setPortletComponentRef,\n renderQuery,\n renderChartType,\n renderChartConfig,\n renderDisplayConfig,\n dashboardFilters,\n dashboardFilterMapping,\n eagerLoad,\n title,\n isMarkdownAutoHeight,\n colorPalette,\n loadingComponent,\n onDebugDataReady,\n}: PortletChartBodyProps) {\n return (\n <div\n ref={setChartContainerRef}\n className={`dc:flex-1 dc:min-h-0 dc:flex dc:flex-col${isTransparent ? '' : ' dc:px-2 dc:py-3 dc:md:px-4 dc:md:py-4'}`}\n >\n <AnalyticsPortlet\n ref={setPortletComponentRef}\n query={renderQuery}\n chartType={renderChartType}\n chartConfig={renderChartConfig as Record<string, unknown> | undefined}\n displayConfig={renderDisplayConfig as Record<string, unknown> | undefined}\n dashboardFilters={dashboardFilters}\n dashboardFilterMapping={dashboardFilterMapping}\n eagerLoad={eagerLoad}\n title={title}\n height={isMarkdownAutoHeight ? 'auto' : '100%'}\n colorPalette={colorPalette}\n loadingComponent={loadingComponent}\n onDebugDataReady={onDebugDataReady}\n />\n </div>\n )\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 layoutMode = 'grid',\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 // Markdown-specific display modes\n // isTransparent gated on !isEditMode so chrome is visible for editing\n const isMarkdown = renderChartType === 'markdown'\n const markdownAutoHeightRequested = isMarkdown && (renderDisplayConfig?.autoHeight ?? true)\n const isMarkdownAutoHeight = layoutMode !== 'grid' && markdownAutoHeightRequested\n const isTransparentContent = isMarkdown && !!renderDisplayConfig?.transparentBackground\n const isTransparent = isTransparentContent && !isEditMode\n // Hide header when: explicitly set to hide, OR markdown with no title\n const shouldHideHeader = renderChartType === 'markdown'\n ? (renderDisplayConfig?.hideHeader ?? true) || !!renderDisplayConfig?.transparentBackground || !portlet.title\n : (renderDisplayConfig?.hideHeader ?? false)\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>(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 isTransparent\n ? 'dc:flex dc:flex-col dc:transition-all'\n : 'bg-dc-surface dc:border dc:rounded-lg dc:flex dc:flex-col dc:transition-all',\n isMarkdownAutoHeight ? '' : 'dc:h-full',\n isInSelectionMode ? 'dc: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 const handleSetPortletRef = useCallback((el: HTMLDivElement | null) => {\n setPortletRef(portlet.id, el)\n }, [portlet.id, setPortletRef])\n\n const handleSetPortletComponentRef = useCallback((el: { refresh: (options?: { bustCache?: boolean }) => void } | null) => {\n setPortletComponentRef(portlet.id, el)\n }, [portlet.id, setPortletComponentRef])\n\n const handleSetChartContainerRef = useCallback((el: HTMLDivElement | null) => {\n chartContainerRef.current = el\n }, [])\n\n return (\n <div\n data-portlet-id={portlet.id}\n ref={handleSetPortletRef}\n className={mergedContainerClassName}\n style={{\n boxShadow: isTransparent ? 'none' : 'var(--dc-shadow-sm)',\n borderColor: isTransparent\n ? 'transparent'\n : isInSelectionMode && hasSelectedFilter\n ? 'var(--dc-primary)'\n : 'var(--dc-border)',\n borderWidth: isTransparent ? '0' : isInSelectionMode && hasSelectedFilter ? '2px' : '1px',\n backgroundColor: isTransparent\n ? 'transparent'\n : 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 {(!shouldHideHeader || 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 <PortletChartBody\n isTransparent={isTransparentContent}\n setChartContainerRef={handleSetChartContainerRef}\n setPortletComponentRef={handleSetPortletComponentRef}\n renderQuery={renderQuery}\n renderChartType={renderChartType}\n renderChartConfig={renderChartConfig}\n renderDisplayConfig={renderDisplayConfig}\n dashboardFilters={dashboardFilters}\n dashboardFilterMapping={portlet.dashboardFilterMapping}\n eagerLoad={portlet.eagerLoad ?? configEagerLoad ?? false}\n title={portlet.title}\n isMarkdownAutoHeight={isMarkdownAutoHeight}\n colorPalette={colorPalette}\n loadingComponent={loadingComponent}\n onDebugDataReady={handleDebugDataReady}\n />\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'\nimport { ensureAnalysisConfig } from '../utils/configMigration'\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 // Row auto-height only when all columns are markdown and request autoHeight.\n const isAutoHeightRow = row.columns.length > 0 && row.columns.every(col => {\n const portlet = portletMap.get(col.portletId)\n if (!portlet) return false\n const normalized = ensureAnalysisConfig(portlet)\n const chartMode = normalized.analysisConfig.charts[normalized.analysisConfig.analysisType]\n return chartMode?.chartType === 'markdown' && (chartMode.displayConfig?.autoHeight ?? true)\n })\n const rowHeight = isAutoHeightRow ? undefined : 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={{\n height: rowHeight ?? 'auto',\n paddingLeft,\n paddingRight,\n }}\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 return (\n <div\n key={portlet.id}\n className=\"dc-row-layout-column-wrapper dc-row-layout-column\"\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 style={{\n flex: `0 0 ${width}px`,\n maxWidth: `${width}px`\n }}\n >\n {renderPortlet(portlet)}\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${isAutoHeightRow ? ' dc-row-resize-handle-drop-only' : ''}${activeDropKey === `row-insert-${rowIndex + 1}` ? ' dc-drop-zone-active' : ''}`}\n onMouseDown={isAutoHeightRow ? undefined : (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')\nfunction TextIcon({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n <text x=\"1\" y=\"20\" fontSize=\"20\" fontWeight=\"700\" fontFamily=\"serif\">T</text>\n <text x=\"14\" y=\"20\" fontSize=\"13\" fontWeight=\"600\" fontFamily=\"serif\">t</text>\n </svg>\n )\n}\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 /** Add new text portlet */\n onAddText?: () => 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 onAddText\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 / Add Text - only in edit mode */}\n {isEditMode && (\n <>\n <div className=\"dc:w-full dc:h-px bg-dc-border dc:my-0.5\" />\n {onAddText && (\n <ToolbarButton\n icon={TextIcon}\n tooltip=\"Add Text\"\n onClick={onAddText}\n />\n )}\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 dc: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'\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 /** @deprecated Dashboard filters are no longer merged into the editor - they are applied at execution time */\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}: 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 // =========================================================================\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\n // Dashboard filters are NOT merged here - they are applied at execution time\n // by AnalyticsPortlet.tsx. The editor shows only the portlet's own filters\n // to prevent dashboard filters from leaking into saved portlet config.\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\n if (derivedConfig.analysisType === 'funnel') {\n return query as CubeQuery | MultiQueryConfig\n }\n\n // Return query as-is (single CubeQuery or MultiQueryConfig)\n return query as CubeQuery | MultiQueryConfig\n }, [derivedConfig])\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: boolean\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 * AnalysisDisplayConfigPanel Component\n *\n * A panel for configuring chart display options (legend, grid, tooltip, etc.)\n * Extracted from AnalysisChartConfigPanel to be shown in its own tab.\n */\n\nimport { useState, useCallback, useEffect } from 'react'\nimport SectionHeading from './SectionHeading'\nimport { useChartConfig } from '../../charts/lazyChartConfigRegistry'\nimport type { ChartType, ChartDisplayConfig, ColorPalette, AxisFormatConfig } from '../../types'\nimport { AxisFormatControls } from '../charts/AxisFormatControls'\n\ninterface AnalysisDisplayConfigPanelProps {\n chartType: ChartType\n displayConfig: ChartDisplayConfig\n colorPalette?: ColorPalette\n onDisplayConfigChange: (config: ChartDisplayConfig) => void\n /** Keys to exclude from displayOptionsConfig rendering (e.g., ['content'] when content is managed elsewhere) */\n excludeKeys?: string[]\n}\n\n/**\n * StringArrayInput - A textarea that edits an array of strings\n * Uses local state while editing and only updates on blur\n */\nfunction StringArrayInput({\n label,\n value,\n onChange,\n placeholder,\n description,\n}: {\n label: string\n value: string[]\n onChange: (value: string[]) => void\n placeholder?: string\n description?: string\n}) {\n // Local state for textarea editing\n const [localText, setLocalText] = useState(() => value.join('\\n'))\n\n // Sync local state when external value changes (e.g., from undo/redo or load)\n useEffect(() => {\n const externalText = value.join('\\n')\n setLocalText(externalText)\n }, [value])\n\n const handleBlur = useCallback(() => {\n // Convert text to array, filtering empty strings\n const arrayValue = localText\n .split('\\n')\n .map(s => s.trim())\n .filter(s => s.length > 0)\n onChange(arrayValue)\n }, [localText, onChange])\n\n return (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{label}</label>\n <textarea\n value={localText}\n onChange={(e) => setLocalText(e.target.value)}\n onBlur={handleBlur}\n placeholder={placeholder}\n rows={4}\n className=\"dc:w-full dc:px-2 dc:py-1 dc:text-sm dc:border border-dc-border dc:rounded-sm focus:ring-dc-accent focus:border-dc-accent bg-dc-surface text-dc-text dc:resize-y\"\n />\n {description && (\n <p className=\"dc:text-xs text-dc-text-muted\">{description}</p>\n )}\n </div>\n )\n}\n\nexport default function AnalysisDisplayConfigPanel({\n chartType,\n displayConfig,\n colorPalette,\n onDisplayConfigChange,\n excludeKeys,\n}: AnalysisDisplayConfigPanelProps) {\n // Get configuration for current chart type\n const { config: chartTypeConfig, loaded: chartConfigLoaded } = useChartConfig(chartType)\n\n if (!chartConfigLoaded) {\n return (\n <div className=\"dc:text-center text-dc-text-muted dc:text-sm dc:py-4\">\n Loading display options...\n </div>\n )\n }\n\n // Check if we have any display options to show\n const hasDisplayOptions =\n (chartTypeConfig.displayOptions && chartTypeConfig.displayOptions.length > 0) ||\n (chartTypeConfig.displayOptionsConfig && chartTypeConfig.displayOptionsConfig.length > 0)\n\n if (!hasDisplayOptions) {\n return (\n <div className=\"dc:text-center text-dc-text-muted dc:text-sm dc:py-4\">\n <p>No display options available for this chart type.</p>\n </div>\n )\n }\n\n return (\n <div className=\"dc:space-y-6\">\n <div>\n <SectionHeading className=\"dc:mb-2\">Display Options</SectionHeading>\n <div className=\"dc:space-y-2\">\n {/* Backward compatibility: Simple boolean display options */}\n {chartTypeConfig.displayOptions?.includes('showLegend') && (\n <label className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"checkbox\"\n checked={displayConfig.showLegend ?? true}\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n showLegend: e.target.checked\n })\n }\n className=\"dc:rounded border-dc-border focus:ring-dc-accent\"\n style={{ color: 'var(--dc-primary)' }}\n />\n <span className=\"dc:text-sm text-dc-text\">Show Legend</span>\n </label>\n )}\n\n {chartTypeConfig.displayOptions?.includes('showGrid') && (\n <label className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"checkbox\"\n checked={displayConfig.showGrid ?? true}\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n showGrid: e.target.checked\n })\n }\n className=\"dc:rounded border-dc-border focus:ring-dc-accent\"\n style={{ color: 'var(--dc-primary)' }}\n />\n <span className=\"dc:text-sm text-dc-text\">Show Grid</span>\n </label>\n )}\n\n {chartTypeConfig.displayOptions?.includes('showTooltip') && (\n <label className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"checkbox\"\n checked={displayConfig.showTooltip ?? true}\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n showTooltip: e.target.checked\n })\n }\n className=\"dc:rounded border-dc-border focus:ring-dc-accent\"\n style={{ color: 'var(--dc-primary)' }}\n />\n <span className=\"dc:text-sm text-dc-text\">Show Tooltip</span>\n </label>\n )}\n\n {chartTypeConfig.displayOptions?.includes('stacked') && (\n <label className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"checkbox\"\n checked={displayConfig.stacked ?? false}\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n stacked: e.target.checked\n })\n }\n className=\"dc:rounded border-dc-border focus:ring-dc-accent\"\n style={{ color: 'var(--dc-primary)' }}\n />\n <span className=\"dc:text-sm text-dc-text\">Stacked</span>\n </label>\n )}\n\n {chartTypeConfig.displayOptions?.includes('hideHeader') && (\n <label className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"checkbox\"\n checked={displayConfig.hideHeader ?? false}\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n hideHeader: e.target.checked\n })\n }\n className=\"dc:rounded border-dc-border focus:ring-dc-accent\"\n style={{ color: 'var(--dc-primary)' }}\n />\n <span className=\"dc:text-sm text-dc-text\">Hide Header</span>\n </label>\n )}\n\n {/* New structured display options */}\n {chartTypeConfig.displayOptionsConfig?.filter(option => !excludeKeys?.includes(option.key)).map((option) => (\n <div key={option.key} className={`dc:space-y-1 ${option.type === 'axisFormat' ? 'dc:mt-6 dc:pt-2' : ''}`}>\n {option.type === 'boolean' && (\n <label className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"checkbox\"\n checked={\n (displayConfig[option.key as keyof ChartDisplayConfig] as boolean) ??\n option.defaultValue ??\n false\n }\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: e.target.checked\n })\n }\n className=\"dc:rounded border-dc-border focus:ring-dc-accent\"\n style={{ color: 'var(--dc-primary)' }}\n />\n <span className=\"dc:text-sm text-dc-text\">{option.label}</span>\n </label>\n )}\n\n {option.type === 'string' && (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">\n {option.label}\n {option.key === 'content' && (\n <span className=\"dc:text-xs text-dc-text-muted dc:ml-1\">\n (only headers, lists and links)\n </span>\n )}\n </label>\n {option.key === 'content' ? (\n <textarea\n value={\n (displayConfig[option.key as keyof ChartDisplayConfig] as string) ??\n option.defaultValue ??\n ''\n }\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: e.target.value\n })\n }\n placeholder={option.placeholder}\n rows={8}\n className=\"dc:w-full dc:px-2 dc:py-1 dc:text-sm dc:border border-dc-border dc:rounded-sm focus:ring-dc-accent focus:border-dc-accent dc:font-mono dc:resize-y bg-dc-surface text-dc-text\"\n />\n ) : (\n <input\n type=\"text\"\n value={\n (displayConfig[option.key as keyof ChartDisplayConfig] as string) ??\n option.defaultValue ??\n ''\n }\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: e.target.value\n })\n }\n placeholder={option.placeholder}\n className=\"dc:w-full dc:px-2 dc:py-1 dc:text-sm dc:border border-dc-border dc:rounded-sm focus:ring-dc-accent focus:border-dc-accent bg-dc-surface text-dc-text\"\n />\n )}\n {option.description && (\n <p className=\"dc:text-xs text-dc-text-muted\">{option.description}</p>\n )}\n </div>\n )}\n\n {option.type === 'paletteColor' && (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{option.label}</label>\n <div className=\"dc:flex dc:flex-wrap dc:gap-2\">\n {colorPalette?.colors.map((color, index) => {\n const isSelected =\n ((displayConfig[option.key as keyof ChartDisplayConfig] as number) ??\n option.defaultValue ??\n 0) === index\n return (\n <button\n key={index}\n type=\"button\"\n onClick={() =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: index\n })\n }\n className={`dc:w-8 dc:h-8 dc:rounded dc:border-2 dc:transition-all dc:duration-200 dc:hover:scale-110 focus:outline-hidden dc:focus:ring-2 focus:ring-dc-accent dc:focus:ring-offset-1 ${\n isSelected\n ? 'dc:ring-2 dc:ring-offset-1 dc:scale-110'\n : 'hover:border-dc-text-muted'\n }`}\n style={{\n backgroundColor: color,\n borderColor: isSelected ? 'var(--dc-primary)' : 'var(--dc-border)'\n }}\n title={`Color ${index + 1}: ${color}`}\n />\n )\n }) || [\n // Fallback if no palette available\n <button\n key={0}\n type=\"button\"\n onClick={() =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: 0\n })\n }\n className=\"dc:w-8 dc:h-8 dc:rounded-sm dc:border-2 dc:ring-2 dc:ring-offset-1\"\n style={{\n backgroundColor: '#8884d8',\n borderColor: 'var(--dc-primary)',\n boxShadow: '0 0 0 2px var(--dc-primary)'\n }}\n title=\"Default Color\"\n />\n ]}\n </div>\n {option.description && (\n <p className=\"dc:text-xs text-dc-text-muted\">{option.description}</p>\n )}\n </div>\n )}\n\n {option.type === 'number' && (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{option.label}</label>\n <input\n type=\"number\"\n value={\n (displayConfig[option.key as keyof ChartDisplayConfig] as number) ??\n option.defaultValue ??\n 0\n }\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: e.target.value === '' ? undefined : Number(e.target.value)\n })\n }\n placeholder={option.placeholder}\n min={option.min}\n max={option.max}\n step={option.step}\n className=\"dc:w-full dc:px-2 dc:py-1 dc:text-sm dc:border border-dc-border dc:rounded-sm focus:ring-dc-accent focus:border-dc-accent bg-dc-surface text-dc-text\"\n />\n {option.description && (\n <p className=\"dc:text-xs text-dc-text-muted\">{option.description}</p>\n )}\n </div>\n )}\n\n {option.type === 'select' && (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{option.label}</label>\n <select\n value={\n (displayConfig[option.key as keyof ChartDisplayConfig] as string) ??\n option.defaultValue ??\n ''\n }\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: e.target.value\n })\n }\n className=\"dc:w-full dc:px-2 dc:py-1 dc:text-sm dc:border border-dc-border dc:rounded-sm focus:ring-dc-accent focus:border-dc-accent bg-dc-surface text-dc-text\"\n >\n {option.options?.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n {option.description && (\n <p className=\"dc:text-xs text-dc-text-muted\">{option.description}</p>\n )}\n </div>\n )}\n\n {option.type === 'color' && (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{option.label}</label>\n <div className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"color\"\n value={\n (displayConfig[option.key as keyof ChartDisplayConfig] as string) ??\n option.defaultValue ??\n '#8884d8'\n }\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: e.target.value\n })\n }\n className=\"dc:w-12 dc:h-8 dc:border border-dc-border dc:rounded-sm dc:cursor-pointer\"\n />\n <input\n type=\"text\"\n value={\n (displayConfig[option.key as keyof ChartDisplayConfig] as string) ??\n option.defaultValue ??\n '#8884d8'\n }\n onChange={(e) =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: e.target.value\n })\n }\n placeholder={option.placeholder || '#8884d8'}\n className=\"dc:flex-1 dc:px-2 dc:py-1 dc:text-sm dc:border border-dc-border dc:rounded-sm focus:ring-dc-accent focus:border-dc-accent bg-dc-surface text-dc-text\"\n />\n </div>\n {option.description && (\n <p className=\"dc:text-xs text-dc-text-muted\">{option.description}</p>\n )}\n </div>\n )}\n\n {option.type === 'axisFormat' && (\n <AxisFormatControls\n axisLabel={option.label}\n value={(displayConfig[option.key as keyof ChartDisplayConfig] as AxisFormatConfig) || {}}\n onChange={(config) =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: Object.keys(config).length > 0 ? config : undefined\n })\n }\n />\n )}\n\n {option.type === 'stringArray' && (\n <StringArrayInput\n label={option.label}\n value={(displayConfig[option.key as keyof ChartDisplayConfig] as string[]) ?? []}\n onChange={(arrayValue) =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: arrayValue.length > 0 ? arrayValue : undefined\n })\n }\n placeholder={option.placeholder}\n description={option.description}\n />\n )}\n\n {option.type === 'buttonGroup' && (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{option.label}</label>\n <div className=\"dc:flex dc:border border-dc-border dc:rounded-sm dc:overflow-hidden\">\n {option.options?.map((opt) => {\n const isSelected = (displayConfig[option.key as keyof ChartDisplayConfig] ?? option.defaultValue) === opt.value\n return (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() =>\n onDisplayConfigChange({\n ...displayConfig,\n [option.key]: opt.value\n })\n }\n className={`dc:flex-1 dc:px-3 dc:py-1.5 dc:text-sm dc:font-medium dc:transition-colors ${\n isSelected\n ? 'bg-dc-primary text-white'\n : 'bg-dc-surface text-dc-text hover:bg-dc-border'\n }`}\n >\n {opt.label}\n </button>\n )\n })}\n </div>\n {option.description && (\n <p className=\"dc:text-xs text-dc-text-muted\">{option.description}</p>\n )}\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n}\n","/**\n * TextPortletModal - Simplified modal for creating/editing markdown portlets\n *\n * Layout: large content textarea at top, display options and live preview below.\n * No query configuration needed — markdown is content-only.\n */\n\nimport { useState, useMemo, useCallback } from 'react'\nimport { getIcon } from '../icons'\nimport AnalysisDisplayConfigPanel from './AnalysisBuilder/AnalysisDisplayConfigPanel'\nimport MarkdownChart from './charts/MarkdownChart'\nimport { ensureAnalysisConfig } from '../utils/configMigration'\nimport type { PortletConfig, ChartDisplayConfig } from '../types'\nimport type { AnalysisConfig } from '../types/analysisConfig'\nimport type { ColorPalette } from '../utils/colorPalettes'\n\nconst CloseIcon = getIcon('close')\n\ninterface TextPortletModalProps {\n isOpen: boolean\n onClose: () => void\n onSave: (portlet: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>) => void\n portlet?: PortletConfig | null\n colorPalette?: ColorPalette\n existingTitles?: string[]\n}\n\nexport default function TextPortletModal({\n isOpen,\n onClose,\n onSave,\n portlet,\n colorPalette,\n existingTitles = [],\n}: TextPortletModalProps) {\n // Initialize displayConfig from existing portlet or defaults\n const initialDisplayConfig = useMemo(() => {\n if (portlet) {\n const normalized = ensureAnalysisConfig(portlet)\n const chartConfig = normalized.analysisConfig.charts.query\n return chartConfig?.displayConfig ?? {}\n }\n return {\n content: '',\n hideHeader: true,\n autoHeight: true,\n fontSize: 'medium' as const,\n alignment: 'left' as const,\n accentColorIndex: 0,\n transparentBackground: false,\n accentBorder: 'none' as const,\n }\n }, [portlet])\n\n const [displayConfig, setDisplayConfig] = useState<ChartDisplayConfig>(initialDisplayConfig)\n const [title, setTitle] = useState(() => portlet?.title ?? '')\n const [titleTouched, setTitleTouched] = useState(false)\n\n // Reset state when modal opens with a different portlet\n const [prevPortlet, setPrevPortlet] = useState(portlet)\n if (portlet !== prevPortlet) {\n setPrevPortlet(portlet)\n setTitleTouched(false)\n if (portlet) {\n const normalized = ensureAnalysisConfig(portlet)\n const chartConfig = normalized.analysisConfig.charts.query\n setDisplayConfig(chartConfig?.displayConfig ?? {})\n setTitle(portlet.title ?? '')\n } else {\n setDisplayConfig({\n content: '',\n hideHeader: true,\n autoHeight: true,\n fontSize: 'medium',\n alignment: 'left',\n accentColorIndex: 0,\n transparentBackground: false,\n accentBorder: 'none',\n })\n setTitle('')\n }\n }\n\n const handleTitleChange = useCallback((value: string) => {\n setTitle(value)\n setTitleTouched(true)\n }, [])\n\n // Resolve title: respect user edits (including clearing). Auto-generate only for new portlets.\n const resolvedTitle = useMemo(() => {\n if (titleTouched) return title\n if (portlet?.title) return portlet.title\n // Auto-generate for new portlets\n let candidate = 'Text'\n let counter = 2\n while (existingTitles.includes(candidate)) {\n candidate = `Text ${counter}`\n counter++\n }\n return candidate\n }, [title, titleTouched, portlet, existingTitles])\n\n const handleDisplayConfigChange = useCallback((config: ChartDisplayConfig) => {\n setDisplayConfig(config)\n }, [])\n\n const handleSave = useCallback(() => {\n // Keep markdown sizing policy explicit and preserve header setting independently.\n const finalDisplayConfig: ChartDisplayConfig = {\n ...displayConfig,\n autoHeight: displayConfig.autoHeight ?? true,\n hideHeader: displayConfig.hideHeader ?? true,\n }\n if (!resolvedTitle.trim()) {\n finalDisplayConfig.hideHeader = true\n }\n\n const analysisConfig: AnalysisConfig = {\n version: 1,\n analysisType: 'query',\n activeView: 'chart',\n charts: {\n query: {\n chartType: 'markdown',\n chartConfig: {},\n displayConfig: finalDisplayConfig,\n },\n },\n query: {},\n }\n\n if (portlet) {\n // Editing existing\n onSave({\n ...portlet,\n title: resolvedTitle,\n analysisConfig,\n })\n } else {\n // Adding new\n onSave({\n title: resolvedTitle,\n analysisConfig,\n w: 12,\n h: 3,\n })\n }\n }, [displayConfig, portlet, resolvedTitle, onSave])\n\n const handleContentChange = useCallback((content: string) => {\n setDisplayConfig(prev => ({ ...prev, content }))\n }, [])\n\n if (!isOpen) return null\n\n return (\n <div\n className=\"dc:fixed dc:inset-0 dc:z-50 dc:flex dc:items-center dc:justify-center\"\n onClick={onClose}\n >\n {/* Backdrop */}\n <div className=\"dc:absolute dc:inset-0 dc:bg-black/50\" />\n\n {/* Modal */}\n <div\n className=\"dc:relative dc:w-full dc:max-w-5xl dc:mx-4 dc:max-h-[85vh] dc:flex dc:flex-col bg-dc-surface dc:rounded-lg dc:border border-dc-border\"\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:px-6 dc:py-4 dc:border-b border-dc-border dc:shrink-0\">\n <h2 className=\"dc:text-lg dc:font-semibold text-dc-text\">\n {portlet ? 'Edit Text' : 'Add Text'}\n </h2>\n <button\n onClick={onClose}\n className=\"dc:p-1 dc:rounded-md text-dc-text-secondary dc:hover:bg-dc-surface-hover dc:transition-colors\"\n >\n <CloseIcon className=\"dc:w-5 dc:h-5\" />\n </button>\n </div>\n\n {/* Content area — two columns: left (textarea + preview), right (display options) */}\n <div className=\"dc:flex-1 dc:min-h-0 dc:overflow-y-auto dc:p-6\">\n <div className=\"dc:flex dc:gap-6 dc:h-full\">\n {/* Left column: title + textarea + preview */}\n <div className=\"dc:flex-1 dc:min-w-0 dc:flex dc:flex-col dc:gap-4\">\n {/* Optional title — visible when not transparent */}\n {!displayConfig.transparentBackground && (\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text dc:mb-1.5\">\n Title\n </label>\n <input\n type=\"text\"\n value={title}\n onChange={(e) => handleTitleChange(e.target.value)}\n placeholder=\"Text\"\n className=\"dc:w-full dc:rounded-md dc:border border-dc-border bg-dc-surface dc:px-3 dc:py-2 dc:text-sm text-dc-text focus:outline-hidden dc:focus:ring-2 focus:ring-dc-accent\"\n />\n </div>\n )}\n\n {/* Content textarea */}\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text dc:mb-1.5\">\n Markdown Content\n </label>\n <textarea\n value={displayConfig.content || ''}\n onChange={(e) => handleContentChange(e.target.value)}\n placeholder={'# Welcome\\n\\nAdd your **markdown** content here:\\n\\n- Lists with bullets\\n- [Links](https://example.com)\\n- *Italic* and **bold** text\\n\\n---\\n\\nUse --- for horizontal rules.'}\n className=\"dc:w-full dc:rounded-md dc:border border-dc-border bg-dc-surface dc:px-3 dc:py-2 dc:text-sm text-dc-text dc:font-mono dc:resize-y focus:outline-hidden dc:focus:ring-2 focus:ring-dc-accent\"\n style={{ minHeight: '140px' }}\n rows={7}\n />\n <p className=\"dc:mt-1 dc:text-xs text-dc-text-muted\">\n Supports headers (#), bold (**text**), italic (*text*), links ([text](url)), lists (- item), and horizontal rules (---).\n </p>\n </div>\n\n {/* Live preview */}\n <div className=\"dc:flex-1 dc:min-h-0\">\n <div className=\"dc:text-xs dc:font-medium dc:uppercase dc:tracking-wider text-dc-text-muted dc:mb-2\">\n Preview\n </div>\n <div\n className=\"dc:overflow-hidden\"\n style={{ minHeight: '200px' }}\n >\n <MarkdownChart\n data={[]}\n displayConfig={displayConfig}\n colorPalette={colorPalette}\n height=\"auto\"\n />\n </div>\n </div>\n </div>\n\n {/* Right column: display options */}\n <div className=\"dc:w-72 dc:shrink-0\">\n <AnalysisDisplayConfigPanel\n chartType=\"markdown\"\n displayConfig={displayConfig}\n colorPalette={colorPalette}\n onDisplayConfigChange={handleDisplayConfigChange}\n excludeKeys={['content', 'hideHeader']}\n />\n </div>\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"dc:flex dc:items-center dc:justify-end dc:gap-3 dc:px-6 dc:py-4 dc:border-t border-dc-border dc:shrink-0\">\n <button\n onClick={onClose}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md dc:border border-dc-border text-dc-text-secondary dc:hover:bg-dc-surface-hover 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 dc:rounded-md dc:text-white dc:transition-colors\"\n style={{ backgroundColor: 'var(--dc-primary)' }}\n >\n {portlet ? 'Update' : 'Save'}\n </button>\n </div>\n </div>\n </div>\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=\"dc: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 dc: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 dc: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 dc: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 dc: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 dc: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=\"dc: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 dc: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 dc: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 dc: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 dc: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 dc: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 dc: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 dc: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 dc: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 dc: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 './types'\nimport { FILTER_OPERATORS } from './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 dc: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 './types'\nimport { FILTER_OPERATORS } from './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 dc: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 dc: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, useEffect } 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 // Local state for immediate UI feedback on filter value changes.\n // Without this, changes require a full round-trip through the parent's\n // onConfigChange → state update → re-render cycle before being visible.\n // If the parent doesn't handle onConfigChange (or uses dashboardFilters prop),\n // the round-trip never completes and clicks appear to do nothing.\n const [localFilters, setLocalFilters] = useState<DashboardFilter[]>(dashboardFilters)\n\n // Sync from props when parent updates (e.g., after round-trip completes,\n // or when filters change externally)\n useEffect(() => {\n setLocalFilters(dashboardFilters)\n }, [dashboardFilters])\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 localFilters.find(df => df.isUniversalTime)\n }, [localFilters])\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 localFilters.filter(df => !df.isUniversalTime)\n }, [localFilters])\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 = localFilters.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 setLocalFilters(updatedFilters)\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 const updatedFilters = [...localFilters, newFilter]\n setLocalFilters(updatedFilters)\n onDashboardFiltersChange(updatedFilters)\n }\n }, [localFilters, 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 = localFilters.map(df =>\n df.id === filterId ? updatedFilter : df\n )\n setLocalFilters(updatedFilters)\n onDashboardFiltersChange(updatedFilters)\n }, [localFilters, 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 && localFilters.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 // Markdown-specific display modes\n const isTransparent = renderChartType === 'markdown' && !!renderDisplayConfig?.transparentBackground\n const isAutoHeight = renderChartType === 'markdown' && (renderDisplayConfig?.autoHeight ?? true)\n const shouldHideHeader = renderChartType === 'markdown'\n ? (renderDisplayConfig?.hideHeader ?? true) || !portlet.title\n : (renderDisplayConfig?.hideHeader ?? false)\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 = shouldHideHeader ? 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={isTransparent\n ? 'dc:flex dc:flex-col'\n : 'bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:flex dc:flex-col'\n }\n style={{\n height: isAutoHeight ? 'auto' : portletHeight,\n boxShadow: isTransparent ? 'none' : 'var(--dc-shadow-sm)',\n borderColor: isTransparent ? 'transparent' : undefined,\n borderWidth: isTransparent ? '0' : undefined,\n backgroundColor: isTransparent ? 'transparent' : undefined,\n }}\n >\n {/* Portlet Header - Simplified for mobile (no edit controls) */}\n {!shouldHideHeader && (\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:overflow-visible dc:flex dc:flex-col${isTransparent ? '' : ' dc:px-2 dc:py-3'}`}\n style={{ height: isAutoHeight ? 'auto' : 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={isAutoHeight ? 'auto' : contentHeight}\n colorPalette={colorPalette}\n />\n </div>\n </div>\n )\n })}\n </div>\n </ScrollContainerProvider>\n )\n}\n","import { useCallback } from 'react'\nimport type { LayoutItem } from 'react-grid-layout'\nimport type { StoreApi } from 'zustand'\nimport type { DashboardStore } from '../../stores/dashboardStore'\n\ninterface UseGridLayoutEngineOptions {\n storeApi: StoreApi<DashboardStore>\n}\n\nexport function useGridLayoutEngine({\n storeApi\n}: UseGridLayoutEngineOptions) {\n const hasLayoutActuallyChanged = useCallback(\n (newLayout: LayoutItem[]) => {\n const { isInitialized, lastKnownLayout } = storeApi.getState()\n if (!isInitialized || lastKnownLayout.length === 0) {\n return false\n }\n\n for (const newItem of newLayout) {\n const oldItem = 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 [storeApi]\n )\n\n return {\n hasLayoutActuallyChanged\n }\n}\n","import type { DashboardGridSettings, PortletConfig, RowLayout, RowLayoutColumn } from '../../types'\n\nexport const createRowId = () => `row-${Date.now()}`\n\nexport const 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\nexport const 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\nexport const 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\nexport const 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\nexport const 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","import { useCallback, useMemo, type MutableRefObject } from 'react'\nimport type {\n DashboardConfig,\n DashboardGridSettings,\n DashboardLayoutMode,\n PortletConfig,\n RowLayout\n} from '../../types'\nimport {\n convertPortletsToRows,\n convertRowsToPortlets,\n normalizeRows\n} from './layoutUtils'\n\ninterface UseRowLayoutEngineOptions {\n layoutMode: DashboardLayoutMode\n draftRows: RowLayout[] | null\n config: DashboardConfig\n gridSettings: DashboardGridSettings\n configRef: MutableRefObject<DashboardConfig>\n onConfigChangeRef: MutableRefObject<((config: DashboardConfig) => void) | undefined>\n onSaveRef: MutableRefObject<((config: DashboardConfig) => Promise<void> | void) | undefined>\n setDraftRows: (rows: RowLayout[] | null) => void\n setThumbnailDirty: (dirty: boolean) => void\n}\n\nexport function useRowLayoutEngine({\n layoutMode,\n draftRows,\n config,\n gridSettings,\n configRef,\n onConfigChangeRef,\n onSaveRef,\n setDraftRows,\n setThumbnailDirty,\n}: UseRowLayoutEngineOptions) {\n const resolvedRows = useMemo(() => {\n if (layoutMode !== 'rows') return []\n const baseRows =\n draftRows ??\n config.rows ??\n convertPortletsToRows(config.portlets, gridSettings)\n return normalizeRows(baseRows, config.portlets, gridSettings)\n }, [layoutMode, draftRows, config.rows, config.portlets, gridSettings])\n\n const updateRowLayout = useCallback(\n async (\n rows: RowLayout[],\n save = true,\n portletsOverride?: PortletConfig[]\n ) => {\n if (!onConfigChangeRef.current) return\n\n const portlets = portletsOverride ?? configRef.current.portlets\n const normalizedRows = normalizeRows(rows, portlets, gridSettings)\n const updatedPortlets = convertRowsToPortlets(normalizedRows, portlets)\n const updatedConfig: DashboardConfig = {\n ...configRef.current,\n layoutMode: 'rows',\n rows: normalizedRows,\n portlets: updatedPortlets,\n }\n\n setDraftRows(null)\n onConfigChangeRef.current(updatedConfig)\n\n if (save) {\n setThumbnailDirty(true)\n }\n\n if (save && onSaveRef.current) {\n try {\n await onSaveRef.current(updatedConfig)\n } catch (error) {\n console.error('Auto-save failed after row layout change:', error)\n }\n }\n },\n [configRef, gridSettings, onConfigChangeRef, onSaveRef, setDraftRows, setThumbnailDirty]\n )\n\n return {\n resolvedRows,\n updateRowLayout,\n }\n}\n","import { startTransition, useCallback, useRef, type MutableRefObject, type RefObject } from 'react'\nimport type { StoreApi } from 'zustand'\nimport { captureThumbnail } from '../../utils/thumbnail'\nimport type {\n DashboardConfig,\n DashboardGridSettings,\n DashboardLayoutMode,\n PortletConfig,\n RowLayout,\n ThumbnailFeatureConfig\n} from '../../types'\nimport type { DashboardStore, DashboardStoreActions } from '../../stores/dashboardStore'\nimport {\n convertPortletsToRows,\n convertRowsToPortlets,\n createRowId,\n equalizeRowColumns,\n normalizeRows\n} from './layoutUtils'\n\ninterface UseDashboardControllerOptions {\n allowedModes: DashboardLayoutMode[]\n canChangeLayoutMode: boolean\n isResponsiveEditable: boolean\n layoutMode: DashboardLayoutMode\n resolvedRows: RowLayout[]\n gridSettings: DashboardGridSettings\n thumbnailConfig?: ThumbnailFeatureConfig\n dashboardRef?: RefObject<HTMLElement | null>\n storeApi: StoreApi<DashboardStore>\n storeActions: Pick<\n DashboardStoreActions,\n | 'setEditMode'\n | 'exitFilterSelectionMode'\n | 'openPortletModal'\n | 'closePortletModal'\n | 'openTextModal'\n | 'closeTextModal'\n | 'openFilterConfigModal'\n | 'closeFilterConfigModal'\n | 'openDeleteConfirm'\n | 'closeDeleteConfirm'\n | 'setThumbnailDirty'\n >\n configRef: MutableRefObject<DashboardConfig>\n onConfigChangeRef: MutableRefObject<((config: DashboardConfig) => void) | undefined>\n onSaveRef: MutableRefObject<((config: DashboardConfig) => Promise<void> | void) | undefined>\n onSaveThumbnailRef: MutableRefObject<((thumbnailData: string) => Promise<string | void>) | undefined>\n updateRowLayout: (\n rows: RowLayout[],\n save?: boolean,\n portletsOverride?: PortletConfig[]\n ) => Promise<void>\n portletComponentRefs?: MutableRefObject<Record<string, { refresh: (options?: { bustCache?: boolean }) => void } | null>>\n onPortletRefresh?: (portletId: string, options?: { bustCache?: boolean }) => void\n}\n\nexport function useDashboardController({\n allowedModes,\n canChangeLayoutMode,\n isResponsiveEditable,\n layoutMode,\n resolvedRows,\n gridSettings,\n thumbnailConfig,\n dashboardRef,\n storeApi,\n storeActions,\n configRef,\n onConfigChangeRef,\n onSaveRef,\n onSaveThumbnailRef,\n updateRowLayout,\n portletComponentRefs,\n onPortletRefresh,\n}: UseDashboardControllerOptions) {\n const layoutModeRef = useRef(layoutMode)\n layoutModeRef.current = layoutMode\n const canChangeLayoutModeRef = useRef(canChangeLayoutMode)\n canChangeLayoutModeRef.current = canChangeLayoutMode\n const resolvedRowsRef = useRef(resolvedRows)\n resolvedRowsRef.current = resolvedRows\n\n const saveConfig = useCallback(\n async (\n updatedConfig: DashboardConfig,\n errorMessage: string\n ) => {\n if (!onConfigChangeRef.current) return\n\n onConfigChangeRef.current(updatedConfig)\n storeActions.setThumbnailDirty(true)\n\n if (onSaveRef.current) {\n try {\n await onSaveRef.current(updatedConfig)\n } catch (error) {\n console.error(errorMessage, error)\n }\n }\n },\n [onConfigChangeRef, onSaveRef, storeActions]\n )\n\n const enterEditMode = useCallback(() => {\n startTransition(() => {\n storeActions.setEditMode(true)\n })\n }, [storeActions])\n\n const exitEditMode = useCallback(() => {\n startTransition(() => {\n storeActions.setEditMode(false)\n })\n\n const isThumbnailDirty = storeApi.getState().thumbnailDirty\n if (isThumbnailDirty && thumbnailConfig?.enabled && dashboardRef) {\n setTimeout(async () => {\n const thumbnailData = await captureThumbnail(dashboardRef, thumbnailConfig)\n if (thumbnailData && onSaveThumbnailRef.current) {\n try {\n const thumbnailUrl = await onSaveThumbnailRef.current(thumbnailData)\n if (thumbnailUrl && onConfigChangeRef.current) {\n onConfigChangeRef.current({\n ...configRef.current,\n thumbnailUrl,\n thumbnailData: undefined\n })\n }\n } catch (error) {\n console.error('Failed to save thumbnail:', error)\n }\n }\n storeActions.setThumbnailDirty(false)\n }, 500)\n }\n }, [\n configRef,\n dashboardRef,\n onConfigChangeRef,\n onSaveThumbnailRef,\n storeApi,\n storeActions,\n thumbnailConfig\n ])\n\n const toggleEditMode = useCallback(() => {\n if (!isResponsiveEditable) return\n const store = storeApi.getState()\n if (store.isEditMode) {\n exitEditMode()\n } else {\n startTransition(() => {\n storeActions.setEditMode(true)\n })\n }\n }, [exitEditMode, isResponsiveEditable, storeActions, storeApi])\n\n const selectFilter = useCallback(\n (filterId: string | null) => {\n const currentSelectedId = storeApi.getState().selectedFilterId\n storeApi.getState().setSelectedFilterId(\n filterId === currentSelectedId ? null : filterId\n )\n },\n [storeApi]\n )\n\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 openAddText = useCallback(() => {\n storeActions.openTextModal(null)\n }, [storeActions])\n\n const openEditText = useCallback(\n (portlet: PortletConfig) => {\n storeActions.openTextModal(portlet)\n },\n [storeActions]\n )\n\n const openFilterConfig = useCallback(\n (portlet: PortletConfig) => {\n storeActions.openFilterConfigModal(portlet)\n },\n [storeActions]\n )\n\n const handleLayoutModeChange = useCallback(\n async (mode: DashboardLayoutMode) => {\n if (\n !onConfigChangeRef.current ||\n mode === layoutModeRef.current ||\n !canChangeLayoutModeRef.current ||\n !allowedModes.includes(mode)\n ) {\n return\n }\n\n const cfg = configRef.current\n const baseRows = normalizeRows(\n cfg.rows && cfg.rows.length > 0\n ? cfg.rows\n : convertPortletsToRows(cfg.portlets, gridSettings),\n cfg.portlets,\n gridSettings\n )\n\n const updatedPortlets = convertRowsToPortlets(baseRows, cfg.portlets)\n const updatedConfig: DashboardConfig = {\n ...cfg,\n layoutMode: mode,\n rows: baseRows,\n portlets: updatedPortlets,\n }\n\n await saveConfig(updatedConfig, 'Auto-save failed after layout mode switch:')\n },\n [allowedModes, configRef, gridSettings, onConfigChangeRef, saveConfig]\n )\n\n const savePortlet = useCallback(\n async (\n portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>\n ): Promise<string | null> => {\n if (!onConfigChangeRef.current) return null\n\n const cfg = configRef.current\n let updatedPortlets = [...cfg.portlets]\n let isNewPortlet = false\n let newPortletId: string | null = null\n\n const store = storeApi.getState()\n const editingExisting = store.editingPortlet || store.editingTextPortlet\n if (editingExisting) {\n const index = updatedPortlets.findIndex((p) => p.id === editingExisting.id)\n if (index !== -1) {\n updatedPortlets[index] = portletData as PortletConfig\n }\n } else {\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 let maxY = 0\n cfg.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 (layoutModeRef.current === 'rows') {\n const currentRows = resolvedRowsRef.current\n const baseRows =\n currentRows.length > 0\n ? currentRows.map((row) => ({\n ...row,\n columns: row.columns.map((col) => ({ ...col })),\n }))\n : normalizeRows(\n cfg.rows ?? convertPortletsToRows(cfg.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, 3),\n columns: equalizeRowColumns([newPortletId], gridSettings),\n },\n ]\n : baseRows\n\n await updateRowLayout(nextRows, true, updatedPortlets)\n } else {\n const updatedConfig: DashboardConfig = {\n ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n }\n\n storeActions.closePortletModal()\n storeActions.closeTextModal()\n return newPortletId\n },\n [configRef, gridSettings, onConfigChangeRef, resolvedRowsRef, saveConfig, storeActions, storeApi, updateRowLayout]\n )\n\n const executeDeletePortlet = useCallback(\n async (portletId: string) => {\n if (!onConfigChangeRef.current) return\n\n const cfg = configRef.current\n const updatedPortlets = cfg.portlets.filter((p) => p.id !== portletId)\n\n if (layoutModeRef.current === 'rows') {\n const nextRows = resolvedRowsRef.current\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 ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n }\n },\n [configRef, gridSettings, onConfigChangeRef, resolvedRowsRef, saveConfig, updateRowLayout]\n )\n\n const deletePortlet = useCallback(\n async (portletId: string) => {\n storeActions.openDeleteConfirm(portletId)\n },\n [storeActions]\n )\n\n const confirmDelete = useCallback(async () => {\n const portletId = storeApi.getState().deleteConfirmPortletId\n if (!portletId) return\n\n await executeDeletePortlet(portletId)\n storeActions.closeDeleteConfirm()\n }, [executeDeletePortlet, storeActions, storeApi])\n\n const duplicatePortlet = useCallback(\n async (portletId: string): Promise<string | undefined> => {\n if (!onConfigChangeRef.current) return undefined\n\n const cfg = configRef.current\n const originalPortlet = cfg.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 cfg.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 = [...cfg.portlets, duplicatedPortlet]\n\n if (layoutModeRef.current === 'rows') {\n const baseRows = resolvedRowsRef.current.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, 3),\n columns: equalizeRowColumns([duplicatedPortlet.id], gridSettings),\n },\n ]\n await updateRowLayout(nextRows, true, updatedPortlets)\n } else {\n const updatedConfig: DashboardConfig = {\n ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n }\n\n return duplicatedPortlet.id\n },\n [configRef, gridSettings, onConfigChangeRef, resolvedRowsRef, saveConfig, 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 [onPortletRefresh, portletComponentRefs]\n )\n\n const toggleFilterForPortlet = useCallback(\n async (portletId: string, filterId: string) => {\n if (!onConfigChangeRef.current) return\n\n const cfg = configRef.current\n const updatedPortlets = cfg.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 ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n },\n [configRef, onConfigChangeRef, saveConfig]\n )\n\n const selectAllForFilter = useCallback(\n async (filterId: string) => {\n if (!onConfigChangeRef.current) return\n\n const cfg = configRef.current\n const updatedPortlets = cfg.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 ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n },\n [configRef, onConfigChangeRef, saveConfig]\n )\n\n const saveFilterConfig = useCallback(\n async (mapping: string[]) => {\n const filterConfigPortlet = storeApi.getState().filterConfigPortlet\n if (!onConfigChangeRef.current || !filterConfigPortlet) return\n\n const cfg = configRef.current\n const updatedPortlets = cfg.portlets.map((p) => {\n if (p.id === filterConfigPortlet.id) {\n return {\n ...p,\n dashboardFilterMapping: mapping,\n }\n }\n return p\n })\n\n const updatedConfig: DashboardConfig = {\n ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n },\n [configRef, onConfigChangeRef, saveConfig, storeApi]\n )\n\n const handlePaletteChange = useCallback(\n async (paletteName: string) => {\n if (!onConfigChangeRef.current) return\n\n const updatedConfig: DashboardConfig = {\n ...configRef.current,\n colorPalette: paletteName,\n }\n\n await saveConfig(updatedConfig, 'Auto-save failed:')\n },\n [configRef, onConfigChangeRef, saveConfig]\n )\n\n return {\n enterEditMode,\n exitEditMode,\n toggleEditMode,\n selectFilter,\n openAddPortlet,\n openEditPortlet,\n openAddText,\n openEditText,\n openFilterConfig,\n handleLayoutModeChange,\n savePortlet,\n deletePortlet,\n confirmDelete,\n duplicatePortlet,\n refreshPortlet,\n toggleFilterForPortlet,\n selectAllForFilter,\n saveFilterConfig,\n handlePaletteChange,\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, useRef } from 'react'\nimport { useShallow } from 'zustand/react/shallow'\nimport {\n useDashboardStore,\n useDashboardStoreApi,\n type DashboardStore,\n type PortletDebugDataEntry,\n} from '../stores/dashboardStore'\nimport { useCubeFeatures } from '../providers/CubeProvider'\nimport type { LayoutItem } from 'react-grid-layout'\nimport type {\n DashboardConfig,\n PortletConfig,\n RowLayout,\n DashboardFilter,\n DashboardGridSettings,\n DashboardLayoutMode,\n} from '../types'\nimport { useGridLayoutEngine } from './dashboard/useGridLayoutEngine'\nimport { useRowLayoutEngine } from './dashboard/useRowLayoutEngine'\nimport { useDashboardController } from './dashboard/useDashboardController'\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 text portlet modal is open */\n isTextModalOpen: boolean\n /** Portlet being edited in text modal */\n editingTextPortlet: 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\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 openAddText: () => void\n openEditText: (portlet: PortletConfig) => void\n closeTextModal: () => 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// Selectors\n// ============================================================================\n\nconst selectStoreState = (state: DashboardStore) => ({\n isEditMode: state.isEditMode,\n selectedFilterId: state.selectedFilterId,\n isPortletModalOpen: state.isPortletModalOpen,\n editingPortlet: state.editingPortlet,\n isTextModalOpen: state.isTextModalOpen,\n editingTextPortlet: state.editingTextPortlet,\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 // NOTE: debugData intentionally excluded — DashboardPortletCard reads it directly from store.\n // Including it here would cause the entire hook to re-run on every portlet data load.\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 openTextModal: state.openTextModal,\n closeTextModal: state.closeTextModal,\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 const storeApi = useDashboardStoreApi()\n\n // Get thumbnail feature config from context\n const { features } = useCubeFeatures()\n const thumbnailConfig = features.thumbnail\n\n // Refs for values used in stable callbacks (avoids recreating callbacks on every state change)\n const configRef = useRef(config)\n configRef.current = config\n const onConfigChangeRef = useRef(onConfigChange)\n onConfigChangeRef.current = onConfigChange\n const onSaveRef = useRef(onSave)\n onSaveRef.current = onSave\n const onSaveThumbnailRef = useRef(onSaveThumbnail)\n onSaveThumbnailRef.current = onSaveThumbnail\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, updateRowLayout } = useRowLayoutEngine({\n layoutMode,\n draftRows: storeState.draftRows,\n config,\n gridSettings,\n configRef,\n onConfigChangeRef,\n onSaveRef,\n setDraftRows: storeActions.setDraftRows,\n setThumbnailDirty: storeActions.setThumbnailDirty,\n })\n\n const { hasLayoutActuallyChanged } = useGridLayoutEngine({\n storeApi,\n })\n\n const {\n enterEditMode,\n exitEditMode,\n toggleEditMode,\n selectFilter,\n openAddPortlet,\n openEditPortlet,\n openAddText,\n openEditText,\n openFilterConfig,\n handleLayoutModeChange,\n savePortlet,\n deletePortlet,\n confirmDelete,\n duplicatePortlet,\n refreshPortlet,\n toggleFilterForPortlet,\n selectAllForFilter,\n saveFilterConfig,\n handlePaletteChange,\n } = useDashboardController({\n allowedModes,\n canChangeLayoutMode,\n isResponsiveEditable,\n layoutMode,\n resolvedRows,\n gridSettings,\n thumbnailConfig,\n dashboardRef,\n storeApi,\n storeActions,\n configRef,\n onConfigChangeRef,\n onSaveRef,\n onSaveThumbnailRef,\n updateRowLayout,\n portletComponentRefs,\n onPortletRefresh,\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 openAddText,\n openEditText,\n closeTextModal: storeActions.closeTextModal,\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 openAddText,\n openEditText,\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 type CSSProperties,\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')\n\n/** Inline \"Tt\" typography icon for Add Text buttons */\nfunction TextIcon({ className, style }: { className?: string; style?: CSSProperties }) {\n return (\n <svg className={className} style={style} viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n <text x=\"1\" y=\"20\" fontSize=\"20\" fontWeight=\"700\" fontFamily=\"serif\">T</text>\n <text x=\"14\" y=\"20\" fontSize=\"13\" fontWeight=\"600\" fontFamily=\"serif\">t</text>\n </svg>\n )\n}\nimport PortletAnalysisModal from './PortletAnalysisModal'\nimport TextPortletModal from './TextPortletModal'\nimport PortletFilterConfigModal from './PortletFilterConfigModal'\nimport ConfirmModal from './ConfirmModal'\nimport { ensureAnalysisConfig } from '../utils/configMigration'\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: 1\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 // Local ref for tracking latest drag rows synchronously (avoids stale reads from useEffect-synced ref)\n const latestDragRowsRef = 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 isTextModalOpen,\n editingTextPortlet,\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 // Keep mutable references so high-frequency row interactions\n // don't force callback identity churn into memoized portlet cards.\n const actionsRef = useRef(actions)\n actionsRef.current = actions\n const canEditRef = useRef(canEdit)\n canEditRef.current = canEdit\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 if (isInitialized) return\n\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 }, [isInitialized, 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 latestDragRowsRef.current = null\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 latestDragRowsRef.current = nextRows\n actions.setDraftRows(nextRows)\n }\n\n const handleMouseUp = () => {\n document.removeEventListener('mousemove', handleMouseMove)\n document.removeEventListener('mouseup', handleMouseUp)\n const finalRows = latestDragRowsRef.current ?? startRows\n latestDragRowsRef.current = null\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 latestDragRowsRef.current = null\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 latestDragRowsRef.current = startRows\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 latestDragRowsRef.current = nextRows\n actions.setDraftRows(nextRows)\n }\n\n const handleMouseUp = () => {\n document.removeEventListener('mousemove', handleMouseMove)\n document.removeEventListener('mouseup', handleMouseUp)\n const finalRows = latestDragRowsRef.current ?? startRows\n latestDragRowsRef.current = null\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 (!canEditRef.current) return\n dragStateRef.current = { rowIndex, colIndex, portletId }\n actionsRef.current.setIsDraggingPortlet(true)\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', portletId)\n }, [])\n\n const handlePortletDragEnd = useCallback(() => {\n dragStateRef.current = null\n actionsRef.current.setIsDraggingPortlet(false)\n }, [])\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, 3),\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 actionsRef.current.refreshPortlet(portletId, options)\n }, [])\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 actionsRef.current.deletePortlet(portletId)\n }, [])\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 actionsRef.current.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 }, [])\n\n // Handle adding new portlet - delegate to hook action\n const handleAddPortlet = useCallback(() => {\n actions.openAddPortlet()\n }, [actions])\n\n // Handle adding new text portlet - delegate to hook action\n const handleAddText = useCallback(() => {\n actions.openAddText()\n }, [actions])\n\n // Handle editing existing portlet - route markdown to text modal, others to analysis builder\n const handleEditPortlet = useCallback((portlet: PortletConfig) => {\n const normalized = ensureAnalysisConfig(portlet)\n const chartType = normalized.analysisConfig.charts[normalized.analysisConfig.analysisType]?.chartType\n if (chartType === 'markdown') {\n actionsRef.current.openEditText(portlet)\n } else {\n actionsRef.current.openEditPortlet(portlet)\n }\n }, [])\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 actionsRef.current.openFilterConfig(portlet)\n }, [])\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 actionsRef.current.toggleFilterForPortlet(portletId, filterId)\n }, [])\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 layoutMode={layoutMode}\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 layoutMode,\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=\"dc: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 <div className=\"dc:flex dc:items-center dc:gap-3\">\n <button\n onClick={handleAddText}\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-text-secondary)',\n borderColor: 'var(--dc-border)'\n }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-surface)'}\n >\n <TextIcon className=\"dc:w-5 dc:h-5 dc:mr-2\" />\n Add Text\n </button>\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 </div>\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 {/* Text Portlet Modal */}\n <TextPortletModal\n isOpen={isTextModalOpen}\n onClose={actions.closeTextModal}\n onSave={handlePortletSave}\n portlet={editingTextPortlet}\n colorPalette={colorPalette}\n existingTitles={config.portlets.map(p => p.title)}\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,\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={handleAddText}\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-text-secondary)',\n borderColor: 'var(--dc-border)'\n }}\n >\n <TextIcon className=\"dc:w-5 dc:h-5 dc:mr-2\" />\n Add Text\n </button>\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 onAddText={actions.openAddText}\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 {/* Text Portlet Modal */}\n <TextPortletModal\n isOpen={isTextModalOpen}\n onClose={actions.closeTextModal}\n onSave={handlePortletSave}\n portlet={editingTextPortlet}\n colorPalette={colorPalette}\n existingTitles={config.portlets.map(p => p.title)}\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}"],"names":["RefreshIcon","getIcon","ChartErrorBoundary","Component","props","error","errorInfo","jsxs","jsx","TimeIcon","className","HierarchyIcon","TableIcon","DrillDownIcon","DrillUpIcon","getDirectionIndicator","option","groupOptions","options","groups","category","existing","getCategoryLabel","DrillMenu","position","onSelect","onClose","menuRef","useRef","mounted","setMounted","useState","useEffect","handleClickOutside","event","handleEscape","handleScroll","menuStyle","groupedOptions","menuContent","categoryOptions","categoryIndex","createPortal","HomeIcon","ChevronIcon","BackIcon","getSafeLabel","label","DrillBreadcrumb","path","onNavigate","onLevelClick","entry","index","safeLabel","React","generateId","isTimeDimension","dimensionName","metadata","cube","dimension","getTimeDimensionGranularities","getMeasureDrillMembers","measureName","measure","m","findHierarchyForDimension","cubeName","c","hierarchy","levelIndex","buildDrillOptions","query","dashboardFilters","dashboardFilterMapping","clickedField","drillMembers","timeDimOptions","buildTimeDrillOptions","hierarchyOptions","buildHierarchyDrillOptions","drillMember","getDimensionLabel","_dashboardFilters","_dashboardFilterMapping","timeDim","currentGranularity","availableGranularities","granularity","capitalizeGranularity","currentIndex","i","targetGranularity","hierarchyInfo","nextDimension","prevDimension","buildDrillQuery","buildDrillDownQuery","buildDrillUpQuery","buildDetailsQuery","xValue","newQuery","filters","getDateRangeForPeriod","currentDimensions","newDimensions","dim","currentDim","d","info","newFilter","_event","targetIndex","lowerLevels","targetDimension","targetDimensionLabel","xAxisDimension","xAxisLabel","isTargetTimeDimension","xFilter","chartConfig","breadcrumbLabel","periodValue","date","year","startMonth","endMonth","month","lastDay","dayOfWeek","diff","weekStart","weekEnd","dateStr","useDrillInteraction","onQueryChange","enabled","menuOpen","setMenuOpen","menuPosition","setMenuPosition","menuOptions","setMenuOptions","currentClickEvent","setCurrentClickEvent","drillPath","setDrillPath","originalGranularity","setOriginalGranularity","originalChartConfig","setOriginalChartConfig","currentChartConfig","setCurrentChartConfig","originalQuery","setOriginalQuery","drillEnabled","useMemo","hasTimeDimensions","hasDimensions","hasDrillMembers","hasDashboardFilterMatch","f","handleDataPointClick","useCallback","handleOptionSelect","existingLevelIndex","newPath","targetEntry","restoredQuery","result","currentGran","prev","closeMenu","navigateBack","previousEntry","navigateToLevel","isMultiQueryConfig","obj","isServerFunnelQuery","isQueryConfig","isFunnelConfig","isFlowConfig","isRetentionConfig","isMultiQuery","config","isSingleQuery","isValidAnalysisConfig","createDefaultQueryConfig","createDefaultFunnelConfig","createDefaultFlowConfig","createDefaultRetentionConfig","createDefaultConfig","type","isValidAnalysisWorkspace","data","w","createDefaultWorkspace","isLegacyFunnelMultiQuery","isServerFlowQuery","extractChartConfig","portlet","analysisType","migrateLegacyPortlet","isServerRetentionQuery","migrateLegacyFunnelMerge","strategy","legacyQuery","bindingKey","timeDimension","steps","step","migrateConfig","wrapped","hasAnalysisConfig","ensureAnalysisConfig","legacyAnalysisType","analysisConfig","shouldIncludeFilter","filter","simpleFilter","getApplicableDashboardFilters","filterMapping","df","convertToServerFormat","groupFilter","convertedFilters","mergeDashboardAndPortletFilters","portletFilters","format","extractDashboardFields","dashboardConfig","measures","dimensions","timeDimensions","extractFromCubeQuery","cubeQuery","td","extractFieldsFromFilters","field","funnelQuery","subQuery","e","fields","getDateRangeFromFilter","applyUniversalTimeFilters","portletTimeDimensions","universalTimeFilters","dateRange","AnalyticsPortlet","forwardRef","chartType","displayConfig","eagerLoad","_isVisible","height","_title","colorPalette","loadingComponent","onDebugDataReady","ref","onDebugDataReadyRef","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","isFunnelMode","isFlowMode","isRetentionMode","drilledQuery","setDrilledQuery","queryObjectJson","previousQueryObjectJson","activeQuery","meta","useCubeMeta","drill","handleNavigateBack","handleNavigateToLevel","shouldSkipSingle","shouldSkipMulti","shouldSkipFunnel","shouldSkipFlow","shouldSkipRetention","queryClient","useQueryClient","funnelConfig","singleQueryResult","useCubeLoadQuery","multiQueryResult","useMultiCubeLoadQuery","funnelQueryResult","useFunnelQuery","flowQueryResult","useFlowQuery","retentionQueryResult","useRetentionQuery","resultSet","isLoading","isFetching","multiQueryData","flowChartData","retentionChartData","useImperativeHandle","bustCache","queryKey","cleanedConfig","cleanQueryForServer","createMultiQueryKey","cleanedQuery","createQueryKey","handleRetry","getData","drillDebugInfo","hasMandatoryFields","zone","LoadingIndicator","isDrilledState","renderChart","chartHeight","effectiveChartType","isValidChartType","chartData","isDrillEnabledForChart","effectiveChartConfig","LazyChart","isDrillEnabled","useScrollDetection","containerRef","threshold","debounceMs","container","isScrolled","setIsScrolled","timeoutRef","shouldBeScrolled","useElementVisibility","elementRef","setIsVisible","hasBeenVisibleRef","checkVisibility","element","elementRect","containerRect","visible","scrollTarget","rafId","useDragAutoScroll","scrollContainerRef","edgeThreshold","maxScrollSpeed","animationFrameRef","scrollDirectionRef","scrollIntensityRef","calculateScrollSpeed","distanceFromEdge","normalizedDistance","scrollLoop","speed","scrollAmount","startScrolling","direction","intensity","stopScrolling","handleDragOver","mouseY","distanceFromTop","distanceFromBottom","handleDragEnd","CodeBlock","code","language","title","maxHeight","headerRight","copied","setCopied","codeRef","CopyIcon","CheckIcon","isActive","loadSyntaxHighlighter","hljs","getSyntaxHighlighter","handleCopy","textArea","Fragment","DebugModal","cacheInfo","isOpen","setIsOpen","handleKeyDown","fieldAnalysis","createDefaultState","buildInitialState","createStoreActions","set","_get","initialState","isEdit","state","filterId","portletId","rows","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","a","b","keysA","keysB","key","PortletChartBody","isTransparent","setChartContainerRef","setPortletComponentRef","renderQuery","renderChartType","renderChartConfig","renderDisplayConfig","isMarkdownAutoHeight","DashboardPortletCard","editable","layoutMode","configEagerLoad","containerProps","headerProps","setPortletRef","callbacks","icons","normalizedPortlet","chartModeConfig","isEditMode","selectedFilterId","debugData","isMarkdown","markdownAutoHeightRequested","isTransparentContent","shouldHideHeader","setDebugData","features","useCubeFeatures","CameraIcon","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","handleSetPortletRef","el","handleSetPortletComponentRef","handleSetChartContainerRef","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","row","isAutoHeightRow","col","normalized","chartMode","rowHeight","safeGridWidth","paddingLeft","paddingRight","unitWidth","column","width","COLOR_PALETTES","getColorPalette","paletteName","p","EditIcon","GridIcon","RowsIcon","AddIcon","TextIcon","SwatchIcon","FloatingEditToolbar","isEditBarVisible","onEditModeToggle","onLayoutModeChange","allowedModes","canChangeLayoutMode","currentPalette","onPaletteChange","onAddPortlet","onAddText","isPaletteOpen","setIsPaletteOpen","paletteRef","positionClasses","isHidden","toolbarContent","ToolbarButton","CompactPaletteDropdown","palette","Icon","tooltip","disabled","onClick","color","Modal","size","closeOnBackdropClick","closeOnEscape","showCloseButton","footer","noPadding","handleEscapeKey","LazyAnalysisBuilder","lazy","AnalysisBuilder","Suspense","generateMetricLabel","findFieldInSchema","fieldName","schema","getFieldTitle","found","schemaToFieldOptions","mode","isTime","filterFieldOptions","searchTerm","selectedCube","filtered","opt","term","groupFieldsByCube","grouped","getCubeNames","getCubeTitle","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","isValidFunnelConfig","funnelModeAdapter","storeState","charts","activeView","errors","warnings","names","s","duplicates","name","startingStep","flow","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","StringArrayInput","value","onChange","placeholder","description","localText","setLocalText","externalText","handleBlur","arrayValue","AnalysisDisplayConfigPanel","onDisplayConfigChange","excludeKeys","chartConfigLoaded","SectionHeading","isSelected","AxisFormatControls","CloseIcon","TextPortletModal","existingTitles","initialDisplayConfig","setDisplayConfig","setTitle","titleTouched","setTitleTouched","prevPortlet","setPrevPortlet","handleTitleChange","resolvedTitle","candidate","counter","handleDisplayConfigChange","finalDisplayConfig","handleContentChange","content","MarkdownChart","PortletFilterConfigModal","currentMapping","portletTitle","selectedFilters","setSelectedFilters","handleToggleFilter","id","formatFilterPreview","values","valuesText","filterCount","ConfirmModal","onConfirm","confirmText","cancelText","confirmVariant","baseClasses","ChevronDownIcon","ColorPaletteSelector","dropdownRef","currentPaletteObj","handlePaletteSelect","FieldSearchItem","isFocused","onMouseEnter","getFieldIcon","getMeasureTypeIcon","getFieldTypeIcon","getBadgeStyle","getTypeLabel","FieldSearchItem$1","memo","FieldDetailPanel","getIconBgStyle","getTypeDisplay","FieldDetailPanel$1","SearchIcon","FieldSearchModal","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","sum","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","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","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","timeoutId","handleApplyFixed","handleApplySince","formatDateForCube","handleApplyLast","tabButtonStyle","handleDropdownClick","tab","XTDDropdown","currentXTD","dateRangeText","FilterValueSelector","onValuesChange","hasLoadedInitial","setHasLoadedInitial","lastSearchedTerm","isDimension","shouldFetchValues","handleDropdownToggle","newIsOpen","handleSearchChange","newSearchText","handleDateRangeEndInput","cubeMeta","FilterValuePopover","anchorRef","popoverRef","handleValuesChange","metaResponse","FilterChip","onEdit","onRemove","showPopover","setShowPopover","chipRef","valueDisplay","handleValueChange","handleChipClick","CalendarIcon","CompactFilterBar","onDashboardFiltersChange","localFilters","setLocalFilters","showCustomDropdown","setShowCustomDropdown","showXTDDropdown","setShowXTDDropdown","customButtonRef","xtdButtonRef","universalTimeFilter","activePresetId","activeXTDId","nonDateFilters","generateFilterId","handleDateRangeChange","newDateRange","updatedFilters","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","isAutoHeight","portletHeight","contentHeight","useGridLayoutEngine","storeApi","newLayout","isInitialized","lastKnownLayout","newItem","oldItem","item","createRowId","equalizeRowColumns","portletIds","count","cols","minW","minTotal","base","remainder","remaining","extra","adjustRowWidths","columns","adjusted","total","overflow","reducible","delta","convertPortletsToRows","sorted","rowsByY","rowY","rowPortlets","normalizeRows","convertRowsToPortlets","currentY","updated","currentX","updatedIds","useRowLayoutEngine","draftRows","configRef","onConfigChangeRef","onSaveRef","setDraftRows","setThumbnailDirty","resolvedRows","baseRows","updateRowLayout","save","portletsOverride","normalizedRows","updatedPortlets","updatedConfig","useDashboardController","isResponsiveEditable","thumbnailConfig","dashboardRef","storeActions","onSaveThumbnailRef","layoutModeRef","canChangeLayoutModeRef","resolvedRowsRef","saveConfig","errorMessage","enterEditMode","startTransition","exitEditMode","thumbnailData","captureThumbnail","thumbnailUrl","toggleEditMode","selectFilter","currentSelectedId","openAddPortlet","openEditPortlet","openAddText","openEditText","openFilterConfig","handleLayoutModeChange","cfg","savePortlet","isNewPortlet","newPortletId","editingExisting","newPortlet","maxY","currentRows","nextRows","executeDeletePortlet","deletePortlet","confirmDelete","duplicatePortlet","originalPortlet","duplicatedPortlet","refreshPortlet","portletComponent","toggleFilterForPortlet","hasFilter","selectAllForFilter","saveFilterConfig","filterConfigPortlet","handlePaletteChange","selectStoreState","selectStoreActions","useDashboard","propAllowedModes","onConfigChange","onSaveThumbnail","useShallow","fallbackMode","configMode","selectedFilter","hasLayoutActuallyChanged","actions","ChartBarIcon","DeleteIcon","DesktopIcon","DEFAULT_GRID_SETTINGS","getGridSettings","DashboardGrid","dashboardModes","containerWidth","displayMode","useResponsiveDashboard","containerElementRef","editBarRef","gridContentRef","combinedContainerRef","foundScrollContainer","portletRefs","draftRowsRef","latestDragRowsRef","dragStateRef","dashboard","isPortletModalOpen","editingPortlet","isTextModalOpen","editingTextPortlet","isFilterConfigModalOpen","deleteConfirmPortletId","isDraggingPortlet","actionsRef","canEditRef","timer","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","handleNewRowDrop","newRow","handlePortletSave","scrollToPortlet","portletElement","handleDeletePortlet","handleDuplicatePortlet","handleAddPortlet","handleAddText","handleEditPortlet","handleOpenFilterConfig","handleSaveFilterConfig","portletIcons","handleToggleFilterForPortlet","handleFilterSelect","handleSelectAllForFilter","portletCallbacks","renderPortletCard","baseLayout","renderActiveLayout","ReactGridLayout","axis","verticalCompactor","AnalyticsDashboard","propDashboardFilters","onDirtyStateChange","handleConfigChangeWithDirtyTracking","handleSaveWithDirtyTracking","useDirtyStateTracking","mergedDashboardFilters","configFilters","propFilters","configFilter","propFilter","pf","configIds","cf","newFilters","handleDashboardFiltersChange","PortletContainer","onRefresh","DashboardEditModal","initialName","initialDescription","setName","setDescription","isSaving","setIsSaving","handleSubmit","handleClose"],"mappings":"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"}
|