drizzle-cube 0.6.4 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/ActivityGridChart.config-Bvi50jl_.cjs +1 -0
- package/dist/adapters/ActivityGridChart.config-BwswifOD.js +2 -0
- package/dist/adapters/AreaChart.config-Cehf1Kjo.cjs +1 -0
- package/dist/adapters/AreaChart.config-DC-gnmli.js +2 -0
- package/dist/adapters/BarChart.config-BY53r96T.cjs +1 -0
- package/dist/adapters/BarChart.config-DJN5Oqmw.js +2 -0
- package/dist/adapters/BoxPlotChart.config-9hLT3yMs.js +2 -0
- package/dist/adapters/BoxPlotChart.config-D9P4PMTf.cjs +1 -0
- package/dist/adapters/BubbleChart.config-CEVSsy7u.js +2 -0
- package/dist/adapters/BubbleChart.config-Cji0z0Tb.cjs +1 -0
- package/dist/adapters/CandlestickChart.config-DPv76FmM.cjs +1 -0
- package/dist/adapters/CandlestickChart.config-tcHTorLu.js +2 -0
- package/dist/adapters/DataTable.config-D4txBrlQ.cjs +1 -0
- package/dist/adapters/DataTable.config-DrObkanI.js +2 -0
- package/dist/adapters/FunnelChart.config-BFYSkibE.js +2 -0
- package/dist/adapters/FunnelChart.config-BTSFeha-.cjs +1 -0
- package/dist/adapters/GaugeChart.config-BFz6NZfT.js +2 -0
- package/dist/adapters/GaugeChart.config-DJKD6oh8.cjs +1 -0
- package/dist/adapters/HeatMapChart.config-C3EZl9Sy.cjs +1 -0
- package/dist/adapters/HeatMapChart.config-jUCb5NAE.js +2 -0
- package/dist/adapters/KpiDelta.config-DhuealNk.js +2 -0
- package/dist/adapters/KpiDelta.config-m6FEIeWo.cjs +1 -0
- package/dist/adapters/KpiNumber.config-Cmd7eHmg.cjs +1 -0
- package/dist/adapters/KpiNumber.config-KQdKrkK-.js +2 -0
- package/dist/adapters/KpiText.config-D_pU4jH3.cjs +1 -0
- package/dist/adapters/KpiText.config-V8SvhllA.js +2 -0
- package/dist/adapters/LineChart.config-BuWM0TJx.js +2 -0
- package/dist/adapters/LineChart.config-CsxJ3f5A.cjs +1 -0
- package/dist/adapters/MarkdownChart.config-CJdjenj7.cjs +1 -0
- package/dist/adapters/MarkdownChart.config-oCWrAuom.js +2 -0
- package/dist/adapters/MeasureProfileChart.config-DBLnhtOk.cjs +1 -0
- package/dist/adapters/MeasureProfileChart.config-DWg0jzxA.js +2 -0
- package/dist/adapters/PieChart.config-D3qI6nbM.cjs +1 -0
- package/dist/adapters/PieChart.config-R2rcul2D.js +2 -0
- package/dist/adapters/RadarChart.config-BlK8QuDi.js +2 -0
- package/dist/adapters/RadarChart.config-fH3ce10I.cjs +1 -0
- package/dist/adapters/RadialBarChart.config-CqCnb27I.js +2 -0
- package/dist/adapters/RadialBarChart.config-DQQHJOCs.cjs +1 -0
- package/dist/adapters/RetentionCombinedChart.config-BFJ8i-kp.js +2 -0
- package/dist/adapters/RetentionCombinedChart.config-B_Wd_YjX.cjs +1 -0
- package/dist/adapters/RetentionHeatmap.config-BLqu9-Ft.js +2 -0
- package/dist/adapters/RetentionHeatmap.config-MvOPw_Dd.cjs +1 -0
- package/dist/adapters/SankeyChart.config-C_4u9Xiq.js +2 -0
- package/dist/adapters/SankeyChart.config-EH6wsjPr.cjs +1 -0
- package/dist/adapters/ScatterChart.config-D-MlQ7mP.js +2 -0
- package/dist/adapters/ScatterChart.config-DmwiEBN-.cjs +1 -0
- package/dist/adapters/SunburstChart.config-BRr_as_L.js +2 -0
- package/dist/adapters/SunburstChart.config-CptDj8e4.cjs +1 -0
- package/dist/adapters/TreeMapChart.config-D5wX2xaT.cjs +1 -0
- package/dist/adapters/TreeMapChart.config-Dyhwpwq3.js +2 -0
- package/dist/adapters/WaterfallChart.config-B_LGJIov.js +2 -0
- package/dist/adapters/WaterfallChart.config-Q4Fv196W.cjs +1 -0
- package/dist/adapters/{compiler-DyyBaLry.cjs → compiler-D0v5KJhv.cjs} +2 -2
- package/dist/adapters/{compiler-lG6eNsoI.js → compiler-ZruQ85ff.js} +35 -30
- package/dist/adapters/core/index.cjs +1 -1
- package/dist/adapters/core/index.js +1 -1
- package/dist/adapters/{core-F1pCQPde.cjs → core-CVqTwOR3.cjs} +1 -1
- package/dist/adapters/{core-Bbchr0ui.js → core-DoSUiMmv.js} +60 -60
- package/dist/adapters/express/index.cjs +2 -2
- package/dist/adapters/express/index.js +80 -75
- package/dist/adapters/fastify/index.cjs +2 -2
- package/dist/adapters/fastify/index.js +63 -58
- package/dist/adapters/{handler-DRZb2ip6.cjs → handler-DyIyrFFp.cjs} +3 -3
- package/dist/adapters/{handler-u8FyzPBT.js → handler-j74EJDdo.js} +28 -28
- package/dist/adapters/hono/index.cjs +2 -2
- package/dist/adapters/hono/index.js +57 -52
- package/dist/adapters/mcp-tools.cjs +1 -1
- package/dist/adapters/mcp-tools.js +2 -2
- package/dist/adapters/mcp-transport-BuU5CrKL.cjs +40 -0
- package/dist/adapters/mcp-transport-l8DhKzR5.js +601 -0
- package/dist/adapters/mcp-transport.d.ts +21 -3
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.js +110 -105
- package/dist/adapters/{openai-CmMzrs1Y.js → openai-BCSKRcbD.js} +10 -10
- package/dist/{server/openai-DhzGY9R4.js → adapters/openai-BjR3ohaS.js} +1 -1
- package/dist/{server/openai-B4kAJTSD.cjs → adapters/openai-ClVVLjNl.cjs} +2 -2
- package/dist/adapters/{openai-Cdf2wWZA.cjs → openai-CsVXOcJZ.cjs} +1 -1
- package/dist/adapters/utils-CBR-9AMD.cjs +128 -0
- package/dist/adapters/{utils-BPWZsX7k.js → utils-U7gV5wQL.js} +789 -788
- package/dist/adapters/utils.cjs +1 -1
- package/dist/adapters/utils.d.ts +16 -4
- package/dist/adapters/utils.js +1 -1
- package/dist/cjs/adapters/mcp-transport.d.ts +21 -3
- package/dist/cjs/adapters/utils.d.ts +16 -4
- package/dist/cjs/server/executors/sqlite-executor.d.ts +8 -2
- package/dist/client/charts.js +13 -13
- package/dist/client/chunks/ConfirmModal-D6eLc9NO.js +3283 -0
- package/dist/client/chunks/ConfirmModal-D6eLc9NO.js.map +1 -0
- package/dist/client/chunks/{DashboardEditModal-Cs1ig1_V.js → DashboardEditModal-DFQSxR_C.js} +1134 -4389
- package/dist/client/chunks/DashboardEditModal-DFQSxR_C.js.map +1 -0
- package/dist/client/chunks/{RetentionCombinedChart-BgbDhsPz.js → RetentionCombinedChart-DY7wKLs5.js} +3 -3
- package/dist/client/chunks/{RetentionCombinedChart-BgbDhsPz.js.map → RetentionCombinedChart-DY7wKLs5.js.map} +1 -1
- package/dist/client/chunks/{RetentionHeatmap-DjXZaTPq.js → RetentionHeatmap-iNCG-dLd.js} +2 -2
- package/dist/client/chunks/{RetentionHeatmap-DjXZaTPq.js.map → RetentionHeatmap-iNCG-dLd.js.map} +1 -1
- package/dist/client/chunks/{analysis-builder-B64yaeDe.js → analysis-builder-CBZ7lOC-.js} +311 -311
- package/dist/client/chunks/{analysis-builder-B64yaeDe.js.map → analysis-builder-CBZ7lOC-.js.map} +1 -1
- package/dist/client/chunks/{analysis-builder-shared-CD0n2Nb8.js → analysis-builder-shared-8alCn-zw.js} +411 -411
- package/dist/client/chunks/{analysis-builder-shared-CD0n2Nb8.js.map → analysis-builder-shared-8alCn-zw.js.map} +1 -1
- package/dist/client/chunks/{chart-activity-grid-DX0SJbxs.js → chart-activity-grid-DDqFVWoa.js} +5 -2
- package/dist/client/chunks/{chart-activity-grid-DX0SJbxs.js.map → chart-activity-grid-DDqFVWoa.js.map} +1 -1
- package/dist/client/chunks/{chart-area-of01_62R.js → chart-area-BHxPNsmc.js} +2 -2
- package/dist/client/chunks/{chart-area-of01_62R.js.map → chart-area-BHxPNsmc.js.map} +1 -1
- package/dist/client/chunks/{chart-bar-BqelQE_I.js → chart-bar-DAO3PPcE.js} +3 -3
- package/dist/client/chunks/{chart-bar-BqelQE_I.js.map → chart-bar-DAO3PPcE.js.map} +1 -1
- package/dist/client/chunks/{chart-box-plot-kkBixZ27.js → chart-box-plot-CUpFyQQk.js} +2 -2
- package/dist/client/chunks/{chart-box-plot-kkBixZ27.js.map → chart-box-plot-CUpFyQQk.js.map} +1 -1
- package/dist/client/chunks/{chart-bubble-B2he1--4.js → chart-bubble-DvP_6_n9.js} +2 -2
- package/dist/client/chunks/{chart-bubble-B2he1--4.js.map → chart-bubble-DvP_6_n9.js.map} +1 -1
- package/dist/client/chunks/{chart-candlestick-DZp19Tzh.js → chart-candlestick-Dx8ZCMzl.js} +2 -2
- package/dist/client/chunks/{chart-candlestick-DZp19Tzh.js.map → chart-candlestick-Dx8ZCMzl.js.map} +1 -1
- package/dist/client/chunks/{chart-data-table-D4g5_jfF.js → chart-data-table-BP6DMdom.js} +32 -16
- package/dist/client/chunks/chart-data-table-BP6DMdom.js.map +1 -0
- package/dist/client/chunks/{chart-funnel-CToKSBaL.js → chart-funnel-xeVGBy-p.js} +2 -2
- package/dist/client/chunks/{chart-funnel-CToKSBaL.js.map → chart-funnel-xeVGBy-p.js.map} +1 -1
- package/dist/client/chunks/{chart-gauge-DTVezSk7.js → chart-gauge-DXQ4r3cb.js} +3 -3
- package/dist/client/chunks/{chart-gauge-DTVezSk7.js.map → chart-gauge-DXQ4r3cb.js.map} +1 -1
- package/dist/client/chunks/{chart-heat-map-DTuUl0BR.js → chart-heat-map-Pnk6rhqQ.js} +3 -3
- package/dist/client/chunks/{chart-heat-map-DTuUl0BR.js.map → chart-heat-map-Pnk6rhqQ.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-delta-BvWOt8-D.js → chart-kpi-delta-nXQnRRBr.js} +3 -3
- package/dist/client/chunks/{chart-kpi-delta-BvWOt8-D.js.map → chart-kpi-delta-nXQnRRBr.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-number-xgZmMiQY.js → chart-kpi-number-m8UGJfBr.js} +5 -5
- package/dist/client/chunks/{chart-kpi-number-xgZmMiQY.js.map → chart-kpi-number-m8UGJfBr.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-text-ZO3eFi7t.js → chart-kpi-text-CurpqmaD.js} +7 -4
- package/dist/client/chunks/{chart-kpi-text-ZO3eFi7t.js.map → chart-kpi-text-CurpqmaD.js.map} +1 -1
- package/dist/client/chunks/{chart-line-DKgrQA6c.js → chart-line-sJK-cJc0.js} +3 -3
- package/dist/client/chunks/{chart-line-DKgrQA6c.js.map → chart-line-sJK-cJc0.js.map} +1 -1
- package/dist/client/chunks/{chart-markdown-CV75S_zL.js → chart-markdown-BVrqqygz.js} +1263 -1192
- package/dist/client/chunks/chart-markdown-BVrqqygz.js.map +1 -0
- package/dist/client/chunks/{chart-measure-profile-Ket8fJyf.js → chart-measure-profile-k9_QDQbB.js} +3 -3
- package/dist/client/chunks/{chart-measure-profile-Ket8fJyf.js.map → chart-measure-profile-k9_QDQbB.js.map} +1 -1
- package/dist/client/chunks/{chart-pie-ByieAX5H.js → chart-pie-BDaaYt7a.js} +3 -3
- package/dist/client/chunks/{chart-pie-ByieAX5H.js.map → chart-pie-BDaaYt7a.js.map} +1 -1
- package/dist/client/chunks/{chart-radar-CiSwHWp4.js → chart-radar-Bz07PIVe.js} +3 -3
- package/dist/client/chunks/{chart-radar-CiSwHWp4.js.map → chart-radar-Bz07PIVe.js.map} +1 -1
- package/dist/client/chunks/{chart-radial-bar-BF6Mhl6f.js → chart-radial-bar-DpyxKQMj.js} +3 -3
- package/dist/client/chunks/{chart-radial-bar-BF6Mhl6f.js.map → chart-radial-bar-DpyxKQMj.js.map} +1 -1
- package/dist/client/chunks/{chart-sankey-DjrvKw6K.js → chart-sankey-BAeNbI-p.js} +2 -2
- package/dist/client/chunks/{chart-sankey-DjrvKw6K.js.map → chart-sankey-BAeNbI-p.js.map} +1 -1
- package/dist/client/chunks/{chart-scatter-Dd9WFqnS.js → chart-scatter-CRhGLCcy.js} +3 -3
- package/dist/client/chunks/{chart-scatter-Dd9WFqnS.js.map → chart-scatter-CRhGLCcy.js.map} +1 -1
- package/dist/client/chunks/{chart-sunburst-leGpuDj7.js → chart-sunburst-D3wV5OrK.js} +3 -3
- package/dist/client/chunks/{chart-sunburst-leGpuDj7.js.map → chart-sunburst-D3wV5OrK.js.map} +1 -1
- package/dist/client/chunks/{chart-tree-map-Ceq5sLZA.js → chart-tree-map-iI3ewOKY.js} +3 -3
- package/dist/client/chunks/{chart-tree-map-Ceq5sLZA.js.map → chart-tree-map-iI3ewOKY.js.map} +1 -1
- package/dist/client/chunks/{chart-waterfall-DyvQReN5.js → chart-waterfall-BrrYTZfs.js} +3 -3
- package/dist/client/chunks/{chart-waterfall-DyvQReN5.js.map → chart-waterfall-BrrYTZfs.js.map} +1 -1
- package/dist/client/chunks/{charts-core-DaXSt1Dd.js → charts-core-Cs-UIsQa.js} +2 -2
- package/dist/client/chunks/{charts-core-DaXSt1Dd.js.map → charts-core-Cs-UIsQa.js.map} +1 -1
- package/dist/client/chunks/charts-loader-DAJbAD7a.js +84 -0
- package/dist/client/chunks/{charts-loader-B0DOQMFH.js.map → charts-loader-DAJbAD7a.js.map} +1 -1
- package/dist/client/chunks/core-D7vPfUIe.js.map +1 -1
- package/dist/client/chunks/{dist-zPWyTBdU.js → dist-BwQr11Fz.js} +251 -252
- package/dist/client/chunks/{dist-zPWyTBdU.js.map → dist-BwQr11Fz.js.map} +1 -1
- package/dist/client/chunks/{exceljs.min-CIqBqwTW.js → exceljs.min-Dey-qIFL.js} +16 -16
- package/dist/client/chunks/{exceljs.min-CIqBqwTW.js.map → exceljs.min-Dey-qIFL.js.map} +1 -1
- package/dist/client/chunks/javascript-DFnKaHmQ.js.map +1 -1
- package/dist/client/chunks/retention-ChW9jYdy.js.map +1 -1
- package/dist/client/chunks/{schema-visualization-DJPTktM6.js → schema-visualization-BGT_cNon.js} +12 -12
- package/dist/client/chunks/{schema-visualization-DJPTktM6.js.map → schema-visualization-BGT_cNon.js.map} +1 -1
- package/dist/client/chunks/sql-CmVzGP4w.js.map +1 -1
- package/dist/client/chunks/syntaxHighlighting-Bq2Bj9Xt.js +30 -0
- package/dist/client/chunks/syntaxHighlighting-Bq2Bj9Xt.js.map +1 -0
- package/dist/client/chunks/{useDirtyStateTracking-BT6zqCFl.js → useDebounce-7suCFq9v.js} +47 -109
- package/dist/client/chunks/useDebounce-7suCFq9v.js.map +1 -0
- package/dist/client/chunks/useDirtyStateTracking-D2OiqKoQ.js +66 -0
- package/dist/client/chunks/useDirtyStateTracking-D2OiqKoQ.js.map +1 -0
- package/dist/client/chunks/{useExplainAI-Dq_meKNN.js → useExplainAI-CEf04DPE.js} +14 -14
- package/dist/client/chunks/{useExplainAI-Dq_meKNN.js.map → useExplainAI-CEf04DPE.js.map} +1 -1
- package/dist/client/chunks/useNotebookLayout-Ck3Z3uzg.js.map +1 -1
- package/dist/client/chunks/utils-DjLiJKS_.js +104 -0
- package/dist/client/chunks/utils-DjLiJKS_.js.map +1 -0
- package/dist/client/chunks/{vendor-D4jADu8t.js → vendor-DFAQ4Ucl.js} +5 -3
- package/dist/client/chunks/{vendor-D4jADu8t.js.map → vendor-DFAQ4Ucl.js.map} +1 -1
- package/dist/client/components.js +4 -3
- package/dist/client/hooks.js +5 -4
- package/dist/client/icons.js +1 -1
- package/dist/client/index.js +33 -29
- package/dist/client/index.js.map +1 -1
- package/dist/client/providers.js +1 -1
- package/dist/client/schema.js +1 -1
- package/dist/client/styles.css +1 -1
- package/dist/client/utils.js +32 -6
- package/dist/client/utils.js.map +1 -0
- package/dist/client-bundle-stats.html +1 -1
- package/dist/mcp-app/mcp-app.html +145 -50
- package/dist/server/executors/sqlite-executor.d.ts +8 -2
- package/dist/server/index.cjs +33 -33
- package/dist/server/index.js +1268 -1262
- package/dist/server/{openai-CmMzrs1Y.js → openai-BCSKRcbD.js} +10 -10
- package/dist/{adapters/openai-DhzGY9R4.js → server/openai-BjR3ohaS.js} +1 -1
- package/dist/{adapters/openai-B4kAJTSD.cjs → server/openai-ClVVLjNl.cjs} +2 -2
- package/dist/server/{openai-Cdf2wWZA.cjs → openai-CsVXOcJZ.cjs} +1 -1
- package/package.json +11 -7
- package/dist/adapters/ActivityGridChart.config-BtECymia.cjs +0 -1
- package/dist/adapters/ActivityGridChart.config-CTd5HYjI.js +0 -2
- package/dist/adapters/AreaChart.config-1B8iQnfn.js +0 -2
- package/dist/adapters/AreaChart.config-hnncyQL4.cjs +0 -1
- package/dist/adapters/BarChart.config-C-QCf1rL.cjs +0 -1
- package/dist/adapters/BarChart.config-CFelK1qP.js +0 -2
- package/dist/adapters/BoxPlotChart.config-CD9WEPyF.js +0 -2
- package/dist/adapters/BoxPlotChart.config-DEbLA0BX.cjs +0 -1
- package/dist/adapters/BubbleChart.config-B_M4hzzV.js +0 -2
- package/dist/adapters/BubbleChart.config-dRZZlAke.cjs +0 -1
- package/dist/adapters/CandlestickChart.config-Dsil2Fop.cjs +0 -1
- package/dist/adapters/CandlestickChart.config-fuhyFvob.js +0 -2
- package/dist/adapters/DataTable.config-q2gqBi6t.cjs +0 -1
- package/dist/adapters/DataTable.config-wIQnHX5R.js +0 -2
- package/dist/adapters/FunnelChart.config-Dz710ssx.js +0 -2
- package/dist/adapters/FunnelChart.config-YNP9CeS-.cjs +0 -1
- package/dist/adapters/GaugeChart.config-BG9FcMCn.cjs +0 -1
- package/dist/adapters/GaugeChart.config-CgwniXkd.js +0 -2
- package/dist/adapters/HeatMapChart.config-1zSMXXoN.cjs +0 -1
- package/dist/adapters/HeatMapChart.config-YVEWx6s5.js +0 -2
- package/dist/adapters/KpiDelta.config-BWvYXuFp.cjs +0 -1
- package/dist/adapters/KpiDelta.config-C_dYM64q.js +0 -2
- package/dist/adapters/KpiNumber.config-CXg6toZG.js +0 -2
- package/dist/adapters/KpiNumber.config-CZmV-yU1.cjs +0 -1
- package/dist/adapters/KpiText.config-BY-SJvAF.js +0 -2
- package/dist/adapters/KpiText.config-GSIxy0CE.cjs +0 -1
- package/dist/adapters/LineChart.config-CJtF8SOu.js +0 -2
- package/dist/adapters/LineChart.config-d0y60gw9.cjs +0 -1
- package/dist/adapters/MarkdownChart.config-B6w0spya.cjs +0 -1
- package/dist/adapters/MarkdownChart.config-BuIQcWOn.js +0 -2
- package/dist/adapters/MeasureProfileChart.config-Bo2n5Ae5.cjs +0 -1
- package/dist/adapters/MeasureProfileChart.config-C5d34lsc.js +0 -2
- package/dist/adapters/PieChart.config-CDxfXCIt.cjs +0 -1
- package/dist/adapters/PieChart.config-CuZxQc5m.js +0 -2
- package/dist/adapters/RadarChart.config-DpdVWyh5.cjs +0 -1
- package/dist/adapters/RadarChart.config-KrxwaaGu.js +0 -2
- package/dist/adapters/RadialBarChart.config-DpRZd0FW.js +0 -2
- package/dist/adapters/RadialBarChart.config-faIY4jpE.cjs +0 -1
- package/dist/adapters/RetentionCombinedChart.config-CUnScqnS.cjs +0 -1
- package/dist/adapters/RetentionCombinedChart.config-Dd8FuawP.js +0 -2
- package/dist/adapters/RetentionHeatmap.config-CPd6GeoQ.js +0 -2
- package/dist/adapters/RetentionHeatmap.config-CPlHbLom.cjs +0 -1
- package/dist/adapters/SankeyChart.config-DbIUqi-H.cjs +0 -1
- package/dist/adapters/SankeyChart.config-kzAnW0C7.js +0 -2
- package/dist/adapters/ScatterChart.config-4Okvn27v.js +0 -2
- package/dist/adapters/ScatterChart.config-4f9nNw3e.cjs +0 -1
- package/dist/adapters/SunburstChart.config-BsUQkIM_.js +0 -2
- package/dist/adapters/SunburstChart.config-lvnYkWs7.cjs +0 -1
- package/dist/adapters/TreeMapChart.config-S0qsgAFW.js +0 -2
- package/dist/adapters/TreeMapChart.config-jbwKsTYt.cjs +0 -1
- package/dist/adapters/WaterfallChart.config-CDQ74-pS.cjs +0 -1
- package/dist/adapters/WaterfallChart.config-XdtH-O5g.js +0 -2
- package/dist/adapters/mcp-transport-Bw_zqky2.js +0 -591
- package/dist/adapters/mcp-transport-CNIPoe1i.cjs +0 -40
- package/dist/adapters/utils-_whV4fXL.cjs +0 -128
- package/dist/client/chunks/DashboardEditModal-Cs1ig1_V.js.map +0 -1
- package/dist/client/chunks/chart-data-table-D4g5_jfF.js.map +0 -1
- package/dist/client/chunks/chart-markdown-CV75S_zL.js.map +0 -1
- package/dist/client/chunks/charts-loader-B0DOQMFH.js +0 -84
- package/dist/client/chunks/useDirtyStateTracking-BT6zqCFl.js.map +0 -1
- package/dist/client/chunks/utils-Cq-j5E5I.js +0 -155
- package/dist/client/chunks/utils-Cq-j5E5I.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfirmModal-D6eLc9NO.js","names":[],"sources":["../../../src/client/types/analysisConfig.ts","../../../src/client/utils/configMigration.ts","../../../src/client/shared/filters/operators.ts","../../../src/client/utils/filterUtils.ts","../../../src/client/shared/components/CodeBlock.tsx","../../../src/client/utils/colorPalettes.ts","../../../src/client/components/ColorPaletteSelector.tsx","../../../src/client/components/AnalysisBuilder/utils/idUtils.ts","../../../src/client/components/AnalysisBuilder/utils/fieldUtils.ts","../../../src/client/components/AnalysisBuilder/utils/recentFieldsUtils.ts","../../../src/client/components/AnalysisBuilder/FieldDetailPanel.tsx","../../../src/client/components/AnalysisBuilder/FieldSearchItem.tsx","../../../src/client/components/AnalysisBuilder/FieldSearchResults.tsx","../../../src/client/components/AnalysisBuilder/hooks/useFieldSearchKeyboard.ts","../../../src/client/components/AnalysisBuilder/FieldSearchModal.tsx","../../../src/client/shared/types.ts","../../../src/client/components/shared/dateRangeUtils.ts","../../../src/client/components/Modal.tsx","../../../src/client/adapters/funnelModeAdapter.ts","../../../src/client/adapters/flowModeAdapter.ts","../../../src/client/adapters/retentionModeAdapter.ts","../../../src/client/components/AnalysisBuilder/StringArrayInput.tsx","../../../src/client/components/AnalysisBuilder/DisplayOptionControl.tsx","../../../src/client/components/AnalysisBuilder/LegacyBooleanOptions.tsx","../../../src/client/components/AnalysisBuilder/AnalysisDisplayConfigPanel.tsx","../../../src/client/components/ConfirmModal.tsx"],"sourcesContent":["/**\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.js'\nimport type { ServerFunnelQuery } from './funnel.js'\nimport type { ServerFlowQuery } from './flow.js'\nimport type { ServerRetentionQuery } from './retention.js'\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.js'\nimport { createDefaultQueryConfig, isValidAnalysisConfig } from '../types/analysisConfig.js'\nimport type {\n ChartType,\n ChartAxisConfig,\n ChartDisplayConfig,\n CubeQuery,\n MultiQueryConfig,\n FunnelBindingKey,\n QueryMergeStrategy,\n PortletConfig,\n} from '../types.js'\nimport type { ServerFunnelQuery, ServerFunnelStep } from '../types/funnel.js'\nimport type { ServerFlowQuery } from '../types/flow.js'\nimport { isServerRetentionQuery } from '../types/retention.js'\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 operator definitions.\n *\n * The single source of truth for which operators exist and their metadata\n * (value requirements, supported field types). Consumed by both the dashboard\n * filter UI and the analysis-builder filter UI. Labels/descriptions are i18n\n * keys resolved at render time, never bare strings.\n */\n\nimport type { FilterOperator } from '../../types.js'\n\nexport interface FilterOperatorMeta {\n label: string\n description: string\n requiresValues: boolean\n supportsMultipleValues: boolean\n valueType: 'string' | 'number' | 'date' | 'boolean' | 'any'\n fieldTypes: string[] // Which field types support this operator\n}\n\nexport const FILTER_OPERATORS: Record<FilterOperator, FilterOperatorMeta> = {\n // String operators\n equals: {\n label: 'filter.operator.equals.label',\n description: 'filter.operator.equals.description',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'boolean', 'time']\n },\n notEquals: {\n label: 'filter.operator.notEquals.label',\n description: 'filter.operator.notEquals.description',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'boolean', 'time']\n },\n contains: {\n label: 'filter.operator.contains.label',\n description: 'filter.operator.contains.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notContains: {\n label: 'filter.operator.notContains.label',\n description: 'filter.operator.notContains.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n startsWith: {\n label: 'filter.operator.startsWith.label',\n description: 'filter.operator.startsWith.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notStartsWith: {\n label: 'filter.operator.notStartsWith.label',\n description: 'filter.operator.notStartsWith.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n endsWith: {\n label: 'filter.operator.endsWith.label',\n description: 'filter.operator.endsWith.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notEndsWith: {\n label: 'filter.operator.notEndsWith.label',\n description: 'filter.operator.notEndsWith.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n like: {\n label: 'filter.operator.like.label',\n description: 'filter.operator.like.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notLike: {\n label: 'filter.operator.notLike.label',\n description: 'filter.operator.notLike.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n ilike: {\n label: 'filter.operator.ilike.label',\n description: 'filter.operator.ilike.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n // Numeric operators\n gt: {\n label: 'filter.operator.gt.label',\n description: 'filter.operator.gt.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n gte: {\n label: 'filter.operator.gte.label',\n description: 'filter.operator.gte.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n lt: {\n label: 'filter.operator.lt.label',\n description: 'filter.operator.lt.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n lte: {\n label: 'filter.operator.lte.label',\n description: 'filter.operator.lte.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n between: {\n label: 'filter.operator.between.label',\n description: 'filter.operator.between.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n notBetween: {\n label: 'filter.operator.notBetween.label',\n description: 'filter.operator.notBetween.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'number',\n fieldTypes: ['number', 'count', 'sum', 'avg', 'min', 'max']\n },\n // Array operators\n in: {\n label: 'filter.operator.in.label',\n description: 'filter.operator.in.description',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'boolean']\n },\n notIn: {\n label: 'filter.operator.notIn.label',\n description: 'filter.operator.notIn.description',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'boolean']\n },\n // Null/Empty operators\n set: {\n label: 'filter.operator.set.label',\n description: 'filter.operator.set.description',\n requiresValues: false,\n supportsMultipleValues: false,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'time', 'boolean']\n },\n notSet: {\n label: 'filter.operator.notSet.label',\n description: 'filter.operator.notSet.description',\n requiresValues: false,\n supportsMultipleValues: false,\n valueType: 'any',\n fieldTypes: ['string', 'number', 'time', 'boolean']\n },\n isEmpty: {\n label: 'filter.operator.isEmpty.label',\n description: 'filter.operator.isEmpty.description',\n requiresValues: false,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n isNotEmpty: {\n label: 'filter.operator.isNotEmpty.label',\n description: 'filter.operator.isNotEmpty.description',\n requiresValues: false,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n // Date operators\n inDateRange: {\n label: 'filter.operator.inDateRange.label',\n description: 'filter.operator.inDateRange.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'date',\n fieldTypes: ['time']\n },\n beforeDate: {\n label: 'filter.operator.beforeDate.label',\n description: 'filter.operator.beforeDate.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'date',\n fieldTypes: ['time']\n },\n afterDate: {\n label: 'filter.operator.afterDate.label',\n description: 'filter.operator.afterDate.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'date',\n fieldTypes: ['time']\n },\n // Regex operators\n regex: {\n label: 'filter.operator.regex.label',\n description: 'filter.operator.regex.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n notRegex: {\n label: 'filter.operator.notRegex.label',\n description: 'filter.operator.notRegex.description',\n requiresValues: true,\n supportsMultipleValues: false,\n valueType: 'string',\n fieldTypes: ['string']\n },\n // PostgreSQL array operators\n arrayContains: {\n label: 'filter.operator.arrayContains.label',\n description: 'filter.operator.arrayContains.description',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'string',\n fieldTypes: ['string']\n },\n arrayOverlaps: {\n label: 'filter.operator.arrayOverlaps.label',\n description: 'filter.operator.arrayOverlaps.description',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'string',\n fieldTypes: ['string']\n },\n arrayContained: {\n label: 'filter.operator.arrayContained.label',\n description: 'filter.operator.arrayContained.description',\n requiresValues: true,\n supportsMultipleValues: true,\n valueType: 'string',\n fieldTypes: ['string']\n }\n}\n\n/**\n * Field types that drive a non-numeric operator set. Every other field type —\n * including all measure aggregation types (count, countDistinct, sum, avg,\n * median, stddev, calculated, movingAvg, rank, …) — is treated as numeric for\n * the purpose of operator availability and validation.\n */\nconst NON_NUMERIC_FILTER_FIELD_TYPES = new Set(['string', 'time', 'boolean'])\n\n/**\n * Normalize a raw field/measure type to the category used by FILTER_OPERATORS.\n * Measure aggregation types collapse to 'number' so the numeric operators apply\n * (without this, e.g. a `countDistinct` measure would expose no operators).\n */\nexport function normalizeFilterFieldType(fieldType: string): string {\n return NON_NUMERIC_FILTER_FIELD_TYPES.has(fieldType) ? fieldType : 'number'\n}\n\n/**\n * Get available operators for a field type\n */\nexport function getAvailableOperators(fieldType: string): Array<{ operator: string; label: string }> {\n const normalized = normalizeFilterFieldType(fieldType)\n const operators: Array<{ operator: string; label: string }> = []\n\n for (const [operator, meta] of Object.entries(FILTER_OPERATORS)) {\n if (meta.fieldTypes.includes(normalized)) {\n operators.push({\n operator,\n label: meta.label\n })\n }\n }\n\n return operators\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 {\n Filter,\n DashboardFilter,\n DashboardFilterMapping,\n DashboardFilterMappingEntry,\n CubeMeta,\n GroupFilter,\n DashboardConfig,\n SimpleFilter\n} from '../types.js'\nimport { ensureAnalysisConfig } from './configMigration.js'\nimport {\n transformFiltersForServer,\n validateFilterForCube,\n extractFilterMembers\n} from '../shared/filters/index.js'\n\nexport { validateFilterForCube }\n\n/**\n * Normalize a portlet filter mapping to entry objects.\n * Plain string entries (the legacy format) become { filterId } with no override.\n */\nexport function normalizeFilterMapping(\n mapping: DashboardFilterMapping | undefined\n): DashboardFilterMappingEntry[] {\n if (!mapping) return []\n return mapping.map(entry =>\n typeof entry === 'string' ? { filterId: entry } : entry\n )\n}\n\n/**\n * Serialize mapping entries for storage.\n * Entries without a member override collapse back to plain strings so saved\n * configs only change shape when an override is actually used.\n */\nexport function serializeFilterMapping(\n entries: DashboardFilterMappingEntry[]\n): DashboardFilterMapping {\n return entries.map(entry => (entry.member ? entry : entry.filterId))\n}\n\n/**\n * Check whether a mapping (either format) includes a given dashboard filter\n */\nexport function mappingIncludesFilter(\n mapping: DashboardFilterMapping | undefined,\n filterId: string\n): boolean {\n if (!mapping) return false\n return mapping.some(entry =>\n typeof entry === 'string' ? entry === filterId : entry.filterId === filterId\n )\n}\n\n/**\n * Get the per-portlet member override for a dashboard filter, if any\n */\nexport function getMappingMemberOverride(\n mapping: DashboardFilterMapping | undefined,\n filterId: string\n): string | undefined {\n if (!mapping) return undefined\n const entry = mapping.find(e =>\n typeof e === 'string' ? e === filterId : e.filterId === filterId\n )\n return typeof entry === 'object' ? entry.member : undefined\n}\n\n/**\n * Clone a filter with its member rewritten to the override field.\n * Only SimpleFilters can be rewritten; GroupFilters pass through unchanged.\n */\nexport function applyMemberOverride(filter: Filter, member?: string): Filter {\n if (!member) return filter\n if ('member' in filter && 'operator' in filter) {\n return { ...filter, member }\n }\n return filter\n}\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 * Mapping entries may carry a per-portlet member override, in which case the\n * filter is applied with its member rewritten to the override field (operator\n * and values stay shared across all portlets).\n *\n * @param dashboardFilters - All available dashboard filters\n * @param filterMapping - The portlet's filter mapping (filter IDs or entries with member overrides)\n * @returns Array of filters that should be applied to the portlet\n */\nexport function getApplicableDashboardFilters(\n dashboardFilters: DashboardFilter[] | undefined,\n filterMapping: DashboardFilterMapping | 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 // rewriting the member where the mapping carries an override\n return dashboardFilters\n .filter(df => mappingIncludesFilter(filterMapping, df.id))\n .filter(df => shouldIncludeFilter(df.filter))\n .map(df => applyMemberOverride(df.filter, getMappingMemberOverride(filterMapping, df.id)))\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 = transformFiltersForServer([...dashboardFilters, ...portletFilters])\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 * 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: DashboardFilterMapping | 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 const entries = normalizeFilterMapping(filterMapping)\n\n if (!dashboardFilters || !dashboardFilters.length) {\n // Mapping references filters that don't exist\n return {\n isValid: false,\n invalidFilterIds: [],\n missingFilterIds: entries.map(e => e.filterId)\n }\n }\n\n const invalidFilterIds: string[] = []\n const missingFilterIds: string[] = []\n\n entries.forEach(({ filterId, member }) => {\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 if (member && !('member' in dashboardFilter.filter)) {\n // A member override on a group filter cannot be applied\n invalidFilterIds.push(filterId)\n } else {\n // Validate the effective filter (with any member override applied)\n const effectiveFilter = applyMemberOverride(dashboardFilter.filter, member)\n const isValid = validateFilterForCube(effectiveFilter, 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 extractFilterMembers(cubeQuery.filters).forEach((field: string) => {\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 * 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: DashboardFilterMapping | 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 && mappingIncludesFilter(filterMapping, 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 * 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/index.js'\nimport { getSyntaxHighlighter, loadSyntaxHighlighter } from '../../utils/syntaxHighlighting.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\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 { t } = useTranslation()\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\">{t('common.actions.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\">{t('common.actions.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","/**\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 * 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.js'\nimport { getIcon } from '../icons/index.js'\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 * 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.js'\nimport type { MetaResponse, MetaField } from '../../../shared/types.js'\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 * Build a measure field option from cube + measure metadata\n */\nfunction measureToOption(cubeName: string, measure: MetaField): FieldOption {\n return {\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,\n fieldType: 'measure'\n }\n}\n\n/**\n * Build a dimension field option from cube + dimension metadata\n */\nfunction dimensionToOption(cubeName: string, dimension: MetaField): FieldOption {\n return {\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,\n fieldType: dimension.type === 'time' ? 'timeDimension' : 'dimension'\n }\n}\n\n/**\n * Convert schema to flat list of field options\n *\n * - 'metrics': measures only\n * - 'breakdown' / 'dimensionFilter': dimensions only ('dimensionFilter' is used\n * for funnel step filters where measures don't work)\n * - 'filter': both measures and dimensions\n */\nexport function schemaToFieldOptions(\n schema: MetaResponse | null,\n mode: 'metrics' | 'breakdown' | 'filter' | 'dimensionFilter'\n): FieldOption[] {\n if (!schema) return []\n\n const includeMeasures = mode === 'metrics' || mode === 'filter'\n const includeDimensions = mode !== 'metrics'\n\n const options: FieldOption[] = []\n\n for (const cube of schema.cubes) {\n if (includeMeasures) {\n for (const measure of cube.measures) {\n options.push(measureToOption(cube.name, measure))\n }\n }\n if (includeDimensions) {\n for (const dimension of cube.dimensions) {\n options.push(dimensionToOption(cube.name, dimension))\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.js'\nimport type { MetaResponse } from '../../../shared/types.js'\nimport { schemaToFieldOptions } from './fieldUtils.js'\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 * 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/index.js'\nimport type { FieldDetailPanelProps } from './types.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\nfunction FieldDetailPanel({ field }: FieldDetailPanelProps) {\n const { t } = useTranslation()\n if (!field) {\n return (\n <div className=\"dc:p-6 dc:text-center text-dc-text-muted\">\n <p className=\"dc:text-sm\">{t('fieldPanel.emptyState')}</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: t('fieldTypes.count'),\n countDistinct: t('fieldTypes.countDistinct'),\n countDistinctApprox: t('fieldTypes.countDistinctApprox'),\n sum: t('fieldTypes.sum'),\n avg: t('fieldTypes.avg'),\n min: t('fieldTypes.min'),\n max: t('fieldTypes.max'),\n runningTotal: t('fieldTypes.runningTotal'),\n number: t('fieldTypes.number')\n }\n return typeMap[field.type] || field.type\n } else if (field.fieldType === 'timeDimension') {\n return t('fieldTypes.time')\n } else {\n const typeMap: Record<string, string> = {\n string: t('fieldTypes.string'),\n number: t('fieldTypes.number'),\n boolean: t('fieldTypes.boolean'),\n geo: t('fieldTypes.geo')\n }\n return typeMap[field.type] || t('fieldTypes.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\">{t('fieldPanel.labels.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\">{t('fieldPanel.labels.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\">{t('fieldPanel.labels.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 ? t('fieldCategory.measure')\n : field.fieldType === 'timeDimension'\n ? t('fieldCategory.timeDimension')\n : t('fieldCategory.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 {t('fieldPanel.usageHint')}\n </p>\n </div>\n </div>\n )\n}\n\nexport default memo(FieldDetailPanel)\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/index.js'\nimport type { FieldSearchItemProps } from './types.js'\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 * FieldSearchResults Component\n *\n * Renders the middle results column of FieldSearchModal: the empty state,\n * recent fields, and cube-grouped field lists. Extracted to keep the modal's\n * render body small.\n */\n\nimport { memo, MouseEvent } from 'react'\nimport type { FieldOption } from './types.js'\nimport type { MetaResponse } from '../../shared/types.js'\nimport FieldSearchItem from './FieldSearchItem.js'\nimport { getCubeTitle } from './utils/index.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\ninterface FieldSearchResultsProps {\n mode: 'metrics' | 'breakdown' | 'filter' | 'dimensionFilter'\n schema: MetaResponse | null\n searchTerm: string\n recentOptions: FieldOption[]\n groupedFields: Map<string, FieldOption[]>\n filteredCount: number\n selectedFields: string[]\n focusedIndex: number\n onSelectField: (field: FieldOption, fieldIndex: number, shiftKey: boolean) => void\n onFocusField: (field: FieldOption, index: number) => void\n}\n\n/**\n * Compute the flat-list index of a field within its cube group, accounting for\n * the recent-options prefix and all preceding cube groups.\n */\nfunction computeFieldIndex(\n groupedFields: Map<string, FieldOption[]>,\n recentCount: number,\n cubeName: string,\n fields: FieldOption[],\n field: FieldOption\n): number {\n const entries = Array.from(groupedFields.entries())\n const cubePosition = Array.from(groupedFields.keys()).indexOf(cubeName)\n const precedingCount = entries\n .slice(0, cubePosition)\n .reduce((sum, [, f]) => sum + f.length, 0)\n return recentCount + precedingCount + fields.indexOf(field)\n}\n\nconst FieldSearchResults = memo(function FieldSearchResults({\n mode,\n schema,\n searchTerm,\n recentOptions,\n groupedFields,\n filteredCount,\n selectedFields,\n focusedIndex,\n onSelectField,\n onFocusField\n}: FieldSearchResultsProps) {\n const { t } = useTranslation()\n\n if (filteredCount === 0 && recentOptions.length === 0) {\n let emptyDetail: string\n if (searchTerm) {\n emptyDetail = mode === 'metrics'\n ? t('fieldSearch.empty.noMatchMetrics', { searchTerm })\n : t('fieldSearch.empty.noMatchDimensions', { searchTerm })\n } else {\n emptyDetail = mode === 'metrics'\n ? t('fieldSearch.empty.noMetrics')\n : t('fieldSearch.empty.noDimensions')\n }\n\n return (\n <div className=\"dc:text-center dc:py-12 text-dc-text-muted\">\n <p className=\"dc:text-lg dc:mb-2\">{t('fieldSearch.empty.heading')}</p>\n <p className=\"dc:text-sm\">{emptyDetail}</p>\n </div>\n )\n }\n\n const renderItem = (field: FieldOption, fieldIndex: number, keyPrefix = '') => (\n <FieldSearchItem\n key={`${keyPrefix}${field.name}`}\n field={field}\n isSelected={selectedFields.includes(field.name)}\n isFocused={focusedIndex === fieldIndex}\n onClick={(e: MouseEvent) => onSelectField(field, fieldIndex, e.shiftKey)}\n onMouseEnter={() => onFocusField(field, fieldIndex)}\n data-field-index={fieldIndex}\n />\n )\n\n return (\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 {t('fieldSearch.section.recents')}\n </h3>\n <div className=\"dc:space-y-1\">\n {recentOptions.map((field, idx) => renderItem(field, idx, 'recent-'))}\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 renderItem(\n field,\n computeFieldIndex(groupedFields, recentOptions.length, cubeName, fields, field)\n )\n )}\n </div>\n </div>\n ))}\n </div>\n )\n})\n\nexport default FieldSearchResults\n","/**\n * useFieldSearchKeyboard Hook\n *\n * Encapsulates the keyboard navigation + focus-scroll behaviour for the\n * FieldSearchModal results list. Owns focusedIndex/focusedField state and the\n * arrow/enter/escape key handling.\n */\n\nimport { useState, useCallback, useEffect, useRef, KeyboardEvent } from 'react'\nimport type { RefObject } from 'react'\nimport type { FieldOption } from '../types.js'\n\nexport interface FieldSearchKeyboardApi {\n focusedField: FieldOption | null\n setFocusedField: (field: FieldOption | null) => void\n focusedIndex: number\n setFocusedIndex: (index: number) => void\n resultsContainerRef: RefObject<HTMLDivElement>\n handleKeyDown: (e: KeyboardEvent) => void\n}\n\nexport function useFieldSearchKeyboard(\n flatFieldsList: FieldOption[],\n onSelectField: (field: FieldOption, fieldIndex: number, shiftKey: boolean) => void,\n onClose: () => void\n): FieldSearchKeyboardApi {\n const [focusedField, setFocusedField] = useState<FieldOption | null>(null)\n const [focusedIndex, setFocusedIndex] = useState(-1)\n const resultsContainerRef = useRef<HTMLDivElement>(null)\n\n const moveFocus = useCallback((delta: 1 | -1) => {\n setFocusedIndex((prev) => {\n const next = delta > 0\n ? Math.min(prev + 1, flatFieldsList.length - 1)\n : Math.max(prev - 1, 0)\n setFocusedField(flatFieldsList[next])\n return next\n })\n }, [flatFieldsList])\n\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 moveFocus(1)\n break\n case 'ArrowUp':\n e.preventDefault()\n moveFocus(-1)\n break\n case 'Enter':\n e.preventDefault()\n if (focusedIndex >= 0 && flatFieldsList[focusedIndex]) {\n onSelectField(flatFieldsList[focusedIndex], focusedIndex, e.shiftKey)\n }\n break\n case 'Escape':\n e.preventDefault()\n onClose()\n break\n }\n },\n [flatFieldsList, focusedIndex, moveFocus, onSelectField, 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 return {\n focusedField,\n setFocusedField,\n focusedIndex,\n setFocusedIndex,\n resultsContainerRef,\n handleKeyDown\n }\n}\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 } from 'react'\nimport { getIcon } from '../../icons/index.js'\nimport type { FieldSearchModalProps, FieldOption } from './types.js'\nimport type { MetaField } from '../../shared/types.js'\nimport {\n schemaToFieldOptions,\n filterFieldOptions,\n groupFieldsByCube,\n getCubeNames,\n getCubeTitle,\n getRecentFields,\n addRecentField,\n getRecentFieldOptions\n} from './utils/index.js'\nimport FieldDetailPanel from './FieldDetailPanel.js'\nimport FieldSearchResults from './FieldSearchResults.js'\nimport { useFieldSearchKeyboard } from './hooks/useFieldSearchKeyboard.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\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 const { t } = useTranslation()\n // State\n const [searchTerm, setSearchTerm] = useState('')\n const [selectedCube, setSelectedCube] = useState<string | null>(null)\n const [lastSelectedIndex, setLastSelectedIndex] = useState<number | null>(null)\n\n // Refs\n const searchInputRef = useRef<HTMLInputElement>(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 // 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 + focus-scroll behaviour\n const {\n focusedField,\n setFocusedField,\n focusedIndex,\n setFocusedIndex,\n resultsContainerRef,\n handleKeyDown\n } = useFieldSearchKeyboard(flatFieldsList, handleSelectField, onClose)\n\n // Focus a field via mouse hover\n const handleFocusField = useCallback((field: FieldOption, index: number) => {\n setFocusedField(field)\n setFocusedIndex(index)\n }, [setFocusedField, setFocusedIndex])\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, setFocusedField, setFocusedIndex])\n\n if (!isOpen) return null\n\n const searchPlaceholder =\n mode === 'metrics' ? t('fieldSearch.placeholder.metrics') : mode === 'filter' ? t('fieldSearch.placeholder.filter') : t('fieldSearch.placeholder.dimensions')\n\n const modalTitle = mode === 'metrics' ? t('fieldSearch.modal.title.metrics') : mode === 'filter' ? t('fieldSearch.modal.title.filter') : t('fieldSearch.modal.title.dimensions')\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=\"\">{t('fieldSearch.filter.allCubes')}</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 {t('fieldSearch.categories.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 <FieldSearchResults\n mode={mode}\n schema={schema}\n searchTerm={searchTerm}\n recentOptions={recentOptions}\n groupedFields={groupedFields}\n filteredCount={filteredFields.length}\n selectedFields={selectedFields}\n focusedIndex={focusedIndex}\n onSelectField={handleSelectField}\n onFocusField={handleFocusField}\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' ? t('fieldSearch.footer.metricsAvailable') : mode === 'filter' ? t('fieldSearch.footer.fieldsAvailable') : t('fieldSearch.footer.dimensionsAvailable')}\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> {t('fieldSearch.shortcut.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\">{t('fieldSearch.shortcut.keyEnter')}</kbd> {t('fieldSearch.shortcut.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\">{t('fieldSearch.shortcut.keyShift')}</kbd>{t('fieldSearch.shortcut.plusClick')} {t('fieldSearch.shortcut.multiSelect')}\n </span>\n <span>\n <kbd className=\"dc:px-1.5 dc:py-0.5 bg-dc-surface-tertiary dc:rounded dc:text-xs\">{t('fieldSearch.shortcut.keyEsc')}</kbd> {t('fieldSearch.shortcut.close')}\n </span>\n </div>\n </div>\n </div>\n </div>\n )\n}\n","/**\n * Shared type definitions used across QueryBuilder and AnalysisBuilder\n */\n\nimport type { CubeQuery } from '../types.js'\n\n// ============================================================================\n// Meta endpoint response types\n// ============================================================================\n\nexport interface MetaField {\n name: string // e.g., \"Employees.count\"\n title: string // e.g., \"Total Employees\"\n shortTitle: string // e.g., \"Total Employees\"\n type: string // e.g., \"count\", \"string\", \"time\", \"number\"\n description?: string // Optional description\n}\n\nexport interface MetaCubeRelationship {\n targetCube: string\n relationship: 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany'\n joinFields?: Array<{\n sourceField: string\n targetField: string\n }>\n}\n\nexport interface MetaCube {\n name: string // e.g., \"Employees\"\n title: string // e.g., \"Employee Analytics\"\n description: string // e.g., \"Employee data and metrics\"\n measures: MetaField[] // e.g., \"Employees.count\"\n dimensions: MetaField[] // e.g., \"Employees.name\"\n segments: MetaField[] // Currently empty in examples\n relationships?: MetaCubeRelationship[] // Optional join relationships to other cubes\n}\n\nexport interface MetaResponse {\n cubes: MetaCube[]\n}\n\n// ============================================================================\n// Query warning types (from server-side query planner)\n// ============================================================================\n\n/**\n * Severity level for query warnings\n */\nexport type QueryWarningSeverity = 'info' | 'warning' | 'error'\n\n/**\n * Warning emitted during query planning or execution\n * Provides user-facing feedback about potential query issues\n */\nexport interface QueryWarning {\n /** Unique code for programmatic handling (e.g., 'FAN_OUT_NO_DIMENSIONS') */\n code: string\n /** Human-readable warning message */\n message: string\n /** Severity level for UI styling */\n severity: QueryWarningSeverity\n /** Cubes involved in the warning (if applicable) */\n cubes?: string[]\n /** Measures involved in the warning (if applicable) */\n measures?: string[]\n /** Actionable suggestion for the user */\n suggestion?: string\n}\n\n// ============================================================================\n// Query analysis types for debugging transparency\n// ============================================================================\n\nexport type PrimaryCubeSelectionReason =\n | 'most_dimensions'\n | 'most_connected'\n | 'alphabetical_fallback'\n | 'single_cube'\n\nexport interface PrimaryCubeCandidate {\n cubeName: string\n dimensionCount: number\n joinCount: number\n canReachAll: boolean\n}\n\nexport interface PrimaryCubeAnalysis {\n selectedCube: string\n reason: PrimaryCubeSelectionReason\n explanation: string\n candidates?: PrimaryCubeCandidate[]\n}\n\nexport interface JoinPathStep {\n fromCube: string\n toCube: string\n relationship: 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany'\n joinType: 'inner' | 'left' | 'right' | 'full'\n joinColumns: Array<{\n sourceColumn: string\n targetColumn: string\n }>\n junctionTable?: {\n tableName: string\n sourceColumns: string[]\n targetColumns: string[]\n }\n}\n\nexport interface JoinPathAnalysis {\n targetCube: string\n pathFound: boolean\n path?: JoinPathStep[]\n pathLength?: number\n error?: string\n visitedCubes?: string[]\n selection?: {\n strategy: 'shortest' | 'preferred' | 'fallbackShortest'\n preferredCubes?: string[]\n selectedRank?: number\n selectedScore?: number\n candidates?: Array<{\n rank: number\n score: number\n usesPreferredJoin: boolean\n preferredCubesInPath: number\n usesProcessed: boolean\n scoreBreakdown: {\n preferredJoinBonus: number\n preferredCubeBonus: number\n lengthPenalty: number\n }\n path: JoinPathStep[]\n }>\n }\n}\n\n/**\n * Reason why a cube requires a CTE\n */\nexport type CTEReason = 'hasMany' | 'fanOutPrevention'\n\nexport interface PreAggregationAnalysis {\n cubeName: string\n cteAlias: string\n /** Why this cube needs a CTE (human-readable explanation) */\n reason: string\n /** Typed reason for programmatic use */\n reasonType?: CTEReason\n measures: string[]\n joinKeys: Array<{\n sourceColumn: string\n targetColumn: string\n }>\n}\n\nexport interface QuerySummary {\n queryType: 'single_cube' | 'multi_cube_join' | 'multi_cube_cte'\n measureStrategy?: 'simple' | 'keysDeduplication' | 'ctePreAggregateFallback' | 'multiFactMerge'\n joinCount: number\n cteCount: number\n hasPreAggregation: boolean\n}\n\nexport interface QueryAnalysis {\n timestamp: string\n cubeCount: number\n cubesInvolved: string[]\n primaryCube: PrimaryCubeAnalysis\n joinPaths: JoinPathAnalysis[]\n preAggregations: PreAggregationAnalysis[]\n querySummary: QuerySummary\n warnings?: string[]\n planningTrace?: {\n steps: Array<{\n phase: 'cube_usage' | 'primary_cube_selection' | 'join_planning' | 'cte_planning' | 'measure_strategy' | 'warnings'\n decision: string\n details?: Record<string, unknown>\n }>\n }\n}\n\n// ============================================================================\n// Validation response from /dry-run endpoint\n// ============================================================================\n\nexport interface ValidationResult {\n valid?: boolean // Our custom property (may not be present in official Cube.js)\n error?: string\n query?: CubeQuery\n sql?: {\n sql: string[]\n params: any[]\n }\n queryType?: string // Always present in successful Cube.js responses\n normalizedQueries?: any[]\n queryOrder?: string[]\n transformedQueries?: any[]\n pivotQuery?: any\n complexity?: string\n cubesUsed?: string[]\n joinType?: string\n // Query analysis for debugging transparency\n analysis?: QueryAnalysis\n}\n\n// ============================================================================\n// Filter operator metadata\n// ============================================================================\n//\n// Definitions live in ./filters/operators.ts (the single filter module).\n// Re-exported here so existing imports via shared/types stay stable.\nexport type { FilterOperatorMeta } from './filters/operators.js'\nexport { FILTER_OPERATORS } from './filters/operators.js'\n\n// ============================================================================\n// Date range types\n// ============================================================================\n\nexport type DateRangeType =\n | 'custom'\n | 'today'\n | 'yesterday'\n | 'this_week'\n | 'this_month'\n | 'this_quarter'\n | 'this_year'\n | 'last_7_days'\n | 'last_30_days'\n | 'last_week'\n | 'last_month'\n | 'last_quarter'\n | 'last_year'\n | 'last_12_months'\n | 'last_n_days'\n | 'last_n_weeks'\n | 'last_n_months'\n | 'last_n_quarters'\n | 'last_n_years'\n\nexport interface DateRangeOption {\n value: DateRangeType\n label: string\n}\n\nexport const DATE_RANGE_OPTIONS: DateRangeOption[] = [\n { value: 'custom', label: 'dateRange.custom' },\n { value: 'today', label: 'dateRange.today' },\n { value: 'yesterday', label: 'dateRange.yesterday' },\n { value: 'this_week', label: 'dateRange.thisWeek' },\n { value: 'this_month', label: 'dateRange.thisMonth' },\n { value: 'this_quarter', label: 'dateRange.thisQuarter' },\n { value: 'this_year', label: 'dateRange.thisYear' },\n { value: 'last_7_days', label: 'dateRange.last7Days' },\n { value: 'last_30_days', label: 'dateRange.last30Days' },\n { value: 'last_n_days', label: 'dateRange.lastNDays' },\n { value: 'last_week', label: 'dateRange.lastWeek' },\n { value: 'last_n_weeks', label: 'dateRange.lastNWeeks' },\n { value: 'last_month', label: 'dateRange.lastMonth' },\n { value: 'last_12_months', label: 'dateRange.last12Months' },\n { value: 'last_n_months', label: 'dateRange.lastNMonths' },\n { value: 'last_quarter', label: 'dateRange.lastQuarter' },\n { value: 'last_n_quarters', label: 'dateRange.lastNQuarters' },\n { value: 'last_year', label: 'dateRange.lastYear' },\n { value: 'last_n_years', label: 'dateRange.lastNYears' }\n]\n\n// ============================================================================\n// Time dimension granularity options\n// ============================================================================\n\nexport const TIME_GRANULARITIES = [\n { value: 'hour', label: 'timeGranularity.hour' },\n { value: 'day', label: 'timeGranularity.day' },\n { value: 'week', label: 'timeGranularity.week' },\n { value: 'month', label: 'timeGranularity.month' },\n { value: 'quarter', label: 'timeGranularity.quarter' },\n { value: 'year', label: 'timeGranularity.year' }\n]\n\nexport type TimeGranularity = 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'\n","/**\n * Date-range utilities for the compact filter bar.\n *\n * Split out of `shared/utils.ts` by concern. Re-exported from there to keep\n * existing import paths stable.\n */\n\nimport type { DateRangeType } from '../../shared/types.js'\nimport { DATE_RANGE_OPTIONS } from '../../shared/types.js'\nimport { convertDateRangeTypeToValue, requiresNumberInput } from '../../shared/utils.js'\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\ntype DateRange = { start: Date, end: Date }\n\n/** Start of the current ISO week (Monday) relative to a normalised \"today\". */\nfunction startOfWeek(today: Date): Date {\n const start = new Date(today)\n const dayOfWeek = start.getDay()\n const diff = dayOfWeek === 0 ? 6 : dayOfWeek - 1 // Monday as first day\n start.setDate(start.getDate() - diff)\n return start\n}\n\n/** Resolve the named/relative-to-now presets (today, this week, etc.). */\nfunction resolveNamedPreset(preset: string, today: Date, endOfToday: Date): DateRange | null {\n switch (preset) {\n case 'today':\n return { start: today, end: endOfToday }\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 return { start: startOfWeek(today), end: endOfToday }\n case 'this month':\n return { start: new Date(today.getFullYear(), today.getMonth(), 1), end: endOfToday }\n case 'this quarter': {\n const quarter = Math.floor(today.getMonth() / 3)\n return { start: new Date(today.getFullYear(), quarter * 3, 1), end: endOfToday }\n }\n case 'this year':\n return { start: new Date(today.getFullYear(), 0, 1), end: endOfToday }\n default:\n return null\n }\n}\n\n/** Resolve \"last N <unit>\" patterns. */\nfunction resolveLastNUnits(preset: string, today: Date, endOfToday: Date): DateRange | null {\n const match = preset.match(/^last\\s+(\\d+)\\s+(day|days|week|weeks|month|months|quarter|quarters|year|years)$/i)\n if (!match) return null\n\n const num = parseInt(match[1], 10)\n const unit = match[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/** Resolve \"last <unit>\" (no number) patterns. */\nfunction resolveLastUnit(preset: string, today: Date): DateRange | null {\n const match = preset.match(/^last\\s+(week|month|quarter|year)$/i)\n if (!match) return null\n\n const unit = match[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 }\n 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 }\n 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 }\n // 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 * Calculate actual date range from a preset string\n * Returns start and end dates for display purposes\n */\nexport function calculateDateRange(preset: string): DateRange | 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 const normalized = preset.toLowerCase()\n\n return (\n resolveNamedPreset(normalized, today, endOfToday) ??\n resolveLastNUnits(preset, today, endOfToday) ??\n resolveLastUnit(preset, today)\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 + XTD presets\n const presetMatch = [...DATE_PRESETS, ...XTD_OPTIONS].find(\n preset => preset.value.toLowerCase() === normalizedRange\n )\n if (presetMatch) {\n return presetMatch.id\n }\n\n // Anything else (including dynamic \"last N units\") is treated as custom\n return 'custom'\n}\n\nexport interface DerivedRange {\n rangeType: DateRangeType\n /** Present only when the range encodes an explicit \"last N\" count. */\n numberValue?: number\n}\n\nconst SINGULAR_TO_PLURAL: Record<string, string> = {\n day: 'days',\n week: 'weeks',\n month: 'months',\n quarter: 'quarters',\n year: 'years'\n}\n\n/**\n * Resolve the rangeType (and optional numberValue) that corresponds to a\n * filter's stored `dateRange`. Returns `null` when there is nothing to derive.\n */\nexport function deriveRangeFromDateRange(\n dateRange: string | string[] | undefined\n): DerivedRange | null {\n if (!dateRange) return null\n\n if (Array.isArray(dateRange)) {\n return { rangeType: 'custom' }\n }\n\n // Match \"last N days/weeks/months/quarters/years\"\n const flexMatch = dateRange.match(/^last (\\d+) (days|weeks|months|quarters|years)$/)\n if (flexMatch) {\n const [, num, unit] = flexMatch\n return { rangeType: `last_n_${unit}` as DateRangeType, numberValue: parseInt(num) || 1 }\n }\n\n // Match singular forms: \"last day/week/month/quarter/year\" (when N=1)\n const singularMatch = dateRange.match(/^last (day|week|month|quarter|year)$/)\n if (singularMatch) {\n const [, unit] = singularMatch\n const pluralUnit = SINGULAR_TO_PLURAL[unit] ?? 'years'\n return { rangeType: `last_n_${pluralUnit}` as DateRangeType, numberValue: 1 }\n }\n\n // Check predefined ranges (only if not a \"last N\" pattern)\n for (const option of DATE_RANGE_OPTIONS) {\n if (option.value !== 'custom' && !requiresNumberInput(option.value)) {\n if (convertDateRangeTypeToValue(option.value) === dateRange) {\n return { rangeType: option.value }\n }\n }\n }\n\n return { rangeType: 'custom' }\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","/**\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.js'\nimport { generateId } from '../components/AnalysisBuilder/utils/index.js'\nimport type {\n AnalysisConfig,\n FunnelAnalysisConfig,\n AnalysisType,\n ChartConfig,\n} from '../types/analysisConfig.js'\nimport type { FunnelStepState, FunnelBindingKey, Filter } from '../types.js'\nimport type { ServerFunnelQuery, ServerFunnelStep } from '../types/funnel.js'\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.js'\nimport type {\n AnalysisConfig,\n FlowAnalysisConfig,\n AnalysisType,\n ChartConfig,\n} from '../types/analysisConfig.js'\nimport type { Filter, FunnelBindingKey } from '../types.js'\nimport type {\n FlowSliceState,\n ServerFlowQuery,\n FlowStartingStep,\n} from '../types/flow.js'\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.js'\nimport type {\n AnalysisConfig,\n RetentionAnalysisConfig,\n AnalysisType,\n ChartConfig,\n} from '../types/analysisConfig.js'\nimport type { Filter } from '../types.js'\nimport type { FunnelBindingKey } from '../types/funnel.js'\nimport type {\n ServerRetentionQuery,\n RetentionSliceState,\n RetentionGranularity,\n RetentionType,\n DateRange,\n RetentionBreakdownItem,\n} from '../types/retention.js'\nimport { defaultRetentionSliceState, getDateRangeFromPreset, DEFAULT_DATE_RANGE_PRESET } from '../types/retention.js'\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","/**\n * StringArrayInput Component\n *\n * A textarea that edits an array of strings. Uses local state while editing and\n * only commits to the parent on blur. Extracted from DisplayOptionControl so the\n * structured-option renderers stay presentational. Behaviour is identical to the\n * previous inline implementation.\n */\n\nimport { useState, useCallback, useEffect } from 'react'\n\ninterface StringArrayInputProps {\n label: string\n value: string[]\n onChange: (value: string[]) => void\n placeholder?: string\n description?: string\n}\n\nexport default function StringArrayInput({\n label,\n value,\n onChange,\n placeholder,\n description,\n}: StringArrayInputProps) {\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 setLocalText(value.join('\\n'))\n }, [value])\n\n const handleBlur = useCallback(() => {\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 && <p className=\"dc:text-xs text-dc-text-muted\">{description}</p>}\n </div>\n )\n}\n","/**\n * DisplayOptionControl Component\n *\n * Renders a single structured display option (boolean, string, number, select,\n * color, paletteColor, axisFormat, stringArray, buttonGroup) for the\n * AnalysisDisplayConfigPanel. Each option type has its own small presentational\n * component so the dispatcher stays flat. Behaviour is identical to the previous\n * inline rendering.\n */\n\nimport type { ReactElement } from 'react'\nimport type { ChartDisplayConfig, ColorPalette, AxisFormatConfig } from '../../types.js'\nimport type { DisplayOptionConfig } from '../../charts/chartConfigs.js'\nimport { AxisFormatControls } from '../charts/AxisFormatControls.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport StringArrayInput from './StringArrayInput.js'\n\ntype SetValue = (value: unknown) => void\n\ninterface OptionRenderProps {\n option: DisplayOptionConfig\n displayConfig: ChartDisplayConfig\n colorPalette?: ColorPalette\n setValue: SetValue\n t: (key: string) => string\n}\n\nfunction OptionDescription({ description, t }: { description?: string; t: (key: string) => string }) {\n if (!description) return null\n return <p className=\"dc:text-xs text-dc-text-muted\">{t(description)}</p>\n}\n\nfunction BooleanOption({ option, displayConfig, setValue, t }: OptionRenderProps) {\n const key = option.key as keyof ChartDisplayConfig\n return (\n <label className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"checkbox\"\n checked={(displayConfig[key] as boolean) ?? option.defaultValue ?? false}\n onChange={(e) => setValue(e.target.checked)}\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\">{t(option.label)}</span>\n </label>\n )\n}\n\nfunction StringOption({ option, displayConfig, setValue, t }: OptionRenderProps) {\n const key = option.key as keyof ChartDisplayConfig\n const value = (displayConfig[key] as string) ?? option.defaultValue ?? ''\n return (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">\n {t(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={value}\n onChange={(e) => setValue(e.target.value)}\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={value}\n onChange={(e) => setValue(e.target.value)}\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 <OptionDescription description={option.description} t={t} />\n </div>\n )\n}\n\nfunction PaletteColorOption({ option, displayConfig, colorPalette, setValue, t }: OptionRenderProps) {\n const key = option.key as keyof ChartDisplayConfig\n const selectedIndex = (displayConfig[key] as number) ?? option.defaultValue ?? 0\n return (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{t(option.label)}</label>\n <div className=\"dc:flex dc:flex-wrap dc:gap-2\">\n {colorPalette?.colors.map((color, index) => (\n <button\n key={index}\n type=\"button\"\n onClick={() => setValue(index)}\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 selectedIndex === index\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: selectedIndex === index ? 'var(--dc-primary)' : 'var(--dc-border)'\n }}\n title={`Color ${index + 1}: ${color}`}\n />\n )) || [\n <button\n key={0}\n type=\"button\"\n onClick={() => setValue(0)}\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 <OptionDescription description={option.description} t={t} />\n </div>\n )\n}\n\nfunction NumberOption({ option, displayConfig, setValue, t }: OptionRenderProps) {\n const key = option.key as keyof ChartDisplayConfig\n return (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{t(option.label)}</label>\n <input\n type=\"number\"\n value={(displayConfig[key] as number) ?? option.defaultValue ?? 0}\n onChange={(e) => setValue(e.target.value === '' ? undefined : Number(e.target.value))}\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 <OptionDescription description={option.description} t={t} />\n </div>\n )\n}\n\nfunction SelectOption({ option, displayConfig, setValue, t }: OptionRenderProps) {\n const key = option.key as keyof ChartDisplayConfig\n return (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{t(option.label)}</label>\n <select\n value={(displayConfig[key] as string) ?? option.defaultValue ?? ''}\n onChange={(e) => setValue(e.target.value)}\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 {t(opt.label)}\n </option>\n ))}\n </select>\n <OptionDescription description={option.description} t={t} />\n </div>\n )\n}\n\nfunction ColorOption({ option, displayConfig, setValue, t }: OptionRenderProps) {\n const key = option.key as keyof ChartDisplayConfig\n const colorValue = (displayConfig[key] as string) ?? option.defaultValue ?? '#8884d8'\n return (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{t(option.label)}</label>\n <div className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"color\"\n value={colorValue}\n onChange={(e) => setValue(e.target.value)}\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={colorValue}\n onChange={(e) => setValue(e.target.value)}\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 <OptionDescription description={option.description} t={t} />\n </div>\n )\n}\n\nfunction AxisFormatOption({ option, displayConfig, setValue, t }: OptionRenderProps) {\n const key = option.key as keyof ChartDisplayConfig\n return (\n <AxisFormatControls\n axisLabel={t(option.label)}\n value={(displayConfig[key] as AxisFormatConfig) || {}}\n onChange={(config) => setValue(Object.keys(config).length > 0 ? config : undefined)}\n />\n )\n}\n\nfunction StringArrayOption({ option, displayConfig, setValue, t }: OptionRenderProps) {\n const key = option.key as keyof ChartDisplayConfig\n return (\n <StringArrayInput\n label={t(option.label)}\n value={(displayConfig[key] as string[]) ?? []}\n onChange={(arrayValue) => setValue(arrayValue.length > 0 ? arrayValue : undefined)}\n placeholder={option.placeholder}\n description={option.description ? t(option.description) : undefined}\n />\n )\n}\n\nfunction ButtonGroupOption({ option, displayConfig, setValue, t }: OptionRenderProps) {\n const key = option.key as keyof ChartDisplayConfig\n return (\n <div className=\"dc:space-y-1\">\n <label className=\"dc:text-sm text-dc-text-secondary\">{t(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[key] ?? option.defaultValue) === opt.value\n return (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => setValue(opt.value)}\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 {t(opt.label)}\n </button>\n )\n })}\n </div>\n <OptionDescription description={option.description} t={t} />\n </div>\n )\n}\n\n// Dispatch table — keyed by option.type. Keeps the control's render flat.\nconst OPTION_RENDERERS: Record<string, (props: OptionRenderProps) => ReactElement | null> = {\n boolean: BooleanOption,\n string: StringOption,\n paletteColor: PaletteColorOption,\n number: NumberOption,\n select: SelectOption,\n color: ColorOption,\n axisFormat: AxisFormatOption,\n stringArray: StringArrayOption,\n buttonGroup: ButtonGroupOption\n}\n\ninterface DisplayOptionControlProps {\n option: DisplayOptionConfig\n displayConfig: ChartDisplayConfig\n colorPalette?: ColorPalette\n onDisplayConfigChange: (config: ChartDisplayConfig) => void\n}\n\nexport default function DisplayOptionControl({\n option,\n displayConfig,\n colorPalette,\n onDisplayConfigChange,\n}: DisplayOptionControlProps) {\n const { t } = useTranslation()\n const setValue: SetValue = (value) =>\n onDisplayConfigChange({ ...displayConfig, [option.key]: value })\n\n const Renderer = OPTION_RENDERERS[option.type]\n if (!Renderer) return null\n return <Renderer option={option} displayConfig={displayConfig} colorPalette={colorPalette} setValue={setValue} t={t} />\n}\n","/**\n * LegacyBooleanOptions Component\n *\n * Backward-compatibility renderer for the simple boolean `displayOptions`\n * (showLegend, showGrid, etc.) used before structured `displayOptionsConfig`.\n * Extracted from AnalysisDisplayConfigPanel to keep it flat. Behaviour is\n * identical to the previous inline checkboxes.\n */\n\nimport { memo } from 'react'\nimport type { ChartDisplayConfig } from '../../types.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\ninterface LegacyBooleanOptionsProps {\n displayOptions?: string[]\n displayConfig: ChartDisplayConfig\n onDisplayConfigChange: (config: ChartDisplayConfig) => void\n}\n\ninterface LegacyOptionSpec {\n key: keyof ChartDisplayConfig\n labelKey: string\n defaultChecked: boolean\n}\n\n// Preserves the original ordering and translation keys exactly.\nconst LEGACY_OPTIONS: LegacyOptionSpec[] = [\n { key: 'showLegend', labelKey: 'display.showLegend', defaultChecked: true },\n { key: 'showGrid', labelKey: 'display.showGrid', defaultChecked: true },\n { key: 'showTooltip', labelKey: 'display.showTooltip', defaultChecked: true },\n { key: 'stacked', labelKey: 'display.stacked', defaultChecked: false },\n { key: 'showAllXLabels', labelKey: 'chart.option.showAllXLabels.label', defaultChecked: true },\n { key: 'hideHeader', labelKey: 'display.hideHeader', defaultChecked: false }\n]\n\nconst LegacyBooleanOptions = memo(function LegacyBooleanOptions({\n displayOptions,\n displayConfig,\n onDisplayConfigChange\n}: LegacyBooleanOptionsProps) {\n const { t } = useTranslation()\n\n if (!displayOptions || displayOptions.length === 0) return null\n\n return (\n <>\n {LEGACY_OPTIONS.filter((spec) => displayOptions.includes(spec.key)).map((spec) => (\n <label key={spec.key} className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"checkbox\"\n checked={(displayConfig[spec.key] as boolean) ?? spec.defaultChecked}\n onChange={(e) =>\n onDisplayConfigChange({ ...displayConfig, [spec.key]: e.target.checked })\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\">{t(spec.labelKey)}</span>\n </label>\n ))}\n </>\n )\n})\n\nexport default LegacyBooleanOptions\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 SectionHeading from './SectionHeading.js'\nimport DisplayOptionControl from './DisplayOptionControl.js'\nimport LegacyBooleanOptions from './LegacyBooleanOptions.js'\nimport { useChartConfig } from '../../charts/lazyChartConfigRegistry.js'\nimport type { ChartType, ChartDisplayConfig, ColorPalette } from '../../types.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\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\nexport default function AnalysisDisplayConfigPanel({\n chartType,\n displayConfig,\n colorPalette,\n onDisplayConfigChange,\n excludeKeys,\n}: AnalysisDisplayConfigPanelProps) {\n const { t } = useTranslation()\n\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 {t('display.loading')}\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>{t('display.noOptions')}</p>\n </div>\n )\n }\n\n return (\n <div className=\"dc:space-y-6\">\n <div>\n <SectionHeading className=\"dc:mb-2\">{t('display.heading')}</SectionHeading>\n <div className=\"dc:space-y-2\">\n {/* Backward compatibility: Simple boolean display options */}\n <LegacyBooleanOptions\n displayOptions={chartTypeConfig.displayOptions}\n displayConfig={displayConfig}\n onDisplayConfigChange={onDisplayConfigChange}\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 <DisplayOptionControl\n option={option}\n displayConfig={displayConfig}\n colorPalette={colorPalette}\n onDisplayConfigChange={onDisplayConfigChange}\n />\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n}\n","import React from 'react'\nimport Modal from './Modal.js'\nimport { useTranslation } from '../hooks/useTranslation.js'\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,\n message,\n confirmText,\n cancelText,\n confirmVariant = 'primary',\n isLoading = false,\n}) => {\n const { t } = useTranslation()\n const resolvedTitle = title ?? t('common.actions.confirm')\n const resolvedConfirmText = confirmText ?? t('common.actions.confirm')\n const resolvedCancelText = cancelText ?? t('common.actions.cancel')\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={resolvedTitle}\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 {resolvedCancelText}\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 {t('common.modal.processing')}\n </span>\n ) : (\n resolvedConfirmText\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"],"mappings":"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AQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EACN,OAAO;EACP,QAAQ;GACN;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA,UAAU;GACR;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,MAAM;EA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"}
|