drizzle-cube 0.5.8 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (725) hide show
  1. package/README.md +2 -0
  2. package/dist/adapters/express/index.cjs +1 -1
  3. package/dist/adapters/{adapters/express → express}/index.d.ts +3 -3
  4. package/dist/adapters/express/index.js +50 -50
  5. package/dist/adapters/fastify/index.cjs +1 -1
  6. package/dist/adapters/{adapters/fastify → fastify}/index.d.ts +3 -3
  7. package/dist/adapters/fastify/index.js +78 -78
  8. package/dist/adapters/google-BOnRIN1w.cjs +2 -0
  9. package/dist/{server/google-CFYljAOF.js → adapters/google-CvKM9yVM.js} +83 -71
  10. package/dist/adapters/{handler-Cx8QYLk6.js → handler-CfgE0JSL.js} +499 -576
  11. package/dist/adapters/handler-Cn0dOctE.cjs +25 -0
  12. package/dist/adapters/hono/agent-handler.d.ts +9 -0
  13. package/dist/adapters/hono/index.cjs +2 -2
  14. package/dist/adapters/{adapters/hono → hono}/index.d.ts +4 -4
  15. package/dist/adapters/hono/index.js +205 -175
  16. package/dist/adapters/hono/mcp-handler.d.ts +37 -0
  17. package/dist/adapters/locale-DDvZDFVn.js +8114 -0
  18. package/dist/adapters/locale-ZLUUNUxr.cjs +166 -0
  19. package/dist/adapters/{adapters/locale.d.ts → locale.d.ts} +1 -1
  20. package/dist/adapters/mcp-tools-handlers.d.ts +18 -0
  21. package/dist/adapters/mcp-tools.cjs +1 -1
  22. package/dist/adapters/{adapters/mcp-tools.d.ts → mcp-tools.d.ts} +5 -5
  23. package/dist/adapters/mcp-tools.js +49 -38
  24. package/dist/adapters/mcp-transport-B7ddET3M.cjs +40 -0
  25. package/dist/adapters/mcp-transport-Dfuj4j4q.js +591 -0
  26. package/dist/adapters/{adapters/mcp-transport.d.ts → mcp-transport.d.ts} +3 -3
  27. package/dist/adapters/nextjs/index.cjs +1 -1
  28. package/dist/adapters/{adapters/nextjs → nextjs}/index.d.ts +3 -3
  29. package/dist/adapters/nextjs/index.js +190 -197
  30. package/dist/adapters/nextjs/mcp-handler.d.ts +36 -0
  31. package/dist/adapters/{openai-BZdAA7Ji.js → openai-cDlFSTk6.js} +60 -58
  32. package/dist/adapters/openai-vXvLni8v.cjs +1 -0
  33. package/dist/adapters/{adapters/types.d.ts → types.d.ts} +1 -1
  34. package/dist/adapters/{utils-D9JPLmfl.js → utils-BPWZsX7k.js} +1271 -1218
  35. package/dist/adapters/utils-_whV4fXL.cjs +128 -0
  36. package/dist/adapters/utils.cjs +1 -1
  37. package/dist/adapters/{adapters/utils.d.ts → utils.d.ts} +17 -92
  38. package/dist/adapters/utils.js +2 -2
  39. package/dist/cli/index.cjs +2 -2
  40. package/dist/client/adapters/adapterRegistry.d.ts +2 -2
  41. package/dist/client/adapters/flowModeAdapter.d.ts +2 -2
  42. package/dist/client/adapters/funnelModeAdapter.d.ts +2 -2
  43. package/dist/client/adapters/index.d.ts +9 -9
  44. package/dist/client/adapters/modeAdapter.d.ts +1 -1
  45. package/dist/client/adapters/queryModeAdapter.d.ts +3 -3
  46. package/dist/client/adapters/retentionModeAdapter.d.ts +2 -2
  47. package/dist/client/charts/ChartLoader.d.ts +3 -17
  48. package/dist/client/charts/chartComponentRegistry.d.ts +46 -0
  49. package/dist/client/charts/chartConfigHelpers.d.ts +38 -0
  50. package/dist/client/charts/chartConfigRegistry.d.ts +1 -1
  51. package/dist/client/charts/chartPlugin.d.ts +3 -3
  52. package/dist/client/charts/lazyChartConfigRegistry.d.ts +2 -2
  53. package/dist/client/charts.d.ts +8 -8
  54. package/dist/client/charts.js +14 -13
  55. package/dist/client/chunks/{DashboardEditModal-CjCIAT-J.js → DashboardEditModal-okVfH8ZK.js} +5296 -4544
  56. package/dist/client/chunks/DashboardEditModal-okVfH8ZK.js.map +1 -0
  57. package/dist/client/chunks/{RetentionCombinedChart-BPkfcTa8.js → RetentionCombinedChart-BgbDhsPz.js} +10 -10
  58. package/dist/client/chunks/RetentionCombinedChart-BgbDhsPz.js.map +1 -0
  59. package/dist/client/chunks/{RetentionHeatmap-DGzLzr7h.js → RetentionHeatmap-DjXZaTPq.js} +5 -5
  60. package/dist/client/chunks/RetentionHeatmap-DjXZaTPq.js.map +1 -0
  61. package/dist/client/chunks/analysis-builder-DB88FojM.js +5674 -0
  62. package/dist/client/chunks/analysis-builder-DB88FojM.js.map +1 -0
  63. package/dist/client/chunks/{analysis-builder-shared-CYVwSPqt.js → analysis-builder-shared-BVK4TYfR.js} +1578 -1292
  64. package/dist/client/chunks/analysis-builder-shared-BVK4TYfR.js.map +1 -0
  65. package/dist/client/chunks/{chart-activity-grid-DmruKPab.js → chart-activity-grid-DX0SJbxs.js} +635 -492
  66. package/dist/client/chunks/chart-activity-grid-DX0SJbxs.js.map +1 -0
  67. package/dist/client/chunks/chart-area-of01_62R.js +775 -0
  68. package/dist/client/chunks/chart-area-of01_62R.js.map +1 -0
  69. package/dist/client/chunks/chart-bar-BqelQE_I.js +194 -0
  70. package/dist/client/chunks/chart-bar-BqelQE_I.js.map +1 -0
  71. package/dist/client/chunks/{chart-box-plot-DYKfyOI8.js → chart-box-plot-kkBixZ27.js} +2 -2
  72. package/dist/client/chunks/chart-box-plot-kkBixZ27.js.map +1 -0
  73. package/dist/client/chunks/chart-bubble-B2he1--4.js +221 -0
  74. package/dist/client/chunks/chart-bubble-B2he1--4.js.map +1 -0
  75. package/dist/client/chunks/{chart-candlestick-Dwc92Mrj.js → chart-candlestick-DZp19Tzh.js} +2 -2
  76. package/dist/client/chunks/chart-candlestick-DZp19Tzh.js.map +1 -0
  77. package/dist/client/chunks/chart-config-activity-grid-D_UX4NHC.js.map +1 -1
  78. package/dist/client/chunks/chart-config-area-BNigHUy8.js +124 -0
  79. package/dist/client/chunks/chart-config-area-BNigHUy8.js.map +1 -0
  80. package/dist/client/chunks/chart-config-bar-DSO_LRTx.js +54 -0
  81. package/dist/client/chunks/chart-config-bar-DSO_LRTx.js.map +1 -0
  82. package/dist/client/chunks/{chart-config-box-plot-DJ-dWWXA.js → chart-config-box-plot-oW8axV2q.js} +5 -10
  83. package/dist/client/chunks/chart-config-box-plot-oW8axV2q.js.map +1 -0
  84. package/dist/client/chunks/chart-config-bubble-gIoqVyjZ.js.map +1 -1
  85. package/dist/client/chunks/chart-config-candlestick-N6DchAA3.js.map +1 -1
  86. package/dist/client/chunks/chart-config-data-table-d7VBY-y_.js.map +1 -1
  87. package/dist/client/chunks/chart-config-funnel-DEYMcxsD.js.map +1 -1
  88. package/dist/client/chunks/{chart-config-gauge-DSwC04l0.js → chart-config-gauge-Dq-_H9UN.js} +5 -7
  89. package/dist/client/chunks/chart-config-gauge-Dq-_H9UN.js.map +1 -0
  90. package/dist/client/chunks/chart-config-heat-map-CfRnRNcw.js.map +1 -1
  91. package/dist/client/chunks/{chart-config-kpi-delta-D6BIkHL3.js → chart-config-kpi-delta-DSQbvqu_.js} +5 -10
  92. package/dist/client/chunks/chart-config-kpi-delta-DSQbvqu_.js.map +1 -0
  93. package/dist/client/chunks/{chart-config-kpi-number-Bx-V9a62.js → chart-config-kpi-number-EFSWY1We.js} +5 -7
  94. package/dist/client/chunks/chart-config-kpi-number-EFSWY1We.js.map +1 -0
  95. package/dist/client/chunks/{chart-config-kpi-text-CcqC1u-8.js → chart-config-kpi-text-KM3V5X2u.js} +5 -7
  96. package/dist/client/chunks/chart-config-kpi-text-KM3V5X2u.js.map +1 -0
  97. package/dist/client/chunks/{chart-config-line-Db3jDsWc.js → chart-config-line-D6jG8PCH.js} +9 -36
  98. package/dist/client/chunks/chart-config-line-D6jG8PCH.js.map +1 -0
  99. package/dist/client/chunks/chart-config-markdown-BtRIe8JN.js.map +1 -1
  100. package/dist/client/chunks/chart-config-measure-profile-DxRGa-zf.js.map +1 -1
  101. package/dist/client/chunks/{chart-config-pie-DD4SmRTF.js → chart-config-pie-BhzW-fdn.js} +6 -16
  102. package/dist/client/chunks/chart-config-pie-BhzW-fdn.js.map +1 -0
  103. package/dist/client/chunks/{chart-config-radar-CC2XAaGr.js → chart-config-radar-D7REP1q_.js} +6 -16
  104. package/dist/client/chunks/chart-config-radar-D7REP1q_.js.map +1 -0
  105. package/dist/client/chunks/{chart-config-radial-bar-DmxKx1R0.js → chart-config-radial-bar-Byx6qOPU.js} +6 -16
  106. package/dist/client/chunks/chart-config-radial-bar-Byx6qOPU.js.map +1 -0
  107. package/dist/client/chunks/chart-config-sankey-BTnWA7EW.js.map +1 -1
  108. package/dist/client/chunks/chart-config-scatter-DSYTjwRb.js.map +1 -1
  109. package/dist/client/chunks/chart-config-sunburst-Bwjtdf7X.js.map +1 -1
  110. package/dist/client/chunks/{chart-config-tree-map-BI-oQStO.js → chart-config-tree-map-DVrvf3yQ.js} +6 -16
  111. package/dist/client/chunks/chart-config-tree-map-DVrvf3yQ.js.map +1 -0
  112. package/dist/client/chunks/{chart-config-waterfall-DSnyixbI.js → chart-config-waterfall-DmFeQdIk.js} +5 -10
  113. package/dist/client/chunks/chart-config-waterfall-DmFeQdIk.js.map +1 -0
  114. package/dist/client/chunks/{chart-data-table-BO4sXsim.js → chart-data-table-CKauQXme.js} +1046 -1121
  115. package/dist/client/chunks/chart-data-table-CKauQXme.js.map +1 -0
  116. package/dist/client/chunks/{chart-funnel-DnWRsmnS.js → chart-funnel-CToKSBaL.js} +184 -222
  117. package/dist/client/chunks/chart-funnel-CToKSBaL.js.map +1 -0
  118. package/dist/client/chunks/chart-gauge-DTVezSk7.js +403 -0
  119. package/dist/client/chunks/chart-gauge-DTVezSk7.js.map +1 -0
  120. package/dist/client/chunks/chart-heat-map-DTuUl0BR.js +236 -0
  121. package/dist/client/chunks/chart-heat-map-DTuUl0BR.js.map +1 -0
  122. package/dist/client/chunks/chart-kpi-delta-Dn-jjO-2.js +408 -0
  123. package/dist/client/chunks/chart-kpi-delta-Dn-jjO-2.js.map +1 -0
  124. package/dist/client/chunks/chart-kpi-number-P_WBiO5S.js +244 -0
  125. package/dist/client/chunks/chart-kpi-number-P_WBiO5S.js.map +1 -0
  126. package/dist/client/chunks/chart-kpi-text-C-Y4eb5H.js +185 -0
  127. package/dist/client/chunks/chart-kpi-text-C-Y4eb5H.js.map +1 -0
  128. package/dist/client/chunks/chart-line-DKgrQA6c.js +101 -0
  129. package/dist/client/chunks/chart-line-DKgrQA6c.js.map +1 -0
  130. package/dist/client/chunks/{chart-markdown-dgUetjyM.js → chart-markdown-CV75S_zL.js} +2 -2
  131. package/dist/client/chunks/chart-markdown-CV75S_zL.js.map +1 -0
  132. package/dist/client/chunks/{chart-measure-profile-ClfpLs4q.js → chart-measure-profile-Ket8fJyf.js} +7 -7
  133. package/dist/client/chunks/chart-measure-profile-Ket8fJyf.js.map +1 -0
  134. package/dist/client/chunks/chart-pie-ByieAX5H.js +119 -0
  135. package/dist/client/chunks/chart-pie-ByieAX5H.js.map +1 -0
  136. package/dist/client/chunks/chart-radar-CiSwHWp4.js +138 -0
  137. package/dist/client/chunks/chart-radar-CiSwHWp4.js.map +1 -0
  138. package/dist/client/chunks/chart-radial-bar-BF6Mhl6f.js +122 -0
  139. package/dist/client/chunks/chart-radial-bar-BF6Mhl6f.js.map +1 -0
  140. package/dist/client/chunks/{chart-sankey-M3XpO_ah.js → chart-sankey-DjrvKw6K.js} +36 -39
  141. package/dist/client/chunks/chart-sankey-DjrvKw6K.js.map +1 -0
  142. package/dist/client/chunks/chart-scatter-Dd9WFqnS.js +258 -0
  143. package/dist/client/chunks/chart-scatter-Dd9WFqnS.js.map +1 -0
  144. package/dist/client/chunks/{chart-sunburst-A_u6lqlS.js → chart-sunburst-leGpuDj7.js} +3 -3
  145. package/dist/client/chunks/chart-sunburst-leGpuDj7.js.map +1 -0
  146. package/dist/client/chunks/chart-tree-map-Ceq5sLZA.js +323 -0
  147. package/dist/client/chunks/chart-tree-map-Ceq5sLZA.js.map +1 -0
  148. package/dist/client/chunks/{chart-waterfall-BuhMoagf.js → chart-waterfall-DyvQReN5.js} +7 -7
  149. package/dist/client/chunks/chart-waterfall-DyvQReN5.js.map +1 -0
  150. package/dist/client/chunks/{charts-core-jRtb0S2M.js → charts-core-DaXSt1Dd.js} +2 -2
  151. package/dist/client/chunks/charts-core-DaXSt1Dd.js.map +1 -0
  152. package/dist/client/chunks/charts-loader-DdTeCeNo.js +84 -0
  153. package/dist/client/chunks/charts-loader-DdTeCeNo.js.map +1 -0
  154. package/dist/client/chunks/{nl-NL-CpVs7ox3.js → nl-NL-2zTAWTej.js} +2 -1
  155. package/dist/client/chunks/{nl-NL-CpVs7ox3.js.map → nl-NL-2zTAWTej.js.map} +1 -1
  156. package/dist/client/chunks/retention-ChW9jYdy.js.map +1 -1
  157. package/dist/client/chunks/{schema-visualization-DE09kQE3.js → schema-visualization-Cb_E9_Gd.js} +12 -12
  158. package/dist/client/chunks/schema-visualization-Cb_E9_Gd.js.map +1 -0
  159. package/dist/client/chunks/{useDirtyStateTracking-CoeQbprt.js → useDirtyStateTracking-MbpxH_v6.js} +95 -84
  160. package/dist/client/chunks/useDirtyStateTracking-MbpxH_v6.js.map +1 -0
  161. package/dist/client/chunks/{useExplainAI-DlnXWdmz.js → useExplainAI-evnZkjCY.js} +14 -14
  162. package/dist/client/chunks/useExplainAI-evnZkjCY.js.map +1 -0
  163. package/dist/client/chunks/{utils-CTKNaXS8.js → utils-3P6z1vz5.js} +2 -2
  164. package/dist/client/chunks/utils-3P6z1vz5.js.map +1 -0
  165. package/dist/client/chunks/{vendor-CPIYyeuD.js → vendor-DSpR0IW6.js} +43 -43
  166. package/dist/client/chunks/{vendor-CPIYyeuD.js.map → vendor-DSpR0IW6.js.map} +1 -1
  167. package/dist/client/client/BatchCoordinator.d.ts +1 -1
  168. package/dist/client/client/CubeClient.d.ts +1 -1
  169. package/dist/client/components/AIAssistant/index.d.ts +3 -3
  170. package/dist/client/components/AIAssistant/utils.d.ts +1 -1
  171. package/dist/client/components/AgenticNotebook/ChatMessage.d.ts +1 -1
  172. package/dist/client/components/AgenticNotebook/NotebookCanvas.d.ts +1 -1
  173. package/dist/client/components/AgenticNotebook/NotebookMarkdownBlock.d.ts +1 -1
  174. package/dist/client/components/AgenticNotebook/NotebookPortletBlock.d.ts +2 -2
  175. package/dist/client/components/AgenticNotebook/agentChatParts.d.ts +34 -0
  176. package/dist/client/components/AgenticNotebook/chatMessageParts.d.ts +39 -0
  177. package/dist/client/components/AgenticNotebook/index.d.ts +2 -2
  178. package/dist/client/components/AgenticNotebook/useAgentChatController.d.ts +25 -0
  179. package/dist/client/components/AgenticNotebook/useNotebookAutosave.d.ts +12 -0
  180. package/dist/client/components/AnalysisBuilder/AnalysisAxisDropZone.d.ts +1 -1
  181. package/dist/client/components/AnalysisBuilder/AnalysisChartConfigPanel.d.ts +4 -4
  182. package/dist/client/components/AnalysisBuilder/AnalysisDisplayConfigPanel.d.ts +1 -1
  183. package/dist/client/components/AnalysisBuilder/AnalysisFilterGroup.d.ts +2 -2
  184. package/dist/client/components/AnalysisBuilder/AnalysisFilterItem.d.ts +2 -2
  185. package/dist/client/components/AnalysisBuilder/AnalysisFilterSection.d.ts +2 -2
  186. package/dist/client/components/AnalysisBuilder/AnalysisModeErrorBoundary.d.ts +1 -1
  187. package/dist/client/components/AnalysisBuilder/AnalysisQueryPanel.d.ts +1 -1
  188. package/dist/client/components/AnalysisBuilder/AnalysisQueryPanelParts.d.ts +64 -0
  189. package/dist/client/components/AnalysisBuilder/AnalysisResultsHeader.d.ts +67 -0
  190. package/dist/client/components/AnalysisBuilder/AnalysisResultsPanel.d.ts +1 -1
  191. package/dist/client/components/AnalysisBuilder/AnalysisTypeSelector.d.ts +1 -1
  192. package/dist/client/components/AnalysisBuilder/BreakdownComparisonToggle.d.ts +13 -0
  193. package/dist/client/components/AnalysisBuilder/BreakdownItemCard.d.ts +1 -1
  194. package/dist/client/components/AnalysisBuilder/BreakdownRow.d.ts +26 -0
  195. package/dist/client/components/AnalysisBuilder/BreakdownSection.d.ts +1 -1
  196. package/dist/client/components/AnalysisBuilder/DisplayOptionControl.d.ts +10 -0
  197. package/dist/client/components/AnalysisBuilder/ExecutionPlanPanel.d.ts +1 -1
  198. package/dist/client/components/AnalysisBuilder/ExecutionPlanPanelParts.d.ts +22 -0
  199. package/dist/client/components/AnalysisBuilder/ExplainAIPanel.d.ts +1 -1
  200. package/dist/client/components/AnalysisBuilder/FieldDetailPanel.d.ts +1 -1
  201. package/dist/client/components/AnalysisBuilder/FieldSearchItem.d.ts +1 -1
  202. package/dist/client/components/AnalysisBuilder/FieldSearchModal.d.ts +1 -1
  203. package/dist/client/components/AnalysisBuilder/FieldSearchResults.d.ts +16 -0
  204. package/dist/client/components/AnalysisBuilder/FilterConfigModal.d.ts +2 -2
  205. package/dist/client/components/AnalysisBuilder/FilterValueInput.d.ts +63 -0
  206. package/dist/client/components/AnalysisBuilder/FlowConfigPanel.d.ts +1 -1
  207. package/dist/client/components/AnalysisBuilder/FlowDepthControls.d.ts +10 -0
  208. package/dist/client/components/AnalysisBuilder/FlowModeContent.d.ts +3 -3
  209. package/dist/client/components/AnalysisBuilder/FlowVisualizationPicker.d.ts +7 -0
  210. package/dist/client/components/AnalysisBuilder/FunnelBindingKeySelector.d.ts +1 -1
  211. package/dist/client/components/AnalysisBuilder/FunnelConfigPanel.d.ts +1 -1
  212. package/dist/client/components/AnalysisBuilder/FunnelModeContent.d.ts +2 -2
  213. package/dist/client/components/AnalysisBuilder/FunnelStepCard.d.ts +1 -1
  214. package/dist/client/components/AnalysisBuilder/FunnelStepList.d.ts +1 -1
  215. package/dist/client/components/AnalysisBuilder/LegacyBooleanOptions.d.ts +8 -0
  216. package/dist/client/components/AnalysisBuilder/MetricItemCard.d.ts +1 -1
  217. package/dist/client/components/AnalysisBuilder/MetricRow.d.ts +23 -0
  218. package/dist/client/components/AnalysisBuilder/MetricsSection.d.ts +1 -1
  219. package/dist/client/components/AnalysisBuilder/RetentionConfigPanel.d.ts +2 -2
  220. package/dist/client/components/AnalysisBuilder/RetentionModeContent.d.ts +3 -3
  221. package/dist/client/components/AnalysisBuilder/SortToggleButton.d.ts +13 -0
  222. package/dist/client/components/AnalysisBuilder/StringArrayInput.d.ts +17 -0
  223. package/dist/client/components/AnalysisBuilder/filterConfigModalUtils.d.ts +30 -0
  224. package/dist/client/components/AnalysisBuilder/hooks/useAnalysisBuilderImperativeHandle.d.ts +13 -0
  225. package/dist/client/components/AnalysisBuilder/hooks/useDragReorder.d.ts +25 -0
  226. package/dist/client/components/AnalysisBuilder/hooks/useFieldSearchKeyboard.d.ts +11 -0
  227. package/dist/client/components/AnalysisBuilder/index.d.ts +1 -1
  228. package/dist/client/components/AnalysisBuilder/types.d.ts +28 -28
  229. package/dist/client/components/AnalysisBuilder/utils/axisConfigUtils.d.ts +23 -0
  230. package/dist/client/components/AnalysisBuilder/utils/executionPlanMarkdown.d.ts +8 -0
  231. package/dist/client/components/AnalysisBuilder/utils/fieldUtils.d.ts +7 -2
  232. package/dist/client/components/AnalysisBuilder/utils/filterUtils.d.ts +1 -1
  233. package/dist/client/components/AnalysisBuilder/utils/index.d.ts +6 -6
  234. package/dist/client/components/AnalysisBuilder/utils/queryUtils.d.ts +2 -2
  235. package/dist/client/components/AnalysisBuilder/utils/recentFieldsUtils.d.ts +2 -2
  236. package/dist/client/components/AnalysisBuilder/utils/resultsPanelDerive.d.ts +68 -0
  237. package/dist/client/components/AnalysisBuilder/utils/shareStateUtils.d.ts +68 -0
  238. package/dist/client/components/AnalysisBuilder/utils/sortUtils.d.ts +7 -0
  239. package/dist/client/components/AnalysisBuilder/utils/storageUtils.d.ts +2 -2
  240. package/dist/client/components/AnalysisBuilderLazy.d.ts +1 -1
  241. package/dist/client/components/AnalyticsDashboard.d.ts +1 -1
  242. package/dist/client/components/AnalyticsPortlet.d.ts +1 -1
  243. package/dist/client/components/ChartTypeSelector.d.ts +2 -2
  244. package/dist/client/components/DashboardFilterPanel.d.ts +1 -1
  245. package/dist/client/components/DashboardFilters/CompactFilterBar.d.ts +1 -1
  246. package/dist/client/components/DashboardFilters/CompactFilterBarParts.d.ts +26 -0
  247. package/dist/client/components/DashboardFilters/DashboardFilterConfigModal.d.ts +2 -2
  248. package/dist/client/components/DashboardFilters/DashboardFilterConfigModalParts.d.ts +27 -0
  249. package/dist/client/components/DashboardFilters/DashboardFilterItem.d.ts +2 -2
  250. package/dist/client/components/DashboardFilters/DashboardFilterValueInput.d.ts +39 -0
  251. package/dist/client/components/DashboardFilters/EditModeFilterList.d.ts +1 -1
  252. package/dist/client/components/DashboardFilters/FilterChip.d.ts +1 -1
  253. package/dist/client/components/DashboardFilters/FilterEditModal.d.ts +2 -2
  254. package/dist/client/components/DashboardFilters/FilterValuePopover.d.ts +1 -1
  255. package/dist/client/components/DashboardFilters/ReadOnlyFilterList.d.ts +2 -2
  256. package/dist/client/components/DashboardFilters/dashboardFilterConfigModalUtils.d.ts +21 -0
  257. package/dist/client/components/DashboardFilters/useCompactFilterBar.d.ts +19 -0
  258. package/dist/client/components/DashboardFilters/useDashboardFilterConfigModal.d.ts +69 -0
  259. package/dist/client/components/DashboardFilters/useDateRangeState.d.ts +19 -0
  260. package/dist/client/components/DashboardFilters/useFilterDropdowns.d.ts +21 -0
  261. package/dist/client/components/DashboardFilters/useFilterValueFetch.d.ts +23 -0
  262. package/dist/client/components/DashboardGrid.d.ts +2 -2
  263. package/dist/client/components/DashboardPortletCard.d.ts +2 -57
  264. package/dist/client/components/DebugModal.d.ts +2 -2
  265. package/dist/client/components/DrillBreadcrumb.d.ts +1 -1
  266. package/dist/client/components/DrillMenu.d.ts +1 -1
  267. package/dist/client/components/FloatingEditToolbar.d.ts +1 -1
  268. package/dist/client/components/MobileStackedLayout.d.ts +2 -2
  269. package/dist/client/components/PortletAnalysisModal.d.ts +1 -1
  270. package/dist/client/components/PortletContainer.d.ts +1 -1
  271. package/dist/client/components/PortletFilterConfigModal.d.ts +1 -1
  272. package/dist/client/components/RowManagedLayout.d.ts +1 -1
  273. package/dist/client/components/SchemaVisualization/CubeNode.d.ts +1 -1
  274. package/dist/client/components/SchemaVisualization/FieldDetailPanel.d.ts +1 -1
  275. package/dist/client/components/SchemaVisualization/RelationshipEdge.d.ts +1 -1
  276. package/dist/client/components/SchemaVisualization/SchemaVisualizationLazy.d.ts +1 -1
  277. package/dist/client/components/TextPortletModal.d.ts +2 -2
  278. package/dist/client/components/analyticsPortlet/PortletChart.d.ts +31 -0
  279. package/dist/client/components/analyticsPortlet/PortletChartView.d.ts +35 -0
  280. package/dist/client/components/analyticsPortlet/PortletStates.d.ts +65 -0
  281. package/dist/client/components/analyticsPortlet/parsePortletQuery.d.ts +22 -0
  282. package/dist/client/components/analyticsPortlet/portletRenderState.d.ts +31 -0
  283. package/dist/client/components/analyticsPortlet/usePortletDebugData.d.ts +37 -0
  284. package/dist/client/components/analyticsPortlet/usePortletDrillState.d.ts +17 -0
  285. package/dist/client/components/analyticsPortlet/usePortletQueryResults.d.ts +37 -0
  286. package/dist/client/components/charts/ActivityGridChart.config.d.ts +1 -1
  287. package/dist/client/components/charts/ActivityGridChart.d.ts +1 -1
  288. package/dist/client/components/charts/ActivityGridChart.helpers.d.ts +33 -0
  289. package/dist/client/components/charts/ActivityGridChart.render.d.ts +31 -0
  290. package/dist/client/components/charts/AreaChart.config.d.ts +1 -1
  291. package/dist/client/components/charts/AreaChart.d.ts +1 -1
  292. package/dist/client/components/charts/AxisFormatControls.d.ts +4 -2
  293. package/dist/client/components/charts/BarChart.config.d.ts +1 -1
  294. package/dist/client/components/charts/BarChart.d.ts +1 -1
  295. package/dist/client/components/charts/BarChart.helpers.d.ts +23 -0
  296. package/dist/client/components/charts/BarSeries.d.ts +29 -0
  297. package/dist/client/components/charts/BoxPlotChart.config.d.ts +1 -1
  298. package/dist/client/components/charts/BoxPlotChart.d.ts +1 -1
  299. package/dist/client/components/charts/BubbleChart.config.d.ts +1 -1
  300. package/dist/client/components/charts/BubbleChart.d.ts +1 -1
  301. package/dist/client/components/charts/BubbleChart.helpers.d.ts +43 -0
  302. package/dist/client/components/charts/BubbleChart.render.d.ts +21 -0
  303. package/dist/client/components/charts/CandlestickChart.config.d.ts +1 -1
  304. package/dist/client/components/charts/CandlestickChart.d.ts +1 -1
  305. package/dist/client/components/charts/ChartStates.d.ts +38 -0
  306. package/dist/client/components/charts/DataTable.config.d.ts +1 -1
  307. package/dist/client/components/charts/DataTable.d.ts +1 -1
  308. package/dist/client/components/charts/FunnelChart.config.d.ts +1 -1
  309. package/dist/client/components/charts/FunnelChart.d.ts +1 -1
  310. package/dist/client/components/charts/FunnelChart.helpers.d.ts +25 -0
  311. package/dist/client/components/charts/FunnelViews.d.ts +16 -0
  312. package/dist/client/components/charts/GaugeChart.config.d.ts +1 -1
  313. package/dist/client/components/charts/GaugeChart.d.ts +1 -1
  314. package/dist/client/components/charts/HeatMapCanvas.d.ts +25 -0
  315. package/dist/client/components/charts/HeatMapChart.config.d.ts +1 -1
  316. package/dist/client/components/charts/HeatMapChart.d.ts +1 -1
  317. package/dist/client/components/charts/HeatMapChart.helpers.d.ts +64 -0
  318. package/dist/client/components/charts/KpiDelta.config.d.ts +1 -1
  319. package/dist/client/components/charts/KpiDelta.d.ts +1 -1
  320. package/dist/client/components/charts/KpiDelta.helpers.d.ts +20 -0
  321. package/dist/client/components/charts/KpiNumber.config.d.ts +1 -1
  322. package/dist/client/components/charts/KpiNumber.d.ts +1 -1
  323. package/dist/client/components/charts/KpiNumber.helpers.d.ts +30 -0
  324. package/dist/client/components/charts/KpiStates.d.ts +19 -0
  325. package/dist/client/components/charts/KpiText.config.d.ts +1 -1
  326. package/dist/client/components/charts/KpiText.d.ts +1 -1
  327. package/dist/client/components/charts/LineChart.config.d.ts +1 -1
  328. package/dist/client/components/charts/LineChart.d.ts +1 -1
  329. package/dist/client/components/charts/MarkdownChart.config.d.ts +1 -1
  330. package/dist/client/components/charts/MarkdownChart.d.ts +1 -1
  331. package/dist/client/components/charts/MeasureProfileChart.config.d.ts +1 -1
  332. package/dist/client/components/charts/MeasureProfileChart.d.ts +1 -1
  333. package/dist/client/components/charts/PieChart.config.d.ts +1 -1
  334. package/dist/client/components/charts/PieChart.d.ts +1 -1
  335. package/dist/client/components/charts/RadarChart.config.d.ts +1 -1
  336. package/dist/client/components/charts/RadarChart.d.ts +1 -1
  337. package/dist/client/components/charts/RadialBarChart.config.d.ts +1 -1
  338. package/dist/client/components/charts/RadialBarChart.d.ts +1 -1
  339. package/dist/client/components/charts/RetentionCombinedChart.config.d.ts +1 -1
  340. package/dist/client/components/charts/RetentionCombinedChart.d.ts +1 -1
  341. package/dist/client/components/charts/RetentionHeatmap.config.d.ts +1 -1
  342. package/dist/client/components/charts/RetentionHeatmap.d.ts +1 -1
  343. package/dist/client/components/charts/SankeyChart.config.d.ts +1 -1
  344. package/dist/client/components/charts/SankeyChart.d.ts +1 -1
  345. package/dist/client/components/charts/ScatterChart.config.d.ts +1 -1
  346. package/dist/client/components/charts/ScatterChart.d.ts +1 -1
  347. package/dist/client/components/charts/ScatterChart.helpers.d.ts +43 -0
  348. package/dist/client/components/charts/ScatterSeries.d.ts +17 -0
  349. package/dist/client/components/charts/ScatterTooltip.d.ts +21 -0
  350. package/dist/client/components/charts/SunburstChart.config.d.ts +1 -1
  351. package/dist/client/components/charts/SunburstChart.d.ts +1 -1
  352. package/dist/client/components/charts/TreeMapChart.config.d.ts +1 -1
  353. package/dist/client/components/charts/TreeMapChart.d.ts +1 -1
  354. package/dist/client/components/charts/TreeMapChart.helpers.d.ts +37 -0
  355. package/dist/client/components/charts/TreeMapContent.d.ts +24 -0
  356. package/dist/client/components/charts/TreeMapLegend.d.ts +13 -0
  357. package/dist/client/components/charts/WaterfallChart.config.d.ts +1 -1
  358. package/dist/client/components/charts/WaterfallChart.d.ts +1 -1
  359. package/dist/client/components/charts/cartesianChartHelpers.d.ts +126 -0
  360. package/dist/client/components/charts/chartAxisResolution.d.ts +32 -0
  361. package/dist/client/components/charts/chartScaffolding.d.ts +77 -0
  362. package/dist/client/components/charts/gaugeChartHelpers.d.ts +36 -0
  363. package/dist/client/components/charts/index.d.ts +21 -21
  364. package/dist/client/components/charts/kpiTextHelpers.d.ts +46 -0
  365. package/dist/client/components/charts/radarChartHelpers.d.ts +14 -0
  366. package/dist/client/components/charts/radialBarChartHelpers.d.ts +22 -0
  367. package/dist/client/components/charts/useChartDimensions.d.ts +20 -0
  368. package/dist/client/components/charts/useKpiDimensions.d.ts +30 -0
  369. package/dist/client/components/dashboard/DashboardContext.d.ts +6 -6
  370. package/dist/client/components/dashboard/DashboardCoordinator.d.ts +1 -1
  371. package/dist/client/components/dashboard/DashboardEditBar.d.ts +5 -0
  372. package/dist/client/components/dashboard/DashboardProvider.d.ts +1 -1
  373. package/dist/client/components/dashboard/LayoutModeToggle.d.ts +10 -0
  374. package/dist/client/components/dashboard/dashboardGridUtils.d.ts +1 -1
  375. package/dist/client/components/dashboard/index.d.ts +7 -7
  376. package/dist/client/components/dashboardPortletCard/FilterFieldChip.d.ts +12 -0
  377. package/dist/client/components/dashboardPortletCard/PortletCardHeader.d.ts +43 -0
  378. package/dist/client/components/dashboardPortletCard/cardStyles.d.ts +33 -0
  379. package/dist/client/components/dashboardPortletCard/filterField.d.ts +12 -0
  380. package/dist/client/components/dashboardPortletCard/propsEqual.d.ts +55 -0
  381. package/dist/client/components/dashboardPortletCard/usePortletCardActions.d.ts +16 -0
  382. package/dist/client/components/portletAnalysisModal/saveValidation.d.ts +8 -0
  383. package/dist/client/components/shared/DateRangeFilter.d.ts +1 -1
  384. package/dist/client/components/shared/FilterBuilder.d.ts +1 -1
  385. package/dist/client/components/shared/FilterGroup.d.ts +1 -1
  386. package/dist/client/components/shared/FilterItem.d.ts +1 -1
  387. package/dist/client/components/shared/FilterValueSelector.d.ts +1 -1
  388. package/dist/client/components/shared/dateRangeUtils.d.ts +40 -0
  389. package/dist/client/components/shared/filterDisplayUtils.d.ts +10 -0
  390. package/dist/client/components/shared/filterItem/FilterDateRangeSelector.d.ts +17 -0
  391. package/dist/client/components/shared/filterItem/FilterFieldDropdown.d.ts +16 -0
  392. package/dist/client/components/shared/filterItem/dateRangeSync.d.ts +14 -0
  393. package/dist/client/components/shared/filterItem/fieldVisuals.d.ts +5 -0
  394. package/dist/client/components/shared/filterValueSelector/FilterValueInputs.d.ts +55 -0
  395. package/dist/client/components/shared/filterValueSelector/useFilterValueSelectorState.d.ts +25 -0
  396. package/dist/client/components/shared/queryFieldUtils.d.ts +21 -0
  397. package/dist/client/components/shared/types.d.ts +8 -8
  398. package/dist/client/components/shared/utils.d.ts +7 -46
  399. package/dist/client/components.d.ts +11 -11
  400. package/dist/client/components.js +3 -3
  401. package/dist/client/hooks/agentChatStream.d.ts +39 -0
  402. package/dist/client/hooks/analysisQueryExecutionModes.d.ts +133 -0
  403. package/dist/client/hooks/dashboard/layoutUtils.d.ts +1 -1
  404. package/dist/client/hooks/dashboard/useDashboardController.d.ts +2 -2
  405. package/dist/client/hooks/dashboard/useGridLayoutEngine.d.ts +1 -1
  406. package/dist/client/hooks/dashboard/useRowLayoutEngine.d.ts +1 -1
  407. package/dist/client/hooks/drillNavigation.d.ts +25 -0
  408. package/dist/client/hooks/queries/index.d.ts +9 -9
  409. package/dist/client/hooks/queries/useCubeLoadQuery.d.ts +2 -2
  410. package/dist/client/hooks/queries/useCubeMetaQuery.d.ts +2 -2
  411. package/dist/client/hooks/queries/useDryRunQuery.d.ts +2 -2
  412. package/dist/client/hooks/queries/useExplainAI.d.ts +1 -1
  413. package/dist/client/hooks/queries/useExplainQuery.d.ts +1 -1
  414. package/dist/client/hooks/queries/useFlowQuery.d.ts +1 -1
  415. package/dist/client/hooks/queries/useFunnelQuery.d.ts +1 -1
  416. package/dist/client/hooks/queries/useMultiCubeLoadQuery.d.ts +1 -1
  417. package/dist/client/hooks/queries/useRetentionQuery.d.ts +1 -1
  418. package/dist/client/hooks/useAgentChat.d.ts +1 -1
  419. package/dist/client/hooks/useAnalysisAI.d.ts +4 -4
  420. package/dist/client/hooks/useAnalysisBuilderHook.d.ts +27 -27
  421. package/dist/client/hooks/useAnalysisChartDefaults.d.ts +4 -4
  422. package/dist/client/hooks/useAnalysisCombinedFields.d.ts +2 -2
  423. package/dist/client/hooks/useAnalysisInitialization.d.ts +1 -1
  424. package/dist/client/hooks/useAnalysisQueryBuilder.d.ts +3 -3
  425. package/dist/client/hooks/useAnalysisQueryExecution.d.ts +7 -7
  426. package/dist/client/hooks/useAnalysisShare.d.ts +1 -1
  427. package/dist/client/hooks/useAnalysisUIState.d.ts +2 -2
  428. package/dist/client/hooks/useDashboardHook.d.ts +2 -2
  429. package/dist/client/hooks/useDataBrowser.d.ts +5 -5
  430. package/dist/client/hooks/useDrillInteraction.d.ts +1 -1
  431. package/dist/client/hooks/useTheme.d.ts +1 -1
  432. package/dist/client/hooks/useTranslation.d.ts +1 -1
  433. package/dist/client/hooks.d.ts +11 -11
  434. package/dist/client/hooks.js +3 -3
  435. package/dist/client/icons/defaultIcons.d.ts +1 -1
  436. package/dist/client/icons/index.d.ts +3 -3
  437. package/dist/client/icons/registry.d.ts +1 -1
  438. package/dist/client/icons.js +2 -2
  439. package/dist/client/index.d.ts +74 -74
  440. package/dist/client/index.js +816 -669
  441. package/dist/client/index.js.map +1 -1
  442. package/dist/client/providers/CubeApiProvider.d.ts +3 -3
  443. package/dist/client/providers/CubeFeaturesProvider.d.ts +1 -1
  444. package/dist/client/providers/CubeMetaContext.d.ts +1 -1
  445. package/dist/client/providers/CubeProvider.d.ts +7 -7
  446. package/dist/client/providers/I18nProvider.d.ts +1 -1
  447. package/dist/client/providers.d.ts +4 -4
  448. package/dist/client/providers.js +3 -3
  449. package/dist/client/schema.d.ts +2 -2
  450. package/dist/client/schema.js +1 -1
  451. package/dist/client/shared/chartConfigBuilders.d.ts +18 -0
  452. package/dist/client/shared/chartDefaults.d.ts +4 -4
  453. package/dist/client/shared/components/QueryAnalysisPanel.d.ts +1 -1
  454. package/dist/client/shared/components/QueryAnalysisPanel.sections.d.ts +41 -0
  455. package/dist/client/shared/index.d.ts +7 -7
  456. package/dist/client/shared/queryTransforms.d.ts +20 -0
  457. package/dist/client/shared/types.d.ts +1 -1
  458. package/dist/client/shared/utils.d.ts +4 -6
  459. package/dist/client/stores/analysisBuilderStore.d.ts +9 -9
  460. package/dist/client/stores/dashboardStore.d.ts +3 -3
  461. package/dist/client/stores/dataBrowserStore.d.ts +1 -1
  462. package/dist/client/stores/notebookStore.d.ts +1 -1
  463. package/dist/client/stores/optionsToAnalysisConfig.d.ts +17 -0
  464. package/dist/client/stores/slices/coreSlice.d.ts +3 -3
  465. package/dist/client/stores/slices/flowSlice.d.ts +3 -3
  466. package/dist/client/stores/slices/funnelSlice.d.ts +3 -3
  467. package/dist/client/stores/slices/index.d.ts +7 -7
  468. package/dist/client/stores/slices/querySlice.d.ts +3 -3
  469. package/dist/client/stores/slices/retentionSlice.d.ts +3 -3
  470. package/dist/client/stores/slices/uiSlice.d.ts +2 -2
  471. package/dist/client/styles.css +1 -1
  472. package/dist/client/types/analysisConfig.d.ts +4 -4
  473. package/dist/client/types/drill.d.ts +5 -5
  474. package/dist/client/types/flow.d.ts +2 -2
  475. package/dist/client/types/funnel.d.ts +1 -1
  476. package/dist/client/types/retention.d.ts +2 -2
  477. package/dist/client/types.d.ts +12 -12
  478. package/dist/client/utils/axisValueFormatting.d.ts +32 -0
  479. package/dist/client/utils/chartUtils.d.ts +1 -1
  480. package/dist/client/utils/configMigration.d.ts +2 -2
  481. package/dist/client/utils/drillQueryBuilder.d.ts +2 -2
  482. package/dist/client/utils/exportXlsx.d.ts +2 -2
  483. package/dist/client/utils/filterUtils.d.ts +1 -1
  484. package/dist/client/utils/funnelExecution.d.ts +2 -2
  485. package/dist/client/utils/funnelValidation.d.ts +2 -2
  486. package/dist/client/utils/index.d.ts +10 -10
  487. package/dist/client/utils/joinReachability.d.ts +1 -1
  488. package/dist/client/utils/multiQueryUtils.d.ts +1 -1
  489. package/dist/client/utils/multiQueryValidation.d.ts +1 -1
  490. package/dist/client/utils/pivotUtils.d.ts +1 -1
  491. package/dist/client/utils/shareUtils.d.ts +1 -1
  492. package/dist/client/utils/thumbnail.d.ts +1 -1
  493. package/dist/client/utils/timeValueFormatting.d.ts +32 -0
  494. package/dist/client/utils.d.ts +2 -2
  495. package/dist/client/utils.js +6 -6
  496. package/dist/client-bundle-stats.html +1 -1
  497. package/dist/mcp-app/mcp-app.html +55 -55
  498. package/dist/server/{server/adapters → adapters}/base-adapter.d.ts +81 -36
  499. package/dist/server/adapters/databend-adapter.d.ts +63 -0
  500. package/dist/server/{server/adapters → adapters}/duckdb-adapter.d.ts +6 -60
  501. package/dist/server/adapters/mysql-adapter.d.ts +65 -0
  502. package/dist/server/adapters/postgres-adapter.d.ts +55 -0
  503. package/dist/server/{server/adapters → adapters}/singlestore-adapter.d.ts +1 -1
  504. package/dist/server/adapters/snowflake-adapter.d.ts +53 -0
  505. package/dist/server/{server/adapters → adapters}/sqlite-adapter.d.ts +13 -33
  506. package/dist/server/adapters/window-function-builder.d.ts +11 -0
  507. package/dist/server/{server/agent → agent}/chart-validation.d.ts +0 -5
  508. package/dist/server/agent/handler-steps.d.ts +37 -0
  509. package/dist/server/{server/agent → agent}/handler.d.ts +3 -3
  510. package/dist/server/agent/index.d.ts +11 -0
  511. package/dist/server/{server/agent → agent}/providers/anthropic.d.ts +1 -1
  512. package/dist/server/{server/agent → agent}/providers/factory.d.ts +1 -1
  513. package/dist/server/{server/agent → agent}/providers/google.d.ts +1 -1
  514. package/dist/server/agent/providers/index.d.ts +6 -0
  515. package/dist/server/{server/agent → agent}/providers/openai.d.ts +1 -1
  516. package/dist/server/{server/agent → agent}/system-prompt.d.ts +1 -1
  517. package/dist/server/{server/agent → agent}/tools.d.ts +4 -4
  518. package/dist/server/{server/agent → agent}/types.d.ts +1 -1
  519. package/dist/server/ai/discovery-helpers.d.ts +29 -0
  520. package/dist/server/{server/ai → ai}/discovery.d.ts +6 -2
  521. package/dist/server/{server/ai → ai}/index.d.ts +6 -6
  522. package/dist/server/ai/suggestion-helpers.d.ts +33 -0
  523. package/dist/server/{server/ai → ai}/suggestion.d.ts +2 -5
  524. package/dist/server/ai/validation-helpers.d.ts +21 -0
  525. package/dist/server/{server/ai → ai}/validation.d.ts +2 -5
  526. package/dist/server/builders/analysis-utils.d.ts +106 -0
  527. package/dist/server/{server/builders → builders}/comparison-query-builder.d.ts +2 -2
  528. package/dist/server/{server/builders → builders}/cte-builder.d.ts +48 -2
  529. package/dist/server/{server/builders → builders}/date-time-builder.d.ts +7 -2
  530. package/dist/server/builders/date-time-helpers.d.ts +34 -0
  531. package/dist/server/{server/builders → builders}/filter-builder.d.ts +5 -3
  532. package/dist/server/builders/filter-operators.d.ts +23 -0
  533. package/dist/server/builders/flow-query-builder.d.ts +136 -0
  534. package/dist/server/{server/builders → builders}/funnel-query-builder.d.ts +18 -7
  535. package/dist/server/{server/builders → builders}/group-by-builder.d.ts +13 -2
  536. package/dist/server/builders/index.d.ts +13 -0
  537. package/dist/server/{server/builders → builders}/measure-builder.d.ts +46 -3
  538. package/dist/server/{server/builders → builders}/retention-query-builder.d.ts +14 -16
  539. package/dist/server/cache-providers/index.d.ts +5 -0
  540. package/dist/server/{server/cache-providers → cache-providers}/memory.d.ts +1 -1
  541. package/dist/server/{server/cache-utils.d.ts → cache-utils.d.ts} +26 -6
  542. package/dist/server/compiler-metadata.d.ts +11 -0
  543. package/dist/server/{server/compiler.d.ts → compiler.d.ts} +9 -15
  544. package/dist/server/{server/cube-utils.d.ts → cube-utils.d.ts} +29 -4
  545. package/dist/server/{server/database-utils.d.ts → database-utils.d.ts} +1 -1
  546. package/dist/server/execution/annotation-builder.d.ts +12 -0
  547. package/dist/server/execution/filter-cache-preloader.d.ts +19 -0
  548. package/dist/server/execution/mode-router.d.ts +25 -0
  549. package/dist/server/execution/query-result-cache.d.ts +21 -0
  550. package/dist/server/execution/result-post-processor.d.ts +10 -0
  551. package/dist/server/{server/executor.d.ts → executor.d.ts} +35 -32
  552. package/dist/server/{server/executors → executors}/base-executor.d.ts +2 -2
  553. package/dist/server/{server/executors → executors}/databend-executor.d.ts +2 -2
  554. package/dist/server/{server/executors → executors}/duckdb-executor.d.ts +2 -2
  555. package/dist/server/executors/explain-utils.d.ts +28 -0
  556. package/dist/server/{server/executors → executors}/index.d.ts +9 -9
  557. package/dist/server/{server/executors → executors}/mysql-executor.d.ts +2 -2
  558. package/dist/server/{server/executors → executors}/postgres-executor.d.ts +2 -2
  559. package/dist/server/{server/executors → executors}/singlestore-executor.d.ts +2 -2
  560. package/dist/server/{server/executors → executors}/snowflake-executor.d.ts +2 -2
  561. package/dist/server/{server/executors → executors}/sqlite-executor.d.ts +2 -2
  562. package/dist/server/{server/explain → explain}/databend-parser.d.ts +1 -1
  563. package/dist/server/{server/explain → explain}/duckdb-parser.d.ts +1 -1
  564. package/dist/server/explain/explain-tree.d.ts +20 -0
  565. package/dist/server/explain/index.d.ts +9 -0
  566. package/dist/server/{server/explain → explain}/mysql-parser.d.ts +1 -1
  567. package/dist/server/{server/explain → explain}/postgres-parser.d.ts +1 -1
  568. package/dist/server/{server/explain → explain}/snowflake-parser.d.ts +1 -1
  569. package/dist/server/{server/explain → explain}/sqlite-parser.d.ts +1 -1
  570. package/dist/server/{server/filter-cache.d.ts → filter-cache.d.ts} +2 -2
  571. package/dist/server/{server/gap-filler.d.ts → gap-filler.d.ts} +8 -2
  572. package/dist/server/google-BOnRIN1w.cjs +2 -0
  573. package/dist/{adapters/google-CFYljAOF.js → server/google-CvKM9yVM.js} +83 -71
  574. package/dist/server/index.cjs +179 -211
  575. package/dist/server/index.d.ts +38 -2
  576. package/dist/server/index.js +6330 -6458
  577. package/dist/server/logical-plan/cte-planner-helpers.d.ts +37 -0
  578. package/dist/server/logical-plan/cte-planner.d.ts +104 -0
  579. package/dist/server/logical-plan/filter-propagation.d.ts +44 -0
  580. package/dist/server/{server/logical-plan → logical-plan}/index.d.ts +11 -6
  581. package/dist/server/logical-plan/join-planner.d.ts +27 -0
  582. package/dist/server/{server/logical-plan → logical-plan}/logical-plan-builder.d.ts +5 -5
  583. package/dist/server/logical-plan/logical-planner.d.ts +39 -0
  584. package/dist/server/{server/logical-plan → logical-plan}/optimiser.d.ts +4 -2
  585. package/dist/server/logical-plan/plan-analysis-reporter.d.ts +59 -0
  586. package/dist/server/logical-plan/planner-utils.d.ts +20 -0
  587. package/dist/server/{server/logical-plan → logical-plan}/types.d.ts +18 -10
  588. package/dist/server/measure-classification.d.ts +40 -0
  589. package/dist/server/{openai-BZdAA7Ji.js → openai-cDlFSTk6.js} +60 -58
  590. package/dist/server/openai-vXvLni8v.cjs +1 -0
  591. package/dist/server/physical-plan/drizzle-plan-builder.d.ts +50 -0
  592. package/dist/server/{server/physical-plan → physical-plan}/drizzle-sql-builder.d.ts +14 -3
  593. package/dist/server/physical-plan/index.d.ts +2 -0
  594. package/dist/server/{server/physical-plan → physical-plan}/processors/cte-processor.d.ts +2 -2
  595. package/dist/server/physical-plan/processors/index.d.ts +8 -0
  596. package/dist/server/{server/physical-plan → physical-plan}/processors/joins-processor.d.ts +2 -2
  597. package/dist/server/physical-plan/processors/keys-dedup-processor.d.ts +13 -0
  598. package/dist/server/physical-plan/processors/multi-fact-processor.d.ts +13 -0
  599. package/dist/server/{server/physical-plan → physical-plan}/processors/predicates-processor.d.ts +2 -2
  600. package/dist/server/{server/physical-plan → physical-plan}/processors/selection-processor.d.ts +2 -2
  601. package/dist/server/{server/physical-plan → physical-plan}/processors/shared.d.ts +9 -4
  602. package/dist/server/{server/physical-plan → physical-plan}/processors/window-processor.d.ts +2 -2
  603. package/dist/server/query-handlers.d.ts +79 -0
  604. package/dist/server/query-modes.d.ts +28 -0
  605. package/dist/server/query-validator.d.ts +9 -0
  606. package/dist/server/{server/resolvers → resolvers}/calculated-measure-resolver.d.ts +6 -1
  607. package/dist/server/{server/resolvers → resolvers}/index.d.ts +2 -2
  608. package/dist/server/{server/resolvers → resolvers}/join-path-resolver.d.ts +7 -1
  609. package/dist/server/sql-format.d.ts +11 -0
  610. package/dist/server/{server/template-substitution.d.ts → template-substitution.d.ts} +1 -1
  611. package/dist/server/{server/types → types}/cache.d.ts +1 -1
  612. package/dist/server/{server/types → types}/cube.d.ts +22 -8
  613. package/dist/server/{server/types → types}/executor.d.ts +2 -2
  614. package/dist/server/{server/types → types}/flow.d.ts +7 -6
  615. package/dist/server/{server/types → types}/funnel.d.ts +1 -1
  616. package/dist/server/types/index.d.ts +11 -0
  617. package/dist/server/{server/types → types}/metadata.d.ts +1 -1
  618. package/dist/server/{server/types → types}/query.d.ts +4 -4
  619. package/dist/server/{server/types → types}/retention.d.ts +1 -1
  620. package/dist/server/{server/types → types}/utils.d.ts +1 -1
  621. package/dist/server/types/validation.d.ts +14 -0
  622. package/package.json +22 -7
  623. package/dist/adapters/google-DUlXeeDA.cjs +0 -2
  624. package/dist/adapters/handler-CzbCuS6_.cjs +0 -25
  625. package/dist/adapters/locale-BPB7flIG.js +0 -8249
  626. package/dist/adapters/locale-tIMrNcCr.cjs +0 -198
  627. package/dist/adapters/mcp-transport-C2QzXm6h.cjs +0 -40
  628. package/dist/adapters/mcp-transport-D0B3S1tB.js +0 -579
  629. package/dist/adapters/openai-D_U4V0kT.cjs +0 -1
  630. package/dist/adapters/utils-KV37IBIv.cjs +0 -128
  631. package/dist/client/chunks/DashboardEditModal-CjCIAT-J.js.map +0 -1
  632. package/dist/client/chunks/RetentionCombinedChart-BPkfcTa8.js.map +0 -1
  633. package/dist/client/chunks/RetentionHeatmap-DGzLzr7h.js.map +0 -1
  634. package/dist/client/chunks/analysis-builder-MuMIzoRq.js +0 -5367
  635. package/dist/client/chunks/analysis-builder-MuMIzoRq.js.map +0 -1
  636. package/dist/client/chunks/analysis-builder-shared-CYVwSPqt.js.map +0 -1
  637. package/dist/client/chunks/chart-activity-grid-DmruKPab.js.map +0 -1
  638. package/dist/client/chunks/chart-area-DHCPM4Em.js +0 -450
  639. package/dist/client/chunks/chart-area-DHCPM4Em.js.map +0 -1
  640. package/dist/client/chunks/chart-bar-KddciGDv.js +0 -271
  641. package/dist/client/chunks/chart-bar-KddciGDv.js.map +0 -1
  642. package/dist/client/chunks/chart-box-plot-DYKfyOI8.js.map +0 -1
  643. package/dist/client/chunks/chart-bubble-CfqiB538.js +0 -273
  644. package/dist/client/chunks/chart-bubble-CfqiB538.js.map +0 -1
  645. package/dist/client/chunks/chart-candlestick-Dwc92Mrj.js.map +0 -1
  646. package/dist/client/chunks/chart-config-area-Bq_UsW3x.js +0 -101
  647. package/dist/client/chunks/chart-config-area-Bq_UsW3x.js.map +0 -1
  648. package/dist/client/chunks/chart-config-bar-_JEGZnAu.js +0 -95
  649. package/dist/client/chunks/chart-config-bar-_JEGZnAu.js.map +0 -1
  650. package/dist/client/chunks/chart-config-box-plot-DJ-dWWXA.js.map +0 -1
  651. package/dist/client/chunks/chart-config-gauge-DSwC04l0.js.map +0 -1
  652. package/dist/client/chunks/chart-config-kpi-delta-D6BIkHL3.js.map +0 -1
  653. package/dist/client/chunks/chart-config-kpi-number-Bx-V9a62.js.map +0 -1
  654. package/dist/client/chunks/chart-config-kpi-text-CcqC1u-8.js.map +0 -1
  655. package/dist/client/chunks/chart-config-line-Db3jDsWc.js.map +0 -1
  656. package/dist/client/chunks/chart-config-pie-DD4SmRTF.js.map +0 -1
  657. package/dist/client/chunks/chart-config-radar-CC2XAaGr.js.map +0 -1
  658. package/dist/client/chunks/chart-config-radial-bar-DmxKx1R0.js.map +0 -1
  659. package/dist/client/chunks/chart-config-tree-map-BI-oQStO.js.map +0 -1
  660. package/dist/client/chunks/chart-config-waterfall-DSnyixbI.js.map +0 -1
  661. package/dist/client/chunks/chart-data-table-BO4sXsim.js.map +0 -1
  662. package/dist/client/chunks/chart-funnel-DnWRsmnS.js.map +0 -1
  663. package/dist/client/chunks/chart-gauge-CgMeqeGb.js +0 -424
  664. package/dist/client/chunks/chart-gauge-CgMeqeGb.js.map +0 -1
  665. package/dist/client/chunks/chart-heat-map-DcGm9SWK.js +0 -231
  666. package/dist/client/chunks/chart-heat-map-DcGm9SWK.js.map +0 -1
  667. package/dist/client/chunks/chart-kpi-delta-C2wKPqCb.js +0 -343
  668. package/dist/client/chunks/chart-kpi-delta-C2wKPqCb.js.map +0 -1
  669. package/dist/client/chunks/chart-kpi-number-gWYF44ol.js +0 -322
  670. package/dist/client/chunks/chart-kpi-number-gWYF44ol.js.map +0 -1
  671. package/dist/client/chunks/chart-kpi-text-DIYtJtk4.js +0 -149
  672. package/dist/client/chunks/chart-kpi-text-DIYtJtk4.js.map +0 -1
  673. package/dist/client/chunks/chart-line-9BEGN5Ti.js +0 -433
  674. package/dist/client/chunks/chart-line-9BEGN5Ti.js.map +0 -1
  675. package/dist/client/chunks/chart-markdown-dgUetjyM.js.map +0 -1
  676. package/dist/client/chunks/chart-measure-profile-ClfpLs4q.js.map +0 -1
  677. package/dist/client/chunks/chart-pie-BjAIhyOi.js +0 -172
  678. package/dist/client/chunks/chart-pie-BjAIhyOi.js.map +0 -1
  679. package/dist/client/chunks/chart-radar-Dgkcl2bN.js +0 -154
  680. package/dist/client/chunks/chart-radar-Dgkcl2bN.js.map +0 -1
  681. package/dist/client/chunks/chart-radial-bar-DdWJjAhK.js +0 -148
  682. package/dist/client/chunks/chart-radial-bar-DdWJjAhK.js.map +0 -1
  683. package/dist/client/chunks/chart-sankey-M3XpO_ah.js.map +0 -1
  684. package/dist/client/chunks/chart-scatter-DaHYP_OL.js +0 -255
  685. package/dist/client/chunks/chart-scatter-DaHYP_OL.js.map +0 -1
  686. package/dist/client/chunks/chart-sunburst-A_u6lqlS.js.map +0 -1
  687. package/dist/client/chunks/chart-tree-map-uiwUU4nb.js +0 -298
  688. package/dist/client/chunks/chart-tree-map-uiwUU4nb.js.map +0 -1
  689. package/dist/client/chunks/chart-waterfall-BuhMoagf.js.map +0 -1
  690. package/dist/client/chunks/charts-core-jRtb0S2M.js.map +0 -1
  691. package/dist/client/chunks/schema-visualization-DE09kQE3.js.map +0 -1
  692. package/dist/client/chunks/useDirtyStateTracking-CoeQbprt.js.map +0 -1
  693. package/dist/client/chunks/useExplainAI-DlnXWdmz.js.map +0 -1
  694. package/dist/client/chunks/utils-CTKNaXS8.js.map +0 -1
  695. package/dist/server/google-DUlXeeDA.cjs +0 -2
  696. package/dist/server/openai-D_U4V0kT.cjs +0 -1
  697. package/dist/server/server/adapters/databend-adapter.d.ts +0 -114
  698. package/dist/server/server/adapters/mysql-adapter.d.ts +0 -122
  699. package/dist/server/server/adapters/postgres-adapter.d.ts +0 -119
  700. package/dist/server/server/adapters/snowflake-adapter.d.ts +0 -116
  701. package/dist/server/server/agent/index.d.ts +0 -11
  702. package/dist/server/server/agent/providers/index.d.ts +0 -6
  703. package/dist/server/server/builders/flow-query-builder.d.ts +0 -109
  704. package/dist/server/server/builders/index.d.ts +0 -13
  705. package/dist/server/server/cache-providers/index.d.ts +0 -5
  706. package/dist/server/server/explain/index.d.ts +0 -9
  707. package/dist/server/server/index.d.ts +0 -36
  708. package/dist/server/server/logical-plan/logical-planner.d.ts +0 -232
  709. package/dist/server/server/physical-plan/drizzle-plan-builder.d.ts +0 -50
  710. package/dist/server/server/physical-plan/index.d.ts +0 -2
  711. package/dist/server/server/physical-plan/processors/index.d.ts +0 -6
  712. package/dist/server/server/types/index.d.ts +0 -11
  713. /package/dist/server/{server/agent → agent}/providers/types.d.ts +0 -0
  714. /package/dist/server/{server/ai → ai}/mcp-prompts.d.ts +0 -0
  715. /package/dist/server/{server/ai → ai}/query-schema.d.ts +0 -0
  716. /package/dist/server/{server/ai → ai}/schemas.d.ts +0 -0
  717. /package/dist/server/{server/prompts → prompts}/explain-analysis-prompt.d.ts +0 -0
  718. /package/dist/server/{server/prompts → prompts}/index.d.ts +0 -0
  719. /package/dist/server/{server/prompts → prompts}/single-step-prompt.d.ts +0 -0
  720. /package/dist/server/{server/prompts → prompts}/step0-validation-prompt.d.ts +0 -0
  721. /package/dist/server/{server/prompts → prompts}/step1-shape-prompt.d.ts +0 -0
  722. /package/dist/server/{server/prompts → prompts}/step2-complete-prompt.d.ts +0 -0
  723. /package/dist/server/{server/prompts → prompts}/types.d.ts +0 -0
  724. /package/dist/server/{server/types → types}/analysis.d.ts +0 -0
  725. /package/dist/server/{server/types → types}/core.d.ts +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DashboardEditModal-okVfH8ZK.js","names":[],"sources":["../../../src/client/types.ts","../../../src/client/types/analysisConfig.ts","../../../src/client/utils/configMigration.ts","../../../src/client/utils/filterUtils.ts","../../../src/client/components/analyticsPortlet/parsePortletQuery.ts","../../../src/client/utils/drillQueryBuilder.ts","../../../src/client/hooks/drillNavigation.ts","../../../src/client/hooks/useDrillInteraction.ts","../../../src/client/components/analyticsPortlet/usePortletDrillState.ts","../../../src/client/components/analyticsPortlet/usePortletQueryResults.ts","../../../src/client/components/analyticsPortlet/usePortletDebugData.ts","../../../src/client/components/analyticsPortlet/portletRenderState.ts","../../../src/client/components/ChartErrorBoundary.tsx","../../../src/client/components/DrillMenu.tsx","../../../src/client/components/DrillBreadcrumb.tsx","../../../src/client/components/analyticsPortlet/PortletChart.tsx","../../../src/client/components/analyticsPortlet/PortletChartView.tsx","../../../src/client/components/analyticsPortlet/PortletStates.tsx","../../../src/client/components/AnalyticsPortlet.tsx","../../../src/client/stores/dashboardStore.tsx","../../../src/client/hooks/useScrollDetection.ts","../../../src/client/hooks/useElementVisibility.ts","../../../src/client/hooks/useDragAutoScroll.ts","../../../src/client/components/dashboardPortletCard/propsEqual.ts","../../../src/client/components/dashboardPortletCard/filterField.ts","../../../src/client/components/dashboardPortletCard/usePortletCardActions.ts","../../../src/client/shared/components/CodeBlock.tsx","../../../src/client/components/DebugModal.tsx","../../../src/client/components/dashboardPortletCard/PortletCardHeader.tsx","../../../src/client/components/dashboardPortletCard/FilterFieldChip.tsx","../../../src/client/components/dashboardPortletCard/cardStyles.ts","../../../src/client/components/DashboardPortletCard.tsx","../../../src/client/components/RowManagedLayout.tsx","../../../src/client/hooks/dashboard/useGridLayoutEngine.ts","../../../src/client/hooks/dashboard/layoutUtils.ts","../../../src/client/hooks/dashboard/useRowLayoutEngine.ts","../../../src/client/hooks/dashboard/useDashboardController.ts","../../../src/client/hooks/useDashboardHook.ts","../../../src/client/components/dashboard/dashboardGridUtils.tsx","../../../src/client/components/dashboard/DashboardContext.tsx","../../../src/client/components/dashboard/DashboardCoordinator.tsx","../../../src/client/components/dashboard/DashboardProvider.tsx","../../../src/client/utils/colorPalettes.ts","../../../src/client/components/FloatingEditToolbar.tsx","../../../src/client/components/ColorPaletteSelector.tsx","../../../src/client/components/dashboard/LayoutModeToggle.tsx","../../../src/client/components/dashboard/DashboardEditBar.tsx","../../../src/client/components/dashboard/DashboardToolbar.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/components/shared/dateRangeUtils.ts","../../../src/client/components/DashboardFilters/dashboardFilterConfigModalUtils.ts","../../../src/client/components/DashboardFilters/useFilterDropdowns.ts","../../../src/client/components/DashboardFilters/useFilterValueFetch.ts","../../../src/client/components/DashboardFilters/useDateRangeState.ts","../../../src/client/components/DashboardFilters/useDashboardFilterConfigModal.ts","../../../src/client/components/DashboardFilters/DashboardFilterValueInput.tsx","../../../src/client/components/DashboardFilters/DashboardFilterConfigModalParts.tsx","../../../src/client/components/DashboardFilters/DashboardFilterConfigModal.tsx","../../../src/client/components/DashboardFilters/FilterEditModal.tsx","../../../src/client/components/DashboardFilters/EditModeFilterList.tsx","../../../src/client/components/shared/filterDisplayUtils.ts","../../../src/client/components/DashboardFilters/useCompactFilterBar.ts","../../../src/client/components/DashboardFilters/DatePresetChips.tsx","../../../src/client/components/DashboardFilters/CustomDateDropdown.tsx","../../../src/client/components/DashboardFilters/XTDDropdown.tsx","../../../src/client/components/shared/filterValueSelector/useFilterValueSelectorState.ts","../../../src/client/components/shared/filterValueSelector/FilterValueInputs.tsx","../../../src/client/components/shared/FilterValueSelector.tsx","../../../src/client/components/DashboardFilters/FilterValuePopover.tsx","../../../src/client/components/DashboardFilters/FilterChip.tsx","../../../src/client/components/DashboardFilters/CompactFilterBarParts.tsx","../../../src/client/components/DashboardFilters/CompactFilterBar.tsx","../../../src/client/components/DashboardFilterPanel.tsx","../../../src/client/components/dashboard/DashboardFilterBar.tsx","../../../src/client/components/MobileStackedLayout.tsx","../../../src/client/components/ScaledGridWrapper.tsx","../../../src/client/components/dashboard/DashboardGridSurface.tsx","../../../src/client/components/Modal.tsx","../../../src/client/components/AnalysisBuilderLazy.tsx","../../../src/client/adapters/funnelModeAdapter.ts","../../../src/client/adapters/flowModeAdapter.ts","../../../src/client/adapters/retentionModeAdapter.ts","../../../src/client/components/portletAnalysisModal/saveValidation.ts","../../../src/client/components/PortletAnalysisModal.tsx","../../../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/TextPortletModal.tsx","../../../src/client/utils/joinReachability.ts","../../../src/client/components/PortletFilterConfigModal.tsx","../../../src/client/components/ConfirmModal.tsx","../../../src/client/components/dashboard/DashboardModals.tsx","../../../src/client/components/DashboardGrid.tsx","../../../src/client/components/AnalyticsDashboard.tsx","../../../src/client/components/PortletContainer.tsx","../../../src/client/components/DashboardEditModal.tsx"],"sourcesContent":["/**\n * Type definitions for drizzle-cube client components\n */\n\nimport type { ReactNode } from 'react'\nimport type { ColorPalette } from './utils/colorPalettes.js'\nimport type { FunnelBindingKey } from './types/funnel.js'\nimport type { FlowChartData } from './types/flow.js'\nimport type { RetentionChartData } from './types/retention.js'\n\n// Time granularity type for drill-down support\nexport type TimeGranularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year'\n\n// Cube metadata types\nexport interface CubeMetaMeasure {\n name: string\n title: string\n shortTitle: string\n type: string\n /** Dimension names shown when drilling into this measure */\n drillMembers?: string[]\n}\n\nexport interface CubeMetaDimension {\n name: string\n title: string\n shortTitle: string\n type: string\n /** Supported granularities for time dimensions (for time-based drill-down) */\n granularities?: TimeGranularity[]\n}\n\n// Legacy type alias for backward compatibility\nexport interface CubeMetaField {\n name: string\n title: string\n shortTitle: string\n type: string\n}\n\nexport interface CubeMetaRelationship {\n targetCube: string\n relationship: 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany'\n joinFields?: Array<{\n sourceField: string\n targetField: string\n }>\n}\n\n/**\n * Hierarchy metadata for structured drill-down paths\n */\nexport interface CubeMetaHierarchy {\n /** Unique identifier for the hierarchy */\n name: string\n /** Display title for the hierarchy */\n title: string\n /** Cube name this hierarchy belongs to */\n cubeName: string\n /** Full dimension names in order from least to most granular */\n levels: string[]\n}\n\nexport interface CubeMetaCube {\n name: string\n title: string\n description?: string\n measures: CubeMetaMeasure[]\n dimensions: CubeMetaDimension[]\n segments: CubeMetaField[]\n relationships?: CubeMetaRelationship[]\n /** Hierarchies for structured drill-down paths */\n hierarchies?: CubeMetaHierarchy[]\n /** Additional cube metadata (e.g., eventStream configuration for funnel queries) */\n meta?: {\n eventStream?: {\n bindingKey: string\n timeDimension: string\n }\n [key: string]: any\n }\n}\n\nexport interface CubeMeta {\n cubes: CubeMetaCube[]\n}\n\nexport type FieldLabelMap = Record<string, string>\n\n// Re-export color palette types\nexport type { ColorPalette } from './utils/colorPalettes.js'\n\n// Built-in chart types shipped with drizzle-cube\nexport type BuiltInChartType =\n | 'line'\n | 'bar'\n | 'pie'\n | 'table'\n | 'area'\n | 'scatter'\n | 'radar'\n | 'radialBar'\n | 'treemap'\n | 'bubble'\n | 'activityGrid'\n | 'kpiNumber'\n | 'kpiDelta'\n | 'kpiText'\n | 'markdown'\n | 'funnel'\n | 'sankey'\n | 'sunburst'\n | 'heatmap'\n | 'retentionHeatmap'\n | 'retentionCombined'\n | 'boxPlot'\n | 'waterfall'\n | 'candlestick'\n | 'measureProfile'\n | 'gauge'\n\n// Chart type identifier — includes all built-in types plus any string for custom chart plugins.\n// Use BuiltInChartType when you need to narrow to built-ins only.\nexport type ChartType = BuiltInChartType | (string & {})\n\n// Axis formatting configuration\nexport interface AxisFormatConfig {\n label?: string // Custom axis label (overrides auto-generated)\n unit?: 'currency' | 'percent' | 'number' | 'custom' // Unit type for formatting\n abbreviate?: boolean // Use K, M, B suffixes for large numbers\n decimals?: number // Decimal places (0-4, undefined = auto)\n customPrefix?: string // Prefix for 'custom' unit type\n customSuffix?: string // Suffix for 'custom' unit type\n}\n\n// Chart configuration\nexport interface ChartAxisConfig {\n // New format (for advanced portlet editor)\n xAxis?: string[] // Dimension fields for X axis\n yAxis?: string[] // Measure fields for Y axis \n series?: string[] // Fields to use for series/grouping\n \n // Bubble chart specific fields\n sizeField?: string // Field for bubble size\n colorField?: string // Field for bubble color\n \n // Activity grid chart specific fields\n dateField?: string[] // Time dimension field for activity grid\n valueField?: string[] // Measure field for activity intensity\n \n // Legacy format (for backward compatibility)\n x?: string // Single dimension field for X axis\n y?: string[] // Measure fields for Y axis\n\n // Dual Y-axis support: per-measure axis assignment (left or right)\n // Default: 'left' for all measures (backward compatible)\n yAxisAssignment?: Record<string, 'left' | 'right'>\n}\n\nexport interface ThresholdBand {\n value: number\n color: string\n}\n\nexport interface ChartDisplayConfig {\n showLegend?: boolean\n showGrid?: boolean\n showTooltip?: boolean\n colors?: string[]\n orientation?: 'horizontal' | 'vertical'\n stacked?: boolean // Deprecated: use stackType instead\n stackType?: 'none' | 'normal' | 'percent' // Stacking mode: none, normal (sum), or percent (100%)\n connectNulls?: boolean // For Area/Line charts: draw continuous lines through missing data\n showAllXLabels?: boolean // Force all X-axis category labels to display (interval=0)\n hideHeader?: boolean // Hide portlet header in non-edit mode\n \n // Pie chart specific display options\n innerRadius?: string // Inner radius for donut variant (e.g., '0%', '20%', '40%', '60%', '80%')\n\n // Bubble chart specific display options\n minBubbleSize?: number\n maxBubbleSize?: number\n bubbleOpacity?: number\n \n // Activity grid specific display options\n showLabels?: boolean\n fitToWidth?: boolean\n\n // DataTable specific display options\n pivotTimeDimension?: boolean // Pivot time dimension as columns (default: true when time dimension present)\n\n // Target functionality\n target?: string // Target values as string (single value or comma-separated for spread)\n \n // KPI specific display options\n template?: string // JavaScript template string for KPI Text\n prefix?: string // Text prefix for KPI Number\n suffix?: string // Text suffix for KPI Number\n decimals?: number // Number of decimal places\n formatValue?: (value: number | null | undefined) => string // Custom value formatter function (takes precedence over prefix/suffix/decimals)\n valueColor?: string // Color for the KPI value (legacy)\n valueColorIndex?: number // Index of color from dashboard palette for KPI value\n \n // KPI Delta specific display options\n positiveColorIndex?: number // Index of color from dashboard palette for positive changes\n negativeColorIndex?: number // Index of color from dashboard palette for negative changes\n showHistogram?: boolean // Whether to show variance histogram\n\n // KPI time period handling\n useLastCompletePeriod?: boolean // Exclude incomplete current period (e.g., partial week/month)\n skipLastPeriod?: boolean // Always exclude the last period regardless of completeness\n \n // Markdown specific display options\n content?: string // Markdown content text\n accentColorIndex?: number // Index of color from dashboard palette for headers, bullets, links\n fontSize?: 'small' | 'medium' | 'large' // Text size for markdown content\n alignment?: 'left' | 'center' | 'right' // Text alignment for markdown content\n transparentBackground?: boolean // Remove card background, border, shadow, padding (for section headers)\n autoHeight?: boolean // Card shrinks to fit content instead of filling grid cell (default: true for markdown)\n accentBorder?: 'none' | 'left' | 'top' | 'bottom' // Accent-colored border on specified side\n\n // Axis formatting options (for Line, Area, Bar, Scatter charts)\n xAxisFormat?: AxisFormatConfig // Formatting for X-axis values\n leftYAxisFormat?: AxisFormatConfig // Formatting for left Y-axis values\n rightYAxisFormat?: AxisFormatConfig // Formatting for right Y-axis values (dual-axis charts)\n\n // Period comparison display options (for compareDateRange queries)\n /**\n * How to display compared periods:\n * - 'separate': Each period as distinct series with different colors (default)\n * - 'overlay': Periods aligned by day-of-period index with ghost styling for prior periods\n */\n comparisonMode?: 'separate' | 'overlay'\n /** Line style for prior periods in overlay mode */\n priorPeriodStyle?: 'solid' | 'dashed' | 'dotted'\n /** Opacity for prior period lines (0-1), default: 0.5 */\n priorPeriodOpacity?: number\n /** Include period labels in legend */\n showPeriodLabels?: boolean\n\n // Funnel chart specific display options\n /** Custom labels for funnel steps (array indexed by step, e.g., [\"Signup\", \"Activation\", \"Purchase\"]) */\n funnelStepLabels?: string[]\n /** Hide the summary footer in funnel charts */\n hideSummaryFooter?: boolean\n /** Funnel orientation: horizontal (bars left to right) or vertical (bars bottom to top) */\n funnelOrientation?: 'horizontal' | 'vertical'\n /** @deprecated Use showFunnelAvgTime, showFunnelMedianTime, showFunnelP90Time instead */\n showFunnelTimeMetrics?: boolean\n /** Funnel visualization style: 'bars' (horizontal bars) or 'funnel' (trapezoid funnel shape) */\n funnelStyle?: 'bars' | 'funnel'\n /** Show step-to-step conversion rate (default: true) */\n showFunnelConversion?: boolean\n /** Show average time-to-convert metric in funnel charts */\n showFunnelAvgTime?: boolean\n /** Show median time-to-convert metric in funnel charts */\n showFunnelMedianTime?: boolean\n /** Show P90 time-to-convert metric in funnel charts */\n showFunnelP90Time?: boolean\n\n // Retention chart specific display options\n /** Retention display mode: line chart, heatmap table, or combined view */\n retentionDisplayMode?: 'line' | 'heatmap' | 'combined'\n\n // Waterfall chart specific display options\n showTotal?: boolean\n showConnectorLine?: boolean\n showDataLabels?: boolean\n\n // Candlestick chart specific display options\n bullColor?: string\n bearColor?: string\n showWicks?: boolean\n rangeMode?: 'ohlc' | 'range'\n\n // Measure profile chart specific display options\n showReferenceLineAtZero?: boolean\n lineType?: 'monotone' | 'linear' | 'step'\n\n // Gauge chart specific display options\n minValue?: number\n maxValue?: number\n thresholds?: string | ThresholdBand[]\n showCenterLabel?: boolean\n showPercentage?: boolean\n}\n\n// Portlet configuration\nexport interface PortletConfig {\n id: string\n title: string\n\n /**\n * Canonical format for analysis configuration.\n * This is the single source of truth for all query/chart config.\n * New portlets only save this field. Legacy portlets are migrated on-the-fly.\n */\n analysisConfig?: import('./types/analysisConfig.js').AnalysisConfig\n\n // === Legacy fields (deprecated - for backward compatibility only) ===\n // These fields are optional and only used for reading old configurations.\n // New portlets do not write these fields.\n /** @deprecated Use analysisConfig.query instead */\n query?: string // JSON string of cube query (CubeQuery, MultiQueryConfig, or ServerFunnelQuery)\n /** @deprecated Use analysisConfig.charts[mode].chartType instead */\n chartType?: ChartType\n /** @deprecated Use analysisConfig.charts[mode].chartConfig instead */\n chartConfig?: ChartAxisConfig\n /** @deprecated Use analysisConfig.charts[mode].displayConfig instead */\n displayConfig?: ChartDisplayConfig\n dashboardFilterMapping?: DashboardFilterMapping // Dashboard filters that apply to this portlet (IDs or entries with member overrides)\n eagerLoad?: boolean // Force immediate loading (overrides dashboard lazy loading setting)\n /** @deprecated Use analysisConfig.analysisType instead */\n analysisType?: AnalysisType // Optional - defaults to 'query' when undefined (backward compatible)\n\n // Funnel mode state (deprecated - now stored in analysisConfig)\n /** @deprecated Use analysisConfig for funnel mode */\n funnelCube?: string | null\n /** @deprecated Use analysisConfig for funnel mode */\n funnelSteps?: FunnelStepState[]\n /** @deprecated Use analysisConfig for funnel mode */\n funnelTimeDimension?: string | null\n /** @deprecated Use analysisConfig for funnel mode */\n funnelBindingKey?: FunnelBindingKey | null\n /** @deprecated Use analysisConfig for funnel mode */\n funnelChartType?: ChartType\n /** @deprecated Use analysisConfig for funnel mode */\n funnelChartConfig?: ChartAxisConfig\n /** @deprecated Use analysisConfig for funnel mode */\n funnelDisplayConfig?: ChartDisplayConfig\n\n w: number // Grid width\n h: number // Grid height\n x: number // Grid x position\n y: number // Grid y position\n}\n\nexport type DashboardLayoutMode = 'grid' | 'rows'\n\nexport interface DashboardGridSettings {\n cols: number\n rowHeight: number\n minW: number\n minH: number\n}\n\nexport interface RowLayoutColumn {\n portletId: string\n w: number\n}\n\nexport interface RowLayout {\n id: string\n h: number\n columns: RowLayoutColumn[]\n}\n\n// Dashboard configuration\nexport interface DashboardConfig {\n portlets: PortletConfig[]\n layoutMode?: DashboardLayoutMode\n grid?: DashboardGridSettings\n rows?: RowLayout[]\n layouts?: { [key: string]: any } // react-grid-layout layouts\n colorPalette?: string // Name of the color palette to use (defaults to 'default')\n filters?: DashboardFilter[] // Dashboard-level filters that can be applied to portlets\n eagerLoad?: boolean // Force immediate loading for all portlets (default: false, lazy load enabled)\n // Thumbnail fields for dashboard preview/sharing\n thumbnailData?: string // Transient: base64 data URI for upload (cleared by server after processing)\n thumbnailUrl?: string // Permanent: CDN URL after server-side processing\n}\n\n// Analysis type for explicit mode selection in AnalysisBuilder\n// 'query' supports both single and multi-query (add more queries via + button)\n// 'funnel' for funnel analysis with sequential steps\n// 'flow' for bidirectional flow analysis with Sankey visualization\n// 'retention' for cohort-based retention analysis\nexport type AnalysisType = 'query' | 'funnel' | 'flow' | 'retention'\n\n/**\n * State for a single funnel step (dedicated for Funnel mode)\n * Each step represents a stage in the funnel with its own cube and filters\n */\nexport interface FunnelStepState {\n /** Unique step identifier */\n id: string\n /** Display name for the step (e.g., \"Signup\", \"Purchase\") */\n name: string\n /** Which cube this step uses (for multi-cube funnels) */\n cube: string\n /** Filters that define which events qualify for this step */\n filters: Filter[]\n /** Time window from previous step (ISO 8601 duration, e.g., \"P7D\" for 7 days) */\n timeToConvert?: string\n}\n\n// Filter types - hierarchical structure supporting AND/OR logic\nexport type FilterOperator = \n // String operators\n | 'equals' | 'notEquals' | 'contains' | 'notContains' \n | 'startsWith' | 'notStartsWith' | 'endsWith' | 'notEndsWith'\n | 'like' | 'notLike' | 'ilike'\n // Numeric operators \n | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'notBetween'\n // Array operators\n | 'in' | 'notIn'\n // PostgreSQL array operators\n | 'arrayContains' | 'arrayOverlaps' | 'arrayContained'\n // Null/Empty operators\n | 'set' | 'notSet' | 'isEmpty' | 'isNotEmpty'\n // Date operators\n | 'inDateRange' | 'beforeDate' | 'afterDate'\n // Regex operators\n | 'regex' | 'notRegex'\n\nexport interface SimpleFilter {\n member: string\n operator: FilterOperator\n values: any[]\n dateRange?: string | string[]\n}\n\nexport interface GroupFilter {\n type: 'and' | 'or'\n filters: Filter[]\n}\n\nexport type Filter = SimpleFilter | GroupFilter\n\n// Dashboard filter with ID and label for dashboard-level filtering\nexport interface DashboardFilter {\n id: string // Unique identifier for the filter\n label: string // Display label for the filter\n filter: Filter // The actual filter definition\n isUniversalTime?: boolean // When true, applies to all timeDimensions in portlets (ignores member field)\n}\n\n// Mapping entry connecting a dashboard filter to a portlet, optionally\n// rewriting the filter's member to a different field for that portlet\nexport interface DashboardFilterMappingEntry {\n filterId: string // The dashboard filter this entry refers to\n member?: string // When set, the filter's member is rewritten to this field for this portlet (SimpleFilter only)\n}\n\n// A portlet's dashboard filter mapping. Plain strings (filter IDs) remain\n// supported for backward compatibility with saved dashboards; object entries\n// are only needed when a per-portlet member override is used.\nexport type DashboardFilterMapping = Array<string | DashboardFilterMappingEntry>\n\n// Cube query types\nexport interface CubeQuery {\n measures?: string[]\n dimensions?: string[]\n timeDimensions?: Array<{\n dimension: string\n granularity?: string\n dateRange?: string[] | string\n fillMissingDates?: boolean\n /**\n * Array of date ranges for period-over-period comparison.\n * When specified, queries are executed for each period and results are merged.\n */\n compareDateRange?: (string | [string, string])[]\n }>\n filters?: Filter[]\n order?: { [key: string]: 'asc' | 'desc' }\n limit?: number\n offset?: number\n segments?: string[]\n /** When true, returns raw row-level data without GROUP BY or aggregation */\n ungrouped?: boolean\n}\n\n/**\n * Merge strategy for combining multiple query results\n * - 'concat': Append rows with __queryIndex marker (for separate series per query)\n * - 'merge': Align data by common dimension key (for combined visualization)\n *\n * Note: For funnel analysis, use the dedicated funnel mode (analysisType === 'funnel')\n */\nexport type QueryMergeStrategy = 'concat' | 'merge'\n\n/**\n * Configuration for multi-query portlets\n * Detected by presence of 'queries' array property\n *\n * Note: For funnel analysis, use the dedicated funnel mode (analysisType === 'funnel')\n */\nexport interface MultiQueryConfig {\n queries: CubeQuery[]\n mergeStrategy: QueryMergeStrategy\n mergeKeys?: string[] // Dimensions to align on (for 'merge' strategy) - composite key\n queryLabels?: string[] // User-defined labels per query\n}\n\n/**\n * Type guard to detect multi-query configuration\n */\nexport function isMultiQueryConfig(obj: unknown): obj is MultiQueryConfig {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'queries' in obj &&\n Array.isArray((obj as MultiQueryConfig).queries) &&\n (obj as MultiQueryConfig).queries.length > 0\n )\n}\n\nexport interface CubeQueryOptions {\n skip?: boolean\n resetResultSetOnChange?: boolean\n subscribe?: boolean\n}\n\nexport interface CubeApiOptions {\n apiUrl?: string\n token?: string\n headers?: Record<string, string>\n credentials?: 'include' | 'omit' | 'same-origin'\n}\n\n// Result set types\nexport interface CubeResultSet {\n rawData(): any[]\n tablePivot(): any[]\n series(): any[]\n annotation(): any\n loadResponse?: any\n cacheInfo?(): { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | undefined\n}\n\n// Component props\nexport interface AnalyticsPortletProps {\n query: string\n chartType: ChartType\n chartConfig?: ChartAxisConfig\n displayConfig?: ChartDisplayConfig\n dashboardFilters?: DashboardFilter[] // Dashboard-level filters to merge with portlet query\n dashboardFilterMapping?: DashboardFilterMapping // Dashboard filters that apply to this portlet (IDs or entries with member overrides)\n eagerLoad?: boolean // Force immediate loading (default: false, lazy load enabled)\n isVisible?: boolean // Whether the portlet is visible in the viewport (for lazy loading)\n height?: string | number\n title?: string\n colorPalette?: ColorPalette // Complete palette with both colors and gradient\n loadingComponent?: ReactNode // Custom loading indicator (defaults to LoadingIndicator)\n onDebugDataReady?: (debugData: {\n chartConfig: ChartAxisConfig\n displayConfig: ChartDisplayConfig\n queryObject: any\n data: any[] | FlowChartData | RetentionChartData\n chartType: ChartType\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n drillState?: {\n isDrilling: boolean\n drillPath: Array<{\n id: string\n label: string\n clickedValue?: unknown\n dimension?: string\n granularity?: string\n hierarchy?: string\n }>\n currentDrillDepth: number\n originalQuery: any\n activeQuery: any\n }\n }) => void\n}\n\nexport interface AnalyticsDashboardProps {\n config: DashboardConfig\n editable?: boolean\n dashboardFilters?: DashboardFilter[] // Programmatic dashboard filters (merged with config.filters)\n loadingComponent?: ReactNode // Custom loading indicator for all portlets (defaults to LoadingIndicator)\n onConfigChange?: (config: DashboardConfig) => void\n onSave?: (config: DashboardConfig) => Promise<void> | void\n onSaveThumbnail?: (thumbnailData: string) => Promise<string | void> // Callback to save thumbnail separately (called on edit mode exit)\n onDirtyStateChange?: (isDirty: boolean) => void\n}\n\nexport interface ChartProps {\n data: any[]\n chartConfig?: ChartAxisConfig\n displayConfig?: ChartDisplayConfig\n queryObject?: CubeQuery\n height?: string | number\n colorPalette?: ColorPalette // Complete palette with both colors and gradient\n\n // Drill-down support\n /** Handler for data point clicks - fires ChartDataPointClickEvent */\n onDataPointClick?: (event: import('./types/drill.js').ChartDataPointClickEvent) => void\n /** Whether drill-down is enabled (shows pointer cursor on clickable elements) */\n drillEnabled?: boolean\n}\n\n// Thumbnail feature configuration\nexport interface ThumbnailFeatureConfig {\n enabled: boolean\n width?: number // default: 1600 (higher resolution for crisp thumbnails)\n height?: number // default: 1200 (4:3 aspect ratio)\n format?: 'png' | 'jpeg' // default: 'png'\n quality?: number // 0-1, mainly for jpeg (PNG ignores this)\n}\n\n// XLS export feature configuration (requires exceljs peer dependency)\nexport interface XlsExportFeatureConfig {\n enabled: boolean\n /** Optional prefix for exported filenames (default: portlet title) */\n filenamePrefix?: string\n}\n\n// Features configuration\nexport interface FeaturesConfig {\n enableAI?: boolean // Default: true for backward compatibility\n aiEndpoint?: string // Custom AI endpoint (default: '/api/ai/generate')\n showSchemaDiagram?: boolean // Show schema visualization button in AnalysisBuilder results panel (requires @xyflow/react and @dagrejs/dagre)\n useAnalysisBuilder?: boolean // Deprecated - AnalysisBuilder modal is now always used (PortletEditModal was removed)\n editToolbar?: 'floating' | 'top' | 'both' // Which edit toolbar(s) to show: 'floating' only, 'top' only, or 'both' (default: 'both')\n floatingToolbarPosition?: 'left' | 'right' // Position of floating toolbar when enabled (default: 'right')\n thumbnail?: ThumbnailFeatureConfig // Optional dashboard thumbnail capture on save\n manualRefresh?: boolean // When true, queries don't auto-execute on config changes. User must click Refresh. (default: false)\n xlsExport?: XlsExportFeatureConfig // Optional XLSX data export from portlets (requires exceljs)\n}\n\n// Grid layout types (simplified)\nexport interface GridLayout {\n i: string\n x: number\n y: number\n w: number\n h: number\n minW?: number\n minH?: number\n}\n\nexport interface ResponsiveLayout {\n [breakpoint: string]: GridLayout[]\n}\n\n// Dashboard display modes for responsive layout\nexport type DashboardDisplayMode = 'desktop' | 'scaled' | 'mobile'\n\n// Re-export funnel types\nexport type {\n FunnelBindingKey,\n FunnelBindingKeyMapping,\n FunnelStep,\n FunnelConfig,\n FunnelStepResult,\n FunnelExecutionResult,\n FunnelChartData,\n FunnelValidationError,\n FunnelValidationResult,\n UseFunnelQueryOptions,\n UseFunnelQueryResult,\n ServerFunnelQuery,\n} from './types/funnel.js'\n\n/**\n * Type guard to detect server funnel query format\n * Used to distinguish { funnel: {...} } from CubeQuery or MultiQueryConfig\n */\nexport function isServerFunnelQuery(obj: unknown): obj is import('./types/funnel.js').ServerFunnelQuery {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'funnel' in obj &&\n typeof (obj as { funnel: unknown }).funnel === 'object'\n )\n}\n\n// Re-export flow types\nexport type {\n FlowStartingStep,\n ServerFlowQuery,\n FlowQueryConfig,\n SankeyNode,\n SankeyLink,\n FlowResultRow,\n FlowChartData,\n FlowSliceState,\n FlowSliceActions,\n} from './types/flow.js'\n\nexport { isServerFlowQuery, isSankeyData } from './types/flow.js'\n\n// ============================================================================\n// EXPLAIN PLAN TYPES\n// These types mirror the server-side types in src/server/types/executor.ts\n// ============================================================================\n\n/**\n * Options for EXPLAIN query execution\n */\nexport interface ExplainOptions {\n /** Use EXPLAIN ANALYZE to actually execute the query and get real timing (PostgreSQL, MySQL 8.0.18+) */\n analyze?: boolean\n}\n\n/**\n * A single operation/node in the query execution plan\n * Normalized structure across all databases\n */\nexport interface ExplainOperation {\n /** Operation type (e.g., 'Seq Scan', 'Index Scan', 'Hash Join', 'Sort') */\n type: string\n /** Table name if applicable */\n table?: string\n /** Index name if used */\n index?: string\n /** Estimated row count from planner */\n estimatedRows?: number\n /** Actual row count (if ANALYZE was used) */\n actualRows?: number\n /** Estimated cost (database-specific units) */\n estimatedCost?: number\n /** Filter condition if any */\n filter?: string\n /** Additional details specific to this operation */\n details?: string\n /** Nested/child operations */\n children?: ExplainOperation[]\n}\n\n/**\n * Summary statistics from the execution plan\n */\nexport interface ExplainSummary {\n /** Database engine type */\n database: 'postgres' | 'mysql' | 'sqlite'\n /** Planning time in milliseconds (if available) */\n planningTime?: number\n /** Execution time in milliseconds (if ANALYZE was used) */\n executionTime?: number\n /** Total estimated cost */\n totalCost?: number\n /** Quick flag: true if any sequential scans detected */\n hasSequentialScans: boolean\n /** List of indexes used in the plan */\n usedIndexes: string[]\n}\n\n/**\n * Result of an EXPLAIN query\n * Provides both normalized structure and raw output\n */\nexport interface ExplainResult {\n /** Normalized hierarchical plan as operations */\n operations: ExplainOperation[]\n /** Summary statistics */\n summary: ExplainSummary\n /** Raw EXPLAIN output as text (for display) */\n raw: string\n /** Original SQL query */\n sql: {\n sql: string\n params?: unknown[]\n }\n}\n\n// ============================================================================\n// AI EXPLAIN ANALYSIS TYPES\n// These types mirror the server-side types in src/server/types/executor.ts\n// ============================================================================\n\n/**\n * A recommendation from AI analysis of an execution plan\n */\nexport interface ExplainRecommendation {\n /** Type of recommendation */\n type: 'index' | 'table' | 'cube' | 'general'\n /** Severity/priority of the recommendation */\n severity: 'critical' | 'warning' | 'suggestion'\n /** Short actionable title */\n title: string\n /** Detailed explanation of why this helps */\n description: string\n /** Actionable SQL statement (e.g., CREATE INDEX) - for index/table recommendations */\n sql?: string\n /** TypeScript code snippet to add to cube definition - for cube recommendations */\n cubeCode?: string\n /** Which cube to modify - for cube recommendations */\n cubeName?: string\n /** Affected database table */\n table?: string\n /** Affected columns */\n columns?: string[]\n /** Expected performance improvement */\n estimatedImpact?: string\n}\n\n/**\n * Issue identified in the execution plan\n */\nexport interface ExplainIssue {\n /** Type of issue */\n type: 'sequential_scan' | 'missing_index' | 'high_cost' | 'sort_operation' | string\n /** Description of the issue */\n description: string\n /** Severity level */\n severity: 'high' | 'medium' | 'low'\n}\n\n/**\n * AI-generated analysis of an execution plan\n */\nexport interface AIExplainAnalysis {\n /** One-sentence description of what the query does */\n summary: string\n /** Overall performance assessment */\n assessment: 'good' | 'warning' | 'critical'\n /** Reason for the assessment */\n assessmentReason: string\n /** Detailed explanation of the query's purpose and structure */\n queryUnderstanding: string\n /** Issues identified in the execution plan */\n issues: ExplainIssue[]\n /** Actionable recommendations for improvement */\n recommendations: ExplainRecommendation[]\n /** Metadata from the AI analysis */\n _meta?: {\n model: string\n usingUserKey: boolean\n }\n}\n\n// Re-export drill types\nexport type {\n ChartDataPointClickEvent,\n DrillOptionType,\n DrillScope,\n DrillOption,\n DrillPathEntry,\n DrillResult,\n UseDrillInteractionOptions,\n DrillInteraction,\n DrillMenuProps,\n DrillBreadcrumbProps,\n} from './types/drill.js'\n","/**\n * AnalysisConfig - Versioned, mode-agnostic configuration format\n *\n * This is the canonical format for persisting analysis state to:\n * - localStorage (standalone AnalysisBuilder)\n * - Share URLs\n * - Dashboard portlets (via analysisConfig field)\n *\n * Key design principles:\n * - `query` field IS the executable query (no transformation needed)\n * - Per-mode chart config via `charts[analysisType]` map\n * - No UI state (activeQueryIndex, activeFunnelStepIndex) - those are transient\n */\n\nimport type {\n ChartType,\n ChartAxisConfig,\n ChartDisplayConfig,\n CubeQuery,\n MultiQueryConfig,\n} from '../types.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 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'\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 * Convert GroupFilter format to server format\n * GroupFilter: { type: 'and', filters: [...] }\n * Server format: { and: [...] } or { or: [...] }\n */\nfunction convertToServerFormat(filter: Filter): any {\n // Handle GroupFilter format\n if ('type' in filter && 'filters' in filter) {\n const groupFilter = filter as GroupFilter\n const convertedFilters = groupFilter.filters.map(convertToServerFormat)\n\n if (groupFilter.type === 'and') {\n return { and: convertedFilters }\n } else {\n return { or: convertedFilters }\n }\n }\n\n // Simple filter - return as-is\n return filter\n}\n\n/**\n * Filter format for merge operation:\n * - 'server': Returns {and: [...]} or {or: [...]} format (for API queries)\n * - 'client': Returns {type: 'and', filters: [...]} format (for UI components)\n */\nexport type FilterFormat = 'server' | 'client'\n\n/**\n * Merge dashboard filters with portlet filters using AND logic\n * Dashboard filters are combined with portlet filters so both sets of filters apply\n *\n * @param dashboardFilters - Filters from dashboard-level configuration\n * @param portletFilters - Filters from portlet query\n * @param format - Output format: 'server' for API queries, 'client' for UI (default: 'server')\n * @returns Merged filter array with AND logic in the specified format\n */\nexport function mergeDashboardAndPortletFilters(\n dashboardFilters: Filter[],\n portletFilters: Filter[] | undefined,\n format: FilterFormat = 'server'\n): Filter[] | undefined {\n // If no dashboard filters, return portlet filters as-is\n if (!dashboardFilters || dashboardFilters.length === 0) {\n return portletFilters\n }\n\n // If no portlet filters, return dashboard filters\n if (!portletFilters || portletFilters.length === 0) {\n return [...dashboardFilters]\n }\n\n // Both exist - need to merge with AND logic\n if (format === 'server') {\n // Server format: convert to {and: [...]} structure\n const allFilters = [...dashboardFilters, ...portletFilters].map(convertToServerFormat)\n return [{\n and: allFilters\n } as any]\n } else {\n // Client format: use {type: 'and', filters: [...]} structure\n const allFilters = [...dashboardFilters, ...portletFilters]\n return [{\n type: 'and',\n filters: allFilters\n } as GroupFilter]\n }\n}\n\n/**\n * @deprecated Use mergeDashboardAndPortletFilters(filters, portletFilters, 'client') instead\n */\nexport function mergeDashboardAndPortletFiltersClientFormat(\n dashboardFilters: Filter[],\n portletFilters: Filter[] | undefined\n): Filter[] | undefined {\n return mergeDashboardAndPortletFilters(dashboardFilters, portletFilters, 'client')\n}\n\n/**\n * Check if a filter field exists in the cube metadata\n * This helps identify filters that might not apply to a specific portlet's data\n * @param filter - The filter to validate\n * @param cubeMeta - Cube metadata to validate against\n * @returns true if the filter field exists in any cube's measures or dimensions\n */\nexport function validateFilterForCube(\n filter: Filter,\n cubeMeta: CubeMeta | null\n): boolean {\n if (!cubeMeta || !cubeMeta.cubes) {\n // If no metadata available, assume filter is valid (fail open)\n return true\n }\n\n // Extract member names from filter recursively\n const memberNames = extractMemberNamesFromFilter(filter)\n\n // Check if any of the member names exist in cube metadata\n return memberNames.some(memberName => {\n return cubeMeta.cubes.some(cube => {\n // Check measures\n const inMeasures = cube.measures?.some(m => m.name === memberName) ?? false\n // Check dimensions\n const inDimensions = cube.dimensions?.some(d => d.name === memberName) ?? false\n\n return inMeasures || inDimensions\n })\n })\n}\n\n/**\n * Extract all member names from a filter (handles nested group filters)\n * @param filter - The filter to extract members from\n * @returns Array of member names\n */\nfunction extractMemberNamesFromFilter(filter: Filter): string[] {\n if ('member' in filter) {\n // SimpleFilter\n return [filter.member]\n } else if ('type' in filter && 'filters' in filter) {\n // GroupFilter - recursively extract from nested filters\n return filter.filters.flatMap(f => extractMemberNamesFromFilter(f))\n }\n\n return []\n}\n\n/**\n * Validate that all dashboard filters in a portlet's mapping exist and are valid\n * @param dashboardFilters - All available dashboard filters\n * @param filterMapping - The portlet's filter mapping\n * @param cubeMeta - Cube metadata for validation\n * @returns Object with validation result and list of invalid filter IDs\n */\nexport function validatePortletFilterMapping(\n dashboardFilters: DashboardFilter[] | undefined,\n filterMapping: 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 extractFieldsFromFilters(cubeQuery.filters).forEach(field => {\n dimensions.add(field)\n })\n }\n }\n\n // Handle different query types\n if ('funnel' in query) {\n // ServerFunnelQuery - extract time dimension from funnel config\n const funnelQuery = query as any\n if (funnelQuery.funnel?.timeDimension) {\n timeDimensions.add(funnelQuery.funnel.timeDimension)\n }\n // Could also extract binding key dimensions if needed\n } else if ('queries' in query) {\n // MultiQueryConfig - extract from all sub-queries\n const multiQuery = query as any\n multiQuery.queries.forEach((subQuery: any) => extractFromCubeQuery(subQuery))\n } else {\n // Single CubeQuery\n extractFromCubeQuery(query)\n }\n } catch (e) {\n // Skip portlets with invalid query\n console.warn('Failed to extract fields from portlet:', portlet.id, e)\n }\n })\n\n return { measures, dimensions, timeDimensions }\n}\n\n/**\n * Extract field names from filters recursively\n * @param filters - Filter array\n * @returns Array of unique field names\n */\nfunction extractFieldsFromFilters(filters: Filter[]): string[] {\n const fields: string[] = []\n\n filters.forEach(filter => {\n if ('member' in filter) {\n // SimpleFilter\n fields.push(filter.member)\n } else if ('type' in filter && 'filters' in filter) {\n // GroupFilter - recurse\n fields.push(...extractFieldsFromFilters(filter.filters))\n }\n })\n\n return [...new Set(fields)] // Return unique fields\n}\n\n/**\n * Time dimension type from CubeQuery\n */\ntype TimeDimension = {\n dimension: string\n granularity?: string\n dateRange?: string[] | string\n}\n\n/**\n * Helper to get date range from a SimpleFilter (backward compatible)\n * Reads from both dateRange and values for compatibility\n * Handles both:\n * - Preset ranges: [\"this quarter\"], [\"last 7 days\"] (single string value)\n * - Custom ranges: [\"2024-01-01\", \"2024-12-31\"] (two date values)\n */\nfunction getDateRangeFromFilter(filter: SimpleFilter): string[] | string | undefined {\n // Prefer dateRange for backward compatibility, fall back to values\n if (filter.dateRange) {\n return filter.dateRange\n }\n if (filter.values && filter.values.length > 0) {\n // Single value = preset like \"this quarter\", return as string\n // Multiple values = custom date range, return as array\n return filter.values.length === 1 ? filter.values[0] : filter.values\n }\n return undefined\n}\n\n/**\n * Apply universal time filters to a portlet's timeDimensions\n * Universal time filters apply their dateRange to ALL time dimensions in the portlet\n *\n * @param dashboardFilters - All dashboard filters\n * @param filterMapping - Filter IDs that apply to this portlet\n * @param portletTimeDimensions - The portlet's existing timeDimensions array\n * @returns Updated timeDimensions array with date ranges applied\n */\nexport function applyUniversalTimeFilters(\n dashboardFilters: DashboardFilter[] | undefined,\n filterMapping: 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 * Query parsing + dashboard-filter merging for AnalyticsPortlet.\n *\n * Parses the portlet's JSON query string and detects which of the supported\n * formats it is (single CubeQuery, MultiQueryConfig, ServerFunnelQuery,\n * ServerFlowQuery, ServerRetentionQuery), merging applicable dashboard filters\n * along the way. Extracted from AnalyticsPortlet to keep the component flat.\n */\n\nimport type {\n CubeQuery,\n DashboardFilter,\n DashboardFilterMapping,\n Filter,\n MultiQueryConfig,\n ServerFunnelQuery\n} from '../../types.js'\nimport { isMultiQueryConfig, isServerFunnelQuery } from '../../types.js'\nimport type { ServerFlowQuery } from '../../types/flow.js'\nimport { isServerFlowQuery } from '../../types/flow.js'\nimport type { ServerRetentionQuery } from '../../types/retention.js'\nimport { isServerRetentionQuery } from '../../types/retention.js'\nimport {\n getApplicableDashboardFilters,\n mergeDashboardAndPortletFilters,\n applyUniversalTimeFilters,\n mappingIncludesFilter\n} from '../../utils/filterUtils.js'\n\nexport interface ParsedPortletQuery {\n queryObject: CubeQuery | null\n multiQueryConfig: MultiQueryConfig | null\n serverFunnelQuery: ServerFunnelQuery | null\n serverFlowQuery: ServerFlowQuery | null\n serverRetentionQuery: ServerRetentionQuery | null\n}\n\nconst EMPTY_RESULT: ParsedPortletQuery = {\n queryObject: null,\n multiQueryConfig: null,\n serverFunnelQuery: null,\n serverFlowQuery: null,\n serverRetentionQuery: null\n}\n\nexport interface ParsePortletQueryParams {\n query: string\n shouldSkipQuery: boolean\n regularFilters?: DashboardFilter[]\n dashboardFilters?: DashboardFilter[]\n dashboardFilterMapping?: DashboardFilterMapping\n}\n\n/**\n * Resolve the time-dimension member string from a funnel's timeDimension config.\n */\nfunction resolveFunnelTimeDimensionMember(\n timeDimension: ServerFunnelQuery['funnel']['timeDimension']\n): string | undefined {\n if (typeof timeDimension === 'string') {\n return timeDimension\n }\n if (Array.isArray(timeDimension) && timeDimension.length > 0) {\n const td = timeDimension[0]\n return `${td.cube}.${td.dimension}`\n }\n return undefined\n}\n\n/**\n * Apply universal time filters to step 0 of a funnel query as an inDateRange filter.\n */\nfunction applyUniversalTimeToFunnel(\n modifiedFunnel: ServerFunnelQuery,\n dashboardFilters: DashboardFilter[] | undefined,\n dashboardFilterMapping: DashboardFilterMapping | undefined\n): void {\n const universalTimeFilters = dashboardFilters?.filter(df =>\n df.isUniversalTime && mappingIncludesFilter(dashboardFilterMapping, df.id)\n )\n if (!universalTimeFilters || universalTimeFilters.length === 0 || modifiedFunnel.funnel.steps.length === 0) {\n return\n }\n\n const timeFilter = universalTimeFilters[0]\n if (!('member' in timeFilter.filter)) return\n\n const simpleFilter = timeFilter.filter as { member: string; operator: string; values?: string[]; dateRange?: string }\n const dateRangeValue = simpleFilter.dateRange || simpleFilter.values?.[0]\n if (!dateRangeValue) return\n\n const timeDimMember = resolveFunnelTimeDimensionMember(modifiedFunnel.funnel.timeDimension)\n if (!timeDimMember) return\n\n const step0 = { ...modifiedFunnel.funnel.steps[0] }\n const timeRangeFilter = {\n member: timeDimMember,\n operator: 'inDateRange',\n values: [] as string[],\n dateRange: dateRangeValue\n }\n const existingFilters = step0.filter ? (Array.isArray(step0.filter) ? step0.filter : [step0.filter]) : []\n step0.filter = [...existingFilters, timeRangeFilter]\n modifiedFunnel.funnel.steps[0] = step0\n}\n\n/**\n * Apply dashboard filters to a ServerFunnelQuery, returning a new (cloned) funnel.\n */\nfunction applyFiltersToFunnel(\n funnelQuery: ServerFunnelQuery,\n applicableFilters: Filter[],\n dashboardFilters: DashboardFilter[] | undefined,\n dashboardFilterMapping: DashboardFilterMapping | undefined\n): ServerFunnelQuery {\n // Clone the funnel query to avoid mutating the original\n const modifiedFunnel = { ...funnelQuery, funnel: { ...funnelQuery.funnel, steps: [...funnelQuery.funnel.steps] } }\n\n // Regular filters → merge into step 0's filter\n if (applicableFilters.length > 0 && modifiedFunnel.funnel.steps.length > 0) {\n const step0 = { ...modifiedFunnel.funnel.steps[0] }\n const existingFilters = step0.filter ? (Array.isArray(step0.filter) ? step0.filter : [step0.filter]) : []\n const mergedFilters = mergeDashboardAndPortletFilters(applicableFilters, existingFilters as any)\n step0.filter = mergedFilters\n modifiedFunnel.funnel.steps[0] = step0\n }\n\n // Universal time filters → apply as inDateRange filter on step 0\n applyUniversalTimeToFunnel(modifiedFunnel, dashboardFilters, dashboardFilterMapping)\n\n return modifiedFunnel\n}\n\n/**\n * Parse the portlet query string and merge applicable dashboard filters.\n * Returns a discriminated set of possible query shapes (only one non-null).\n */\nexport function parsePortletQuery(params: ParsePortletQueryParams): ParsedPortletQuery {\n const { query, shouldSkipQuery, regularFilters, dashboardFilters, dashboardFilterMapping } = params\n\n // Skip query parsing for charts that don't need queries\n if (shouldSkipQuery) {\n return EMPTY_RESULT\n }\n\n try {\n const parsed = JSON.parse(query)\n\n // Get applicable dashboard filters (excluding universal time filters - they apply to timeDimensions)\n const applicableFilters = getApplicableDashboardFilters(regularFilters, dashboardFilterMapping)\n\n // ServerRetentionQuery format { retention: {...} }\n // Retention queries don't have dashboard filter merging yet (could be added later)\n if (isServerRetentionQuery(parsed)) {\n return { ...EMPTY_RESULT, serverRetentionQuery: parsed as ServerRetentionQuery }\n }\n\n // ServerFlowQuery format { flow: {...} }\n // Flow queries don't have dashboard filter merging yet (could be added later)\n if (isServerFlowQuery(parsed)) {\n return { ...EMPTY_RESULT, serverFlowQuery: parsed as ServerFlowQuery }\n }\n\n // ServerFunnelQuery format { funnel: {...} }\n if (isServerFunnelQuery(parsed)) {\n const modifiedFunnel = applyFiltersToFunnel(\n parsed as ServerFunnelQuery,\n applicableFilters,\n dashboardFilters,\n dashboardFilterMapping\n )\n return { ...EMPTY_RESULT, serverFunnelQuery: modifiedFunnel }\n }\n\n // Multi-query configuration: apply filters to each query in the array\n if (isMultiQueryConfig(parsed)) {\n const multiConfig: MultiQueryConfig = {\n ...parsed,\n queries: parsed.queries.map(q => ({\n ...q,\n filters: mergeDashboardAndPortletFilters(applicableFilters, q.filters),\n timeDimensions: applyUniversalTimeFilters(dashboardFilters, dashboardFilterMapping, q.timeDimensions)\n }))\n }\n return { ...EMPTY_RESULT, multiQueryConfig: multiConfig }\n }\n\n // Single query: existing behavior\n const mergedFilters = mergeDashboardAndPortletFilters(applicableFilters, parsed.filters)\n const mergedTimeDimensions = applyUniversalTimeFilters(\n dashboardFilters,\n dashboardFilterMapping,\n parsed.timeDimensions\n )\n\n return {\n ...EMPTY_RESULT,\n queryObject: {\n ...parsed,\n filters: mergedFilters,\n timeDimensions: mergedTimeDimensions\n }\n }\n } catch (e) {\n console.error('AnalyticsPortlet: Invalid query JSON:', e)\n return EMPTY_RESULT\n }\n}\n","/**\n * Utilities for building drill queries from drill options\n */\n\nimport type {\n CubeQuery,\n Filter,\n CubeMeta,\n CubeMetaHierarchy,\n TimeGranularity,\n DashboardFilter,\n DashboardFilterMapping\n} from '../types.js'\nimport type {\n DrillOption,\n DrillResult,\n ChartDataPointClickEvent\n} from '../types/drill.js'\n\n/**\n * Generate a simple unique ID (no external dependency)\n */\nfunction generateId(): string {\n return `drill-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n}\n\n/**\n * Time granularity order from least to most granular\n * Used for determining drill direction\n */\nconst TIME_GRANULARITY_ORDER: TimeGranularity[] = [\n 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second'\n]\n\n/**\n * Check if a dimension is a time dimension based on metadata\n */\nexport function isTimeDimension(\n dimensionName: string,\n metadata: CubeMeta\n): boolean {\n for (const cube of metadata.cubes) {\n const dimension = cube.dimensions.find(d => d.name === dimensionName)\n if (dimension && dimension.type === 'time') {\n return true\n }\n }\n return false\n}\n\n/**\n * Get the granularities available for a time dimension\n */\nexport function getTimeDimensionGranularities(\n dimensionName: string,\n metadata: CubeMeta\n): TimeGranularity[] {\n for (const cube of metadata.cubes) {\n const dimension = cube.dimensions.find(d => d.name === dimensionName)\n if (dimension && dimension.type === 'time' && dimension.granularities) {\n return dimension.granularities\n }\n }\n // Default granularities if not specified\n return ['year', 'quarter', 'month', 'week', 'day', 'hour']\n}\n\n/**\n * Get the current granularity from a query's time dimensions\n */\nexport function getCurrentGranularity(query: CubeQuery): TimeGranularity | null {\n if (!query.timeDimensions || query.timeDimensions.length === 0) {\n return null\n }\n const granularity = query.timeDimensions[0].granularity\n if (granularity && TIME_GRANULARITY_ORDER.includes(granularity as TimeGranularity)) {\n return granularity as TimeGranularity\n }\n return null\n}\n\n/**\n * Get drillMembers for a measure from metadata\n */\nexport function getMeasureDrillMembers(\n measureName: string,\n metadata: CubeMeta\n): string[] | null {\n for (const cube of metadata.cubes) {\n const measure = cube.measures.find(m => m.name === measureName)\n if (measure && measure.drillMembers && measure.drillMembers.length > 0) {\n return measure.drillMembers\n }\n }\n return null\n}\n\n/**\n * Get hierarchy by name from metadata\n */\nexport function getHierarchy(\n hierarchyName: string,\n cubeName: string,\n metadata: CubeMeta\n): CubeMetaHierarchy | null {\n const cube = metadata.cubes.find(c => c.name === cubeName)\n if (cube && cube.hierarchies) {\n return cube.hierarchies.find(h => h.name === hierarchyName) || null\n }\n return null\n}\n\n/**\n * Get all hierarchies for a cube\n */\nexport function getCubeHierarchies(\n cubeName: string,\n metadata: CubeMeta\n): CubeMetaHierarchy[] {\n const cube = metadata.cubes.find(c => c.name === cubeName)\n return cube?.hierarchies || []\n}\n\n/**\n * Find which hierarchy (if any) contains a dimension\n */\nexport function findHierarchyForDimension(\n dimensionName: string,\n metadata: CubeMeta\n): { hierarchy: CubeMetaHierarchy; levelIndex: number } | null {\n const [cubeName] = dimensionName.split('.')\n const cube = metadata.cubes.find(c => c.name === cubeName)\n\n if (cube && cube.hierarchies) {\n for (const hierarchy of cube.hierarchies) {\n const levelIndex = hierarchy.levels.indexOf(dimensionName)\n if (levelIndex !== -1) {\n return { hierarchy, levelIndex }\n }\n }\n }\n return null\n}\n\n/**\n * Build drill options for a clicked data point\n */\nexport function buildDrillOptions(\n event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta | null,\n dashboardFilters?: DashboardFilter[],\n dashboardFilterMapping?: DashboardFilterMapping\n): DrillOption[] {\n if (!metadata) {\n return []\n }\n\n const options: DrillOption[] = []\n const { clickedField } = event\n\n // Find the measure being drilled (if clicking on a measure)\n const measureName = clickedField\n const drillMembers = getMeasureDrillMembers(measureName, metadata)\n\n // 1. Time dimension drill options (if query has time dimensions)\n const timeDimOptions = buildTimeDrillOptions(query, metadata, dashboardFilters, dashboardFilterMapping)\n options.push(...timeDimOptions)\n\n // 2. Hierarchy drill options (for any dimension hierarchies in the query)\n const hierarchyOptions = buildHierarchyDrillOptions(query, metadata, dashboardFilters, dashboardFilterMapping)\n options.push(...hierarchyOptions)\n\n // 3. Detail drill options (if measure has drillMembers)\n // Show each drillMember as a separate option so user can choose what to drill into\n if (drillMembers && drillMembers.length > 0) {\n for (const drillMember of drillMembers) {\n const label = getDimensionLabel(drillMember, metadata)\n options.push({\n id: `details-${measureName}-${drillMember}`,\n label: `Show by ${label}`,\n type: 'details',\n icon: 'table',\n scope: 'portlet',\n measure: measureName,\n targetDimension: drillMember // Which drillMember to use\n })\n }\n }\n return options\n}\n\n/**\n * Build time-based drill options\n */\nfunction buildTimeDrillOptions(\n query: CubeQuery,\n metadata: CubeMeta,\n _dashboardFilters?: DashboardFilter[],\n _dashboardFilterMapping?: DashboardFilterMapping\n): DrillOption[] {\n const options: DrillOption[] = []\n\n if (!query.timeDimensions || query.timeDimensions.length === 0) {\n return options\n }\n\n const timeDim = query.timeDimensions[0]\n const currentGranularity = timeDim.granularity as TimeGranularity | undefined\n const availableGranularities = getTimeDimensionGranularities(timeDim.dimension, metadata)\n\n if (availableGranularities.length === 0) {\n return options\n }\n\n // When no granularity is set, offer \"View by X\" options\n if (!currentGranularity) {\n for (const granularity of availableGranularities) {\n options.push({\n id: `time-set-${granularity}-portlet`,\n label: `View by ${capitalizeGranularity(granularity)}`,\n type: 'drillDown',\n icon: 'time',\n targetGranularity: granularity,\n scope: 'portlet'\n })\n }\n return options\n }\n\n const currentIndex = availableGranularities.indexOf(currentGranularity)\n\n // Drill down options (more granular)\n for (let i = currentIndex + 1; i < availableGranularities.length; i++) {\n const targetGranularity = availableGranularities[i]\n\n options.push({\n id: `time-down-${targetGranularity}-portlet`,\n label: `Drill to ${capitalizeGranularity(targetGranularity)}`,\n type: 'drillDown',\n icon: 'time',\n targetGranularity,\n scope: 'portlet'\n })\n }\n\n // Drill up options (less granular)\n for (let i = currentIndex - 1; i >= 0; i--) {\n const targetGranularity = availableGranularities[i]\n\n options.push({\n id: `time-up-${targetGranularity}-portlet`,\n label: `Roll up to ${capitalizeGranularity(targetGranularity)}`,\n type: 'drillUp',\n icon: 'time',\n targetGranularity,\n scope: 'portlet'\n })\n }\n\n return options\n}\n\n/**\n * Build hierarchy-based drill options\n */\nfunction buildHierarchyDrillOptions(\n query: CubeQuery,\n metadata: CubeMeta,\n _dashboardFilters?: DashboardFilter[],\n _dashboardFilterMapping?: DashboardFilterMapping\n): DrillOption[] {\n const options: DrillOption[] = []\n\n if (!query.dimensions || query.dimensions.length === 0) {\n return options\n }\n\n // Check each dimension for hierarchy membership\n for (const dimension of query.dimensions) {\n const hierarchyInfo = findHierarchyForDimension(dimension, metadata)\n\n if (!hierarchyInfo) {\n continue\n }\n\n const { hierarchy, levelIndex } = hierarchyInfo\n\n // Drill down (more granular - next level in hierarchy)\n if (levelIndex < hierarchy.levels.length - 1) {\n const nextDimension = hierarchy.levels[levelIndex + 1]\n options.push({\n id: `hierarchy-down-${hierarchy.name}-${nextDimension}`,\n label: `Drill to ${getDimensionLabel(nextDimension, metadata)}`,\n type: 'drillDown',\n icon: 'hierarchy',\n hierarchy: hierarchy.name,\n targetDimension: nextDimension,\n scope: 'portlet'\n })\n }\n\n // Drill up (less granular - previous level in hierarchy)\n if (levelIndex > 0) {\n const prevDimension = hierarchy.levels[levelIndex - 1]\n options.push({\n id: `hierarchy-up-${hierarchy.name}-${prevDimension}`,\n label: `Roll up to ${getDimensionLabel(prevDimension, metadata)}`,\n type: 'drillUp',\n icon: 'hierarchy',\n hierarchy: hierarchy.name,\n targetDimension: prevDimension,\n scope: 'portlet'\n })\n }\n }\n\n return options\n}\n\n/**\n * Get display label for a dimension\n */\nfunction getDimensionLabel(dimensionName: string, metadata: CubeMeta): string {\n for (const cube of metadata.cubes) {\n const dimension = cube.dimensions.find(d => d.name === dimensionName)\n if (dimension) {\n return dimension.title || dimension.shortTitle || dimensionName.split('.')[1]\n }\n }\n return dimensionName.split('.')[1]\n}\n\n/**\n * Capitalize a granularity name\n */\nfunction capitalizeGranularity(granularity: string): string {\n return granularity.charAt(0).toUpperCase() + granularity.slice(1)\n}\n\n/**\n * Build a drill query based on the selected option\n */\nexport function buildDrillQuery(\n option: DrillOption,\n event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta\n): DrillResult {\n switch (option.type) {\n case 'drillDown':\n return buildDrillDownQuery(option, event, query, metadata)\n case 'drillUp':\n return buildDrillUpQuery(option, event, query, metadata)\n case 'details':\n return buildDetailsQuery(option, event, query, metadata)\n default:\n throw new Error(`Unknown drill type: ${option.type}`)\n }\n}\n\n/**\n * Build a drill-down query (more granular)\n */\nfunction buildDrillDownQuery(\n option: DrillOption,\n event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta\n): DrillResult {\n const { xValue } = event\n const newQuery = { ...query }\n const filters: Filter[] = []\n\n if (option.targetGranularity && query.timeDimensions) {\n // Time-based drill down\n const timeDim = query.timeDimensions[0]\n const currentGranularity = timeDim.granularity\n\n // Update granularity\n newQuery.timeDimensions = [{\n ...timeDim,\n granularity: option.targetGranularity,\n // Update date range to filter to the clicked period\n dateRange: getDateRangeForPeriod(String(xValue), currentGranularity || 'month')\n }]\n\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: String(xValue),\n query: newQuery,\n filters,\n granularity: option.targetGranularity,\n clickedValue: xValue\n }\n }\n } else if (option.targetDimension) {\n // Hierarchy-based drill down\n const currentDimensions = query.dimensions || []\n const hierarchyInfo = option.hierarchy ? findHierarchyForDimension(option.targetDimension, metadata) : null\n\n // Replace the current hierarchy dimension with the target\n const newDimensions = currentDimensions.map(dim => {\n if (hierarchyInfo && hierarchyInfo.hierarchy.levels.includes(dim)) {\n return option.targetDimension!\n }\n return dim\n })\n\n // If not replacing, add the new dimension\n if (!newDimensions.includes(option.targetDimension)) {\n newDimensions.push(option.targetDimension)\n }\n\n newQuery.dimensions = newDimensions\n\n // Add filter for the clicked value\n const currentDim = currentDimensions.find(d => {\n const info = findHierarchyForDimension(d, metadata)\n return info && info.hierarchy.name === option.hierarchy\n })\n\n if (currentDim && xValue !== undefined) {\n const newFilter: Filter = {\n member: currentDim,\n operator: 'equals',\n values: [String(xValue)]\n }\n filters.push(newFilter)\n newQuery.filters = [...(query.filters || []), newFilter]\n }\n\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: String(xValue),\n query: newQuery,\n filters,\n dimension: option.targetDimension,\n hierarchy: option.hierarchy,\n clickedValue: xValue\n }\n }\n }\n\n // Fallback - return query unchanged\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: 'Drill',\n query: newQuery,\n filters,\n clickedValue: xValue\n }\n }\n}\n\n/**\n * Build a drill-up query (less granular / roll-up)\n */\nfunction buildDrillUpQuery(\n option: DrillOption,\n _event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta\n): DrillResult {\n const newQuery = { ...query }\n\n if (option.targetGranularity && query.timeDimensions) {\n // Time-based drill up\n const timeDim = query.timeDimensions[0]\n\n newQuery.timeDimensions = [{\n ...timeDim,\n granularity: option.targetGranularity,\n // Clear date range to show all data at new granularity\n dateRange: timeDim.dateRange\n }]\n\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: `By ${capitalizeGranularity(option.targetGranularity)}`,\n query: newQuery,\n granularity: option.targetGranularity\n }\n }\n } else if (option.targetDimension) {\n // Hierarchy-based drill up\n const currentDimensions = query.dimensions || []\n const hierarchyInfo = option.hierarchy ? findHierarchyForDimension(option.targetDimension, metadata) : null\n\n // Replace the current hierarchy dimension with the target\n const newDimensions = currentDimensions.map(dim => {\n if (hierarchyInfo && hierarchyInfo.hierarchy.levels.includes(dim)) {\n return option.targetDimension!\n }\n return dim\n })\n\n newQuery.dimensions = newDimensions\n\n // Remove filters for dimensions below the target level\n if (query.filters && hierarchyInfo) {\n const targetIndex = hierarchyInfo.hierarchy.levels.indexOf(option.targetDimension)\n const lowerLevels = hierarchyInfo.hierarchy.levels.slice(targetIndex + 1)\n\n newQuery.filters = query.filters.filter(f => {\n if ('member' in f) {\n return !lowerLevels.includes(f.member)\n }\n return true\n })\n }\n\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: `By ${getDimensionLabel(option.targetDimension, metadata)}`,\n query: newQuery,\n dimension: option.targetDimension,\n hierarchy: option.hierarchy\n }\n }\n }\n\n // Fallback\n return {\n query: newQuery,\n pathEntry: {\n id: generateId(),\n label: 'Roll Up',\n query: newQuery\n }\n }\n}\n\n/**\n * Build a details query using the selected drillMember\n */\nfunction buildDetailsQuery(\n option: DrillOption,\n event: ChartDataPointClickEvent,\n query: CubeQuery,\n metadata: CubeMeta\n): DrillResult {\n const { xValue } = event\n const measureName = option.measure || event.clickedField\n\n // Use the specific drillMember selected by the user\n const targetDimension = option.targetDimension\n if (!targetDimension) {\n throw new Error(`No targetDimension specified for details drill on measure ${measureName}`)\n }\n\n // Get labels for breadcrumb\n const targetDimensionLabel = getDimensionLabel(targetDimension, metadata)\n\n // Get the current x-axis dimension label for context\n const xAxisDimension = query.dimensions?.[0] || query.timeDimensions?.[0]?.dimension\n const xAxisLabel = xAxisDimension ? getDimensionLabel(xAxisDimension, metadata) : null\n\n // Check if the target dimension is a time dimension\n const isTargetTimeDimension = isTimeDimension(targetDimension, metadata)\n\n // Build a new query with the selected drillMember\n // If it's a time dimension, put it in timeDimensions; otherwise in dimensions\n const newQuery: CubeQuery = {\n measures: [measureName], // Keep the measure to show its value\n dimensions: isTargetTimeDimension ? [] : [targetDimension],\n timeDimensions: isTargetTimeDimension\n ? [{\n dimension: targetDimension,\n // Use granularity from original query or default to 'day'\n granularity: query.timeDimensions?.[0]?.granularity || 'day',\n // Preserve dateRange from original query (from dashboard filter)\n dateRange: query.timeDimensions?.[0]?.dateRange\n }]\n : query.timeDimensions, // Preserve existing time context for non-time drills\n filters: [...(query.filters || [])],\n limit: 100 // Reasonable default limit for detail view\n }\n\n // Add filter for the clicked data point\n if (xAxisDimension && xValue !== undefined && xValue !== null && xValue !== '') {\n const xFilter: Filter = {\n member: xAxisDimension,\n operator: 'equals',\n values: [String(xValue)]\n }\n newQuery.filters = [...(newQuery.filters || []), xFilter]\n }\n\n // Generate chart config that maps the selected dimension to xAxis\n // For time dimensions, use the timeDimension format in xAxis\n const chartConfig = {\n xAxis: [targetDimension], // Selected drillMember as x-axis\n yAxis: [measureName] // The measure being drilled\n }\n\n // Build a meaningful breadcrumb label\n // Format: \"By {dimension} ({clicked value context})\" or just \"By {dimension}\"\n const breadcrumbLabel = xValue !== undefined && xValue !== null && xValue !== ''\n ? `By ${targetDimensionLabel} (${xAxisLabel}: ${xValue})`\n : `By ${targetDimensionLabel}`\n\n return {\n query: newQuery,\n chartConfig,\n pathEntry: {\n id: generateId(),\n label: breadcrumbLabel,\n query: newQuery,\n filters: newQuery.filters,\n clickedValue: xValue,\n chartConfig\n }\n }\n}\n\n/**\n * Get a date range for a specific period value\n * @param periodValue - The period value (e.g., '2024-01', 'Q1 2024', '2024')\n * @param granularity - The current granularity\n * @returns Date range as [startDate, endDate] strings\n */\nfunction getDateRangeForPeriod(\n periodValue: string,\n granularity: string\n): [string, string] {\n // This is a simplified implementation\n // In production, you'd want more robust date parsing\n const date = new Date(periodValue)\n\n if (isNaN(date.getTime())) {\n // If we can't parse, return the value as-is (let the server handle it)\n return [periodValue, periodValue]\n }\n\n switch (granularity) {\n case 'year': {\n const year = date.getFullYear()\n return [`${year}-01-01`, `${year}-12-31`]\n }\n case 'quarter': {\n const year = date.getFullYear()\n const quarter = Math.floor(date.getMonth() / 3)\n const startMonth = quarter * 3\n const endMonth = startMonth + 2\n return [\n `${year}-${String(startMonth + 1).padStart(2, '0')}-01`,\n `${year}-${String(endMonth + 1).padStart(2, '0')}-${new Date(year, endMonth + 1, 0).getDate()}`\n ]\n }\n case 'month': {\n const year = date.getFullYear()\n const month = date.getMonth()\n const lastDay = new Date(year, month + 1, 0).getDate()\n return [\n `${year}-${String(month + 1).padStart(2, '0')}-01`,\n `${year}-${String(month + 1).padStart(2, '0')}-${lastDay}`\n ]\n }\n case 'week': {\n // Get the start of the week (Monday)\n const dayOfWeek = date.getDay()\n const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek\n const weekStart = new Date(date)\n weekStart.setDate(date.getDate() + diff)\n const weekEnd = new Date(weekStart)\n weekEnd.setDate(weekStart.getDate() + 6)\n return [\n weekStart.toISOString().split('T')[0],\n weekEnd.toISOString().split('T')[0]\n ]\n }\n case 'day':\n default: {\n const dateStr = date.toISOString().split('T')[0]\n return [dateStr, dateStr]\n }\n }\n}\n","/**\n * Pure helpers for useDrillInteraction.\n *\n * These functions compute \"what should happen\" for a drill interaction without\n * touching React state. The hook applies the resulting descriptors via its\n * setters, preserving the original ordering of state updates.\n */\n\nimport type {\n DrillOption,\n DrillPathEntry,\n} from '../types/drill.js'\nimport type { ChartAxisConfig, CubeQuery } from '../types.js'\n\n/**\n * Find an existing drill-path level for the given option and compute the\n * \"navigate back\" descriptor (the truncated path + the query/chartConfig to\n * restore). Returns null when there is no matching existing level.\n *\n * Used for both time-granularity drilling (matched by `granularity`) and\n * hierarchy-dimension drilling (matched by `dimension`).\n */\nexport function findNavigateBackToExistingLevel(\n drillPath: DrillPathEntry[],\n matches: (entry: DrillPathEntry) => boolean\n): { newPath: DrillPathEntry[]; query: CubeQuery; chartConfig: ChartAxisConfig | null } | null {\n const existingLevelIndex = drillPath.findIndex(matches)\n if (existingLevelIndex === -1) return null\n\n const targetIndex = existingLevelIndex + 1\n if (targetIndex < drillPath.length) {\n const newPath = drillPath.slice(0, targetIndex)\n const targetEntry = newPath[newPath.length - 1]\n return {\n newPath,\n query: targetEntry.query,\n chartConfig: targetEntry.chartConfig || null,\n }\n }\n // Existing level is already the deepest entry: only close the menu (no path change).\n return null\n}\n\n/**\n * Whether selecting this option means returning all the way to the root via the\n * original time granularity.\n */\nexport function isDrillBackToRootGranularity(\n option: DrillOption,\n originalGranularity: string | null\n): boolean {\n return Boolean(\n option.targetGranularity &&\n originalGranularity &&\n option.targetGranularity === originalGranularity\n )\n}\n\n/**\n * Compute the granularity to remember as the \"original\" when first drilling via\n * a time-granularity option. Returns null when nothing should be stored.\n */\nexport function computeOriginalGranularity(\n option: DrillOption,\n query: CubeQuery\n): string | null {\n if (option.targetGranularity && query.timeDimensions?.[0]) {\n return query.timeDimensions[0].granularity ?? null\n }\n return null\n}\n","/**\n * useDrillInteraction - Hook for managing drill-down interaction state\n * Coordinates between chart clicks, drill menu, and query updates\n */\n\nimport { useState, useCallback, useMemo } from 'react'\nimport type {\n ChartDataPointClickEvent,\n DrillOption,\n DrillPathEntry,\n DrillInteraction,\n UseDrillInteractionOptions\n} from '../types/drill.js'\nimport type { ChartAxisConfig, CubeQuery } from '../types.js'\nimport {\n buildDrillOptions,\n buildDrillQuery,\n getMeasureDrillMembers\n} from '../utils/drillQueryBuilder.js'\nimport { mappingIncludesFilter } from '../utils/filterUtils.js'\nimport {\n findNavigateBackToExistingLevel,\n isDrillBackToRootGranularity,\n computeOriginalGranularity\n} from './drillNavigation.js'\n\n/**\n * Hook for managing drill-down interaction\n *\n * @param options - Configuration options\n * @returns DrillInteraction object with handlers and state\n */\nexport function useDrillInteraction(options: UseDrillInteractionOptions): DrillInteraction {\n const {\n query,\n metadata,\n onQueryChange,\n chartConfig,\n dashboardFilters,\n dashboardFilterMapping,\n // onDashboardFilterChange is no longer used - dashboard-scope options removed\n enabled = true\n } = options\n\n // Menu state\n const [menuOpen, setMenuOpen] = useState(false)\n const [menuPosition, setMenuPosition] = useState<{ x: number; y: number } | null>(null)\n const [menuOptions, setMenuOptions] = useState<DrillOption[]>([])\n const [currentClickEvent, setCurrentClickEvent] = useState<ChartDataPointClickEvent | null>(null)\n\n // Drill path (breadcrumb navigation)\n const [drillPath, setDrillPath] = useState<DrillPathEntry[]>([])\n\n // Track the original granularity before any drilling (to detect when we should go back to root)\n const [originalGranularity, setOriginalGranularity] = useState<string | null>(null)\n\n // Track the original chart config to restore on drill up to root\n const [originalChartConfig, setOriginalChartConfig] = useState<ChartAxisConfig | null>(null)\n\n // Current chart config override (from drill details)\n const [currentChartConfig, setCurrentChartConfig] = useState<ChartAxisConfig | null>(null)\n\n // Track the complete original query to restore on drill up to root\n const [originalQuery, setOriginalQuery] = useState<CubeQuery | null>(null)\n\n /**\n * Check if any drill options are available for the current query/metadata\n */\n const drillEnabled = useMemo(() => {\n if (!enabled || !metadata) {\n return false\n }\n\n // Check if query has time dimensions (can drill time)\n const hasTimeDimensions = (query.timeDimensions?.length ?? 0) > 0\n\n // Check if query has dimensions that might be in hierarchies\n const hasDimensions = (query.dimensions?.length ?? 0) > 0\n\n // Check if any measures have drillMembers\n const hasDrillMembers = query.measures?.some(measure =>\n getMeasureDrillMembers(measure, metadata) !== null\n ) ?? false\n\n return hasTimeDimensions || hasDimensions || hasDrillMembers\n }, [enabled, metadata, query])\n\n /**\n * Check if any dimension in the query matches a dashboard filter\n */\n const hasDashboardFilterMatch = useMemo(() => {\n if (!dashboardFilters || !dashboardFilterMapping) {\n return false\n }\n\n // Check for universal time filter\n return dashboardFilters.some(f =>\n f.isUniversalTime && mappingIncludesFilter(dashboardFilterMapping, f.id)\n )\n }, [dashboardFilters, dashboardFilterMapping])\n\n /**\n * Handle data point click from chart\n */\n const handleDataPointClick = useCallback((event: ChartDataPointClickEvent) => {\n if (!enabled || !metadata) {\n return\n }\n\n // Build drill options for this click\n const options = buildDrillOptions(\n event,\n query,\n metadata,\n dashboardFilters,\n dashboardFilterMapping\n )\n\n if (options.length === 0) {\n return\n }\n\n // Store the click event and show menu\n setCurrentClickEvent(event)\n setMenuOptions(options)\n setMenuPosition(event.position)\n setMenuOpen(true)\n }, [enabled, metadata, query, dashboardFilters, dashboardFilterMapping])\n\n /**\n * Restore the complete original query and clear all drill tracking state.\n * Shared by the granularity drill-back-to-root branch and the breadcrumb\n * navigation handlers. The setter ordering matches the original inline code.\n */\n const restoreToRoot = useCallback(() => {\n // Restore the complete original query (fully restores dimensions, measures, filters, etc.)\n const restoredQuery = originalQuery || query\n setDrillPath([])\n // Restore original chart config (or clear if none was stored)\n setCurrentChartConfig(originalChartConfig)\n setOriginalChartConfig(null)\n setOriginalGranularity(null)\n setOriginalQuery(null)\n onQueryChange(restoredQuery)\n }, [originalQuery, query, originalChartConfig, onQueryChange])\n\n /**\n * Apply a \"navigate back to existing level\" descriptor (shared between the\n * granularity and hierarchy-dimension branches of handleOptionSelect).\n */\n const applyNavigateBack = useCallback(\n (nav: { newPath: DrillPathEntry[]; query: CubeQuery; chartConfig: ChartAxisConfig | null }) => {\n setDrillPath(nav.newPath)\n onQueryChange(nav.query)\n setCurrentChartConfig(nav.chartConfig)\n },\n [onQueryChange]\n )\n\n /**\n * Handle drill option selection\n */\n const handleOptionSelect = useCallback((option: DrillOption) => {\n if (!currentClickEvent || !metadata) {\n return\n }\n\n try {\n // For time dimension drilling, check if we should navigate back instead of creating new entries\n if (option.targetGranularity && drillPath.length > 0) {\n // Check if target granularity exists in drill path (navigate back to that level)\n const nav = findNavigateBackToExistingLevel(\n drillPath,\n entry => entry.granularity === option.targetGranularity\n )\n if (nav) {\n applyNavigateBack(nav)\n setMenuOpen(false)\n setCurrentClickEvent(null)\n return\n }\n\n // Check if drilling back to the original granularity (return to root)\n if (isDrillBackToRootGranularity(option, originalGranularity)) {\n restoreToRoot()\n setMenuOpen(false)\n setCurrentClickEvent(null)\n return\n }\n }\n\n // For hierarchy dimension drilling, check if we should navigate back\n if (option.targetDimension && drillPath.length > 0) {\n const nav = findNavigateBackToExistingLevel(\n drillPath,\n entry => entry.dimension === option.targetDimension\n )\n if (nav) {\n applyNavigateBack(nav)\n setMenuOpen(false)\n setCurrentClickEvent(null)\n return\n }\n }\n\n // Build the drill query\n const result = buildDrillQuery(option, currentClickEvent, query, metadata)\n\n // Track original state when first drilling\n if (drillPath.length === 0) {\n // Store the complete original query for restoration on drill back to root\n setOriginalQuery(query)\n\n // Store original chart config for restoration on drill back to root\n if (chartConfig) {\n setOriginalChartConfig(chartConfig)\n }\n\n // Track original granularity for detecting drill-back-to-root via time granularity\n const currentGran = computeOriginalGranularity(option, query)\n if (currentGran) {\n setOriginalGranularity(currentGran)\n }\n }\n\n // Add to drill path and update query\n setDrillPath(prev => [...prev, result.pathEntry])\n onQueryChange(result.query)\n\n // Update chart config if provided (e.g., when drilling to details)\n if (result.chartConfig) {\n setCurrentChartConfig(result.chartConfig)\n }\n } catch (error) {\n console.error('Error building drill query:', error)\n }\n\n // Close menu\n setMenuOpen(false)\n setCurrentClickEvent(null)\n }, [currentClickEvent, metadata, query, drillPath, originalGranularity, onQueryChange, chartConfig, applyNavigateBack, restoreToRoot])\n\n /**\n * Close the drill menu\n */\n const closeMenu = useCallback(() => {\n setMenuOpen(false)\n setMenuPosition(null)\n setMenuOptions([])\n setCurrentClickEvent(null)\n }, [])\n\n /**\n * Navigate back one level in the drill path\n */\n const navigateBack = useCallback(() => {\n if (drillPath.length === 0) {\n return\n }\n\n if (drillPath.length === 1) {\n // Going back to root - restore the complete original query\n restoreToRoot()\n } else {\n // Go back to previous level\n const newPath = drillPath.slice(0, -1)\n const previousEntry = newPath[newPath.length - 1]\n setDrillPath(newPath)\n onQueryChange(previousEntry.query)\n // Restore chart config from previous entry\n setCurrentChartConfig(previousEntry.chartConfig || null)\n }\n }, [drillPath, onQueryChange, restoreToRoot])\n\n /**\n * Navigate to a specific level in the drill path\n */\n const navigateToLevel = useCallback((index: number) => {\n if (index <= 0) {\n // Navigate to root - restore the complete original query\n restoreToRoot()\n } else if (index < drillPath.length) {\n // Navigate to a specific level\n const newPath = drillPath.slice(0, index)\n const targetEntry = newPath[newPath.length - 1]\n setDrillPath(newPath)\n onQueryChange(targetEntry.query)\n // Restore chart config from target entry\n setCurrentChartConfig(targetEntry.chartConfig || null)\n }\n }, [drillPath, onQueryChange, restoreToRoot])\n\n return {\n handleDataPointClick,\n menuOpen,\n menuPosition,\n menuOptions,\n handleOptionSelect,\n closeMenu,\n drillPath,\n navigateBack,\n navigateToLevel,\n drillEnabled,\n hasDashboardFilterMatch,\n currentChartConfig\n }\n}\n\nexport default useDrillInteraction\n","/**\n * Local drill-down query state for AnalyticsPortlet: tracks the temporarily\n * drilled query, resets it when the base query changes, wires the\n * useDrillInteraction hook, and exposes navigation handlers. Extracted to keep\n * the component flat. No behaviour change.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { useDrillInteraction } from '../../hooks/useDrillInteraction.js'\nimport { useCubeMeta } from '../../providers/CubeMetaContext.js'\nimport type { ChartAxisConfig, CubeQuery, DashboardFilter, DashboardFilterMapping } from '../../types.js'\n\nexport interface UsePortletDrillStateParams {\n queryObject: CubeQuery | null\n chartConfig?: ChartAxisConfig\n dashboardFilters?: DashboardFilter[]\n dashboardFilterMapping?: DashboardFilterMapping\n isMultiQuery: boolean\n isFunnelMode: boolean\n isFlowMode: boolean\n isRetentionMode: boolean\n}\n\nexport function usePortletDrillState(params: UsePortletDrillStateParams) {\n const {\n queryObject,\n chartConfig,\n dashboardFilters,\n dashboardFilterMapping,\n isMultiQuery,\n isFunnelMode,\n isFlowMode,\n isRetentionMode\n } = params\n\n // Track the current drilled query (null means using the original/parsed query)\n const [drilledQuery, setDrilledQuery] = useState<CubeQuery | null>(null)\n\n // Reset drilled query when the base query changes (e.g., dashboard filters change)\n const queryObjectJson = queryObject ? JSON.stringify(queryObject) : null\n const previousQueryObjectJson = useRef<string | null>(null)\n useEffect(() => {\n if (queryObjectJson !== previousQueryObjectJson.current) {\n previousQueryObjectJson.current = queryObjectJson\n // Reset drill state when base query changes\n if (drilledQuery) {\n setDrilledQuery(null)\n }\n }\n }, [queryObjectJson, drilledQuery])\n\n // The active query is either the drilled query or the original parsed query\n const activeQuery = drilledQuery || queryObject\n\n // Get metadata for drill options (only for single query mode)\n const { meta } = useCubeMeta()\n\n // Drill interaction hook - only enabled for single query mode\n const drill = useDrillInteraction({\n query: activeQuery || { measures: [], dimensions: [] },\n metadata: meta,\n onQueryChange: (newQuery) => {\n setDrilledQuery(newQuery)\n },\n chartConfig,\n dashboardFilters,\n dashboardFilterMapping,\n enabled: !isMultiQuery && !isFunnelMode && !isFlowMode && !isRetentionMode && !!activeQuery\n })\n\n // Handle navigating back to root - restore the original query\n const handleNavigateBack = useCallback(() => {\n if (drill.drillPath.length === 1) {\n // Going back to root - restore original query\n setDrilledQuery(null)\n }\n drill.navigateBack()\n }, [drill])\n\n // Handle navigating to a specific level\n const handleNavigateToLevel = useCallback((index: number) => {\n if (index === 0) {\n // Navigate to root - restore original query\n setDrilledQuery(null)\n }\n drill.navigateToLevel(index)\n }, [drill])\n\n return {\n drill,\n activeQuery,\n handleNavigateBack,\n handleNavigateToLevel\n }\n}\n","/**\n * Runs the five query hooks (single, multi, funnel, flow, retention) for a\n * portlet and derives a single combined result set: loading/fetching/error\n * state, the active data for the current mode, and `refresh`/`retry` handlers.\n *\n * Extracted from AnalyticsPortlet to keep the component flat. Behaviour is\n * identical to the original inline logic.\n */\n\nimport { useCallback } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport {\n useCubeLoadQuery,\n useMultiCubeLoadQuery,\n useFunnelQuery,\n useFlowQuery,\n useRetentionQuery,\n createQueryKey,\n createMultiQueryKey\n} from '../../hooks/queries/index.js'\nimport { cleanQueryForServer } from '../../shared/utils.js'\nimport type { CubeQuery, MultiQueryConfig, ServerFunnelQuery } from '../../types.js'\nimport type { FlowChartData, ServerFlowQuery } from '../../types/flow.js'\nimport type { RetentionChartData, ServerRetentionQuery } from '../../types/retention.js'\n\ninterface RefreshOptions {\n bustCache?: boolean\n}\n\nexport interface UsePortletQueryResultsParams {\n activeQuery: CubeQuery | null\n multiQueryConfig: MultiQueryConfig | null\n serverFunnelQuery: ServerFunnelQuery | null\n serverFlowQuery: ServerFlowQuery | null\n serverRetentionQuery: ServerRetentionQuery | null\n isMultiQuery: boolean\n isFunnelMode: boolean\n isFlowMode: boolean\n isRetentionMode: boolean\n shouldSkipQuery: boolean\n eagerLoad: boolean\n isVisible: boolean\n}\n\nexport interface PortletQueryResults {\n resultSet: ReturnType<typeof useCubeLoadQuery>['resultSet']\n isLoading: boolean\n isFetching: boolean\n error: unknown\n multiQueryData: unknown[] | null\n flowChartData: FlowChartData | null\n retentionChartData: RetentionChartData | null\n funnelCacheInfo: ReturnType<typeof useFunnelQuery>['cacheInfo']\n flowCacheInfo: ReturnType<typeof useFlowQuery>['cacheInfo']\n retentionCacheInfo: ReturnType<typeof useRetentionQuery>['cacheInfo']\n refresh: (options?: RefreshOptions) => void\n retry: () => void\n}\n\nexport function usePortletQueryResults(params: UsePortletQueryResultsParams): PortletQueryResults {\n const {\n activeQuery,\n multiQueryConfig,\n serverFunnelQuery,\n serverFlowQuery,\n serverRetentionQuery,\n isMultiQuery,\n isFunnelMode,\n isFlowMode,\n isRetentionMode,\n shouldSkipQuery,\n eagerLoad,\n isVisible\n } = params\n\n const queryClient = useQueryClient()\n\n // Derive per-hook skip flags from visibility + mode. A hook runs only for its\n // own mode, when the portlet is visible (or eager) and queries aren't skipped.\n const notReady = shouldSkipQuery || (!eagerLoad && !isVisible)\n const shouldSkipSingle = !activeQuery || notReady || isMultiQuery || isFunnelMode || isFlowMode || isRetentionMode\n const shouldSkipMulti = !multiQueryConfig || notReady || isFunnelMode || isFlowMode || isRetentionMode\n const shouldSkipFunnel = !isFunnelMode || notReady\n const shouldSkipFlow = !isFlowMode || notReady\n const shouldSkipRetention = !isRetentionMode || notReady\n\n // Note: Legacy funnel config via mergeStrategy === 'funnel' is no longer supported\n // Funnel mode now uses ServerFunnelQuery format exclusively\n const funnelConfig = null\n\n // Use activeQuery to support drill-down (which replaces the query temporarily)\n const singleQueryResult = useCubeLoadQuery(activeQuery, {\n skip: shouldSkipSingle,\n resetResultSetOnChange: true,\n debounceMs: 100, // Lower debounce for portlets (faster response)\n })\n\n const multiQueryResult = useMultiCubeLoadQuery(multiQueryConfig, {\n skip: shouldSkipMulti,\n resetResultSetOnChange: true,\n debounceMs: 100,\n })\n\n const funnelQueryResult = useFunnelQuery(funnelConfig, {\n skip: shouldSkipFunnel || (!funnelConfig && !serverFunnelQuery),\n debounceMs: 100,\n // Pass prebuilt ServerFunnelQuery directly (new dedicated funnel mode)\n prebuiltServerQuery: serverFunnelQuery,\n })\n\n const flowQueryResult = useFlowQuery(serverFlowQuery, {\n skip: shouldSkipFlow,\n debounceMs: 100,\n })\n\n const retentionQueryResult = useRetentionQuery(serverRetentionQuery, {\n skip: shouldSkipRetention,\n debounceMs: 100,\n })\n\n // Combine results from all hooks\n const resultSet = isMultiQuery ? null : singleQueryResult.resultSet\n\n // Pick a per-mode value following the mode priority order\n // (retention → flow → funnel → multi → single).\n function pickByMode<T>(vals: { retention: T; flow: T; funnel: T; multi: T; single: T }): T {\n if (isRetentionMode) return vals.retention\n if (isFlowMode) return vals.flow\n if (isFunnelMode) return vals.funnel\n if (isMultiQuery) return vals.multi\n return vals.single\n }\n\n const isLoading = pickByMode({\n retention: retentionQueryResult.isLoading || retentionQueryResult.isDebouncing,\n flow: flowQueryResult.isLoading || flowQueryResult.isDebouncing,\n funnel: funnelQueryResult.isExecuting || funnelQueryResult.isDebouncing,\n multi: multiQueryResult.isLoading,\n single: singleQueryResult.isLoading\n })\n\n const isFetching = pickByMode({\n retention: retentionQueryResult.isFetching,\n flow: flowQueryResult.isFetching,\n funnel: funnelQueryResult.isExecuting,\n multi: multiQueryResult.isFetching,\n single: singleQueryResult.isFetching\n })\n\n const error = pickByMode({\n retention: retentionQueryResult.error,\n flow: flowQueryResult.error,\n funnel: funnelQueryResult.error,\n multi: multiQueryResult.error,\n single: singleQueryResult.error\n })\n\n // Retention returns data in retentionQueryResult.chartData; flow in\n // flowQueryResult.data — both have non-array structures and stay null here.\n const multiQueryData = pickByMode<unknown[] | null>({\n retention: null,\n flow: null,\n funnel: funnelQueryResult.chartData as unknown[] | null,\n multi: multiQueryResult.data,\n single: null\n })\n\n // Flow data is separate since it has a different structure (nodes/links vs array)\n const flowChartData = isFlowMode ? flowQueryResult.data : null\n // Retention data is separate since it has a different structure (rows/periods vs array)\n const retentionChartData = isRetentionMode ? retentionQueryResult.chartData : null\n\n // Either remove (bustCache) or invalidate the given cache key.\n const bustOrInvalidate = useCallback((queryKey: readonly unknown[], bustCache: boolean) => {\n if (bustCache) {\n queryClient.removeQueries({ queryKey })\n } else {\n queryClient.invalidateQueries({ queryKey })\n }\n }, [queryClient])\n\n // Expose refresh function. Invalidates cache and forces a fresh fetch from the\n // server. Pass bustCache: true to bypass both client and server caches.\n const refresh = useCallback((options?: RefreshOptions) => {\n const bustCache = options?.bustCache ?? false\n\n if (isRetentionMode && serverRetentionQuery) {\n // Retention query key format: ['cube', 'retention', JSON.stringify(serverQuery)]\n bustOrInvalidate(['cube', 'retention', JSON.stringify(serverRetentionQuery)], bustCache)\n retentionQueryResult.refetch()\n } else if (isFlowMode && serverFlowQuery) {\n // Flow query key format: ['cube', 'flow', JSON.stringify(serverQuery)]\n bustOrInvalidate(['cube', 'flow', JSON.stringify(serverFlowQuery)], bustCache)\n flowQueryResult.refetch({ bustCache })\n } else if (isFunnelMode && serverFunnelQuery) {\n // Funnel query key format: ['cube', 'funnel', stepCount, JSON.stringify(serverQuery)]\n const stepCount = serverFunnelQuery.funnel?.steps?.length || 0\n bustOrInvalidate(['cube', 'funnel', stepCount, JSON.stringify(serverFunnelQuery)], bustCache)\n funnelQueryResult.execute({ bustCache })\n } else if (isMultiQuery && multiQueryConfig) {\n // Clean each query to match the cache key format used by useMultiCubeLoadQuery\n const cleanedConfig = {\n ...multiQueryConfig,\n queries: multiQueryConfig.queries.map((q: CubeQuery) => cleanQueryForServer(q))\n }\n bustOrInvalidate(createMultiQueryKey(cleanedConfig), bustCache)\n multiQueryResult.refetch({ bustCache })\n } else if (activeQuery) {\n // Clean the query to match the cache key format used by useCubeLoadQuery\n bustOrInvalidate(createQueryKey(cleanQueryForServer(activeQuery)), bustCache)\n singleQueryResult.refetch({ bustCache })\n }\n }, [isRetentionMode, isFlowMode, isFunnelMode, isMultiQuery, multiQueryConfig, activeQuery, bustOrInvalidate, serverRetentionQuery, serverFlowQuery, serverFunnelQuery, retentionQueryResult, flowQueryResult, funnelQueryResult, multiQueryResult, singleQueryResult])\n\n const retry = useCallback(() => {\n if (isRetentionMode) {\n retentionQueryResult.refetch()\n } else if (isFlowMode) {\n flowQueryResult.refetch()\n } else if (isFunnelMode) {\n funnelQueryResult.execute()\n } else if (isMultiQuery) {\n multiQueryResult.refetch()\n } else {\n singleQueryResult.refetch()\n }\n }, [isRetentionMode, isFlowMode, isFunnelMode, isMultiQuery, retentionQueryResult, flowQueryResult, funnelQueryResult, multiQueryResult, singleQueryResult])\n\n return {\n resultSet,\n isLoading,\n isFetching,\n error,\n multiQueryData,\n flowChartData,\n retentionChartData,\n funnelCacheInfo: funnelQueryResult.cacheInfo,\n flowCacheInfo: flowQueryResult.cacheInfo,\n retentionCacheInfo: retentionQueryResult.cacheInfo,\n refresh,\n retry\n }\n}\n","/**\n * Reports portlet debug data (chart config, query, resolved data, cache info,\n * drill state) up to the parent whenever results change. Extracted from\n * AnalyticsPortlet to keep the component flat. Behaviour is identical to the\n * original inline effect.\n */\n\nimport { useEffect, useRef } from 'react'\nimport type { AnalyticsPortletProps, ChartAxisConfig, ChartDisplayConfig, ChartType, CubeQuery, ServerFunnelQuery } from '../../types.js'\nimport type { FlowChartData, ServerFlowQuery } from '../../types/flow.js'\nimport type { RetentionChartData, ServerRetentionQuery } from '../../types/retention.js'\nimport type { DrillPathEntry } from '../../types/drill.js'\n\ntype DebugEntry = Parameters<NonNullable<AnalyticsPortletProps['onDebugDataReady']>>[0]\ntype CacheInfo = DebugEntry['cacheInfo']\n\ninterface ResultSetLike {\n tablePivot: () => unknown\n rawData: () => unknown\n cacheInfo?: () => CacheInfo\n}\n\nexport interface UsePortletDebugDataParams {\n onDebugDataReady?: AnalyticsPortletProps['onDebugDataReady']\n error: unknown\n chartType: ChartType\n chartConfig?: ChartAxisConfig\n displayConfig?: ChartDisplayConfig\n // Mode flags\n isFunnelMode: boolean\n isFlowMode: boolean\n isRetentionMode: boolean\n // Parsed queries\n queryObject: CubeQuery | null\n activeQuery: CubeQuery | null\n serverFunnelQuery: ServerFunnelQuery | null\n serverFlowQuery: ServerFlowQuery | null\n serverRetentionQuery: ServerRetentionQuery | null\n // Data\n resultSet: ResultSetLike | null\n multiQueryData: unknown[] | null\n flowChartData: FlowChartData | null\n retentionChartData: RetentionChartData | null\n // Cache info\n funnelCacheInfo?: CacheInfo\n flowCacheInfo?: CacheInfo\n retentionCacheInfo?: CacheInfo\n // Drill\n drillPath: DrillPathEntry[]\n currentChartConfig?: ChartAxisConfig | null\n}\n\n/**\n * Resolve single-query data based on the chart type (pie/table use the pivot).\n */\nfunction getSingleQueryData(chartType: ChartType, resultSet: ResultSetLike): unknown {\n switch (chartType) {\n case 'pie':\n case 'table':\n return resultSet.tablePivot()\n default:\n return resultSet.rawData()\n }\n}\n\n/**\n * Build the single-query debug entry, including drill state when active.\n */\nfunction buildSingleQueryEntry(params: UsePortletDebugDataParams): DebugEntry | null {\n const { chartConfig, displayConfig, chartType, queryObject, activeQuery, resultSet, drillPath, currentChartConfig } = params\n if (!chartConfig || !queryObject || !resultSet) return null\n\n const data = getSingleQueryData(chartType, resultSet) as unknown[]\n if (!data) return null\n\n // Include drill state in debug info if drilling is active\n const drillState = drillPath.length > 0 ? {\n isDrilling: true,\n drillPath: drillPath.map(entry => ({\n id: entry.id,\n label: entry.label,\n clickedValue: entry.clickedValue,\n dimension: entry.dimension,\n granularity: entry.granularity,\n hierarchy: entry.hierarchy\n })),\n currentDrillDepth: drillPath.length,\n originalQuery: queryObject,\n activeQuery: activeQuery\n } : undefined\n\n return {\n chartConfig: currentChartConfig || chartConfig || {},\n displayConfig: displayConfig || {},\n queryObject: activeQuery || queryObject,\n data,\n chartType,\n cacheInfo: resultSet.cacheInfo?.(),\n drillState\n }\n}\n\n/**\n * Compute the debug entry for whichever mode is active, or null if there is\n * nothing to report yet.\n */\nfunction buildDebugEntry(params: UsePortletDebugDataParams): DebugEntry | null {\n const {\n chartConfig, displayConfig, chartType,\n isFunnelMode, isFlowMode, isRetentionMode,\n serverFunnelQuery, serverFlowQuery, serverRetentionQuery,\n multiQueryData, flowChartData, retentionChartData,\n funnelCacheInfo, flowCacheInfo, retentionCacheInfo\n } = params\n\n const baseConfig = { chartConfig: chartConfig || {}, displayConfig: displayConfig || {}, chartType }\n\n if (isFunnelMode && multiQueryData && multiQueryData.length > 0) {\n return {\n ...baseConfig,\n queryObject: serverFunnelQuery as unknown as Record<string, unknown>,\n data: multiQueryData,\n cacheInfo: funnelCacheInfo ?? undefined\n }\n }\n\n if (isFlowMode && serverFlowQuery && flowChartData) {\n return {\n ...baseConfig,\n queryObject: serverFlowQuery as unknown as Record<string, unknown>,\n data: flowChartData,\n cacheInfo: flowCacheInfo\n }\n }\n\n if (isRetentionMode && serverRetentionQuery && retentionChartData) {\n return {\n ...baseConfig,\n queryObject: serverRetentionQuery as unknown as Record<string, unknown>,\n data: retentionChartData,\n cacheInfo: retentionCacheInfo ?? undefined\n }\n }\n\n return buildSingleQueryEntry(params)\n}\n\nexport function usePortletDebugData(params: UsePortletDebugDataParams): void {\n const {\n onDebugDataReady,\n error,\n chartType,\n chartConfig,\n displayConfig,\n isFunnelMode,\n isFlowMode,\n isRetentionMode,\n queryObject,\n activeQuery,\n serverFunnelQuery,\n serverFlowQuery,\n serverRetentionQuery,\n resultSet,\n multiQueryData,\n flowChartData,\n retentionChartData,\n funnelCacheInfo,\n flowCacheInfo,\n retentionCacheInfo,\n drillPath,\n currentChartConfig\n } = params\n\n // Use ref for callback to prevent infinite loops\n const onDebugDataReadyRef = useRef(onDebugDataReady)\n useEffect(() => {\n onDebugDataReadyRef.current = onDebugDataReady\n }, [onDebugDataReady])\n\n useEffect(() => {\n const report = onDebugDataReadyRef.current\n if (!report || error) return\n\n const entry = buildDebugEntry(params)\n if (entry) report(entry)\n // We intentionally depend on the individual values (not `params`) to keep\n // the effect from re-running on every render due to object identity.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [chartConfig, displayConfig, queryObject, activeQuery, resultSet, chartType, error, isFunnelMode, isFlowMode, isRetentionMode, multiQueryData, serverFunnelQuery, serverFlowQuery, serverRetentionQuery, flowChartData, retentionChartData, flowCacheInfo, funnelCacheInfo, retentionCacheInfo, drillPath, currentChartConfig])\n}\n","/**\n * Decides which view AnalyticsPortlet should render (config-required, lazy\n * placeholder, loading, error, no-data, or the chart) from the resolved query\n * state. Pure logic extracted from the component to keep it flat. No behaviour\n * change.\n */\n\nimport type { FlowChartData } from '../../types/flow.js'\nimport type { RetentionChartData } from '../../types/retention.js'\nimport type { CubeQuery, MultiQueryConfig, ServerFunnelQuery } from '../../types.js'\nimport type { ServerFlowQuery } from '../../types/flow.js'\nimport type { ServerRetentionQuery } from '../../types/retention.js'\n\nexport type PortletRenderKind =\n | 'config-required'\n | 'lazy-placeholder'\n | 'loading'\n | 'error'\n | 'no-data'\n | 'chart'\n\nexport interface PortletRenderStateParams {\n hasChartConfig: boolean\n hasMandatoryFields: boolean\n shouldSkipQuery: boolean\n eagerLoad: boolean\n isVisible: boolean\n isLoading: boolean\n isFetching: boolean\n error: unknown\n // Mode flags\n isMultiQuery: boolean\n isFunnelMode: boolean\n isFlowMode: boolean\n isRetentionMode: boolean\n // Parsed queries\n queryObject: CubeQuery | null\n multiQueryConfig: MultiQueryConfig | null\n serverFunnelQuery: ServerFunnelQuery | null\n serverFlowQuery: ServerFlowQuery | null\n serverRetentionQuery: ServerRetentionQuery | null\n // Data\n resultSet: unknown\n multiQueryData: unknown[] | null\n flowChartData: FlowChartData | null\n retentionChartData: RetentionChartData | null\n}\n\n/**\n * Whether the active mode has produced valid data to render a chart.\n */\nfunction hasValidDataForMode(p: PortletRenderStateParams): boolean {\n if (p.isRetentionMode) return p.retentionChartData !== null && p.serverRetentionQuery !== null\n if (p.isFlowMode) return p.flowChartData !== null && p.serverFlowQuery !== null\n if (p.isFunnelMode) return p.multiQueryData !== null && p.serverFunnelQuery !== null\n if (p.isMultiQuery) return p.multiQueryData !== null && p.multiQueryConfig !== null\n return p.resultSet !== null && p.queryObject !== null\n}\n\n/**\n * Resolve which view the portlet should render.\n */\nexport function resolvePortletRenderKind(p: PortletRenderStateParams): PortletRenderKind {\n // Config required (not for skipQuery charts)\n if (!p.hasChartConfig && p.hasMandatoryFields) return 'config-required'\n\n // Lazy-load placeholder for portlets not yet visible\n if (!p.shouldSkipQuery && !p.eagerLoad && !p.isVisible) return 'lazy-placeholder'\n\n // Charts that don't need queries skip loading/error/no-data handling\n if (p.shouldSkipQuery) return 'chart'\n\n // Loading during initial load OR during refresh (isFetching)\n if (p.isLoading || p.isFetching || (p.queryObject && !p.resultSet && !p.error)) return 'loading'\n\n if (p.error) return 'error'\n\n if (!hasValidDataForMode(p)) return 'no-data'\n\n return 'chart'\n}\n","import React, { Component, ReactNode } from 'react'\nimport { getIcon } from '../icons/index.js'\nimport { t } from '../../i18n/runtime.js'\n\nconst RefreshIcon = getIcon('refresh')\n\ninterface Props {\n children: ReactNode\n fallback?: ReactNode\n portletTitle?: string\n portletConfig?: any\n cubeQuery?: string\n}\n\ninterface State {\n hasError: boolean\n error: Error | null\n errorInfo: string | null\n}\n\nexport default class ChartErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props)\n this.state = {\n hasError: false,\n error: null,\n errorInfo: null\n }\n }\n\n static getDerivedStateFromError(error: Error): State {\n // Update state so the next render will show the fallback UI\n return {\n hasError: true,\n error,\n errorInfo: null\n }\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n // Update state with error details\n this.setState({\n error,\n errorInfo: errorInfo.componentStack || null\n })\n\n // Log the error for debugging\n console.error('Chart Error Boundary caught a rendering error:', error, errorInfo)\n }\n\n handleReset = () => {\n this.setState({\n hasError: false,\n error: null,\n errorInfo: null\n })\n }\n\n render() {\n if (this.state.hasError) {\n // Custom fallback UI\n if (this.props.fallback) {\n return this.props.fallback\n }\n\n // Default error display\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full dc:h-full dc:p-6 dc:text-center dc:border dc:border-dashed dc:rounded-lg\"\n style={{ borderColor: 'var(--dc-border)', backgroundColor: 'var(--dc-surface)' }}>\n <div className=\"dc:h-12 dc:w-12 dc:mb-4 text-dc-text-muted\">⚠️</div>\n <h3 className=\"dc:text-lg dc:font-semibold dc:mb-2 text-dc-text\">\n {this.props.portletTitle ? `Unable to render ${this.props.portletTitle}` : 'Unable to render chart'}\n </h3>\n <p className=\"dc:text-sm text-dc-text-secondary dc:mb-4 dc:max-w-md\">\n {t('error.renderDescription')}\n </p>\n\n {/* Error details */}\n <div className=\"dc:w-full dc:max-w-2xl dc:mb-4\">\n <div className=\"bg-dc-surface-secondary dc:rounded-lg dc:p-3 dc:text-left\">\n <div className=\"dc:text-xs dc:font-mono dc:mb-2 text-dc-text\">\n <strong>{t('error.errorLabel')}</strong> {this.state.error?.message}\n </div>\n {this.state.error?.name && (\n <div className=\"dc:text-xs dc:font-mono text-dc-text-secondary dc:mb-2\">\n <strong>{t('error.typeLabel')}</strong> {this.state.error.name}\n </div>\n )}\n\n {/* Portlet Config Debug Info */}\n {this.props.portletConfig && (\n <details className=\"dc:text-xs dc:font-mono text-dc-text-secondary dc:mb-2\">\n <summary className=\"dc:cursor-pointer\">{t('error.portletConfig')}</summary>\n <pre className=\"dc:mt-2 dc:whitespace-pre-wrap dc:p-2 dc:rounded-sm dc:overflow-auto dc:max-h-32\"\n style={{ backgroundColor: 'rgba(var(--dc-primary-rgb), 0.1)' }}>\n {JSON.stringify(this.props.portletConfig, null, 2)}\n </pre>\n </details>\n )}\n\n {/* Cube Query Debug Info */}\n {this.props.cubeQuery && (\n <details className=\"dc:text-xs dc:font-mono text-dc-text-secondary dc:mb-2\">\n <summary className=\"dc:cursor-pointer\">{t('error.cubeQuery')}</summary>\n <pre className=\"dc:mt-2 dc:whitespace-pre-wrap dc:p-2 dc:rounded-sm dc:overflow-auto dc:max-h-32\"\n style={{ backgroundColor: '#d1fae5' }}>\n {typeof this.props.cubeQuery === 'string' \n ? JSON.stringify(JSON.parse(this.props.cubeQuery), null, 2)\n : JSON.stringify(this.props.cubeQuery, null, 2)\n }\n </pre>\n </details>\n )}\n\n {this.state.errorInfo && (\n <details className=\"dc:text-xs dc:font-mono text-dc-text-secondary\">\n <summary className=\"dc:cursor-pointer\">{t('error.componentStack')}</summary>\n <pre className=\"dc:mt-2 dc:whitespace-pre-wrap\">{this.state.errorInfo}</pre>\n </details>\n )}\n </div>\n </div>\n\n {/* Reset button */}\n <button\n onClick={this.handleReset}\n className=\"dc:px-3 dc:py-1 text-white dc:rounded-sm dc:text-sm dc:hover:opacity-90 dc:transition-opacity\"\n style={{\n backgroundColor: 'var(--dc-primary)'\n }}\n >\n <RefreshIcon style={{ width: '16px', height: '16px', display: 'inline', marginRight: '4px' }} />{t('error.tryAgain')}\n </button>\n </div>\n )\n }\n\n return this.props.children\n }\n}\n","/**\n * DrillMenu - Popover menu for drill-down options\n * Shows available drill directions (down/up) grouped by type (time, hierarchy, details)\n * Rendered in a portal to avoid stacking context issues with react-grid-layout\n */\n\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport type { DrillMenuProps, DrillOption } from '../types/drill.js'\n\n/**\n * Icon components for drill options\n */\nfunction TimeIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"8\" cy=\"8\" r=\"6\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M8 5V8L10 10\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nfunction HierarchyIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"6\" y=\"2\" width=\"4\" height=\"3\" rx=\"0.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <rect x=\"2\" y=\"11\" width=\"4\" height=\"3\" rx=\"0.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <rect x=\"10\" y=\"11\" width=\"4\" height=\"3\" rx=\"0.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M8 5V8M8 8L4 11M8 8L12 11\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n </svg>\n )\n}\n\nfunction TableIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"2\" y=\"2\" width=\"12\" height=\"12\" rx=\"1\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n <path d=\"M2 6H14M6 6V14\" stroke=\"currentColor\" strokeWidth=\"1.5\" />\n </svg>\n )\n}\n\nfunction DrillDownIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 2V10M6 10L3 7M6 10L9 7\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\nfunction DrillUpIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 10V2M6 2L3 5M6 2L9 5\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n/**\n * Get the direction indicator for a drill option\n */\nfunction getDirectionIndicator(option: DrillOption) {\n if (option.type === 'drillDown') {\n return <DrillDownIcon className=\"dc:w-3 dc:h-3 text-dc-success\" />\n }\n if (option.type === 'drillUp') {\n return <DrillUpIcon className=\"dc:w-3 dc:h-3 text-dc-warning\" />\n }\n return null\n}\n\n/**\n * Group options by category (time, hierarchy, details)\n */\nfunction groupOptions(options: DrillOption[]): Map<string, DrillOption[]> {\n const groups = new Map<string, DrillOption[]>()\n\n for (const option of options) {\n const category = option.icon || 'other'\n const existing = groups.get(category) || []\n existing.push(option)\n groups.set(category, existing)\n }\n\n return groups\n}\n\n/**\n * Get display name for a category\n */\nfunction getCategoryLabel(category: string): string {\n switch (category) {\n case 'time':\n return 'Time'\n case 'hierarchy':\n return 'Hierarchy'\n case 'table':\n return 'Details'\n default:\n return 'Options'\n }\n}\n\n/**\n * DrillMenu component\n * Uses createPortal to render directly to document.body, avoiding stacking context issues\n */\nexport function DrillMenu({ options, position, onSelect, onClose }: DrillMenuProps) {\n const menuRef = useRef<HTMLDivElement>(null)\n const [mounted, setMounted] = useState(false)\n\n // Track if we're mounted (for SSR safety)\n useEffect(() => {\n setMounted(true)\n return () => setMounted(false)\n }, [])\n\n // Close on click outside\n useEffect(() => {\n function handleClickOutside(event: MouseEvent) {\n if (menuRef.current && !menuRef.current.contains(event.target as Node)) {\n onClose()\n }\n }\n\n function handleEscape(event: KeyboardEvent) {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n\n // Close on scroll to avoid menu getting out of sync with clicked element\n function handleScroll() {\n onClose()\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n // Listen to scroll on capture phase to catch scrolling containers\n window.addEventListener('scroll', handleScroll, true)\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n window.removeEventListener('scroll', handleScroll, true)\n }\n }, [onClose])\n\n if (options.length === 0 || !mounted) {\n return null\n }\n\n // Position menu near clicked point but within viewport\n // Use very high z-index to ensure menu appears above everything\n const menuStyle: React.CSSProperties = {\n position: 'fixed',\n left: Math.min(position.x, window.innerWidth - 250),\n top: Math.min(position.y, window.innerHeight - 300),\n zIndex: 99999\n }\n\n const groupedOptions = groupOptions(options)\n\n const menuContent = (\n <div\n ref={menuRef}\n className=\"dc:min-w-[200px] dc:max-w-[280px] bg-dc-surface dc:rounded-lg dc:shadow-lg border border-dc-border dc:overflow-hidden\"\n style={menuStyle}\n >\n {Array.from(groupedOptions.entries()).map(([category, categoryOptions], categoryIndex) => (\n <div key={category}>\n {/* Category header */}\n <div className=\"dc:px-3 dc:py-2 dc:flex dc:items-center dc:gap-2 bg-dc-surface-secondary text-dc-text-secondary dc:text-xs dc:font-medium dc:uppercase dc:tracking-wide\">\n {category === 'time' && <TimeIcon className=\"dc:w-3 dc:h-3\" />}\n {category === 'hierarchy' && <HierarchyIcon className=\"dc:w-3 dc:h-3\" />}\n {category === 'table' && <TableIcon className=\"dc:w-3 dc:h-3\" />}\n {getCategoryLabel(category)}\n </div>\n\n {/* Options in this category */}\n <div className=\"dc:py-1\">\n {categoryOptions.map((option) => (\n <button\n key={option.id}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:flex dc:items-center dc:gap-2 dc:cursor-pointer text-dc-text dc:text-sm dc:text-left hover:bg-dc-surface-hover hover:text-dc-accent dc:transition-colors dc:rounded-sm\"\n onClick={() => {\n onSelect(option)\n onClose()\n }}\n >\n {/* Direction indicator */}\n <span className=\"dc:w-4 dc:flex dc:justify-center\">\n {getDirectionIndicator(option)}\n </span>\n\n {/* Label */}\n <span className=\"dc:flex-1\">{option.label}</span>\n </button>\n ))}\n </div>\n\n {/* Separator between categories */}\n {categoryIndex < groupedOptions.size - 1 && (\n <div className=\"border-t border-dc-border\" />\n )}\n </div>\n ))}\n </div>\n )\n\n // Render in a portal to avoid stacking context issues with react-grid-layout\n return createPortal(menuContent, document.body)\n}\n\nexport default DrillMenu\n","/**\n * DrillBreadcrumb - Navigation breadcrumb for drill path\n * Shows the current drill history and allows navigating back\n */\n\nimport React from 'react'\nimport type { DrillBreadcrumbProps } from '../types/drill.js'\nimport { useTranslation } from '../hooks/useTranslation.js'\n\n/**\n * Home icon for the root level\n */\nfunction HomeIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 7L7 2L12 7\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M3 6V11.5C3 11.7761 3.22386 12 3.5 12H5.5V9H8.5V12H10.5C10.7761 12 11 11.7761 11 11.5V6\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n/**\n * Chevron separator between breadcrumb items\n */\nfunction ChevronIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M4 2L8 6L4 10\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n/**\n * Back arrow icon\n */\nfunction BackIcon({ className }: { className?: string }) {\n return (\n <svg className={className} width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12 7H2M2 7L6 3M2 7L6 11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n/**\n * Get a safe display label, handling edge cases\n */\nfunction getSafeLabel(label: string | undefined | null): string {\n if (!label || label === 'undefined' || label === 'null' || label === '') {\n return '(empty)'\n }\n // Handle labels that are just whitespace\n if (label.trim() === '') {\n return '(empty)'\n }\n return label\n}\n\n/**\n * DrillBreadcrumb component\n */\nexport function DrillBreadcrumb({ path, onNavigate, onLevelClick }: DrillBreadcrumbProps) {\n const { t } = useTranslation()\n if (path.length === 0) {\n return null\n }\n\n return (\n <div className=\"dc:flex dc:items-center dc:gap-1 dc:px-2 dc:py-1.5 bg-dc-surface-secondary dc:rounded-md dc:text-xs\">\n {/* Back button */}\n <button\n className=\"dc:flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded dc:cursor-pointer dc:hover:bg-dc-surface-hover text-dc-text-secondary dc:hover:text-dc-text dc:transition-colors\"\n onClick={onNavigate}\n title=\"Go back one level\"\n >\n <BackIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span className=\"dc:sr-only\">{t('drill.back')}</span>\n </button>\n\n <span className=\"text-dc-text-muted\">|</span>\n\n {/* Home / root level */}\n <button\n className=\"dc:flex dc:items-center dc:gap-1 dc:px-1.5 dc:py-1 dc:rounded dc:cursor-pointer dc:hover:bg-dc-surface-hover text-dc-text-secondary dc:hover:text-dc-text dc:transition-colors\"\n onClick={() => onLevelClick?.(0)}\n title=\"Return to top level\"\n >\n <HomeIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n\n {/* Path items */}\n {path.map((entry, index) => {\n const safeLabel = getSafeLabel(entry.label)\n return (\n <React.Fragment key={entry.id}>\n <ChevronIcon className=\"dc:w-3 dc:h-3 text-dc-text-muted\" />\n\n {index === path.length - 1 ? (\n // Current level (not clickable)\n <span className=\"dc:px-1.5 dc:py-1 text-dc-text dc:font-medium\" title={safeLabel}>\n {safeLabel}\n </span>\n ) : (\n // Previous levels (clickable)\n <button\n className=\"dc:px-1.5 dc:py-1 dc:rounded dc:cursor-pointer dc:hover:bg-dc-surface-hover text-dc-text-secondary dc:hover:text-dc-text dc:transition-colors\"\n onClick={() => onLevelClick?.(index + 1)}\n title={`Navigate to ${safeLabel}`}\n >\n {safeLabel}\n </button>\n )}\n </React.Fragment>\n )\n })}\n </div>\n )\n}\n\nexport default DrillBreadcrumb\n","/**\n * Resolves the portlet's data for the active mode and renders the lazy chart\n * (with the sankey/sunburst toggle and drill wiring). Extracted from\n * AnalyticsPortlet to keep the component flat. No behaviour change.\n */\n\nimport { LazyChart, isValidChartType } from '../../charts/ChartLoader.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport type { ChartAxisConfig, ChartDisplayConfig, ChartType, CubeQuery } from '../../types.js'\nimport type { FlowChartData } from '../../types/flow.js'\nimport type { RetentionChartData } from '../../types/retention.js'\nimport type { ColorPalette } from '../../utils/colorPalettes.js'\n\ntype Height = string | number\n\ninterface ResultSetLike {\n tablePivot: () => unknown\n rawData: () => unknown\n}\n\nexport interface PortletChartProps {\n chartType: ChartType\n height: Height\n shouldSkipQuery: boolean\n isMultiQuery: boolean\n isFunnelMode: boolean\n isFlowMode: boolean\n isRetentionMode: boolean\n resultSet: ResultSetLike | null\n multiQueryData: unknown[] | null\n flowChartData: FlowChartData | null\n retentionChartData: RetentionChartData | null\n chartConfig?: ChartAxisConfig\n displayConfig?: ChartDisplayConfig\n activeQuery: CubeQuery | null\n colorPalette?: ColorPalette\n drillEnabled: boolean\n currentChartConfig?: ChartAxisConfig | null\n onDataPointClick?: (...args: any[]) => void\n}\n\n/**\n * Resolve the chart data array (or flow/retention structure) for the active mode.\n */\nfunction resolvePortletData(props: PortletChartProps): unknown {\n const {\n shouldSkipQuery,\n isRetentionMode,\n isFlowMode,\n isFunnelMode,\n isMultiQuery,\n resultSet,\n multiQueryData,\n flowChartData,\n retentionChartData,\n chartType\n } = props\n\n // Charts that don't use query data\n if (shouldSkipQuery) return []\n // Retention charts expect { rows: [], periods: [] } structure\n if (isRetentionMode) return retentionChartData || { rows: [], periods: [] }\n // Sankey chart expects { nodes: [], links: [] } structure\n if (isFlowMode) return flowChartData || { nodes: [], links: [] }\n if (isFunnelMode) return multiQueryData || []\n if (isMultiQuery) return multiQueryData || []\n if (!resultSet) return []\n\n switch (chartType) {\n case 'pie':\n case 'table':\n return resultSet.tablePivot()\n default:\n return resultSet.rawData()\n }\n}\n\nexport function PortletChart(props: PortletChartProps) {\n const { t } = useTranslation()\n const {\n chartType,\n height,\n chartConfig,\n displayConfig,\n activeQuery,\n colorPalette,\n drillEnabled,\n currentChartConfig,\n onDataPointClick\n } = props\n\n try {\n // Determine effective chart type (handles sankey/sunburst toggle)\n const effectiveChartType = chartType === 'sankey' &&\n (displayConfig as Record<string, unknown>)?.flowVisualization === 'sunburst'\n ? 'sunburst'\n : chartType\n\n // Handle unsupported chart types\n if (!isValidChartType(effectiveChartType)) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full\" style={{ height }}>\n <div className=\"dc:text-center text-dc-text-muted\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('portlet.unsupportedChartType')}</div>\n <div className=\"dc:text-xs\">{effectiveChartType}</div>\n </div>\n </div>\n )\n }\n\n // Cast to unknown[] for ChartProps - specific charts (like Sankey) handle their own data format\n const data = resolvePortletData(props) as unknown as unknown[]\n\n // For markdown chart, use empty data array\n const chartData = effectiveChartType === 'markdown' ? [] : data\n\n // Use drill chart config if available, otherwise fall back to original\n const effectiveChartConfig = (drillEnabled && currentChartConfig)\n ? currentChartConfig\n : chartConfig\n\n return (\n <LazyChart\n chartType={effectiveChartType}\n data={chartData}\n chartConfig={effectiveChartConfig}\n displayConfig={displayConfig}\n queryObject={activeQuery ?? undefined}\n height={height}\n colorPalette={colorPalette}\n onDataPointClick={drillEnabled ? onDataPointClick : undefined}\n drillEnabled={drillEnabled}\n fallback={\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full\"\n style={{ height: typeof height === 'number' ? `${height}px` : height }}\n >\n <div className=\"dc:animate-pulse bg-dc-surface-secondary dc:rounded dc:w-full dc:h-full dc:min-h-[100px]\" />\n </div>\n }\n />\n )\n } catch (error) {\n console.error('Chart rendering error:', error)\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('portlet.unableToRender')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">{error instanceof Error ? error.message : t('errorBoundary.unknownError')}</div>\n </div>\n </div>\n )\n }\n}\n","/**\n * Success-state view for AnalyticsPortlet: error boundary wrapper, optional\n * drill breadcrumb, the chart, and the drill menu. Extracted to keep the main\n * component flat. No behaviour change.\n */\n\nimport ChartErrorBoundary from '../ChartErrorBoundary.js'\nimport { DrillMenu } from '../DrillMenu.js'\nimport { DrillBreadcrumb } from '../DrillBreadcrumb.js'\nimport { PortletChart } from './PortletChart.js'\nimport type { ChartAxisConfig, ChartDisplayConfig, ChartType, CubeQuery } from '../../types.js'\nimport type { FlowChartData } from '../../types/flow.js'\nimport type { RetentionChartData } from '../../types/retention.js'\nimport type { ColorPalette } from '../../utils/colorPalettes.js'\nimport type { DrillInteraction } from '../../types/drill.js'\n\ntype Height = string | number\n\ninterface ResultSetLike {\n tablePivot: () => unknown\n rawData: () => unknown\n}\n\nexport interface PortletChartViewProps {\n title?: string\n query: string\n chartType: ChartType\n height: Height\n chartConfig?: ChartAxisConfig\n displayConfig?: ChartDisplayConfig\n colorPalette?: ColorPalette\n shouldSkipQuery: boolean\n isMultiQuery: boolean\n isFunnelMode: boolean\n isFlowMode: boolean\n isRetentionMode: boolean\n resultSet: ResultSetLike | null\n multiQueryData: unknown[] | null\n flowChartData: FlowChartData | null\n retentionChartData: RetentionChartData | null\n activeQuery: CubeQuery | null\n drill: DrillInteraction\n isDrillEnabled: boolean\n onNavigateBack: () => void\n onNavigateToLevel: (index: number) => void\n}\n\nexport function PortletChartView(props: PortletChartViewProps) {\n const {\n title,\n query,\n chartType,\n height,\n chartConfig,\n displayConfig,\n colorPalette,\n shouldSkipQuery,\n isMultiQuery,\n isFunnelMode,\n isFlowMode,\n isRetentionMode,\n resultSet,\n multiQueryData,\n flowChartData,\n retentionChartData,\n activeQuery,\n drill,\n isDrillEnabled,\n onNavigateBack,\n onNavigateToLevel\n } = props\n\n return (\n <>\n <ChartErrorBoundary\n portletTitle={title}\n portletConfig={{ chartType, chartConfig, displayConfig, height }}\n cubeQuery={query}\n >\n <div className=\"dc:w-full dc:h-full dc:flex dc:flex-col dc:flex-1\" style={{ minHeight: chartType === 'markdown' ? undefined : '200px' }}>\n {/* Drill breadcrumb - shows when drilling into data */}\n {isDrillEnabled && drill.drillPath.length > 0 && (\n <div className=\"dc:mb-2 dc:flex-shrink-0\">\n <DrillBreadcrumb\n path={drill.drillPath}\n onNavigate={onNavigateBack}\n onLevelClick={onNavigateToLevel}\n />\n </div>\n )}\n\n {/* Chart content */}\n <div className=\"dc:flex-1 dc:min-h-0\">\n <PortletChart\n chartType={chartType}\n height={height}\n shouldSkipQuery={shouldSkipQuery}\n isMultiQuery={isMultiQuery}\n isFunnelMode={isFunnelMode}\n isFlowMode={isFlowMode}\n isRetentionMode={isRetentionMode}\n resultSet={resultSet}\n multiQueryData={multiQueryData}\n flowChartData={flowChartData}\n retentionChartData={retentionChartData}\n chartConfig={chartConfig}\n displayConfig={displayConfig}\n activeQuery={activeQuery}\n colorPalette={colorPalette}\n drillEnabled={isDrillEnabled}\n currentChartConfig={drill.currentChartConfig}\n onDataPointClick={drill.handleDataPointClick}\n />\n </div>\n </div>\n </ChartErrorBoundary>\n\n {/* Drill menu - positioned absolutely near clicked point */}\n {isDrillEnabled && drill.menuOpen && drill.menuPosition && (\n <DrillMenu\n options={drill.menuOptions}\n position={drill.menuPosition}\n onSelect={drill.handleOptionSelect}\n onClose={drill.closeMenu}\n />\n )}\n </>\n )\n}\n","/**\n * Presentational state views for AnalyticsPortlet: config-required, lazy-load\n * placeholder, loading, query error, and no-data. Extracted to keep the main\n * component flat. No behaviour change.\n */\n\nimport { type ReactNode, type Ref } from 'react'\nimport LoadingIndicator from '../LoadingIndicator.js'\nimport { DrillBreadcrumb } from '../DrillBreadcrumb.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport type { ChartType } from '../../types.js'\nimport type { DrillPathEntry } from '../../types/drill.js'\n\ntype Height = string | number\n\nexport function PortletConfigRequired({ inViewRef, height }: { inViewRef: Ref<HTMLDivElement>; height: Height }) {\n const { t } = useTranslation()\n return (\n <div ref={inViewRef} className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('portlet.configRequired')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">{t('portlet.configRequiredHint')}</div>\n </div>\n </div>\n )\n}\n\nexport function PortletLazyPlaceholder({ inViewRef, height }: { inViewRef: Ref<HTMLDivElement>; height: Height }) {\n return (\n <div ref={inViewRef} className=\"dc:w-full dc:h-full\" style={{ height }}>\n <div className=\"dc:w-full dc:h-full dc:animate-pulse bg-dc-surface-secondary dc:rounded\" style={{ minHeight: '100px' }} />\n </div>\n )\n}\n\nexport function PortletLoading({ inViewRef, height, loadingComponent }: { inViewRef: Ref<HTMLDivElement>; height: Height; loadingComponent?: ReactNode }) {\n return (\n <div ref={inViewRef} className=\"dc:flex dc:items-center dc:justify-center dc:w-full\" style={{ height }}>\n {loadingComponent || <LoadingIndicator size=\"md\" />}\n </div>\n )\n}\n\ninterface PortletErrorProps {\n inViewRef: Ref<HTMLDivElement>\n height: Height\n error: { message?: string; toString: () => string }\n onRetry: () => void\n activeQuery: unknown\n query: string\n chartType: ChartType\n chartConfig: unknown\n displayConfig: unknown\n}\n\nexport function PortletError({ inViewRef, height, error, onRetry, activeQuery, query, chartType, chartConfig, displayConfig }: PortletErrorProps) {\n const { t } = useTranslation()\n return (\n <div ref={inViewRef} className=\"dc:p-4 dc:border dc:rounded-sm\" style={{ height, borderColor: 'var(--dc-border)', backgroundColor: 'var(--dc-surface)' }}>\n <div className=\"dc:mb-2\">\n <div className=\"dc:flex dc:items-center dc:justify-between\">\n <span className=\"dc:font-medium dc:text-sm\" style={{ color: 'var(--dc-text)' }}>{`⚠️ ${t('portlet.queryError')}`}</span>\n <button\n onClick={onRetry}\n className=\"dc:px-2 dc:py-1 text-white dc:rounded-sm dc:text-xs\"\n style={{ backgroundColor: 'var(--dc-primary)' }}\n >\n {t('common.actions.retry')}\n </button>\n </div>\n </div>\n\n <div className=\"dc:mb-3\">\n <div className=\"dc:text-xs dc:p-2 dc:rounded-sm dc:border\" style={{ color: 'var(--dc-text-secondary)', backgroundColor: 'var(--dc-surface)', borderColor: 'var(--dc-border)' }}>\n {error.message || error.toString()}\n </div>\n </div>\n\n <div className=\"dc:space-y-2 dc:text-xs\">\n <details>\n <summary className=\"dc:cursor-pointer dc:font-medium\" style={{ color: 'var(--dc-text-secondary)' }}>{t('portlet.queryWithFilters')}</summary>\n <pre className=\"dc:mt-1 dc:p-2 dc:rounded-sm dc:text-xs dc:overflow-auto dc:max-h-20\" style={{ backgroundColor: 'rgba(var(--dc-primary-rgb), 0.1)' }}>\n {activeQuery ? JSON.stringify(activeQuery, null, 2) : query}\n </pre>\n </details>\n\n <details>\n <summary className=\"dc:cursor-pointer dc:font-medium\" style={{ color: 'var(--dc-text-secondary)' }}>{t('portlet.chartConfig')}</summary>\n <pre className=\"dc:mt-1 dc:p-2 dc:rounded-sm dc:text-xs dc:overflow-auto dc:max-h-20\" style={{ backgroundColor: 'rgba(var(--dc-primary-rgb), 0.05)' }}>\n {JSON.stringify({ chartType, chartConfig, displayConfig }, null, 2)}\n </pre>\n </details>\n </div>\n </div>\n )\n}\n\ninterface PortletNoDataProps {\n inViewRef: Ref<HTMLDivElement>\n height: Height\n drillPath: DrillPathEntry[]\n onNavigateBack: () => void\n onNavigateToLevel: (index: number) => void\n}\n\nexport function PortletNoData({ inViewRef, height, drillPath, onNavigateBack, onNavigateToLevel }: PortletNoDataProps) {\n const isDrilledState = drillPath.length > 0\n return (\n <div ref={inViewRef} className=\"dc:flex dc:flex-col dc:w-full\" style={{ height }}>\n {/* Show breadcrumb when drilling even if no data */}\n {isDrilledState && (\n <div className=\"dc:mb-2 dc:flex-shrink-0\">\n <DrillBreadcrumb\n path={drillPath}\n onNavigate={onNavigateBack}\n onLevelClick={onNavigateToLevel}\n />\n </div>\n )}\n <div className=\"dc:flex dc:items-center dc:justify-center dc:flex-1 text-dc-text-muted\">\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {isDrilledState\n ? 'No data points to display for the current filter'\n : 'Invalid query or no results'\n }\n </div>\n </div>\n </div>\n </div>\n )\n}\n\nexport type PortletStateKind = 'config-required' | 'lazy-placeholder' | 'loading' | 'error'\n\ninterface PortletStateViewProps {\n kind: PortletStateKind | 'no-data'\n inViewRef: Ref<HTMLDivElement>\n height: Height\n loadingComponent?: ReactNode\n // error\n error: { message?: string; toString: () => string } | null\n onRetry: () => void\n activeQuery: unknown\n query: string\n chartType: ChartType\n chartConfig: unknown\n displayConfig: unknown\n // no-data\n drillPath: DrillPathEntry[]\n onNavigateBack: () => void\n onNavigateToLevel: (index: number) => void\n}\n\n/**\n * Renders the appropriate non-chart state view for the given render kind.\n */\nexport function PortletStateView(props: PortletStateViewProps) {\n const { kind, inViewRef, height } = props\n\n if (kind === 'config-required') {\n return <PortletConfigRequired inViewRef={inViewRef} height={height} />\n }\n if (kind === 'lazy-placeholder') {\n return <PortletLazyPlaceholder inViewRef={inViewRef} height={height} />\n }\n if (kind === 'loading') {\n return <PortletLoading inViewRef={inViewRef} height={height} loadingComponent={props.loadingComponent} />\n }\n if (kind === 'error') {\n return (\n <PortletError\n inViewRef={inViewRef}\n height={height}\n error={props.error ?? { toString: () => '' }}\n onRetry={props.onRetry}\n activeQuery={props.activeQuery}\n query={props.query}\n chartType={props.chartType}\n chartConfig={props.chartConfig}\n displayConfig={props.displayConfig}\n />\n )\n }\n return (\n <PortletNoData\n inViewRef={inViewRef}\n height={height}\n drillPath={props.drillPath}\n onNavigateBack={props.onNavigateBack}\n onNavigateToLevel={props.onNavigateToLevel}\n />\n )\n}\n","/**\n * Analytics Portlet Component\n * Simplified version with minimal dependencies\n */\n\nimport React, { useMemo, forwardRef, useImperativeHandle } from 'react'\nimport { useInView } from 'react-intersection-observer'\nimport { useScrollContainer } from '../providers/ScrollContainerContext.js'\nimport { useChartConfig } from '../charts/lazyChartConfigRegistry.js'\nimport type { AnalyticsPortletProps } from '../types.js'\nimport { parsePortletQuery } from './analyticsPortlet/parsePortletQuery.js'\nimport { usePortletDrillState } from './analyticsPortlet/usePortletDrillState.js'\nimport { usePortletQueryResults } from './analyticsPortlet/usePortletQueryResults.js'\nimport { usePortletDebugData } from './analyticsPortlet/usePortletDebugData.js'\nimport { resolvePortletRenderKind } from './analyticsPortlet/portletRenderState.js'\nimport { PortletChartView } from './analyticsPortlet/PortletChartView.js'\nimport { PortletStateView } from './analyticsPortlet/PortletStates.js'\n\ninterface RefreshOptions {\n bustCache?: boolean\n}\n\ninterface AnalyticsPortletRef {\n refresh: (options?: RefreshOptions) => void\n}\n\n// Memoize component to prevent re-renders when props haven't changed\nconst AnalyticsPortlet = React.memo(forwardRef<AnalyticsPortletRef, AnalyticsPortletProps>(({\n query,\n chartType,\n chartConfig,\n displayConfig,\n dashboardFilters,\n dashboardFilterMapping,\n eagerLoad = false,\n isVisible: _isVisible, // Deprecated - visibility now handled internally via useInView\n height = 300,\n title: _title,\n colorPalette,\n loadingComponent,\n onDebugDataReady\n}, ref) => {\n // Lazy loading: Use IntersectionObserver to detect when portlet is visible\n // Get scroll container from context (null = viewport, element = container scroll)\n const scrollContainer = useScrollContainer()\n const { ref: inViewRef, inView } = useInView({\n root: scrollContainer,\n rootMargin: '500px', // Start loading 500px before entering viewport (about half screen)\n triggerOnce: true, // Once visible, stay \"visible\" (don't unload data)\n initialInView: false, // Start as not visible, let observer determine actual state\n skip: eagerLoad // Skip observation entirely if eagerLoad is true\n })\n\n // Effective visibility: eagerLoad forces visible, otherwise use inView from IntersectionObserver\n // Note: Batching is handled by BatchCoordinator which collects queries for 100ms before flushing\n const isVisible = eagerLoad || inView\n\n // Check if this chart type skips queries (using lazy-loaded config)\n const { config: chartTypeConfig } = useChartConfig(chartType)\n const shouldSkipQuery = chartTypeConfig.skipQuery === true\n\n // Memoize regular filters to prevent array recreation on every render\n const regularFilters = useMemo(() => {\n return dashboardFilters?.filter(df => !df.isUniversalTime)\n }, [dashboardFilters])\n\n // Parse query from JSON string, merge dashboard filters, and detect query type\n // Supports: CubeQuery, MultiQueryConfig, ServerFunnelQuery, ServerFlowQuery, and ServerRetentionQuery formats\n const { queryObject, multiQueryConfig, serverFunnelQuery, serverFlowQuery, serverRetentionQuery } = useMemo(\n () => parsePortletQuery({ query, shouldSkipQuery, regularFilters, dashboardFilters, dashboardFilterMapping }),\n [query, shouldSkipQuery, regularFilters, dashboardFilters, dashboardFilterMapping]\n )\n\n // Determine whether to skip queries based on various conditions\n const isMultiQuery = multiQueryConfig !== null\n // Funnel mode: ServerFunnelQuery format (dedicated funnel mode)\n // Note: Legacy mergeStrategy === 'funnel' is no longer supported\n const isFunnelMode = serverFunnelQuery !== null\n // Flow mode: ServerFlowQuery format (dedicated flow mode for Sankey charts)\n const isFlowMode = serverFlowQuery !== null\n // Retention mode: ServerRetentionQuery format (cohort retention analysis)\n const isRetentionMode = serverRetentionQuery !== null\n\n // Drill-down state, active query, and navigation handlers\n const { drill, activeQuery, handleNavigateBack, handleNavigateToLevel } = usePortletDrillState({\n queryObject,\n chartConfig,\n dashboardFilters,\n dashboardFilterMapping,\n isMultiQuery,\n isFunnelMode,\n isFlowMode,\n isRetentionMode\n })\n\n // Run all query hooks and derive combined loading/error/data + refresh/retry\n const {\n resultSet,\n isLoading,\n isFetching,\n error,\n multiQueryData,\n flowChartData,\n retentionChartData,\n funnelCacheInfo,\n flowCacheInfo,\n retentionCacheInfo,\n refresh,\n retry\n } = usePortletQueryResults({\n activeQuery,\n multiQueryConfig,\n serverFunnelQuery,\n serverFlowQuery,\n serverRetentionQuery,\n isMultiQuery,\n isFunnelMode,\n isFlowMode,\n isRetentionMode,\n shouldSkipQuery,\n eagerLoad,\n isVisible\n })\n\n // Expose refresh function through ref\n useImperativeHandle(ref, () => ({ refresh }), [refresh])\n\n // Send debug data to parent when ready\n usePortletDebugData({\n onDebugDataReady,\n error,\n chartType,\n chartConfig,\n displayConfig,\n isFunnelMode,\n isFlowMode,\n isRetentionMode,\n queryObject,\n activeQuery,\n serverFunnelQuery,\n serverFlowQuery,\n serverRetentionQuery,\n resultSet,\n multiQueryData,\n flowChartData,\n retentionChartData,\n funnelCacheInfo,\n flowCacheInfo,\n retentionCacheInfo,\n drillPath: drill.drillPath,\n currentChartConfig: drill.currentChartConfig\n })\n\n // Validate that chartConfig is provided when required (not required for skipQuery charts)\n // Check if any dropZones are mandatory for this chart type\n const hasMandatoryFields = !shouldSkipQuery && chartTypeConfig.dropZones.some(zone => zone.mandatory === true)\n\n // Decide which view to render based on the resolved query state\n const renderKind = resolvePortletRenderKind({\n hasChartConfig: !!chartConfig,\n hasMandatoryFields,\n shouldSkipQuery,\n eagerLoad,\n isVisible,\n isLoading,\n isFetching,\n error,\n isMultiQuery,\n isFunnelMode,\n isFlowMode,\n isRetentionMode,\n queryObject,\n multiQueryConfig,\n serverFunnelQuery,\n serverFlowQuery,\n serverRetentionQuery,\n resultSet,\n multiQueryData,\n flowChartData,\n retentionChartData\n })\n\n // Non-chart states (config-required, placeholder, loading, error, no-data)\n if (renderKind !== 'chart') {\n return (\n <PortletStateView\n kind={renderKind}\n inViewRef={inViewRef}\n height={height}\n loadingComponent={loadingComponent}\n error={(error as { message?: string; toString: () => string }) ?? null}\n onRetry={retry}\n activeQuery={activeQuery}\n query={query}\n chartType={chartType}\n chartConfig={drill.currentChartConfig || chartConfig}\n displayConfig={displayConfig}\n drillPath={drill.drillPath}\n onNavigateBack={handleNavigateBack}\n onNavigateToLevel={handleNavigateToLevel}\n />\n )\n }\n\n // Check if drill is enabled for this portlet (only single query mode)\n const isDrillEnabled = !isMultiQuery && !isFunnelMode && !isFlowMode && !isRetentionMode && drill.drillEnabled\n\n return (\n <div ref={inViewRef} className=\"dc:w-full dc:h-full dc:relative\">\n <PortletChartView\n title={_title}\n query={query}\n chartType={chartType}\n height={height}\n chartConfig={chartConfig}\n displayConfig={displayConfig}\n colorPalette={colorPalette}\n shouldSkipQuery={shouldSkipQuery}\n isMultiQuery={isMultiQuery}\n isFunnelMode={isFunnelMode}\n isFlowMode={isFlowMode}\n isRetentionMode={isRetentionMode}\n resultSet={resultSet}\n multiQueryData={multiQueryData}\n flowChartData={flowChartData}\n retentionChartData={retentionChartData}\n activeQuery={activeQuery}\n drill={drill}\n isDrillEnabled={isDrillEnabled}\n onNavigateBack={handleNavigateBack}\n onNavigateToLevel={handleNavigateToLevel}\n />\n </div>\n )\n}))\n\nAnalyticsPortlet.displayName = 'AnalyticsPortlet'\n\nexport default AnalyticsPortlet\n","/**\n * Dashboard Zustand Store (Instance-based)\n *\n * Centralized state management for Dashboard components, consolidating:\n * - Edit mode state (isEditMode, selectedFilterId)\n * - Modal state (portlet edit, analysis, filter config)\n * - Layout state (draftRows, drag state, initialization)\n * - Debug data (per-portlet debug information)\n *\n * KEY ARCHITECTURE: Instance-based stores\n * - Each Dashboard gets its own store instance via Context\n * - No localStorage persistence (dashboard state is transient)\n * - State is per-dashboard session, not persisted\n *\n * Uses Zustand's createStore (factory) instead of create (singleton).\n * Store is provided via React Context.\n */\n\nimport { createContext, useContext, useRef, type ReactNode } from 'react'\nimport { createStore, useStore, type StoreApi } from 'zustand'\nimport { devtools, subscribeWithSelector } from 'zustand/middleware'\nimport type { LayoutItem } from 'react-grid-layout'\nimport type {\n PortletConfig,\n RowLayout,\n ChartType,\n ChartAxisConfig,\n ChartDisplayConfig,\n CubeQuery,\n} from '../types.js'\nimport type { FlowChartData } from '../types/flow.js'\nimport type { RetentionChartData } from '../types/retention.js'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Debug data entry for a portlet (used by chart inspector)\n */\nexport interface PortletDebugDataEntry {\n chartConfig: ChartAxisConfig\n displayConfig: ChartDisplayConfig\n queryObject: CubeQuery | null\n data: unknown[] | FlowChartData | RetentionChartData\n chartType: ChartType\n cacheInfo?: {\n hit: boolean\n cachedAt: string\n ttlMs: number\n ttlRemainingMs: number\n } | null\n}\n\n/**\n * @deprecated Use PortletDebugDataEntry instead. Kept for backward compatibility.\n */\nexport type DebugDataEntry = PortletDebugDataEntry\n\n/**\n * Drag state for row-based layout\n */\nexport interface DragState {\n rowIndex: number\n colIndex: number\n portletId: string\n}\n\n/**\n * Complete store state interface\n */\nexport interface DashboardStoreState {\n // =========================================================================\n // Edit Mode State\n // =========================================================================\n /** Whether dashboard is in edit mode */\n isEditMode: boolean\n /** Currently selected filter ID for filter-assignment mode */\n selectedFilterId: string | null\n\n // =========================================================================\n // Modal State\n // =========================================================================\n /** Whether portlet edit modal is open */\n isPortletModalOpen: boolean\n /** Portlet being edited (null = adding new) */\n editingPortlet: PortletConfig | null\n /** Whether filter config modal is open */\n isFilterConfigModalOpen: boolean\n /** Portlet for filter configuration */\n filterConfigPortlet: PortletConfig | null\n /** Portlet ID pending delete confirmation (null = no confirmation active) */\n deleteConfirmPortletId: string | null\n /** Whether text portlet modal is open */\n isTextModalOpen: boolean\n /** Portlet being edited in text modal (null = adding new) */\n editingTextPortlet: PortletConfig | null\n\n // =========================================================================\n // Layout State\n // =========================================================================\n /** Draft row layout during drag operations (rows mode) */\n draftRows: RowLayout[] | null\n /** Whether a portlet is currently being dragged */\n isDraggingPortlet: boolean\n /** Last known layout for change detection */\n lastKnownLayout: LayoutItem[]\n /** Whether component has been initialized (prevents saves during load) */\n isInitialized: boolean\n /** Current drag state for row-based layout */\n dragState: DragState | null\n\n // =========================================================================\n // Debug Data\n // =========================================================================\n /** Debug data keyed by portlet ID (for chart inspector) */\n debugData: Record<string, DebugDataEntry>\n\n // =========================================================================\n // Thumbnail State\n // =========================================================================\n /** Whether the dashboard has been modified and needs a new thumbnail */\n thumbnailDirty: boolean\n}\n\n/**\n * Store actions interface\n */\nexport interface DashboardStoreActions {\n // =========================================================================\n // Edit Mode Actions\n // =========================================================================\n /** Set edit mode */\n setEditMode: (isEdit: boolean) => void\n /** Toggle edit mode */\n toggleEditMode: () => void\n /** Set selected filter ID for filter selection mode */\n setSelectedFilterId: (filterId: string | null) => void\n /** Exit filter selection mode */\n exitFilterSelectionMode: () => void\n\n // =========================================================================\n // Modal Actions\n // =========================================================================\n /** Open portlet modal (optionally with a portlet to edit) */\n openPortletModal: (portlet?: PortletConfig | null) => void\n /** Close portlet modal */\n closePortletModal: () => void\n /** Open filter config modal for a portlet */\n openFilterConfigModal: (portlet: PortletConfig) => void\n /** Close filter config modal */\n closeFilterConfigModal: () => void\n /** Open text portlet modal (optionally with a portlet to edit) */\n openTextModal: (portlet?: PortletConfig | null) => void\n /** Close text portlet modal */\n closeTextModal: () => void\n /** Open delete confirmation for a portlet */\n openDeleteConfirm: (portletId: string) => void\n /** Close delete confirmation */\n closeDeleteConfirm: () => void\n\n // =========================================================================\n // Layout Actions\n // =========================================================================\n /** Set draft rows during drag operations */\n setDraftRows: (rows: RowLayout[] | null) => void\n /** Set whether a portlet is being dragged */\n setIsDraggingPortlet: (isDragging: boolean) => void\n /** Set last known layout for change detection */\n setLastKnownLayout: (layout: LayoutItem[]) => void\n /** Set whether component is initialized */\n setIsInitialized: (initialized: boolean) => void\n /** Set drag state */\n setDragState: (state: DragState | null) => void\n /** Clear drag state */\n clearDragState: () => void\n\n // =========================================================================\n // Debug Data Actions\n // =========================================================================\n /** Set debug data for a portlet */\n setDebugData: (portletId: string, data: DebugDataEntry) => void\n /** Clear debug data for a portlet (or all if no portletId) */\n clearDebugData: (portletId?: string) => void\n\n // =========================================================================\n // Thumbnail Actions\n // =========================================================================\n /** Set thumbnail dirty state (needs new capture) */\n setThumbnailDirty: (dirty: boolean) => void\n\n // =========================================================================\n // Utility Actions\n // =========================================================================\n /** Reset store to initial state */\n reset: () => void\n}\n\n/**\n * Combined store type\n */\nexport type DashboardStore = DashboardStoreState & DashboardStoreActions\n\n/**\n * Options for creating a store instance\n */\nexport interface CreateDashboardStoreOptions {\n /** Initial edit mode state */\n initialEditMode?: boolean\n}\n\n// ============================================================================\n// Initial State\n// ============================================================================\n\nconst createDefaultState = (): DashboardStoreState => ({\n // Edit mode\n isEditMode: false,\n selectedFilterId: null,\n\n // Modal state\n isPortletModalOpen: false,\n editingPortlet: null,\n isFilterConfigModalOpen: false,\n filterConfigPortlet: null,\n deleteConfirmPortletId: null,\n isTextModalOpen: false,\n editingTextPortlet: null,\n\n // Layout state\n draftRows: null,\n isDraggingPortlet: false,\n lastKnownLayout: [],\n isInitialized: false,\n dragState: null,\n\n // Debug data\n debugData: {},\n\n // Thumbnail state\n thumbnailDirty: false,\n})\n\n/**\n * Build initial state from options\n */\nfunction buildInitialState(options: CreateDashboardStoreOptions): DashboardStoreState {\n const defaultState = createDefaultState()\n\n return {\n ...defaultState,\n isEditMode: options.initialEditMode ?? false,\n }\n}\n\n// ============================================================================\n// Store Factory\n// ============================================================================\n\n/**\n * Create store actions\n */\nfunction createStoreActions(\n set: (\n partial:\n | Partial<DashboardStore>\n | ((state: DashboardStore) => Partial<DashboardStore>)\n ) => void,\n _get: () => DashboardStore,\n initialState: DashboardStoreState\n): DashboardStoreActions {\n return {\n // =================================================================\n // Edit Mode Actions\n // =================================================================\n\n setEditMode: (isEdit) =>\n set({\n isEditMode: isEdit,\n // Exit filter selection mode when leaving edit mode\n selectedFilterId: isEdit ? null : null,\n }),\n\n toggleEditMode: () =>\n set((state) => ({\n isEditMode: !state.isEditMode,\n // Exit filter selection mode when toggling\n selectedFilterId: null,\n })),\n\n setSelectedFilterId: (filterId) => set({ selectedFilterId: filterId }),\n\n exitFilterSelectionMode: () => set({ selectedFilterId: null }),\n\n // =================================================================\n // Modal Actions\n // =================================================================\n\n openPortletModal: (portlet) =>\n set({\n isPortletModalOpen: true,\n editingPortlet: portlet ?? null,\n }),\n\n closePortletModal: () =>\n set({\n isPortletModalOpen: false,\n editingPortlet: null,\n }),\n\n openFilterConfigModal: (portlet) =>\n set({\n isFilterConfigModalOpen: true,\n filterConfigPortlet: portlet,\n }),\n\n closeFilterConfigModal: () =>\n set({\n isFilterConfigModalOpen: false,\n filterConfigPortlet: null,\n }),\n\n openTextModal: (portlet) =>\n set({\n isTextModalOpen: true,\n editingTextPortlet: portlet ?? null,\n }),\n\n closeTextModal: () =>\n set({\n isTextModalOpen: false,\n editingTextPortlet: null,\n }),\n\n openDeleteConfirm: (portletId: string) =>\n set({ deleteConfirmPortletId: portletId }),\n\n closeDeleteConfirm: () =>\n set({ deleteConfirmPortletId: null }),\n\n // =================================================================\n // Layout Actions\n // =================================================================\n\n setDraftRows: (rows) => set({ draftRows: rows }),\n\n setIsDraggingPortlet: (isDragging) => set({ isDraggingPortlet: isDragging }),\n\n setLastKnownLayout: (layout) => set({ lastKnownLayout: layout }),\n\n setIsInitialized: (initialized) => set({ isInitialized: initialized }),\n\n setDragState: (state) => set({ dragState: state }),\n\n clearDragState: () =>\n set({\n dragState: null,\n isDraggingPortlet: false,\n }),\n\n // =================================================================\n // Debug Data Actions\n // =================================================================\n\n setDebugData: (portletId, data) =>\n set((state) => ({\n debugData: {\n ...state.debugData,\n [portletId]: data,\n },\n })),\n\n clearDebugData: (portletId) =>\n set((state) => {\n if (portletId) {\n const { [portletId]: _, ...rest } = state.debugData\n return { debugData: rest }\n }\n return { debugData: {} }\n }),\n\n // =================================================================\n // Thumbnail Actions\n // =================================================================\n\n setThumbnailDirty: (dirty) => set({ thumbnailDirty: dirty }),\n\n // =================================================================\n // Utility Actions\n // =================================================================\n\n reset: () => set(initialState),\n }\n}\n\n/**\n * Create a new dashboard store instance\n */\nexport function createDashboardStore(options: CreateDashboardStoreOptions = {}) {\n const initialState = buildInitialState(options)\n\n // No localStorage persistence - dashboard state is transient\n return createStore<DashboardStore>()(\n devtools(\n subscribeWithSelector((set, get) => ({\n ...initialState,\n ...createStoreActions(set, get, initialState),\n })),\n { name: 'DashboardStore' }\n )\n )\n}\n\n/**\n * Fallback store for useDashboardStoreOptional - created lazily at module level\n * This allows the hook to always call useStore unconditionally (React hooks rules)\n */\nlet fallbackStore: StoreApi<DashboardStore> | null = null\nfunction getFallbackStore(): StoreApi<DashboardStore> {\n if (!fallbackStore) {\n fallbackStore = createDashboardStore()\n }\n return fallbackStore\n}\n\n// ============================================================================\n// React Context & Provider\n// ============================================================================\n\n/**\n * Context for the store instance\n */\nconst DashboardStoreContext = createContext<StoreApi<DashboardStore> | null>(null)\n\n/**\n * Provider props\n */\nexport interface DashboardStoreProviderProps {\n children: ReactNode\n /** Initial edit mode state */\n initialEditMode?: boolean\n}\n\n/**\n * Provider component that creates a store instance per Dashboard\n */\nexport function DashboardStoreProvider({\n children,\n initialEditMode,\n}: DashboardStoreProviderProps) {\n // Create store instance once per provider mount\n const storeRef = useRef<StoreApi<DashboardStore> | null>(null)\n\n if (!storeRef.current) {\n storeRef.current = createDashboardStore({\n initialEditMode,\n })\n }\n\n return (\n <DashboardStoreContext.Provider value={storeRef.current}>\n {children}\n </DashboardStoreContext.Provider>\n )\n}\n\n/**\n * Hook to access the store from context\n * @throws Error if used outside of provider\n */\nexport function useDashboardStore<T>(selector: (state: DashboardStore) => T): T {\n const store = useContext(DashboardStoreContext)\n if (!store) {\n throw new Error('useDashboardStore must be used within DashboardStoreProvider')\n }\n return useStore(store, selector)\n}\n\n/**\n * Hook to get the raw store API (for actions that need direct access)\n */\nexport function useDashboardStoreApi(): StoreApi<DashboardStore> {\n const store = useContext(DashboardStoreContext)\n if (!store) {\n throw new Error('useDashboardStoreApi must be used within DashboardStoreProvider')\n }\n return store\n}\n\n/**\n * Optional hook that returns null if used outside provider (for optional store access)\n * Uses a fallback store to ensure useStore is always called (React hooks rules compliance)\n */\nexport function useDashboardStoreOptional<T>(\n selector: (state: DashboardStore) => T\n): T | null {\n const contextStore = useContext(DashboardStoreContext)\n // Always call useStore unconditionally (React hooks rules)\n // Use fallback store when context is null to maintain hook order\n const result = useStore(contextStore ?? getFallbackStore(), selector)\n // Return null if there was no real store (using fallback)\n return contextStore ? result : null\n}\n\n// ============================================================================\n// Selectors (for optimized re-renders)\n// ============================================================================\n\n/**\n * Select edit mode state\n */\nexport const selectEditModeState = (state: DashboardStore) => ({\n isEditMode: state.isEditMode,\n selectedFilterId: state.selectedFilterId,\n})\n\n/**\n * Select modal state\n */\nexport const selectModalState = (state: DashboardStore) => ({\n isPortletModalOpen: state.isPortletModalOpen,\n editingPortlet: state.editingPortlet,\n isFilterConfigModalOpen: state.isFilterConfigModalOpen,\n filterConfigPortlet: state.filterConfigPortlet,\n isTextModalOpen: state.isTextModalOpen,\n editingTextPortlet: state.editingTextPortlet,\n})\n\n/**\n * Select layout state\n */\nexport const selectLayoutState = (state: DashboardStore) => ({\n draftRows: state.draftRows,\n isDraggingPortlet: state.isDraggingPortlet,\n lastKnownLayout: state.lastKnownLayout,\n isInitialized: state.isInitialized,\n dragState: state.dragState,\n})\n\n/**\n * Select debug data\n */\nexport const selectDebugData = (state: DashboardStore) => state.debugData\n\n/**\n * Select debug data for a specific portlet\n */\nexport const selectPortletDebugData = (portletId: string) => (state: DashboardStore) =>\n state.debugData[portletId]\n\n/**\n * Select all edit mode actions\n */\nexport const selectEditModeActions = (state: DashboardStore) => ({\n setEditMode: state.setEditMode,\n toggleEditMode: state.toggleEditMode,\n setSelectedFilterId: state.setSelectedFilterId,\n exitFilterSelectionMode: state.exitFilterSelectionMode,\n})\n\n/**\n * Select all modal actions\n */\nexport const selectModalActions = (state: DashboardStore) => ({\n openPortletModal: state.openPortletModal,\n closePortletModal: state.closePortletModal,\n openTextModal: state.openTextModal,\n closeTextModal: state.closeTextModal,\n openFilterConfigModal: state.openFilterConfigModal,\n closeFilterConfigModal: state.closeFilterConfigModal,\n openDeleteConfirm: state.openDeleteConfirm,\n closeDeleteConfirm: state.closeDeleteConfirm,\n})\n\n/**\n * Select all layout actions\n */\nexport const selectLayoutActions = (state: DashboardStore) => ({\n setDraftRows: state.setDraftRows,\n setIsDraggingPortlet: state.setIsDraggingPortlet,\n setLastKnownLayout: state.setLastKnownLayout,\n setIsInitialized: state.setIsInitialized,\n setDragState: state.setDragState,\n clearDragState: state.clearDragState,\n})\n\n/**\n * Select all debug data actions\n */\nexport const selectDebugDataActions = (state: DashboardStore) => ({\n setDebugData: state.setDebugData,\n clearDebugData: state.clearDebugData,\n})\n\n/**\n * Select thumbnail dirty state\n */\nexport const selectThumbnailDirty = (state: DashboardStore) => state.thumbnailDirty\n\n/**\n * Select all actions\n */\nexport const selectAllActions = (state: DashboardStore) => ({\n // Edit mode\n setEditMode: state.setEditMode,\n toggleEditMode: state.toggleEditMode,\n setSelectedFilterId: state.setSelectedFilterId,\n exitFilterSelectionMode: state.exitFilterSelectionMode,\n // Modals\n openPortletModal: state.openPortletModal,\n closePortletModal: state.closePortletModal,\n openTextModal: state.openTextModal,\n closeTextModal: state.closeTextModal,\n openFilterConfigModal: state.openFilterConfigModal,\n closeFilterConfigModal: state.closeFilterConfigModal,\n openDeleteConfirm: state.openDeleteConfirm,\n closeDeleteConfirm: state.closeDeleteConfirm,\n // Layout\n setDraftRows: state.setDraftRows,\n setIsDraggingPortlet: state.setIsDraggingPortlet,\n setLastKnownLayout: state.setLastKnownLayout,\n setIsInitialized: state.setIsInitialized,\n setDragState: state.setDragState,\n clearDragState: state.clearDragState,\n // Debug data\n setDebugData: state.setDebugData,\n clearDebugData: state.clearDebugData,\n // Thumbnail\n setThumbnailDirty: state.setThumbnailDirty,\n // Utility\n reset: state.reset,\n})\n","/**\n * useScrollDetection - Debounced Scroll Detection Hook\n *\n * Detects when a container has been scrolled past a threshold with debouncing\n * to prevent excessive state updates.\n *\n * This fixes the issue where scroll detection was listening to window.pageYOffset\n * instead of the actual scroll container (overflow-y-auto div in Layout).\n */\n\nimport { useEffect, useState, useRef, type RefObject } from 'react'\n\ninterface UseScrollDetectionOptions {\n /** Scroll threshold in pixels (default: 20) */\n threshold?: number\n /** Debounce delay in milliseconds (default: 150) */\n debounceMs?: number\n /** Optional container state to trigger re-initialization when found */\n container?: HTMLElement | null\n}\n\n/**\n * Hook to detect scroll position in a container\n *\n * @param containerRef - Ref to the scrollable container element\n * @param options - Configuration options for threshold and debounce\n * @returns Boolean indicating if scrolled past threshold\n *\n * @example\n * const scrollContainerRef = useRef<HTMLDivElement>(null)\n * const isScrolled = useScrollDetection(scrollContainerRef, {\n * threshold: 20,\n * debounceMs: 150\n * })\n *\n * <div ref={scrollContainerRef} className=\"overflow-y-auto\">\n * {isScrolled && <div>Shadow visible</div>}\n * </div>\n */\nexport function useScrollDetection(\n containerRef: RefObject<HTMLElement | null>,\n { threshold = 20, debounceMs = 150, container }: UseScrollDetectionOptions = {}\n) {\n const [isScrolled, setIsScrolled] = useState(false)\n const timeoutRef = useRef<number>()\n\n useEffect(() => {\n // Note: containerRef is intentionally not in deps - refs are stable and we access .current inside\n const scrollContainer = containerRef.current\n if (!scrollContainer) return\n\n const handleScroll = () => {\n // Clear existing timeout\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n\n // Debounce scroll updates\n timeoutRef.current = window.setTimeout(() => {\n const scrollTop = scrollContainer.scrollTop\n const shouldBeScrolled = scrollTop > threshold\n\n // Only update state if value actually changed\n setIsScrolled(prev => prev !== shouldBeScrolled ? shouldBeScrolled : prev)\n }, debounceMs)\n }\n\n // Attach scroll listener to actual container (not window!)\n scrollContainer.addEventListener('scroll', handleScroll, { passive: true })\n\n // Initial check\n handleScroll()\n\n // Cleanup\n return () => {\n scrollContainer.removeEventListener('scroll', handleScroll)\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [threshold, debounceMs, container]) // containerRef is stable, container triggers re-init\n\n return isScrolled\n}\n","/**\n * useElementVisibility - Detects when an element scrolls out of view\n *\n * Used to detect when the static edit bar scrolls out of view, triggering\n * the floating toolbar to appear. Works with both viewport and custom\n * scroll containers.\n */\n\nimport { useEffect, useState, useRef, type RefObject } from 'react'\n\ninterface UseElementVisibilityOptions {\n /** Threshold in pixels - element considered out of view when this much scrolls past top */\n threshold?: number\n /** Debounce delay in milliseconds */\n debounceMs?: number\n /** Custom scroll container ref (uses viewport if not provided) */\n containerRef?: RefObject<HTMLElement | null>\n /** Optional state value to trigger re-initialization when container is found */\n container?: HTMLElement | null\n}\n\n/**\n * Hook to detect whether an element is visible in the viewport/container\n *\n * @param elementRef - Ref to the element to track\n * @param options - Configuration options\n * @returns Boolean indicating if the element is visible (true when in view, false when scrolled out)\n *\n * @example\n * const editBarRef = useRef<HTMLDivElement>(null)\n * const isEditBarVisible = useElementVisibility(editBarRef, {\n * threshold: 80,\n * containerRef: scrollContainerRef\n * })\n *\n * // Show floating toolbar when edit bar scrolls out of view\n * {!isEditBarVisible && <FloatingToolbar />}\n */\nexport function useElementVisibility(\n elementRef: RefObject<HTMLElement | null>,\n { threshold = 80, debounceMs = 100, containerRef, container }: UseElementVisibilityOptions = {}\n): boolean {\n // Start with visible=true to prevent flash on initial render\n const [isVisible, setIsVisible] = useState(true)\n const timeoutRef = useRef<number>()\n // Track if we've ever seen the element visible (prevents animation on load)\n const hasBeenVisibleRef = useRef(false)\n\n useEffect(() => {\n const container = containerRef?.current\n\n const checkVisibility = () => {\n const element = elementRef.current\n // If element not yet mounted, stay visible (don't show floating toolbar)\n if (!element) return\n\n // Clear existing timeout for debouncing\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n\n timeoutRef.current = window.setTimeout(() => {\n const elementRect = element.getBoundingClientRect()\n\n if (container) {\n // Check against scroll container\n const containerRect = container.getBoundingClientRect()\n // Element is \"visible\" when its bottom is below the container's top by at least threshold\n const visible = elementRect.bottom > containerRect.top + threshold\n\n // Track that we've seen the element visible at least once\n if (visible) {\n hasBeenVisibleRef.current = true\n }\n\n // Only update state if value changed\n setIsVisible(prev => prev !== visible ? visible : prev)\n } else {\n // Check against viewport (window)\n // Element is \"visible\" when its bottom is in the viewport (plus threshold buffer)\n const visible = elementRect.bottom > threshold\n\n // Track that we've seen the element visible at least once\n if (visible) {\n hasBeenVisibleRef.current = true\n }\n\n // Only update state if value changed\n setIsVisible(prev => prev !== visible ? visible : prev)\n }\n }, debounceMs)\n }\n\n // Attach scroll listener to container or window\n const scrollTarget = container || window\n scrollTarget.addEventListener('scroll', checkVisibility, { passive: true })\n\n // Also listen for resize events\n window.addEventListener('resize', checkVisibility, { passive: true })\n\n // Initial check\n checkVisibility()\n\n // Deferred re-check after React render cycle completes\n // This handles the case where elementRef.current isn't set yet on first render\n const rafId = requestAnimationFrame(() => {\n checkVisibility()\n })\n\n // Cleanup\n return () => {\n scrollTarget.removeEventListener('scroll', checkVisibility)\n window.removeEventListener('resize', checkVisibility)\n cancelAnimationFrame(rafId)\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current)\n }\n }\n }, [elementRef, containerRef, threshold, debounceMs, container])\n\n return isVisible\n}\n","import { useEffect, useRef, useCallback, type RefObject } from 'react'\n\ninterface UseDragAutoScrollOptions {\n /** Distance from edge that triggers scrolling (default: 80px) */\n edgeThreshold?: number\n /** Maximum scroll speed in pixels per frame (default: 15) */\n maxScrollSpeed?: number\n /** Whether auto-scroll is currently enabled */\n enabled?: boolean\n}\n\n/**\n * Hook to enable auto-scrolling when dragging near the edges of a scroll container.\n * Works with HTML5 native drag-and-drop API by listening to dragover events.\n *\n * @param scrollContainerRef - Ref to the scrollable container element\n * @param options - Configuration options\n */\nexport function useDragAutoScroll(\n scrollContainerRef: RefObject<HTMLElement | null>,\n options: UseDragAutoScrollOptions = {}\n) {\n const {\n edgeThreshold = 80,\n maxScrollSpeed = 15,\n enabled = true\n } = options\n\n const animationFrameRef = useRef<number | null>(null)\n const scrollDirectionRef = useRef<'up' | 'down' | null>(null)\n const scrollIntensityRef = useRef<number>(0)\n\n // Calculate scroll speed based on proximity to edge\n const calculateScrollSpeed = useCallback((distanceFromEdge: number): number => {\n // Closer to edge = faster scrolling (exponential curve for smoother feel)\n const normalizedDistance = Math.max(0, Math.min(1, 1 - distanceFromEdge / edgeThreshold))\n return Math.round(normalizedDistance * normalizedDistance * maxScrollSpeed)\n }, [edgeThreshold, maxScrollSpeed])\n\n // Animation frame loop for smooth scrolling\n const scrollLoop = useCallback(() => {\n const container = scrollContainerRef.current\n if (!container || !scrollDirectionRef.current) {\n animationFrameRef.current = null\n return\n }\n\n const speed = scrollIntensityRef.current\n if (speed > 0) {\n const scrollAmount = scrollDirectionRef.current === 'up' ? -speed : speed\n container.scrollTop += scrollAmount\n }\n\n // Continue the loop while dragging\n animationFrameRef.current = requestAnimationFrame(scrollLoop)\n }, [scrollContainerRef])\n\n // Start the scroll animation\n const startScrolling = useCallback((direction: 'up' | 'down', intensity: number) => {\n scrollDirectionRef.current = direction\n scrollIntensityRef.current = intensity\n\n if (animationFrameRef.current === null) {\n animationFrameRef.current = requestAnimationFrame(scrollLoop)\n }\n }, [scrollLoop])\n\n // Stop the scroll animation\n const stopScrolling = useCallback(() => {\n scrollDirectionRef.current = null\n scrollIntensityRef.current = 0\n\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current)\n animationFrameRef.current = null\n }\n }, [])\n\n // Handle dragover events\n const handleDragOver = useCallback((event: DragEvent) => {\n const container = scrollContainerRef.current\n if (!container) return\n\n // Get container bounds\n const containerRect = container.getBoundingClientRect()\n const mouseY = event.clientY\n\n // Check if mouse is within the container's horizontal bounds\n if (event.clientX < containerRect.left || event.clientX > containerRect.right) {\n stopScrolling()\n return\n }\n\n // Calculate distance from edges\n const distanceFromTop = mouseY - containerRect.top\n const distanceFromBottom = containerRect.bottom - mouseY\n\n // Check if we should scroll\n if (distanceFromTop < edgeThreshold && container.scrollTop > 0) {\n // Near top edge - scroll up\n const speed = calculateScrollSpeed(distanceFromTop)\n startScrolling('up', speed)\n } else if (distanceFromBottom < edgeThreshold && container.scrollTop < container.scrollHeight - container.clientHeight) {\n // Near bottom edge - scroll down\n const speed = calculateScrollSpeed(distanceFromBottom)\n startScrolling('down', speed)\n } else {\n // Not near any edge - stop scrolling\n stopScrolling()\n }\n }, [scrollContainerRef, edgeThreshold, calculateScrollSpeed, startScrolling, stopScrolling])\n\n // Handle drag end - stop all scrolling\n const handleDragEnd = useCallback(() => {\n stopScrolling()\n }, [stopScrolling])\n\n // Setup event listeners\n useEffect(() => {\n if (!enabled) {\n stopScrolling()\n return\n }\n\n // Use capture phase to catch drag events before they're handled by other elements\n document.addEventListener('dragover', handleDragOver, { capture: true })\n document.addEventListener('dragend', handleDragEnd)\n document.addEventListener('drop', handleDragEnd)\n\n return () => {\n document.removeEventListener('dragover', handleDragOver, { capture: true })\n document.removeEventListener('dragend', handleDragEnd)\n document.removeEventListener('drop', handleDragEnd)\n stopScrolling()\n }\n }, [enabled, handleDragOver, handleDragEnd, stopScrolling])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current)\n }\n }\n }, [])\n}\n","/**\n * Memoization comparison helpers for DashboardPortletCard. Extracted so the\n * card module focuses on rendering.\n */\n\nimport type { HTMLAttributes, ReactNode, ComponentType, CSSProperties } from 'react'\nimport type { ChartType, DashboardFilter, DashboardLayoutMode, PortletConfig } from '../../types.js'\nimport type { ColorPalette } from '../../utils/colorPalettes.js'\n\n// Shape mirrors DashboardPortletCardProps; kept here to type the comparators.\nexport interface DashboardPortletCardProps {\n portlet: PortletConfig\n editable: boolean\n layoutMode?: DashboardLayoutMode\n dashboardFilters?: DashboardFilter[]\n configEagerLoad?: boolean\n loadingComponent?: ReactNode\n colorPalette?: ColorPalette\n containerProps?: HTMLAttributes<HTMLDivElement>\n headerProps?: HTMLAttributes<HTMLDivElement>\n setPortletRef: (portletId: string, element: HTMLDivElement | null) => void\n setPortletComponentRef: (portletId: string, element: { refresh: (options?: { bustCache?: boolean }) => void } | null) => void\n callbacks: {\n onToggleFilter: (portletId: string, filterId: string) => void\n onRefresh: (portletId: string, options?: { bustCache?: boolean }) => void\n onDuplicate: (portletId: string) => void\n onEdit: (portlet: PortletConfig) => void\n onDelete: (portletId: string) => void\n onOpenFilterConfig: (portlet: PortletConfig) => void\n }\n icons: {\n RefreshIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n EditIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n DeleteIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n CopyIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n FilterIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n }\n}\n\nexport type { ChartType }\n\n// Shallow comparison for objects\nexport function shallowEqualObjects<T extends object>(\n a: T | undefined,\n b: T | undefined\n): boolean {\n if (a === b) return true\n if (!a || !b) return a === b\n\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n\n if (keysA.length !== keysB.length) return false\n\n for (const key of keysA) {\n if ((a as Record<string, unknown>)[key] !== (b as Record<string, unknown>)[key]) return false\n }\n\n return true\n}\n\nconst SCALAR_KEYS = ['editable', 'layoutMode', 'configEagerLoad'] as const\nconst REF_KEYS = [\n 'portlet', 'dashboardFilters', 'colorPalette', 'loadingComponent', 'callbacks', 'icons',\n 'setPortletRef', 'setPortletComponentRef'\n] as const\n\n// Custom comparison function to handle containerProps/headerProps object recreation\nexport function arePropsEqual(\n prevProps: DashboardPortletCardProps,\n nextProps: DashboardPortletCardProps\n): boolean {\n // Fast path: if object references are the same, props are equal\n if (prevProps === nextProps) return true\n\n const prev = prevProps as unknown as Record<string, unknown>\n const next = nextProps as unknown as Record<string, unknown>\n\n // Check scalar + reference props (all by ===)\n for (const key of [...SCALAR_KEYS, ...REF_KEYS]) {\n if (prev[key] !== next[key]) return false\n }\n\n // Special handling for containerProps and headerProps - compare properties shallowly\n return (\n shallowEqualObjects(prevProps.containerProps, nextProps.containerProps) &&\n shallowEqualObjects(prevProps.headerProps, nextProps.headerProps)\n )\n}\n","/**\n * Resolves which field a selected dashboard filter targets on a given portlet.\n * Extracted from DashboardPortletCard's memo.\n */\n\nimport type { DashboardFilter, PortletConfig } from '../../types.js'\nimport { getMappingMemberOverride } from '../../utils/filterUtils.js'\n\nexport interface EffectiveFilterField {\n field: string\n isOverride: boolean\n}\n\nexport function resolveEffectiveFilterField(params: {\n isInSelectionMode: boolean\n hasSelectedFilter: boolean\n selectedFilterId: string | null | undefined\n dashboardFilters?: DashboardFilter[]\n dashboardFilterMapping: PortletConfig['dashboardFilterMapping']\n}): EffectiveFilterField | null {\n const { isInSelectionMode, hasSelectedFilter, selectedFilterId, dashboardFilters, dashboardFilterMapping } = params\n\n if (!isInSelectionMode || !hasSelectedFilter || !selectedFilterId) return null\n\n const selectedFilter = dashboardFilters?.find(f => f.id === selectedFilterId)\n if (!selectedFilter || selectedFilter.isUniversalTime) return null\n\n const override = getMappingMemberOverride(dashboardFilterMapping, selectedFilterId)\n if (override) return { field: override, isOverride: true }\n\n if ('member' in selectedFilter.filter && selectedFilter.filter.member) {\n return { field: selectedFilter.filter.member, isOverride: false }\n }\n return null\n}\n","/**\n * Copy-to-clipboard, XLSX export, and refresh shift-key feedback state for\n * DashboardPortletCard. Extracted to keep the card component flat.\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport { useCubeFeatures } from '../../providers/CubeFeaturesProvider.js'\nimport { isPortletCopyAvailable, copyPortletToClipboard } from '../../utils/thumbnail.js'\nimport { isExportAvailable, exportPortletToXlsx } from '../../utils/exportXlsx.js'\nimport type { PortletDebugDataEntry } from '../../stores/dashboardStore.js'\n\nexport function usePortletCardActions(params: {\n portletTitle: string\n debugData?: PortletDebugDataEntry\n}) {\n const { portletTitle, debugData } = params\n const { features } = useCubeFeatures()\n\n // State and ref for copy-to-clipboard functionality\n const [copySuccess, setCopySuccess] = useState(false)\n const [copyAvailable, setCopyAvailable] = useState(false)\n const chartContainerRef = useRef<HTMLDivElement | null>(null)\n\n // State for XLSX export\n const [xlsExportAvailable, setXlsExportAvailable] = useState(false)\n const [exportInProgress, setExportInProgress] = useState(false)\n\n // Track shift key + hover state for cache bust visual feedback on refresh button\n const [isShiftHeld, setIsShiftHeld] = useState(false)\n const [isHoveringRefresh, setIsHoveringRefresh] = useState(false)\n\n // Listen for shift key up/down to show visual feedback on refresh button (only when hovering)\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Shift') setIsShiftHeld(true)\n }\n const handleKeyUp = (e: KeyboardEvent) => {\n if (e.key === 'Shift') setIsShiftHeld(false)\n }\n window.addEventListener('keydown', handleKeyDown)\n window.addEventListener('keyup', handleKeyUp)\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n window.removeEventListener('keyup', handleKeyUp)\n }\n }, [])\n\n // Check if copy-to-clipboard capability is available on mount\n useEffect(() => {\n if (features.thumbnail?.enabled) {\n isPortletCopyAvailable().then(setCopyAvailable)\n } else {\n setCopyAvailable(false)\n }\n }, [features.thumbnail?.enabled])\n\n // Check if XLSX export is available on mount\n useEffect(() => {\n if (features.xlsExport?.enabled) {\n isExportAvailable().then(setXlsExportAvailable)\n } else {\n setXlsExportAvailable(false)\n }\n }, [features.xlsExport?.enabled])\n\n // Handler for XLSX export\n const handleExportXlsx = useCallback(async (event: React.MouseEvent | React.TouchEvent) => {\n event.stopPropagation()\n if (!debugData || exportInProgress) return\n setExportInProgress(true)\n try {\n await exportPortletToXlsx(portletTitle || 'export', debugData)\n } finally {\n setExportInProgress(false)\n }\n }, [debugData, exportInProgress, portletTitle])\n\n // Handler for copy-to-clipboard\n const handleCopyToClipboard = useCallback(async (event: React.MouseEvent | React.TouchEvent) => {\n event.stopPropagation()\n if (!chartContainerRef.current) return\n\n const success = await copyPortletToClipboard(chartContainerRef.current)\n if (success) {\n setCopySuccess(true)\n setTimeout(() => setCopySuccess(false), 2000)\n }\n }, [])\n\n // Show warning styling only when hovering AND shift is held\n const showCacheBustIndicator = isShiftHeld && isHoveringRefresh\n\n return {\n chartContainerRef,\n copySuccess,\n copyAvailable,\n xlsExportAvailable,\n exportInProgress,\n showCacheBustIndicator,\n setIsHoveringRefresh,\n handleExportXlsx,\n handleCopyToClipboard\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","import { useState, useEffect } from 'react'\nimport type { FlowChartData } from '../types/flow.js'\nimport type { RetentionChartData } from '../types/retention.js'\nimport { CodeBlock } from '../shared/components/CodeBlock.js'\nimport { useTranslation } from '../hooks/useTranslation.js'\n\ninterface DebugModalProps {\n chartConfig: any\n displayConfig: any\n queryObject: any\n data: any[] | FlowChartData | RetentionChartData\n chartType: string\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n}\n\nexport default function DebugModal({\n chartConfig,\n displayConfig,\n queryObject,\n data,\n chartType,\n cacheInfo\n}: DebugModalProps) {\n const { t } = useTranslation()\n const [isOpen, setIsOpen] = useState(false)\n\n // Handle ESC key to close modal\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && isOpen) {\n setIsOpen(false)\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [isOpen])\n\n\n if (!isOpen) {\n return (\n <button\n onClick={() => setIsOpen(true)}\n className=\"dc:p-1 text-dc-text-muted hover:text-dc-text-secondary dc:transition-colors\"\n title=\"Debug chart configuration\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/>\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>\n </svg>\n </button>\n )\n }\n\n const fieldAnalysis = [\n `xAxis: ${Array.isArray(chartConfig?.xAxis) ? `[${chartConfig.xAxis.join(', ')}]` : JSON.stringify(chartConfig?.xAxis ?? null)}`,\n `yAxis: ${Array.isArray(chartConfig?.yAxis) ? `[${chartConfig.yAxis.join(', ')}]` : JSON.stringify(chartConfig?.yAxis ?? null)}`,\n `series: ${Array.isArray(chartConfig?.series) ? `[${chartConfig.series.join(', ')}]` : JSON.stringify(chartConfig?.series ?? null)}`,\n ...(chartConfig?.sizeField ? [`sizeField: ${JSON.stringify(chartConfig.sizeField)}`] : []),\n ...(chartConfig?.colorField ? [`colorField: ${JSON.stringify(chartConfig.colorField)}`] : []),\n ].join('\\n')\n\n return (\n <div\n className=\"dc:absolute dc:inset-0 bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:z-50 dc:overflow-auto\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"dc:p-4 dc:h-full dc:flex dc:flex-col\">\n <div className=\"dc:flex dc:justify-between dc:items-center dc:mb-4 dc:shrink-0\">\n <h2 className=\"dc:text-lg dc:font-semibold text-dc-text\">{t('debug.title')}</h2>\n <button\n onClick={() => setIsOpen(false)}\n className=\"dc:p-2 text-dc-text-muted hover:text-dc-text-secondary hover:bg-dc-surface-secondary dc:rounded-sm\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n\n <div className=\"dc:grid dc:grid-cols-1 dc:lg:grid-cols-2 dc:gap-4 dc:flex-1 dc:overflow-auto\">\n <CodeBlock\n code={chartType}\n language=\"json\"\n title=\"Chart Type\"\n maxHeight=\"3rem\"\n />\n\n <CodeBlock\n code={fieldAnalysis}\n language=\"json\"\n title=\"Field Analysis\"\n maxHeight=\"8rem\"\n />\n\n <CodeBlock\n code={JSON.stringify(chartConfig, null, 2)}\n language=\"json\"\n title=\"Chart Config\"\n className=\"dc:lg:col-span-2\"\n />\n\n <CodeBlock\n code={JSON.stringify(displayConfig, null, 2)}\n language=\"json\"\n title=\"Display Config\"\n className=\"dc:lg:col-span-2\"\n />\n\n <CodeBlock\n code={JSON.stringify(queryObject, null, 2)}\n language=\"json\"\n title=\"Query Object\"\n className=\"dc:lg:col-span-2\"\n />\n\n <CodeBlock\n code={JSON.stringify(Array.isArray(data) ? data.slice(0, 3) : data, null, 2)}\n language=\"json\"\n title=\"Data Sample (first 3 rows)\"\n className=\"dc:lg:col-span-2\"\n />\n\n <div className=\"dc:lg:col-span-2\">\n <h4 className=\"dc:text-sm dc:font-semibold text-dc-text dc:mb-2\">{t('debug.cacheStatus')}</h4>\n <div className=\"bg-dc-surface-secondary dc:p-2 dc:rounded-sm dc:text-xs dc:border border-dc-border\">\n {cacheInfo ? (\n <div className=\"dc:flex dc:items-center dc:gap-4 dc:flex-wrap\">\n <span className=\"dc:inline-flex dc:items-center dc:px-2 dc:py-0.5 dc:rounded-sm dc:text-xs dc:font-medium bg-dc-success-bg text-dc-success\">\n {t('debug.cacheHit')}\n </span>\n <span><strong>{t('debug.cachedAt')}</strong> {new Date(cacheInfo.cachedAt).toLocaleString()}</span>\n <span><strong>{t('debug.ttl')}</strong> {Math.round(cacheInfo.ttlMs / 1000)}s</span>\n <span><strong>{t('debug.ttlRemaining')}</strong> {Math.round(cacheInfo.ttlRemainingMs / 1000)}s</span>\n </div>\n ) : (\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <span className=\"dc:inline-flex dc:items-center dc:px-2 dc:py-0.5 dc:rounded-sm dc:text-xs dc:font-medium bg-dc-surface text-dc-text-muted dc:border border-dc-border\">\n {t('debug.freshQuery')}\n </span>\n <span className=\"text-dc-text-muted\">{t('debug.notFromCache')}</span>\n </div>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"dc:mt-4 dc:pt-2 dc:border-t border-dc-border dc:text-xs text-dc-text-muted dc:shrink-0\">\n {t('debug.escToClose')} <kbd className=\"dc:px-1 dc:py-0.5 bg-dc-surface-secondary dc:rounded-sm dc:text-xs\">{t('debug.escKey')}</kbd> {t('debug.toClose')}\n </div>\n </div>\n </div>\n )\n}\n","/**\n * Header (title + action toolbar) for DashboardPortletCard. Extracted to keep\n * the card component flat.\n */\n\nimport React, { type CSSProperties, type ComponentType } from 'react'\nimport type { PortletConfig } from '../../types.js'\nimport DebugModal from '../DebugModal.js'\nimport { getIcon } from '../../icons/registry.js'\nimport type { PortletDebugDataEntry } from '../../stores/dashboardStore.js'\n\nconst ICON_STYLE: CSSProperties = { width: '16px', height: '16px', color: 'currentColor' }\n\ntype IconComponent = ComponentType<{ className?: string; style?: CSSProperties }>\n\ninterface CardIcons {\n RefreshIcon: IconComponent\n EditIcon: IconComponent\n DeleteIcon: IconComponent\n CopyIcon: IconComponent\n FilterIcon: IconComponent\n}\n\ninterface PortletCardHeaderProps {\n portlet: PortletConfig\n className: string\n headerStyle?: CSSProperties\n restHeaderProps: Record<string, unknown>\n headerOnClick?: (event: React.MouseEvent<HTMLDivElement>) => void\n editable: boolean\n isEditMode: boolean\n isInSelectionMode: boolean\n debugData?: PortletDebugDataEntry\n copyAvailable: boolean\n copySuccess: boolean\n xlsExportAvailable: boolean\n exportInProgress: boolean\n showCacheBustIndicator: boolean\n icons: CardIcons\n onRefresh: (options?: { bustCache?: boolean }) => void\n onHoverRefreshChange: (hovering: boolean) => void\n onCopyToClipboard: (event: React.MouseEvent | React.TouchEvent) => void\n onExportXlsx: (event: React.MouseEvent | React.TouchEvent) => void\n onOpenFilterConfig: () => void\n onDuplicate: () => void\n onEdit: () => void\n onDelete: () => void\n}\n\n/** Suppresses drag/touch propagation on header action containers. */\nconst STOP_HANDLERS = {\n onMouseDown: (event: React.MouseEvent) => {\n event.stopPropagation()\n event.preventDefault()\n },\n onClick: (event: React.MouseEvent) => event.stopPropagation(),\n onTouchStart: (event: React.TouchEvent) => {\n event.stopPropagation()\n event.preventDefault()\n },\n onTouchEnd: (event: React.TouchEvent) => event.stopPropagation()\n}\n\nfunction CacheIndicator({ cachedAt }: { cachedAt: string }) {\n return (\n <span\n className=\"dc:p-1 text-dc-text-muted dc:opacity-40\"\n title={`Cached ${Math.round((Date.now() - new Date(cachedAt).getTime()) / 1000)}s ago`}\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\" />\n <path d=\"M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5\" />\n <path d=\"M3 12c0 1.66 4 3 9 3s9-1.34 9-3\" />\n </svg>\n </span>\n )\n}\n\n/** Edit-mode action buttons: filter config, duplicate, edit, delete. */\nfunction EditActionButtons({\n portlet,\n icons,\n onOpenFilterConfig,\n onDuplicate,\n onEdit,\n onDelete\n}: {\n portlet: PortletConfig\n icons: CardIcons\n onOpenFilterConfig: () => void\n onDuplicate: () => void\n onEdit: () => void\n onDelete: () => void\n}) {\n const mappingCount = portlet.dashboardFilterMapping?.length ?? 0\n\n const stop = (handler: () => void) => (event: React.MouseEvent | React.TouchEvent) => {\n event.stopPropagation()\n if ('preventDefault' in event && event.type === 'touchend') event.preventDefault()\n handler()\n }\n\n return (\n <>\n <button\n onClick={stop(onOpenFilterConfig)}\n onTouchEnd={stop(onOpenFilterConfig)}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors dc:relative\"\n title={`Configure dashboard filters${mappingCount > 0 ? ` (${mappingCount} active)` : ''}`}\n style={{ color: mappingCount > 0 ? 'var(--dc-primary)' : 'var(--dc-text-secondary)' }}\n >\n <icons.FilterIcon style={ICON_STYLE} />\n </button>\n\n <button\n onClick={stop(onDuplicate)}\n onTouchEnd={stop(onDuplicate)}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm text-dc-text-secondary dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors\"\n title=\"Duplicate portlet\"\n >\n <icons.CopyIcon style={ICON_STYLE} />\n </button>\n <button\n onClick={stop(onEdit)}\n onTouchEnd={stop(onEdit)}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm text-dc-text-secondary dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors\"\n title=\"Edit portlet\"\n >\n <icons.EditIcon style={ICON_STYLE} />\n </button>\n <button\n onClick={stop(onDelete)}\n onTouchEnd={stop(onDelete)}\n className=\"dc:p-1 dc:mr-0.5 bg-transparent dc:border-none dc:rounded-sm dc:cursor-pointer hover:bg-dc-danger-bg text-dc-danger dc:transition-colors\"\n title=\"Delete portlet\"\n >\n <icons.DeleteIcon style={ICON_STYLE} />\n </button>\n </>\n )\n}\n\nexport default function PortletCardHeader(props: PortletCardHeaderProps) {\n const {\n portlet, className, headerStyle, restHeaderProps, headerOnClick,\n editable, isEditMode, isInSelectionMode, debugData,\n copyAvailable, copySuccess, xlsExportAvailable, exportInProgress, showCacheBustIndicator,\n icons, onRefresh, onHoverRefreshChange, onCopyToClipboard, onExportXlsx,\n onOpenFilterConfig, onDuplicate, onEdit, onDelete\n } = props\n\n const CameraIcon = getIcon('camera')\n const CheckIcon = getIcon('check')\n const DownloadIcon = getIcon('download')\n\n return (\n <div\n className={className}\n style={headerStyle}\n onClick={(event) => { headerOnClick?.(event) }}\n {...restHeaderProps}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:flex-1 dc:min-w-0\">\n <h3 className=\"dc:font-semibold dc:text-sm text-dc-text dc:truncate\">{portlet.title}</h3>\n {editable && isEditMode && debugData && (\n <div {...STOP_HANDLERS}>\n <DebugModal\n chartConfig={debugData.chartConfig}\n displayConfig={debugData.displayConfig}\n queryObject={debugData.queryObject}\n data={debugData.data}\n chartType={debugData.chartType}\n cacheInfo={debugData.cacheInfo as { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | undefined}\n />\n </div>\n )}\n </div>\n <div className=\"dc:flex dc:items-center dc:gap-1 dc:shrink-0 dc:ml-4 dc:-mr-2\" {...STOP_HANDLERS}>\n {/* Cache indicator - show when result was served from cache */}\n {debugData?.cacheInfo && <CacheIndicator cachedAt={debugData.cacheInfo.cachedAt} />}\n <button\n onClick={(event) => {\n event.stopPropagation()\n onRefresh({ bustCache: event.shiftKey })\n }}\n onTouchEnd={(event) => {\n event.stopPropagation()\n event.preventDefault()\n onRefresh()\n }}\n onMouseEnter={() => onHoverRefreshChange(true)}\n onMouseLeave={() => onHoverRefreshChange(false)}\n disabled={isInSelectionMode}\n className={`dc:p-1 bg-transparent dc:border-none dc:rounded-sm dc:transition-colors ${\n isInSelectionMode\n ? 'dc:cursor-not-allowed dc:opacity-50 text-dc-text-secondary'\n : showCacheBustIndicator\n ? 'dc:cursor-pointer text-dc-warning bg-dc-warning-bg'\n : 'dc:cursor-pointer text-dc-text-secondary hover:bg-dc-surface-hover'\n }`}\n title={showCacheBustIndicator ? 'Click to refresh and bypass cache' : 'Refresh portlet data (Shift+click to bypass cache)'}\n >\n <icons.RefreshIcon style={ICON_STYLE} />\n </button>\n\n {/* Copy to clipboard button - visible when thumbnail feature is enabled and capability is available */}\n {copyAvailable && !isInSelectionMode && (\n <button\n onClick={onCopyToClipboard}\n onTouchEnd={(event) => {\n event.preventDefault()\n onCopyToClipboard(event)\n }}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm text-dc-text-secondary dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors\"\n title={copySuccess ? 'Copied!' : 'Copy chart to clipboard'}\n >\n {copySuccess ? <CheckIcon style={ICON_STYLE} /> : <CameraIcon style={ICON_STYLE} />}\n </button>\n )}\n\n {/* XLSX export button - visible when xlsExport feature is enabled, exceljs is installed, and portlet has data */}\n {xlsExportAvailable && !isInSelectionMode && debugData && (\n <button\n onClick={onExportXlsx}\n onTouchEnd={(event) => {\n event.preventDefault()\n onExportXlsx(event)\n }}\n disabled={exportInProgress}\n className={`dc:p-1 bg-transparent dc:border-none dc:rounded-sm dc:transition-colors ${\n exportInProgress\n ? 'dc:opacity-50 dc:cursor-wait text-dc-text-secondary'\n : 'text-dc-text-secondary dc:cursor-pointer dc:hover:bg-dc-surface-hover'\n }`}\n title={exportInProgress ? 'Exporting...' : 'Download data as XLSX'}\n >\n <DownloadIcon style={ICON_STYLE} />\n </button>\n )}\n\n {editable && isEditMode && !isInSelectionMode && (\n <EditActionButtons\n portlet={portlet}\n icons={icons}\n onOpenFilterConfig={onOpenFilterConfig}\n onDuplicate={onDuplicate}\n onEdit={onEdit}\n onDelete={onDelete}\n />\n )}\n </div>\n </div>\n )\n}\n","/**\n * Chip shown in filter-selection mode indicating which field a selected filter\n * targets on this portlet. Click opens the filter config modal.\n */\n\nimport { type CSSProperties, type ComponentType } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport type { EffectiveFilterField } from './filterField.js'\n\ninterface FilterFieldChipProps {\n field: EffectiveFilterField\n FilterIcon: ComponentType<{ className?: string; style?: CSSProperties }>\n onOpenFilterConfig: () => void\n}\n\nexport default function FilterFieldChip({ field, FilterIcon, onOpenFilterConfig }: FilterFieldChipProps) {\n const { t } = useTranslation()\n\n return (\n <button\n onClick={(event) => {\n event.stopPropagation()\n onOpenFilterConfig()\n }}\n onTouchEnd={(event) => {\n event.stopPropagation()\n event.preventDefault()\n onOpenFilterConfig()\n }}\n title={t('dashboard.filterFieldChipHint')}\n className=\"dc:absolute dc:bottom-2 dc:left-1/2 dc:-translate-x-1/2 dc:z-10 dc:flex dc:items-center dc:gap-1 dc:px-2.5 dc:py-1 dc:text-xs dc:font-medium dc:rounded-full dc:border dc:cursor-pointer dc:transition-colors dc:max-w-[90%] dc:truncate\"\n style={{\n backgroundColor: field.isOverride ? 'var(--dc-primary)' : 'var(--dc-surface)',\n color: field.isOverride ? 'white' : 'var(--dc-text-secondary)',\n borderColor: 'var(--dc-primary)',\n boxShadow: 'var(--dc-shadow-sm)'\n }}\n >\n <FilterIcon style={{ width: '12px', height: '12px' }} />\n <span className=\"dc:truncate\">{field.field}</span>\n </button>\n )\n}\n","/**\n * Class-name and inline-style builders for DashboardPortletCard's container and\n * header. Extracted to keep the card component flat.\n */\n\nimport type { CSSProperties } from 'react'\nimport type { ChartType, ChartDisplayConfig } from '../../types.js'\n\nexport interface PortletDisplayModes {\n isMarkdownAutoHeight: boolean\n isTransparentContent: boolean\n isTransparent: boolean\n shouldHideHeader: boolean\n}\n\n/**\n * Resolve the markdown / transparency / header-visibility display modes for a\n * portlet from its chart type and display config. Extracted to keep the card\n * component flat.\n */\nexport function resolveDisplayModes(params: {\n renderChartType: ChartType\n renderDisplayConfig?: ChartDisplayConfig\n layoutMode: string\n isEditMode: boolean\n portletTitle?: string\n}): PortletDisplayModes {\n const { renderChartType, renderDisplayConfig, layoutMode, isEditMode, portletTitle } = params\n\n const isMarkdown = renderChartType === 'markdown'\n // isTransparent gated on !isEditMode so chrome is visible for editing\n const markdownAutoHeightRequested = isMarkdown && (renderDisplayConfig?.autoHeight ?? true)\n const isMarkdownAutoHeight = layoutMode !== 'grid' && markdownAutoHeightRequested\n const isTransparentContent = isMarkdown && !!renderDisplayConfig?.transparentBackground\n const isTransparent = isTransparentContent && !isEditMode\n // Hide header when: explicitly set to hide, OR markdown with no title\n const shouldHideHeader = isMarkdown\n ? (renderDisplayConfig?.hideHeader ?? true) || !!renderDisplayConfig?.transparentBackground || !portletTitle\n : (renderDisplayConfig?.hideHeader ?? false)\n\n return { isMarkdownAutoHeight, isTransparentContent, isTransparent, shouldHideHeader }\n}\n\nexport function buildContainerClassName(params: {\n isTransparent: boolean\n isMarkdownAutoHeight: boolean\n isInSelectionMode: boolean\n extraClassName?: string\n}): string {\n const { isTransparent, isMarkdownAutoHeight, isInSelectionMode, extraClassName } = params\n return [\n isTransparent\n ? 'dc:flex dc:flex-col dc:transition-all'\n : 'bg-dc-surface dc:border dc:rounded-lg dc:flex dc:flex-col dc:transition-all',\n isMarkdownAutoHeight ? '' : 'dc:h-full',\n isInSelectionMode ? 'dc:cursor-pointer dc:relative' : '',\n extraClassName\n ]\n .filter(Boolean)\n .join(' ')\n}\n\nexport function buildHeaderClassName(isEditMode: boolean, extraClassName?: string): string {\n return [\n 'flex items-center justify-between px-3 py-1.5 md:px-4 md:py-1 border-b border-dc-border shrink-0 bg-dc-surface-secondary rounded-t-lg portlet-drag-handle',\n isEditMode ? 'cursor-move' : 'cursor-default',\n extraClassName\n ]\n .filter(Boolean)\n .join(' ')\n}\n\nexport function buildContainerStyle(params: {\n isTransparent: boolean\n isInSelectionMode: boolean\n hasSelectedFilter: boolean\n containerStyle?: CSSProperties\n}): CSSProperties {\n const { isTransparent, isInSelectionMode, hasSelectedFilter, containerStyle } = params\n const selected = isInSelectionMode && hasSelectedFilter\n\n return {\n boxShadow: isTransparent ? 'none' : 'var(--dc-shadow-sm)',\n borderColor: isTransparent ? 'transparent' : selected ? 'var(--dc-primary)' : 'var(--dc-border)',\n borderWidth: isTransparent ? '0' : selected ? '2px' : '1px',\n backgroundColor: isTransparent\n ? 'transparent'\n : selected\n ? 'rgba(var(--dc-primary-rgb), 0.05)'\n : 'var(--dc-surface)',\n opacity: isInSelectionMode && !hasSelectedFilter ? '0.5' : '1',\n ...containerStyle\n }\n}\n","import React, { useCallback, useMemo, type ReactNode } from 'react'\nimport type { ChartType, DashboardFilter, DashboardFilterMapping } from '../types.js'\nimport AnalyticsPortlet from './AnalyticsPortlet.js'\nimport type { ColorPalette } from '../utils/colorPalettes.js'\nimport { useDashboardStore, type PortletDebugDataEntry } from '../stores/dashboardStore.js'\nimport { ensureAnalysisConfig } from '../utils/configMigration.js'\nimport { mappingIncludesFilter } from '../utils/filterUtils.js'\nimport { arePropsEqual, type DashboardPortletCardProps } from './dashboardPortletCard/propsEqual.js'\nimport { resolveEffectiveFilterField } from './dashboardPortletCard/filterField.js'\nimport { usePortletCardActions } from './dashboardPortletCard/usePortletCardActions.js'\nimport PortletCardHeader from './dashboardPortletCard/PortletCardHeader.js'\nimport FilterFieldChip from './dashboardPortletCard/FilterFieldChip.js'\nimport { buildContainerClassName, buildHeaderClassName, buildContainerStyle, resolveDisplayModes } from './dashboardPortletCard/cardStyles.js'\n\ninterface PortletChartBodyProps {\n isTransparent: boolean\n setChartContainerRef: (el: HTMLDivElement | null) => void\n setPortletComponentRef: (el: { refresh: (options?: { bustCache?: boolean }) => void } | null) => void\n renderQuery: string\n renderChartType: ChartType\n renderChartConfig: unknown\n renderDisplayConfig: unknown\n dashboardFilters?: DashboardFilter[]\n dashboardFilterMapping?: DashboardFilterMapping\n eagerLoad: boolean\n title: string\n isMarkdownAutoHeight: boolean\n colorPalette?: ColorPalette\n loadingComponent?: ReactNode\n onDebugDataReady: (data: PortletDebugDataEntry) => void\n}\n\nconst PortletChartBody = React.memo(function PortletChartBody({\n isTransparent,\n setChartContainerRef,\n setPortletComponentRef,\n renderQuery,\n renderChartType,\n renderChartConfig,\n renderDisplayConfig,\n dashboardFilters,\n dashboardFilterMapping,\n eagerLoad,\n title,\n isMarkdownAutoHeight,\n colorPalette,\n loadingComponent,\n onDebugDataReady,\n}: PortletChartBodyProps) {\n return (\n <div\n ref={setChartContainerRef}\n className={`dc:flex-1 dc:min-h-0 dc:flex dc:flex-col${isTransparent ? '' : ' dc:px-2 dc:py-3 dc:md:px-4 dc:md:py-4'}`}\n >\n <AnalyticsPortlet\n ref={setPortletComponentRef}\n query={renderQuery}\n chartType={renderChartType}\n chartConfig={renderChartConfig as Record<string, unknown> | undefined}\n displayConfig={renderDisplayConfig as Record<string, unknown> | undefined}\n dashboardFilters={dashboardFilters}\n dashboardFilterMapping={dashboardFilterMapping}\n eagerLoad={eagerLoad}\n title={title}\n height={isMarkdownAutoHeight ? 'auto' : '100%'}\n colorPalette={colorPalette}\n loadingComponent={loadingComponent}\n onDebugDataReady={onDebugDataReady}\n />\n </div>\n )\n})\n\n// Memoize component - now using store for state, so fewer props to compare\nconst DashboardPortletCard = React.memo(function DashboardPortletCard({\n portlet,\n editable,\n layoutMode = 'grid',\n dashboardFilters,\n configEagerLoad,\n loadingComponent,\n colorPalette,\n containerProps,\n headerProps,\n setPortletRef,\n setPortletComponentRef,\n callbacks,\n icons\n}: DashboardPortletCardProps) {\n // Normalize portlet to ensure analysisConfig exists (on-the-fly migration from legacy format)\n const normalizedPortlet = useMemo(() => ensureAnalysisConfig(portlet), [portlet])\n const { analysisConfig } = normalizedPortlet\n\n // Extract rendering props from analysisConfig\n const chartModeConfig = analysisConfig.charts[analysisConfig.analysisType]\n const renderQuery = useMemo(() => JSON.stringify(analysisConfig.query), [analysisConfig.query])\n const renderChartType = chartModeConfig?.chartType || 'line'\n const renderChartConfig = chartModeConfig?.chartConfig\n const renderDisplayConfig = chartModeConfig?.displayConfig\n\n // Get state from Zustand store - automatic memoization via selectors\n const isEditMode = useDashboardStore(state => state.isEditMode)\n const selectedFilterId = useDashboardStore(state => state.selectedFilterId)\n const debugData = useDashboardStore(state => state.debugData[portlet.id])\n\n // Markdown-specific display modes (transparency, auto-height, header visibility)\n const { isMarkdownAutoHeight, isTransparentContent, isTransparent, shouldHideHeader } = resolveDisplayModes({\n renderChartType,\n renderDisplayConfig,\n layoutMode,\n isEditMode,\n portletTitle: portlet.title\n })\n\n // Get setDebugData action from store\n const setDebugData = useDashboardStore(state => state.setDebugData)\n\n // Copy/export/refresh action state and handlers\n const {\n chartContainerRef,\n copySuccess,\n copyAvailable,\n xlsExportAvailable,\n exportInProgress,\n showCacheBustIndicator,\n setIsHoveringRefresh,\n handleExportXlsx,\n handleCopyToClipboard\n } = usePortletCardActions({ portletTitle: portlet.title, debugData })\n\n const hasSelectedFilter = selectedFilterId\n ? mappingIncludesFilter(portlet.dashboardFilterMapping, selectedFilterId)\n : false\n const isInSelectionMode = !!selectedFilterId\n\n // In filter selection mode, show which field the selected filter targets on\n // this portlet (the per-portlet override, or the filter's own field)\n const effectiveFilterField = useMemo(() => resolveEffectiveFilterField({\n isInSelectionMode,\n hasSelectedFilter,\n selectedFilterId,\n dashboardFilters,\n dashboardFilterMapping: portlet.dashboardFilterMapping\n }), [isInSelectionMode, hasSelectedFilter, selectedFilterId, dashboardFilters, portlet.dashboardFilterMapping])\n\n const mergedContainerClassName = buildContainerClassName({\n isTransparent,\n isMarkdownAutoHeight,\n isInSelectionMode,\n extraClassName: containerProps?.className\n })\n\n const mergedHeaderClassName = buildHeaderClassName(isEditMode, headerProps?.className)\n\n const {\n onClick: containerOnClick,\n className: _containerClassName,\n style: containerStyle,\n ...restContainerProps\n } = containerProps ?? {}\n\n const {\n onClick: headerOnClick,\n className: _headerClassName,\n style: headerStyle,\n ...restHeaderProps\n } = headerProps ?? {}\n\n // Memoize debug data callback - now uses store action directly\n const handleDebugDataReady = useCallback((data: PortletDebugDataEntry) => {\n setDebugData(portlet.id, data)\n }, [portlet.id, setDebugData])\n\n const handleSetPortletRef = useCallback((el: HTMLDivElement | null) => {\n setPortletRef(portlet.id, el)\n }, [portlet.id, setPortletRef])\n\n const handleSetPortletComponentRef = useCallback((el: { refresh: (options?: { bustCache?: boolean }) => void } | null) => {\n setPortletComponentRef(portlet.id, el)\n }, [portlet.id, setPortletComponentRef])\n\n const handleSetChartContainerRef = useCallback((el: HTMLDivElement | null) => {\n chartContainerRef.current = el\n }, [chartContainerRef])\n\n return (\n <div\n data-portlet-id={portlet.id}\n ref={handleSetPortletRef}\n className={mergedContainerClassName}\n style={buildContainerStyle({ isTransparent, isInSelectionMode, hasSelectedFilter, containerStyle })}\n onClick={(event) => {\n if (isInSelectionMode && selectedFilterId) {\n event.stopPropagation()\n callbacks.onToggleFilter(portlet.id, selectedFilterId)\n }\n containerOnClick?.(event)\n }}\n {...restContainerProps}\n >\n {/* Filter selection mode: show which field the selected filter targets\n on this portlet; click opens the filter config modal to change it */}\n {effectiveFilterField && (\n <FilterFieldChip\n field={effectiveFilterField}\n FilterIcon={icons.FilterIcon}\n onOpenFilterConfig={() => callbacks.onOpenFilterConfig(portlet)}\n />\n )}\n {(!shouldHideHeader || isEditMode) && (\n <PortletCardHeader\n portlet={portlet}\n className={mergedHeaderClassName}\n headerStyle={headerStyle}\n restHeaderProps={restHeaderProps}\n headerOnClick={headerOnClick}\n editable={editable}\n isEditMode={isEditMode}\n isInSelectionMode={isInSelectionMode}\n debugData={debugData}\n copyAvailable={copyAvailable}\n copySuccess={copySuccess}\n xlsExportAvailable={xlsExportAvailable}\n exportInProgress={exportInProgress}\n showCacheBustIndicator={showCacheBustIndicator}\n icons={icons}\n onRefresh={(options) => callbacks.onRefresh(portlet.id, options)}\n onHoverRefreshChange={setIsHoveringRefresh}\n onCopyToClipboard={handleCopyToClipboard}\n onExportXlsx={handleExportXlsx}\n onOpenFilterConfig={() => callbacks.onOpenFilterConfig(portlet)}\n onDuplicate={() => callbacks.onDuplicate(portlet.id)}\n onEdit={() => callbacks.onEdit(portlet)}\n onDelete={() => callbacks.onDelete(portlet.id)}\n />\n )}\n\n <PortletChartBody\n isTransparent={isTransparentContent}\n setChartContainerRef={handleSetChartContainerRef}\n setPortletComponentRef={handleSetPortletComponentRef}\n renderQuery={renderQuery}\n renderChartType={renderChartType}\n renderChartConfig={renderChartConfig}\n renderDisplayConfig={renderDisplayConfig}\n dashboardFilters={dashboardFilters}\n dashboardFilterMapping={portlet.dashboardFilterMapping}\n eagerLoad={portlet.eagerLoad ?? configEagerLoad ?? false}\n title={portlet.title}\n isMarkdownAutoHeight={isMarkdownAutoHeight}\n colorPalette={colorPalette}\n loadingComponent={loadingComponent}\n onDebugDataReady={handleDebugDataReady}\n />\n </div>\n )\n}, arePropsEqual)\n\nexport default DashboardPortletCard\n","import { useState, useCallback, type HTMLAttributes, type ReactNode, type MouseEvent, type DragEvent } from 'react'\nimport type { DashboardGridSettings, PortletConfig, RowLayout } from '../types.js'\nimport { ensureAnalysisConfig } from '../utils/configMigration.js'\n\ninterface RowManagedLayoutProps {\n rows: RowLayout[]\n portlets: PortletConfig[]\n gridSettings: DashboardGridSettings\n gridWidth: number\n canEdit: boolean\n isDragging: boolean\n onRowResize: (rowIndex: number, event: MouseEvent<HTMLDivElement>) => void\n onColumnResize: (rowIndex: number, columnIndex: number, event: MouseEvent<HTMLDivElement>) => void\n onPortletDragStart: (rowIndex: number, columnIndex: number, portletId: string, event: DragEvent<HTMLDivElement>) => void\n onPortletDragEnd: () => void\n onRowDrop: (rowIndex: number, insertIndex: number | null) => void\n onNewRowDrop: (insertIndex: number) => void\n renderPortlet: (portlet: PortletConfig, containerProps?: HTMLAttributes<HTMLDivElement>, headerProps?: HTMLAttributes<HTMLDivElement>) => ReactNode\n}\n\nconst COLUMN_GAP = 16\n\nexport default function RowManagedLayout({\n rows,\n portlets,\n gridSettings,\n gridWidth,\n canEdit,\n isDragging,\n onRowResize,\n onColumnResize,\n onPortletDragStart,\n onPortletDragEnd,\n onRowDrop,\n onNewRowDrop,\n renderPortlet\n}: RowManagedLayoutProps) {\n const portletMap = new Map(portlets.map(portlet => [portlet.id, portlet]))\n const [activeDropKey, setActiveDropKey] = useState<string | null>(null)\n\n const setDropActive = (key: string | null) => {\n setActiveDropKey(key)\n }\n\n const isDragActive = isDragging || activeDropKey !== null\n\n // Stable drag event handlers using data attributes to prevent containerProps recreation\n const handlePortletDragStart = useCallback((event: DragEvent<HTMLDivElement>) => {\n const rowIndex = parseInt(event.currentTarget.dataset.rowIndex || '0', 10)\n const columnIndex = parseInt(event.currentTarget.dataset.columnIndex || '0', 10)\n const portletId = event.currentTarget.dataset.portletId || ''\n onPortletDragStart(rowIndex, columnIndex, portletId, event)\n }, [onPortletDragStart])\n\n const handlePortletDragEnd = useCallback(() => {\n setDropActive(null)\n onPortletDragEnd()\n }, [onPortletDragEnd])\n\n const topDropActive = activeDropKey === 'row-insert-0'\n const bottomDropActive = activeDropKey === 'row-bottom'\n\n return (\n <div\n className={`dc-row-layout${canEdit ? ' dc-row-layout-editable' : ''}${isDragActive ? ' dc-row-layout-dragging' : ''}`}\n style={{\n ['--dc-row-gap' as string]: '24px',\n ['--dc-column-gap' as string]: `${COLUMN_GAP}px`,\n ['--dc-top-drop-space' as string]: topDropActive ? '24px' : '0px',\n ['--dc-bottom-drop-space' as string]: bottomDropActive ? '24px' : '0px'\n }}\n >\n {canEdit && (\n <div\n className={`dc-row-boundary-drop dc-row-boundary-drop-top dc-split-handle${activeDropKey === 'dc:row-insert-0' ? ' dc-drop-zone-active' : ''}`}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive('row-insert-0')\n }}\n onDragLeave={() => setDropActive(null)}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onNewRowDrop(0)\n }}\n />\n )}\n {rows.map((row, rowIndex) => {\n // Row auto-height only when all columns are markdown and request autoHeight.\n const isAutoHeightRow = row.columns.length > 0 && row.columns.every(col => {\n const portlet = portletMap.get(col.portletId)\n if (!portlet) return false\n const normalized = ensureAnalysisConfig(portlet)\n const chartMode = normalized.analysisConfig.charts[normalized.analysisConfig.analysisType]\n return chartMode?.chartType === 'markdown' && (chartMode.displayConfig?.autoHeight ?? true)\n })\n const rowHeight = isAutoHeightRow ? undefined : row.h * gridSettings.rowHeight\n const safeGridWidth = gridWidth || gridSettings.cols * gridSettings.rowHeight\n const paddingLeft = activeDropKey === `row-${rowIndex}-insert-0` ? COLUMN_GAP : 0\n const paddingRight = activeDropKey === `row-${rowIndex}-insert-${row.columns.length}` ? COLUMN_GAP : 0\n const rowContentWidth = safeGridWidth - (row.columns.length - 1) * COLUMN_GAP - paddingLeft - paddingRight\n const unitWidth = rowContentWidth / gridSettings.cols\n\n return (\n <div key={row.id} className=\"dc-row-layout-row-wrapper\">\n <div\n className=\"dc-row-layout-row\"\n style={{\n height: rowHeight ?? 'auto',\n paddingLeft,\n paddingRight,\n }}\n >\n {row.columns.map((column, columnIndex) => {\n const portlet = portletMap.get(column.portletId)\n if (!portlet) return null\n const width = column.w * unitWidth\n\n return (\n <div\n key={portlet.id}\n className=\"dc-row-layout-column-wrapper dc-row-layout-column\"\n draggable={canEdit}\n data-row-index={rowIndex.toString()}\n data-column-index={columnIndex.toString()}\n data-portlet-id={portlet.id}\n onDragStart={handlePortletDragStart}\n onDragEnd={handlePortletDragEnd}\n style={{\n flex: `0 0 ${width}px`,\n maxWidth: `${width}px`\n }}\n >\n {renderPortlet(portlet)}\n {columnIndex < row.columns.length - 1 && (\n <div\n className={`dc-column-resize-handle dc-split-handle${activeDropKey === `row-${rowIndex}-insert-${columnIndex + 1}` ? ' dc-drop-zone-active' : ''}`}\n onMouseDown={(event) => onColumnResize(rowIndex, columnIndex, event)}\n onDragOver={(event) => {\n if (!canEdit) return\n event.preventDefault()\n setDropActive(`row-${rowIndex}-insert-${columnIndex + 1}`)\n }}\n onDragLeave={() => setDropActive(null)}\n onDrop={(event) => {\n if (!canEdit) return\n event.preventDefault()\n event.stopPropagation()\n setDropActive(null)\n onRowDrop(rowIndex, columnIndex + 1)\n }}\n />\n )}\n </div>\n )\n })}\n </div>\n {canEdit && (\n <>\n <div\n className={`dc-row-edge-drop dc-row-edge-drop-left dc-split-handle${activeDropKey === `row-${rowIndex}-insert-0` ? ' dc-drop-zone-active' : ''}`}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive(`row-${rowIndex}-insert-0`)\n }}\n onDragLeave={() => {\n setDropActive(null)\n }}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onRowDrop(rowIndex, 0)\n }}\n />\n <div\n className={`dc-row-edge-drop dc-row-edge-drop-right dc-split-handle${activeDropKey === `row-${rowIndex}-insert-${row.columns.length}` ? ' dc-drop-zone-active' : ''}`}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive(`row-${rowIndex}-insert-${row.columns.length}`)\n }}\n onDragLeave={() => {\n setDropActive(null)\n }}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onRowDrop(rowIndex, row.columns.length)\n }}\n />\n </>\n )}\n {canEdit && (\n <div\n className={`dc-row-resize-handle dc-split-handle${isAutoHeightRow ? ' dc-row-resize-handle-drop-only' : ''}${activeDropKey === `row-insert-${rowIndex + 1}` ? ' dc-drop-zone-active' : ''}`}\n onMouseDown={isAutoHeightRow ? undefined : (event) => onRowResize(rowIndex, event)}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive(`row-insert-${rowIndex + 1}`)\n }}\n onDragLeave={() => setDropActive(null)}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onNewRowDrop(rowIndex + 1)\n }}\n />\n )}\n </div>\n )\n })}\n {canEdit && (\n <div\n className={`dc-row-boundary-drop dc-row-boundary-drop-bottom dc-split-handle${activeDropKey === 'dc:row-bottom' ? ' dc-drop-zone-active' : ''}`}\n onDragOver={(event) => {\n event.preventDefault()\n setDropActive('row-bottom')\n }}\n onDragLeave={() => setDropActive(null)}\n onDrop={(event) => {\n event.preventDefault()\n setDropActive(null)\n onNewRowDrop(rows.length)\n }}\n />\n )}\n </div>\n )\n}\n","import { useCallback } from 'react'\nimport type { LayoutItem } from 'react-grid-layout'\nimport type { StoreApi } from 'zustand'\nimport type { DashboardStore } from '../../stores/dashboardStore.js'\n\ninterface UseGridLayoutEngineOptions {\n storeApi: StoreApi<DashboardStore>\n}\n\nexport function useGridLayoutEngine({\n storeApi\n}: UseGridLayoutEngineOptions) {\n const hasLayoutActuallyChanged = useCallback(\n (newLayout: LayoutItem[]) => {\n const { isInitialized, lastKnownLayout } = storeApi.getState()\n if (!isInitialized || lastKnownLayout.length === 0) {\n return false\n }\n\n for (const newItem of newLayout) {\n const oldItem = lastKnownLayout.find((item) => item.i === newItem.i)\n if (!oldItem) continue\n\n if (\n oldItem.x !== newItem.x ||\n oldItem.y !== newItem.y ||\n oldItem.w !== newItem.w ||\n oldItem.h !== newItem.h\n ) {\n return true\n }\n }\n return false\n },\n [storeApi]\n )\n\n return {\n hasLayoutActuallyChanged\n }\n}\n","import type { DashboardGridSettings, PortletConfig, RowLayout, RowLayoutColumn } from '../../types.js'\n\nexport const createRowId = () => `row-${Date.now()}`\n\nexport const equalizeRowColumns = (\n portletIds: string[],\n gridSettings: DashboardGridSettings\n): RowLayoutColumn[] => {\n const count = portletIds.length\n if (count === 0) return []\n\n const { cols, minW } = gridSettings\n const minTotal = minW * count\n\n if (minTotal > cols) {\n const base = Math.floor(cols / count)\n const remainder = cols % count\n return portletIds.map((id, index) => ({\n portletId: id,\n w: base + (index < remainder ? 1 : 0),\n }))\n }\n\n const remaining = cols - minTotal\n const extra = Math.floor(remaining / count)\n const remainder = remaining % count\n\n return portletIds.map((id, index) => ({\n portletId: id,\n w: minW + extra + (index < remainder ? 1 : 0),\n }))\n}\n\nexport const adjustRowWidths = (\n columns: RowLayoutColumn[],\n gridSettings: DashboardGridSettings\n): RowLayoutColumn[] => {\n if (columns.length === 0) return []\n\n const { cols, minW } = gridSettings\n const adjusted = columns.map((column) => ({\n ...column,\n w: Math.max(minW, column.w),\n }))\n\n let total = adjusted.reduce((sum, column) => sum + column.w, 0)\n if (total === cols) return adjusted\n\n if (total < cols) {\n let remaining = cols - total\n let index = 0\n while (remaining > 0) {\n adjusted[index % adjusted.length].w += 1\n remaining -= 1\n index += 1\n }\n return adjusted\n }\n\n let overflow = total - cols\n for (let index = adjusted.length - 1; index >= 0 && overflow > 0; index -= 1) {\n const column = adjusted[index]\n const reducible = Math.max(0, column.w - minW)\n if (reducible === 0) continue\n const delta = Math.min(reducible, overflow)\n column.w -= delta\n overflow -= delta\n }\n\n return adjusted\n}\n\nexport const convertPortletsToRows = (\n portlets: PortletConfig[],\n gridSettings: DashboardGridSettings\n): RowLayout[] => {\n if (portlets.length === 0) return []\n\n const sorted = [...portlets].sort((a, b) => {\n if (a.y !== b.y) return a.y - b.y\n return a.x - b.x\n })\n\n const rowsByY = new Map<number, PortletConfig[]>()\n sorted.forEach((portlet) => {\n const row = rowsByY.get(portlet.y) ?? []\n row.push(portlet)\n rowsByY.set(portlet.y, row)\n })\n\n return Array.from(rowsByY.entries())\n .sort(([a], [b]) => a - b)\n .map(([rowY, rowPortlets]) => {\n const rowHeight = Math.max(gridSettings.minH, ...rowPortlets.map((p) => p.h))\n const portletIds = rowPortlets.map((p) => p.id)\n return {\n id: `row-${rowY}`,\n h: rowHeight,\n columns: equalizeRowColumns(portletIds, gridSettings),\n }\n })\n}\n\nexport const normalizeRows = (\n rows: RowLayout[],\n portlets: PortletConfig[],\n gridSettings: DashboardGridSettings\n): RowLayout[] => {\n const portletIds = new Set(portlets.map((p) => p.id))\n return rows\n .map((row) => ({\n ...row,\n h: Math.max(gridSettings.minH, row.h),\n columns: adjustRowWidths(\n row.columns.filter((col) => portletIds.has(col.portletId)),\n gridSettings\n ),\n }))\n .filter((row) => row.columns.length > 0)\n}\n\nexport const convertRowsToPortlets = (\n rows: RowLayout[],\n portlets: PortletConfig[]\n): PortletConfig[] => {\n const portletMap = new Map(portlets.map((p) => [p.id, p]))\n let currentY = 0\n\n const updated: PortletConfig[] = []\n rows.forEach((row) => {\n let currentX = 0\n row.columns.forEach((column) => {\n const portlet = portletMap.get(column.portletId)\n if (!portlet) return\n updated.push({\n ...portlet,\n x: currentX,\n y: currentY,\n w: column.w,\n h: row.h,\n })\n currentX += column.w\n })\n currentY += row.h\n })\n\n const updatedIds = new Set(updated.map((p) => p.id))\n portlets.forEach((portlet) => {\n if (!updatedIds.has(portlet.id)) {\n updated.push(portlet)\n }\n })\n\n return updated\n}\n","import { useCallback, useMemo, type MutableRefObject } from 'react'\nimport type {\n DashboardConfig,\n DashboardGridSettings,\n DashboardLayoutMode,\n PortletConfig,\n RowLayout\n} from '../../types.js'\nimport {\n convertPortletsToRows,\n convertRowsToPortlets,\n normalizeRows\n} from './layoutUtils.js'\n\ninterface UseRowLayoutEngineOptions {\n layoutMode: DashboardLayoutMode\n draftRows: RowLayout[] | null\n config: DashboardConfig\n gridSettings: DashboardGridSettings\n configRef: MutableRefObject<DashboardConfig>\n onConfigChangeRef: MutableRefObject<((config: DashboardConfig) => void) | undefined>\n onSaveRef: MutableRefObject<((config: DashboardConfig) => Promise<void> | void) | undefined>\n setDraftRows: (rows: RowLayout[] | null) => void\n setThumbnailDirty: (dirty: boolean) => void\n}\n\nexport function useRowLayoutEngine({\n layoutMode,\n draftRows,\n config,\n gridSettings,\n configRef,\n onConfigChangeRef,\n onSaveRef,\n setDraftRows,\n setThumbnailDirty,\n}: UseRowLayoutEngineOptions) {\n const resolvedRows = useMemo(() => {\n if (layoutMode !== 'rows') return []\n const baseRows =\n draftRows ??\n config.rows ??\n convertPortletsToRows(config.portlets, gridSettings)\n return normalizeRows(baseRows, config.portlets, gridSettings)\n }, [layoutMode, draftRows, config.rows, config.portlets, gridSettings])\n\n const updateRowLayout = useCallback(\n async (\n rows: RowLayout[],\n save = true,\n portletsOverride?: PortletConfig[]\n ) => {\n if (!onConfigChangeRef.current) return\n\n const portlets = portletsOverride ?? configRef.current.portlets\n const normalizedRows = normalizeRows(rows, portlets, gridSettings)\n const updatedPortlets = convertRowsToPortlets(normalizedRows, portlets)\n const updatedConfig: DashboardConfig = {\n ...configRef.current,\n layoutMode: 'rows',\n rows: normalizedRows,\n portlets: updatedPortlets,\n }\n\n setDraftRows(null)\n onConfigChangeRef.current(updatedConfig)\n\n if (save) {\n setThumbnailDirty(true)\n }\n\n if (save && onSaveRef.current) {\n try {\n await onSaveRef.current(updatedConfig)\n } catch (error) {\n console.error('Auto-save failed after row layout change:', error)\n }\n }\n },\n [configRef, gridSettings, onConfigChangeRef, onSaveRef, setDraftRows, setThumbnailDirty]\n )\n\n return {\n resolvedRows,\n updateRowLayout,\n }\n}\n","import { startTransition, useCallback, useRef, type MutableRefObject, type RefObject } from 'react'\nimport type { StoreApi } from 'zustand'\nimport { captureThumbnail } from '../../utils/thumbnail.js'\nimport type {\n DashboardConfig,\n DashboardFilterMapping,\n DashboardGridSettings,\n DashboardLayoutMode,\n PortletConfig,\n RowLayout,\n ThumbnailFeatureConfig\n} from '../../types.js'\nimport type { DashboardStore, DashboardStoreActions } from '../../stores/dashboardStore.js'\nimport { mappingIncludesFilter } from '../../utils/filterUtils.js'\nimport {\n convertPortletsToRows,\n convertRowsToPortlets,\n createRowId,\n equalizeRowColumns,\n normalizeRows\n} from './layoutUtils.js'\n\ninterface UseDashboardControllerOptions {\n allowedModes: DashboardLayoutMode[]\n canChangeLayoutMode: boolean\n isResponsiveEditable: boolean\n layoutMode: DashboardLayoutMode\n resolvedRows: RowLayout[]\n gridSettings: DashboardGridSettings\n thumbnailConfig?: ThumbnailFeatureConfig\n dashboardRef?: RefObject<HTMLElement | null>\n storeApi: StoreApi<DashboardStore>\n storeActions: Pick<\n DashboardStoreActions,\n | 'setEditMode'\n | 'exitFilterSelectionMode'\n | 'openPortletModal'\n | 'closePortletModal'\n | 'openTextModal'\n | 'closeTextModal'\n | 'openFilterConfigModal'\n | 'closeFilterConfigModal'\n | 'openDeleteConfirm'\n | 'closeDeleteConfirm'\n | 'setThumbnailDirty'\n >\n configRef: MutableRefObject<DashboardConfig>\n onConfigChangeRef: MutableRefObject<((config: DashboardConfig) => void) | undefined>\n onSaveRef: MutableRefObject<((config: DashboardConfig) => Promise<void> | void) | undefined>\n onSaveThumbnailRef: MutableRefObject<((thumbnailData: string) => Promise<string | void>) | undefined>\n updateRowLayout: (\n rows: RowLayout[],\n save?: boolean,\n portletsOverride?: PortletConfig[]\n ) => Promise<void>\n portletComponentRefs?: MutableRefObject<Record<string, { refresh: (options?: { bustCache?: boolean }) => void } | null>>\n onPortletRefresh?: (portletId: string, options?: { bustCache?: boolean }) => void\n}\n\nexport function useDashboardController({\n allowedModes,\n canChangeLayoutMode,\n isResponsiveEditable,\n layoutMode,\n resolvedRows,\n gridSettings,\n thumbnailConfig,\n dashboardRef,\n storeApi,\n storeActions,\n configRef,\n onConfigChangeRef,\n onSaveRef,\n onSaveThumbnailRef,\n updateRowLayout,\n portletComponentRefs,\n onPortletRefresh,\n}: UseDashboardControllerOptions) {\n const layoutModeRef = useRef(layoutMode)\n layoutModeRef.current = layoutMode\n const canChangeLayoutModeRef = useRef(canChangeLayoutMode)\n canChangeLayoutModeRef.current = canChangeLayoutMode\n const resolvedRowsRef = useRef(resolvedRows)\n resolvedRowsRef.current = resolvedRows\n\n const saveConfig = useCallback(\n async (\n updatedConfig: DashboardConfig,\n errorMessage: string\n ) => {\n if (!onConfigChangeRef.current) return\n\n onConfigChangeRef.current(updatedConfig)\n storeActions.setThumbnailDirty(true)\n\n if (onSaveRef.current) {\n try {\n await onSaveRef.current(updatedConfig)\n } catch (error) {\n console.error(errorMessage, error)\n }\n }\n },\n [onConfigChangeRef, onSaveRef, storeActions]\n )\n\n const enterEditMode = useCallback(() => {\n startTransition(() => {\n storeActions.setEditMode(true)\n })\n }, [storeActions])\n\n const exitEditMode = useCallback(() => {\n startTransition(() => {\n storeActions.setEditMode(false)\n })\n\n const isThumbnailDirty = storeApi.getState().thumbnailDirty\n if (isThumbnailDirty && thumbnailConfig?.enabled && dashboardRef) {\n setTimeout(async () => {\n const thumbnailData = await captureThumbnail(dashboardRef, thumbnailConfig)\n if (thumbnailData && onSaveThumbnailRef.current) {\n try {\n const thumbnailUrl = await onSaveThumbnailRef.current(thumbnailData)\n if (thumbnailUrl && onConfigChangeRef.current) {\n onConfigChangeRef.current({\n ...configRef.current,\n thumbnailUrl,\n thumbnailData: undefined\n })\n }\n } catch (error) {\n console.error('Failed to save thumbnail:', error)\n }\n }\n storeActions.setThumbnailDirty(false)\n }, 500)\n }\n }, [\n configRef,\n dashboardRef,\n onConfigChangeRef,\n onSaveThumbnailRef,\n storeApi,\n storeActions,\n thumbnailConfig\n ])\n\n const toggleEditMode = useCallback(() => {\n if (!isResponsiveEditable) return\n const store = storeApi.getState()\n if (store.isEditMode) {\n exitEditMode()\n } else {\n startTransition(() => {\n storeActions.setEditMode(true)\n })\n }\n }, [exitEditMode, isResponsiveEditable, storeActions, storeApi])\n\n const selectFilter = useCallback(\n (filterId: string | null) => {\n const currentSelectedId = storeApi.getState().selectedFilterId\n storeApi.getState().setSelectedFilterId(\n filterId === currentSelectedId ? null : filterId\n )\n },\n [storeApi]\n )\n\n const openAddPortlet = useCallback(() => {\n storeActions.openPortletModal(null)\n }, [storeActions])\n\n const openEditPortlet = useCallback(\n (portlet: PortletConfig) => {\n storeActions.openPortletModal(portlet)\n },\n [storeActions]\n )\n\n const openAddText = useCallback(() => {\n storeActions.openTextModal(null)\n }, [storeActions])\n\n const openEditText = useCallback(\n (portlet: PortletConfig) => {\n storeActions.openTextModal(portlet)\n },\n [storeActions]\n )\n\n const openFilterConfig = useCallback(\n (portlet: PortletConfig) => {\n storeActions.openFilterConfigModal(portlet)\n },\n [storeActions]\n )\n\n const handleLayoutModeChange = useCallback(\n async (mode: DashboardLayoutMode) => {\n if (\n !onConfigChangeRef.current ||\n mode === layoutModeRef.current ||\n !canChangeLayoutModeRef.current ||\n !allowedModes.includes(mode)\n ) {\n return\n }\n\n const cfg = configRef.current\n const baseRows = normalizeRows(\n cfg.rows && cfg.rows.length > 0\n ? cfg.rows\n : convertPortletsToRows(cfg.portlets, gridSettings),\n cfg.portlets,\n gridSettings\n )\n\n const updatedPortlets = convertRowsToPortlets(baseRows, cfg.portlets)\n const updatedConfig: DashboardConfig = {\n ...cfg,\n layoutMode: mode,\n rows: baseRows,\n portlets: updatedPortlets,\n }\n\n await saveConfig(updatedConfig, 'Auto-save failed after layout mode switch:')\n },\n [allowedModes, configRef, gridSettings, onConfigChangeRef, saveConfig]\n )\n\n const savePortlet = useCallback(\n async (\n portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>\n ): Promise<string | null> => {\n if (!onConfigChangeRef.current) return null\n\n const cfg = configRef.current\n let updatedPortlets = [...cfg.portlets]\n let isNewPortlet = false\n let newPortletId: string | null = null\n\n const store = storeApi.getState()\n const editingExisting = store.editingPortlet || store.editingTextPortlet\n if (editingExisting) {\n const index = updatedPortlets.findIndex((p) => p.id === editingExisting.id)\n if (index !== -1) {\n updatedPortlets[index] = portletData as PortletConfig\n }\n } else {\n isNewPortlet = true\n const newPortlet: PortletConfig = {\n ...portletData,\n id: `portlet-${Date.now()}`,\n x: 0,\n y: 0,\n } as PortletConfig\n\n newPortletId = newPortlet.id\n\n let maxY = 0\n cfg.portlets.forEach((p) => {\n if (p.y + p.h > maxY) {\n maxY = p.y + p.h\n }\n })\n newPortlet.y = maxY\n\n updatedPortlets.push(newPortlet)\n }\n\n if (layoutModeRef.current === 'rows') {\n const currentRows = resolvedRowsRef.current\n const baseRows =\n currentRows.length > 0\n ? currentRows.map((row) => ({\n ...row,\n columns: row.columns.map((col) => ({ ...col })),\n }))\n : normalizeRows(\n cfg.rows ?? convertPortletsToRows(cfg.portlets, gridSettings),\n updatedPortlets,\n gridSettings\n )\n\n const nextRows =\n isNewPortlet && newPortletId\n ? [\n ...baseRows,\n {\n id: createRowId(),\n h: Math.max(gridSettings.minH, 3),\n columns: equalizeRowColumns([newPortletId], gridSettings),\n },\n ]\n : baseRows\n\n await updateRowLayout(nextRows, true, updatedPortlets)\n } else {\n const updatedConfig: DashboardConfig = {\n ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n }\n\n storeActions.closePortletModal()\n storeActions.closeTextModal()\n return newPortletId\n },\n [configRef, gridSettings, onConfigChangeRef, resolvedRowsRef, saveConfig, storeActions, storeApi, updateRowLayout]\n )\n\n const executeDeletePortlet = useCallback(\n async (portletId: string) => {\n if (!onConfigChangeRef.current) return\n\n const cfg = configRef.current\n const updatedPortlets = cfg.portlets.filter((p) => p.id !== portletId)\n\n if (layoutModeRef.current === 'rows') {\n const nextRows = resolvedRowsRef.current\n .map((row) => ({\n ...row,\n columns: row.columns.filter((col) => col.portletId !== portletId),\n }))\n .filter((row) => row.columns.length > 0)\n .map((row) => ({\n ...row,\n columns: equalizeRowColumns(\n row.columns.map((col) => col.portletId),\n gridSettings\n ),\n }))\n\n await updateRowLayout(nextRows, true, updatedPortlets)\n } else {\n const updatedConfig: DashboardConfig = {\n ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n }\n },\n [configRef, gridSettings, onConfigChangeRef, resolvedRowsRef, saveConfig, updateRowLayout]\n )\n\n const deletePortlet = useCallback(\n async (portletId: string) => {\n storeActions.openDeleteConfirm(portletId)\n },\n [storeActions]\n )\n\n const confirmDelete = useCallback(async () => {\n const portletId = storeApi.getState().deleteConfirmPortletId\n if (!portletId) return\n\n await executeDeletePortlet(portletId)\n storeActions.closeDeleteConfirm()\n }, [executeDeletePortlet, storeActions, storeApi])\n\n const duplicatePortlet = useCallback(\n async (portletId: string): Promise<string | undefined> => {\n if (!onConfigChangeRef.current) return undefined\n\n const cfg = configRef.current\n const originalPortlet = cfg.portlets.find((p) => p.id === portletId)\n if (!originalPortlet) return undefined\n\n const duplicatedPortlet: PortletConfig = {\n ...originalPortlet,\n id: `portlet-${Date.now()}`,\n title: `${originalPortlet.title} Duplicated`,\n x: 0,\n y: 0,\n }\n\n let maxY = 0\n cfg.portlets.forEach((p) => {\n if (p.y + p.h > maxY) {\n maxY = p.y + p.h\n }\n })\n duplicatedPortlet.y = maxY\n\n const updatedPortlets = [...cfg.portlets, duplicatedPortlet]\n\n if (layoutModeRef.current === 'rows') {\n const baseRows = resolvedRowsRef.current.map((row) => ({\n ...row,\n columns: row.columns.map((col) => ({ ...col })),\n }))\n const nextRows = [\n ...baseRows,\n {\n id: createRowId(),\n h: Math.max(gridSettings.minH, 3),\n columns: equalizeRowColumns([duplicatedPortlet.id], gridSettings),\n },\n ]\n await updateRowLayout(nextRows, true, updatedPortlets)\n } else {\n const updatedConfig: DashboardConfig = {\n ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n }\n\n return duplicatedPortlet.id\n },\n [configRef, gridSettings, onConfigChangeRef, resolvedRowsRef, saveConfig, updateRowLayout]\n )\n\n const refreshPortlet = useCallback(\n (portletId: string, options?: { bustCache?: boolean }) => {\n const portletComponent = portletComponentRefs?.current?.[portletId]\n if (portletComponent?.refresh) {\n portletComponent.refresh(options)\n }\n onPortletRefresh?.(portletId, options)\n },\n [onPortletRefresh, portletComponentRefs]\n )\n\n const toggleFilterForPortlet = useCallback(\n async (portletId: string, filterId: string) => {\n if (!onConfigChangeRef.current) return\n\n const cfg = configRef.current\n const updatedPortlets = cfg.portlets.map((p) => {\n if (p.id === portletId) {\n const currentMapping = p.dashboardFilterMapping || []\n const hasFilter = mappingIncludesFilter(currentMapping, filterId)\n\n return {\n ...p,\n dashboardFilterMapping: hasFilter\n ? currentMapping.filter((entry) =>\n typeof entry === 'string' ? entry !== filterId : entry.filterId !== filterId\n )\n : [...currentMapping, filterId],\n }\n }\n return p\n })\n\n const updatedConfig: DashboardConfig = {\n ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n },\n [configRef, onConfigChangeRef, saveConfig]\n )\n\n const selectAllForFilter = useCallback(\n async (filterId: string) => {\n if (!onConfigChangeRef.current) return\n\n const cfg = configRef.current\n const updatedPortlets = cfg.portlets.map((p) => {\n const currentMapping = p.dashboardFilterMapping || []\n if (!mappingIncludesFilter(currentMapping, filterId)) {\n return {\n ...p,\n dashboardFilterMapping: [...currentMapping, filterId],\n }\n }\n return p\n })\n\n const updatedConfig: DashboardConfig = {\n ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n },\n [configRef, onConfigChangeRef, saveConfig]\n )\n\n const saveFilterConfig = useCallback(\n async (mapping: DashboardFilterMapping) => {\n const filterConfigPortlet = storeApi.getState().filterConfigPortlet\n if (!onConfigChangeRef.current || !filterConfigPortlet) return\n\n const cfg = configRef.current\n const updatedPortlets = cfg.portlets.map((p) => {\n if (p.id === filterConfigPortlet.id) {\n return {\n ...p,\n dashboardFilterMapping: mapping,\n }\n }\n return p\n })\n\n const updatedConfig: DashboardConfig = {\n ...cfg,\n portlets: updatedPortlets,\n }\n await saveConfig(updatedConfig, 'Auto-save failed:')\n },\n [configRef, onConfigChangeRef, saveConfig, storeApi]\n )\n\n const handlePaletteChange = useCallback(\n async (paletteName: string) => {\n if (!onConfigChangeRef.current) return\n\n const updatedConfig: DashboardConfig = {\n ...configRef.current,\n colorPalette: paletteName,\n }\n\n await saveConfig(updatedConfig, 'Auto-save failed:')\n },\n [configRef, onConfigChangeRef, saveConfig]\n )\n\n return {\n enterEditMode,\n exitEditMode,\n toggleEditMode,\n selectFilter,\n openAddPortlet,\n openEditPortlet,\n openAddText,\n openEditText,\n openFilterConfig,\n handleLayoutModeChange,\n savePortlet,\n deletePortlet,\n confirmDelete,\n duplicatePortlet,\n refreshPortlet,\n toggleFilterForPortlet,\n selectAllForFilter,\n saveFilterConfig,\n handlePaletteChange,\n }\n}\n","/**\n * useDashboard - Master Coordination Hook\n *\n * The single hook that provides everything DashboardGrid needs:\n * - Zustand store state and actions (from Context)\n * - Computed values (canEdit, resolvedRows, etc.)\n * - Config-modifying actions (that call onConfigChange/onSave)\n *\n * This hook replaces 14+ useState calls and 25+ useCallback handlers,\n * providing a clean interface for the DashboardGrid component.\n *\n * IMPORTANT: This hook must be used within DashboardStoreProvider\n *\n * Usage:\n * ```tsx\n * const dashboard = useDashboard({\n * config,\n * editable,\n * gridSettings,\n * onConfigChange,\n * onSave,\n * })\n *\n * // Access state\n * const { isEditMode, selectedFilterId } = dashboard\n *\n * // Access computed values\n * const { canEdit, resolvedRows } = dashboard\n *\n * // Access actions\n * dashboard.actions.openAddPortlet()\n * ```\n */\n\nimport React, { useMemo, useRef } from 'react'\nimport { useShallow } from 'zustand/react/shallow'\nimport {\n useDashboardStore,\n useDashboardStoreApi,\n type DashboardStore,\n type PortletDebugDataEntry,\n} from '../stores/dashboardStore.js'\nimport { useCubeFeatures } from '../providers/CubeProvider.js'\nimport type { LayoutItem } from 'react-grid-layout'\nimport type {\n DashboardConfig,\n PortletConfig,\n RowLayout,\n DashboardFilter,\n DashboardFilterMapping,\n DashboardGridSettings,\n DashboardLayoutMode,\n} from '../types.js'\nimport { useGridLayoutEngine } from './dashboard/useGridLayoutEngine.js'\nimport { useRowLayoutEngine } from './dashboard/useRowLayoutEngine.js'\nimport { useDashboardController } from './dashboard/useDashboardController.js'\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface UseDashboardOptions {\n /** Dashboard configuration */\n config: DashboardConfig\n /** Whether dashboard is editable */\n editable?: boolean\n /** Dashboard filters */\n dashboardFilters?: DashboardFilter[]\n /** Grid settings */\n gridSettings: DashboardGridSettings\n /** Allowed layout modes */\n allowedModes?: DashboardLayoutMode[]\n /** Whether responsive mode allows editing (desktop only) */\n isResponsiveEditable?: boolean\n /** Config change handler */\n onConfigChange?: (config: DashboardConfig) => void\n /** Save handler */\n onSave?: (config: DashboardConfig) => Promise<void> | void\n /** Callback to save thumbnail separately - called on edit mode exit when thumbnail feature is enabled */\n onSaveThumbnail?: (thumbnailData: string) => Promise<string | void>\n /** Grid width for row calculations */\n gridWidth?: number\n /** Portlet component refs for refresh functionality */\n portletComponentRefs?: React.MutableRefObject<Record<string, { refresh: (options?: { bustCache?: boolean }) => void } | null>>\n /** Portlet refresh handler (external) */\n onPortletRefresh?: (portletId: string, options?: { bustCache?: boolean }) => void\n /** Ref to the dashboard container element for thumbnail capture */\n dashboardRef?: React.RefObject<HTMLElement | null>\n}\n\nexport interface UseDashboardResult {\n // =========================================================================\n // Store State\n // =========================================================================\n /** Whether dashboard is in edit mode */\n isEditMode: boolean\n /** Selected filter ID for filter assignment mode */\n selectedFilterId: string | null\n /** Whether portlet modal is open */\n isPortletModalOpen: boolean\n /** Portlet being edited */\n editingPortlet: PortletConfig | null\n /** Whether text portlet modal is open */\n isTextModalOpen: boolean\n /** Portlet being edited in text modal */\n editingTextPortlet: PortletConfig | null\n /** Whether filter config modal is open */\n isFilterConfigModalOpen: boolean\n /** Portlet for filter configuration */\n filterConfigPortlet: PortletConfig | null\n /** Portlet ID pending delete confirmation */\n deleteConfirmPortletId: string | null\n /** Draft rows during drag operations */\n draftRows: RowLayout[] | null\n /** Whether a portlet is being dragged */\n isDraggingPortlet: boolean\n /** Last known layout for change detection */\n lastKnownLayout: LayoutItem[]\n /** Whether component is initialized */\n isInitialized: boolean\n\n // =========================================================================\n // Computed Values\n // =========================================================================\n /** Whether editing is allowed (editable && isEditMode && desktop && !filterMode) */\n canEdit: boolean\n /** Whether layout mode can be changed */\n canChangeLayoutMode: boolean\n /** Currently selected filter object */\n selectedFilter: DashboardFilter | null\n /** Resolved rows for row-based layout */\n resolvedRows: RowLayout[]\n /** Current layout mode */\n layoutMode: DashboardLayoutMode\n /** Allowed layout modes */\n allowedModes: DashboardLayoutMode[]\n\n // =========================================================================\n // Actions\n // =========================================================================\n actions: UseDashboardActions\n}\n\nexport interface UseDashboardActions {\n // Edit Mode\n enterEditMode: () => void\n exitEditMode: () => void\n toggleEditMode: () => void\n selectFilter: (filterId: string | null) => void\n exitFilterSelectionMode: () => void\n\n // Modals\n openAddPortlet: () => void\n openEditPortlet: (portlet: PortletConfig) => void\n closePortletModal: () => void\n openAddText: () => void\n openEditText: (portlet: PortletConfig) => void\n closeTextModal: () => void\n openFilterConfig: (portlet: PortletConfig) => void\n closeFilterConfig: () => void\n\n // Layout State (store-only)\n setDraftRows: (rows: RowLayout[] | null) => void\n setIsDraggingPortlet: (isDragging: boolean) => void\n setLastKnownLayout: (layout: LayoutItem[]) => void\n setIsInitialized: (initialized: boolean) => void\n setDragState: (state: { rowIndex: number; colIndex: number; portletId: string } | null) => void\n clearDragState: () => void\n\n // Layout Operations (config-modifying)\n hasLayoutActuallyChanged: (newLayout: LayoutItem[]) => boolean\n updateRowLayout: (rows: RowLayout[], save?: boolean, portletsOverride?: PortletConfig[]) => Promise<void>\n handleLayoutModeChange: (mode: DashboardLayoutMode) => Promise<void>\n\n // Portlet Operations\n savePortlet: (portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>) => Promise<string | null>\n deletePortlet: (portletId: string) => Promise<void>\n duplicatePortlet: (portletId: string) => Promise<string | undefined>\n refreshPortlet: (portletId: string, options?: { bustCache?: boolean }) => void\n\n // Filter Operations\n toggleFilterForPortlet: (portletId: string, filterId: string) => Promise<void>\n selectAllForFilter: (filterId: string) => Promise<void>\n saveFilterConfig: (mapping: DashboardFilterMapping) => Promise<void>\n\n // Config Operations\n handlePaletteChange: (paletteName: string) => Promise<void>\n\n // Delete Confirmation\n openDeleteConfirm: (portletId: string) => void\n closeDeleteConfirm: () => void\n confirmDelete: () => Promise<void>\n\n // Debug\n setDebugData: (portletId: string, data: PortletDebugDataEntry) => void\n clearDebugData: (portletId?: string) => void\n}\n\n// ============================================================================\n// Selectors\n// ============================================================================\n\nconst selectStoreState = (state: DashboardStore) => ({\n isEditMode: state.isEditMode,\n selectedFilterId: state.selectedFilterId,\n isPortletModalOpen: state.isPortletModalOpen,\n editingPortlet: state.editingPortlet,\n isTextModalOpen: state.isTextModalOpen,\n editingTextPortlet: state.editingTextPortlet,\n isFilterConfigModalOpen: state.isFilterConfigModalOpen,\n filterConfigPortlet: state.filterConfigPortlet,\n deleteConfirmPortletId: state.deleteConfirmPortletId,\n draftRows: state.draftRows,\n isDraggingPortlet: state.isDraggingPortlet,\n lastKnownLayout: state.lastKnownLayout,\n isInitialized: state.isInitialized,\n // NOTE: debugData intentionally excluded — DashboardPortletCard reads it directly from store.\n // Including it here would cause the entire hook to re-run on every portlet data load.\n})\n\nconst selectStoreActions = (state: DashboardStore) => ({\n setEditMode: state.setEditMode,\n toggleEditMode: state.toggleEditMode,\n setSelectedFilterId: state.setSelectedFilterId,\n exitFilterSelectionMode: state.exitFilterSelectionMode,\n openPortletModal: state.openPortletModal,\n closePortletModal: state.closePortletModal,\n openTextModal: state.openTextModal,\n closeTextModal: state.closeTextModal,\n openFilterConfigModal: state.openFilterConfigModal,\n closeFilterConfigModal: state.closeFilterConfigModal,\n openDeleteConfirm: state.openDeleteConfirm,\n closeDeleteConfirm: state.closeDeleteConfirm,\n setDraftRows: state.setDraftRows,\n setIsDraggingPortlet: state.setIsDraggingPortlet,\n setLastKnownLayout: state.setLastKnownLayout,\n setIsInitialized: state.setIsInitialized,\n setDragState: state.setDragState,\n clearDragState: state.clearDragState,\n setDebugData: state.setDebugData,\n clearDebugData: state.clearDebugData,\n setThumbnailDirty: state.setThumbnailDirty,\n})\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useDashboard(options: UseDashboardOptions): UseDashboardResult {\n const {\n config,\n editable = false,\n dashboardFilters,\n gridSettings,\n allowedModes: propAllowedModes,\n isResponsiveEditable = true,\n onConfigChange,\n onSave,\n onSaveThumbnail,\n portletComponentRefs,\n onPortletRefresh,\n dashboardRef,\n } = options\n\n // =========================================================================\n // Store Access\n // =========================================================================\n\n const storeState = useDashboardStore(useShallow(selectStoreState))\n const storeActions = useDashboardStore(useShallow(selectStoreActions))\n const storeApi = useDashboardStoreApi()\n\n // Get thumbnail feature config from context\n const { features } = useCubeFeatures()\n const thumbnailConfig = features.thumbnail\n\n // Refs for values used in stable callbacks (avoids recreating callbacks on every state change)\n const configRef = useRef(config)\n configRef.current = config\n const onConfigChangeRef = useRef(onConfigChange)\n onConfigChangeRef.current = onConfigChange\n const onSaveRef = useRef(onSave)\n onSaveRef.current = onSave\n const onSaveThumbnailRef = useRef(onSaveThumbnail)\n onSaveThumbnailRef.current = onSaveThumbnail\n\n // =========================================================================\n // Computed Values\n // =========================================================================\n\n const allowedModes: DashboardLayoutMode[] = useMemo(() => {\n return propAllowedModes && propAllowedModes.length > 0\n ? propAllowedModes\n : ['rows', 'grid']\n }, [propAllowedModes])\n\n const layoutMode: DashboardLayoutMode = useMemo(() => {\n const fallbackMode: DashboardLayoutMode = allowedModes.includes('rows')\n ? 'rows'\n : allowedModes[0] ?? 'grid'\n const configMode = config.layoutMode ?? 'grid'\n return allowedModes.includes(configMode) ? configMode : fallbackMode\n }, [config.layoutMode, allowedModes])\n\n const canEdit = useMemo(() => {\n return (\n editable &&\n storeState.isEditMode &&\n isResponsiveEditable &&\n !storeState.selectedFilterId\n )\n }, [editable, storeState.isEditMode, isResponsiveEditable, storeState.selectedFilterId])\n\n const canChangeLayoutMode = useMemo(() => {\n return (\n editable &&\n storeState.isEditMode &&\n isResponsiveEditable &&\n !storeState.selectedFilterId &&\n allowedModes.length > 1\n )\n }, [\n editable,\n storeState.isEditMode,\n isResponsiveEditable,\n storeState.selectedFilterId,\n allowedModes.length,\n ])\n\n const selectedFilter = useMemo(() => {\n if (!storeState.selectedFilterId || !dashboardFilters) return null\n return dashboardFilters.find((f) => f.id === storeState.selectedFilterId) ?? null\n }, [storeState.selectedFilterId, dashboardFilters])\n\n const { resolvedRows, updateRowLayout } = useRowLayoutEngine({\n layoutMode,\n draftRows: storeState.draftRows,\n config,\n gridSettings,\n configRef,\n onConfigChangeRef,\n onSaveRef,\n setDraftRows: storeActions.setDraftRows,\n setThumbnailDirty: storeActions.setThumbnailDirty,\n })\n\n const { hasLayoutActuallyChanged } = useGridLayoutEngine({\n storeApi,\n })\n\n const {\n enterEditMode,\n exitEditMode,\n toggleEditMode,\n selectFilter,\n openAddPortlet,\n openEditPortlet,\n openAddText,\n openEditText,\n openFilterConfig,\n handleLayoutModeChange,\n savePortlet,\n deletePortlet,\n confirmDelete,\n duplicatePortlet,\n refreshPortlet,\n toggleFilterForPortlet,\n selectAllForFilter,\n saveFilterConfig,\n handlePaletteChange,\n } = useDashboardController({\n allowedModes,\n canChangeLayoutMode,\n isResponsiveEditable,\n layoutMode,\n resolvedRows,\n gridSettings,\n thumbnailConfig,\n dashboardRef,\n storeApi,\n storeActions,\n configRef,\n onConfigChangeRef,\n onSaveRef,\n onSaveThumbnailRef,\n updateRowLayout,\n portletComponentRefs,\n onPortletRefresh,\n })\n\n // =========================================================================\n // Assemble Result\n // =========================================================================\n\n const actions: UseDashboardActions = useMemo(\n () => ({\n // Edit mode\n enterEditMode,\n exitEditMode,\n toggleEditMode,\n selectFilter,\n exitFilterSelectionMode: storeActions.exitFilterSelectionMode,\n\n // Modals\n openAddPortlet,\n openEditPortlet,\n closePortletModal: storeActions.closePortletModal,\n openAddText,\n openEditText,\n closeTextModal: storeActions.closeTextModal,\n openFilterConfig,\n closeFilterConfig: storeActions.closeFilterConfigModal,\n\n // Layout state\n setDraftRows: storeActions.setDraftRows,\n setIsDraggingPortlet: storeActions.setIsDraggingPortlet,\n setLastKnownLayout: storeActions.setLastKnownLayout,\n setIsInitialized: storeActions.setIsInitialized,\n setDragState: storeActions.setDragState,\n clearDragState: storeActions.clearDragState,\n\n // Layout operations\n hasLayoutActuallyChanged,\n updateRowLayout,\n handleLayoutModeChange,\n\n // Portlet operations\n savePortlet,\n deletePortlet,\n duplicatePortlet,\n refreshPortlet,\n\n // Filter operations\n toggleFilterForPortlet,\n selectAllForFilter,\n saveFilterConfig,\n\n // Config operations\n handlePaletteChange,\n\n // Delete confirmation\n openDeleteConfirm: storeActions.openDeleteConfirm,\n closeDeleteConfirm: storeActions.closeDeleteConfirm,\n confirmDelete,\n\n // Debug\n setDebugData: storeActions.setDebugData,\n clearDebugData: storeActions.clearDebugData,\n }),\n [\n enterEditMode,\n exitEditMode,\n toggleEditMode,\n selectFilter,\n storeActions,\n openAddPortlet,\n openEditPortlet,\n openAddText,\n openEditText,\n openFilterConfig,\n hasLayoutActuallyChanged,\n updateRowLayout,\n handleLayoutModeChange,\n savePortlet,\n deletePortlet,\n duplicatePortlet,\n refreshPortlet,\n toggleFilterForPortlet,\n selectAllForFilter,\n saveFilterConfig,\n handlePaletteChange,\n confirmDelete,\n ]\n )\n\n return {\n // Store state\n ...storeState,\n\n // Computed values\n canEdit,\n canChangeLayoutMode,\n selectedFilter,\n resolvedRows,\n layoutMode,\n allowedModes,\n\n // Actions\n actions,\n }\n}\n","/**\n * Shared helpers for the composable dashboard pieces.\n * Extracted verbatim from the former monolithic DashboardGrid so the coordinator,\n * toolbar, and grid surface can all reuse them.\n */\n\nimport type { CSSProperties } from 'react'\nimport type {\n DashboardConfig,\n DashboardGridSettings,\n RowLayoutColumn\n} from '../../types.js'\n\nexport const DEFAULT_GRID_SETTINGS: DashboardGridSettings = {\n cols: 12,\n rowHeight: 80,\n minW: 2,\n minH: 1\n}\n\nexport const createRowId = () => `row-${Date.now()}`\n\nexport const getGridSettings = (config: DashboardConfig): DashboardGridSettings => ({\n cols: config.grid?.cols ?? DEFAULT_GRID_SETTINGS.cols,\n rowHeight: config.grid?.rowHeight ?? DEFAULT_GRID_SETTINGS.rowHeight,\n minW: config.grid?.minW ?? DEFAULT_GRID_SETTINGS.minW,\n minH: config.grid?.minH ?? DEFAULT_GRID_SETTINGS.minH\n})\n\nexport const equalizeRowColumns = (\n portletIds: string[],\n gridSettings: DashboardGridSettings\n): RowLayoutColumn[] => {\n const count = portletIds.length\n if (count === 0) return []\n\n const { cols, minW } = gridSettings\n const minTotal = minW * count\n\n if (minTotal > cols) {\n const base = Math.floor(cols / count)\n const remainder = cols % count\n return portletIds.map((id, index) => ({\n portletId: id,\n w: base + (index < remainder ? 1 : 0)\n }))\n }\n\n const remaining = cols - minTotal\n const extra = Math.floor(remaining / count)\n const remainder = remaining % count\n\n return portletIds.map((id, index) => ({\n portletId: id,\n w: minW + extra + (index < remainder ? 1 : 0)\n }))\n}\n\nexport const adjustRowWidths = (\n columns: RowLayoutColumn[],\n gridSettings: DashboardGridSettings\n): RowLayoutColumn[] => {\n if (columns.length === 0) return []\n\n const { cols, minW } = gridSettings\n const adjusted = columns.map(column => ({\n ...column,\n w: Math.max(minW, column.w)\n }))\n\n let total = adjusted.reduce((sum, column) => sum + column.w, 0)\n if (total === cols) return adjusted\n\n if (total < cols) {\n let remaining = cols - total\n let index = 0\n while (remaining > 0) {\n adjusted[index % adjusted.length].w += 1\n remaining -= 1\n index += 1\n }\n return adjusted\n }\n\n let overflow = total - cols\n for (let index = adjusted.length - 1; index >= 0 && overflow > 0; index -= 1) {\n const column = adjusted[index]\n const reducible = Math.max(0, column.w - minW)\n if (reducible === 0) continue\n const delta = Math.min(reducible, overflow)\n column.w -= delta\n overflow -= delta\n }\n\n return adjusted\n}\n\n/**\n * Finds the nearest scrollable ancestor of an element.\n * Used to detect scroll container for lazy loading IntersectionObserver.\n */\nexport function findScrollableAncestor(element: HTMLElement | null): HTMLElement | null {\n if (!element) return null\n\n let current = element.parentElement\n\n while (current) {\n const style = window.getComputedStyle(current)\n const overflowY = style.overflowY\n const overflowX = style.overflowX\n\n const hasScrollableOverflow =\n overflowY === 'auto' || overflowY === 'scroll' ||\n overflowX === 'auto' || overflowX === 'scroll'\n\n const hasScrollContent =\n current.scrollHeight > current.clientHeight ||\n current.scrollWidth > current.clientWidth\n\n if (hasScrollableOverflow && hasScrollContent) {\n return current\n }\n\n if (current === document.body) break\n current = current.parentElement\n }\n\n return null // Use viewport\n}\n\n/** Inline \"Tt\" typography icon for Add Text buttons */\nexport function TextIcon({ className, style }: { className?: string; style?: CSSProperties }) {\n return (\n <svg className={className} style={style} viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n <text x=\"1\" y=\"20\" fontSize=\"20\" fontWeight=\"700\" fontFamily=\"serif\">T</text>\n <text x=\"14\" y=\"20\" fontSize=\"13\" fontWeight=\"600\" fontFamily=\"serif\">t</text>\n </svg>\n )\n}\n","/**\n * DashboardContext\n *\n * Published once by DashboardCoordinator and consumed by the composable dashboard\n * pieces (DashboardToolbar, DashboardFilterBar, DashboardGridSurface, DashboardModals)\n * as well as any host-supplied custom toolbar via useDashboardContext().\n *\n * The coordinator runs useDashboard() plus the responsive/scroll/drag hooks exactly\n * once; everything those produce is threaded through this context so the sibling\n * pieces never re-run the state machine.\n */\n\nimport {\n createContext,\n useContext,\n type ReactNode,\n type HTMLAttributes,\n type DragEvent,\n type MouseEvent,\n type MutableRefObject\n} from 'react'\nimport type { LayoutItem, Layout } from 'react-grid-layout'\nimport type { UseDashboardActions } from '../../hooks/useDashboardHook.js'\nimport type { DashboardDisplayMode } from '../../hooks/useResponsiveDashboard.js'\nimport type { ColorPalette } from '../../utils/colorPalettes.js'\nimport type {\n DashboardConfig,\n PortletConfig,\n DashboardFilter,\n DashboardFilterMapping,\n CubeMeta,\n DashboardLayoutMode,\n DashboardGridSettings,\n RowLayout,\n FeaturesConfig\n} from '../../types.js'\n\n/**\n * Props accepted by DashboardProvider (the public composable entry point).\n * Mirrors the former DashboardGridProps plus `hideToolbar` and `children`.\n */\nexport interface DashboardProviderProps {\n config: DashboardConfig\n editable?: boolean\n /** Dashboard-level filters to apply to portlets */\n dashboardFilters?: DashboardFilter[]\n /** Custom loading indicator for all portlets */\n loadingComponent?: ReactNode\n onConfigChange?: (config: DashboardConfig) => void\n onPortletRefresh?: (portletId: string) => void\n onSave?: (config: DashboardConfig) => Promise<void> | void\n /** Callback to save thumbnail separately - called on edit mode exit when thumbnail feature is enabled */\n onSaveThumbnail?: (thumbnailData: string) => Promise<string | void>\n /** Complete palette with both colors and gradient */\n colorPalette?: ColorPalette\n /** Cube metadata for filter panel */\n schema?: CubeMeta | null\n /** Handler for dashboard filter changes */\n onDashboardFiltersChange?: (filters: DashboardFilter[]) => void\n dashboardModes?: DashboardLayoutMode[]\n /** When true, DashboardToolbar renders nothing (both the top bar and floating toolbar) */\n hideToolbar?: boolean\n children: ReactNode\n}\n\n/**\n * Everything the composable pieces need. Built fresh each render by the coordinator,\n * matching the re-render cadence of the former monolithic DashboardGrid.\n */\nexport interface DashboardContextValue {\n // ---- Props threaded through ----\n config: DashboardConfig\n editable: boolean\n dashboardFilters?: DashboardFilter[]\n loadingComponent?: ReactNode\n colorPalette?: ColorPalette\n schema?: CubeMeta | null\n onSave?: (config: DashboardConfig) => Promise<void> | void\n onConfigChange?: (config: DashboardConfig) => void\n onDashboardFiltersChange?: (filters: DashboardFilter[]) => void\n hideToolbar?: boolean\n\n // ---- Store state (from useDashboard) ----\n isEditMode: boolean\n selectedFilterId: string | null\n isPortletModalOpen: boolean\n editingPortlet: PortletConfig | null\n isTextModalOpen: boolean\n editingTextPortlet: PortletConfig | null\n isFilterConfigModalOpen: boolean\n filterConfigPortlet: PortletConfig | null\n deleteConfirmPortletId: string | null\n draftRows: RowLayout[] | null\n isDraggingPortlet: boolean\n isInitialized: boolean\n\n // ---- Computed (from useDashboard) ----\n canEdit: boolean\n canChangeLayoutMode: boolean\n selectedFilter: DashboardFilter | null\n resolvedRows: RowLayout[]\n layoutMode: DashboardLayoutMode\n allowedModes: DashboardLayoutMode[]\n\n // ---- Actions ----\n actions: UseDashboardActions\n\n // ---- Responsive ----\n displayMode: DashboardDisplayMode\n scaleFactor: number\n designWidth: number\n gridWidth: number\n isResponsiveEditable: boolean\n\n // ---- Scroll / visibility ----\n isScrolled: boolean\n isEditBarVisible: boolean\n scrollContainer: HTMLElement | null\n\n // ---- Features ----\n features: FeaturesConfig\n\n // ---- Refs (attached by the consuming pieces) ----\n editBarRef: MutableRefObject<HTMLDivElement | null>\n gridContentRef: MutableRefObject<HTMLDivElement | null>\n\n // ---- Layout data ----\n gridSettings: DashboardGridSettings\n baseLayout: LayoutItem[]\n\n // ---- Render helpers ----\n renderPortletCard: (\n portlet: PortletConfig,\n containerProps?: HTMLAttributes<HTMLDivElement>,\n headerProps?: HTMLAttributes<HTMLDivElement>\n ) => ReactNode\n /** Renders the active (grid or row) layout. A function so it's not evaluated in mobile/empty cases. */\n renderActiveLayout: () => ReactNode\n\n // ---- Handlers ----\n handleAddPortlet: () => void\n handleAddText: () => void\n handlePaletteChange: (paletteName: string) => Promise<void>\n handleFilterSelect: (filterId: string) => void\n handleSelectAllForFilter: (filterId: string) => Promise<void>\n handleSaveFilterConfig: (mapping: DashboardFilterMapping) => Promise<void>\n handlePortletSave: (portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>) => Promise<void>\n handlePortletRefresh: (portletId: string, options?: { bustCache?: boolean }) => void\n handleLayoutChange: (layout: Layout) => void\n handleDragStop: (layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | null) => void\n handleResizeStop: (layout: Layout, oldItem: LayoutItem | null, newItem: LayoutItem | null, placeholder: LayoutItem | null, e: Event, element: HTMLElement | null) => void\n startRowResize: (rowIndex: number, event: MouseEvent<HTMLDivElement>) => void\n startColumnResize: (rowIndex: number, columnIndex: number, event: MouseEvent<HTMLDivElement>) => void\n handlePortletDragStart: (rowIndex: number, colIndex: number, portletId: string, event: DragEvent<HTMLDivElement>) => void\n handlePortletDragEnd: () => void\n handleRowDrop: (rowIndex: number, insertIndex: number | null) => void\n handleNewRowDrop: (insertIndex: number) => void\n}\n\nconst DashboardContext = createContext<DashboardContextValue | null>(null)\n\n/**\n * Access the dashboard context published by DashboardProvider.\n * Lets a host render its own toolbar/controls while drizzle-cube owns the\n * edit/save state machine.\n *\n * @throws if used outside a DashboardProvider (or DashboardGrid).\n */\nexport function useDashboardContext(): DashboardContextValue {\n const ctx = useContext(DashboardContext)\n if (!ctx) {\n throw new Error('useDashboardContext must be used within a DashboardProvider')\n }\n return ctx\n}\n\nexport default DashboardContext\n","/**\n * DashboardCoordinator\n *\n * Runs the dashboard state machine and all responsive/scroll/drag machinery exactly\n * once, then publishes everything through DashboardContext for the composable pieces.\n *\n * This is the former body of DashboardGrid — the JSX layer now lives in the sibling\n * piece components (DashboardToolbar / DashboardFilterBar / DashboardGridSurface /\n * DashboardModals), which read from context.\n */\n\nimport {\n useCallback,\n useRef,\n useState,\n useEffect,\n useMemo,\n type ReactNode,\n type HTMLAttributes,\n type DragEvent,\n type MouseEvent,\n type Ref\n} from 'react'\nimport ReactGridLayout, { verticalCompactor, type LayoutItem, type Layout } from 'react-grid-layout'\nimport { getIcon } from '../../icons/index.js'\nimport { useScrollDetection } from '../../hooks/useScrollDetection.js'\nimport { useElementVisibility } from '../../hooks/useElementVisibility.js'\nimport { useDragAutoScroll } from '../../hooks/useDragAutoScroll.js'\nimport DashboardPortletCard from '../DashboardPortletCard.js'\nimport RowManagedLayout from '../RowManagedLayout.js'\nimport { ensureAnalysisConfig } from '../../utils/configMigration.js'\nimport { useCubeFeatures } from '../../providers/CubeProvider.js'\nimport { useResponsiveDashboard } from '../../hooks/useResponsiveDashboard.js'\nimport { ScrollContainerProvider } from '../../providers/ScrollContainerContext.js'\nimport { useDashboard } from '../../hooks/useDashboardHook.js'\nimport type {\n PortletConfig,\n DashboardFilterMapping,\n DashboardLayoutMode,\n RowLayout\n} from '../../types.js'\nimport {\n getGridSettings,\n equalizeRowColumns,\n adjustRowWidths,\n createRowId,\n findScrollableAncestor\n} from './dashboardGridUtils.js'\nimport DashboardContext, {\n type DashboardContextValue,\n type DashboardProviderProps\n} from './DashboardContext.js'\n\nconst RefreshIcon = getIcon('refresh')\nconst EditIcon = getIcon('edit')\nconst DeleteIcon = getIcon('delete')\nconst CopyIcon = getIcon('copy')\nconst FilterIcon = getIcon('filter')\n\nexport default function DashboardCoordinator({\n config,\n editable = false,\n dashboardFilters,\n loadingComponent,\n onConfigChange,\n onPortletRefresh,\n onSave,\n onSaveThumbnail,\n colorPalette,\n schema,\n onDashboardFiltersChange,\n dashboardModes,\n hideToolbar,\n children\n}: DashboardProviderProps) {\n // Get features from context for conditional rendering\n const { features } = useCubeFeatures()\n\n // Responsive dashboard hook for three-tier layout strategy\n const {\n containerRef,\n containerWidth,\n displayMode,\n scaleFactor,\n isEditable: isResponsiveEditable,\n designWidth\n } = useResponsiveDashboard()\n\n // allowedModes is passed to useDashboard hook which computes layoutMode\n const allowedModes: DashboardLayoutMode[] = dashboardModes && dashboardModes.length > 0\n ? dashboardModes\n : ['rows', 'grid']\n const gridSettings = useMemo(() => getGridSettings(config), [config])\n\n // Scroll container detection for lazy loading\n // Null = viewport, element = scrolling container\n // Detection happens once in the ref callback to avoid double state updates\n const [scrollContainer, setScrollContainer] = useState<HTMLElement | null>(null)\n const containerElementRef = useRef<HTMLDivElement | null>(null)\n const scrollContainerRef = useRef<HTMLElement | null>(null)\n const editBarRef = useRef<HTMLDivElement | null>(null)\n // Separate ref for grid content area (used for thumbnail capture - excludes toolbar/filters)\n const gridContentRef = useRef<HTMLDivElement | null>(null)\n\n // Combined ref for container\n const combinedContainerRef = useCallback((node: HTMLDivElement | null) => {\n containerElementRef.current = node\n containerRef(node)\n if (node) {\n const foundScrollContainer = findScrollableAncestor(node)\n setScrollContainer(foundScrollContainer)\n scrollContainerRef.current = foundScrollContainer\n }\n }, [containerRef])\n\n // Calculate grid width based on display mode\n // Desktop: use actual container width (allows wider than 1200px)\n // Scaled: use design width (1200px) and apply CSS scaling\n const gridWidth = displayMode === 'desktop' ? containerWidth : designWidth\n\n // Refs to store portlet refs for refresh functionality (kept local - DOM-specific)\n const portletRefs = useRef<{ [key: string]: HTMLDivElement | null }>({})\n const portletComponentRefs = useRef<{ [key: string]: { refresh: () => void } | null }>({})\n const draftRowsRef = useRef<RowLayout[] | null>(null)\n // Local ref for tracking latest drag rows synchronously (avoids stale reads from useEffect-synced ref)\n const latestDragRowsRef = useRef<RowLayout[] | null>(null)\n const dragStateRef = useRef<{ rowIndex: number; colIndex: number; portletId: string } | null>(null)\n\n // =========================================================================\n // Dashboard State from Zustand Store via useDashboard hook\n // =========================================================================\n const dashboard = useDashboard({\n config,\n editable,\n dashboardFilters,\n gridSettings,\n allowedModes,\n isResponsiveEditable,\n onConfigChange,\n onSave,\n onSaveThumbnail,\n gridWidth,\n portletComponentRefs,\n onPortletRefresh,\n dashboardRef: gridContentRef, // For thumbnail capture on exit edit mode (grid content only, excludes toolbar/filters)\n })\n\n const {\n isEditMode,\n selectedFilterId,\n isPortletModalOpen,\n editingPortlet,\n isTextModalOpen,\n editingTextPortlet,\n isFilterConfigModalOpen,\n filterConfigPortlet,\n deleteConfirmPortletId,\n draftRows,\n isDraggingPortlet,\n isInitialized,\n canEdit,\n canChangeLayoutMode,\n selectedFilter,\n resolvedRows,\n layoutMode,\n actions,\n } = dashboard\n\n // Keep mutable references so high-frequency row interactions\n // don't force callback identity churn into memoized portlet cards.\n const actionsRef = useRef(actions)\n actionsRef.current = actions\n const canEditRef = useRef(canEdit)\n canEditRef.current = canEdit\n\n // Sync draftRowsRef with store state (for mouse event handlers)\n useEffect(() => {\n draftRowsRef.current = draftRows\n }, [draftRows])\n\n // Exit filter selection mode when leaving edit mode or when switching to non-desktop mode\n useEffect(() => {\n if ((!isEditMode || !isResponsiveEditable) && selectedFilterId) {\n actions.exitFilterSelectionMode()\n }\n }, [isEditMode, isResponsiveEditable, selectedFilterId, actions])\n\n // Exit edit mode when switching to non-desktop view\n useEffect(() => {\n if (!isResponsiveEditable && isEditMode) {\n actions.exitEditMode()\n }\n }, [isResponsiveEditable, isEditMode, actions])\n\n // Track scroll state for sticky header using debounced scroll detection\n const isScrolled = useScrollDetection(scrollContainerRef, {\n threshold: 20,\n debounceMs: 150,\n container: scrollContainer\n })\n\n // Track edit bar visibility for floating toolbar\n const isEditBarVisible = useElementVisibility(editBarRef, {\n threshold: 80,\n debounceMs: 100,\n containerRef: scrollContainerRef,\n container: scrollContainer\n })\n\n // Auto-scroll when dragging portlets near edges in row mode\n useDragAutoScroll(scrollContainerRef, {\n enabled: layoutMode === 'rows' && isDraggingPortlet,\n edgeThreshold: 80,\n maxScrollSpeed: 15\n })\n\n // Set up initialization tracking\n useEffect(() => {\n if (isInitialized) return\n\n // Mark as initialized after first render to prevent saves during load/resize\n const timer = setTimeout(() => {\n actions.setIsInitialized(true)\n // Store initial layout for comparison\n const initialLayout = config.portlets.map(portlet => ({\n i: portlet.id,\n x: portlet.x,\n y: portlet.y,\n w: portlet.w,\n h: portlet.h\n }))\n actions.setLastKnownLayout(initialLayout)\n }, 200) // Slightly longer delay to ensure responsive grid is fully settled\n\n return () => clearTimeout(timer)\n }, [isInitialized, config.portlets, actions])\n\n // Whether any dashboard modal is open - Enter/Esc belong to the modal then\n const isAnyModalOpen =\n isPortletModalOpen || isTextModalOpen || isFilterConfigModalOpen || !!deleteConfirmPortletId\n\n // Set up Enter/ESC key listener for filter selection mode\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (!selectedFilterId || isAnyModalOpen) return\n\n if (e.key === 'Escape') {\n actions.exitFilterSelectionMode()\n } else if (e.key === 'Enter') {\n // Ignore Enter on interactive elements so keyboard activation\n // (e.g. pressing a focused button) doesn't also exit the mode\n const target = e.target as HTMLElement | null\n if (target?.closest('button, a, input, select, textarea, [contenteditable=\"true\"]')) return\n actions.exitFilterSelectionMode()\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown)\n }\n }, [selectedFilterId, isAnyModalOpen, actions])\n\n const handleLayoutChange = useCallback((_layout: Layout) => {\n // This function is called for ALL layout changes\n // We should NOT save here - only update internal state if needed\n // Actual saving only happens in handleDragStop and handleResizeStop for explicit user actions\n }, [])\n\n // Handle drag stop - save when user finishes dragging (only if layout actually changed)\n const handleDragStop = useCallback(async (layout: Layout, _oldItem: LayoutItem | null, _newItem: LayoutItem | null, _placeholder: LayoutItem | null, _e: Event, _element: HTMLElement | null) => {\n if (!editable || !isEditMode || !onSave || !isInitialized) return\n\n // Only save if the layout actually changed from user interaction\n const mutableLayout = [...layout]\n if (!actions.hasLayoutActuallyChanged(mutableLayout)) {\n return // No actual change, don't save\n }\n\n // Get the current updated config (only called in desktop mode)\n const updatedPortlets = config.portlets.map(portlet => {\n const layoutItem = mutableLayout.find(item => item.i === portlet.id)\n if (layoutItem) {\n return {\n ...portlet,\n x: layoutItem.x,\n y: layoutItem.y,\n w: layoutItem.w,\n h: layoutItem.h\n }\n }\n return portlet\n })\n\n // Preserve existing responsive layouts, only update with new lg layout\n const updatedConfig = {\n ...config,\n portlets: updatedPortlets,\n layouts: {\n ...config.layouts,\n lg: mutableLayout // Only save the large layout, let RGL handle responsive adjustments\n }\n }\n\n // Update our tracking of the last known layout\n actions.setLastKnownLayout(mutableLayout)\n\n // Update config state first\n onConfigChange?.(updatedConfig)\n\n // Auto-save after drag operation\n try {\n await onSave(updatedConfig)\n } catch (error) {\n console.error('Auto-save failed after drag:', error)\n }\n }, [config, editable, isEditMode, onConfigChange, onSave, isInitialized, actions])\n\n // Handle resize stop - update config and save (resize is user interaction)\n const handleResizeStop = useCallback(async (layout: Layout, _oldItem: LayoutItem | null, _newItem: LayoutItem | null, _placeholder: LayoutItem | null, _e: Event, _element: HTMLElement | null) => {\n if (!editable || !isEditMode || !onConfigChange || !isInitialized) return\n\n // Only proceed if the layout actually changed from user interaction\n const mutableLayout = [...layout]\n if (!actions.hasLayoutActuallyChanged(mutableLayout)) {\n return // No actual change, don't save\n }\n\n // Get the current updated config (only called in desktop mode)\n const updatedPortlets = config.portlets.map(portlet => {\n const layoutItem = mutableLayout.find(item => item.i === portlet.id)\n if (layoutItem) {\n return {\n ...portlet,\n x: layoutItem.x,\n y: layoutItem.y,\n w: layoutItem.w,\n h: layoutItem.h\n }\n }\n return portlet\n })\n\n // Preserve existing responsive layouts, only update with new lg layout\n const updatedConfig = {\n ...config,\n portlets: updatedPortlets,\n layouts: {\n ...config.layouts,\n lg: mutableLayout // Only save the large layout, let RGL handle responsive adjustments\n }\n }\n\n // Update our tracking of the last known layout\n actions.setLastKnownLayout(mutableLayout)\n\n // Update config state\n onConfigChange(updatedConfig)\n\n // Auto-save after resize operation (user deliberately resized)\n if (onSave) {\n try {\n await onSave(updatedConfig)\n } catch (error) {\n console.error('Auto-save failed after resize:', error)\n }\n }\n }, [config, editable, isEditMode, onConfigChange, onSave, isInitialized, actions])\n\n const startRowResize = useCallback((rowIndex: number, event: MouseEvent<HTMLDivElement>) => {\n if (!canEdit) return\n event.preventDefault()\n\n const startY = event.clientY\n const startRows = resolvedRows.map(row => ({\n ...row,\n columns: row.columns.map(column => ({ ...column }))\n }))\n latestDragRowsRef.current = null\n\n const handleMouseMove = (moveEvent: globalThis.MouseEvent) => {\n const delta = moveEvent.clientY - startY\n const deltaUnits = Math.round(delta / gridSettings.rowHeight)\n const nextRows = startRows.map((row, index) => {\n if (index !== rowIndex) return row\n return {\n ...row,\n h: Math.max(gridSettings.minH, row.h + deltaUnits)\n }\n })\n latestDragRowsRef.current = nextRows\n actions.setDraftRows(nextRows)\n }\n\n const handleMouseUp = () => {\n document.removeEventListener('mousemove', handleMouseMove)\n document.removeEventListener('mouseup', handleMouseUp)\n const finalRows = latestDragRowsRef.current ?? startRows\n latestDragRowsRef.current = null\n actions.updateRowLayout(finalRows)\n }\n\n document.addEventListener('mousemove', handleMouseMove)\n document.addEventListener('mouseup', handleMouseUp)\n }, [canEdit, gridSettings, resolvedRows, actions])\n\n const startColumnResize = useCallback((rowIndex: number, columnIndex: number, event: MouseEvent<HTMLDivElement>) => {\n if (!canEdit) return\n event.preventDefault()\n\n const startX = event.clientX\n const startRows = resolvedRows.map(row => ({\n ...row,\n columns: row.columns.map(column => ({ ...column }))\n }))\n\n const row = startRows[rowIndex]\n const leftColumn = row?.columns[columnIndex]\n const rightColumn = row?.columns[columnIndex + 1]\n if (!row || !leftColumn || !rightColumn) return\n\n const columnGap = 16\n const rowContentWidth = gridWidth - (row.columns.length - 1) * columnGap\n const unitWidth = rowContentWidth / gridSettings.cols\n\n latestDragRowsRef.current = null\n\n const handleMouseMove = (moveEvent: globalThis.MouseEvent) => {\n const delta = moveEvent.clientX - startX\n const deltaUnits = Math.round(delta / unitWidth)\n if (deltaUnits === 0) {\n latestDragRowsRef.current = startRows\n actions.setDraftRows(startRows)\n return\n }\n\n let nextLeft = leftColumn.w + deltaUnits\n let nextRight = rightColumn.w - deltaUnits\n\n if (nextLeft < gridSettings.minW) {\n const diff = gridSettings.minW - nextLeft\n nextLeft = gridSettings.minW\n nextRight -= diff\n }\n\n if (nextRight < gridSettings.minW) {\n const diff = gridSettings.minW - nextRight\n nextRight = gridSettings.minW\n nextLeft -= diff\n }\n\n if (nextLeft < gridSettings.minW || nextRight < gridSettings.minW) return\n\n const nextRows = startRows.map((rowItem, index) => {\n if (index !== rowIndex) return rowItem\n const nextColumns = rowItem.columns.map((column, colIndex) => {\n if (colIndex === columnIndex) {\n return { ...column, w: nextLeft }\n }\n if (colIndex === columnIndex + 1) {\n return { ...column, w: nextRight }\n }\n return column\n })\n return {\n ...rowItem,\n columns: adjustRowWidths(nextColumns, gridSettings)\n }\n })\n latestDragRowsRef.current = nextRows\n actions.setDraftRows(nextRows)\n }\n\n const handleMouseUp = () => {\n document.removeEventListener('mousemove', handleMouseMove)\n document.removeEventListener('mouseup', handleMouseUp)\n const finalRows = latestDragRowsRef.current ?? startRows\n latestDragRowsRef.current = null\n actions.updateRowLayout(finalRows)\n }\n\n document.addEventListener('mousemove', handleMouseMove)\n document.addEventListener('mouseup', handleMouseUp)\n }, [canEdit, gridSettings, gridWidth, resolvedRows, actions])\n\n const handlePortletDragStart = useCallback((rowIndex: number, colIndex: number, portletId: string, event: DragEvent<HTMLDivElement>) => {\n if (!canEditRef.current) return\n dragStateRef.current = { rowIndex, colIndex, portletId }\n actionsRef.current.setIsDraggingPortlet(true)\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', portletId)\n }, [])\n\n const handlePortletDragEnd = useCallback(() => {\n dragStateRef.current = null\n actionsRef.current.setIsDraggingPortlet(false)\n }, [])\n\n const handleRowDrop = useCallback((rowIndex: number, insertIndex: number | null) => {\n const dragState = dragStateRef.current\n if (!dragState) return\n\n const nextRows = resolvedRows.map(row => ({\n ...row,\n columns: row.columns.map(column => ({ ...column }))\n }))\n\n const sourceRowIndex = dragState.rowIndex\n const sourceRow = nextRows[sourceRowIndex]\n if (!sourceRow) return\n\n const [movedColumn] = sourceRow.columns.splice(dragState.colIndex, 1)\n let sourceRowRemoved = false\n if (sourceRow.columns.length === 0) {\n nextRows.splice(sourceRowIndex, 1)\n sourceRowRemoved = true\n }\n\n let targetRowIndex = rowIndex\n if (sourceRowRemoved && sourceRowIndex < rowIndex) {\n targetRowIndex -= 1\n }\n\n const targetRow = nextRows[targetRowIndex]\n if (!targetRow) return\n\n let targetIndex = insertIndex ?? targetRow.columns.length\n if (!sourceRowRemoved && sourceRowIndex === targetRowIndex && insertIndex !== null) {\n if (insertIndex > dragState.colIndex) {\n targetIndex -= 1\n }\n }\n targetRow.columns.splice(targetIndex, 0, movedColumn)\n\n const movedBetweenRows = sourceRowIndex !== targetRowIndex || sourceRowRemoved\n if (movedBetweenRows) {\n if (!sourceRowRemoved) {\n nextRows[sourceRowIndex] = {\n ...nextRows[sourceRowIndex],\n columns: equalizeRowColumns(\n nextRows[sourceRowIndex].columns.map(column => column.portletId),\n gridSettings\n )\n }\n }\n nextRows[targetRowIndex] = {\n ...nextRows[targetRowIndex],\n columns: equalizeRowColumns(\n nextRows[targetRowIndex].columns.map(column => column.portletId),\n gridSettings\n )\n }\n }\n\n actions.updateRowLayout(nextRows)\n }, [gridSettings, resolvedRows, actions])\n\n const handleNewRowDrop = useCallback((insertIndex: number) => {\n const dragState = dragStateRef.current\n if (!dragState) return\n\n const nextRows = resolvedRows.map(row => ({\n ...row,\n columns: row.columns.map(column => ({ ...column }))\n }))\n\n const sourceRow = nextRows[dragState.rowIndex]\n if (!sourceRow) return\n\n const [movedColumn] = sourceRow.columns.splice(dragState.colIndex, 1)\n if (sourceRow.columns.length === 0) {\n nextRows.splice(dragState.rowIndex, 1)\n } else {\n sourceRow.columns = equalizeRowColumns(\n sourceRow.columns.map(column => column.portletId),\n gridSettings\n )\n }\n\n const newRow: RowLayout = {\n id: createRowId(),\n h: Math.max(gridSettings.minH, 3),\n columns: equalizeRowColumns([movedColumn.portletId], gridSettings)\n }\n nextRows.splice(insertIndex, 0, newRow)\n\n actions.updateRowLayout(nextRows)\n }, [gridSettings, resolvedRows, actions])\n\n // Handle portlet refresh - use action from hook\n // Pass { bustCache: true } to bypass client and server caches (shift+click)\n const handlePortletRefresh = useCallback((portletId: string, options?: { bustCache?: boolean }) => {\n actionsRef.current.refreshPortlet(portletId, options)\n }, [])\n\n // Handle portlet save with scroll-to-new behavior\n const handlePortletSave = useCallback(async (portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>) => {\n const newPortletId = await actions.savePortlet(portletData)\n actions.closePortletModal()\n\n // Scroll to the new portlet after DOM update\n if (newPortletId) {\n setTimeout(() => {\n const scrollToPortlet = () => {\n let portletElement: HTMLElement | null = portletRefs.current[newPortletId]\n if (!portletElement) {\n portletElement = document.querySelector(`[data-portlet-id=\"${newPortletId}\"]`)\n }\n\n if (portletElement) {\n portletElement.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n inline: 'nearest'\n })\n return true\n }\n return false\n }\n\n // Try multiple times with increasing delays\n if (!scrollToPortlet()) {\n setTimeout(() => {\n if (!scrollToPortlet()) {\n setTimeout(() => {\n scrollToPortlet()\n }, 300)\n }\n }, 200)\n }\n }, 200)\n }\n }, [actions])\n\n // Handle deleting portlet - delegate to hook action\n const handleDeletePortlet = useCallback(async (portletId: string) => {\n await actionsRef.current.deletePortlet(portletId)\n }, [])\n\n // Handle duplicating portlet - delegate to hook action with scroll to new portlet\n const handleDuplicatePortlet = useCallback(async (portletId: string) => {\n const newPortletId = await actionsRef.current.duplicatePortlet(portletId)\n\n // Scroll to the duplicated portlet after DOM update\n if (newPortletId) {\n setTimeout(() => {\n const scrollToPortlet = () => {\n let portletElement: HTMLElement | null = portletRefs.current[newPortletId]\n if (!portletElement) {\n portletElement = document.querySelector(`[data-portlet-id=\"${newPortletId}\"]`)\n }\n\n if (portletElement) {\n portletElement.scrollIntoView({\n behavior: 'smooth',\n block: 'center',\n inline: 'nearest'\n })\n return true\n }\n return false\n }\n\n // Try multiple times with increasing delays\n if (!scrollToPortlet()) {\n setTimeout(() => {\n if (!scrollToPortlet()) {\n setTimeout(() => {\n scrollToPortlet()\n }, 300)\n }\n }, 200)\n }\n }, 200)\n }\n }, [])\n\n // Handle adding new portlet - delegate to hook action\n const handleAddPortlet = useCallback(() => {\n actions.openAddPortlet()\n }, [actions])\n\n // Handle adding new text portlet - delegate to hook action\n const handleAddText = useCallback(() => {\n actions.openAddText()\n }, [actions])\n\n // Handle editing existing portlet - route markdown to text modal, others to analysis builder\n const handleEditPortlet = useCallback((portlet: PortletConfig) => {\n const normalized = ensureAnalysisConfig(portlet)\n const chartType = normalized.analysisConfig.charts[normalized.analysisConfig.analysisType]?.chartType\n if (chartType === 'markdown') {\n actionsRef.current.openEditText(portlet)\n } else {\n actionsRef.current.openEditPortlet(portlet)\n }\n }, [])\n\n // Handle palette change - delegate to hook action\n const handlePaletteChange = useCallback(async (paletteName: string) => {\n await actions.handlePaletteChange(paletteName)\n }, [actions])\n\n // Handle opening filter config modal - delegate to hook action\n const handleOpenFilterConfig = useCallback((portlet: PortletConfig) => {\n actionsRef.current.openFilterConfig(portlet)\n }, [])\n\n // Handle saving filter configuration - delegate to hook action\n const handleSaveFilterConfig = useCallback(async (mapping: DashboardFilterMapping) => {\n await actions.saveFilterConfig(mapping)\n }, [actions])\n\n // Memoized ref callbacks for DashboardPortletCard\n const handleSetPortletRef = useCallback((portletId: string, element: HTMLDivElement | null) => {\n portletRefs.current[portletId] = element\n }, [])\n\n const handleSetPortletComponentRef = useCallback((portletId: string, element: { refresh: () => void } | null) => {\n portletComponentRefs.current[portletId] = element\n }, [])\n\n // Memoized icons object to prevent re-renders\n const portletIcons = useMemo(() => ({\n RefreshIcon, EditIcon, DeleteIcon, CopyIcon, FilterIcon\n }), [])\n\n // Handle toggling filter for a portlet - delegate to hook action\n const handleToggleFilterForPortlet = useCallback(async (portletId: string, filterId: string) => {\n await actionsRef.current.toggleFilterForPortlet(portletId, filterId)\n }, [])\n\n // Handle filter selection (click on filter chip) - delegate to hook action\n const handleFilterSelect = useCallback((filterId: string) => {\n actions.selectFilter(filterId)\n }, [actions])\n\n // Handle select all - delegate to hook action\n const handleSelectAllForFilter = useCallback(async (filterId: string) => {\n await actions.selectAllForFilter(filterId)\n }, [actions])\n\n // Memoized callbacks object for DashboardPortletCard\n const portletCallbacks = useMemo(() => ({\n onToggleFilter: handleToggleFilterForPortlet,\n onRefresh: handlePortletRefresh,\n onDuplicate: handleDuplicatePortlet,\n onEdit: handleEditPortlet,\n onDelete: handleDeletePortlet,\n onOpenFilterConfig: handleOpenFilterConfig,\n }), [\n handleToggleFilterForPortlet,\n handlePortletRefresh,\n handleDuplicatePortlet,\n handleEditPortlet,\n handleDeletePortlet,\n handleOpenFilterConfig,\n ])\n\n // Memoize renderPortletCard to prevent unnecessary re-renders of DashboardPortletCard\n const renderPortletCard = useCallback((\n portlet: PortletConfig,\n containerProps?: HTMLAttributes<HTMLDivElement>,\n headerProps?: HTMLAttributes<HTMLDivElement>\n ): ReactNode => (\n <DashboardPortletCard\n portlet={portlet}\n editable={editable}\n layoutMode={layoutMode}\n dashboardFilters={dashboardFilters}\n configEagerLoad={config.eagerLoad}\n loadingComponent={loadingComponent}\n colorPalette={colorPalette}\n containerProps={containerProps}\n headerProps={headerProps}\n callbacks={portletCallbacks}\n setPortletRef={handleSetPortletRef}\n setPortletComponentRef={handleSetPortletComponentRef}\n icons={portletIcons}\n />\n ), [\n editable,\n layoutMode,\n dashboardFilters,\n config.eagerLoad,\n loadingComponent,\n colorPalette,\n portletCallbacks,\n handleSetPortletRef,\n handleSetPortletComponentRef,\n portletIcons\n ])\n\n // Generate grid layout from portlets - only use for lg (desktop) breakpoint\n const baseLayout: LayoutItem[] = config.portlets.map(portlet => ({\n i: portlet.id,\n x: portlet.x,\n y: portlet.y,\n w: portlet.w,\n h: portlet.h,\n minW: gridSettings.minW,\n minH: gridSettings.minH,\n // Only enable drag/resize in edit mode\n isDraggable: canEdit,\n isResizable: canEdit,\n ...(canEdit ? { resizeHandles: ['s', 'w', 'e', 'n', 'se', 'sw', 'ne', 'nw'] as const } : {})\n }))\n\n // Render the portlet grid content (shared between desktop and scaled modes)\n const renderGridContent = useCallback((): ReactNode => (\n <ReactGridLayout\n className=\"layout\"\n layout={baseLayout}\n onLayoutChange={handleLayoutChange}\n onDragStop={handleDragStop}\n onResizeStop={handleResizeStop}\n width={gridWidth}\n gridConfig={{\n cols: gridSettings.cols,\n rowHeight: gridSettings.rowHeight,\n margin: [16, 16],\n containerPadding: [0, 0]\n }}\n dragConfig={{\n enabled: canEdit,\n handle: '.portlet-drag-handle'\n }}\n resizeConfig={{\n enabled: canEdit,\n handles: ['s', 'w', 'e', 'n', 'se', 'sw', 'ne', 'nw'],\n // Invisible but functional resize handles\n handleComponent: (axis, ref) => (\n <div\n ref={ref as Ref<HTMLDivElement>}\n className={`react-resizable-handle react-resizable-handle-${axis}`}\n style={{ opacity: 0 }}\n />\n )\n }}\n compactor={verticalCompactor}\n >\n {config.portlets\n .filter(portlet => portlet && portlet.id) // Guard against malformed portlets\n .map(portlet => (\n <div key={portlet.id}>\n {renderPortletCard(portlet)}\n </div>\n ))}\n </ReactGridLayout>\n ), [baseLayout, handleLayoutChange, handleDragStop, handleResizeStop, gridWidth, gridSettings, canEdit, config.portlets, renderPortletCard])\n\n const renderRowContent = useCallback((): ReactNode => (\n <RowManagedLayout\n rows={resolvedRows}\n portlets={config.portlets}\n gridSettings={gridSettings}\n gridWidth={gridWidth}\n canEdit={canEdit}\n isDragging={isDraggingPortlet}\n onRowResize={startRowResize}\n onColumnResize={startColumnResize}\n onPortletDragStart={handlePortletDragStart}\n onPortletDragEnd={handlePortletDragEnd}\n onRowDrop={handleRowDrop}\n onNewRowDrop={handleNewRowDrop}\n renderPortlet={renderPortletCard}\n />\n ), [resolvedRows, config.portlets, gridSettings, gridWidth, canEdit, isDraggingPortlet, startRowResize, startColumnResize, handlePortletDragStart, handlePortletDragEnd, handleRowDrop, handleNewRowDrop, renderPortletCard])\n\n const renderActiveLayout = useCallback((): ReactNode => (\n layoutMode === 'rows' ? renderRowContent() : renderGridContent()\n ), [layoutMode, renderRowContent, renderGridContent])\n\n const value: DashboardContextValue = {\n // Props\n config,\n editable,\n dashboardFilters,\n loadingComponent,\n colorPalette,\n schema,\n onSave,\n onConfigChange,\n onDashboardFiltersChange,\n hideToolbar,\n\n // Store state\n isEditMode,\n selectedFilterId,\n isPortletModalOpen,\n editingPortlet,\n isTextModalOpen,\n editingTextPortlet,\n isFilterConfigModalOpen,\n filterConfigPortlet,\n deleteConfirmPortletId,\n draftRows,\n isDraggingPortlet,\n isInitialized,\n\n // Computed\n canEdit,\n canChangeLayoutMode,\n selectedFilter,\n resolvedRows,\n layoutMode,\n allowedModes,\n\n // Actions\n actions,\n\n // Responsive\n displayMode,\n scaleFactor,\n designWidth,\n gridWidth,\n isResponsiveEditable,\n\n // Scroll / visibility\n isScrolled,\n isEditBarVisible,\n scrollContainer,\n\n // Features\n features,\n\n // Refs\n editBarRef,\n gridContentRef,\n\n // Layout data\n gridSettings,\n baseLayout,\n\n // Render helpers\n renderPortletCard,\n renderActiveLayout,\n\n // Handlers\n handleAddPortlet,\n handleAddText,\n handlePaletteChange,\n handleFilterSelect,\n handleSelectAllForFilter,\n handleSaveFilterConfig,\n handlePortletSave,\n handlePortletRefresh,\n handleLayoutChange,\n handleDragStop,\n handleResizeStop,\n startRowResize,\n startColumnResize,\n handlePortletDragStart,\n handlePortletDragEnd,\n handleRowDrop,\n handleNewRowDrop,\n }\n\n return (\n <DashboardContext.Provider value={value}>\n <ScrollContainerProvider value={scrollContainer}>\n <div\n ref={combinedContainerRef}\n className=\"dashboard-grid-container dc:w-full\"\n style={{ maxWidth: '100%', overflow: 'hidden' }}\n >\n {children}\n </div>\n </ScrollContainerProvider>\n </DashboardContext.Provider>\n )\n}\n","/**\n * DashboardProvider\n *\n * Public composable entry point. Owns the per-instance Zustand store and runs the\n * coordinator that publishes DashboardContext. Compose the dashboard pieces (or your\n * own toolbar via useDashboardContext) as children:\n *\n * <DashboardProvider config={config} editable onSave={onSave}>\n * <DashboardToolbar />\n * <DashboardFilterBar />\n * <DashboardGridSurface />\n * <DashboardModals />\n * </DashboardProvider>\n */\n\nimport { DashboardStoreProvider } from '../../stores/dashboardStore.js'\nimport DashboardCoordinator from './DashboardCoordinator.js'\nimport type { DashboardProviderProps } from './DashboardContext.js'\n\nexport default function DashboardProvider({ children, ...props }: DashboardProviderProps) {\n return (\n <DashboardStoreProvider>\n <DashboardCoordinator {...props}>{children}</DashboardCoordinator>\n </DashboardStoreProvider>\n )\n}\n","/**\n * Unified Color Palette System\n * Each palette contains coordinated series and gradient colors that work well together\n */\n\nexport interface ColorPalette {\n name: string\n label: string\n colors: string[] // For series-based charts (bar, line, pie, area, scatter, radar, etc.)\n gradient: string[] // For gradient-based charts (bubble, activity grid)\n}\n\n// Predefined color palettes with visually coordinated series and gradient colors\nexport const COLOR_PALETTES: ColorPalette[] = [\n {\n name: 'default',\n label: 'Default',\n colors: [\n '#3b82f6', // blue\n '#10b981', // green\n '#f59e0b', // yellow\n '#ef4444', // red\n '#8b5cf6', // purple\n '#f97316', // orange\n '#06b6d4', // cyan\n '#84cc16', // lime\n ],\n gradient: [\n '#fde725', // yellow (light - for low values)\n '#7ad151', // green\n '#22a884', // green-teal\n '#2a788e', // teal\n '#414487', // purple-blue\n '#440154', // dark purple (dark - for high values)\n ]\n },\n {\n name: 'ocean',\n label: 'Ocean',\n colors: [\n '#1e3a8a', // deep blue\n '#1e40af', // blue\n '#2563eb', // bright blue\n '#3b82f6', // light blue\n '#06b6d4', // cyan\n '#0891b2', // dark cyan\n '#0e7490', // teal\n '#0f766e', // dark teal\n ],\n gradient: [\n '#38bdf8', // cyan blue (light - for low values)\n '#0ea5e9', // light blue\n '#0284c7', // bright blue\n '#0369a1', // medium blue\n '#075985', // dark blue\n '#0c4a6e', // very dark blue (dark - for high values)\n ]\n },\n {\n name: 'sunset',\n label: 'Sunset',\n colors: [\n '#dc2626', // red\n '#ea580c', // orange-red\n '#f59e0b', // orange\n '#eab308', // yellow-orange\n '#d97706', // amber\n '#b45309', // dark amber\n '#92400e', // brown\n '#7c2d12', // dark brown\n ],\n gradient: [\n '#fbbf24', // light orange (light - for low values)\n '#f59e0b', // orange\n '#d97706', // amber\n '#b45309', // dark amber\n '#92400e', // brown\n '#7c2d12', // dark brown (dark - for high values)\n ]\n },\n {\n name: 'forest',\n label: 'Forest',\n colors: [\n '#166534', // dark green\n '#15803d', // green\n '#16a34a', // bright green\n '#22c55e', // light green\n '#4ade80', // lighter green\n '#65a30d', // lime green\n '#84cc16', // lime\n '#a3e635', // light lime\n ],\n gradient: [\n '#4ade80', // lighter green (light - for low values)\n '#22c55e', // light green\n '#16a34a', // bright green\n '#15803d', // green\n '#166534', // dark green\n '#14532d', // very dark green (dark - for high values)\n ]\n },\n {\n name: 'purple',\n label: 'Purple',\n colors: [\n '#581c87', // dark purple\n '#7c3aed', // purple\n '#8b5cf6', // bright purple\n '#a855f7', // light purple\n '#c084fc', // lighter purple\n '#e879f9', // magenta\n '#f0abfc', // light magenta\n '#fbbf24', // accent yellow\n ],\n gradient: [\n '#a855f7', // light purple (light - for low values)\n '#8b5cf6', // bright purple\n '#7c3aed', // purple\n '#6d28d9', // medium purple\n '#581c87', // dark purple\n '#4c1d95', // very dark purple (dark - for high values)\n ]\n },\n {\n name: 'monochrome',\n label: 'Monochrome',\n colors: [\n '#1f2937', // very dark gray\n '#374151', // dark gray\n '#4b5563', // medium gray\n '#6b7280', // gray\n '#9ca3af', // light gray\n '#d1d5db', // lighter gray\n '#e5e7eb', // very light gray\n '#f3f4f6', // almost white\n ],\n gradient: [\n '#9ca3af', // light gray (light - for low values)\n '#6b7280', // gray\n '#4b5563', // medium gray\n '#374151', // dark gray\n '#1f2937', // very dark gray\n '#111827', // black (dark - for high values)\n ]\n },\n {\n name: 'pastel',\n label: 'Pastel',\n colors: [\n '#93c5fd', // light blue\n '#86efac', // light green\n '#fde047', // light yellow\n '#fca5a5', // light red\n '#c4b5fd', // light purple\n '#fdba74', // light orange\n '#67e8f9', // light cyan\n '#bef264', // light lime\n ],\n gradient: [\n '#fed7aa', // very light orange (light - for low values)\n '#ddd6fe', // very light purple\n '#fecaca', // very light red\n '#fef08a', // very light yellow\n '#a7f3d0', // very light green\n '#bfdbfe', // very light blue (darker - for high values)\n ]\n },\n {\n name: 'vibrant',\n label: 'Vibrant',\n colors: [\n '#0000ff', // pure blue\n '#00ff00', // pure green\n '#ffff00', // pure yellow\n '#ff0000', // pure red\n '#ff00ff', // pure magenta\n '#ff8000', // pure orange\n '#00ffff', // pure cyan\n '#8000ff', // pure violet\n ],\n gradient: [\n '#ffff00', // yellow (light - for low values)\n '#80ff00', // lime\n '#00ff80', // green\n '#00ffff', // cyan\n '#0080ff', // blue\n '#4000ff', // blue-violet (dark - for high values)\n ]\n },\n {\n name: 'd3Category10',\n label: 'D3 Category 10',\n colors: [\n '#1f77b4', // blue\n '#ff7f0e', // orange\n '#2ca02c', // green\n '#d62728', // red\n '#9467bd', // purple\n '#8c564b', // brown\n '#e377c2', // pink\n '#7f7f7f', // gray\n '#bcbd22', // olive\n '#17becf', // cyan\n ],\n gradient: [\n '#9467bd', // purple (light - for low values)\n '#d62728', // red\n '#ff7f0e', // orange\n '#bcbd22', // olive\n '#2ca02c', // green\n '#1f77b4', // blue (dark - for high values)\n ]\n },\n {\n name: 'd3Tableau10',\n label: 'D3 Tableau 10',\n colors: [\n '#4e79a7', // blue\n '#f28e2c', // orange\n '#e15759', // red\n '#76b7b2', // teal\n '#59a14f', // green\n '#edc949', // yellow\n '#af7aa1', // purple\n '#ff9da7', // pink\n '#9c755f', // brown\n '#bab0ab', // gray\n ],\n gradient: [\n '#e15759', // red (light - for low values)\n '#f28e2c', // orange\n '#edc949', // yellow\n '#59a14f', // green\n '#76b7b2', // teal\n '#4e79a7', // blue (dark - for high values)\n ]\n },\n {\n name: 'colorBrewerSet1',\n label: 'ColorBrewer Set 1',\n colors: [\n '#e41a1c', // red\n '#377eb8', // blue\n '#4daf4a', // green\n '#984ea3', // purple\n '#ff7f00', // orange\n '#ffff33', // yellow\n '#a65628', // brown\n '#f781bf', // pink\n '#999999', // gray\n ],\n gradient: [\n '#984ea3', // purple (light - for low values)\n '#e41a1c', // red\n '#ff7f00', // orange\n '#ffff33', // yellow\n '#4daf4a', // green\n '#377eb8', // blue (dark - for high values)\n ]\n },\n {\n name: 'colorBrewerSet2',\n label: 'ColorBrewer Set 2',\n colors: [\n '#66c2a5', // teal\n '#fc8d62', // orange\n '#8da0cb', // blue\n '#e78ac3', // pink\n '#a6d854', // lime\n '#ffd92f', // yellow\n '#e5c494', // tan\n '#b3b3b3', // gray\n ],\n gradient: [\n '#e78ac3', // pink (light - for low values)\n '#fc8d62', // orange\n '#ffd92f', // yellow\n '#a6d854', // lime\n '#66c2a5', // teal\n '#8da0cb', // blue (dark - for high values)\n ]\n },\n {\n name: 'colorBrewerDark2',\n label: 'ColorBrewer Dark 2',\n colors: [\n '#1b9e77', // dark teal\n '#d95f02', // dark orange\n '#7570b3', // dark blue\n '#e7298a', // dark pink\n '#66a61e', // dark green\n '#e6ab02', // dark yellow\n '#a6761d', // dark brown\n '#666666', // dark gray\n ],\n gradient: [\n '#e7298a', // dark pink (light - for low values)\n '#d95f02', // dark orange\n '#e6ab02', // dark yellow\n '#66a61e', // dark green\n '#1b9e77', // dark teal\n '#7570b3', // dark blue (dark - for high values)\n ]\n },\n {\n name: 'colorBrewerPaired',\n label: 'ColorBrewer Paired',\n colors: [\n '#a6cee3', // light blue\n '#1f78b4', // blue\n '#b2df8a', // light green\n '#33a02c', // green\n '#fb9a99', // light red\n '#e31a1c', // red\n '#fdbf6f', // light orange\n '#ff7f00', // orange\n '#cab2d6', // light purple\n '#6a3d9a', // purple\n '#ffff99', // light yellow\n '#b15928', // brown\n ],\n gradient: [\n '#6a3d9a', // purple (light - for low values)\n '#e31a1c', // red\n '#ff7f00', // orange\n '#ffff99', // light yellow\n '#33a02c', // green\n '#1f78b4', // blue (dark - for high values)\n ]\n },\n {\n name: 'viridis',\n label: 'Viridis',\n colors: [\n '#440154', // dark purple\n '#482677', // purple\n '#3f4a8a', // blue-purple\n '#31678e', // blue\n '#26838f', // teal\n '#1f9d8a', // green-teal\n '#6cce5a', // green\n '#b6de2b', // yellow-green\n ],\n gradient: [\n '#b6de2b', // yellow-green (light - for low values)\n '#6cce5a', // green\n '#1f9d8a', // green-teal\n '#26838f', // teal\n '#31678e', // blue\n '#3f4a8a', // blue-purple\n '#482677', // purple\n '#440154', // dark purple (dark - for high values)\n ]\n },\n {\n name: 'plasma',\n label: 'Plasma',\n colors: [\n '#0c0786', // dark blue\n '#5c01a6', // purple\n '#900da4', // magenta\n '#bf3984', // pink\n '#e16462', // coral\n '#f99b45', // orange\n '#fcce25', // yellow\n '#f0f921', // bright yellow\n ],\n gradient: [\n '#f0f921', // bright yellow (light - for low values)\n '#fcce25', // yellow\n '#f99b45', // orange\n '#e16462', // coral\n '#bf3984', // pink\n '#900da4', // magenta\n '#5c01a6', // purple\n '#0c0786', // dark blue (dark - for high values)\n ]\n },\n {\n name: 'inferno',\n label: 'Inferno',\n colors: [\n '#000003', // black\n '#1f0c47', // dark blue\n '#550f6d', // purple\n '#88226a', // magenta\n '#a83655', // red\n '#cc4f39', // orange-red\n '#e6862a', // orange\n '#fec228', // yellow\n ],\n gradient: [\n '#fec228', // yellow (light - for low values)\n '#e6862a', // orange\n '#cc4f39', // orange-red\n '#a83655', // red\n '#88226a', // magenta\n '#550f6d', // purple\n '#1f0c47', // dark blue\n '#000003', // black (dark - for high values)\n ]\n },\n {\n name: 'magma',\n label: 'Magma',\n colors: [\n '#000003', // black\n '#140b34', // dark purple\n '#3b0f6f', // purple\n '#641a80', // magenta\n '#8b2981', // pink\n '#b63679', // coral\n '#de4968', // red\n '#fd9f6c', // orange\n ],\n gradient: [\n '#fd9f6c', // orange (light - for low values)\n '#de4968', // red\n '#b63679', // coral\n '#8b2981', // pink\n '#641a80', // magenta\n '#3b0f6f', // purple\n '#140b34', // dark purple\n '#000003', // black (dark - for high values)\n ]\n },\n {\n name: 'cividis',\n label: 'Cividis',\n colors: [\n '#00204c', // dark blue\n '#003f5c', // blue\n '#2c4b7a', // blue\n '#51576f', // blue-gray\n '#7f6874', // gray\n '#a8786e', // brown\n '#d2906d', // orange\n '#ffb570', // yellow\n ],\n gradient: [\n '#ffb570', // yellow (light - for low values)\n '#d2906d', // orange\n '#a8786e', // brown\n '#7f6874', // gray\n '#51576f', // blue-gray\n '#2c4b7a', // blue\n '#003f5c', // blue\n '#00204c', // dark blue (dark - for high values)\n ]\n },\n {\n name: 'turbo',\n label: 'Turbo',\n colors: [\n '#30123b', // purple\n '#4454c4', // blue\n '#1dd3c0', // cyan\n '#42f465', // green\n '#b2df22', // lime\n '#faba39', // yellow\n '#f66c19', // orange\n '#c42e02', // red\n ],\n gradient: [\n '#c42e02', // red (light - for low values)\n '#f66c19', // orange\n '#faba39', // yellow\n '#b2df22', // lime\n '#42f465', // green\n '#1dd3c0', // cyan\n '#4454c4', // blue\n '#30123b', // purple (dark - for high values)\n ]\n },\n {\n name: 'warm',\n label: 'Warm',\n colors: [\n '#8b0000', // dark red\n '#b22222', // red\n '#cd5c5c', // light red\n '#ff6347', // tomato\n '#ff8c00', // dark orange\n '#ffa500', // orange\n '#ffd700', // gold\n '#ffff00', // yellow\n ],\n gradient: [\n '#ffd700', // gold (light - for low values)\n '#ffa500', // orange\n '#ff8c00', // dark orange\n '#ff6347', // tomato\n '#b22222', // red\n '#8b0000', // dark red (dark - for high values)\n ]\n },\n {\n name: 'cool',\n label: 'Cool',\n colors: [\n '#000080', // navy\n '#0000ff', // blue\n '#4169e1', // royal blue\n '#00bfff', // deep sky blue\n '#00ffff', // cyan\n '#40e0d0', // turquoise\n '#20b2aa', // light sea green\n '#008b8b', // dark cyan\n ],\n gradient: [\n '#40e0d0', // turquoise (light - for low values)\n '#00ffff', // cyan\n '#00bfff', // deep sky blue\n '#4169e1', // royal blue\n '#0000ff', // blue\n '#000080', // navy (dark - for high values)\n ]\n },\n {\n name: 'earth',\n label: 'Earth',\n colors: [\n '#8b4513', // saddle brown\n '#a0522d', // sienna\n '#cd853f', // peru\n '#daa520', // goldenrod\n '#d2691e', // chocolate\n '#bc8f8f', // rosy brown\n '#f4a460', // sandy brown\n '#deb887', // burlywood\n ],\n gradient: [\n '#f4a460', // sandy brown (light - for low values)\n '#d2691e', // chocolate\n '#daa520', // goldenrod\n '#cd853f', // peru\n '#a0522d', // sienna\n '#8b4513', // saddle brown (dark - for high values)\n ]\n },\n {\n name: 'autumn',\n label: 'Autumn',\n colors: [\n '#8b0000', // dark red\n '#a0522d', // sienna\n '#cd853f', // peru\n '#daa520', // goldenrod\n '#ff8c00', // dark orange\n '#ff4500', // orange red\n '#dc143c', // crimson\n '#b22222', // fire brick\n ],\n gradient: [\n '#ff4500', // orange red (light - for low values)\n '#ff8c00', // dark orange\n '#daa520', // goldenrod\n '#cd853f', // peru\n '#a0522d', // sienna\n '#8b0000', // dark red (dark - for high values)\n ]\n },\n {\n name: 'spring',\n label: 'Spring',\n colors: [\n '#32cd32', // lime green\n '#98fb98', // pale green\n '#90ee90', // light green\n '#ffb6c1', // light pink\n '#ffc0cb', // pink\n '#ffffe0', // light yellow\n '#f0fff0', // honeydew\n '#e0ffff', // light cyan\n ],\n gradient: [\n '#e0ffff', // light cyan (light - for low values)\n '#ffc0cb', // pink\n '#ffb6c1', // light pink\n '#ffffe0', // light yellow\n '#98fb98', // pale green\n '#32cd32', // lime green (dark - for high values)\n ]\n },\n {\n name: 'winter',\n label: 'Winter',\n colors: [\n '#191970', // midnight blue\n '#4682b4', // steel blue\n '#87ceeb', // sky blue\n '#b0e0e6', // powder blue\n '#e0ffff', // light cyan\n '#f0f8ff', // alice blue\n '#c0c0c0', // silver\n '#708090', // slate gray\n ],\n gradient: [\n '#f0f8ff', // alice blue (light - for low values)\n '#e0ffff', // light cyan\n '#b0e0e6', // powder blue\n '#87ceeb', // sky blue\n '#4682b4', // steel blue\n '#191970', // midnight blue (dark - for high values)\n ]\n },\n {\n name: 'neon',\n label: 'Neon',\n colors: [\n '#ff0080', // neon pink\n '#00ff80', // neon green\n '#8000ff', // neon purple\n '#ff8000', // neon orange\n '#0080ff', // neon blue\n '#80ff00', // neon lime\n '#ff0040', // neon red\n '#40ff00', // bright green\n ],\n gradient: [\n '#ff0080', // neon pink (light - for low values)\n '#ff8000', // neon orange\n '#80ff00', // neon lime\n '#00ff80', // neon green\n '#0080ff', // neon blue\n '#8000ff', // neon purple (dark - for high values)\n ]\n },\n {\n name: 'retro',\n label: 'Retro',\n colors: [\n '#ff69b4', // hot pink\n '#ffd700', // gold\n '#32cd32', // lime green\n '#00ced1', // dark turquoise\n '#ff6347', // tomato\n '#9370db', // medium purple\n '#ffa500', // orange\n '#20b2aa', // light sea green\n ],\n gradient: [\n '#00ced1', // dark turquoise (light - for low values)\n '#32cd32', // lime green\n '#ffd700', // gold\n '#ff6347', // tomato\n '#ff69b4', // hot pink\n '#9370db', // medium purple (dark - for high values)\n ]\n },\n {\n name: 'corporate',\n label: 'Corporate',\n colors: [\n '#003366', // dark blue\n '#0066cc', // blue\n '#336699', // steel blue\n '#6699cc', // light blue\n '#4d4d4d', // dark gray\n '#808080', // gray\n '#b3b3b3', // light gray\n '#cccccc', // very light gray\n ],\n gradient: [\n '#b3b3b3', // light gray (light - for low values)\n '#808080', // gray\n '#6699cc', // light blue\n '#336699', // steel blue\n '#0066cc', // blue\n '#003366', // dark blue (dark - for high values)\n ]\n },\n {\n name: 'material',\n label: 'Material Design',\n colors: [\n '#f44336', // red\n '#e91e63', // pink\n '#9c27b0', // purple\n '#673ab7', // deep purple\n '#3f51b5', // indigo\n '#2196f3', // blue\n '#03a9f4', // light blue\n '#00bcd4', // cyan\n ],\n gradient: [\n '#e91e63', // pink (light - for low values)\n '#03a9f4', // light blue\n '#00bcd4', // cyan\n '#2196f3', // blue\n '#3f51b5', // indigo\n '#673ab7', // deep purple (dark - for high values)\n ]\n }\n]\n\n/**\n * Get a color palette by name, with fallback to default\n */\nexport function getColorPalette(paletteName?: string): ColorPalette {\n if (!paletteName) {\n return COLOR_PALETTES[0] // default palette\n }\n \n const palette = COLOR_PALETTES.find(p => p.name === paletteName)\n return palette || COLOR_PALETTES[0] // fallback to default\n}\n\n/**\n * Get just the series colors for a palette\n */\nexport function getSeriesColors(paletteName?: string): string[] {\n return getColorPalette(paletteName).colors\n}\n\n/**\n * Get just the gradient colors for a palette\n */\nexport function getGradientColors(paletteName?: string): string[] {\n return getColorPalette(paletteName).gradient\n}\n\n/**\n * Chart types that use series colors (discrete categories)\n */\nexport const SERIES_CHART_TYPES = [\n 'bar', 'line', 'area', 'pie', 'scatter', 'radar', 'radialBar', 'treeMap'\n] as const\n\n/**\n * Chart types that use gradient colors (continuous values)\n */\nexport const GRADIENT_CHART_TYPES = [\n 'bubble', 'activityGrid'\n] as const\n\n/**\n * Determine if a chart type uses gradient colors\n */\nexport function usesGradientColors(chartType: string): boolean {\n return GRADIENT_CHART_TYPES.includes(chartType as any)\n}","/**\n * FloatingEditToolbar - Vertical floating toolbar for dashboard editing\n *\n * Appears when the static edit bar scrolls out of view, providing quick access\n * to edit controls in a compact vertical format with icon-only buttons.\n *\n * Features:\n * - Icon-only buttons with tooltips\n * - Configurable left/right positioning\n * - Smooth slide-in/out animation\n * - Only visible on desktop (≥1200px)\n * - Compact palette dropdown that opens opposite to toolbar position\n */\n\nimport React, { useState, useRef, useEffect } from 'react'\nimport { createPortal } from 'react-dom'\nimport { getIcon } from '../icons/index.js'\nimport { COLOR_PALETTES } from '../utils/colorPalettes.js'\nimport type { DashboardLayoutMode } from '../types.js'\n\nconst EditIcon = getIcon('edit')\nconst CheckIcon = getIcon('check')\nconst GridIcon = getIcon('segment')\nconst RowsIcon = getIcon('table')\nconst AddIcon = getIcon('add')\nfunction TextIcon({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n <text x=\"1\" y=\"20\" fontSize=\"20\" fontWeight=\"700\" fontFamily=\"serif\">T</text>\n <text x=\"14\" y=\"20\" fontSize=\"13\" fontWeight=\"600\" fontFamily=\"serif\">t</text>\n </svg>\n )\n}\nconst SwatchIcon = getIcon('swatch')\n\ninterface FloatingEditToolbarProps {\n /** Whether the static edit bar is visible (toolbar hidden when true) */\n isEditBarVisible: boolean\n /** Position of the floating toolbar */\n position: 'left' | 'right'\n /** Whether currently in edit mode */\n isEditMode: boolean\n /** Toggle edit mode on/off */\n onEditModeToggle: () => void\n /** Current layout mode */\n layoutMode: DashboardLayoutMode\n /** Change layout mode */\n onLayoutModeChange: (mode: DashboardLayoutMode) => void\n /** Available layout modes */\n allowedModes: DashboardLayoutMode[]\n /** Whether layout mode can be changed */\n canChangeLayoutMode: boolean\n /** Current color palette name */\n currentPalette: string\n /** Change color palette */\n onPaletteChange: (palette: string) => void\n /** Add new portlet */\n onAddPortlet: () => void\n /** Add new text portlet */\n onAddText?: () => void\n}\n\nexport default function FloatingEditToolbar({\n isEditBarVisible,\n position,\n isEditMode,\n onEditModeToggle,\n layoutMode,\n onLayoutModeChange,\n allowedModes,\n canChangeLayoutMode,\n currentPalette,\n onPaletteChange,\n onAddPortlet,\n onAddText\n}: FloatingEditToolbarProps) {\n const [isPaletteOpen, setIsPaletteOpen] = useState(false)\n const paletteRef = useRef<HTMLDivElement>(null)\n\n // Close palette dropdown on outside click\n useEffect(() => {\n if (!isPaletteOpen) return\n\n const handleClickOutside = (e: MouseEvent) => {\n if (paletteRef.current && !paletteRef.current.contains(e.target as Node)) {\n setIsPaletteOpen(false)\n }\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [isPaletteOpen])\n\n // Close palette when toolbar hides\n useEffect(() => {\n if (isEditBarVisible) {\n setIsPaletteOpen(false)\n }\n }, [isEditBarVisible])\n\n // Position classes - fixed positioning with vertical centering\n const positionClasses = position === 'left'\n ? 'dc:left-4'\n : 'dc:right-4'\n\n // Animation: slide in from edge when edit bar not visible\n // When edit bar IS visible, slide out and hide\n const isHidden = isEditBarVisible\n const translateClasses = position === 'left'\n ? (isHidden ? 'dc:-translate-x-16 dc:opacity-0 dc:pointer-events-none' : 'dc:translate-x-0 dc:opacity-100')\n : (isHidden ? 'dc:translate-x-16 dc:opacity-0 dc:pointer-events-none' : 'dc:translate-x-0 dc:opacity-100')\n\n // Use portal to render outside the grid container to avoid react-grid-layout issues\n const toolbarContent = (\n <div\n className={`dc:fixed dc:top-1/2 dc:-translate-y-1/2 dc:z-50 dc:flex dc:flex-col dc:gap-1.5 dc:p-2\n bg-dc-surface-tertiary dc:border border-dc-border dc:rounded-lg\n dc:transition-all dc:duration-300 dc:ease-in-out\n ${positionClasses} ${translateClasses}`}\n style={{\n boxShadow: 'var(--dc-shadow-lg)'\n }}\n >\n {/* Edit Toggle */}\n <ToolbarButton\n icon={isEditMode ? CheckIcon : EditIcon}\n tooltip={isEditMode ? 'Finish Editing' : 'Edit Dashboard'}\n isActive={isEditMode}\n onClick={onEditModeToggle}\n />\n\n {/* Layout Mode Switcher - only in edit mode with multiple modes */}\n {isEditMode && allowedModes.length > 1 && (\n <>\n <div className=\"dc:w-full dc:h-px bg-dc-border dc:my-0.5\" />\n <ToolbarButton\n icon={GridIcon}\n tooltip=\"Grid Layout\"\n isActive={layoutMode === 'grid'}\n disabled={!canChangeLayoutMode}\n onClick={() => onLayoutModeChange('grid')}\n />\n <ToolbarButton\n icon={RowsIcon}\n tooltip=\"Rows Layout\"\n isActive={layoutMode === 'rows'}\n disabled={!canChangeLayoutMode}\n onClick={() => onLayoutModeChange('rows')}\n />\n </>\n )}\n\n {/* Color Palette - only in edit mode */}\n {isEditMode && (\n <>\n <div className=\"dc:w-full dc:h-px bg-dc-border dc:my-0.5\" />\n <div ref={paletteRef} className=\"dc:relative\">\n <ToolbarButton\n icon={SwatchIcon}\n tooltip=\"Color Palette\"\n isActive={isPaletteOpen}\n onClick={() => setIsPaletteOpen(!isPaletteOpen)}\n />\n {isPaletteOpen && (\n <CompactPaletteDropdown\n position={position}\n currentPalette={currentPalette}\n onPaletteChange={(palette) => {\n onPaletteChange(palette)\n setIsPaletteOpen(false)\n }}\n />\n )}\n </div>\n </>\n )}\n\n {/* Add Portlet / Add Text - only in edit mode */}\n {isEditMode && (\n <>\n <div className=\"dc:w-full dc:h-px bg-dc-border dc:my-0.5\" />\n {onAddText && (\n <ToolbarButton\n icon={TextIcon}\n tooltip=\"Add Text\"\n onClick={onAddText}\n />\n )}\n <ToolbarButton\n icon={AddIcon}\n tooltip=\"Add Portlet\"\n onClick={onAddPortlet}\n />\n </>\n )}\n </div>\n )\n\n // Render via portal to document.body to avoid react-grid-layout child iteration issues\n if (typeof document === 'undefined') {\n return null // SSR safety\n }\n\n return createPortal(toolbarContent, document.body)\n}\n\n// Internal ToolbarButton component\ninterface ToolbarButtonProps {\n icon: React.ComponentType<{ className?: string }>\n tooltip: string\n isActive?: boolean\n disabled?: boolean\n onClick: () => void\n}\n\nfunction ToolbarButton({ icon: Icon, tooltip, isActive, disabled, onClick }: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n title={tooltip}\n className={`dc:p-2 dc:rounded-md dc:transition-colors focus:outline-hidden dc:focus:ring-2 focus:ring-dc-accent\n ${disabled\n ? 'dc:opacity-50 dc:cursor-not-allowed bg-dc-surface-secondary text-dc-text-muted'\n : isActive\n ? 'bg-dc-accent-bg text-dc-accent'\n : 'bg-dc-surface text-dc-text-secondary hover:bg-dc-surface-hover'\n }`}\n >\n <Icon className=\"dc:w-5 dc:h-5\" />\n </button>\n )\n}\n\n// Compact palette dropdown for floating toolbar\ninterface CompactPaletteDropdownProps {\n position: 'left' | 'right'\n currentPalette: string\n onPaletteChange: (palette: string) => void\n}\n\nfunction CompactPaletteDropdown({ position, currentPalette, onPaletteChange }: CompactPaletteDropdownProps) {\n // Position dropdown opposite to toolbar position to avoid off-screen\n const positionClasses = position === 'left' ? 'dc:left-full dc:ml-2' : 'dc:right-full dc:mr-2'\n\n return (\n <div\n className={`dc:absolute dc:top-0 ${positionClasses} dc:w-52 bg-dc-surface dc:border border-dc-border dc:rounded-md dc:z-50 dc:max-h-72 dc:overflow-y-auto`}\n style={{\n boxShadow: 'var(--dc-shadow-lg)'\n }}\n >\n <div className=\"dc:py-1\">\n {COLOR_PALETTES.slice().sort((a, b) => a.label.localeCompare(b.label)).map((palette) => (\n <button\n key={palette.name}\n type=\"button\"\n onClick={() => onPaletteChange(palette.name)}\n className={`dc:w-full dc:px-3 dc:py-2 dc:text-left dc:text-sm hover:bg-dc-surface-hover dc:transition-colors ${\n palette.name === currentPalette ? 'bg-dc-surface-secondary text-dc-primary' : 'text-dc-text-secondary'\n }`}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n {/* Compact color preview - 4 series colors */}\n <div className=\"dc:flex dc:gap-0.5 dc:shrink-0\">\n {palette.colors.slice(0, 4).map((color, index) => (\n <div\n key={index}\n className=\"dc:w-2.5 dc:h-2.5 rounded-xs dc:border border-dc-border\"\n style={{ backgroundColor: color }}\n />\n ))}\n </div>\n <span className=\"dc:text-xs dc:font-medium dc:truncate text-dc-text\">{palette.label}</span>\n {/* Current indicator */}\n {palette.name === currentPalette && (\n <div className=\"dc:ml-auto dc:w-1.5 dc:h-1.5 dc:rounded-full dc:shrink-0 bg-dc-primary\" />\n )}\n </div>\n </button>\n ))}\n </div>\n </div>\n )\n}\n","/**\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 * Grid / Rows layout-mode toggle used inside the dashboard edit bar.\n */\n\nimport type { ReactNode } from 'react'\nimport { getIcon } from '../../icons/index.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\nconst GridIcon = getIcon('segment')\nconst RowsIcon = getIcon('table')\n\ninterface LayoutModeToggleProps {\n layoutMode: string\n canChangeLayoutMode: boolean\n onLayoutModeChange: (mode: 'grid' | 'rows') => void\n}\n\ninterface ModeButtonProps {\n active: boolean\n disabled: boolean\n onClick: () => void\n icon: ReactNode\n label: string\n}\n\nfunction ModeButton({ active, disabled, onClick, icon, label }: ModeButtonProps) {\n return (\n <button\n onClick={onClick}\n disabled={disabled}\n className={`dc:inline-flex dc:items-center dc:gap-2 dc:whitespace-nowrap dc:px-3 dc:py-1.5 dc:text-sm dc:font-medium dc:transition-colors dc:border-b-2 ${\n active\n ? 'bg-dc-accent-bg text-dc-accent border-b-dc-accent'\n : 'bg-dc-surface text-dc-text-secondary hover:bg-dc-surface-hover border-b-transparent'\n } ${disabled ? 'dc:cursor-not-allowed dc:opacity-50' : ''}`}\n >\n {icon}\n {label}\n </button>\n )\n}\n\nexport default function LayoutModeToggle({\n layoutMode,\n canChangeLayoutMode,\n onLayoutModeChange\n}: LayoutModeToggleProps) {\n const { t } = useTranslation()\n\n return (\n <div className=\"dc:inline-flex dc:rounded-md dc:border border-dc-border dc:overflow-hidden dc:whitespace-nowrap\">\n <ModeButton\n active={layoutMode === 'grid'}\n disabled={!canChangeLayoutMode}\n onClick={() => onLayoutModeChange('grid')}\n icon={<GridIcon className=\"dc:w-4 dc:h-4 dc:shrink-0\" />}\n label={t('dashboard.grid')}\n />\n <ModeButton\n active={layoutMode === 'rows'}\n disabled={!canChangeLayoutMode}\n onClick={() => onLayoutModeChange('rows')}\n icon={<RowsIcon className=\"dc:w-4 dc:h-4 dc:shrink-0\" />}\n label={t('dashboard.rows')}\n />\n </div>\n )\n}\n","/**\n * Sticky top edit bar for the dashboard toolbar. Reads everything from\n * DashboardContext; rendered only when the top toolbar variant is active.\n */\n\nimport { getIcon } from '../../icons/index.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport ColorPaletteSelector from '../ColorPaletteSelector.js'\nimport { TextIcon } from './dashboardGridUtils.js'\nimport { useDashboardContext } from './DashboardContext.js'\nimport LayoutModeToggle from './LayoutModeToggle.js'\n\nconst EditIcon = getIcon('edit')\nconst CheckIcon = getIcon('check')\nconst AddIcon = getIcon('add')\nconst DesktopIcon = getIcon('desktop')\n\n/** The Edit / Finish-editing toggle button. */\nfunction EditToggleButton({\n isEditMode,\n isResponsiveEditable,\n onToggle\n}: {\n isEditMode: boolean\n isResponsiveEditable: boolean\n onToggle: () => void\n}) {\n const { t } = useTranslation()\n\n const stateClass = !isResponsiveEditable\n ? 'dc:opacity-50 dc:cursor-not-allowed bg-dc-surface-secondary dc:border border-dc-border'\n : isEditMode\n ? 'bg-dc-surface-secondary dc:border border-dc-border hover:bg-dc-surface-hover'\n : 'bg-dc-surface dc:border border-dc-border hover:bg-dc-surface-hover'\n\n return (\n <button\n onClick={() => isResponsiveEditable && onToggle()}\n disabled={!isResponsiveEditable}\n className={`dc:inline-flex dc:items-center dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md dc:transition-colors focus:outline-hidden dc:focus:ring-2 dc:focus:ring-offset-2 ${stateClass}`}\n style={{\n color: !isResponsiveEditable ? 'var(--dc-text-muted)' : 'var(--dc-primary)',\n borderColor: !isResponsiveEditable ? 'var(--dc-border)' : isEditMode ? 'var(--dc-border)' : 'var(--dc-primary)'\n }}\n >\n {isEditMode ? <CheckIcon className=\"dc:w-4 dc:h-4 dc:mr-1.5\" /> : <EditIcon className=\"dc:w-4 dc:h-4 dc:mr-1.5\" />}\n {isEditMode ? t('dashboard.finishEditing') : t('dashboard.edit')}\n </button>\n )\n}\n\n/** Right-hand edit actions: palette, add text, add portlet. */\nfunction EditActions({\n colorPalette,\n onPaletteChange,\n onAddText,\n onAddPortlet\n}: {\n colorPalette: string | undefined\n onPaletteChange: (palette: string) => void\n onAddText: () => void\n onAddPortlet: () => void\n}) {\n const { t } = useTranslation()\n\n return (\n <div className=\"dc:flex dc:items-center dc:gap-3\">\n <ColorPaletteSelector\n currentPalette={colorPalette}\n onPaletteChange={onPaletteChange}\n className=\"dc:shrink-0\"\n />\n\n <button\n onClick={onAddText}\n className=\"dc:inline-flex dc:items-center dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:border dc:rounded-md focus:outline-hidden dc:focus:ring-2 dc:focus:ring-offset-2 border-dc-border bg-dc-surface hover:bg-dc-surface-hover\"\n style={{ color: 'var(--dc-text-secondary)', borderColor: 'var(--dc-border)' }}\n >\n <TextIcon className=\"dc:w-5 dc:h-5 dc:mr-2\" />\n {t('dashboard.addText')}\n </button>\n\n <button\n onClick={onAddPortlet}\n className=\"dc:inline-flex dc:items-center dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:border dc:rounded-md focus:outline-hidden dc:focus:ring-2 dc:focus:ring-offset-2 border-dc-border bg-dc-surface hover:bg-dc-surface-hover\"\n style={{ color: 'var(--dc-primary)', borderColor: 'var(--dc-primary)' }}\n >\n <AddIcon className=\"dc:w-5 dc:h-5 dc:mr-2\" />\n {t('dashboard.addPortlet')}\n </button>\n </div>\n )\n}\n\nexport default function DashboardEditBar() {\n const { t } = useTranslation()\n const {\n isEditMode,\n isResponsiveEditable,\n layoutMode,\n allowedModes,\n canChangeLayoutMode,\n isScrolled,\n editBarRef,\n config,\n actions,\n handleAddText,\n handleAddPortlet,\n handlePaletteChange,\n } = useDashboardContext()\n\n return (\n <div\n ref={editBarRef}\n className={`dc:mb-4 dc:flex dc:justify-between dc:items-center dc:sticky dc:top-0 dc:z-10 dc:px-4 dc:py-4 bg-dc-surface-tertiary dc:border border-dc-border dc:rounded-lg dc:transition-all dc:duration-200 ${\n isScrolled ? 'dc:border-b' : ''\n }`}\n style={{ boxShadow: isScrolled ? 'var(--dc-shadow-md)' : 'var(--dc-shadow-sm)' }}\n >\n <div className=\"dc:flex dc:items-center dc:gap-4\">\n <EditToggleButton\n isEditMode={isEditMode}\n isResponsiveEditable={isResponsiveEditable}\n onToggle={actions.toggleEditMode}\n />\n {isEditMode && allowedModes.length > 1 && (\n <LayoutModeToggle\n layoutMode={layoutMode}\n canChangeLayoutMode={canChangeLayoutMode}\n onLayoutModeChange={actions.handleLayoutModeChange}\n />\n )}\n {!isResponsiveEditable && (\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:text-sm text-dc-text-secondary\">\n <DesktopIcon className=\"dc:w-4 dc:h-4\" />\n <span>{t('dashboard.desktopRequired')}</span>\n </div>\n )}\n {isEditMode && isResponsiveEditable && (\n <p className=\"dc:hidden dc:md:block dc:text-sm text-dc-text-secondary\">\n {t('dashboard.editModeHint')}\n </p>\n )}\n </div>\n\n {/* Color Palette Selector and Add Portlet - Only show in edit mode */}\n {isEditMode && (\n <EditActions\n colorPalette={config.colorPalette}\n onPaletteChange={handlePaletteChange}\n onAddText={handleAddText}\n onAddPortlet={handleAddPortlet}\n />\n )}\n </div>\n )\n}\n","/**\n * DashboardToolbar\n *\n * The bundled action toolbar: the sticky top edit bar (Edit / layout-mode toggle /\n * palette / Add Text / Add Portlet) plus the FloatingEditToolbar that appears when the\n * top bar scrolls out of view.\n *\n * Reads everything from DashboardContext. Renders nothing when `hideToolbar` is set, so\n * a host can either omit this component or pass `hideToolbar` to suppress it.\n */\n\nimport FloatingEditToolbar from '../FloatingEditToolbar.js'\nimport { useDashboardContext } from './DashboardContext.js'\nimport DashboardEditBar from './DashboardEditBar.js'\n\nexport default function DashboardToolbar() {\n const {\n editable,\n hideToolbar,\n features,\n displayMode,\n isEditMode,\n isResponsiveEditable,\n layoutMode,\n allowedModes,\n canChangeLayoutMode,\n isEditBarVisible,\n config,\n actions,\n } = useDashboardContext()\n\n if (!editable || hideToolbar) return null\n\n return (\n <>\n {features.editToolbar !== 'floating' && <DashboardEditBar />}\n\n {/* Floating Edit Toolbar - appears when top edit bar scrolls out of view (or always if editToolbar='floating') */}\n {features.editToolbar !== 'top' && displayMode === 'desktop' && (\n <FloatingEditToolbar\n isEditBarVisible={features.editToolbar === 'floating' ? false : isEditBarVisible}\n position={features.floatingToolbarPosition || 'right'}\n isEditMode={isEditMode}\n onEditModeToggle={() => isResponsiveEditable && actions.toggleEditMode()}\n layoutMode={layoutMode}\n onLayoutModeChange={actions.handleLayoutModeChange}\n allowedModes={allowedModes}\n canChangeLayoutMode={canChangeLayoutMode}\n currentPalette={config.colorPalette || 'default'}\n onPaletteChange={actions.handlePaletteChange}\n onAddPortlet={actions.openAddPortlet}\n onAddText={actions.openAddText}\n />\n )}\n </>\n )\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 * 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","/**\n * dashboardFilterConfigModalUtils\n *\n * Pure helpers extracted from DashboardFilterConfigModal to keep the component's\n * effects and handlers flat. Behaviour is identical to the previous inline logic.\n *\n * `deriveRangeFromDateRange` lives in the shared `dateRangeUtils` module and is\n * re-exported here to keep existing import paths stable.\n */\n\nexport type { DerivedRange } from '../shared/dateRangeUtils.js'\nexport { deriveRangeFromDateRange } from '../shared/dateRangeUtils.js'\n\n/**\n * Compute the next filter `values` for a direct text/number input change.\n * Mirrors the previous inline `handleDirectInput` logic exactly:\n * - For number-typed operators: keep numeric values, clear on empty/partial sign.\n * - For everything else: store the raw string (or clear when empty).\n *\n * Returns `null` when the current values should be left untouched (e.g. an\n * unparseable number that is neither empty nor a lone \"-\").\n */\nexport function computeDirectInputValues(\n value: string,\n valueType: string | undefined\n): unknown[] | null {\n if (valueType === 'number') {\n const numValue = parseFloat(value)\n if (!isNaN(numValue)) {\n return [numValue]\n }\n if (value === '' || value === '-') {\n return []\n }\n return null\n }\n return value ? [value] : []\n}\n","/**\n * useFilterDropdowns\n *\n * Owns the open/closed state of the three mutually-exclusive popover dropdowns\n * (operator, value, date-range) plus the click-outside effect that closes them\n * all. This is the single \"which dropdown is open\" concern; it exposes the\n * container ref the effect watches and the individual open flags/setters.\n *\n * Behaviour is identical to the previous inline implementation — same effect,\n * same dependency array, same setters.\n */\n\nimport { useState, useRef, useEffect } from 'react'\n\nexport function useFilterDropdowns() {\n const containerRef = useRef<HTMLDivElement>(null)\n\n const [isOperatorDropdownOpen, setIsOperatorDropdownOpen] = useState(false)\n const [isValueDropdownOpen, setIsValueDropdownOpen] = useState(false)\n const [isDateRangeDropdownOpen, setIsDateRangeDropdownOpen] = useState(false)\n\n // Close dropdowns when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(event.target as Node)) {\n setIsOperatorDropdownOpen(false)\n setIsValueDropdownOpen(false)\n setIsDateRangeDropdownOpen(false)\n }\n }\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [])\n\n return {\n containerRef,\n isOperatorDropdownOpen,\n setIsOperatorDropdownOpen,\n isValueDropdownOpen,\n setIsValueDropdownOpen,\n isDateRangeDropdownOpen,\n setIsDateRangeDropdownOpen\n }\n}\n\nexport type UseFilterDropdowns = ReturnType<typeof useFilterDropdowns>\n","/**\n * useFilterValueFetch\n *\n * Owns the combo-box value concern: the search text, its debounce, the distinct\n * value list fetched from `useFilterValues`, the effects that (re)load options\n * when the value dropdown opens or the search text changes, and the\n * select/remove handlers that write the chosen value(s) back onto the filter.\n *\n * Behaviour is identical to the previous inline implementation — same effects,\n * same dependency arrays, same handler bodies.\n */\n\nimport { useState, useEffect, useCallback } from 'react'\nimport type { SimpleFilter } from '../../types.js'\nimport { useFilterValues } from '../../hooks/useFilterValues.js'\nimport { useDebounce } from '../../hooks/useDebounce.js'\n\ninterface OperatorMetaLike {\n supportsMultipleValues?: boolean\n}\n\ninterface UseFilterValueFetchParams {\n localFilter: SimpleFilter\n setLocalFilter: (filter: SimpleFilter) => void\n operatorMeta: OperatorMetaLike | undefined\n shouldShowComboBox: boolean\n isValueDropdownOpen: boolean\n setIsValueDropdownOpen: (open: boolean) => void\n}\n\nexport function useFilterValueFetch({\n localFilter,\n setLocalFilter,\n operatorMeta,\n shouldShowComboBox,\n isValueDropdownOpen,\n setIsValueDropdownOpen\n}: UseFilterValueFetchParams) {\n const [searchText, setSearchText] = useState('')\n\n // Debounce search text for API calls\n const debouncedSearchText = useDebounce(searchText, 300)\n\n // Fetch distinct values for combo box\n const {\n values: distinctValues,\n loading: valuesLoading,\n error: valuesError,\n searchValues\n } = useFilterValues(localFilter.member, shouldShowComboBox)\n\n // Load values when dropdown opens\n useEffect(() => {\n if (isValueDropdownOpen && shouldShowComboBox && searchValues) {\n searchValues('', true)\n }\n }, [isValueDropdownOpen, shouldShowComboBox, searchValues])\n\n // Search when debounced text changes\n useEffect(() => {\n if (isValueDropdownOpen && shouldShowComboBox && searchValues && debouncedSearchText !== undefined) {\n searchValues(debouncedSearchText)\n }\n }, [debouncedSearchText, isValueDropdownOpen, shouldShowComboBox, searchValues])\n\n // Handle value selection from combo box\n const handleValueSelect = useCallback((value: unknown) => {\n const values = localFilter.values || []\n if (operatorMeta?.supportsMultipleValues) {\n if (!values.includes(value)) {\n setLocalFilter({ ...localFilter, values: [...values, value] })\n }\n } else {\n setLocalFilter({ ...localFilter, values: [value] })\n setIsValueDropdownOpen(false)\n }\n setSearchText('')\n }, [localFilter, operatorMeta?.supportsMultipleValues, setLocalFilter, setIsValueDropdownOpen])\n\n // Handle value removal\n const handleValueRemove = useCallback((valueToRemove: unknown) => {\n const values = (localFilter.values || []).filter((v: unknown) => v !== valueToRemove)\n setLocalFilter({ ...localFilter, values })\n }, [localFilter, setLocalFilter])\n\n return {\n searchText,\n setSearchText,\n distinctValues,\n valuesLoading,\n valuesError,\n handleValueSelect,\n handleValueRemove\n }\n}\n\nexport type UseFilterValueFetch = ReturnType<typeof useFilterValueFetch>\n","/**\n * useDateRangeState\n *\n * Owns the date-range editing concern for time fields using `inDateRange`:\n * the selected preset (`rangeType`), the \"last N\" number input (`numberValue`),\n * the effect that syncs those back from the filter's `dateRange`, and the four\n * handlers that translate UI changes into a new `dateRange` on the filter.\n *\n * Behaviour is identical to the previous inline implementation — same effect,\n * same dependency arrays, same handler bodies.\n */\n\nimport { useState, useEffect, useCallback, ChangeEvent } from 'react'\nimport type { SimpleFilter } from '../../types.js'\nimport type { DateRangeType } from '../../shared/types.js'\nimport {\n convertDateRangeTypeToValue,\n requiresNumberInput\n} from '../../shared/utils.js'\nimport { deriveRangeFromDateRange } from './dashboardFilterConfigModalUtils.js'\n\ninterface UseDateRangeStateParams {\n localFilter: SimpleFilter\n setLocalFilter: (filter: SimpleFilter) => void\n shouldShowDateRange: boolean\n setIsDateRangeDropdownOpen: (open: boolean) => void\n}\n\nexport function useDateRangeState({\n localFilter,\n setLocalFilter,\n shouldShowDateRange,\n setIsDateRangeDropdownOpen\n}: UseDateRangeStateParams) {\n const [rangeType, setRangeType] = useState<DateRangeType>('this_month')\n const [numberValue, setNumberValue] = useState(1)\n\n // Sync rangeType state with filter.dateRange\n useEffect(() => {\n if (!shouldShowDateRange) return\n const derived = deriveRangeFromDateRange(localFilter.dateRange)\n if (!derived) return\n setRangeType(derived.rangeType)\n if (derived.numberValue !== undefined) {\n setNumberValue(derived.numberValue)\n }\n }, [localFilter.dateRange, shouldShowDateRange])\n\n // Handle date range type change\n const handleRangeTypeChange = useCallback((newRangeType: DateRangeType) => {\n setRangeType(newRangeType)\n setIsDateRangeDropdownOpen(false)\n\n let dateRange: string | string[]\n if (newRangeType === 'custom') {\n const today = new Date().toISOString().split('T')[0]\n dateRange = [today, today]\n } else if (requiresNumberInput(newRangeType)) {\n dateRange = convertDateRangeTypeToValue(newRangeType, numberValue)\n } else {\n dateRange = convertDateRangeTypeToValue(newRangeType)\n }\n\n setLocalFilter({ ...localFilter, dateRange } as SimpleFilter)\n }, [localFilter, numberValue, setLocalFilter, setIsDateRangeDropdownOpen])\n\n // Handle number value change for \"last N days/weeks/etc\"\n const handleNumberValueChange = useCallback((value: number) => {\n setNumberValue(value)\n if (requiresNumberInput(rangeType)) {\n const dateRange = convertDateRangeTypeToValue(rangeType, value)\n setLocalFilter({ ...localFilter, dateRange } as SimpleFilter)\n }\n }, [localFilter, rangeType, setLocalFilter])\n\n // Handle custom date range inputs\n const handleCustomStartDate = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const start = e.target.value\n const currentRange = Array.isArray(localFilter.dateRange) ? localFilter.dateRange : [localFilter.dateRange || '', '']\n const end = currentRange[1] || start\n setLocalFilter({ ...localFilter, dateRange: [start, end] } as SimpleFilter)\n }, [localFilter, setLocalFilter])\n\n const handleCustomEndDate = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const end = e.target.value\n const currentRange = Array.isArray(localFilter.dateRange) ? localFilter.dateRange : ['', localFilter.dateRange || '']\n const start = currentRange[0] || end\n setLocalFilter({ ...localFilter, dateRange: [start, end] } as SimpleFilter)\n }, [localFilter, setLocalFilter])\n\n return {\n rangeType,\n numberValue,\n handleRangeTypeChange,\n handleNumberValueChange,\n handleCustomStartDate,\n handleCustomEndDate\n }\n}\n\nexport type UseDateRangeState = ReturnType<typeof useDateRangeState>\n","/**\n * useDashboardFilterConfigModal\n *\n * Thin orchestrator behind DashboardFilterConfigModal. It owns the small core\n * editing state (label + filter) and derives field/operator metadata, then\n * composes three focused sub-hooks, each owning a single state+effect concern:\n *\n * - useFilterDropdowns — which popover (operator/value/date-range) is open\n * - useFilterValueFetch — combo-box value search + distinct-value fetching\n * - useDateRangeState — date-range preset / \"last N\" state + sync\n *\n * The remaining field/operator/save handlers stay here. The return is grouped\n * into sub-objects by concern (`dropdowns`, `values`, `dateRange`, `field`)\n * rather than one flat bag. Behaviour is identical to the previous inline\n * implementation — same state, same effects, same handlers.\n */\n\nimport { useState, useEffect, useCallback, ChangeEvent } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport type { DashboardFilter, SimpleFilter, FilterOperator } from '../../types.js'\nimport type { MetaResponse, MetaField } from '../../shared/types.js'\nimport type { FieldType } from '../AnalysisBuilder/types.js'\nimport { FILTER_OPERATORS, DATE_RANGE_OPTIONS } from '../../shared/types.js'\nimport { getAvailableOperators } from '../../shared/utils.js'\nimport { findFieldInSchema, getFieldTitle } from '../AnalysisBuilder/utils/index.js'\nimport { computeDirectInputValues } from './dashboardFilterConfigModalUtils.js'\nimport { useFilterDropdowns } from './useFilterDropdowns.js'\nimport { useFilterValueFetch } from './useFilterValueFetch.js'\nimport { useDateRangeState } from './useDateRangeState.js'\n\ninterface UseDashboardFilterConfigModalParams {\n initialFilter: DashboardFilter\n fullSchema: MetaResponse | null\n filteredSchema: MetaResponse | null\n isOpen: boolean\n onSave: (filter: DashboardFilter) => void\n}\n\nexport function useDashboardFilterConfigModal({\n initialFilter,\n fullSchema,\n filteredSchema,\n isOpen,\n onSave\n}: UseDashboardFilterConfigModalParams) {\n const { t } = useTranslation()\n\n // Core editing state\n const [localLabel, setLocalLabel] = useState(initialFilter.label)\n const [localFilter, setLocalFilter] = useState<SimpleFilter>(initialFilter.filter as SimpleFilter)\n const [showAllFields, setShowAllFields] = useState(false)\n const [showFieldSearch, setShowFieldSearch] = useState(false)\n\n // Which popover dropdown is open + the click-outside container.\n const dropdowns = useFilterDropdowns()\n const { setIsOperatorDropdownOpen, setIsValueDropdownOpen, setIsDateRangeDropdownOpen } = dropdowns\n\n // Schema to use based on toggle\n const activeSchema = showAllFields ? fullSchema : filteredSchema\n\n // Sync state when filter changes or modal opens\n useEffect(() => {\n if (isOpen) {\n setLocalLabel(initialFilter.label)\n setLocalFilter(initialFilter.filter as SimpleFilter)\n }\n }, [initialFilter, isOpen])\n\n // Get field info\n const fieldInfo = findFieldInSchema(localFilter.member, activeSchema)\n const fieldType = fieldInfo?.field.type || 'string'\n const isTimeField = fieldType === 'time'\n const isMeasureField = fieldInfo?.fieldType === 'measure'\n const isDimensionField = fieldInfo?.fieldType === 'dimension'\n\n // Get display title for field\n const fieldTitle = getFieldTitle(localFilter.member, activeSchema)\n\n // Get operator metadata\n const operatorMeta = FILTER_OPERATORS[localFilter.operator as FilterOperator]\n\n // Get available operators for this field type\n const availableOperators = getAvailableOperators(fieldType)\n\n // Should show date range selector\n const shouldShowDateRange = isTimeField && localFilter.operator === 'inDateRange'\n\n // Should use combo box for value selection\n const shouldShowComboBox = (() => {\n const comboOperators = ['equals', 'notEquals', 'in', 'notIn']\n return comboOperators.includes(localFilter.operator) && isDimensionField && !isTimeField\n })()\n\n // Combo-box value search + distinct-value fetching.\n const values = useFilterValueFetch({\n localFilter,\n setLocalFilter,\n operatorMeta,\n shouldShowComboBox,\n isValueDropdownOpen: dropdowns.isValueDropdownOpen,\n setIsValueDropdownOpen\n })\n\n // Date-range preset / \"last N\" state + sync effect.\n const dateRange = useDateRangeState({\n localFilter,\n setLocalFilter,\n shouldShowDateRange,\n setIsDateRangeDropdownOpen\n })\n\n // Handle field selection from FieldSearchModal\n const handleFieldSelected = useCallback((field: MetaField, _fieldType: FieldType) => {\n // Reset operator and values when changing field\n const newFieldType = field.type\n const newOperators = getAvailableOperators(newFieldType)\n const defaultOperator = newOperators[0]?.operator || 'equals'\n\n setLocalFilter({\n member: field.name,\n operator: defaultOperator as FilterOperator,\n values: []\n })\n setShowFieldSearch(false)\n }, [])\n\n // Handle operator change\n const handleOperatorChange = useCallback((operator: FilterOperator) => {\n setLocalFilter({\n member: localFilter.member,\n operator,\n values: []\n })\n setIsOperatorDropdownOpen(false)\n }, [localFilter.member, setIsOperatorDropdownOpen])\n\n // Handle direct text/number input\n const handleDirectInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const nextValues = computeDirectInputValues(e.target.value, operatorMeta?.valueType)\n if (nextValues === null) return\n setLocalFilter({ ...localFilter, values: nextValues })\n }, [localFilter, operatorMeta?.valueType])\n\n // Handle between range inputs\n const handleBetweenStartInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = parseFloat(e.target.value)\n const currentValues = localFilter.values?.length >= 2 ? localFilter.values : ['', '']\n const newValues = [!isNaN(value) ? value : '', currentValues[1]].filter(v => v !== '')\n setLocalFilter({ ...localFilter, values: newValues })\n }, [localFilter])\n\n const handleBetweenEndInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = parseFloat(e.target.value)\n const currentValues = localFilter.values?.length >= 2 ? localFilter.values : ['', '']\n const newValues = [currentValues[0], !isNaN(value) ? value : ''].filter(v => v !== '')\n setLocalFilter({ ...localFilter, values: newValues })\n }, [localFilter])\n\n // Handle date input\n const handleDateInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n setLocalFilter({ ...localFilter, values: value ? [value] : [] })\n }, [localFilter])\n\n // Handle save\n const handleSave = useCallback(() => {\n if (!localLabel.trim()) {\n alert(t('dashboardFilter.filterLabelRequired'))\n return\n }\n\n // Don't require field selection for universal time filters\n if (!initialFilter.isUniversalTime && !localFilter.member) {\n alert(t('dashboardFilter.selectFieldRequired'))\n return\n }\n\n const updatedFilter: DashboardFilter = {\n id: initialFilter.id,\n label: localLabel,\n filter: localFilter,\n ...(initialFilter.isUniversalTime && { isUniversalTime: true })\n }\n\n onSave(updatedFilter)\n }, [initialFilter.id, initialFilter.isUniversalTime, localLabel, localFilter, onSave, t])\n\n // Get current operator label\n const operatorLabel = t(availableOperators.find(op => op.operator === localFilter.operator)?.label || localFilter.operator)\n\n // Get current date range label\n const dateRangeLabel = t(DATE_RANGE_OPTIONS.find(opt => opt.value === dateRange.rangeType)?.label || 'filter.modal.selectRange')\n\n return {\n // Core editing state\n localLabel,\n setLocalLabel,\n localFilter,\n showAllFields,\n setShowAllFields,\n showFieldSearch,\n setShowFieldSearch,\n // Derived field/operator metadata\n field: {\n activeSchema,\n isTimeField,\n isMeasureField,\n fieldTitle,\n operatorMeta,\n availableOperators,\n operatorLabel,\n shouldShowDateRange,\n shouldShowComboBox\n },\n // Sub-hook concerns\n dropdowns,\n values,\n dateRange: { ...dateRange, dateRangeLabel },\n // Field/operator/value/save handlers owned here\n handleFieldSelected,\n handleOperatorChange,\n handleDirectInput,\n handleBetweenStartInput,\n handleBetweenEndInput,\n handleDateInput,\n handleSave\n }\n}\n","/**\n * DashboardFilterValueInput\n *\n * Presentational value-input renderer extracted from DashboardFilterConfigModal.\n * Picks the right input control for the current operator/field type (no-value,\n * date range, between, date, number, dimension combo-box, or plain text). The\n * date-range and combo-box variants are split into their own sub-components to\n * keep each piece flat. Behaviour and markup are identical to the previous\n * inline `renderValueInput`.\n */\n\nimport type { ChangeEvent } from 'react'\nimport { getIcon } from '../../icons/index.js'\nimport type { SimpleFilter, FilterOperator } from '../../types.js'\nimport type { DateRangeType } from '../../shared/types.js'\nimport { DATE_RANGE_OPTIONS } from '../../shared/types.js'\nimport { requiresNumberInput } from '../../shared/utils.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\nconst CloseIcon = getIcon('close')\nconst ChevronDownIcon = getIcon('chevronDown')\n\ntype OperatorMeta = {\n requiresValues?: boolean\n valueType?: string\n supportsMultipleValues?: boolean\n} | undefined\n\nexport interface DashboardFilterValueInputProps {\n filter: SimpleFilter\n operatorMeta: OperatorMeta\n shouldShowDateRange: boolean\n shouldShowComboBox: boolean\n // Date range\n rangeType: DateRangeType\n numberValue: number\n dateRangeLabel: string\n isDateRangeDropdownOpen: boolean\n setIsOperatorDropdownOpen: (open: boolean) => void\n setIsValueDropdownOpen: (open: boolean) => void\n setIsDateRangeDropdownOpen: (open: boolean) => void\n handleRangeTypeChange: (rangeType: DateRangeType) => void\n handleNumberValueChange: (value: number) => void\n handleCustomStartDate: (e: ChangeEvent<HTMLInputElement>) => void\n handleCustomEndDate: (e: ChangeEvent<HTMLInputElement>) => void\n // Between/number/date/text\n handleBetweenStartInput: (e: ChangeEvent<HTMLInputElement>) => void\n handleBetweenEndInput: (e: ChangeEvent<HTMLInputElement>) => void\n handleDateInput: (e: ChangeEvent<HTMLInputElement>) => void\n handleDirectInput: (e: ChangeEvent<HTMLInputElement>) => void\n // Combo box\n isValueDropdownOpen: boolean\n distinctValues: unknown[]\n valuesLoading: boolean\n valuesError: unknown\n searchText: string\n setSearchText: (text: string) => void\n handleValueSelect: (value: unknown) => void\n handleValueRemove: (value: unknown) => void\n}\n\nfunction DateRangeInput(props: DashboardFilterValueInputProps) {\n const { t } = useTranslation()\n const {\n filter,\n rangeType,\n numberValue,\n dateRangeLabel,\n isDateRangeDropdownOpen,\n setIsOperatorDropdownOpen,\n setIsValueDropdownOpen,\n setIsDateRangeDropdownOpen,\n handleRangeTypeChange,\n handleNumberValueChange,\n handleCustomStartDate,\n handleCustomEndDate\n } = props\n return (\n <div className=\"dc:space-y-2\">\n {/* Range type dropdown */}\n <div className=\"dc:relative\">\n <button\n onClick={() => {\n setIsOperatorDropdownOpen(false)\n setIsValueDropdownOpen(false)\n setIsDateRangeDropdownOpen(!isDateRangeDropdownOpen)\n }}\n className=\"dc:w-full dc:flex dc:items-center dc:justify-between dc:text-left dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text hover:bg-dc-surface-hover\"\n >\n <span className=\"dc:truncate\">{dateRangeLabel}</span>\n <ChevronDownIcon className={`dc:w-4 dc:h-4 text-dc-text-muted dc:shrink-0 dc:ml-2 dc:transition-transform ${\n isDateRangeDropdownOpen ? 'dc:rotate-180' : ''\n }`} />\n </button>\n\n {isDateRangeDropdownOpen && (\n <div className=\"dc:absolute dc:z-[60] dc:left-0 dc:right-0 dc:mt-1 bg-dc-surface dc:border border-dc-border dc:rounded dc:shadow-lg dc:max-h-48 dc:overflow-y-auto\">\n {DATE_RANGE_OPTIONS.map((option) => (\n <button\n key={option.value}\n onClick={() => handleRangeTypeChange(option.value)}\n className={`dc:w-full dc:text-left dc:px-3 dc:py-2 dc:text-sm hover:bg-dc-surface-hover ${\n option.value === rangeType ? 'bg-dc-primary/10 text-dc-primary' : 'text-dc-text'\n }`}\n >\n {t(option.label)}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Number input for \"last N\" ranges */}\n {requiresNumberInput(rangeType) && (\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <input\n type=\"number\"\n min=\"1\"\n max=\"1000\"\n value={numberValue}\n onChange={(e) => handleNumberValueChange(Math.max(1, parseInt(e.target.value) || 1))}\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text dc:w-20\"\n />\n <span className=\"dc:text-sm text-dc-text-muted\">\n {rangeType.replace('last_n_', '')}\n </span>\n </div>\n )}\n\n {/* Custom date inputs */}\n {rangeType === 'custom' && (\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <input\n type=\"date\"\n value={Array.isArray(filter.dateRange) ? filter.dateRange[0] : ''}\n onChange={handleCustomStartDate}\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-2 dc:py-2 bg-dc-surface text-dc-text\"\n />\n <span className=\"dc:text-sm text-dc-text-muted\">to</span>\n <input\n type=\"date\"\n value={Array.isArray(filter.dateRange) ? filter.dateRange[1] : ''}\n onChange={handleCustomEndDate}\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-2 dc:py-2 bg-dc-surface text-dc-text\"\n />\n </div>\n )}\n </div>\n )\n}\n\nfunction ComboBoxValuesList(props: DashboardFilterValueInputProps) {\n const { t } = useTranslation()\n const {\n filter,\n distinctValues,\n valuesLoading,\n valuesError,\n handleValueSelect\n } = props\n\n if (valuesLoading) {\n return <div className=\"dc:px-3 dc:py-2 dc:text-sm text-dc-text-muted\">{t('common.loading')}</div>\n }\n if (valuesError) {\n return <div className=\"dc:px-3 dc:py-2 dc:text-sm text-dc-error\">{t('dashboardFilter.errorPrefix')}{String(valuesError)}</div>\n }\n if (distinctValues.length === 0) {\n return <div className=\"dc:px-3 dc:py-2 dc:text-sm text-dc-text-muted\">{t('dashboardFilter.noValuesFound')}</div>\n }\n\n return (\n <div className=\"dc:max-h-40 dc:overflow-y-auto\">\n {distinctValues.map((value, index) => {\n const isSelected = filter.values?.includes(value)\n return (\n <button\n key={`${value}-${index}`}\n onClick={() => handleValueSelect(value)}\n className={`dc:w-full dc:text-left dc:px-3 dc:py-2 dc:text-sm hover:bg-dc-surface-hover ${\n isSelected ? 'bg-dc-primary/10 text-dc-primary' : 'text-dc-text'\n }`}\n >\n {String(value)}\n {isSelected && <span className=\"dc:float-right\">✓</span>}\n </button>\n )\n })}\n </div>\n )\n}\n\nfunction ComboBoxInput(props: DashboardFilterValueInputProps) {\n const { t } = useTranslation()\n const {\n filter,\n isValueDropdownOpen,\n valuesLoading,\n searchText,\n setSearchText,\n setIsOperatorDropdownOpen,\n setIsValueDropdownOpen,\n setIsDateRangeDropdownOpen,\n handleValueRemove\n } = props\n\n return (\n <div className=\"dc:space-y-2\">\n {/* Selected values as tags */}\n {filter.values && filter.values.length > 0 && (\n <div className=\"dc:flex dc:flex-wrap dc:gap-1.5\">\n {filter.values.map((value: unknown, index: number) => (\n <span\n key={index}\n className=\"dc:inline-flex dc:items-center dc:gap-1 bg-dc-primary/10 text-dc-primary dc:text-sm dc:px-2 dc:py-1 dc:rounded\"\n >\n <span className=\"dc:max-w-[150px] dc:truncate\">{String(value)}</span>\n <button\n onClick={() => handleValueRemove(value)}\n className=\"hover:text-dc-danger\"\n >\n <CloseIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n </span>\n ))}\n </div>\n )}\n\n {/* Dropdown trigger */}\n <div className=\"dc:relative\">\n <button\n onClick={() => {\n setIsOperatorDropdownOpen(false)\n setIsDateRangeDropdownOpen(false)\n setIsValueDropdownOpen(!isValueDropdownOpen)\n }}\n className=\"dc:w-full dc:flex dc:items-center dc:justify-between dc:text-left dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text hover:bg-dc-surface-hover\"\n >\n <span className=\"text-dc-text-muted dc:truncate\">\n {valuesLoading ? 'Loading...' : 'Select value...'}\n </span>\n <ChevronDownIcon className={`dc:w-4 dc:h-4 text-dc-text-muted dc:shrink-0 dc:ml-2 dc:transition-transform ${\n isValueDropdownOpen ? 'dc:rotate-180' : ''\n }`} />\n </button>\n\n {isValueDropdownOpen && (\n <div className=\"dc:absolute dc:z-[60] dc:left-0 dc:right-0 dc:mt-1 bg-dc-surface dc:border border-dc-border dc:rounded dc:shadow-lg dc:max-h-56 dc:overflow-hidden\">\n {/* Search input */}\n <div className=\"dc:p-2 dc:border-b border-dc-border\">\n <input\n type=\"text\"\n value={searchText}\n onChange={(e) => setSearchText(e.target.value)}\n placeholder={t('dashboardFilter.search')}\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n autoFocus\n />\n </div>\n\n {/* Values list */}\n <ComboBoxValuesList {...props} />\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default function DashboardFilterValueInput(props: DashboardFilterValueInputProps) {\n const { t } = useTranslation()\n const {\n filter,\n operatorMeta,\n shouldShowDateRange,\n shouldShowComboBox,\n handleBetweenStartInput,\n handleBetweenEndInput,\n handleDateInput,\n handleDirectInput\n } = props\n\n // No value required for set/notSet\n if (!operatorMeta?.requiresValues) {\n return (\n <div className=\"dc:text-sm text-dc-text-muted dc:italic dc:py-2\">\n {t('dashboardFilter.noValueRequired')}\n </div>\n )\n }\n\n // Date range selector for inDateRange on time fields\n if (shouldShowDateRange) {\n return <DateRangeInput {...props} />\n }\n\n // Between/notBetween range inputs\n if (filter.operator === ('between' as FilterOperator) || filter.operator === ('notBetween' as FilterOperator)) {\n return (\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <input\n type=\"number\"\n value={filter.values?.[0] ?? ''}\n onChange={handleBetweenStartInput}\n placeholder=\"Min\"\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n <span className=\"dc:text-sm text-dc-text-muted\">to</span>\n <input\n type=\"number\"\n value={filter.values?.[1] ?? ''}\n onChange={handleBetweenEndInput}\n placeholder=\"Max\"\n className=\"dc:flex-1 dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n </div>\n )\n }\n\n // Date picker for date operators\n if (operatorMeta?.valueType === 'date') {\n return (\n <input\n type=\"date\"\n value={filter.values?.[0] || ''}\n onChange={handleDateInput}\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n )\n }\n\n // Number input\n if (operatorMeta?.valueType === 'number') {\n return (\n <input\n type=\"number\"\n value={filter.values?.[0] ?? ''}\n onChange={handleDirectInput}\n placeholder=\"Enter number\"\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n )\n }\n\n // Combo box for equals/notEquals/in/notIn on dimensions\n if (shouldShowComboBox) {\n return <ComboBoxInput {...props} />\n }\n\n // Default: text input\n return (\n <input\n type=\"text\"\n value={filter.values?.[0] ?? ''}\n onChange={handleDirectInput}\n placeholder=\"Enter value...\"\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text placeholder-dc-text-muted\"\n />\n )\n}\n","/**\n * DashboardFilterConfigModalParts\n *\n * Presentational sections extracted from DashboardFilterConfigModal (field\n * selector, operator dropdown). Markup, classes, and i18n keys are identical to\n * the previous inline JSX — pure extraction to keep the main render flat.\n */\n\nimport { getIcon } from '../../icons/index.js'\nimport type { MetaResponse } from '../../shared/types.js'\nimport type { SimpleFilter, FilterOperator } from '../../types.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport { getFieldTitle } from '../AnalysisBuilder/utils/index.js'\n\nconst ChevronDownIcon = getIcon('chevronDown')\nconst DimensionIcon = getIcon('dimension')\nconst TimeDimensionIcon = getIcon('timeDimension')\nconst MeasureIcon = getIcon('measure')\nconst EditIcon = getIcon('edit')\nconst EyeIcon = getIcon('eye')\nconst EyeOffIcon = getIcon('eyeOff')\n\ninterface FieldSelectionSectionProps {\n localFilter: SimpleFilter\n activeSchema: MetaResponse | null\n isTimeField: boolean\n isMeasureField: boolean\n showAllFields: boolean\n setShowAllFields: (next: boolean) => void\n setShowFieldSearch: (next: boolean) => void\n}\n\nexport function FieldSelectionSection({\n localFilter,\n activeSchema,\n isTimeField,\n isMeasureField,\n showAllFields,\n setShowAllFields,\n setShowFieldSearch\n}: FieldSelectionSectionProps) {\n const { t } = useTranslation()\n const fieldTitle = getFieldTitle(localFilter.member, activeSchema)\n const FieldIcon = isTimeField ? TimeDimensionIcon : isMeasureField ? MeasureIcon : DimensionIcon\n const iconBgClass = isTimeField ? 'bg-dc-time-dimension' : isMeasureField ? 'bg-dc-measure' : 'bg-dc-dimension'\n const iconTextClass = isTimeField ? 'text-dc-time-dimension-text' : isMeasureField ? 'text-dc-measure-text' : 'text-dc-dimension-text'\n\n return (\n <div>\n <div className=\"dc:flex dc:items-center dc:justify-between dc:mb-2\">\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary\">\n {t('dashboardFilter.field')}\n </label>\n <button\n onClick={() => setShowAllFields(!showAllFields)}\n className=\"dc:flex dc:items-center dc:gap-1 dc:text-xs dc:px-2 dc:py-1 dc:rounded hover:bg-dc-surface-hover text-dc-text-muted\"\n title={showAllFields ? 'Show dashboard fields only' : 'Show all fields'}\n >\n {showAllFields ? (\n <>\n <EyeOffIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span>{t('dashboardFilter.dashboard')}</span>\n </>\n ) : (\n <>\n <EyeIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span>{t('dashboardFilter.all')}</span>\n </>\n )}\n </button>\n </div>\n <button\n onClick={() => setShowFieldSearch(true)}\n className=\"dc:w-full dc:flex dc:items-center dc:gap-2 dc:p-3 bg-dc-surface-secondary dc:rounded hover:bg-dc-surface-tertiary dc:transition-colors\"\n >\n {localFilter.member ? (\n <>\n <span className={`dc:w-6 dc:h-6 dc:flex dc:items-center dc:justify-center dc:rounded ${iconBgClass} ${iconTextClass}`}>\n {FieldIcon && <FieldIcon className=\"dc:w-4 dc:h-4\" />}\n </span>\n <span className=\"dc:flex-1 dc:text-sm dc:font-medium text-dc-text dc:text-left\">{fieldTitle}</span>\n </>\n ) : (\n <>\n <span className=\"dc:w-6 dc:h-6 dc:flex dc:items-center dc:justify-center dc:rounded bg-dc-surface-tertiary text-dc-text-muted\">\n <DimensionIcon className=\"dc:w-4 dc:h-4\" />\n </span>\n <span className=\"dc:flex-1 dc:text-sm text-dc-text-muted dc:text-left\">{t('dashboardFilter.clickToSelectField')}</span>\n </>\n )}\n <EditIcon className=\"dc:w-4 dc:h-4 text-dc-text-muted\" />\n </button>\n </div>\n )\n}\n\ninterface OperatorSectionProps {\n localFilter: SimpleFilter\n operatorLabel: string\n availableOperators: { operator: string; label: string }[]\n isOperatorDropdownOpen: boolean\n setIsOperatorDropdownOpen: (next: boolean) => void\n setIsValueDropdownOpen: (next: boolean) => void\n setIsDateRangeDropdownOpen: (next: boolean) => void\n handleOperatorChange: (operator: FilterOperator) => void\n}\n\nexport function OperatorSection({\n localFilter,\n operatorLabel,\n availableOperators,\n isOperatorDropdownOpen,\n setIsOperatorDropdownOpen,\n setIsValueDropdownOpen,\n setIsDateRangeDropdownOpen,\n handleOperatorChange\n}: OperatorSectionProps) {\n const { t } = useTranslation()\n return (\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">\n {t('dashboardFilter.operator')}\n </label>\n <div className=\"dc:relative\">\n <button\n onClick={() => {\n setIsValueDropdownOpen(false)\n setIsDateRangeDropdownOpen(false)\n setIsOperatorDropdownOpen(!isOperatorDropdownOpen)\n }}\n className=\"dc:w-full dc:flex dc:items-center dc:justify-between dc:text-left dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text hover:bg-dc-surface-hover\"\n >\n <span className=\"dc:truncate\">{operatorLabel}</span>\n <ChevronDownIcon className={`dc:w-4 dc:h-4 text-dc-text-muted dc:shrink-0 dc:ml-2 dc:transition-transform ${\n isOperatorDropdownOpen ? 'dc:rotate-180' : ''\n }`} />\n </button>\n\n {isOperatorDropdownOpen && (\n <div className=\"dc:absolute dc:z-[60] dc:left-0 dc:right-0 dc:mt-1 bg-dc-surface dc:border border-dc-border dc:rounded dc:shadow-lg dc:max-h-48 dc:overflow-y-auto\">\n {availableOperators.map((op) => (\n <button\n key={op.operator}\n onClick={() => handleOperatorChange(op.operator as FilterOperator)}\n className={`dc:w-full dc:text-left dc:px-3 dc:py-2 dc:text-sm hover:bg-dc-surface-hover ${\n op.operator === localFilter.operator ? 'bg-dc-primary/10 text-dc-primary' : 'text-dc-text'\n }`}\n >\n {t(op.label)}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n )\n}\n","/**\n * DashboardFilterConfigModal Component\n *\n * Modal for editing dashboard filter configuration including:\n * - Filter label\n * - Field selection (via FieldSearchModal)\n * - Operator selection\n * - Value input (adapts to field/operator type)\n * - Date range selection for time dimensions\n *\n * Based on FilterConfigModal but adapted for DashboardFilter with:\n * - Label editing\n * - Clickable field section to change field\n * - \"Dashboard fields only\" toggle\n * - Delete action\n *\n * State, effects, and handlers live in `useDashboardFilterConfigModal`; the\n * value-input control and field/operator sections are extracted into sibling\n * components. This file is the layout shell.\n */\n\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport { getIcon } from '../../icons/index.js'\nimport type { DashboardFilter } from '../../types.js'\nimport type { MetaResponse } from '../../shared/types.js'\nimport FieldSearchModal from '../AnalysisBuilder/FieldSearchModal.js'\nimport { useDashboardFilterConfigModal } from './useDashboardFilterConfigModal.js'\nimport DashboardFilterValueInput from './DashboardFilterValueInput.js'\nimport { FieldSelectionSection, OperatorSection } from './DashboardFilterConfigModalParts.js'\n\nconst CloseIcon = getIcon('close')\n\ninterface DashboardFilterConfigModalProps {\n /** The dashboard filter being edited */\n filter: DashboardFilter\n /** Full schema (unfiltered) */\n fullSchema: MetaResponse | null\n /** Filtered schema (dashboard fields only) */\n filteredSchema: MetaResponse | null\n /** Whether the modal is open */\n isOpen: boolean\n /** Callback when user saves changes */\n onSave: (filter: DashboardFilter) => void\n /** Callback when user deletes the filter */\n onDelete: () => void\n /** Callback when user closes/cancels */\n onClose: () => void\n}\n\nexport default function DashboardFilterConfigModal({\n filter: initialFilter,\n fullSchema,\n filteredSchema,\n isOpen,\n onSave,\n onDelete,\n onClose\n}: DashboardFilterConfigModalProps) {\n const { t } = useTranslation()\n const modal = useDashboardFilterConfigModal({\n initialFilter,\n fullSchema,\n filteredSchema,\n isOpen,\n onSave\n })\n\n if (!isOpen) return null\n\n const {\n localLabel,\n setLocalLabel,\n localFilter,\n showAllFields,\n setShowAllFields,\n showFieldSearch,\n setShowFieldSearch,\n field,\n dropdowns,\n values,\n dateRange,\n handleFieldSelected,\n handleOperatorChange,\n handleDirectInput,\n handleBetweenStartInput,\n handleBetweenEndInput,\n handleDateInput,\n handleSave\n } = modal\n\n const { containerRef, isOperatorDropdownOpen, setIsOperatorDropdownOpen, isValueDropdownOpen, setIsValueDropdownOpen, isDateRangeDropdownOpen, setIsDateRangeDropdownOpen } = dropdowns\n const { activeSchema, isTimeField, isMeasureField, operatorMeta, availableOperators, operatorLabel, shouldShowDateRange, shouldShowComboBox } = field\n const { distinctValues, valuesLoading, valuesError, searchText, setSearchText, handleValueSelect, handleValueRemove } = values\n const { rangeType, numberValue, dateRangeLabel, handleRangeTypeChange, handleNumberValueChange, handleCustomStartDate, handleCustomEndDate } = dateRange\n\n const showFieldSection = !initialFilter.isUniversalTime\n const showOperatorSection = localFilter.member && !initialFilter.isUniversalTime\n const showValueSection = localFilter.member && !initialFilter.isUniversalTime\n\n return (\n <>\n {/* Modal overlay */}\n <div\n className=\"dc:fixed dc:inset-0 dc:z-50 dc:flex dc:items-center dc:justify-center dc:p-4\"\n style={{ backgroundColor: 'var(--dc-overlay)' }}\n onClick={onClose}\n >\n <div\n ref={containerRef}\n className=\"bg-dc-surface dc:rounded-lg dc:border border-dc-border dc:max-w-md dc:w-full dc:max-h-[90vh] dc:overflow-auto\"\n style={{ boxShadow: 'var(--dc-shadow-xl)' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div className=\"dc:flex dc:items-center dc:justify-between dc:p-4 dc:border-b border-dc-border\">\n <h2 className=\"dc:text-lg dc:font-semibold text-dc-text\">{t('dashboardFilter.editFilter')}</h2>\n <button\n onClick={onClose}\n className=\"dc:p-1 text-dc-text-muted hover:text-dc-text dc:transition-colors\"\n >\n <CloseIcon className=\"dc:w-5 dc:h-5\" />\n </button>\n </div>\n\n {/* Body */}\n <div className=\"dc:p-4 dc:space-y-4\">\n {/* Filter Label */}\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">\n {t('dashboardFilter.filterLabel')}\n </label>\n <input\n type=\"text\"\n value={localLabel}\n onChange={(e) => setLocalLabel(e.target.value)}\n placeholder=\"Enter filter label\"\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded dc:px-3 dc:py-2 bg-dc-surface text-dc-text\"\n />\n </div>\n\n {/* Info box for universal time filters */}\n {initialFilter.isUniversalTime && (\n <div className=\"dc:p-3 dc:rounded-md bg-dc-info-bg dc:border border-dc-info-border\">\n <div className=\"dc:text-sm dc:font-medium text-dc-info dc:mb-1\">\n {t('dashboardFilter.universalTimeFilter')}\n </div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {t('dashboardFilter.universalTimeDescription')}\n </div>\n </div>\n )}\n\n {/* Field selection (not for universal time filters) */}\n {showFieldSection && (\n <FieldSelectionSection\n localFilter={localFilter}\n activeSchema={activeSchema}\n isTimeField={isTimeField}\n isMeasureField={isMeasureField}\n showAllFields={showAllFields}\n setShowAllFields={setShowAllFields}\n setShowFieldSearch={setShowFieldSearch}\n />\n )}\n\n {/* Operator selector (only if field is selected) */}\n {showOperatorSection && (\n <OperatorSection\n localFilter={localFilter}\n operatorLabel={operatorLabel}\n availableOperators={availableOperators}\n isOperatorDropdownOpen={isOperatorDropdownOpen}\n setIsOperatorDropdownOpen={setIsOperatorDropdownOpen}\n setIsValueDropdownOpen={setIsValueDropdownOpen}\n setIsDateRangeDropdownOpen={setIsDateRangeDropdownOpen}\n handleOperatorChange={handleOperatorChange}\n />\n )}\n\n {/* Value input (only if field is selected, not for universal time filters) */}\n {showValueSection && (\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-2\">\n {t('dashboardFilter.defaultValue')}\n </label>\n <DashboardFilterValueInput\n filter={localFilter}\n operatorMeta={operatorMeta}\n shouldShowDateRange={shouldShowDateRange}\n shouldShowComboBox={shouldShowComboBox}\n rangeType={rangeType}\n numberValue={numberValue}\n dateRangeLabel={dateRangeLabel}\n isDateRangeDropdownOpen={isDateRangeDropdownOpen}\n setIsOperatorDropdownOpen={setIsOperatorDropdownOpen}\n setIsValueDropdownOpen={setIsValueDropdownOpen}\n setIsDateRangeDropdownOpen={setIsDateRangeDropdownOpen}\n handleRangeTypeChange={handleRangeTypeChange}\n handleNumberValueChange={handleNumberValueChange}\n handleCustomStartDate={handleCustomStartDate}\n handleCustomEndDate={handleCustomEndDate}\n handleBetweenStartInput={handleBetweenStartInput}\n handleBetweenEndInput={handleBetweenEndInput}\n handleDateInput={handleDateInput}\n handleDirectInput={handleDirectInput}\n isValueDropdownOpen={isValueDropdownOpen}\n distinctValues={distinctValues}\n valuesLoading={valuesLoading}\n valuesError={valuesError}\n searchText={searchText}\n setSearchText={setSearchText}\n handleValueSelect={handleValueSelect}\n handleValueRemove={handleValueRemove}\n />\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"dc:flex dc:items-center dc:justify-between dc:p-4 dc:border-t border-dc-border\">\n <button\n onClick={onDelete}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-danger hover:bg-dc-danger-bg dc:rounded dc:transition-colors\"\n >\n {t('dashboardFilter.deleteFilter')}\n </button>\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <button\n onClick={onClose}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-text-secondary hover:text-dc-text dc:transition-colors\"\n >\n {t('common.actions.cancel')}\n </button>\n <button\n onClick={handleSave}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-primary-content bg-dc-primary hover:bg-dc-primary-hover dc:rounded dc:transition-colors\"\n >\n {t('common.actions.done')}\n </button>\n </div>\n </div>\n </div>\n </div>\n\n {/* Field Search Modal */}\n {showFieldSearch && (\n <FieldSearchModal\n isOpen={showFieldSearch}\n onClose={() => setShowFieldSearch(false)}\n onSelect={handleFieldSelected}\n mode=\"filter\"\n schema={activeSchema}\n selectedFields={localFilter.member ? [localFilter.member] : []}\n />\n )}\n </>\n )\n}\n","/**\n * FilterEditModal Component\n *\n * Modal for editing dashboard filter details including label, field, operator, and values.\n * Now delegates to DashboardFilterConfigModal for the modern search-based UX.\n *\n * Pattern: Self-contained modal with local state (matches PortletEditModal pattern)\n * - All editing state is local to the modal\n * - Changes only propagate on \"Done\" button click via onSave callback\n * - Cancel/close resets local state without saving\n */\n\nimport React, { useMemo, useCallback } from 'react'\nimport { extractDashboardFields } from '../../utils/filterUtils.js'\nimport DashboardFilterConfigModal from './DashboardFilterConfigModal.js'\nimport type { DashboardFilter, CubeMeta, DashboardConfig } from '../../types.js'\nimport type { MetaResponse } from '../../shared/types.js'\n\ninterface FilterEditModalProps {\n filter: DashboardFilter\n schema: CubeMeta | null\n dashboardConfig: DashboardConfig\n isOpen: boolean\n onSave: (filter: DashboardFilter) => void | Promise<void>\n onClose: () => void\n onDelete: () => void\n convertToMetaResponse: (cubeMeta: CubeMeta | null) => MetaResponse | null\n}\n\nconst FilterEditModal: React.FC<FilterEditModalProps> = ({\n filter,\n schema,\n dashboardConfig,\n isOpen,\n onSave,\n onClose,\n onDelete,\n convertToMetaResponse\n}) => {\n // Convert full schema to MetaResponse format\n const fullSchema = useMemo(() => {\n return convertToMetaResponse(schema)\n }, [schema, convertToMetaResponse])\n\n // Extract fields used in dashboard\n const dashboardFields = useMemo(() => {\n return extractDashboardFields(dashboardConfig)\n }, [dashboardConfig])\n\n // Create filtered schema showing only dashboard fields\n const filteredSchema = useMemo<MetaResponse | null>(() => {\n if (!schema) return null\n\n const filteredCubes = schema.cubes\n .map(cube => {\n const cubeName = cube.name\n\n const filteredMeasures = cube.measures.filter(measure => {\n const fullName = measure.name.includes('.')\n ? measure.name\n : `${cubeName}.${measure.name}`\n return dashboardFields.measures.has(fullName)\n })\n\n const filteredDimensions = cube.dimensions.filter(dimension => {\n const fullName = dimension.name.includes('.')\n ? dimension.name\n : `${cubeName}.${dimension.name}`\n return dashboardFields.dimensions.has(fullName) ||\n dashboardFields.timeDimensions.has(fullName)\n })\n\n if (filteredMeasures.length > 0 || filteredDimensions.length > 0) {\n return {\n ...cube,\n measures: filteredMeasures,\n dimensions: filteredDimensions\n }\n }\n\n return null\n })\n .filter((cube): cube is NonNullable<typeof cube> => cube !== null)\n\n const filteredCubeMeta: CubeMeta = {\n ...schema,\n cubes: filteredCubes\n }\n\n return convertToMetaResponse(filteredCubeMeta)\n }, [schema, dashboardFields, convertToMetaResponse])\n\n // Handle save with async support\n const handleSave = useCallback(async (updatedFilter: DashboardFilter) => {\n try {\n await onSave(updatedFilter)\n onClose()\n } catch (error) {\n console.error('Failed to save filter:', error)\n alert('Failed to save filter. Please try again.')\n }\n }, [onSave, onClose])\n\n if (!isOpen) return null\n\n return (\n <DashboardFilterConfigModal\n filter={filter}\n fullSchema={fullSchema}\n filteredSchema={filteredSchema}\n isOpen={isOpen}\n onSave={handleSave}\n onDelete={onDelete}\n onClose={onClose}\n />\n )\n}\n\nexport default FilterEditModal\n","/**\n * EditModeFilterList Component\n *\n * Displays filters in edit mode with chips showing filter labels and edit/delete actions\n */\n\nimport React from 'react'\nimport { getIcon } from '../../icons/index.js'\nimport type { DashboardFilter } from '../../types.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\nconst FilterIcon = getIcon('filter')\nconst AddIcon = getIcon('add')\nconst CloseIcon = getIcon('close')\nconst EditIcon = getIcon('edit')\nconst ChevronDownIcon = getIcon('chevronDown')\nconst ClockIcon = getIcon('timeDimension')\n\ninterface EditModeFilterListProps {\n dashboardFilters: DashboardFilter[]\n onAddFilter: () => void\n onAddTimeFilter: () => void\n onEditFilter: (filterId: string) => void\n onRemoveFilter: (filterId: string) => void\n selectedFilterId?: string | null\n onFilterSelect?: (filterId: string) => void\n}\n\nconst EditModeFilterList: React.FC<EditModeFilterListProps> = ({\n dashboardFilters,\n onAddFilter,\n onAddTimeFilter,\n onEditFilter,\n onRemoveFilter,\n selectedFilterId,\n onFilterSelect\n}) => {\n const { t } = useTranslation()\n const [isCollapsed, setIsCollapsed] = React.useState(false)\n\n // Render compact filter chip - just label + edit + delete\n const renderFilterChip = (dashboardFilter: DashboardFilter) => {\n const { id, label, isUniversalTime } = dashboardFilter\n const isSelected = selectedFilterId === id\n\n // Use calendar icon for universal time filters, funnel for regular filters\n const IconComponent = isUniversalTime ? ClockIcon : FilterIcon\n\n return (\n <div\n key={id}\n className={`dc:inline-flex dc:items-center dc:gap-1.5 dc:px-2.5 dc:py-1 dc:rounded-md dc:border dc:text-xs dc:transition-all ${\n 'dc:cursor-pointer dc:hover:shadow-md'\n }`}\n style={{\n backgroundColor: isSelected ? 'var(--dc-primary)' : 'var(--dc-surface)',\n borderColor: isSelected ? 'var(--dc-primary)' : 'var(--dc-border)',\n borderWidth: isSelected ? '2px' : '1px',\n color: isSelected ? 'white' : 'var(--dc-text)',\n boxShadow: isSelected ? '0 0 0 3px rgba(var(--dc-primary-rgb), 0.1)' : 'none'\n }}\n onClick={() => {\n if (onFilterSelect) {\n onFilterSelect(id)\n }\n }}\n >\n <IconComponent\n className=\"dc:w-3.5 dc:h-3.5 dc:shrink-0\"\n style={{ color: isSelected ? 'white' : 'var(--dc-primary)' }}\n />\n <span className=\"dc:font-medium dc:truncate\">{label}</span>\n\n {!isSelected && (\n <div className=\"dc:flex dc:items-center dc:gap-0.5 dc:ml-1\" onClick={(e) => e.stopPropagation()}>\n <button\n onClick={() => onEditFilter(id)}\n className=\"dc:p-0.5 hover:bg-dc-hover dc:rounded dc:transition-colors\"\n title=\"Edit filter\"\n >\n <EditIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n <button\n onClick={() => onRemoveFilter(id)}\n className=\"dc:p-0.5 hover:bg-dc-danger-bg hover:text-dc-danger dc:rounded dc:transition-colors\"\n title=\"Remove filter\"\n >\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n </div>\n )}\n </div>\n )\n }\n\n return (\n <>\n {/* Mobile: Header + collapsible content */}\n <div className=\"dc:md:hidden\">\n {/* Header - clickable to toggle */}\n <div\n className=\"dc:px-4 dc:py-2 dc:flex dc:items-center dc:justify-between dc:cursor-pointer\"\n onClick={() => setIsCollapsed(!isCollapsed)}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <FilterIcon className=\"dc:w-4 dc:h-4 dc:shrink-0\" style={{ color: 'var(--dc-primary)' }} />\n <h3 className=\"dc:text-sm dc:font-semibold\" style={{ color: 'var(--dc-text)' }}>\n {t('dashboardFilter.editMode.filters')}\n </h3>\n {dashboardFilters.length > 0 && (\n <span\n className=\"dc:px-1.5 dc:py-0.5 dc:rounded-full dc:text-xs dc:font-medium\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n {dashboardFilters.length}\n </span>\n )}\n <ChevronDownIcon\n className={`dc:w-4 dc:h-4 dc:transition-transform ${isCollapsed ? '' : 'dc:rotate-180'}`}\n style={{ color: 'var(--dc-text-secondary)' }}\n />\n </div>\n\n <div className=\"dc:flex dc:items-center dc:gap-1\">\n {/* Only show Date Range button if no universal time filter exists */}\n {!dashboardFilters.some(f => f.isUniversalTime) && (\n <button\n onClick={(e) => {\n e.stopPropagation()\n onAddTimeFilter()\n }}\n className=\"dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded-md dc:text-xs dc:font-medium dc:transition-colors dc:hover:opacity-80\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n color: 'var(--dc-primary)',\n border: '1px solid var(--dc-border)'\n }}\n title=\"Add date range filter (applies to all time dimensions)\"\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <ClockIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n )}\n <button\n onClick={(e) => {\n e.stopPropagation()\n onAddFilter()\n }}\n className=\"dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded-md dc:text-xs dc:font-medium dc:transition-colors dc:hover:opacity-80\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n </div>\n </div>\n\n {/* Mobile Filter Chips - Collapsible */}\n {dashboardFilters.length > 0 && !isCollapsed && (\n <div className=\"dc:px-4 dc:pb-2 dc:flex dc:flex-col dc:gap-2\">\n {dashboardFilters.map(renderFilterChip)}\n </div>\n )}\n\n {/* Mobile Empty State */}\n {dashboardFilters.length === 0 && !isCollapsed && (\n <div className=\"dc:px-4 dc:pb-2\">\n <div\n className=\"dc:text-xs dc:p-2 dc:rounded-md dc:text-center\"\n style={{\n backgroundColor: 'var(--dc-surface-secondary)',\n color: 'var(--dc-text-secondary)'\n }}\n >\n {t('dashboardFilter.editMode.noFilters')}\n </div>\n </div>\n )}\n </div>\n\n {/* Desktop: Single row layout */}\n <div className=\"dc:hidden dc:md:flex dc:md:items-center dc:md:gap-3 dc:px-4 dc:py-2\">\n {/* Header Section */}\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:shrink-0\">\n <FilterIcon className=\"dc:w-4 dc:h-4 dc:shrink-0\" style={{ color: 'var(--dc-primary)' }} />\n <h3 className=\"dc:text-sm dc:font-semibold dc:whitespace-nowrap\" style={{ color: 'var(--dc-text)' }}>\n {t('dashboardFilter.editMode.filters')}\n </h3>\n {dashboardFilters.length > 0 && (\n <span\n className=\"dc:px-1.5 dc:py-0.5 dc:rounded-full dc:text-xs dc:font-medium\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n {dashboardFilters.length}\n </span>\n )}\n </div>\n\n {/* Filter Chips Section - grows to fill space */}\n {dashboardFilters.length > 0 ? (\n <div className=\"dc:flex dc:flex-wrap dc:gap-2 dc:flex-1 dc:min-w-0\">\n {dashboardFilters.map(renderFilterChip)}\n </div>\n ) : (\n <div className=\"dc:flex-1 dc:min-w-0\">\n <div\n className=\"dc:text-xs dc:px-3 dc:py-1 dc:rounded-md dc:inline-block\"\n style={{\n backgroundColor: 'var(--dc-surface-secondary)',\n color: 'var(--dc-text-secondary)'\n }}\n >\n {t('dashboardFilter.editMode.noFilters')}\n </div>\n </div>\n )}\n\n {/* Add Button Section */}\n <div className=\"dc:flex dc:items-center dc:gap-1 dc:shrink-0\">\n {/* Only show Date Range button if no universal time filter exists */}\n {!dashboardFilters.some(f => f.isUniversalTime) && (\n <button\n onClick={onAddTimeFilter}\n className=\"dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded-md dc:text-xs dc:font-medium dc:transition-colors dc:hover:opacity-80\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n color: 'var(--dc-primary)',\n border: '1px solid var(--dc-border)'\n }}\n title=\"Add date range filter (applies to all time dimensions)\"\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span>{t('dashboardFilter.editMode.dateRange')}</span>\n </button>\n )}\n <button\n onClick={onAddFilter}\n className=\"dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded-md dc:text-xs dc:font-medium dc:transition-colors dc:hover:opacity-80\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n <span>{t('dashboardFilter.editMode.filter')}</span>\n </button>\n </div>\n </div>\n </>\n )\n}\n\nexport default EditModeFilterList\n","/**\n * Filter value formatting utilities for compact chips.\n *\n * Split out of `shared/utils.ts` by concern. Re-exported from there to keep\n * existing import paths stable.\n */\n\n/** Operators that render without any values. */\nconst NO_VALUE_LABELS: Record<string, string> = {\n set: 'is set',\n notSet: 'is not set',\n isEmpty: 'is empty',\n isNotEmpty: 'is not empty'\n}\n\n/** Operators rendered as `<symbol> <firstValue>`. */\nconst UNARY_SYMBOLS: Record<string, string> = {\n gt: '>',\n gte: '>=',\n lt: '<',\n lte: '<='\n}\n\n/** Operators rendered as `<verb> \"<firstValue>\"`. */\nconst QUOTED_VERBS: Record<string, string> = {\n contains: 'contains',\n notContains: '!contains',\n startsWith: 'starts with',\n endsWith: 'ends with'\n}\n\nfunction stringifyValue(v: any): string {\n if (v === true) return 'true'\n if (v === false) return 'false'\n if (v === null || v === undefined) return 'null'\n return String(v)\n}\n\n/**\n * Format filter value for display in a compact chip\n */\nexport function formatFilterValueDisplay(values: any[], operator: string): string {\n if (!values || values.length === 0) {\n // Handle operators that don't need values\n return NO_VALUE_LABELS[operator] ?? ''\n }\n\n const formattedValues = values.map(stringifyValue)\n\n if (UNARY_SYMBOLS[operator]) {\n return `${UNARY_SYMBOLS[operator]} ${formattedValues[0]}`\n }\n\n if (QUOTED_VERBS[operator]) {\n return `${QUOTED_VERBS[operator]} \"${formattedValues[0]}\"`\n }\n\n switch (operator) {\n case 'equals':\n return formattedValues.length === 1 ? `= ${formattedValues[0]}` : `in (${formattedValues.join(', ')})`\n case 'notEquals':\n return formattedValues.length === 1 ? `!= ${formattedValues[0]}` : `not in (${formattedValues.join(', ')})`\n case 'between':\n return `${formattedValues[0]} - ${formattedValues[1] || '?'}`\n case 'in':\n return `in (${formattedValues.join(', ')})`\n case 'notIn':\n return `not in (${formattedValues.join(', ')})`\n case 'set':\n return 'is set'\n case 'notSet':\n return 'is not set'\n default:\n return formattedValues.join(', ')\n }\n}\n","/**\n * useCompactFilterBar\n *\n * State + derived values + handlers backing CompactFilterBar. Extracted from the\n * component so the render stays flat. Behaviour is identical to the previous\n * inline implementation.\n */\n\nimport { useState, useCallback, useMemo, useRef, useEffect } from 'react'\nimport type { DashboardFilter, SimpleFilter } from '../../types.js'\nimport {\n detectPresetFromDateRange,\n calculateDateRange,\n formatDateRangeDisplay,\n XTD_OPTIONS\n} from '../shared/utils.js'\n\nexport function useCompactFilterBar(\n dashboardFilters: DashboardFilter[],\n onDashboardFiltersChange: (filters: DashboardFilter[]) => void\n) {\n // Local state for immediate UI feedback on filter value changes.\n // Without this, changes require a full round-trip through the parent's\n // onConfigChange → state update → re-render cycle before being visible.\n // If the parent doesn't handle onConfigChange (or uses dashboardFilters prop),\n // the round-trip never completes and clicks appear to do nothing.\n const [localFilters, setLocalFilters] = useState<DashboardFilter[]>(dashboardFilters)\n\n // Sync from props when parent updates (e.g., after round-trip completes,\n // or when filters change externally)\n useEffect(() => {\n setLocalFilters(dashboardFilters)\n }, [dashboardFilters])\n\n // Dropdown state\n const [showCustomDropdown, setShowCustomDropdown] = useState(false)\n const [showXTDDropdown, setShowXTDDropdown] = useState(false)\n\n // Refs for dropdown positioning\n const customButtonRef = useRef<HTMLButtonElement>(null)\n const xtdButtonRef = useRef<HTMLButtonElement>(null)\n\n // Find universal time filter\n const universalTimeFilter = useMemo(() => {\n return localFilters.find(df => df.isUniversalTime)\n }, [localFilters])\n\n // Get current date range from universal time filter\n const currentDateRange = useMemo(() => {\n if (!universalTimeFilter) return null\n const filter = universalTimeFilter.filter as SimpleFilter\n // Handle both dateRange property and values array\n if (filter.dateRange) return filter.dateRange\n if (filter.values && filter.values.length > 0) {\n // Single string value (preset) - return as string\n if (filter.values.length === 1 && typeof filter.values[0] === 'string') {\n return filter.values[0]\n }\n // Array of dates for custom range\n return filter.values\n }\n return null\n }, [universalTimeFilter])\n\n // Detect active preset from current date range\n const activePresetId = useMemo(() => {\n return detectPresetFromDateRange(currentDateRange as string | string[] | undefined)\n }, [currentDateRange])\n\n // Check if XTD is active\n const activeXTDId = useMemo(() => {\n if (!currentDateRange || Array.isArray(currentDateRange)) return null\n const preset = detectPresetFromDateRange(currentDateRange)\n return XTD_OPTIONS.find(opt => opt.id === preset)?.id || null\n }, [currentDateRange])\n\n // Get non-date filters (exclude universal time filter)\n const nonDateFilters = useMemo(() => {\n return localFilters.filter(df => !df.isUniversalTime)\n }, [localFilters])\n\n // Generate unique ID for new filters\n const generateFilterId = useCallback(() => {\n return `df_${Date.now()}_${Math.random().toString(36).substring(7)}`\n }, [])\n\n // Handle date range change (preset, custom, or XTD)\n const handleDateRangeChange = useCallback((newDateRange: string | string[]) => {\n if (universalTimeFilter) {\n // Update existing filter\n const updatedFilters = localFilters.map(df => {\n if (df.id === universalTimeFilter.id) {\n return {\n ...df,\n filter: {\n ...(df.filter as SimpleFilter),\n values: Array.isArray(newDateRange) ? newDateRange : [newDateRange],\n dateRange: newDateRange\n }\n }\n }\n return df\n })\n setLocalFilters(updatedFilters)\n onDashboardFiltersChange(updatedFilters)\n } else {\n // Create new universal time filter\n const newFilter: DashboardFilter = {\n id: generateFilterId(),\n label: 'Date Range',\n isUniversalTime: true,\n filter: {\n member: '__universal_time__',\n operator: 'inDateRange',\n values: Array.isArray(newDateRange) ? newDateRange : [newDateRange],\n dateRange: newDateRange\n }\n }\n const updatedFilters = [...localFilters, newFilter]\n setLocalFilters(updatedFilters)\n onDashboardFiltersChange(updatedFilters)\n }\n }, [localFilters, universalTimeFilter, onDashboardFiltersChange, generateFilterId])\n\n // Handle preset selection\n const handlePresetSelect = useCallback((presetValue: string) => {\n handleDateRangeChange(presetValue)\n }, [handleDateRangeChange])\n\n // Handle XTD selection\n const handleXTDSelect = useCallback((xtdValue: string) => {\n handleDateRangeChange(xtdValue)\n setShowXTDDropdown(false)\n }, [handleDateRangeChange])\n\n // Handle custom date selection\n const handleCustomDateSelect = useCallback((dateRange: string | string[]) => {\n handleDateRangeChange(dateRange)\n setShowCustomDropdown(false)\n }, [handleDateRangeChange])\n\n // Handle filter value change (for non-date filters)\n const handleFilterChange = useCallback((filterId: string, updatedFilter: DashboardFilter) => {\n const updatedFilters = localFilters.map(df =>\n df.id === filterId ? updatedFilter : df\n )\n setLocalFilters(updatedFilters)\n onDashboardFiltersChange(updatedFilters)\n }, [localFilters, onDashboardFiltersChange])\n\n // Calculate tooltip for active date range\n const dateRangeTooltip = useMemo(() => {\n if (!currentDateRange) return null\n\n if (Array.isArray(currentDateRange)) {\n // Custom date range - format the dates\n const start = new Date(currentDateRange[0])\n const end = new Date(currentDateRange[1] || currentDateRange[0])\n return formatDateRangeDisplay(start, end)\n }\n\n // Preset - calculate the actual range\n const range = calculateDateRange(currentDateRange)\n if (range) {\n return formatDateRangeDisplay(range.start, range.end)\n }\n\n return currentDateRange\n }, [currentDateRange])\n\n return {\n localFilters,\n showCustomDropdown,\n setShowCustomDropdown,\n showXTDDropdown,\n setShowXTDDropdown,\n customButtonRef,\n xtdButtonRef,\n currentDateRange,\n activePresetId,\n activeXTDId,\n nonDateFilters,\n handlePresetSelect,\n handleXTDSelect,\n handleCustomDateSelect,\n handleFilterChange,\n dateRangeTooltip\n }\n}\n","/**\n * DatePresetChips Component\n *\n * Quick-select date preset chips for the compact filter bar.\n * Displays common date ranges as clickable chips (Today, Yesterday, 7D, 30D, etc.)\n */\n\nimport React, { useMemo } from 'react'\nimport { DATE_PRESETS, calculateDateRange, formatDateRangeDisplay } from '../shared/utils.js'\n\ninterface DatePresetChipsProps {\n activePreset: string | null\n onPresetSelect: (presetValue: string) => void\n disabled?: boolean\n}\n\nconst DatePresetChips: React.FC<DatePresetChipsProps> = ({\n activePreset,\n onPresetSelect,\n disabled = false\n}) => {\n // Memoize tooltip content for each preset\n const presetTooltips = useMemo(() => {\n const tooltips: Record<string, string> = {}\n for (const preset of DATE_PRESETS) {\n const range = calculateDateRange(preset.value)\n if (range) {\n tooltips[preset.id] = formatDateRangeDisplay(range.start, range.end)\n }\n }\n return tooltips\n }, [])\n\n return (\n <div className=\"dc:flex dc:items-center dc:gap-1\">\n {DATE_PRESETS.map(preset => {\n const isActive = activePreset === preset.id\n const tooltip = presetTooltips[preset.id]\n\n return (\n <button\n key={preset.id}\n type=\"button\"\n onClick={() => onPresetSelect(preset.value)}\n disabled={disabled}\n title={tooltip}\n className={`\n dc:px-2.5 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:transition-colors\n dc:focus:outline-none dc:focus:ring-2 dc:focus:ring-offset-1\n dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\n ${isActive ? 'dc:shadow-sm' : 'dc:border'}\n `}\n style={{\n backgroundColor: isActive ? 'var(--dc-primary)' : 'var(--dc-surface)',\n color: isActive ? 'white' : 'var(--dc-text)',\n borderColor: isActive ? 'transparent' : 'var(--dc-border)',\n ...(disabled ? {} : {\n cursor: 'pointer'\n })\n }}\n onMouseEnter={(e) => {\n if (!isActive && !disabled) {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive && !disabled) {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface)'\n }\n }}\n >\n {preset.label}\n </button>\n )\n })}\n </div>\n )\n}\n\nexport default DatePresetChips\n","/**\n * CustomDateDropdown Component\n *\n * Tabbed dropdown for custom date selection with Fixed, Since, and Last tabs.\n */\n\nimport React, { useState, useEffect, useRef, useCallback } from 'react'\nimport { formatDateForCube, convertDateRangeTypeToValue } from '../shared/utils.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\ntype TabType = 'fixed' | 'since' | 'last'\ntype LastUnit = 'days' | 'weeks' | 'months' | 'quarters' | 'years'\n\ninterface CustomDateDropdownProps {\n isOpen: boolean\n onClose: () => void\n onDateRangeChange: (dateRange: string | string[]) => void\n currentDateRange?: string | string[]\n anchorRef: React.RefObject<HTMLElement>\n}\n\nconst LAST_UNITS: { value: LastUnit; label: string }[] = [\n { value: 'days', label: 'Days' },\n { value: 'weeks', label: 'Weeks' },\n { value: 'months', label: 'Months' },\n { value: 'quarters', label: 'Quarters' },\n { value: 'years', label: 'Years' }\n]\n\nconst CustomDateDropdown: React.FC<CustomDateDropdownProps> = ({\n isOpen,\n onClose,\n onDateRangeChange,\n currentDateRange\n}) => {\n const { t } = useTranslation()\n const dropdownRef = useRef<HTMLDivElement>(null)\n\n // Tab state\n const [activeTab, setActiveTab] = useState<TabType>('fixed')\n\n // Fixed tab state\n const [fixedStartDate, setFixedStartDate] = useState('')\n const [fixedEndDate, setFixedEndDate] = useState('')\n\n // Since tab state\n const [sinceDate, setSinceDate] = useState('')\n\n // Last tab state\n const [lastNumber, setLastNumber] = useState(7)\n const [lastUnit, setLastUnit] = useState<LastUnit>('days')\n\n // Initialize state from current date range\n useEffect(() => {\n if (!currentDateRange) return\n\n if (Array.isArray(currentDateRange)) {\n // Custom date range - set fixed tab\n setActiveTab('fixed')\n setFixedStartDate(currentDateRange[0] || '')\n setFixedEndDate(currentDateRange[1] || currentDateRange[0] || '')\n } else {\n // Check for \"last N units\" pattern\n const lastNMatch = currentDateRange.match(/^last\\s+(\\d+)\\s+(day|days|week|weeks|month|months|quarter|quarters|year|years)$/i)\n if (lastNMatch) {\n setActiveTab('last')\n setLastNumber(parseInt(lastNMatch[1], 10))\n const unit = lastNMatch[2].toLowerCase()\n if (unit === 'day') setLastUnit('days')\n else if (unit === 'week') setLastUnit('weeks')\n else if (unit === 'month') setLastUnit('months')\n else if (unit === 'quarter') setLastUnit('quarters')\n else if (unit === 'year') setLastUnit('years')\n else setLastUnit(unit.endsWith('s') ? unit as LastUnit : `${unit}s` as LastUnit)\n }\n }\n }, [currentDateRange])\n\n // Handle click outside to close\n useEffect(() => {\n if (!isOpen) return\n\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n onClose()\n }\n }\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n\n // Delay adding listener to prevent immediate close\n const timeoutId = setTimeout(() => {\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n }, 0)\n\n return () => {\n clearTimeout(timeoutId)\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n }\n }, [isOpen, onClose])\n\n // Handle apply for Fixed tab\n const handleApplyFixed = useCallback(() => {\n if (fixedStartDate && fixedEndDate) {\n onDateRangeChange([fixedStartDate, fixedEndDate])\n } else if (fixedStartDate) {\n onDateRangeChange([fixedStartDate, fixedStartDate])\n }\n }, [fixedStartDate, fixedEndDate, onDateRangeChange])\n\n // Handle apply for Since tab\n const handleApplySince = useCallback(() => {\n if (sinceDate) {\n const today = formatDateForCube(new Date())\n onDateRangeChange([sinceDate, today])\n }\n }, [sinceDate, onDateRangeChange])\n\n // Handle apply for Last tab\n const handleApplyLast = useCallback(() => {\n if (lastNumber > 0) {\n // Convert to the internal DateRangeType format then to value\n const rangeType = `last_n_${lastUnit}`\n const value = convertDateRangeTypeToValue(rangeType, lastNumber)\n onDateRangeChange(value)\n }\n }, [lastNumber, lastUnit, onDateRangeChange])\n\n if (!isOpen) return null\n\n const tabButtonStyle = (isActive: boolean) => ({\n backgroundColor: isActive ? 'var(--dc-primary)' : 'transparent',\n color: isActive ? 'white' : 'var(--dc-text)',\n borderBottom: isActive ? 'none' : `2px solid var(--dc-border)`\n })\n\n // Stop propagation to prevent parent handlers from interfering\n const handleDropdownClick = (e: React.MouseEvent) => {\n e.stopPropagation()\n }\n\n return (\n <div\n ref={dropdownRef}\n className=\"dc:absolute dc:top-full dc:left-0 dc:mt-1 dc:z-50 dc:border dc:rounded-lg dc:shadow-lg dc:min-w-[280px]\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: 'var(--dc-border)',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15)'\n }}\n onClick={handleDropdownClick}\n onMouseDown={handleDropdownClick}\n >\n {/* Tab Headers */}\n <div\n className=\"dc:flex dc:border-b\"\n style={{ borderColor: 'var(--dc-border)' }}\n >\n {(['fixed', 'since', 'last'] as TabType[]).map(tab => (\n <button\n key={tab}\n type=\"button\"\n onClick={() => setActiveTab(tab)}\n className=\"dc:flex-1 dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:capitalize dc:transition-colors\"\n style={tabButtonStyle(activeTab === tab)}\n >\n {tab}\n </button>\n ))}\n </div>\n\n {/* Tab Content */}\n <div className=\"dc:p-4\">\n {/* Fixed Tab */}\n {activeTab === 'fixed' && (\n <div className=\"dc:space-y-3\">\n <div>\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {t('dashboardFilter.customDate.startDate')}\n </label>\n <input\n type=\"date\"\n value={fixedStartDate}\n onChange={(e) => setFixedStartDate(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n />\n </div>\n <div>\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {t('dashboardFilter.customDate.endDate')}\n </label>\n <input\n type=\"date\"\n value={fixedEndDate}\n onChange={(e) => setFixedEndDate(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n />\n </div>\n <button\n type=\"button\"\n onClick={handleApplyFixed}\n disabled={!fixedStartDate}\n className=\"dc:w-full dc:py-2 dc:text-sm dc:font-medium dc:rounded dc:transition-colors dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n {t('common.actions.apply')}\n </button>\n </div>\n )}\n\n {/* Since Tab */}\n {activeTab === 'since' && (\n <div className=\"dc:space-y-3\">\n <div>\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {t('dashboardFilter.customDate.sinceDate')}\n </label>\n <input\n type=\"date\"\n value={sinceDate}\n onChange={(e) => setSinceDate(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n />\n </div>\n <p\n className=\"dc:text-xs\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {t('dashboardFilter.customDate.fromSelectedToToday')}\n </p>\n <button\n type=\"button\"\n onClick={handleApplySince}\n disabled={!sinceDate}\n className=\"dc:w-full dc:py-2 dc:text-sm dc:font-medium dc:rounded dc:transition-colors dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n {t('common.actions.apply')}\n </button>\n </div>\n )}\n\n {/* Last Tab */}\n {activeTab === 'last' && (\n <div className=\"dc:space-y-3\">\n <div className=\"dc:flex dc:gap-2\">\n <div className=\"dc:flex-1\">\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {t('dashboardFilter.customDate.number')}\n </label>\n <input\n type=\"number\"\n min=\"1\"\n max=\"999\"\n value={lastNumber}\n onChange={(e) => setLastNumber(Math.max(1, parseInt(e.target.value, 10) || 1))}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n />\n </div>\n <div className=\"dc:flex-1\">\n <label\n className=\"dc:block dc:text-xs dc:font-medium dc:mb-1\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {t('dashboardFilter.customDate.unit')}\n </label>\n <select\n value={lastUnit}\n onChange={(e) => setLastUnit(e.target.value as LastUnit)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-sm dc:border dc:rounded dc:focus:outline-none dc:focus:ring-2\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-bg)',\n color: 'var(--dc-text)'\n }}\n >\n {LAST_UNITS.map(unit => (\n <option key={unit.value} value={unit.value}>\n {unit.label}\n </option>\n ))}\n </select>\n </div>\n </div>\n <p\n className=\"dc:text-xs\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {t('dashboardFilter.customDate.lastNPreview', { number: lastNumber, unit: lastNumber === 1 ? lastUnit.slice(0, -1) : lastUnit })}\n </p>\n <button\n type=\"button\"\n onClick={handleApplyLast}\n disabled={lastNumber < 1}\n className=\"dc:w-full dc:py-2 dc:text-sm dc:font-medium dc:rounded dc:transition-colors dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n {t('common.actions.apply')}\n </button>\n </div>\n )}\n </div>\n\n {/* Cancel Button */}\n <div\n className=\"dc:px-4 dc:pb-4\"\n >\n <button\n type=\"button\"\n onClick={onClose}\n className=\"dc:w-full dc:py-2 dc:text-sm dc:font-medium dc:rounded dc:border dc:transition-colors\"\n style={{\n borderColor: 'var(--dc-border)',\n color: 'var(--dc-text-secondary)',\n backgroundColor: 'transparent'\n }}\n >\n {t('common.actions.cancel')}\n </button>\n </div>\n </div>\n )\n}\n\nexport default CustomDateDropdown\n","/**\n * XTDDropdown Component\n *\n * Dropdown for X-to-Date period selections (Week, Month, Quarter, Year to Date).\n */\n\nimport React, { useEffect, useRef } from 'react'\nimport { getIcon } from '../../icons/index.js'\nimport { XTD_OPTIONS, calculateDateRange, formatDateRangeDisplay } from '../shared/utils.js'\n\nconst CheckIcon = getIcon('check')\n\ninterface XTDDropdownProps {\n isOpen: boolean\n onClose: () => void\n onSelect: (xtdValue: string) => void\n currentXTD?: string | null\n anchorRef: React.RefObject<HTMLElement>\n}\n\nconst XTDDropdown: React.FC<XTDDropdownProps> = ({\n isOpen,\n onClose,\n onSelect,\n currentXTD\n}) => {\n const dropdownRef = useRef<HTMLDivElement>(null)\n\n // Handle click outside to close\n useEffect(() => {\n if (!isOpen) return\n\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n onClose()\n }\n }\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n\n // Delay adding listener to prevent immediate close\n const timeoutId = setTimeout(() => {\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n }, 0)\n\n return () => {\n clearTimeout(timeoutId)\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n }\n }, [isOpen, onClose])\n\n if (!isOpen) return null\n\n // Stop propagation to prevent parent handlers from interfering\n const handleDropdownClick = (e: React.MouseEvent) => {\n e.stopPropagation()\n }\n\n return (\n <div\n ref={dropdownRef}\n className=\"dc:absolute dc:top-full dc:left-0 dc:mt-1 dc:z-50 dc:border dc:rounded-lg dc:shadow-lg dc:min-w-[180px] dc:py-1\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: 'var(--dc-border)',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15)'\n }}\n onClick={handleDropdownClick}\n onMouseDown={handleDropdownClick}\n >\n {XTD_OPTIONS.map(option => {\n const isActive = currentXTD === option.id\n const dateRange = calculateDateRange(option.value)\n const dateRangeText = dateRange\n ? formatDateRangeDisplay(dateRange.start, dateRange.end)\n : ''\n\n return (\n <button\n key={option.id}\n type=\"button\"\n onClick={() => onSelect(option.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:text-left dc:text-sm dc:transition-colors dc:flex dc:items-center dc:justify-between dc:gap-2\"\n style={{\n backgroundColor: isActive ? 'var(--dc-primary-bg)' : 'transparent',\n color: 'var(--dc-text)'\n }}\n onMouseEnter={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent'\n }\n }}\n >\n <div className=\"dc:flex dc:flex-col\">\n <span className=\"dc:font-medium\">{option.label}</span>\n {dateRangeText && (\n <span\n className=\"dc:text-xs dc:mt-0.5\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {dateRangeText}\n </span>\n )}\n </div>\n {isActive && (\n <CheckIcon\n className=\"dc:w-4 dc:h-4 dc:shrink-0\"\n style={{ color: 'var(--dc-primary)' }}\n />\n )}\n </button>\n )\n })}\n </div>\n )\n}\n\nexport default XTDDropdown\n","/**\n * State + effect cluster for FilterValueSelector, extracted to keep the\n * component a thin presentational dispatcher.\n */\n\nimport { useState, useEffect, useRef, useCallback, useMemo } from 'react'\nimport type { ChangeEvent } from 'react'\nimport { useFilterValues } from '../../../hooks/useFilterValues.js'\nimport { useDebounce } from '../../../hooks/useDebounce.js'\nimport { FILTER_OPERATORS } from '../types.js'\nimport type { FilterValueSelectorProps } from '../types.js'\n\nconst COMBO_OPERATORS = ['equals', 'notEquals', 'in', 'notIn']\n\nexport function useFilterValueSelectorState({\n fieldName,\n operator,\n values,\n onValuesChange,\n schema\n}: FilterValueSelectorProps) {\n const operatorMeta = FILTER_OPERATORS[operator]\n const [isOpen, setIsOpen] = useState(false)\n const [searchText, setSearchText] = useState('')\n const [hasLoadedInitial, setHasLoadedInitial] = useState(false)\n const dropdownRef = useRef<HTMLDivElement>(null)\n const lastSearchedTerm = useRef<string>('')\n\n // Debounce the search text\n const debouncedSearchText = useDebounce(searchText, 300)\n\n // Check if the field is a dimension (not a measure)\n const isDimension = useMemo(() => schema ? schema.cubes.some(cube =>\n cube.dimensions.some(dim => dim.name === fieldName)\n ) : false, [schema, fieldName])\n\n // Check if the field is a time dimension\n const isTimeDimension = useMemo(() => schema ? schema.cubes.some(cube =>\n cube.dimensions.some(dim => dim.name === fieldName && dim.type === 'time')\n ) : false, [schema, fieldName])\n\n // Fetch distinct values for combo box (only for equals/notEquals/in/notIn on non-time dimensions)\n const shouldFetchValues = useMemo(() =>\n COMBO_OPERATORS.includes(operator) && isDimension && !isTimeDimension,\n [operator, isDimension, isTimeDimension]\n )\n const shouldShowComboBox = shouldFetchValues\n\n const {\n values: distinctValues,\n loading: valuesLoading,\n error: valuesError,\n searchValues\n } = useFilterValues(fieldName, shouldFetchValues)\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false)\n }\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [])\n\n // Load initial values when dropdown opens\n useEffect(() => {\n if (isOpen && shouldFetchValues && searchValues) {\n searchValues('', true) // Force load with empty search\n setHasLoadedInitial(true)\n lastSearchedTerm.current = ''\n }\n }, [isOpen, shouldFetchValues, searchValues])\n\n // Trigger search when debounced search text changes\n useEffect(() => {\n if (hasLoadedInitial && shouldFetchValues && searchValues && debouncedSearchText !== lastSearchedTerm.current) {\n lastSearchedTerm.current = debouncedSearchText\n searchValues(debouncedSearchText)\n }\n }, [debouncedSearchText, hasLoadedInitial, shouldFetchValues, searchValues])\n\n // Handle dropdown toggle\n const handleDropdownToggle = useCallback(() => {\n const newIsOpen = !isOpen\n setIsOpen(newIsOpen)\n\n // Reset search when closing dropdown\n if (!newIsOpen) {\n setSearchText('')\n lastSearchedTerm.current = ''\n }\n }, [isOpen])\n\n // Handle search input change\n const handleSearchChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n setSearchText(e.target.value)\n }, [])\n\n // Handle value selection for combo box\n const handleValueSelect = useCallback((value: any) => {\n if (operatorMeta.supportsMultipleValues) {\n // Add to selection if not already selected\n if (!values.includes(value)) {\n onValuesChange([...values, value])\n }\n } else {\n // Replace current value\n onValuesChange([value])\n setIsOpen(false)\n }\n // Clear search after selection\n setSearchText('')\n }, [operatorMeta.supportsMultipleValues, values, onValuesChange])\n\n // Handle value removal for multi-select\n const handleValueRemove = useCallback((valueToRemove: any) => {\n onValuesChange(values.filter(v => v !== valueToRemove))\n }, [values, onValuesChange])\n\n // Handle direct text input for non-combo operators\n const handleDirectInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n if (operatorMeta.valueType === 'number') {\n const numValue = parseFloat(value)\n // Accept valid numbers including zero\n if (!isNaN(numValue)) {\n onValuesChange([numValue])\n } else if (value === '' || value === '-') {\n // Allow empty string or just a minus sign for negative numbers being typed\n onValuesChange([])\n }\n } else {\n onValuesChange(value ? [value] : [])\n }\n }, [operatorMeta.valueType, onValuesChange])\n\n // Handle date input\n const handleDateInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n if (operator === 'inDateRange') {\n // For date range, we need two values\n const currentValues = values.length >= 2 ? values : ['', '']\n onValuesChange([value, currentValues[1]])\n } else {\n // Single date value\n onValuesChange(value ? [value] : [])\n }\n }, [operator, values, onValuesChange])\n\n const handleDateRangeEndInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n const currentValues = values.length >= 2 ? values : ['', '']\n onValuesChange([currentValues[0], value])\n }, [values, onValuesChange])\n\n // Handle between/notBetween range inputs (must be defined at top level, not inside conditionals)\n const handleBetweenStartInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = parseFloat(e.target.value)\n const currentValues = values.length >= 2 ? values : ['', '']\n const newValues = [!isNaN(value) ? value : e.target.value === '' ? '' : currentValues[0], currentValues[1]]\n onValuesChange(newValues.filter(v => v !== ''))\n }, [values, onValuesChange])\n\n const handleBetweenEndInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n const value = parseFloat(e.target.value)\n const currentValues = values.length >= 2 ? values : ['', '']\n const newValues = [currentValues[0], !isNaN(value) ? value : e.target.value === '' ? '' : currentValues[1]]\n onValuesChange(newValues.filter(v => v !== ''))\n }, [values, onValuesChange])\n\n return {\n operatorMeta,\n isOpen,\n searchText,\n hasLoadedInitial,\n dropdownRef,\n isTimeDimension,\n shouldShowComboBox,\n distinctValues,\n valuesLoading,\n valuesError,\n handleDropdownToggle,\n handleSearchChange,\n handleValueSelect,\n handleValueRemove,\n handleDirectInput,\n handleDateInput,\n handleDateRangeEndInput,\n handleBetweenStartInput,\n handleBetweenEndInput\n }\n}\n\nexport { COMBO_OPERATORS }\n","/**\n * Presentational leaf inputs for FilterValueSelector. Each renders one input\n * variant; the parent component dispatches to the appropriate one.\n */\n\nimport React from 'react'\nimport { getIcon } from '../../../icons/index.js'\nimport { useTranslation } from '../../../hooks/useTranslation.js'\n\nconst ChevronDownIcon = getIcon('chevronDown')\nconst CloseIcon = getIcon('close')\n\nconst INPUT_CLASS =\n 'dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent'\n\ntype ChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => void\n\nexport function NoValueInput() {\n return (\n <div className=\"dc:text-sm text-dc-text-muted dc:italic\">\n No value required\n </div>\n )\n}\n\ninterface DateRangeInputProps {\n values: any[]\n onStartChange: ChangeHandler\n onEndChange: ChangeHandler\n}\n\nexport function DateRangeInput({ values, onStartChange, onEndChange }: DateRangeInputProps) {\n return (\n <div className=\"dc:flex dc:items-center dc:space-x-2\">\n <input type=\"date\" value={values[0] || ''} onChange={onStartChange} className={INPUT_CLASS} />\n <span className=\"dc:text-sm text-dc-text-muted\">to</span>\n <input type=\"date\" value={values[1] || ''} onChange={onEndChange} className={INPUT_CLASS} />\n </div>\n )\n}\n\ninterface BetweenInputProps {\n values: any[]\n onStartChange: ChangeHandler\n onEndChange: ChangeHandler\n}\n\nexport function BetweenInput({ values, onStartChange, onEndChange }: BetweenInputProps) {\n return (\n <div className=\"dc:flex dc:items-center dc:space-x-2\">\n <input\n type=\"number\"\n value={values[0] !== undefined && values[0] !== null ? values[0] : ''}\n onChange={onStartChange}\n placeholder=\"Min\"\n className={INPUT_CLASS}\n />\n <span className=\"dc:text-sm text-dc-text-muted\">to</span>\n <input\n type=\"number\"\n value={values[1] !== undefined && values[1] !== null ? values[1] : ''}\n onChange={onEndChange}\n placeholder=\"Max\"\n className={INPUT_CLASS}\n />\n </div>\n )\n}\n\ninterface SingleDateInputProps {\n values: any[]\n onChange: ChangeHandler\n}\n\nexport function SingleDateInput({ values, onChange }: SingleDateInputProps) {\n return <input type=\"date\" value={values[0] || ''} onChange={onChange} className={INPUT_CLASS} />\n}\n\ninterface NumberInputProps {\n values: any[]\n onChange: ChangeHandler\n}\n\nexport function NumberInput({ values, onChange }: NumberInputProps) {\n return (\n <input\n type=\"number\"\n value={values[0] !== undefined && values[0] !== null ? values[0] : ''}\n onChange={onChange}\n placeholder=\"Enter number\"\n className={INPUT_CLASS}\n />\n )\n}\n\ninterface TextInputProps {\n values: any[]\n onChange: ChangeHandler\n valueType: string\n}\n\nexport function TextInput({ values, onChange, valueType }: TextInputProps) {\n return (\n <input\n type=\"text\"\n value={values[0] !== undefined && values[0] !== null ? values[0] : ''}\n onChange={onChange}\n placeholder={`Enter ${valueType} value`}\n className={INPUT_CLASS}\n />\n )\n}\n\ninterface ValueChipProps {\n value: any\n onRemove: () => void\n}\n\n/** A single removable selected-value chip. */\nfunction ValueChip({ value, onRemove }: ValueChipProps) {\n return (\n <div className=\"dc:inline-flex dc:items-center bg-dc-time-dimension text-dc-time-dimension dc:text-xs dc:px-2 dc:py-1 dc:rounded-sm dc:border border-dc-time-dimension\">\n <span className=\"dc:mr-1\">{String(value)}</span>\n <button onClick={onRemove} className=\"text-dc-accent hover:text-dc-accent focus:outline-hidden\">\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n </div>\n )\n}\n\ninterface MultiDateInputProps {\n values: any[]\n onValuesChange: (values: any[]) => void\n onValueRemove: (value: any) => void\n}\n\nexport function MultiDateInput({ values, onValuesChange, onValueRemove }: MultiDateInputProps) {\n return (\n <div className=\"dc:space-y-2 dc:min-w-0 dc:max-w-full\">\n {/* Selected dates display */}\n {values.length > 0 && (\n <div className=\"dc:flex dc:flex-wrap dc:gap-1 dc:max-w-full\">\n {values.map((value, index) => (\n <ValueChip key={index} value={value} onRemove={() => onValueRemove(value)} />\n ))}\n </div>\n )}\n\n {/* Add new date */}\n <input\n type=\"date\"\n onChange={(e) => {\n if (e.target.value && !values.includes(e.target.value)) {\n onValuesChange([...values, e.target.value])\n e.target.value = '' // Clear the input\n }\n }}\n className={INPUT_CLASS}\n placeholder=\"Add date...\"\n />\n </div>\n )\n}\n\ninterface ComboBoxInputProps {\n dropdownRef: React.RefObject<HTMLDivElement>\n supportsMultipleValues: boolean\n values: any[]\n isOpen: boolean\n searchText: string\n hasLoadedInitial: boolean\n valuesLoading: boolean\n valuesError: any\n distinctValues: any[]\n onValuesChange: (values: any[]) => void\n onValueRemove: (value: any) => void\n onValueSelect: (value: any) => void\n onToggle: () => void\n onSearchChange: ChangeHandler\n}\n\n/** Selected-values header for the combo box (single or multi). */\nfunction ComboBoxSelection({\n supportsMultipleValues,\n values,\n onValueRemove,\n onClear\n}: {\n supportsMultipleValues: boolean\n values: any[]\n onValueRemove: (value: any) => void\n onClear: () => void\n}) {\n if (values.length === 0) return null\n\n if (supportsMultipleValues) {\n return (\n <div className=\"dc:flex dc:flex-wrap dc:gap-1 dc:mb-2 dc:max-w-full\">\n {values.map((value, index) => (\n <ValueChip key={index} value={value} onRemove={() => onValueRemove(value)} />\n ))}\n </div>\n )\n }\n\n return (\n <div className=\"dc:mb-2\">\n <ValueChip value={values[0]} onRemove={onClear} />\n </div>\n )\n}\n\n/** Body of the open dropdown (loading / error / empty / value list). */\nfunction ComboBoxMenu({\n searchText,\n valuesLoading,\n valuesError,\n distinctValues,\n values,\n onValueSelect,\n onSearchChange\n}: {\n searchText: string\n valuesLoading: boolean\n valuesError: any\n distinctValues: any[]\n values: any[]\n onValueSelect: (value: any) => void\n onSearchChange: ChangeHandler\n}) {\n const { t } = useTranslation()\n\n const renderList = () => {\n if (valuesLoading) {\n return (\n <div className=\"dc:p-2 dc:text-sm text-dc-text-muted\">\n {searchText ? t('filter.shared.valueSelector.searching') : t('filter.shared.valueSelector.loadingValues')}\n </div>\n )\n }\n if (valuesError) {\n return (\n <div className=\"dc:p-2 dc:text-sm text-dc-error\">\n {t('filter.shared.valueSelector.errorLoading', { error: valuesError })}\n </div>\n )\n }\n if (distinctValues.length === 0) {\n return (\n <div className=\"dc:p-2 dc:text-sm text-dc-text-muted\">\n {searchText ? t('filter.shared.valueSelector.noMatchingValues') : t('filter.shared.valueSelector.noValuesAvailable')}\n </div>\n )\n }\n return distinctValues.map((value, index) => {\n const isSelected = values.includes(value)\n return (\n <button\n key={`${value}-${index}`}\n onClick={() => onValueSelect(value)}\n className={`dc:w-full dc:text-left dc:px-3 dc:py-2 dc:text-sm hover:bg-dc-surface-hover focus:outline-hidden focus:bg-dc-surface-hover ${\n isSelected ? 'bg-dc-accent-bg text-dc-accent' : 'text-dc-text-secondary'\n }`}\n >\n {String(value)}\n {isSelected && <span className=\"dc:float-right text-dc-accent\">✓</span>}\n </button>\n )\n })\n }\n\n return (\n <div className=\"dc:absolute dc:z-30 dc:left-0 dc:right-0 dc:mt-1 bg-dc-surface dc:border border-dc-border dc:rounded-md dc:shadow-lg dc:max-h-60 dc:overflow-y-auto\">\n {/* Search input */}\n <div className=\"dc:p-2 dc:border-b border-dc-border\">\n <input\n type=\"text\"\n value={searchText}\n onChange={onSearchChange}\n placeholder={t('filter.shared.valueSelector.searchValues')}\n className=\"dc:w-full dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface text-dc-text dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent\"\n autoFocus\n />\n </div>\n\n {/* Values list */}\n <div className=\"dc:max-h-48 dc:overflow-y-auto\">{renderList()}</div>\n </div>\n )\n}\n\nexport function ComboBoxInput({\n dropdownRef,\n supportsMultipleValues,\n values,\n isOpen,\n searchText,\n hasLoadedInitial,\n valuesLoading,\n valuesError,\n distinctValues,\n onValuesChange,\n onValueRemove,\n onValueSelect,\n onToggle,\n onSearchChange\n}: ComboBoxInputProps) {\n const { t } = useTranslation()\n\n return (\n <div className=\"dc:relative dc:min-w-0 dc:max-w-full\" ref={dropdownRef}>\n <ComboBoxSelection\n supportsMultipleValues={supportsMultipleValues}\n values={values}\n onValueRemove={onValueRemove}\n onClear={() => onValuesChange([])}\n />\n\n {/* Dropdown trigger */}\n <button\n onClick={onToggle}\n className=\"dc:w-full dc:text-left dc:text-sm dc:border border-dc-border dc:rounded-sm dc:px-2 dc:py-1 bg-dc-surface hover:bg-dc-surface-hover dc:focus:ring-2 focus:ring-dc-accent focus:border-dc-accent dc:flex dc:items-center dc:justify-between dc:min-w-0\"\n >\n <span className=\"text-dc-text-muted dc:truncate\">\n {valuesLoading && !hasLoadedInitial ? t('filter.shared.valueSelector.loadingValues') : t('filter.shared.valueSelector.selectValue')}\n </span>\n <ChevronDownIcon className=\"dc:w-4 dc:h-4 text-dc-text-muted\" />\n </button>\n\n {/* Dropdown menu */}\n {isOpen && (\n <ComboBoxMenu\n searchText={searchText}\n valuesLoading={valuesLoading}\n valuesError={valuesError}\n distinctValues={distinctValues}\n values={values}\n onValueSelect={onValueSelect}\n onSearchChange={onSearchChange}\n />\n )}\n </div>\n )\n}\n","/**\n * FilterValueSelector Component\n *\n * Smart input component that adapts to operator type:\n * - Combo box for equals/notEquals with API-fetched values\n * - Number input for numeric operators\n * - Date picker for date operators\n * - No input for set/notSet operators\n */\n\nimport React from 'react'\nimport type { FilterValueSelectorProps } from './types.js'\nimport {\n useFilterValueSelectorState,\n COMBO_OPERATORS\n} from './filterValueSelector/useFilterValueSelectorState.js'\nimport {\n NoValueInput,\n DateRangeInput,\n BetweenInput,\n SingleDateInput,\n NumberInput,\n TextInput,\n MultiDateInput,\n ComboBoxInput\n} from './filterValueSelector/FilterValueInputs.js'\n\nconst FilterValueSelector: React.FC<FilterValueSelectorProps> = (props) => {\n const { operator, values, onValuesChange } = props\n const state = useFilterValueSelectorState(props)\n const {\n operatorMeta,\n isOpen,\n searchText,\n hasLoadedInitial,\n dropdownRef,\n isTimeDimension,\n shouldShowComboBox,\n distinctValues,\n valuesLoading,\n valuesError,\n handleDropdownToggle,\n handleSearchChange,\n handleValueSelect,\n handleValueRemove,\n handleDirectInput,\n handleDateInput,\n handleDateRangeEndInput,\n handleBetweenStartInput,\n handleBetweenEndInput\n } = state\n\n // No input needed for set/notSet\n if (!operatorMeta.requiresValues) {\n return <NoValueInput />\n }\n\n if (operator === 'inDateRange') {\n return (\n <DateRangeInput\n values={values}\n onStartChange={handleDateInput}\n onEndChange={handleDateRangeEndInput}\n />\n )\n }\n\n if (operator === 'between' || operator === 'notBetween') {\n return (\n <BetweenInput\n values={values}\n onStartChange={handleBetweenStartInput}\n onEndChange={handleBetweenEndInput}\n />\n )\n }\n\n if (operatorMeta.valueType === 'date') {\n return <SingleDateInput values={values} onChange={handleDateInput} />\n }\n\n if (operatorMeta.valueType === 'number') {\n return <NumberInput values={values} onChange={handleDirectInput} />\n }\n\n // Time dimension with equals/notEquals/in/notIn - use date picker\n if (isTimeDimension && COMBO_OPERATORS.includes(operator)) {\n if (operatorMeta.supportsMultipleValues) {\n return (\n <MultiDateInput\n values={values}\n onValuesChange={onValuesChange}\n onValueRemove={handleValueRemove}\n />\n )\n }\n return <SingleDateInput values={values} onChange={handleDateInput} />\n }\n\n if (shouldShowComboBox) {\n return (\n <ComboBoxInput\n dropdownRef={dropdownRef}\n supportsMultipleValues={operatorMeta.supportsMultipleValues}\n values={values}\n isOpen={isOpen}\n searchText={searchText}\n hasLoadedInitial={hasLoadedInitial}\n valuesLoading={valuesLoading}\n valuesError={valuesError}\n distinctValues={distinctValues}\n onValuesChange={onValuesChange}\n onValueRemove={handleValueRemove}\n onValueSelect={handleValueSelect}\n onToggle={handleDropdownToggle}\n onSearchChange={handleSearchChange}\n />\n )\n }\n\n // Fallback to text input\n return <TextInput values={values} onChange={handleDirectInput} valueType={operatorMeta.valueType} />\n}\n\nexport default FilterValueSelector\n","/**\n * FilterValuePopover Component\n *\n * Inline popover for editing filter values.\n * Uses FilterValueSelector for the actual value input.\n */\n\nimport React, { useEffect, useRef, useCallback } from 'react'\nimport FilterValueSelector from '../shared/FilterValueSelector.js'\nimport type { SimpleFilter, CubeMeta } from '../../types.js'\nimport type { MetaResponse } from '../../shared/types.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\ninterface FilterValuePopoverProps {\n filter: SimpleFilter\n schema: CubeMeta | null\n onValuesChange: (values: any[]) => void\n onClose: () => void\n anchorRef: React.RefObject<HTMLElement>\n}\n\n// Convert CubeMeta to MetaResponse format\nfunction convertToMetaResponse(cubeMeta: CubeMeta | null): MetaResponse | null {\n if (!cubeMeta) return null\n\n return {\n cubes: cubeMeta.cubes.map(cube => ({\n name: cube.name,\n title: cube.title || cube.name,\n description: cube.description || '',\n measures: cube.measures.map(m => ({\n name: m.name,\n title: m.title || m.name,\n type: m.type,\n description: '',\n shortTitle: m.shortTitle || m.title || m.name\n })),\n dimensions: cube.dimensions.map(d => ({\n name: d.name,\n title: d.title || d.name,\n type: d.type,\n description: '',\n shortTitle: d.shortTitle || d.title || d.name\n })),\n segments: cube.segments?.map(s => ({\n name: s.name,\n title: s.title || s.name,\n type: s.type,\n description: '',\n shortTitle: s.shortTitle || s.title || s.name\n })) || []\n }))\n }\n}\n\nconst FilterValuePopover: React.FC<FilterValuePopoverProps> = ({\n filter,\n schema,\n onValuesChange,\n onClose,\n anchorRef\n}) => {\n const { t } = useTranslation()\n const popoverRef = useRef<HTMLDivElement>(null)\n\n // Handle click outside to close\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (\n popoverRef.current &&\n !popoverRef.current.contains(event.target as Node) &&\n anchorRef.current &&\n !anchorRef.current.contains(event.target as Node)\n ) {\n onClose()\n }\n }\n\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose()\n }\n }\n\n // Delay adding listener to prevent immediate close\n const timeoutId = setTimeout(() => {\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n }, 0)\n\n return () => {\n clearTimeout(timeoutId)\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n }\n }, [onClose, anchorRef])\n\n // Handle value change\n const handleValuesChange = useCallback((newValues: any[]) => {\n onValuesChange(newValues)\n }, [onValuesChange])\n\n // Convert schema to MetaResponse format\n const metaResponse = convertToMetaResponse(schema)\n\n return (\n <div\n ref={popoverRef}\n className=\"dc:absolute dc:top-full dc:left-0 dc:mt-1 dc:z-50 dc:border dc:rounded-lg dc:shadow-lg dc:p-3 dc:min-w-[220px]\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: 'var(--dc-border)',\n boxShadow: 'var(--dc-shadow-lg)'\n }}\n >\n {/* Filter label */}\n <div\n className=\"dc:text-xs dc:font-medium dc:mb-2\"\n style={{ color: 'var(--dc-text-secondary)' }}\n >\n {t('dashboardFilter.filterValue.editValue')}\n </div>\n\n {/* Value selector */}\n <div className=\"dc:min-w-[180px]\">\n <FilterValueSelector\n fieldName={filter.member}\n operator={filter.operator}\n values={filter.values || []}\n onValuesChange={handleValuesChange}\n schema={metaResponse}\n />\n </div>\n\n {/* Action buttons */}\n <div className=\"dc:flex dc:justify-end dc:gap-2 dc:mt-3 dc:pt-2 dc:border-t\" style={{ borderColor: 'var(--dc-border)' }}>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"dc:px-3 dc:py-1 dc:text-xs dc:font-medium dc:rounded dc:border dc:transition-colors\"\n style={{\n borderColor: 'var(--dc-border)',\n color: 'var(--dc-text-secondary)',\n backgroundColor: 'transparent'\n }}\n >\n {t('common.actions.close')}\n </button>\n </div>\n </div>\n )\n}\n\nexport default FilterValuePopover\n","/**\n * FilterChip Component\n *\n * Compact display of non-date filters as clickable chips.\n * Clicking opens a popover for inline value editing.\n */\n\nimport React, { useState, useRef, useCallback } from 'react'\nimport { getIcon } from '../../icons/index.js'\nimport FilterValuePopover from './FilterValuePopover.js'\nimport { formatFilterValueDisplay } from '../shared/utils.js'\nimport type { DashboardFilter, CubeMeta, SimpleFilter } from '../../types.js'\n\nconst CloseIcon = getIcon('close')\nconst EditIcon = getIcon('edit')\n\ninterface FilterChipProps {\n filter: DashboardFilter\n schema: CubeMeta | null\n isEditMode: boolean\n onChange: (updatedFilter: DashboardFilter) => void\n onEdit?: () => void\n onRemove?: () => void\n}\n\nconst FilterChip: React.FC<FilterChipProps> = ({\n filter,\n schema,\n isEditMode,\n onChange,\n onEdit,\n onRemove\n}) => {\n const [showPopover, setShowPopover] = useState(false)\n const chipRef = useRef<HTMLDivElement>(null)\n\n // Get filter details\n const simpleFilter = filter.filter as SimpleFilter\n const { label } = filter\n const { operator, values } = simpleFilter\n\n // Format value display\n const valueDisplay = formatFilterValueDisplay(values || [], operator)\n\n // Handle value change from popover\n const handleValueChange = useCallback((newValues: any[]) => {\n onChange({\n ...filter,\n filter: {\n ...simpleFilter,\n values: newValues\n }\n })\n }, [filter, simpleFilter, onChange])\n\n // Handle chip click - open popover in view mode, or edit in edit mode\n const handleChipClick = useCallback(() => {\n if (isEditMode) {\n // In edit mode, clicking opens the full modal\n onEdit?.()\n } else {\n // In view mode, show inline popover for value editing\n setShowPopover(true)\n }\n }, [isEditMode, onEdit])\n\n // Don't show chips for group filters\n if (!('member' in filter.filter)) {\n return null\n }\n\n return (\n <div ref={chipRef} className=\"dc:relative dc:inline-flex\">\n <div\n className={`\n dc:inline-flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded dc:text-xs\n dc:border dc:transition-colors dc:cursor-pointer\n ${isEditMode ? 'dc:pr-1' : ''}\n `}\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: 'var(--dc-border)',\n color: 'var(--dc-text)'\n }}\n onClick={handleChipClick}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface)'\n }}\n title={`${label} ${valueDisplay}`}\n >\n <span className=\"dc:font-medium dc:truncate dc:max-w-[100px]\">{label}</span>\n {valueDisplay && (\n <>\n <span style={{ color: 'var(--dc-text-secondary)' }}>{valueDisplay}</span>\n </>\n )}\n\n {/* Edit mode: show edit and remove buttons */}\n {isEditMode && (\n <>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onEdit?.()\n }}\n className=\"dc:p-0.5 dc:rounded dc:transition-colors\"\n style={{ color: 'var(--dc-text-secondary)' }}\n onMouseEnter={(e) => {\n e.currentTarget.style.color = 'var(--dc-text)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.color = 'var(--dc-text-secondary)'\n }}\n >\n <EditIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onRemove?.()\n }}\n className=\"dc:p-0.5 dc:rounded dc:transition-colors\"\n style={{ color: 'var(--dc-text-secondary)' }}\n onMouseEnter={(e) => {\n e.currentTarget.style.color = 'var(--dc-error)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.color = 'var(--dc-text-secondary)'\n }}\n >\n <CloseIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n </>\n )}\n </div>\n\n {/* Value editing popover (view mode only) */}\n {showPopover && !isEditMode && (\n <FilterValuePopover\n filter={simpleFilter}\n schema={schema}\n onValuesChange={handleValueChange}\n onClose={() => setShowPopover(false)}\n anchorRef={chipRef}\n />\n )}\n </div>\n )\n}\n\nexport default FilterChip\n","/**\n * CompactFilterBarParts\n *\n * Presentational sub-components and the desktop/mobile layouts extracted from\n * CompactFilterBar. Markup, classes, and i18n keys are identical to the previous\n * inline JSX — pure extraction to keep the main render flat.\n */\n\nimport type { RefObject } from 'react'\nimport { getIcon } from '../../icons/index.js'\nimport DatePresetChips from './DatePresetChips.js'\nimport CustomDateDropdown from './CustomDateDropdown.js'\nimport XTDDropdown from './XTDDropdown.js'\nimport FilterChip from './FilterChip.js'\nimport type { DashboardFilter, CubeMeta } from '../../types.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\nconst AddIcon = getIcon('add')\nconst CalendarIcon = getIcon('timeDimension')\nconst ChevronDownIcon = getIcon('chevronDown')\nconst FilterIcon = getIcon('filter')\n\nexport interface CompactFilterBarViewProps {\n schema: CubeMeta | null\n isEditMode: boolean\n onAddFilter?: () => void\n onEditFilter?: (filterId: string) => void\n onRemoveFilter?: (filterId: string) => void\n currentDateRange: string | string[] | null\n activePresetId: string | null\n activeXTDId: string | null\n nonDateFilters: DashboardFilter[]\n dateRangeTooltip: string | null\n showCustomDropdown: boolean\n setShowCustomDropdown: (next: boolean) => void\n showXTDDropdown: boolean\n setShowXTDDropdown: (next: boolean) => void\n customButtonRef: RefObject<HTMLButtonElement>\n xtdButtonRef: RefObject<HTMLButtonElement>\n handlePresetSelect: (presetValue: string) => void\n handleXTDSelect: (xtdValue: string) => void\n handleCustomDateSelect: (dateRange: string | string[]) => void\n handleFilterChange: (filterId: string, updatedFilter: DashboardFilter) => void\n}\n\nfunction AddFilterButton({\n isEditMode,\n onAddFilter,\n withHoverHandlers\n}: {\n isEditMode: boolean\n onAddFilter?: () => void\n withHoverHandlers: boolean\n}) {\n if (!isEditMode || !onAddFilter) return null\n return (\n <button\n type=\"button\"\n onClick={onAddFilter}\n className=\"dc:flex dc:items-center dc:gap-1 dc:px-2 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:border dc:transition-colors\"\n style={{\n borderColor: 'var(--dc-border)',\n color: 'var(--dc-text-secondary)',\n backgroundColor: 'transparent'\n }}\n onMouseEnter={withHoverHandlers ? (e) => {\n e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'\n } : undefined}\n onMouseLeave={withHoverHandlers ? (e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n } : undefined}\n >\n <AddIcon className=\"dc:w-3.5 dc:h-3.5\" />\n </button>\n )\n}\n\nfunction NonDateFilterChips({\n nonDateFilters,\n schema,\n isEditMode,\n onEditFilter,\n onRemoveFilter,\n handleFilterChange\n}: Pick<\n CompactFilterBarViewProps,\n 'nonDateFilters' | 'schema' | 'isEditMode' | 'onEditFilter' | 'onRemoveFilter' | 'handleFilterChange'\n>) {\n return (\n <>\n {nonDateFilters.map(filter => (\n <FilterChip\n key={filter.id}\n filter={filter}\n schema={schema}\n isEditMode={isEditMode}\n onChange={(updatedFilter) => handleFilterChange(filter.id, updatedFilter)}\n onEdit={() => onEditFilter?.(filter.id)}\n onRemove={() => onRemoveFilter?.(filter.id)}\n />\n ))}\n </>\n )\n}\n\nfunction CustomDateButton({\n props,\n showLabel,\n withTitle\n}: {\n props: CompactFilterBarViewProps\n showLabel: boolean\n withTitle: boolean\n}) {\n const { t } = useTranslation()\n const {\n activePresetId,\n dateRangeTooltip,\n showCustomDropdown,\n setShowCustomDropdown,\n setShowXTDDropdown,\n customButtonRef,\n currentDateRange,\n handleCustomDateSelect\n } = props\n return (\n <div className=\"dc:relative\">\n <button\n ref={customButtonRef}\n type=\"button\"\n onClick={() => {\n setShowCustomDropdown(!showCustomDropdown)\n setShowXTDDropdown(false)\n }}\n title={withTitle ? (activePresetId === 'custom' && dateRangeTooltip ? dateRangeTooltip : 'Custom date range') : undefined}\n className={`\n dc:flex dc:items-center dc:gap-1 dc:px-2.5 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:border\n dc:transition-colors${withTitle ? ' dc:focus:outline-none dc:focus:ring-2 dc:focus:ring-offset-1' : ''}\n `}\n style={{\n backgroundColor: activePresetId === 'custom' ? 'var(--dc-primary)' : 'var(--dc-surface)',\n color: activePresetId === 'custom' ? 'white' : 'var(--dc-text)',\n borderColor: activePresetId === 'custom' ? 'transparent' : 'var(--dc-border)'\n }}\n >\n <CalendarIcon className=\"dc:w-3 dc:h-3\" />\n <span>{t('dateRange.custom')}</span>\n {showLabel && <ChevronDownIcon className=\"dc:w-3 dc:h-3\" />}\n </button>\n\n {showCustomDropdown && (\n <CustomDateDropdown\n isOpen={showCustomDropdown}\n onClose={() => setShowCustomDropdown(false)}\n onDateRangeChange={handleCustomDateSelect}\n currentDateRange={currentDateRange as string | string[] | undefined}\n anchorRef={customButtonRef}\n />\n )}\n </div>\n )\n}\n\nfunction XTDButton({\n props,\n withTitle\n}: {\n props: CompactFilterBarViewProps\n withTitle: boolean\n}) {\n const {\n activeXTDId,\n dateRangeTooltip,\n showXTDDropdown,\n setShowXTDDropdown,\n setShowCustomDropdown,\n xtdButtonRef,\n handleXTDSelect\n } = props\n return (\n <div className=\"dc:relative\">\n <button\n ref={xtdButtonRef}\n type=\"button\"\n onClick={() => {\n setShowXTDDropdown(!showXTDDropdown)\n setShowCustomDropdown(false)\n }}\n title={withTitle ? (activeXTDId && dateRangeTooltip ? dateRangeTooltip : 'X to Date options') : undefined}\n className={`\n dc:flex dc:items-center dc:gap-1 dc:px-2.5 dc:py-1 dc:rounded dc:text-xs dc:font-medium dc:border\n dc:transition-colors${withTitle ? ' dc:focus:outline-none dc:focus:ring-2 dc:focus:ring-offset-1' : ''}\n `}\n style={{\n backgroundColor: activeXTDId ? 'var(--dc-primary)' : 'var(--dc-surface)',\n color: activeXTDId ? 'white' : 'var(--dc-text)',\n borderColor: activeXTDId ? 'transparent' : 'var(--dc-border)'\n }}\n >\n <span>XTD</span>\n <ChevronDownIcon className=\"dc:w-3 dc:h-3\" />\n </button>\n\n {showXTDDropdown && (\n <XTDDropdown\n isOpen={showXTDDropdown}\n onClose={() => setShowXTDDropdown(false)}\n onSelect={handleXTDSelect}\n currentXTD={activeXTDId}\n anchorRef={xtdButtonRef}\n />\n )}\n </div>\n )\n}\n\nexport function DesktopLayout(props: CompactFilterBarViewProps) {\n const { activePresetId, activeXTDId, nonDateFilters, handlePresetSelect, isEditMode, onAddFilter } = props\n return (\n <div className=\"dc:hidden dc:md:flex dc:items-center dc:gap-2 dc:px-3 dc:py-2\">\n {/* Filter Icon */}\n <FilterIcon\n className=\"dc:w-4 dc:h-4 dc:shrink-0\"\n style={{ color: 'var(--dc-text-secondary)' }}\n />\n\n {/* Date Preset Chips */}\n <DatePresetChips\n activePreset={activePresetId !== 'custom' && !activeXTDId ? activePresetId : null}\n onPresetSelect={handlePresetSelect}\n />\n\n {/* Custom Date Button */}\n <CustomDateButton props={props} showLabel withTitle />\n\n {/* XTD Button */}\n <XTDButton props={props} withTitle />\n\n {/* Separator */}\n {nonDateFilters.length > 0 && (\n <div\n className=\"dc:h-5 dc:w-px dc:mx-1\"\n style={{ backgroundColor: 'var(--dc-border)' }}\n />\n )}\n\n {/* Non-date Filter Chips */}\n <div className=\"dc:flex dc:items-center dc:gap-1.5 dc:flex-wrap\">\n <NonDateFilterChips {...props} />\n </div>\n\n {/* Add Filter Button (Edit Mode) */}\n <AddFilterButton isEditMode={isEditMode} onAddFilter={onAddFilter} withHoverHandlers />\n </div>\n )\n}\n\nexport function MobileLayout(props: CompactFilterBarViewProps) {\n const { activePresetId, activeXTDId, nonDateFilters, handlePresetSelect, isEditMode, onAddFilter } = props\n return (\n <div className=\"dc:md:hidden\">\n {/* Presets row with horizontal scroll */}\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:overflow-x-auto dc:px-3 dc:py-2 scrollbar-thin\">\n {/* Filter Icon */}\n <FilterIcon\n className=\"dc:w-4 dc:h-4 dc:shrink-0\"\n style={{ color: 'var(--dc-text-secondary)' }}\n />\n <DatePresetChips\n activePreset={activePresetId !== 'custom' && !activeXTDId ? activePresetId : null}\n onPresetSelect={handlePresetSelect}\n />\n </div>\n\n {/* Custom, XTD, and Add buttons */}\n <div\n className=\"dc:flex dc:items-center dc:justify-between dc:px-3 dc:py-2 dc:border-t\"\n style={{ borderColor: 'var(--dc-border)' }}\n >\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n {/* Custom Button */}\n <CustomDateButton props={props} showLabel={false} withTitle={false} />\n\n {/* XTD Button */}\n <XTDButton props={props} withTitle={false} />\n </div>\n\n {/* Add Filter Button (Edit Mode) */}\n <AddFilterButton isEditMode={isEditMode} onAddFilter={onAddFilter} withHoverHandlers={false} />\n </div>\n\n {/* Non-date Filter Chips (Mobile) */}\n {nonDateFilters.length > 0 && (\n <div\n className=\"dc:px-3 dc:py-2 dc:border-t\"\n style={{ borderColor: 'var(--dc-border)' }}\n >\n <div className=\"dc:flex dc:items-center dc:gap-1.5 dc:flex-wrap\">\n <NonDateFilterChips {...props} />\n </div>\n </div>\n )}\n </div>\n )\n}\n","/**\n * CompactFilterBar Component\n *\n * A Mixpanel-inspired compact horizontal filter bar for dashboards.\n * Provides quick preset date selection, custom date options, XTD options,\n * and compact non-date filter display.\n *\n * State, derived values, and handlers live in `useCompactFilterBar`; the\n * desktop/mobile layouts live in `CompactFilterBarParts`. This file is the\n * layout shell.\n */\n\nimport React from 'react'\nimport type { DashboardFilter, CubeMeta } from '../../types.js'\nimport { useCompactFilterBar } from './useCompactFilterBar.js'\nimport { DesktopLayout, MobileLayout, type CompactFilterBarViewProps } from './CompactFilterBarParts.js'\n\ninterface CompactFilterBarProps {\n dashboardFilters: DashboardFilter[]\n schema: CubeMeta | null\n isEditMode: boolean\n onDashboardFiltersChange: (filters: DashboardFilter[]) => void\n onAddFilter?: () => void\n onEditFilter?: (filterId: string) => void\n onRemoveFilter?: (filterId: string) => void\n}\n\nconst CompactFilterBar: React.FC<CompactFilterBarProps> = ({\n dashboardFilters,\n schema,\n isEditMode,\n onDashboardFiltersChange,\n onAddFilter,\n onEditFilter,\n onRemoveFilter\n}) => {\n const bar = useCompactFilterBar(dashboardFilters, onDashboardFiltersChange)\n\n // If no filters and not in edit mode, don't show anything\n if (!isEditMode && bar.localFilters.length === 0) {\n return null\n }\n\n const viewProps: CompactFilterBarViewProps = {\n schema,\n isEditMode,\n onAddFilter,\n onEditFilter,\n onRemoveFilter,\n currentDateRange: bar.currentDateRange as string | string[] | null,\n activePresetId: bar.activePresetId as string | null,\n activeXTDId: bar.activeXTDId,\n nonDateFilters: bar.nonDateFilters,\n dateRangeTooltip: bar.dateRangeTooltip as string | null,\n showCustomDropdown: bar.showCustomDropdown,\n setShowCustomDropdown: bar.setShowCustomDropdown,\n showXTDDropdown: bar.showXTDDropdown,\n setShowXTDDropdown: bar.setShowXTDDropdown,\n customButtonRef: bar.customButtonRef,\n xtdButtonRef: bar.xtdButtonRef,\n handlePresetSelect: bar.handlePresetSelect,\n handleXTDSelect: bar.handleXTDSelect,\n handleCustomDateSelect: bar.handleCustomDateSelect,\n handleFilterChange: bar.handleFilterChange\n }\n\n return (\n <div\n className=\"dc:border dc:rounded-lg\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-surface)'\n }}\n >\n <DesktopLayout {...viewProps} />\n <MobileLayout {...viewProps} />\n </div>\n )\n}\n\nexport default CompactFilterBar\n","/**\n * DashboardFilterPanel Component\n *\n * Orchestrates dashboard-level filtering UI\n * - Edit mode: Shows filter chips with edit/delete actions\n * - View mode: Shows interactive read-only filters\n *\n * Pattern: Simplified coordination layer (matches portlet editing pattern)\n * - No local editing state (modal owns all edit state)\n * - Just tracks which filter is being edited and modal visibility\n * - Delegates to FilterEditModal for all editing logic\n */\n\nimport React, { useState, useCallback } from 'react'\nimport FilterEditModal from './DashboardFilters/FilterEditModal.js'\nimport EditModeFilterList from './DashboardFilters/EditModeFilterList.js'\nimport CompactFilterBar from './DashboardFilters/CompactFilterBar.js'\nimport type { DashboardFilter, CubeMeta, DashboardConfig } from '../types.js'\nimport type { MetaResponse } from '../shared/types.js'\n\ninterface DashboardFilterPanelProps {\n dashboardFilters: DashboardFilter[]\n editable: boolean\n schema: CubeMeta | null\n dashboardConfig: DashboardConfig\n onDashboardFiltersChange: (filters: DashboardFilter[]) => void\n onSaveFilters?: (filters: DashboardFilter[]) => void | Promise<void>\n selectedFilterId?: string | null\n onFilterSelect?: (filterId: string) => void\n isEditMode?: boolean\n}\n\nconst DashboardFilterPanel: React.FC<DashboardFilterPanelProps> = ({\n dashboardFilters,\n editable,\n schema,\n dashboardConfig,\n onDashboardFiltersChange,\n onSaveFilters,\n selectedFilterId,\n onFilterSelect,\n isEditMode = false\n}) => {\n // Track which filter is being edited and modal visibility (no local editing state)\n const [editingFilter, setEditingFilter] = useState<DashboardFilter | null>(null)\n const [showFilterBuilder, setShowFilterBuilder] = useState(false)\n\n // Convert CubeMeta to MetaResponse (QueryBuilder type)\n const convertToMetaResponse = useCallback((cubeMeta: CubeMeta | null): MetaResponse | null => {\n if (!cubeMeta) return null\n\n return {\n cubes: cubeMeta.cubes.map(cube => ({\n name: cube.name,\n title: cube.title || cube.name,\n description: cube.description || '',\n measures: cube.measures.map(m => ({\n name: m.name,\n title: m.title,\n type: m.type,\n description: '',\n shortTitle: m.shortTitle\n })),\n dimensions: cube.dimensions.map(d => ({\n name: d.name,\n title: d.title,\n type: d.type,\n description: '',\n shortTitle: d.shortTitle\n })),\n segments: cube.segments?.map(s => ({\n name: s.name,\n title: s.title,\n type: s.type,\n description: '',\n shortTitle: s.shortTitle\n })) || []\n }))\n }\n }, [])\n\n // Generate unique ID for new filters\n const generateFilterId = useCallback(() => {\n return `df_${Date.now()}_${Math.random().toString(36).substring(7)}`\n }, [])\n\n // Apply a committed filter mutation: update dashboard state, then persist\n const applyFilters = useCallback(async (updatedFilters: DashboardFilter[]) => {\n onDashboardFiltersChange(updatedFilters)\n\n if (onSaveFilters) {\n await onSaveFilters(updatedFilters)\n }\n }, [onDashboardFiltersChange, onSaveFilters])\n\n // Handle adding a new filter - create temporary filter for modal\n const handleAddFilter = useCallback(() => {\n const newFilter: DashboardFilter = {\n id: generateFilterId(),\n label: `Filter ${dashboardFilters.length + 1}`,\n filter: {\n member: '',\n operator: 'equals',\n values: []\n }\n }\n setEditingFilter(newFilter)\n setShowFilterBuilder(true)\n }, [dashboardFilters.length, generateFilterId])\n\n // Handle adding a universal time filter - applies to all time dimensions\n // Creates filter directly without opening modal (fixed name, user just sets date range in view mode)\n const handleAddTimeFilter = useCallback(() => {\n const newFilter: DashboardFilter = {\n id: generateFilterId(),\n label: 'Date Range Filter',\n isUniversalTime: true,\n filter: {\n member: '__universal_time__', // Placeholder, not used in merge logic\n operator: 'inDateRange',\n values: ['last 30 days']\n }\n }\n // Add directly to filters without opening modal\n const updatedFilters = [...dashboardFilters, newFilter]\n applyFilters(updatedFilters).catch(error => {\n console.error('Failed to save filters:', error)\n })\n }, [generateFilterId, dashboardFilters, applyFilters])\n\n // Handle editing an existing filter - just open modal with filter\n const handleEditFilter = useCallback((filterId: string) => {\n const filterToEdit = dashboardFilters.find(df => df.id === filterId)\n if (filterToEdit) {\n setEditingFilter(filterToEdit)\n setShowFilterBuilder(true)\n }\n }, [dashboardFilters])\n\n // Handle removing a filter - update filter list and persist\n const handleRemoveFilter = useCallback((filterId: string) => {\n const updatedFilters = dashboardFilters.filter(df => df.id !== filterId)\n applyFilters(updatedFilters).catch(error => {\n console.error('Failed to save filters:', error)\n })\n\n // Close modal if we're deleting the filter being edited\n if (editingFilter?.id === filterId) {\n setEditingFilter(null)\n setShowFilterBuilder(false)\n }\n }, [dashboardFilters, editingFilter, applyFilters])\n\n // Handle save from modal - update or add filter and save\n const handleSaveFilter = useCallback(async (filterData: DashboardFilter) => {\n // Check if this is a new filter (not in current list) or an update\n const existingFilterIndex = dashboardFilters.findIndex(f => f.id === filterData.id)\n\n let updatedFilters: DashboardFilter[]\n if (existingFilterIndex >= 0) {\n // Update existing filter\n updatedFilters = dashboardFilters.map(f =>\n f.id === filterData.id ? filterData : f\n )\n } else {\n // Add new filter\n updatedFilters = [...dashboardFilters, filterData]\n }\n\n // Update dashboard state and persist\n try {\n await applyFilters(updatedFilters)\n } catch (error) {\n console.error('Failed to save filters:', error)\n throw error // Re-throw so modal can handle it\n }\n }, [dashboardFilters, applyFilters])\n\n // Handle modal close - just clean up state\n const handleCloseFilterBuilder = useCallback(() => {\n setEditingFilter(null)\n setShowFilterBuilder(false)\n }, [])\n\n // Hide filter panel completely when not editable (fully embedded mode without filter support)\n if (!editable) {\n return null\n }\n\n // Hide if no filters exist and not in edit mode (nothing to show)\n if (!isEditMode && dashboardFilters.length === 0) {\n return null\n }\n\n return (\n <div className=\"dc:mb-4\">\n {/* Edit Mode - Full filter management with chips and actions */}\n {isEditMode ? (\n <div\n className=\"dc:border dc:rounded-lg\"\n style={{\n borderColor: 'var(--dc-border)',\n backgroundColor: 'var(--dc-surface)',\n boxShadow: 'var(--dc-shadow-sm)'\n }}\n >\n <EditModeFilterList\n dashboardFilters={dashboardFilters}\n onAddFilter={handleAddFilter}\n onAddTimeFilter={handleAddTimeFilter}\n onEditFilter={handleEditFilter}\n onRemoveFilter={handleRemoveFilter}\n selectedFilterId={selectedFilterId}\n onFilterSelect={onFilterSelect}\n />\n </div>\n ) : (\n /* View Mode - Compact Mixpanel-style filter bar */\n <CompactFilterBar\n dashboardFilters={dashboardFilters}\n schema={schema}\n isEditMode={false}\n onDashboardFiltersChange={onDashboardFiltersChange}\n onAddFilter={handleAddFilter}\n onEditFilter={handleEditFilter}\n onRemoveFilter={handleRemoveFilter}\n />\n )}\n\n {/* Filter Edit Modal */}\n\n {showFilterBuilder && editingFilter && (\n <FilterEditModal\n filter={editingFilter}\n schema={schema}\n dashboardConfig={dashboardConfig}\n isOpen={showFilterBuilder}\n onSave={handleSaveFilter}\n onClose={handleCloseFilterBuilder}\n onDelete={() => handleRemoveFilter(editingFilter.id)}\n convertToMetaResponse={convertToMetaResponse}\n />\n )}\n </div>\n )\n}\n\nexport default DashboardFilterPanel\n","/**\n * DashboardFilterBar\n *\n * The dashboard-level filter UI (the internal DashboardFilterPanel) plus the\n * filter-selection-mode banner shown while assigning a filter to portlets.\n * Reads everything from DashboardContext.\n */\n\nimport { getIcon } from '../../icons/index.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport DashboardFilterPanel from '../DashboardFilterPanel.js'\nimport { useDashboardContext } from './DashboardContext.js'\nimport type { DashboardFilter } from '../../types.js'\n\nconst FilterIcon = getIcon('filter')\n\nexport default function DashboardFilterBar() {\n const { t } = useTranslation()\n const {\n dashboardFilters,\n editable,\n schema,\n config,\n onDashboardFiltersChange,\n onSave,\n selectedFilterId,\n selectedFilter,\n isEditMode,\n handleFilterSelect,\n handleSelectAllForFilter,\n actions,\n } = useDashboardContext()\n\n return (\n <>\n {/* Dashboard Filter Panel - Always visible below toolbar */}\n <DashboardFilterPanel\n dashboardFilters={dashboardFilters || []}\n editable={editable}\n schema={schema || null}\n dashboardConfig={config}\n onDashboardFiltersChange={onDashboardFiltersChange || (() => {})}\n onSaveFilters={onSave ? async (filters: DashboardFilter[]) => {\n const updatedConfig = {\n ...config,\n filters\n }\n await onSave(updatedConfig)\n } : undefined}\n selectedFilterId={selectedFilterId}\n onFilterSelect={handleFilterSelect}\n isEditMode={isEditMode}\n />\n\n {/* Filter Selection Mode Banner */}\n {selectedFilterId && selectedFilter && (\n <div\n className=\"dc:mb-4 dc:px-4 dc:py-3 dc:rounded-md dc:border-2 dc:transition-all\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n borderColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n <div className=\"dc:flex dc:items-center dc:justify-between dc:flex-wrap dc:gap-2\">\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:flex-wrap\">\n <FilterIcon className=\"dc:w-5 dc:h-5 dc:shrink-0\" />\n <span className=\"dc:font-medium\">\n {t('dashboard.filterSelectionMode', { filterLabel: selectedFilter.label })}\n </span>\n <span className=\"dc:text-sm dc:opacity-90 dc:hidden dc:sm:inline\">{t('dashboard.filterSelectionEscHint')}</span>\n </div>\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <button\n onClick={() => handleSelectAllForFilter(selectedFilterId)}\n className=\"dc:px-3 dc:py-1 dc:rounded-md dc:transition-colors dc:text-sm dc:font-medium\"\n style={{\n backgroundColor: 'rgba(255, 255, 255, 0.2)',\n color: 'white'\n }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.3)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.2)'}\n >\n {t('common.actions.selectAll')}\n </button>\n <button\n onClick={() => actions.exitFilterSelectionMode()}\n className=\"dc:px-3 dc:py-1 dc:rounded-md dc:transition-colors dc:text-sm dc:font-medium\"\n style={{\n backgroundColor: 'rgba(255, 255, 255, 0.2)',\n color: 'white'\n }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.3)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'rgba(255, 255, 255, 0.2)'}\n >\n {t('common.actions.exit')}\n </button>\n </div>\n </div>\n </div>\n )}\n </>\n )\n}\n","/**\n * MobileStackedLayout component\n * Simple vertical stack layout for mobile screens (<768px)\n * Read-only view with portlets sorted by grid position\n */\n\nimport { useMemo, useRef, useState, useCallback } from 'react'\nimport { getIcon } from '../icons/index.js'\nimport AnalyticsPortlet from './AnalyticsPortlet.js'\n\nconst RefreshIcon = getIcon('refresh')\nimport { ScrollContainerProvider } from '../providers/ScrollContainerContext.js'\nimport type { DashboardFilter, DashboardConfig } from '../types.js'\nimport type { ColorPalette } from '../utils/colorPalettes.js'\nimport { ensureAnalysisConfig } from '../utils/configMigration.js'\n\n/**\n * Finds the nearest scrollable ancestor of an element.\n */\nfunction findScrollableAncestor(element: HTMLElement | null): HTMLElement | null {\n if (!element) return null\n\n let current = element.parentElement\n\n while (current) {\n const style = window.getComputedStyle(current)\n const overflowY = style.overflowY\n const overflowX = style.overflowX\n\n const hasScrollableOverflow =\n overflowY === 'auto' || overflowY === 'scroll' ||\n overflowX === 'auto' || overflowX === 'scroll'\n\n const hasScrollContent =\n current.scrollHeight > current.clientHeight ||\n current.scrollWidth > current.clientWidth\n\n if (hasScrollableOverflow && hasScrollContent) {\n return current\n }\n\n if (current === document.body) break\n current = current.parentElement\n }\n\n return null\n}\n\ninterface MobileStackedLayoutProps {\n config: DashboardConfig\n colorPalette?: ColorPalette\n dashboardFilters?: DashboardFilter[]\n onPortletRefresh?: (portletId: string) => void\n}\n\n/**\n * Mobile-optimized stacked layout for dashboard portlets\n * Renders portlets in a single column, sorted by grid position\n */\nexport default function MobileStackedLayout({\n config,\n colorPalette,\n dashboardFilters,\n onPortletRefresh\n}: MobileStackedLayoutProps) {\n const portletComponentRefs = useRef<{ [key: string]: { refresh: () => void } | null }>({})\n\n // Scroll container detection for lazy loading\n const [scrollContainer, setScrollContainer] = useState<HTMLElement | null>(null)\n const containerRef = useRef<HTMLDivElement | null>(null)\n\n const setContainerRef = useCallback((node: HTMLDivElement | null) => {\n containerRef.current = node\n if (node) {\n setScrollContainer(findScrollableAncestor(node))\n }\n }, [])\n\n // Sort portlets by y position, then x position (top-to-bottom, left-to-right)\n const sortedPortlets = useMemo(() => {\n return [...config.portlets].sort((a, b) => {\n if (a.y !== b.y) return a.y - b.y\n return a.x - b.x\n })\n }, [config.portlets])\n\n const handlePortletRefresh = (portletId: string) => {\n // Refresh the specific portlet component\n portletComponentRefs.current[portletId]?.refresh()\n // Also call external handler if provided\n onPortletRefresh?.(portletId)\n }\n\n return (\n <ScrollContainerProvider value={scrollContainer}>\n <div ref={setContainerRef} className=\"mobile-stacked-layout dc:space-y-4 dc:px-2\">\n {sortedPortlets.map(portlet => {\n // Normalize portlet to ensure analysisConfig exists (on-the-fly migration)\n const normalizedPortlet = ensureAnalysisConfig(portlet)\n const { analysisConfig } = normalizedPortlet\n const chartModeConfig = analysisConfig.charts[analysisConfig.analysisType]\n const renderQuery = JSON.stringify(analysisConfig.query)\n const renderChartType = chartModeConfig?.chartType || 'line'\n const renderChartConfig = chartModeConfig?.chartConfig\n const renderDisplayConfig = chartModeConfig?.displayConfig\n\n // Markdown-specific display modes\n const isTransparent = renderChartType === 'markdown' && !!renderDisplayConfig?.transparentBackground\n const isAutoHeight = renderChartType === 'markdown' && (renderDisplayConfig?.autoHeight ?? true)\n const shouldHideHeader = renderChartType === 'markdown'\n ? (renderDisplayConfig?.hideHeader ?? true) || !portlet.title\n : (renderDisplayConfig?.hideHeader ?? false)\n\n // Calculate height: use stored h * rowHeight (80px), with minimum\n const portletHeight = Math.max(300, portlet.h * 80)\n // Header is approximately 40px when shown\n const headerHeight = shouldHideHeader ? 0 : 40\n // Content height = total - header - padding (py-3 = 24px)\n const contentHeight = portletHeight - headerHeight - 24\n\n return (\n <div\n key={portlet.id}\n data-portlet-id={portlet.id}\n className={isTransparent\n ? 'dc:flex dc:flex-col'\n : 'bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:flex dc:flex-col'\n }\n style={{\n height: isAutoHeight ? 'auto' : portletHeight,\n boxShadow: isTransparent ? 'none' : 'var(--dc-shadow-sm)',\n borderColor: isTransparent ? 'transparent' : undefined,\n borderWidth: isTransparent ? '0' : undefined,\n backgroundColor: isTransparent ? 'transparent' : undefined,\n }}\n >\n {/* Portlet Header - Simplified for mobile (no edit controls) */}\n {!shouldHideHeader && (\n <div className=\"dc:flex dc:items-center dc:justify-between dc:px-3 dc:py-2 dc:border-b border-dc-border dc:shrink-0 bg-dc-surface-secondary dc:rounded-t-lg\">\n <h3 className=\"dc:font-semibold dc:text-sm text-dc-text dc:truncate dc:flex-1\">\n {portlet.title}\n </h3>\n <div className=\"dc:flex dc:items-center dc:gap-1 dc:shrink-0 dc:ml-2\">\n <button\n onClick={() => handlePortletRefresh(portlet.id)}\n className=\"dc:p-1 bg-transparent dc:border-none dc:rounded-sm text-dc-text-secondary dc:cursor-pointer hover:bg-dc-surface-hover dc:transition-colors\"\n title=\"Refresh portlet data\"\n >\n <RefreshIcon style={{ width: '16px', height: '16px', color: 'currentColor' }} />\n </button>\n </div>\n </div>\n )}\n\n {/* Portlet Content - explicit height for charts to render */}\n <div\n className={`dc:overflow-visible dc:flex dc:flex-col${isTransparent ? '' : ' dc:px-2 dc:py-3'}`}\n style={{ height: isAutoHeight ? 'auto' : contentHeight }}\n >\n <AnalyticsPortlet\n ref={el => { portletComponentRefs.current[portlet.id] = el }}\n query={renderQuery}\n chartType={renderChartType}\n chartConfig={renderChartConfig}\n displayConfig={renderDisplayConfig}\n dashboardFilters={dashboardFilters}\n dashboardFilterMapping={portlet.dashboardFilterMapping}\n eagerLoad={portlet.eagerLoad ?? config.eagerLoad ?? false}\n title={portlet.title}\n height={isAutoHeight ? 'auto' : contentHeight}\n colorPalette={colorPalette}\n />\n </div>\n </div>\n )\n })}\n </div>\n </ScrollContainerProvider>\n )\n}\n","/**\n * ScaledGridWrapper component\n * Applies CSS transform scaling to the dashboard grid for intermediate screen sizes\n * Maintains the exact desktop layout appearance, just proportionally smaller\n */\n\nimport React, { useState, useEffect, useRef } from 'react'\n\ninterface ScaledGridWrapperProps {\n scaleFactor: number\n designWidth: number\n children: React.ReactNode\n}\n\n/**\n * Wrapper component that scales the grid using CSS transform\n * Handles height compensation to prevent overflow/whitespace issues\n */\nexport default function ScaledGridWrapper({\n scaleFactor,\n designWidth,\n children\n}: ScaledGridWrapperProps) {\n const [actualHeight, setActualHeight] = useState(0)\n const innerRef = useRef<HTMLDivElement>(null)\n\n // Measure actual grid height to calculate visible height\n useEffect(() => {\n if (!innerRef.current) return\n\n const observer = new ResizeObserver((entries) => {\n setActualHeight(entries[0]?.contentRect.height ?? 0)\n })\n\n observer.observe(innerRef.current)\n\n // Set initial height\n setActualHeight(innerRef.current.offsetHeight || 0)\n\n return () => observer.disconnect()\n }, [])\n\n // The scaled visual height\n const visualHeight = actualHeight * scaleFactor\n\n return (\n <div\n className=\"scaled-grid-container\"\n style={{\n height: visualHeight > 0 ? visualHeight : 'auto',\n overflow: 'hidden',\n width: '100%'\n }}\n >\n <div\n ref={innerRef}\n className=\"scaled-grid-inner\"\n style={{\n transform: `scale(${scaleFactor})`,\n transformOrigin: 'top left',\n width: designWidth\n }}\n >\n {children}\n </div>\n </div>\n )\n}\n","/**\n * DashboardGridSurface\n *\n * Renders the portlet layout (grid / row / scaled / mobile) inside the\n * thumbnail-capture wrapper, or the empty-state placeholder when there are no\n * portlets. Reads everything from DashboardContext.\n */\n\nimport { getIcon } from '../../icons/index.js'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport MobileStackedLayout from '../MobileStackedLayout.js'\nimport ScaledGridWrapper from '../ScaledGridWrapper.js'\nimport { TextIcon } from './dashboardGridUtils.js'\nimport { useDashboardContext } from './DashboardContext.js'\n\nconst ChartBarIcon = getIcon('measure')\nconst AddIcon = getIcon('add')\n\nexport default function DashboardGridSurface() {\n const { t } = useTranslation()\n const {\n config,\n gridContentRef,\n displayMode,\n scaleFactor,\n designWidth,\n colorPalette,\n dashboardFilters,\n handlePortletRefresh,\n renderActiveLayout,\n editable,\n handleAddText,\n handleAddPortlet,\n } = useDashboardContext()\n\n const isEmpty = !config.portlets || config.portlets.length === 0\n\n if (isEmpty) {\n return (\n <div className=\"dc:flex dc:justify-center dc:items-center dc:min-h-[50vh]\">\n <div className=\"dc:text-center\">\n <ChartBarIcon style={{ width: '64px', height: '64px', color: 'var(--dc-text-muted)', margin: '0 auto 16px auto' }} />\n <h3 className=\"dc:text-lg dc:font-semibold dc:mb-2 text-dc-text\">{t('dashboard.noPortlets')}</h3>\n <p className=\"dc:text-sm text-dc-text-secondary dc:mb-4\">{t('dashboard.noPortletsDescription')}</p>\n {editable && (\n <div className=\"dc:flex dc:items-center dc:gap-3\">\n <button\n onClick={handleAddText}\n className=\"dc:inline-flex dc:items-center dc:px-4 dc:py-2 dc:border border-dc-border bg-dc-surface dc:rounded-md focus:outline-hidden dc:focus:ring-2\"\n style={{\n color: 'var(--dc-text-secondary)',\n borderColor: 'var(--dc-border)'\n }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-surface)'}\n >\n <TextIcon className=\"dc:w-5 dc:h-5 dc:mr-2\" />\n {t('dashboard.addText')}\n </button>\n <button\n onClick={handleAddPortlet}\n className=\"dc:inline-flex dc:items-center dc:px-4 dc:py-2 dc:border border-dc-border bg-dc-surface dc:rounded-md focus:outline-hidden dc:focus:ring-2\"\n style={{\n color: 'var(--dc-primary)',\n borderColor: 'var(--dc-primary)'\n }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-surface-hover)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-surface)'}\n >\n <AddIcon className=\"dc:w-5 dc:h-5 dc:mr-2\" />\n {t('dashboard.addPortlet')}\n </button>\n </div>\n )}\n </div>\n </div>\n )\n }\n\n // Grid content ref wrapper for thumbnail capture (excludes toolbar/filters)\n return (\n <div ref={gridContentRef}>\n {displayMode === 'mobile' ? (\n <MobileStackedLayout\n config={config}\n colorPalette={colorPalette}\n dashboardFilters={dashboardFilters}\n onPortletRefresh={handlePortletRefresh}\n />\n ) : displayMode === 'scaled' ? (\n <ScaledGridWrapper scaleFactor={scaleFactor} designWidth={designWidth}>\n {renderActiveLayout()}\n </ScaledGridWrapper>\n ) : (\n renderActiveLayout()\n )}\n </div>\n )\n}\n","import React, { useEffect, useCallback } from 'react'\n\nexport interface ModalProps {\n isOpen: boolean\n onClose: () => void\n title?: string\n size?: 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'full' | 'fullscreen' | 'fullscreen-mobile'\n closeOnBackdropClick?: boolean\n closeOnEscape?: boolean\n showCloseButton?: boolean\n className?: string\n children: React.ReactNode\n footer?: React.ReactNode\n noPadding?: boolean\n}\n\nconst Modal: React.FC<ModalProps> = ({\n isOpen,\n onClose,\n title,\n size = 'md',\n closeOnBackdropClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n children,\n footer,\n noPadding = false\n}) => {\n // Handle ESC key press\n const handleEscapeKey = useCallback((event: KeyboardEvent) => {\n if (event.key === 'Escape' && closeOnEscape) {\n onClose()\n }\n }, [closeOnEscape, onClose])\n\n\n // Manage ESC key listener and body scroll\n useEffect(() => {\n if (isOpen) {\n // Add ESC key listener\n if (closeOnEscape) {\n document.addEventListener('keydown', handleEscapeKey)\n }\n\n // Prevent body scroll when modal is open\n document.body.style.overflow = 'hidden'\n } else {\n // Restore body scroll\n document.body.style.overflow = 'unset'\n }\n\n // Cleanup\n return () => {\n document.removeEventListener('keydown', handleEscapeKey)\n document.body.style.overflow = 'unset'\n }\n }, [isOpen, closeOnEscape, handleEscapeKey])\n\n\n if (!isOpen) return null\n\n const getSizeClasses = () => {\n switch (size) {\n case 'sm':\n return 'dc:max-w-md'\n case 'md':\n return 'dc:max-w-lg'\n case 'lg':\n return 'dc:max-w-2xl'\n case 'xl':\n return 'dc:max-w-6xl'\n case 'xxl':\n return 'dc:max-w-[1400px]' // Good for retina/mac displays\n case 'full':\n return 'dc:max-w-7xl'\n case 'fullscreen':\n return 'dc:w-[90vw] dc:h-[90vh] dc:max-w-none'\n case 'fullscreen-mobile':\n return 'dc:w-full dc:h-full dc:md:w-[min(90vw,1400px)] dc:md:h-[90vh]'\n default:\n return 'dc:max-w-lg'\n }\n }\n\n return (\n <div\n className={`dc:fixed dc:inset-0 dc:z-50 dc:backdrop-blur-md ${size === 'fullscreen-mobile' ? 'dc:flex dc:md:flex dc:md:items-center dc:md:justify-center' : 'dc:flex dc:items-center dc:justify-center'}`}\n style={{ backgroundColor: 'var(--dc-overlay)' }}\n onClick={closeOnBackdropClick ? onClose : undefined}\n >\n <div\n className={`dc:relative bg-dc-surface dc:border border-dc-border ${size === 'fullscreen-mobile' ? 'dc:rounded-none dc:md:rounded-lg' : 'dc:rounded-lg'} ${size === 'fullscreen' || size === 'fullscreen-mobile' ? '' : 'dc:mx-4'} ${getSizeClasses()} ${size === 'fullscreen' || size === 'fullscreen-mobile' ? '' : 'dc:max-h-[90vh]'} dc:flex dc:flex-col`}\n style={{ boxShadow: 'var(--dc-shadow-2xl)' }}\n onClick={(e) => e.stopPropagation()}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'modal-title' : undefined}\n >\n {/* Header */}\n {(title || showCloseButton) && (\n <div className=\"dc:flex dc:items-center dc:justify-between dc:px-6 dc:py-4 dc:border-b border-dc-border\">\n {title && (\n <h2 id=\"modal-title\" className=\"dc:text-xl dc:font-semibold text-dc-text\">\n {title}\n </h2>\n )}\n {showCloseButton && (\n <button\n type=\"button\"\n onClick={onClose}\n className=\"text-dc-text-muted hover:text-dc-text-secondary dc:transition-colors dc:p-2 dc:-mr-2\"\n aria-label=\"Close modal\"\n >\n <svg width=\"24\" height=\"24\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </div>\n )}\n\n {/* Content */}\n <div className={`dc:flex-1 dc:overflow-y-auto ${noPadding ? '' : 'dc:px-6 dc:py-4'}`}>\n {children}\n </div>\n\n {/* Footer */}\n {footer && (\n <div className=\"dc:flex dc:items-center dc:justify-end dc:space-x-3 dc:px-6 dc:py-4 dc:border-t border-dc-border bg-dc-surface-secondary\">\n {React.Children.toArray(footer)}\n </div>\n )}\n </div>\n </div>\n )\n}\n\nexport default Modal","import { forwardRef, lazy, Suspense } from 'react'\nimport type { ForwardRefExoticComponent, RefAttributes } from 'react'\nimport type { AnalysisBuilderProps, AnalysisBuilderRef } from './AnalysisBuilder/types.js'\nimport LoadingIndicator from './LoadingIndicator.js'\n\nconst LazyAnalysisBuilder = lazy(() => import('./AnalysisBuilder/index.js')) as ForwardRefExoticComponent<\n AnalysisBuilderProps & RefAttributes<AnalysisBuilderRef>\n>\n\nconst AnalysisBuilder = forwardRef<AnalysisBuilderRef, AnalysisBuilderProps>((props, ref) => (\n <Suspense\n fallback={\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:py-6\">\n <LoadingIndicator />\n </div>\n }\n >\n <LazyAnalysisBuilder {...props} ref={ref} />\n </Suspense>\n))\n\nAnalysisBuilder.displayName = 'AnalysisBuilder'\n\nexport default AnalysisBuilder\n","/**\n * 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 * Save-time validation helpers for PortletAnalysisModal, extracted to keep\n * the save handler flat.\n */\n\nimport type { CubeQuery } from '../../types.js'\n\n/** True if a (possibly multi/funnel/flow/retention) query has content worth saving. */\nexport function analysisConfigHasContent(query: any): boolean {\n // Check for ServerFlowQuery format { flow: {...} }\n if ('flow' in query && query.flow) {\n // Flow mode: check for required configuration\n return !!(\n query.flow.bindingKey &&\n query.flow.timeDimension &&\n query.flow.eventDimension &&\n query.flow.startingStep?.filter\n )\n }\n\n if ('retention' in query && query.retention) {\n // Retention mode: check for required configuration\n return !!(\n query.retention.bindingKey &&\n query.retention.timeDimension &&\n query.retention.dateRange?.start &&\n query.retention.dateRange?.end\n )\n }\n\n if ('funnel' in query && query.funnel) {\n // Funnel mode: check for steps\n return !!(query.funnel.steps && query.funnel.steps.length >= 2)\n }\n\n if ('queries' in query) {\n // Multi-query: check the first query\n return queryHasFields(query.queries[0])\n }\n\n // Single query: check directly (type narrowed to CubeQuery)\n return queryHasFields(query as CubeQuery)\n}\n\n/** True if a single cube query selects at least one measure/dimension/time dimension. */\nfunction queryHasFields(query: CubeQuery | undefined): boolean {\n return !!(\n (query?.measures && query.measures.length > 0) ||\n (query?.dimensions && query.dimensions.length > 0) ||\n (query?.timeDimensions && query.timeDimensions.length > 0)\n )\n}\n\n/** Alert message shown when a query of the given analysis type has no content. */\nexport function getEmptyContentMessage(analysisType: string): string {\n switch (analysisType) {\n case 'flow':\n return 'Please configure the flow analysis (binding key, time dimension, event dimension, and starting step filter).'\n case 'retention':\n return 'Please configure the retention analysis (binding key, time dimension, and date range).'\n case 'funnel':\n return 'Please add at least two funnel steps.'\n default:\n return 'Please add at least one metric or breakdown to your query.'\n }\n}\n","import { useState, useEffect, useRef, useCallback, useMemo } from 'react'\nimport Modal from './Modal.js'\nimport AnalysisBuilder from './AnalysisBuilderLazy.js'\nimport type { AnalysisBuilderRef, AnalysisBuilderInitialFunnelState, AnalysisBuilderInitialFlowState, AnalysisBuilderInitialRetentionState } from './AnalysisBuilder/types.js'\nimport type { PortletConfig, ColorPalette, CubeQuery, MultiQueryConfig, DashboardFilter, AnalysisType } from '../types.js'\nimport type { AnalysisConfig } from '../types/analysisConfig.js'\nimport { ensureAnalysisConfig } from '../utils/configMigration.js'\nimport { funnelModeAdapter } from '../adapters/funnelModeAdapter.js'\nimport { flowModeAdapter } from '../adapters/flowModeAdapter.js'\nimport { retentionModeAdapter } from '../adapters/retentionModeAdapter.js'\nimport { useTranslation } from '../hooks/useTranslation.js'\nimport { analysisConfigHasContent, getEmptyContentMessage } from './portletAnalysisModal/saveValidation.js'\n\ninterface PortletAnalysisModalProps {\n isOpen: boolean\n onClose: () => void\n onSave: (portlet: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>) => void\n portlet?: PortletConfig | null\n /** Initial data to display (avoids re-fetching when editing) */\n initialData?: any[]\n title: string\n submitText: string\n colorPalette?: ColorPalette\n /** @deprecated Dashboard filters are no longer merged into the editor - they are applied at execution time */\n dashboardFilters?: DashboardFilter[]\n}\n\n/**\n * PortletAnalysisModal - A modal wrapper around AnalysisBuilder for portlet editing\n *\n * This replaces PortletEditModal with the modern AnalysisBuilder interface.\n * Features:\n * - Two-panel layout with results and query builder\n * - Auto-execution of queries\n * - Smart chart defaults\n * - Title input in header\n * - Initial data support (no re-fetch when editing)\n */\nexport default function PortletAnalysisModal({\n isOpen,\n onClose,\n onSave,\n portlet,\n initialData,\n title: modalTitle,\n submitText,\n colorPalette\n}: PortletAnalysisModalProps) {\n const { t } = useTranslation()\n\n // Ref to AnalysisBuilder for getting current query and chart config\n const builderRef = useRef<AnalysisBuilderRef>(null)\n\n // Title state\n const [formTitle, setFormTitle] = useState('')\n\n // =========================================================================\n // Load from analysisConfig (migrating from legacy format if needed)\n // =========================================================================\n const derivedConfig = useMemo<AnalysisConfig | null>(() => {\n if (!portlet) return null\n\n // Use ensureAnalysisConfig which handles both new and legacy formats\n const normalizedPortlet = ensureAnalysisConfig(portlet)\n return normalizedPortlet.analysisConfig\n }, [portlet])\n\n // Parse initial query from derived config\n // Dashboard filters are NOT merged here - they are applied at execution time\n // by AnalyticsPortlet.tsx. The editor shows only the portlet's own filters\n // to prevent dashboard filters from leaking into saved portlet config.\n const initialQuery = useMemo<CubeQuery | MultiQueryConfig | undefined>(() => {\n if (!derivedConfig) return undefined\n\n // Get query from derived config\n const query = derivedConfig.query\n if (!query) return undefined\n\n // Handle funnel mode - return the ServerFunnelQuery as-is\n if (derivedConfig.analysisType === 'funnel') {\n return query as CubeQuery | MultiQueryConfig\n }\n\n // Return query as-is (single CubeQuery or MultiQueryConfig)\n return query as CubeQuery | MultiQueryConfig\n }, [derivedConfig])\n\n // Initial chart config from derived config\n const initialChartConfig = useMemo(() => {\n if (!derivedConfig) return undefined\n\n // Get chart config for current mode\n const modeCharts = derivedConfig.charts[derivedConfig.analysisType]\n if (!modeCharts) return undefined\n\n return {\n chartType: modeCharts.chartType,\n chartConfig: modeCharts.chartConfig,\n displayConfig: modeCharts.displayConfig\n }\n }, [derivedConfig])\n\n // Initial analysis type from derived config\n const initialAnalysisType: AnalysisType | undefined = derivedConfig?.analysisType\n\n // Initial funnel state from portlet (when analysisType === 'funnel')\n // Note: The funnel query data is in derivedConfig.query, but we need to pass\n // the store-level funnel state (funnelCube, funnelSteps, etc.) separately\n const initialFunnelState: AnalysisBuilderInitialFunnelState | undefined = useMemo(() => {\n if (derivedConfig?.analysisType !== 'funnel') return undefined\n\n // Option 1: Use legacy fields if present (backward compatibility)\n if (portlet?.funnelSteps && portlet.funnelSteps.length > 0) {\n return {\n funnelCube: portlet.funnelCube,\n funnelSteps: portlet.funnelSteps,\n funnelTimeDimension: portlet.funnelTimeDimension,\n funnelBindingKey: portlet.funnelBindingKey,\n funnelChartType: portlet.funnelChartType,\n funnelChartConfig: portlet.funnelChartConfig,\n funnelDisplayConfig: portlet.funnelDisplayConfig,\n }\n }\n\n // Option 2: Parse from analysisConfig.query (Phase 4 implementation)\n // This handles the case where only analysisConfig exists (no legacy fields)\n if (derivedConfig.query && 'funnel' in derivedConfig.query) {\n // Use adapter's conversion logic - already handles all formats\n const funnelState = funnelModeAdapter.load(derivedConfig)\n const chartConfig = derivedConfig.charts?.funnel\n\n return {\n ...funnelState,\n funnelChartType: chartConfig?.chartType,\n funnelChartConfig: chartConfig?.chartConfig,\n funnelDisplayConfig: chartConfig?.displayConfig,\n }\n }\n\n return undefined\n }, [derivedConfig, portlet])\n\n // Initial flow state from portlet (when analysisType === 'flow')\n const initialFlowState: AnalysisBuilderInitialFlowState | undefined = useMemo(() => {\n if (derivedConfig?.analysisType !== 'flow') return undefined\n\n // Parse from analysisConfig.query\n if (derivedConfig.query && 'flow' in derivedConfig.query) {\n // Use adapter's conversion logic\n const flowState = flowModeAdapter.load(derivedConfig)\n const chartConfig = derivedConfig.charts?.flow\n\n return {\n ...flowState,\n flowChartType: chartConfig?.chartType,\n flowChartConfig: chartConfig?.chartConfig,\n flowDisplayConfig: chartConfig?.displayConfig,\n }\n }\n\n return undefined\n }, [derivedConfig])\n\n // Initial retention state from portlet (when analysisType === 'retention')\n const initialRetentionState: AnalysisBuilderInitialRetentionState | undefined = useMemo(() => {\n if (derivedConfig?.analysisType !== 'retention') return undefined\n\n // Parse from analysisConfig.query\n if (derivedConfig.query && 'retention' in derivedConfig.query) {\n // Use adapter's conversion logic\n const retentionState = retentionModeAdapter.load(derivedConfig)\n const chartConfig = derivedConfig.charts?.retention\n\n return {\n ...retentionState,\n retentionChartType: chartConfig?.chartType,\n retentionChartConfig: chartConfig?.chartConfig,\n retentionDisplayConfig: chartConfig?.displayConfig,\n }\n }\n\n return undefined\n }, [derivedConfig])\n\n // Reset form state when modal opens/closes or portlet changes\n useEffect(() => {\n if (isOpen) {\n setFormTitle(portlet?.title || '')\n }\n }, [isOpen, portlet])\n\n // Handle save - Phase 4: Only save analysisConfig (no more dual-write)\n const handleSave = useCallback(() => {\n if (!formTitle.trim()) {\n alert('Please enter a title for the portlet.')\n return\n }\n\n // Get AnalysisConfig from store - this is the only format we save now\n const analysisConfig = builderRef.current?.getAnalysisConfig()\n\n if (!analysisConfig) {\n alert('Please configure a query before saving.')\n return\n }\n\n // Validate content based on analysis type\n const { query, analysisType } = analysisConfig\n\n if (!analysisConfigHasContent(query)) {\n alert(getEmptyContentMessage(analysisType))\n return\n }\n\n // Build portlet config with ONLY analysisConfig (no more legacy fields)\n const portletData: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'> = {\n ...(portlet || {}),\n title: formTitle.trim(),\n\n // === Canonical format - single source of truth ===\n analysisConfig,\n\n // Preserve dashboard filter mapping and eager load settings\n dashboardFilterMapping: portlet?.dashboardFilterMapping,\n eagerLoad: portlet?.eagerLoad,\n\n // Preserve existing position or use defaults for new portlets\n w: portlet?.w || 5,\n h: portlet?.h || 4\n } as PortletConfig\n\n onSave(portletData)\n onClose()\n }, [formTitle, portlet, onSave, onClose])\n\n // Handle cancel\n const handleCancel = useCallback(() => {\n onClose()\n }, [onClose])\n\n // Footer with save/cancel buttons\n const footer = (\n <>\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-text-secondary hover:text-dc-text bg-dc-surface dc:border border-dc-border dc:rounded-md hover:bg-dc-surface-hover dc:transition-colors\"\n >\n {t('common.actions.cancel')}\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-white bg-dc-accent hover:bg-dc-accent-hover dc:rounded-md dc:transition-colors\"\n >\n {submitText}\n </button>\n </>\n )\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={modalTitle}\n size=\"fullscreen-mobile\"\n showCloseButton={true}\n closeOnBackdropClick={false}\n closeOnEscape={true}\n noPadding={true}\n footer={footer}\n >\n {/* Custom content with title input */}\n <div className=\"dc:flex dc:flex-col dc:h-full\">\n {/* Title input section */}\n <div className=\"dc:shrink-0 dc:px-4 dc:py-3 dc:border-b border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:gap-3\">\n <label htmlFor=\"portlet-title\" className=\"dc:text-sm dc:font-medium text-dc-text-secondary dc:shrink-0\">\n {t('common.labels.title')}\n </label>\n <input\n id=\"portlet-title\"\n type=\"text\"\n value={formTitle}\n onChange={(e) => setFormTitle(e.target.value)}\n placeholder=\"Enter portlet title...\"\n autoComplete=\"off\"\n className=\"dc:flex-1 dc:px-3 dc:py-1.5 dc:text-sm bg-dc-surface dc:border border-dc-border dc:rounded-md text-dc-text placeholder-dc-text-muted dc:focus:outline-none dc:focus:ring-2 focus:ring-dc-accent focus:border-transparent\"\n autoFocus\n />\n </div>\n </div>\n\n {/* AnalysisBuilder content */}\n <div className=\"dc:flex-1 dc:min-h-0\">\n <AnalysisBuilder\n ref={builderRef}\n maxHeight=\"100%\"\n initialQuery={initialQuery}\n initialChartConfig={initialChartConfig}\n initialAnalysisType={initialAnalysisType}\n initialFunnelState={initialFunnelState}\n initialFlowState={initialFlowState}\n initialRetentionState={initialRetentionState}\n initialData={initialData}\n colorPalette={colorPalette}\n disableLocalStorage={true}\n className=\"dc:h-full\"\n />\n </div>\n </div>\n </Modal>\n )\n}\n","/**\n * 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","/**\n * TextPortletModal - Simplified modal for creating/editing markdown portlets\n *\n * Layout: large content textarea at top, display options and live preview below.\n * No query configuration needed — markdown is content-only.\n */\n\nimport { useState, useMemo, useCallback } from 'react'\nimport { getIcon } from '../icons/index.js'\nimport AnalysisDisplayConfigPanel from './AnalysisBuilder/AnalysisDisplayConfigPanel.js'\nimport MarkdownChart from './charts/MarkdownChart.js'\nimport { ensureAnalysisConfig } from '../utils/configMigration.js'\nimport type { PortletConfig, ChartDisplayConfig } from '../types.js'\nimport type { AnalysisConfig } from '../types/analysisConfig.js'\nimport type { ColorPalette } from '../utils/colorPalettes.js'\nimport { useTranslation } from '../hooks/useTranslation.js'\n\nconst CloseIcon = getIcon('close')\n\ninterface TextPortletModalProps {\n isOpen: boolean\n onClose: () => void\n onSave: (portlet: PortletConfig | Omit<PortletConfig, 'id' | 'x' | 'y'>) => void\n portlet?: PortletConfig | null\n colorPalette?: ColorPalette\n existingTitles?: string[]\n}\n\nexport default function TextPortletModal({\n isOpen,\n onClose,\n onSave,\n portlet,\n colorPalette,\n existingTitles = [],\n}: TextPortletModalProps) {\n const { t } = useTranslation()\n // Initialize displayConfig from existing portlet or defaults\n const initialDisplayConfig = useMemo(() => {\n if (portlet) {\n const normalized = ensureAnalysisConfig(portlet)\n const chartConfig = normalized.analysisConfig.charts.query\n return chartConfig?.displayConfig ?? {}\n }\n return {\n content: '',\n hideHeader: true,\n autoHeight: true,\n fontSize: 'medium' as const,\n alignment: 'left' as const,\n accentColorIndex: 0,\n transparentBackground: false,\n accentBorder: 'none' as const,\n }\n }, [portlet])\n\n const [displayConfig, setDisplayConfig] = useState<ChartDisplayConfig>(initialDisplayConfig)\n const [title, setTitle] = useState(() => portlet?.title ?? '')\n const [titleTouched, setTitleTouched] = useState(false)\n\n // Reset state when modal opens with a different portlet\n const [prevPortlet, setPrevPortlet] = useState(portlet)\n if (portlet !== prevPortlet) {\n setPrevPortlet(portlet)\n setTitleTouched(false)\n if (portlet) {\n const normalized = ensureAnalysisConfig(portlet)\n const chartConfig = normalized.analysisConfig.charts.query\n setDisplayConfig(chartConfig?.displayConfig ?? {})\n setTitle(portlet.title ?? '')\n } else {\n setDisplayConfig({\n content: '',\n hideHeader: true,\n autoHeight: true,\n fontSize: 'medium',\n alignment: 'left',\n accentColorIndex: 0,\n transparentBackground: false,\n accentBorder: 'none',\n })\n setTitle('')\n }\n }\n\n const handleTitleChange = useCallback((value: string) => {\n setTitle(value)\n setTitleTouched(true)\n }, [])\n\n // Resolve title: respect user edits (including clearing). Auto-generate only for new portlets.\n const resolvedTitle = useMemo(() => {\n if (titleTouched) return title\n if (portlet?.title) return portlet.title\n // Auto-generate for new portlets\n let candidate = 'Text'\n let counter = 2\n while (existingTitles.includes(candidate)) {\n candidate = `Text ${counter}`\n counter++\n }\n return candidate\n }, [title, titleTouched, portlet, existingTitles])\n\n const handleDisplayConfigChange = useCallback((config: ChartDisplayConfig) => {\n setDisplayConfig(config)\n }, [])\n\n const handleSave = useCallback(() => {\n // Keep markdown sizing policy explicit and preserve header setting independently.\n const finalDisplayConfig: ChartDisplayConfig = {\n ...displayConfig,\n autoHeight: displayConfig.autoHeight ?? true,\n hideHeader: displayConfig.hideHeader ?? true,\n }\n if (!resolvedTitle.trim()) {\n finalDisplayConfig.hideHeader = true\n }\n\n const analysisConfig: AnalysisConfig = {\n version: 1,\n analysisType: 'query',\n activeView: 'chart',\n charts: {\n query: {\n chartType: 'markdown',\n chartConfig: {},\n displayConfig: finalDisplayConfig,\n },\n },\n query: {},\n }\n\n if (portlet) {\n // Editing existing\n onSave({\n ...portlet,\n title: resolvedTitle,\n analysisConfig,\n })\n } else {\n // Adding new\n onSave({\n title: resolvedTitle,\n analysisConfig,\n w: 12,\n h: 3,\n })\n }\n }, [displayConfig, portlet, resolvedTitle, onSave])\n\n const handleContentChange = useCallback((content: string) => {\n setDisplayConfig(prev => ({ ...prev, content }))\n }, [])\n\n if (!isOpen) return null\n\n return (\n <div\n className=\"dc:fixed dc:inset-0 dc:z-50 dc:flex dc:items-center dc:justify-center\"\n onClick={onClose}\n >\n {/* Backdrop */}\n <div className=\"dc:absolute dc:inset-0 dc:bg-black/50\" />\n\n {/* Modal */}\n <div\n className=\"dc:relative dc:w-full dc:max-w-5xl dc:mx-4 dc:max-h-[85vh] dc:flex dc:flex-col bg-dc-surface dc:rounded-lg dc:border border-dc-border\"\n style={{ boxShadow: 'var(--dc-shadow-xl)' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div className=\"dc:flex dc:items-center dc:justify-between dc:px-6 dc:py-4 dc:border-b border-dc-border dc:shrink-0\">\n <h2 className=\"dc:text-lg dc:font-semibold text-dc-text\">\n {portlet ? t('textPortlet.editText') : t('textPortlet.addText')}\n </h2>\n <button\n onClick={onClose}\n className=\"dc:p-1 dc:rounded-md text-dc-text-secondary dc:hover:bg-dc-surface-hover dc:transition-colors\"\n >\n <CloseIcon className=\"dc:w-5 dc:h-5\" />\n </button>\n </div>\n\n {/* Content area — two columns: left (textarea + preview), right (display options) */}\n <div className=\"dc:flex-1 dc:min-h-0 dc:overflow-y-auto dc:p-6\">\n <div className=\"dc:flex dc:gap-6 dc:h-full\">\n {/* Left column: title + textarea + preview */}\n <div className=\"dc:flex-1 dc:min-w-0 dc:flex dc:flex-col dc:gap-4\">\n {/* Optional title — visible when not transparent */}\n {!displayConfig.transparentBackground && (\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text dc:mb-1.5\">\n {t('common.labels.title')}\n </label>\n <input\n type=\"text\"\n value={title}\n onChange={(e) => handleTitleChange(e.target.value)}\n placeholder=\"Text\"\n className=\"dc:w-full dc:rounded-md dc:border border-dc-border bg-dc-surface dc:px-3 dc:py-2 dc:text-sm text-dc-text focus:outline-hidden dc:focus:ring-2 focus:ring-dc-accent\"\n />\n </div>\n )}\n\n {/* Content textarea */}\n <div>\n <label className=\"dc:block dc:text-sm dc:font-medium text-dc-text dc:mb-1.5\">\n {t('textPortlet.markdownContent')}\n </label>\n <textarea\n value={displayConfig.content || ''}\n onChange={(e) => handleContentChange(e.target.value)}\n placeholder={'# Welcome\\n\\nAdd your **markdown** content here:\\n\\n- Lists with bullets\\n- [Links](https://example.com)\\n- *Italic* and **bold** text\\n\\n---\\n\\nUse --- for horizontal rules.'}\n className=\"dc:w-full dc:rounded-md dc:border border-dc-border bg-dc-surface dc:px-3 dc:py-2 dc:text-sm text-dc-text dc:font-mono dc:resize-y focus:outline-hidden dc:focus:ring-2 focus:ring-dc-accent\"\n style={{ minHeight: '140px' }}\n rows={7}\n />\n <p className=\"dc:mt-1 dc:text-xs text-dc-text-muted\">\n {t('textPortlet.markdownHint')}\n </p>\n </div>\n\n {/* Live preview */}\n <div className=\"dc:flex-1 dc:min-h-0\">\n <div className=\"dc:text-xs dc:font-medium dc:uppercase dc:tracking-wider text-dc-text-muted dc:mb-2\">\n {t('textPortlet.preview')}\n </div>\n <div\n className=\"dc:overflow-hidden\"\n style={{ minHeight: '200px' }}\n >\n <MarkdownChart\n data={[]}\n displayConfig={displayConfig}\n colorPalette={colorPalette}\n height=\"auto\"\n />\n </div>\n </div>\n </div>\n\n {/* Right column: display options */}\n <div className=\"dc:w-72 dc:shrink-0\">\n <AnalysisDisplayConfigPanel\n chartType=\"markdown\"\n displayConfig={displayConfig}\n colorPalette={colorPalette}\n onDisplayConfigChange={handleDisplayConfigChange}\n excludeKeys={['content', 'hideHeader']}\n />\n </div>\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"dc:flex dc:items-center dc:justify-end dc:gap-3 dc:px-6 dc:py-4 dc:border-t border-dc-border dc:shrink-0\">\n <button\n onClick={onClose}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md dc:border border-dc-border text-dc-text-secondary dc:hover:bg-dc-surface-hover dc:transition-colors\"\n >\n {t('common.actions.cancel')}\n </button>\n <button\n onClick={handleSave}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md dc:text-white dc:transition-colors\"\n style={{ backgroundColor: 'var(--dc-primary)' }}\n >\n {portlet ? 'Update' : 'Save'}\n </button>\n </div>\n </div>\n </div>\n )\n}\n","/**\n * Join reachability utilities for dashboard filter field mapping\n *\n * Used to determine which fields a dashboard filter can be remapped to for a\n * given portlet: only dimensions of cubes that are join-reachable from the\n * cubes the portlet already queries are guaranteed to execute (the server\n * auto-joins cubes referenced only in filters).\n */\n\nimport type { CubeMeta, CubeMetaDimension, PortletConfig } from '../types.js'\nimport { ensureAnalysisConfig } from './configMigration.js'\n\n/**\n * Compute the set of cubes reachable from the given start cubes via join\n * relationships. Relationships are treated as undirected, mirroring the\n * server's bidirectional join resolution. Start cubes are included.\n */\nexport function getReachableCubes(\n meta: CubeMeta | null,\n startCubes: string[]\n): Set<string> {\n const reachable = new Set<string>(startCubes)\n if (!meta?.cubes || startCubes.length === 0) {\n return reachable\n }\n\n // Build an undirected adjacency map from all declared relationships\n const adjacency = new Map<string, Set<string>>()\n const addEdge = (a: string, b: string) => {\n if (!adjacency.has(a)) adjacency.set(a, new Set())\n if (!adjacency.has(b)) adjacency.set(b, new Set())\n adjacency.get(a)!.add(b)\n adjacency.get(b)!.add(a)\n }\n meta.cubes.forEach(cube => {\n cube.relationships?.forEach(rel => addEdge(cube.name, rel.targetCube))\n })\n\n // BFS from the start cubes\n const queue = [...startCubes]\n while (queue.length > 0) {\n const current = queue.shift()!\n const neighbours = adjacency.get(current)\n if (!neighbours) continue\n neighbours.forEach(neighbour => {\n if (!reachable.has(neighbour)) {\n reachable.add(neighbour)\n queue.push(neighbour)\n }\n })\n }\n\n return reachable\n}\n\n/**\n * Extract the cube name from a fully-qualified field name (\"Cube.field\")\n */\nfunction cubeNameOf(field: string): string | null {\n const dotIndex = field.indexOf('.')\n return dotIndex > 0 ? field.substring(0, dotIndex) : null\n}\n\n/**\n * Get the cube names referenced by a portlet's query (measures, dimensions,\n * timeDimensions) across single-query, multi-query, and funnel shapes.\n */\nexport function getPortletQueryCubes(portlet: PortletConfig): string[] {\n const cubes = new Set<string>()\n const addField = (field: unknown) => {\n if (typeof field === 'string') {\n const cube = cubeNameOf(field)\n if (cube) cubes.add(cube)\n }\n }\n\n const extractFromCubeQuery = (cubeQuery: any) => {\n if (Array.isArray(cubeQuery?.measures)) cubeQuery.measures.forEach(addField)\n if (Array.isArray(cubeQuery?.dimensions)) cubeQuery.dimensions.forEach(addField)\n if (Array.isArray(cubeQuery?.timeDimensions)) {\n cubeQuery.timeDimensions.forEach((td: any) => addField(td?.dimension))\n }\n }\n\n try {\n const normalizedPortlet = ensureAnalysisConfig(portlet)\n const query = normalizedPortlet.analysisConfig.query as any\n\n if (!query) return []\n\n if ('funnel' in query) {\n // ServerFunnelQuery: cubes come from steps, binding key, and time dimension\n const funnel = query.funnel\n if (typeof funnel?.timeDimension === 'string') {\n addField(funnel.timeDimension)\n } else if (Array.isArray(funnel?.timeDimension)) {\n funnel.timeDimension.forEach((td: any) => td?.cube && cubes.add(td.cube))\n }\n if (typeof funnel?.bindingKey === 'string') {\n addField(funnel.bindingKey)\n } else if (Array.isArray(funnel?.bindingKey)) {\n funnel.bindingKey.forEach((bk: any) => bk?.cube && cubes.add(bk.cube))\n }\n if (Array.isArray(funnel?.steps)) {\n funnel.steps.forEach((step: any) => step?.cube && cubes.add(step.cube))\n }\n } else if ('queries' in query && Array.isArray(query.queries)) {\n // MultiQueryConfig\n query.queries.forEach(extractFromCubeQuery)\n } else {\n // Single CubeQuery\n extractFromCubeQuery(query)\n }\n } catch (e) {\n console.warn('Failed to extract cubes from portlet:', portlet.id, e)\n }\n\n return [...cubes]\n}\n\nexport interface ReachableDimensionGroup {\n cubeName: string\n cubeTitle: string\n dimensions: CubeMetaDimension[]\n}\n\n/**\n * Get the dimensions a dashboard filter can be remapped to for a portlet:\n * dimensions of join-reachable cubes, grouped by cube. When `sameTypeAs` is a\n * known dimension, options are restricted to dimensions of the same type\n * (operator and values are shared across portlets, so the target field must\n * be type-compatible).\n */\nexport function getReachableDimensionOptions(\n meta: CubeMeta | null,\n portlet: PortletConfig,\n options?: { sameTypeAs?: string }\n): ReachableDimensionGroup[] {\n if (!meta?.cubes) return []\n\n const startCubes = getPortletQueryCubes(portlet)\n if (startCubes.length === 0) return []\n\n const reachable = getReachableCubes(meta, startCubes)\n\n // Resolve the type of the reference dimension, if provided and known\n let requiredType: string | undefined\n if (options?.sameTypeAs) {\n for (const cube of meta.cubes) {\n const match = cube.dimensions?.find(d => d.name === options.sameTypeAs)\n if (match) {\n requiredType = match.type\n break\n }\n }\n }\n\n return meta.cubes\n .filter(cube => reachable.has(cube.name))\n .map(cube => ({\n cubeName: cube.name,\n cubeTitle: cube.title || cube.name,\n dimensions: (cube.dimensions || []).filter(\n d => !requiredType || d.type === requiredType\n )\n }))\n .filter(group => group.dimensions.length > 0)\n}\n","/**\n * Portlet Filter Configuration Modal\n * Allows users to configure which dashboard filters apply to a specific portlet,\n * and optionally remap a filter to a different (join-reachable) field for this portlet\n */\n\nimport { useState, useEffect, useMemo } from 'react'\nimport type {\n DashboardFilter,\n DashboardFilterMapping,\n DashboardFilterMappingEntry,\n CubeMeta,\n PortletConfig\n} from '../types.js'\nimport { normalizeFilterMapping, serializeFilterMapping } from '../utils/filterUtils.js'\nimport { getReachableDimensionOptions } from '../utils/joinReachability.js'\nimport { useTranslation } from '../hooks/useTranslation.js'\n\ninterface PortletFilterConfigModalProps {\n isOpen: boolean\n onClose: () => void\n dashboardFilters: DashboardFilter[]\n currentMapping: DashboardFilterMapping\n onSave: (mapping: DashboardFilterMapping) => void\n portletTitle: string\n schema?: CubeMeta | null\n portlet?: PortletConfig | null\n}\n\nexport default function PortletFilterConfigModal({\n isOpen,\n onClose,\n dashboardFilters = [],\n currentMapping = [],\n onSave,\n portletTitle,\n schema = null,\n portlet = null\n}: PortletFilterConfigModalProps) {\n const { t } = useTranslation()\n const [selectedEntries, setSelectedEntries] = useState<DashboardFilterMappingEntry[]>(\n () => normalizeFilterMapping(currentMapping)\n )\n\n // Update local state when props change. Bail out (return the previous\n // state) when the content is unchanged: the prop may be a fresh array\n // identity on every render, and always setting fresh state would loop.\n useEffect(() => {\n setSelectedEntries(prev => {\n const next = normalizeFilterMapping(currentMapping)\n return JSON.stringify(next) === JSON.stringify(prev) ? prev : next\n })\n }, [currentMapping, isOpen])\n\n const handleToggleFilter = (filterId: string) => {\n setSelectedEntries(prev => {\n if (prev.some(e => e.filterId === filterId)) {\n return prev.filter(e => e.filterId !== filterId)\n } else {\n return [...prev, { filterId }]\n }\n })\n }\n\n const handleMemberChange = (filterId: string, member: string) => {\n setSelectedEntries(prev =>\n prev.map(e =>\n e.filterId === filterId\n ? (member ? { filterId, member } : { filterId })\n : e\n )\n )\n }\n\n const handleSave = () => {\n onSave(serializeFilterMapping(selectedEntries))\n onClose()\n }\n\n const handleCancel = () => {\n setSelectedEntries(normalizeFilterMapping(currentMapping)) // Reset to original\n onClose()\n }\n\n // Reachable, type-compatible field options per simple filter member.\n // Keyed by the filter's own member so filters sharing a member share options.\n const fieldOptionsByMember = useMemo(() => {\n const result = new Map<string, ReturnType<typeof getReachableDimensionOptions>>()\n if (!schema || !portlet) return result\n\n dashboardFilters.forEach(df => {\n if (df.isUniversalTime) return\n if (!('member' in df.filter) || !df.filter.member) return\n if (result.has(df.filter.member)) return\n result.set(\n df.filter.member,\n getReachableDimensionOptions(schema, portlet, { sameTypeAs: df.filter.member })\n )\n })\n return result\n }, [schema, portlet, dashboardFilters])\n\n // Format filter preview text\n const formatFilterPreview = (filter: DashboardFilter): string => {\n if (!filter.filter) return ''\n\n // Handle simple filters\n if ('member' in filter.filter && filter.filter.member) {\n const values = filter.filter.values || []\n const valuesText = values.length > 0 ? values.join(', ') : t('portlet.filterConfig.noValue')\n return `${filter.filter.member} ${filter.filter.operator} ${valuesText}`\n }\n\n // Handle group filters (AND/OR)\n if ('type' in filter.filter && filter.filter.type) {\n const filterCount = filter.filter.filters?.length || 0\n return filterCount === 1\n ? t('portlet.filterConfig.groupFilter', { type: filter.filter.type.toUpperCase(), count: filterCount })\n : t('portlet.filterConfig.groupFilterPlural', { type: filter.filter.type.toUpperCase(), count: filterCount })\n }\n\n return t('portlet.filterConfig.complexFilter')\n }\n\n if (!isOpen) return null\n\n return (\n <div className=\"dc:fixed dc:inset-0 dc:z-50 dc:flex dc:items-center dc:justify-center bg-black bg-opacity-50\" onClick={handleCancel}>\n <div\n className=\"bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:max-w-2xl dc:w-full dc:mx-4 dc:max-h-[80vh] dc:flex dc:flex-col\"\n style={{ boxShadow: 'var(--dc-shadow-lg)' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div className=\"dc:px-6 dc:py-4 dc:border-b border-dc-border bg-dc-surface-secondary dc:rounded-t-lg\">\n <h2 className=\"dc:text-lg dc:font-semibold text-dc-text\">{t('portlet.filterConfig.title')}</h2>\n <p className=\"dc:text-sm text-dc-text-secondary dc:mt-1\">\n {t('portlet.filterConfig.subtitle', { portletTitle })}\n </p>\n </div>\n\n {/* Content */}\n <div className=\"dc:flex-1 dc:overflow-y-auto dc:px-6 dc:py-4\">\n {dashboardFilters.length === 0 ? (\n <div className=\"dc:text-center dc:py-8 text-dc-text-muted\">\n <svg\n className=\"dc:mx-auto dc:h-12 dc:w-12 dc:mb-3\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z\"\n />\n </svg>\n <p className=\"dc:text-sm dc:font-medium\">{t('portlet.filterConfig.noFilters')}</p>\n <p className=\"dc:text-xs dc:mt-1\">{t('portlet.filterConfig.noFiltersHint')}</p>\n </div>\n ) : (\n <div className=\"dc:space-y-3\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:mb-4 dc:pb-2 dc:border-b border-dc-border\">\n <span className=\"dc:text-sm dc:font-medium text-dc-text\">{t('portlet.filterConfig.availableFilters')}</span>\n <span className=\"dc:text-xs text-dc-text-secondary\">\n {t('portlet.filterConfig.selectedCount', {\n // Count only entries for filters that still exist - stale\n // mapping entries (deleted filters) aren't shown in the list\n selected: selectedEntries.filter(e => dashboardFilters.some(df => df.id === e.filterId)).length,\n total: dashboardFilters.length\n })}\n </span>\n </div>\n\n {dashboardFilters.map(filter => {\n const entry = selectedEntries.find(e => e.filterId === filter.id)\n const isSelected = !!entry\n const isSimpleFilter = !!filter.filter && 'member' in filter.filter && !!filter.filter.member\n const canRemap = isSelected && isSimpleFilter && !filter.isUniversalTime\n const fieldOptions = canRemap\n ? fieldOptionsByMember.get((filter.filter as { member: string }).member) || []\n : []\n // A saved override pointing at a field that's no longer offered\n // (schema changed) is kept selected, with a warning\n const overrideIsStale = !!entry?.member &&\n !fieldOptions.some(group => group.dimensions.some(d => d.name === entry.member))\n\n return (\n <label\n key={filter.id}\n className={`dc:flex dc:items-start dc:p-3 dc:rounded-md dc:border dc:cursor-pointer dc:transition-colors ${\n isSelected\n ? 'border-dc-primary bg-dc-surface-secondary'\n : 'border-dc-border hover:bg-dc-surface-hover'\n }`}\n >\n <input\n type=\"checkbox\"\n checked={isSelected}\n onChange={() => handleToggleFilter(filter.id)}\n className=\"dc:mt-0.5 dc:mr-3 dc:h-4 dc:w-4 dc:rounded border-dc-border dc:focus:ring-2 focus:ring-dc-primary\"\n style={{\n accentColor: 'var(--dc-primary)'\n }}\n />\n <div className=\"dc:flex-1 dc:min-w-0\">\n <div className=\"dc:flex dc:items-center dc:gap-2\">\n <span className=\"dc:font-medium dc:text-sm text-dc-text dc:truncate\">\n {filter.label}\n </span>\n {isSelected && (\n <span\n className=\"dc:px-2 dc:py-0.5 dc:text-xs dc:rounded-full\"\n style={{\n backgroundColor: 'var(--dc-primary)',\n color: 'white'\n }}\n >\n {t('portlet.filterConfig.applied')}\n </span>\n )}\n {entry?.member && (\n <span\n className=\"dc:px-2 dc:py-0.5 dc:text-xs dc:rounded-full bg-dc-accent-bg text-dc-accent dc:truncate\"\n title={entry.member}\n >\n {t('portlet.filterConfig.mappedTo', { field: entry.member })}\n </span>\n )}\n </div>\n <div className=\"dc:mt-1 dc:text-xs text-dc-text-secondary dc:break-words\">\n {formatFilterPreview(filter)}\n </div>\n {canRemap && fieldOptions.length > 0 && (\n <div className=\"dc:mt-2\" onClick={(e) => e.preventDefault()}>\n <label className=\"dc:block dc:text-xs dc:font-medium text-dc-text-secondary dc:mb-1\">\n {t('portlet.filterConfig.applyToField')}\n </label>\n <select\n value={entry?.member || ''}\n onChange={(e) => handleMemberChange(filter.id, e.target.value)}\n className=\"dc:w-full dc:text-sm dc:rounded-md dc:border border-dc-border bg-dc-surface text-dc-text dc:px-2 dc:py-1.5 dc:focus:ring-2 focus:ring-dc-primary\"\n >\n <option value=\"\">\n {t('portlet.filterConfig.applyToFieldDefault', { field: (filter.filter as { member: string }).member })}\n </option>\n {overrideIsStale && entry?.member && (\n <option value={entry.member}>{entry.member}</option>\n )}\n {fieldOptions.map(group => (\n <optgroup key={group.cubeName} label={group.cubeTitle}>\n {group.dimensions.map(dimension => (\n <option key={dimension.name} value={dimension.name}>\n {dimension.title || dimension.name}\n </option>\n ))}\n </optgroup>\n ))}\n </select>\n {overrideIsStale && (\n <p className=\"dc:mt-1 dc:text-xs text-dc-warning\">\n {t('portlet.filterConfig.mappedFieldMissing', { field: entry?.member || '' })}\n </p>\n )}\n </div>\n )}\n </div>\n </label>\n )\n })}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"dc:px-6 dc:py-4 dc:border-t border-dc-border bg-dc-surface-secondary dc:rounded-b-lg dc:flex dc:justify-end dc:gap-3\">\n <button\n onClick={handleCancel}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md dc:border border-dc-border bg-dc-surface hover:bg-dc-surface-hover dc:transition-colors text-dc-text\"\n >\n {t('common.actions.cancel')}\n </button>\n <button\n onClick={handleSave}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium dc:rounded-md text-white dc:transition-colors\"\n style={{\n backgroundColor: 'var(--dc-primary)'\n }}\n >\n {t('portlet.filterConfig.applyFilters')}\n </button>\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","/**\n * DashboardModals\n *\n * The four dashboard modals (add/edit portlet, add/edit text, portlet filter config,\n * delete confirmation). All read their open/target state from DashboardContext; when\n * closed they render nothing, so always mounting them is harmless.\n */\n\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport PortletAnalysisModal from '../PortletAnalysisModal.js'\nimport TextPortletModal from '../TextPortletModal.js'\nimport PortletFilterConfigModal from '../PortletFilterConfigModal.js'\nimport ConfirmModal from '../ConfirmModal.js'\nimport { useDashboardContext } from './DashboardContext.js'\n\nexport default function DashboardModals() {\n const { t } = useTranslation()\n const {\n config,\n colorPalette,\n dashboardFilters,\n schema,\n isPortletModalOpen,\n editingPortlet,\n isTextModalOpen,\n editingTextPortlet,\n isFilterConfigModalOpen,\n filterConfigPortlet,\n deleteConfirmPortletId,\n handlePortletSave,\n handleSaveFilterConfig,\n actions,\n } = useDashboardContext()\n\n const deletePortletTitle =\n config.portlets.find(p => p.id === deleteConfirmPortletId)?.title || t('dashboard.thisPortlet')\n\n return (\n <>\n {/* Portlet Modal */}\n <PortletAnalysisModal\n isOpen={isPortletModalOpen}\n onClose={actions.closePortletModal}\n onSave={handlePortletSave}\n portlet={editingPortlet}\n title={editingPortlet ? t('dashboard.editPortlet') : t('dashboard.addNewPortlet')}\n submitText={editingPortlet ? t('dashboard.updatePortlet') : t('dashboard.addPortlet')}\n colorPalette={colorPalette}\n dashboardFilters={dashboardFilters}\n />\n\n {/* Text Portlet Modal */}\n <TextPortletModal\n isOpen={isTextModalOpen}\n onClose={actions.closeTextModal}\n onSave={handlePortletSave}\n portlet={editingTextPortlet}\n colorPalette={colorPalette}\n existingTitles={config.portlets.map(p => p.title)}\n />\n\n {/* Filter Configuration Modal */}\n <PortletFilterConfigModal\n isOpen={isFilterConfigModalOpen}\n onClose={actions.closeFilterConfig}\n dashboardFilters={dashboardFilters || []}\n currentMapping={filterConfigPortlet?.dashboardFilterMapping || []}\n onSave={handleSaveFilterConfig}\n portletTitle={filterConfigPortlet?.title || ''}\n schema={schema || null}\n portlet={filterConfigPortlet}\n />\n\n {/* Delete Confirmation Modal */}\n <ConfirmModal\n isOpen={!!deleteConfirmPortletId}\n onClose={actions.closeDeleteConfirm}\n onConfirm={actions.confirmDelete}\n title={t('dashboard.deletePortlet')}\n message={\n <>\n {t('dashboard.deletePortletConfirm')}{' '}\n <strong>{deletePortletTitle}</strong>\n {t('dashboard.deletePortletSuffix')}\n </>\n }\n confirmText={t('common.actions.delete')}\n confirmVariant=\"danger\"\n />\n </>\n )\n}\n","/**\n * Dashboard Grid Component\n *\n * Back-compat composition of the composable dashboard pieces. The state machine and\n * all layout/scroll/drag machinery live in DashboardProvider/DashboardCoordinator;\n * this component simply wires the four pieces together to preserve the original\n * `<DashboardGrid config={...} />` API.\n *\n * For full control over the toolbar/layout, compose the pieces yourself:\n * <DashboardProvider config={config} editable onSave={onSave}>\n * <DashboardToolbar /> // or your own toolbar via useDashboardContext()\n * <DashboardFilterBar />\n * <DashboardGridSurface />\n * <DashboardModals />\n * </DashboardProvider>\n */\n\nimport type { ReactNode } from 'react'\nimport DashboardProvider from './dashboard/DashboardProvider.js'\nimport DashboardToolbar from './dashboard/DashboardToolbar.js'\nimport DashboardFilterBar from './dashboard/DashboardFilterBar.js'\nimport DashboardGridSurface from './dashboard/DashboardGridSurface.js'\nimport DashboardModals from './dashboard/DashboardModals.js'\nimport type { ColorPalette } from '../utils/colorPalettes.js'\nimport type {\n DashboardConfig,\n DashboardFilter,\n CubeMeta,\n DashboardLayoutMode\n} from '../types.js'\n\ninterface DashboardGridProps {\n config: DashboardConfig\n editable?: boolean\n dashboardFilters?: DashboardFilter[] // Dashboard-level filters to apply to portlets\n loadingComponent?: ReactNode // Custom loading indicator for all portlets\n onConfigChange?: (config: DashboardConfig) => void\n onPortletRefresh?: (portletId: string) => void\n onSave?: (config: DashboardConfig) => Promise<void> | void\n /** Callback to save thumbnail separately - called on edit mode exit when thumbnail feature is enabled */\n onSaveThumbnail?: (thumbnailData: string) => Promise<string | void>\n colorPalette?: ColorPalette // Complete palette with both colors and gradient\n schema?: CubeMeta | null // Cube metadata for filter panel\n onDashboardFiltersChange?: (filters: DashboardFilter[]) => void // Handler for filter changes\n dashboardModes?: DashboardLayoutMode[]\n /** When true, no toolbar is rendered (neither the top edit bar nor the floating toolbar) */\n hideToolbar?: boolean\n}\n\nexport default function DashboardGrid(props: DashboardGridProps) {\n const isEmpty = !props.config.portlets || props.config.portlets.length === 0\n\n return (\n <DashboardProvider {...props}>\n {/* Match the original: when empty, only the placeholder + modals render */}\n {!isEmpty && !props.hideToolbar && <DashboardToolbar />}\n {!isEmpty && <DashboardFilterBar />}\n <DashboardGridSurface />\n <DashboardModals />\n </DashboardProvider>\n )\n}\n","/**\n * Analytics Dashboard Component\n * Main dashboard container that uses CubeProvider context\n * Minimal dependencies, designed to be embedded in existing apps\n */\n\nimport { useCallback, useMemo } from 'react'\nimport DashboardGrid from './DashboardGrid.js'\nimport { useCubeFeatures, useCubeMeta } from '../providers/CubeProvider.js'\nimport { getColorPalette } from '../utils/colorPalettes.js'\nimport { useDirtyStateTracking } from '../hooks/useDirtyStateTracking.js'\nimport type { AnalyticsDashboardProps, DashboardConfig, DashboardFilter } from '../types.js'\n\nexport default function AnalyticsDashboard({\n config,\n editable = false,\n dashboardFilters: propDashboardFilters,\n loadingComponent,\n onConfigChange,\n onSave,\n onSaveThumbnail,\n onDirtyStateChange\n}: AnalyticsDashboardProps) {\n // Get cube metadata for filter building\n const { meta } = useCubeMeta()\n const { dashboardModes } = useCubeFeatures()\n\n // Track dirty state and wrap save/change handlers\n const {\n handleConfigChange: handleConfigChangeWithDirtyTracking,\n handleSave: handleSaveWithDirtyTracking,\n } = useDirtyStateTracking<DashboardConfig>({\n initialConfig: config,\n onConfigChange,\n onSave,\n onDirtyStateChange,\n })\n\n // Merge programmatic filters (props) with config filters by ID\n // Config provides structure/metadata, props provide value overrides\n const mergedDashboardFilters = useMemo<DashboardFilter[]>(() => {\n const configFilters = config.filters || []\n const propFilters = propDashboardFilters || []\n\n // If no prop filters, use config filters as-is\n if (propFilters.length === 0) {\n return configFilters\n }\n\n // If no config filters, use prop filters as-is\n if (configFilters.length === 0) {\n return propFilters\n }\n\n // Merge by ID: config filters provide structure, prop filters provide values\n const mergedFilters: DashboardFilter[] = configFilters.map(configFilter => {\n // Find matching prop filter by ID\n const propFilter = propFilters.find(pf => pf.id === configFilter.id)\n\n if (propFilter) {\n // Merge: preserve config metadata, use prop filter values\n return {\n ...configFilter, // Preserve id, label, isUniversalTime from config\n filter: propFilter.filter // Use filter values from prop override\n }\n }\n\n // No override for this filter, use config as-is\n return configFilter\n })\n\n // Add any prop filters that don't exist in config (new filters)\n const configIds = new Set(configFilters.map(cf => cf.id))\n const newFilters = propFilters.filter(pf => !configIds.has(pf.id))\n\n return [...mergedFilters, ...newFilters]\n }, [config.filters, propDashboardFilters])\n\n // Handle dashboard filter changes\n const handleDashboardFiltersChange = useCallback((filters: DashboardFilter[]) => {\n // Only update config if we're not using programmatic filters\n if (!propDashboardFilters || propDashboardFilters.length === 0) {\n const updatedConfig = {\n ...config,\n filters\n }\n handleConfigChangeWithDirtyTracking(updatedConfig)\n } else {\n console.warn('Dashboard filters are controlled via props - config changes ignored')\n }\n }, [config, propDashboardFilters, handleConfigChangeWithDirtyTracking])\n\n // Resolve complete palette object based on config\n const colorPalette = useMemo(() => {\n const paletteName = config.colorPalette\n return getColorPalette(paletteName)\n }, [config.colorPalette])\n\n return (\n <div className=\"dc:w-full\">\n {/* Dashboard Grid (owns its store via DashboardProvider, includes filter panel) */}\n <DashboardGrid\n config={config}\n editable={editable}\n dashboardFilters={mergedDashboardFilters}\n loadingComponent={loadingComponent}\n onConfigChange={handleConfigChangeWithDirtyTracking}\n onSave={handleSaveWithDirtyTracking}\n onSaveThumbnail={onSaveThumbnail}\n colorPalette={colorPalette}\n schema={meta}\n dashboardModes={dashboardModes}\n onDashboardFiltersChange={handleDashboardFiltersChange}\n />\n </div>\n )\n}\n","/**\n * Portlet Container Component\n * Simple wrapper for individual portlets\n */\n\nimport { useState, useCallback, useMemo } from 'react'\nimport AnalyticsPortlet from './AnalyticsPortlet.js'\nimport DebugModal from './DebugModal.js'\nimport type { PortletConfig } from '../types.js'\nimport type { FlowChartData } from '../types/flow.js'\nimport type { RetentionChartData } from '../types/retention.js'\nimport { ensureAnalysisConfig } from '../utils/configMigration.js'\n\ninterface PortletContainerProps {\n portlet: PortletConfig\n editable?: boolean\n onEdit?: (portlet: PortletConfig) => void\n onDelete?: (portletId: string) => void\n onRefresh?: (portletId: string) => void\n}\n\nexport default function PortletContainer({ \n portlet, \n editable = false,\n onEdit,\n onDelete,\n onRefresh\n}: PortletContainerProps) {\n // Normalize portlet to ensure analysisConfig exists (on-the-fly migration)\n const normalizedPortlet = useMemo(() => ensureAnalysisConfig(portlet), [portlet])\n const { analysisConfig } = normalizedPortlet\n\n // Extract rendering props from analysisConfig\n const chartModeConfig = analysisConfig.charts[analysisConfig.analysisType]\n const renderQuery = useMemo(() => JSON.stringify(analysisConfig.query), [analysisConfig.query])\n const renderChartType = chartModeConfig?.chartType || 'line'\n const renderChartConfig = chartModeConfig?.chartConfig\n const renderDisplayConfig = chartModeConfig?.displayConfig\n\n const [debugData, setDebugData] = useState<{\n chartConfig: any\n displayConfig: any\n queryObject: any\n data: any[] | FlowChartData | RetentionChartData\n chartType: string\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n } | null>(null)\n\n // Memoize debug data callback to prevent AnalyticsPortlet re-renders\n const handleDebugDataReady = useCallback((data: {\n chartConfig: any\n displayConfig: any\n queryObject: any\n data: any[] | FlowChartData | RetentionChartData\n chartType: string\n cacheInfo?: { hit: true; cachedAt: string; ttlMs: number; ttlRemainingMs: number } | null\n }) => {\n setDebugData(data)\n }, [])\n\n return (\n <div className=\"bg-dc-surface dc:border border-dc-border dc:rounded-lg dc:flex dc:flex-col dc:h-full\" style={{ boxShadow: 'var(--dc-shadow-sm)' }}>\n {/* Header */}\n <div className=\"dc:flex dc:items-center dc:justify-between dc:border-b border-dc-border dc:shrink-0 bg-dc-surface-secondary dc:rounded-t-lg dc:px-3 dc:py-2 dc:md:px-6 dc:md:py-3\">\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:flex-1 dc:min-w-0\">\n <h3 className=\"dc:font-semibold dc:text-sm dc:truncate text-dc-text\">{portlet.title}</h3>\n {/* Debug button - right next to title */}\n {debugData && (\n <DebugModal\n chartConfig={debugData.chartConfig}\n displayConfig={debugData.displayConfig}\n queryObject={debugData.queryObject}\n data={debugData.data}\n chartType={debugData.chartType}\n cacheInfo={debugData.cacheInfo}\n />\n )}\n </div>\n\n <div className=\"dc:flex dc:items-center dc:gap-2 dc:ml-4\">\n\n {editable && (\n <>\n <button\n onClick={() => onRefresh?.(portlet.id)}\n className=\"dc:p-1.5 hover:bg-dc-surface-hover dc:rounded-sm text-dc-text-secondary\"\n title=\"Refresh\"\n >\n <svg className=\"dc:h-4 dc:w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n </button>\n <button\n onClick={() => onEdit?.(portlet)}\n className=\"dc:p-1.5 hover:bg-dc-surface-hover dc:rounded-sm text-dc-text-secondary\"\n title=\"Edit\"\n >\n <svg className=\"dc:h-4 dc:w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\" />\n </svg>\n </button>\n <button\n onClick={() => onDelete?.(portlet.id)}\n className=\"dc:p-1.5 dc:rounded-sm text-dc-danger\"\n style={{ backgroundColor: 'transparent' }}\n onMouseEnter={(e) => e.currentTarget.style.backgroundColor = 'var(--dc-danger-bg)'}\n onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'transparent'}\n title=\"Delete\"\n >\n <svg className=\"dc:h-4 dc:w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\" />\n </svg>\n </button>\n </>\n )}\n </div>\n </div>\n\n {/* Content */}\n <div className=\"dc:px-2 dc:py-3 dc:md:px-4 dc:md:pt-6 dc:md:pb-4 dc:flex-1 dc:min-h-0\">\n <AnalyticsPortlet\n query={renderQuery}\n chartType={renderChartType}\n chartConfig={renderChartConfig}\n displayConfig={renderDisplayConfig}\n title={portlet.title}\n height=\"100%\"\n onDebugDataReady={handleDebugDataReady}\n />\n </div>\n </div>\n )\n}","import React, { useState, useEffect } from 'react'\nimport Modal from './Modal.js'\nimport { useTranslation } from '../hooks/useTranslation.js'\n\ninterface DashboardEditModalProps {\n isOpen: boolean\n onClose: () => void\n onSave: (data: { name: string; description?: string }) => Promise<void> | void\n title: string\n submitText: string\n initialName?: string\n initialDescription?: string\n}\n\nexport default function DashboardEditModal({\n isOpen,\n onClose,\n onSave,\n title,\n submitText,\n initialName = '',\n initialDescription = ''\n}: DashboardEditModalProps) {\n const { t } = useTranslation()\n const [name, setName] = useState('')\n const [description, setDescription] = useState('')\n const [isSaving, setIsSaving] = useState(false)\n\n // Initialize form values when modal opens\n useEffect(() => {\n if (isOpen) {\n setName(initialName)\n setDescription(initialDescription)\n }\n }, [isOpen, initialName, initialDescription])\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n \n if (!name.trim()) {\n return\n }\n\n setIsSaving(true)\n \n try {\n await onSave({\n name: name.trim(),\n description: description.trim() || undefined\n })\n handleClose()\n } catch {\n // Failed to save dashboard\n // Don't close modal on error so user can retry\n } finally {\n setIsSaving(false)\n }\n }\n\n const handleClose = () => {\n setName('')\n setDescription('')\n setIsSaving(false)\n onClose()\n }\n\n const footer = (\n <>\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={isSaving}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-dc-text-secondary bg-dc-surface dc:border border-dc-border dc:rounded-md hover:bg-dc-surface-hover dc:disabled:opacity-50\"\n >\n {t('common.actions.cancel')}\n </button>\n <button\n type=\"submit\"\n form=\"dashboard-form\"\n disabled={isSaving || !name.trim()}\n className=\"dc:px-4 dc:py-2 dc:text-sm dc:font-medium text-white bg-dc-primary dc:border border-transparent dc:rounded-md hover:bg-dc-primary-hover dc:disabled:opacity-50 dc:disabled:cursor-not-allowed\"\n >\n {isSaving ? 'Saving...' : submitText}\n </button>\n </>\n )\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={handleClose}\n title={title}\n size=\"fullscreen-mobile\"\n footer={footer}\n >\n <form id=\"dashboard-form\" onSubmit={handleSubmit} className=\"dc:space-y-4 dc:w-full\">\n <div>\n <label htmlFor=\"dashboard-name\" className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-1\">\n {t('dashboard.editModal.dashboardName')}\n </label>\n <input\n type=\"text\"\n id=\"dashboard-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:border border-dc-border dc:rounded-md bg-dc-surface text-dc-text dc:focus:outline-none dc:focus:ring-2 focus:ring-dc-primary focus:border-dc-primary\"\n placeholder=\"Enter dashboard name...\"\n required\n autoFocus\n />\n </div>\n\n <div>\n <label htmlFor=\"dashboard-description\" className=\"dc:block dc:text-sm dc:font-medium text-dc-text-secondary dc:mb-1\">\n {t('dashboard.editModal.descriptionOptional')}\n </label>\n <textarea\n id=\"dashboard-description\"\n rows={3}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n className=\"dc:w-full dc:px-3 dc:py-2 dc:border border-dc-border dc:rounded-md bg-dc-surface text-dc-text dc:focus:outline-none dc:focus:ring-2 focus:ring-dc-primary focus:border-dc-primary\"\n placeholder=\"Enter description...\"\n />\n </div>\n </form>\n </Modal>\n )\n}\n"],"mappings":"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CA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CAEA,OACE,kBAAC,GAAiB,UAAlB;EAAkC;YAChC,kBAAC,GAAD;GAAyB,OAAO;aAC9B,kBAAC,OAAD;IACE,KAAK;IACL,WAAU;IACV,OAAO;KAAE,UAAU;KAAQ,UAAU;IAAS;IAE7C;GACE,CAAA;EACkB,CAAA;CACA,CAAA;AAE/B;;;ACz7BA,SAAwB,GAAkB,EAAE,aAAU,GAAG,KAAiC;CACxF,OACE,kBAAC,IAAD,EAAA,UACE,kBAAC,IAAD;EAAsB,GAAI;EAAQ;CAA+B,CAAA,EAC3C,CAAA;AAE5B;;;ACZA,IAAa,KAAiC;CAC5C;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;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;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;AACF;AAKA,SAAgB,GAAgB,GAAoC;CAMlE,OALK,KAIW,GAAe,MAAK,MAAK,EAAE,SAAS,CAC7C,KAJE,GAAe;AAK1B;;;AC/qBA,IAAM,KAAW,EAAQ,MAAM,GACzB,KAAY,EAAQ,OAAO,GAC3B,KAAW,EAAQ,SAAS,GAC5B,KAAW,EAAQ,OAAO,GAC1B,KAAU,EAAQ,KAAK;AAC7B,SAAS,GAAS,EAAE,gBAAqC;CACvD,OACE,kBAAC,OAAD;EAAgB;EAAW,SAAQ;EAAY,MAAK;EAAe,OAAM;YAAzE,CACE,kBAAC,QAAD;GAAM,GAAE;GAAI,GAAE;GAAK,UAAS;GAAK,YAAW;GAAM,YAAW;aAAQ;EAAO,CAAA,GAC5E,kBAAC,QAAD;GAAM,GAAE;GAAK,GAAE;GAAK,UAA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"}