drizzle-cube 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/express/index.cjs +1 -1
- package/dist/adapters/express/index.js +4 -4
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/fastify/index.js +4 -4
- package/dist/adapters/{handler-CNn3q29F.cjs → handler-C0nUppAK.cjs} +3 -3
- package/dist/adapters/{handler-_TKfigrZ.js → handler-Odsi9_Rd.js} +125 -1
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/hono/index.js +4 -4
- package/dist/adapters/{locale-Dl_3R6hP.cjs → locale-Dv6bl_eU.cjs} +2 -2
- package/dist/adapters/{locale-BQQrZYhz.js → locale-Dy3LcTwN.js} +4 -2
- package/dist/adapters/mcp-tools.cjs +1 -1
- package/dist/adapters/mcp-tools.js +8 -3
- package/dist/adapters/mcp-transport-BCtjU0lC.cjs +40 -0
- package/dist/adapters/mcp-transport-DW_Uks-O.js +579 -0
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.js +4 -4
- package/dist/adapters/{utils-DG8ti3FT.js → utils-Bd5mzZfk.js} +44 -18
- package/dist/adapters/utils-DklqMBHn.cjs +128 -0
- package/dist/adapters/utils.cjs +1 -1
- package/dist/adapters/utils.d.ts +6 -1
- package/dist/adapters/utils.js +1 -1
- package/dist/client/charts/chartConfigs.d.ts +34 -0
- package/dist/client/charts.js +12 -12
- package/dist/client/chunks/{DashboardEditModal-BBcB0E2g.js → DashboardEditModal-BiJwVv0b.js} +11 -11
- package/dist/client/chunks/DashboardEditModal-BiJwVv0b.js.map +1 -0
- package/dist/client/chunks/{FieldSearchModal-CisOov-_.js → FieldSearchModal-DdcbCwAi.js} +21 -5
- package/dist/client/chunks/{FieldSearchModal-CisOov-_.js.map → FieldSearchModal-DdcbCwAi.js.map} +1 -1
- package/dist/client/chunks/{RetentionCombinedChart-DiyZwiPv.js → RetentionCombinedChart-CivIny9P.js} +3 -3
- package/dist/client/chunks/{RetentionCombinedChart-DiyZwiPv.js.map → RetentionCombinedChart-CivIny9P.js.map} +1 -1
- package/dist/client/chunks/{RetentionHeatmap-usGF7BCo.js → RetentionHeatmap-BHCgwZmB.js} +2 -2
- package/dist/client/chunks/{RetentionHeatmap-usGF7BCo.js.map → RetentionHeatmap-BHCgwZmB.js.map} +1 -1
- package/dist/client/chunks/{af-ZA-xDmO5F0s.js → af-ZA-BdL6DOWy.js} +10 -3
- package/dist/client/chunks/af-ZA-BdL6DOWy.js.map +1 -0
- package/dist/client/chunks/{analysis-builder-0o1W-k3K.js → analysis-builder-MoGvbMRe.js} +245 -255
- package/dist/client/chunks/analysis-builder-MoGvbMRe.js.map +1 -0
- package/dist/client/chunks/{analysis-builder-shared-Cz4KAlIC.js → analysis-builder-shared-DmyRh2O3.js} +391 -459
- package/dist/client/chunks/analysis-builder-shared-DmyRh2O3.js.map +1 -0
- package/dist/client/chunks/{chart-activity-grid-VFFm85hC.js → chart-activity-grid-Bdb8U_NC.js} +14 -7
- package/dist/client/chunks/chart-activity-grid-Bdb8U_NC.js.map +1 -0
- package/dist/client/chunks/{chart-area-CwwIHTmK.js → chart-area-BZEnT-tf.js} +39 -38
- package/dist/client/chunks/chart-area-BZEnT-tf.js.map +1 -0
- package/dist/client/chunks/{chart-bar-Bmny922L.js → chart-bar-CTmdv_v0.js} +53 -52
- package/dist/client/chunks/chart-bar-CTmdv_v0.js.map +1 -0
- package/dist/client/chunks/{chart-box-plot-DM7GwtCV.js → chart-box-plot-CYObdFtp.js} +3 -3
- package/dist/client/chunks/{chart-box-plot-DM7GwtCV.js.map → chart-box-plot-CYObdFtp.js.map} +1 -1
- package/dist/client/chunks/{chart-bubble-DJOq4IpT.js → chart-bubble-CjFprySz.js} +3 -3
- package/dist/client/chunks/{chart-bubble-DJOq4IpT.js.map → chart-bubble-CjFprySz.js.map} +1 -1
- package/dist/client/chunks/{chart-candlestick-C2nzVCv1.js → chart-candlestick-D2HoM3B5.js} +3 -3
- package/dist/client/chunks/{chart-candlestick-C2nzVCv1.js.map → chart-candlestick-D2HoM3B5.js.map} +1 -1
- package/dist/client/chunks/{chart-config-activity-grid-C-EkgYoa.js → chart-config-activity-grid-Bom99j9m.js} +9 -2
- package/dist/client/chunks/chart-config-activity-grid-Bom99j9m.js.map +1 -0
- package/dist/client/chunks/{chart-config-area-CMZpbIah.js → chart-config-area-DtYTKZxS.js} +10 -2
- package/dist/client/chunks/chart-config-area-DtYTKZxS.js.map +1 -0
- package/dist/client/chunks/{chart-config-bar-B8_V4YLg.js → chart-config-bar-7v5JVY2y.js} +10 -2
- package/dist/client/chunks/chart-config-bar-7v5JVY2y.js.map +1 -0
- package/dist/client/chunks/{chart-config-box-plot-Dwj7sEbU.js → chart-config-box-plot-BHGv-wqu.js} +9 -2
- package/dist/client/chunks/chart-config-box-plot-BHGv-wqu.js.map +1 -0
- package/dist/client/chunks/{chart-config-bubble-B0w0ZVp4.js → chart-config-bubble-BkHm-mfu.js} +9 -2
- package/dist/client/chunks/chart-config-bubble-BkHm-mfu.js.map +1 -0
- package/dist/client/chunks/{chart-config-candlestick-Bvo3zeIn.js → chart-config-candlestick-D1aaHvTe.js} +9 -2
- package/dist/client/chunks/chart-config-candlestick-D1aaHvTe.js.map +1 -0
- package/dist/client/chunks/{chart-config-data-table-BQXSn4b_.js → chart-config-data-table-B_Hw8w2J.js} +2 -2
- package/dist/client/chunks/{chart-config-data-table-BQXSn4b_.js.map → chart-config-data-table-B_Hw8w2J.js.map} +1 -1
- package/dist/client/chunks/{chart-config-funnel-BzEsHmjR.js → chart-config-funnel-DL8PSGuL.js} +2 -2
- package/dist/client/chunks/{chart-config-funnel-BzEsHmjR.js.map → chart-config-funnel-DL8PSGuL.js.map} +1 -1
- package/dist/client/chunks/{chart-config-gauge-C5ZiyZy7.js → chart-config-gauge-BVLxuA3f.js} +6 -2
- package/dist/client/chunks/chart-config-gauge-BVLxuA3f.js.map +1 -0
- package/dist/client/chunks/{chart-config-heat-map-Cv8qNnVP.js → chart-config-heat-map-CjudGdui.js} +9 -2
- package/dist/client/chunks/chart-config-heat-map-CjudGdui.js.map +1 -0
- package/dist/client/chunks/{chart-config-kpi-delta-BraHQc2E.js → chart-config-kpi-delta-Bf47hGqD.js} +9 -2
- package/dist/client/chunks/chart-config-kpi-delta-Bf47hGqD.js.map +1 -0
- package/dist/client/chunks/{chart-config-kpi-number-CeCkx7mC.js → chart-config-kpi-number-QTQRNgOi.js} +6 -2
- package/dist/client/chunks/chart-config-kpi-number-QTQRNgOi.js.map +1 -0
- package/dist/client/chunks/{chart-config-kpi-text-CImM3SvH.js → chart-config-kpi-text-BRze2eyh.js} +6 -2
- package/dist/client/chunks/chart-config-kpi-text-BRze2eyh.js.map +1 -0
- package/dist/client/chunks/{chart-config-line-BVKapAQK.js → chart-config-line-BFeCqmKH.js} +10 -2
- package/dist/client/chunks/chart-config-line-BFeCqmKH.js.map +1 -0
- package/dist/client/chunks/{chart-config-markdown-C-_g_8te.js → chart-config-markdown-EWYckwXv.js} +2 -2
- package/dist/client/chunks/{chart-config-markdown-C-_g_8te.js.map → chart-config-markdown-EWYckwXv.js.map} +1 -1
- package/dist/client/chunks/{chart-config-measure-profile-KTVV1gO3.js → chart-config-measure-profile-B9nIhqKR.js} +6 -2
- package/dist/client/chunks/chart-config-measure-profile-B9nIhqKR.js.map +1 -0
- package/dist/client/chunks/{chart-config-pie-BZxVl25X.js → chart-config-pie-6LHtEyMM.js} +9 -2
- package/dist/client/chunks/chart-config-pie-6LHtEyMM.js.map +1 -0
- package/dist/client/chunks/{chart-config-radar-B7FByX3t.js → chart-config-radar-BuLS6Inn.js} +9 -2
- package/dist/client/chunks/chart-config-radar-BuLS6Inn.js.map +1 -0
- package/dist/client/chunks/{chart-config-radial-bar-UfW_3yyX.js → chart-config-radial-bar-BP0eMohx.js} +9 -2
- package/dist/client/chunks/chart-config-radial-bar-BP0eMohx.js.map +1 -0
- package/dist/client/chunks/{chart-config-sankey-DGAThN9i.js → chart-config-sankey-CT8oGIGP.js} +2 -2
- package/dist/client/chunks/{chart-config-sankey-DGAThN9i.js.map → chart-config-sankey-CT8oGIGP.js.map} +1 -1
- package/dist/client/chunks/{chart-config-scatter-BVVJuOnt.js → chart-config-scatter-DOSpN07Z.js} +9 -2
- package/dist/client/chunks/chart-config-scatter-DOSpN07Z.js.map +1 -0
- package/dist/client/chunks/{chart-config-sunburst-utejM2YS.js → chart-config-sunburst-BSUX_YoB.js} +2 -2
- package/dist/client/chunks/{chart-config-sunburst-utejM2YS.js.map → chart-config-sunburst-BSUX_YoB.js.map} +1 -1
- package/dist/client/chunks/{chart-config-tree-map-IHp97OyV.js → chart-config-tree-map-D8GuAgVB.js} +9 -2
- package/dist/client/chunks/chart-config-tree-map-D8GuAgVB.js.map +1 -0
- package/dist/client/chunks/{chart-config-waterfall-BdqG1V-x.js → chart-config-waterfall-BDi7BoJP.js} +9 -2
- package/dist/client/chunks/chart-config-waterfall-BDi7BoJP.js.map +1 -0
- package/dist/client/chunks/{chart-data-table-zZtwLf55.js → chart-data-table-DT4uBZaq.js} +92 -90
- package/dist/client/chunks/{chart-data-table-zZtwLf55.js.map → chart-data-table-DT4uBZaq.js.map} +1 -1
- package/dist/client/chunks/{chart-funnel-COTJy8BP.js → chart-funnel-B9crR3b3.js} +3 -3
- package/dist/client/chunks/{chart-funnel-COTJy8BP.js.map → chart-funnel-B9crR3b3.js.map} +1 -1
- package/dist/client/chunks/{chart-gauge-C8lIneI0.js → chart-gauge-DAMFsicz.js} +3 -3
- package/dist/client/chunks/{chart-gauge-C8lIneI0.js.map → chart-gauge-DAMFsicz.js.map} +1 -1
- package/dist/client/chunks/{chart-heat-map-BJXt3RMt.js → chart-heat-map-BxgM_X1G.js} +3 -3
- package/dist/client/chunks/{chart-heat-map-BJXt3RMt.js.map → chart-heat-map-BxgM_X1G.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-delta-DHkNqufb.js → chart-kpi-delta-DJKL02Ut.js} +75 -73
- package/dist/client/chunks/{chart-kpi-delta-DHkNqufb.js.map → chart-kpi-delta-DJKL02Ut.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-number-BrXw7m-S.js → chart-kpi-number-CuNEYbRx.js} +66 -65
- package/dist/client/chunks/{chart-kpi-number-BrXw7m-S.js.map → chart-kpi-number-CuNEYbRx.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-text-D0plngLV.js → chart-kpi-text-u8FJaZDQ.js} +27 -26
- package/dist/client/chunks/{chart-kpi-text-D0plngLV.js.map → chart-kpi-text-u8FJaZDQ.js.map} +1 -1
- package/dist/client/chunks/{chart-line-DKvW32U-.js → chart-line-BZhzOjRl.js} +119 -118
- package/dist/client/chunks/chart-line-BZhzOjRl.js.map +1 -0
- package/dist/client/chunks/{chart-markdown-CJU2hUq3.js → chart-markdown-DEtjn8gx.js} +70 -69
- package/dist/client/chunks/chart-markdown-DEtjn8gx.js.map +1 -0
- package/dist/client/chunks/{chart-measure-profile-DNT_tbh4.js → chart-measure-profile-BnpIOS4Q.js} +4 -4
- package/dist/client/chunks/{chart-measure-profile-DNT_tbh4.js.map → chart-measure-profile-BnpIOS4Q.js.map} +1 -1
- package/dist/client/chunks/{chart-pie-CzYnncO-.js → chart-pie-CsdzxmSK.js} +4 -4
- package/dist/client/chunks/{chart-pie-CzYnncO-.js.map → chart-pie-CsdzxmSK.js.map} +1 -1
- package/dist/client/chunks/{chart-radar-8iAt3QZl.js → chart-radar-C_9RwMKw.js} +4 -4
- package/dist/client/chunks/{chart-radar-8iAt3QZl.js.map → chart-radar-C_9RwMKw.js.map} +1 -1
- package/dist/client/chunks/{chart-radial-bar-CJbG7RIe.js → chart-radial-bar-CewRelyQ.js} +4 -4
- package/dist/client/chunks/{chart-radial-bar-CJbG7RIe.js.map → chart-radial-bar-CewRelyQ.js.map} +1 -1
- package/dist/client/chunks/{chart-sankey-C-wLBUmb.js → chart-sankey-ItraHWH1.js} +3 -3
- package/dist/client/chunks/{chart-sankey-C-wLBUmb.js.map → chart-sankey-ItraHWH1.js.map} +1 -1
- package/dist/client/chunks/{chart-scatter-NMjD1lbW.js → chart-scatter-k2IJbO2Y.js} +4 -4
- package/dist/client/chunks/{chart-scatter-NMjD1lbW.js.map → chart-scatter-k2IJbO2Y.js.map} +1 -1
- package/dist/client/chunks/{chart-sunburst-HtJ-LJ7n.js → chart-sunburst-NsFRuqi9.js} +4 -4
- package/dist/client/chunks/{chart-sunburst-HtJ-LJ7n.js.map → chart-sunburst-NsFRuqi9.js.map} +1 -1
- package/dist/client/chunks/{chart-tree-map-CetaLMt8.js → chart-tree-map-Cj_ewpwJ.js} +4 -4
- package/dist/client/chunks/{chart-tree-map-CetaLMt8.js.map → chart-tree-map-Cj_ewpwJ.js.map} +1 -1
- package/dist/client/chunks/{chart-waterfall-ontNp1Sd.js → chart-waterfall-C-MTFcOz.js} +4 -4
- package/dist/client/chunks/{chart-waterfall-ontNp1Sd.js.map → chart-waterfall-C-MTFcOz.js.map} +1 -1
- package/dist/client/chunks/{charts-core-B5UXUg6_.js → charts-core-vZA3zPKb.js} +2 -2
- package/dist/client/chunks/{charts-core-B5UXUg6_.js.map → charts-core-vZA3zPKb.js.map} +1 -1
- package/dist/client/chunks/{core-Dk6z6kC0.js → core-Su6tIYhp.js} +5 -3
- package/dist/client/chunks/{core-Dk6z6kC0.js.map → core-Su6tIYhp.js.map} +1 -1
- package/dist/client/chunks/{dist-eZurnOde.js → dist-BTq3NoG3.js} +38 -32
- package/dist/client/chunks/dist-BTq3NoG3.js.map +1 -0
- package/dist/client/chunks/{en-US-5xPTdCXg.js → en-US-D-1JPTPv.js} +1 -1
- package/dist/client/chunks/{en-US-5xPTdCXg.js.map → en-US-D-1JPTPv.js.map} +1 -1
- package/dist/client/chunks/{exceljs.min-DaJsLlWM.js → exceljs.min-Dc1cBQ5l.js} +71 -45
- package/dist/client/chunks/{exceljs.min-DaJsLlWM.js.map → exceljs.min-Dc1cBQ5l.js.map} +1 -1
- package/dist/client/chunks/{javascript-DFvvCuoP.js → javascript-YXkoOgWa.js} +1 -1
- package/dist/client/chunks/{javascript-DFvvCuoP.js.map → javascript-YXkoOgWa.js.map} +1 -1
- package/dist/client/chunks/{json-BBm9TlrA.js → json-O7MKB_4V.js} +1 -1
- package/dist/client/chunks/{json-BBm9TlrA.js.map → json-O7MKB_4V.js.map} +1 -1
- package/dist/client/chunks/{nl-NL-DDf0OdfW.js → nl-NL-BErZMygi.js} +10 -3
- package/dist/client/chunks/nl-NL-BErZMygi.js.map +1 -0
- package/dist/client/chunks/{rolldown-runtime-CCl2IeXn.js → rolldown-runtime-CKnJJeip.js} +1 -1
- package/dist/client/chunks/{schema-visualization-cnB2xZxn.js → schema-visualization-BY9L2nBQ.js} +294 -290
- package/dist/client/chunks/{schema-visualization-cnB2xZxn.js.map → schema-visualization-BY9L2nBQ.js.map} +1 -1
- package/dist/client/chunks/{sql-k0GA6oZ_.js → sql-r2a-9CCK.js} +1 -1
- package/dist/client/chunks/{sql-k0GA6oZ_.js.map → sql-r2a-9CCK.js.map} +1 -1
- package/dist/client/chunks/{syntaxHighlighting-D8J6Yt9j.js → syntaxHighlighting-5zHcjn27.js} +2 -2
- package/dist/client/chunks/{syntaxHighlighting-D8J6Yt9j.js.map → syntaxHighlighting-5zHcjn27.js.map} +1 -1
- package/dist/client/chunks/{useDebounce-BOBSvhHy.js → useDebounce-DGfYXtkm.js} +4 -4
- package/dist/client/chunks/{useDebounce-BOBSvhHy.js.map → useDebounce-DGfYXtkm.js.map} +1 -1
- package/dist/client/chunks/{useExplainAI-B_Pi4eXW.js → useExplainAI-CD0KuKwY.js} +4 -4
- package/dist/client/chunks/{useExplainAI-B_Pi4eXW.js.map → useExplainAI-CD0KuKwY.js.map} +1 -1
- package/dist/client/chunks/{utils-BIuqPQuJ.js → utils-D2SCtAkZ.js} +2 -2
- package/dist/client/chunks/{utils-BIuqPQuJ.js.map → utils-D2SCtAkZ.js.map} +1 -1
- package/dist/client/chunks/{vendor-BxLCTfvm.js → vendor-CfR5hJGc.js} +3 -3
- package/dist/client/chunks/{vendor-BxLCTfvm.js.map → vendor-CfR5hJGc.js.map} +1 -1
- package/dist/client/components/AnalysisBuilder/types.d.ts +2 -2
- package/dist/client/components.js +3 -3
- package/dist/client/hooks.js +3 -3
- package/dist/client/icons.js +1 -1
- package/dist/client/index.js +15 -15
- package/dist/client/providers.js +1 -1
- package/dist/client/schema.js +1 -1
- package/dist/client/shared/chartDefaults.d.ts +5 -10
- package/dist/client/types.d.ts +1 -0
- package/dist/client/utils.js +6 -6
- package/dist/client-bundle-stats.html +1 -1
- package/dist/mcp-app/mcp-app.html +48 -48
- package/dist/server/index.cjs +7 -7
- package/dist/server/index.js +159 -16
- package/package.json +2 -2
- package/dist/adapters/mcp-transport-CkyawtUT.cjs +0 -40
- package/dist/adapters/mcp-transport-DSbd6M_u.js +0 -586
- package/dist/adapters/utils-DrWvXf0G.cjs +0 -128
- package/dist/client/chunks/DashboardEditModal-BBcB0E2g.js.map +0 -1
- package/dist/client/chunks/KpiDelta-D09hA_UJ.js +0 -2
- package/dist/client/chunks/KpiNumber-B7F9F9fC.js +0 -2
- package/dist/client/chunks/KpiText-C3ZXOF8b.js +0 -2
- package/dist/client/chunks/SchemaVisualization-DP4k1fPp.js +0 -2
- package/dist/client/chunks/SchemaVisualizationLazy-Brqv_PY9.js +0 -2
- package/dist/client/chunks/af-ZA-xDmO5F0s.js.map +0 -1
- package/dist/client/chunks/analysis-builder-0o1W-k3K.js.map +0 -1
- package/dist/client/chunks/analysis-builder-shared-Cz4KAlIC.js.map +0 -1
- package/dist/client/chunks/chart-activity-grid-VFFm85hC.js.map +0 -1
- package/dist/client/chunks/chart-area-CwwIHTmK.js.map +0 -1
- package/dist/client/chunks/chart-bar-Bmny922L.js.map +0 -1
- package/dist/client/chunks/chart-config-activity-grid-C-EkgYoa.js.map +0 -1
- package/dist/client/chunks/chart-config-area-CMZpbIah.js.map +0 -1
- package/dist/client/chunks/chart-config-bar-B8_V4YLg.js.map +0 -1
- package/dist/client/chunks/chart-config-box-plot-Dwj7sEbU.js.map +0 -1
- package/dist/client/chunks/chart-config-bubble-B0w0ZVp4.js.map +0 -1
- package/dist/client/chunks/chart-config-candlestick-Bvo3zeIn.js.map +0 -1
- package/dist/client/chunks/chart-config-gauge-C5ZiyZy7.js.map +0 -1
- package/dist/client/chunks/chart-config-heat-map-Cv8qNnVP.js.map +0 -1
- package/dist/client/chunks/chart-config-kpi-delta-BraHQc2E.js.map +0 -1
- package/dist/client/chunks/chart-config-kpi-number-CeCkx7mC.js.map +0 -1
- package/dist/client/chunks/chart-config-kpi-text-CImM3SvH.js.map +0 -1
- package/dist/client/chunks/chart-config-line-BVKapAQK.js.map +0 -1
- package/dist/client/chunks/chart-config-measure-profile-KTVV1gO3.js.map +0 -1
- package/dist/client/chunks/chart-config-pie-BZxVl25X.js.map +0 -1
- package/dist/client/chunks/chart-config-radar-B7FByX3t.js.map +0 -1
- package/dist/client/chunks/chart-config-radial-bar-UfW_3yyX.js.map +0 -1
- package/dist/client/chunks/chart-config-scatter-BVVJuOnt.js.map +0 -1
- package/dist/client/chunks/chart-config-tree-map-IHp97OyV.js.map +0 -1
- package/dist/client/chunks/chart-config-waterfall-BdqG1V-x.js.map +0 -1
- package/dist/client/chunks/chart-line-DKvW32U-.js.map +0 -1
- package/dist/client/chunks/chart-markdown-CJU2hUq3.js.map +0 -1
- package/dist/client/chunks/dist-eZurnOde.js.map +0 -1
- package/dist/client/chunks/nl-NL-DDf0OdfW.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-activity-grid-Bdb8U_NC.js","names":[],"sources":["../../../src/client/providers/CubeMetaContext.tsx","../../../src/i18n/locales/en.json","../../../src/i18n/runtime.ts","../../../src/client/providers/I18nProvider.tsx","../../../src/client/hooks/useTranslation.ts","../../../src/client/utils/chartUtils.ts","../../../src/client/hooks/useCubeFieldLabel.ts","../../../src/client/theme/index.ts","../../../src/client/utils/chartConstants.ts","../../../src/client/components/charts/ActivityGridChart.tsx"],"sourcesContent":["/**\n * CubeMetaContext - Metadata context definitions.\n *\n * Split from CubeMetaProvider so consumers can access metadata context\n * without pulling in TanStack Query dependencies.\n */\n\nimport { createContext, useContext } from 'react'\nimport type { CubeMeta, FieldLabelMap } from '../types'\n\nexport interface CubeMetaContextValue {\n meta: CubeMeta | null\n labelMap: FieldLabelMap\n metaLoading: boolean\n metaError: string | null\n getFieldLabel: (fieldName: string) => string\n refetchMeta: () => void\n}\n\nexport const CubeMetaContext = createContext<CubeMetaContextValue | null>(null)\n\nexport function useCubeMeta() {\n const context = useContext(CubeMetaContext)\n if (!context) {\n throw new Error('useCubeMeta must be used within CubeMetaProvider')\n }\n return context\n}\n","{\n \"analysis.modes.query.label\": \"Query\",\n \"analysis.modes.query.description\": \"Standard analysis (single or multi-query)\",\n \"analysis.modes.funnel.label\": \"Funnel\",\n \"analysis.modes.funnel.description\": \"Sequential conversion analysis\",\n \"analysis.modes.flow.label\": \"Flow\",\n \"analysis.modes.flow.description\": \"Bidirectional path analysis with Sankey visualisation\",\n \"analysis.modes.retention.label\": \"Retention\",\n \"analysis.modes.retention.description\": \"Cohort-based retention analysis over time periods\",\n\n \"analysis.ai.title\": \"AI Query Generator\",\n \"analysis.ai.generating\": \"Generating...\",\n \"analysis.ai.placeholder\": \"Describe your query in natural language... (e.g., 'Show total sales by month for the last year')\",\n \"analysis.ai.shortcutHint\": \"Press Enter to generate, Shift+Enter for new line\",\n \"analysis.ai.successMessage\": \"Query generated and loaded! Check the results below, then click Accept to keep or Cancel to revert.\",\n \"analysis.ai.button.accept\": \"Accept\",\n \"analysis.ai.button.cancel\": \"Cancel\",\n \"analysis.ai.button.close\": \"Close\",\n \"analysis.ai.button.generate\": \"Generate\",\n \"analysis.ai.button.generating\": \"Generating...\",\n\n \"analysis.sections.metrics\": \"Metrics\",\n \"analysis.sections.breakdown\": \"Breakdown\",\n \"analysis.sections.dimensions\": \"Dimensions\",\n \"analysis.sections.filters\": \"Filter\",\n \"analysis.sections.limit\": \"Limit\",\n\n \"analysis.tabs.query\": \"Query\",\n \"analysis.tabs.chart\": \"Chart\",\n \"analysis.tabs.chartTitle\": \"Chart configuration\",\n \"analysis.tabs.display\": \"Display\",\n \"analysis.tabs.displayTitle\": \"Display options\",\n\n \"analysis.multiQuery.removeQuery\": \"Remove query\",\n \"analysis.multiQuery.addQuery\": \"Add new query\",\n \"analysis.multiQuery.addAnother\": \"Add another query\",\n \"analysis.multiQuery.mergeExplanation\": \"In merge mode, dimensions are shared from Q1.\",\n \"analysis.multiQuery.switchToSeparate\": \"Switch to separate series\",\n\n \"analysis.mergeStrategy.concat\": \"Separate series\",\n \"analysis.mergeStrategy.merge\": \"Merge by dimension\",\n \"analysis.mergeStrategy.funnel\": \"Funnel\",\n\n \"analysis.placeholders.addMetrics\": \"Add metrics to generate SQL\",\n \"analysis.placeholders.hoverField\": \"Hover over a field to see details\",\n \"analysis.placeholders.noData\": \"No data available\",\n\n \"common.actions.accept\": \"Accept\",\n \"common.actions.cancel\": \"Cancel\",\n \"common.actions.delete\": \"Delete\",\n \"common.actions.close\": \"Close\",\n \"common.actions.edit\": \"Edit\",\n \"common.actions.save\": \"Save\",\n \"common.actions.copy\": \"Copy\",\n \"common.actions.duplicate\": \"Duplicate\",\n \"common.actions.refresh\": \"Refresh\",\n \"common.actions.confirm\": \"Confirm\",\n \"common.actions.add\": \"Add\",\n \"common.actions.clear\": \"Clear\",\n \"common.actions.select\": \"Select\",\n \"common.actions.navigate\": \"Navigate\",\n \"common.actions.share\": \"Share\",\n \"common.loading\": \"Loading...\",\n\n \"common.modal.processing\": \"Processing...\",\n \"common.modal.dashboardName\": \"Dashboard Name\",\n \"common.modal.deleteConfirmation\": \"Are you sure? This action cannot be undone.\",\n\n \"common.sorting.ascending\": \"Sorted ascending (click for descending)\",\n \"common.sorting.descending\": \"Sorted descending (click to remove)\",\n \"common.sorting.none\": \"Click to sort ascending\",\n\n \"fieldTypes.count\": \"Count\",\n \"fieldTypes.countDistinct\": \"Count Distinct\",\n \"fieldTypes.countDistinctApprox\": \"Count Distinct (Approx)\",\n \"fieldTypes.sum\": \"Sum\",\n \"fieldTypes.avg\": \"Average\",\n \"fieldTypes.min\": \"Minimum\",\n \"fieldTypes.max\": \"Maximum\",\n \"fieldTypes.runningTotal\": \"Running Total\",\n \"fieldTypes.number\": \"Number\",\n \"fieldTypes.string\": \"Text\",\n \"fieldTypes.boolean\": \"Boolean\",\n \"fieldTypes.time\": \"Time Dimension\",\n \"fieldTypes.geo\": \"Geographic\",\n \"fieldTypes.dimension\": \"Dimension\",\n\n \"fieldPanel.emptyState\": \"Hover over a field to see details\",\n \"fieldPanel.usageHint\": \"Press Enter or click to add this field to your query.\",\n \"fieldPanel.labels.type\": \"Type\",\n \"fieldPanel.labels.cube\": \"Cube\",\n \"fieldPanel.labels.category\": \"Category\",\n \n \"fieldCategory.measure\": \"Measure\",\n \"fieldCategory.timeDimension\": \"Time Dimension\",\n \"fieldCategory.dimension\": \"Dimension\",\n\n \"fieldSearch.placeholder.metrics\": \"Search metrics...\",\n \"fieldSearch.placeholder.filter\": \"Search fields to filter...\",\n \"fieldSearch.placeholder.dimensions\": \"Search dimensions...\",\n \"fieldSearch.modal.title.metrics\": \"Select a Metric\",\n \"fieldSearch.modal.title.filter\": \"Select a Field to Filter\",\n \"fieldSearch.modal.title.dimensions\": \"Select a Dimension\",\n \"fieldSearch.filter.allCubes\": \"All Cubes\",\n \"fieldSearch.categories.all\": \"All\",\n \"fieldSearch.empty.heading\": \"No fields found\",\n \"fieldSearch.empty.noMatchMetrics\": \"No metrics match \\\"{searchTerm}\\\"\",\n \"fieldSearch.empty.noMatchDimensions\": \"No dimensions match \\\"{searchTerm}\\\"\",\n \"fieldSearch.empty.noMetrics\": \"No metrics available\",\n \"fieldSearch.empty.noDimensions\": \"No dimensions available\",\n \"fieldSearch.section.recents\": \"Recents\",\n \"fieldSearch.footer.metricsAvailable\": \"metrics available\",\n \"fieldSearch.footer.fieldsAvailable\": \"fields available\",\n \"fieldSearch.footer.dimensionsAvailable\": \"dimensions available\",\n \"fieldSearch.shortcut.navigate\": \"Navigate\",\n \"fieldSearch.shortcut.keyEnter\": \"Enter\",\n \"fieldSearch.shortcut.keyShift\": \"Shift\",\n \"fieldSearch.shortcut.keyEsc\": \"Esc\",\n \"fieldSearch.shortcut.plusClick\": \"+Click\",\n \"fieldSearch.shortcut.select\": \"Select\",\n \"fieldSearch.shortcut.multiSelect\": \"Multi-select\",\n \"fieldSearch.shortcut.close\": \"Close\",\n\n \"filter.group.condition\": \"condition\",\n \"filter.group.conditions\": \"conditions\",\n \"filter.group.addFilter\": \"Add Filter\",\n \"filter.group.addAndGroup\": \"Add AND Group\",\n \"filter.group.addOrGroup\": \"Add OR Group\",\n \"filter.group.removeGroup\": \"Remove group\",\n \"filter.group.empty\": \"No conditions in this group\",\n \"filter.group.addFilterLink\": \"Add a filter\",\n\n \"filter.modal.title\": \"Edit Filter\",\n \"filter.modal.fieldLabel\": \"Field\",\n \"filter.modal.operatorLabel\": \"Operator\",\n \"filter.modal.valueLabel\": \"Value\",\n \"filter.modal.selectRange\": \"Select range\",\n \"filter.modal.noValueRequired\": \"No value required\",\n \"filter.modal.dateTo\": \"to\",\n \"filter.modal.min\": \"Min\",\n \"filter.modal.to\": \"to\",\n \"filter.modal.max\": \"Max\",\n \"filter.modal.enterNumber\": \"Enter number\",\n \"filter.modal.loading\": \"Loading...\",\n \"filter.modal.selectValue\": \"Select value...\",\n \"filter.modal.search\": \"Search...\",\n \"filter.modal.errorPrefix\": \"Error: \",\n \"filter.modal.noValues\": \"No values found\",\n \"filter.modal.multiSelectHint\": \"Hold Shift to select multiple values\",\n \"filter.modal.enterValue\": \"Enter value...\",\n\n \"filter.removeButton.title\": \"Remove filter\",\n \"filter.section.clearAll\": \"Clear all\",\n \"filter.section.dropHint\": \"Drop to add filter\",\n \"filter.section.empty\": \"No filters applied\",\n \"filter.valueDisplay.empty\": \"(empty)\",\n \"filter.valueDisplay.more\": \"more\",\n\n \"filter.operator.equals.label\": \"equals\",\n \"filter.operator.equals.description\": \"Exact match\",\n \"filter.operator.notEquals.label\": \"not equals\",\n \"filter.operator.notEquals.description\": \"Does not match\",\n \"filter.operator.contains.label\": \"contains\",\n \"filter.operator.contains.description\": \"Contains text (case insensitive)\",\n \"filter.operator.notContains.label\": \"not contains\",\n \"filter.operator.notContains.description\": \"Does not contain text\",\n \"filter.operator.startsWith.label\": \"starts with\",\n \"filter.operator.startsWith.description\": \"Starts with text\",\n \"filter.operator.notStartsWith.label\": \"not starts with\",\n \"filter.operator.notStartsWith.description\": \"Does not start with text\",\n \"filter.operator.endsWith.label\": \"ends with\",\n \"filter.operator.endsWith.description\": \"Ends with text\",\n \"filter.operator.notEndsWith.label\": \"not ends with\",\n \"filter.operator.notEndsWith.description\": \"Does not end with text\",\n \"filter.operator.like.label\": \"like\",\n \"filter.operator.like.description\": \"SQL LIKE pattern matching (case sensitive)\",\n \"filter.operator.notLike.label\": \"not like\",\n \"filter.operator.notLike.description\": \"SQL NOT LIKE pattern matching (case sensitive)\",\n \"filter.operator.ilike.label\": \"ilike\",\n \"filter.operator.ilike.description\": \"SQL ILIKE pattern matching (case insensitive)\",\n \"filter.operator.gt.label\": \"greater than\",\n \"filter.operator.gt.description\": \"Greater than value\",\n \"filter.operator.gte.label\": \"greater than or equal\",\n \"filter.operator.gte.description\": \"Greater than or equal to value\",\n \"filter.operator.lt.label\": \"less than\",\n \"filter.operator.lt.description\": \"Less than value\",\n \"filter.operator.lte.label\": \"less than or equal\",\n \"filter.operator.lte.description\": \"Less than or equal to value\",\n \"filter.operator.between.label\": \"between\",\n \"filter.operator.between.description\": \"Between two values (inclusive)\",\n \"filter.operator.notBetween.label\": \"not between\",\n \"filter.operator.notBetween.description\": \"Not between two values\",\n \"filter.operator.in.label\": \"in\",\n \"filter.operator.in.description\": \"Matches any of the provided values\",\n \"filter.operator.notIn.label\": \"not in\",\n \"filter.operator.notIn.description\": \"Does not match any of the provided values\",\n \"filter.operator.set.label\": \"is set\",\n \"filter.operator.set.description\": \"Is not null/empty\",\n \"filter.operator.notSet.label\": \"is not set\",\n \"filter.operator.notSet.description\": \"Is null/empty\",\n \"filter.operator.isEmpty.label\": \"is empty\",\n \"filter.operator.isEmpty.description\": \"Is empty string or null\",\n \"filter.operator.isNotEmpty.label\": \"is not empty\",\n \"filter.operator.isNotEmpty.description\": \"Is not empty string and not null\",\n \"filter.operator.inDateRange.label\": \"in date range\",\n \"filter.operator.inDateRange.description\": \"Between two dates\",\n \"filter.operator.beforeDate.label\": \"before date\",\n \"filter.operator.beforeDate.description\": \"Before specified date\",\n \"filter.operator.afterDate.label\": \"after date\",\n \"filter.operator.afterDate.description\": \"After specified date\",\n \"filter.operator.regex.label\": \"matches regex\",\n \"filter.operator.regex.description\": \"Matches regular expression pattern\",\n \"filter.operator.notRegex.label\": \"not matches regex\",\n \"filter.operator.notRegex.description\": \"Does not match regular expression pattern\",\n \"filter.operator.arrayContains.label\": \"array contains all\",\n \"filter.operator.arrayContains.description\": \"Array field contains all specified values (PostgreSQL only)\",\n \"filter.operator.arrayOverlaps.label\": \"array contains any\",\n \"filter.operator.arrayOverlaps.description\": \"Array field contains any of the specified values (PostgreSQL only)\",\n \"filter.operator.arrayContained.label\": \"array values in\",\n \"filter.operator.arrayContained.description\": \"All array field values are within specified values (PostgreSQL only)\",\n\n \"dateRange.custom\": \"Custom\",\n \"dateRange.today\": \"Today\",\n \"dateRange.yesterday\": \"Yesterday\",\n \"dateRange.thisWeek\": \"This week\",\n \"dateRange.thisMonth\": \"This month\",\n \"dateRange.thisQuarter\": \"This quarter\",\n \"dateRange.thisYear\": \"This year\",\n \"dateRange.last7Days\": \"Last 7 days\",\n \"dateRange.last30Days\": \"Last 30 days\",\n \"dateRange.lastNDays\": \"Last N days\",\n \"dateRange.lastWeek\": \"Last week\",\n \"dateRange.lastNWeeks\": \"Last N weeks\",\n \"dateRange.lastMonth\": \"Last month\",\n \"dateRange.last12Months\": \"Last 12 months\",\n \"dateRange.lastNMonths\": \"Last N months\",\n \"dateRange.lastQuarter\": \"Last quarter\",\n \"dateRange.lastNQuarters\": \"Last N quarters\",\n \"dateRange.lastYear\": \"Last year\",\n \"dateRange.lastNYears\": \"Last N years\",\n\n \"timeGranularity.hour\": \"Hour\",\n \"timeGranularity.day\": \"Day\",\n \"timeGranularity.week\": \"Week\",\n \"timeGranularity.month\": \"Month\",\n \"timeGranularity.quarter\": \"Quarter\",\n \"timeGranularity.year\": \"Year\",\n\n \"query.limit.label\": \"Limit\",\n \"query.limit.clear\": \"Clear\",\n\n \"chart.bar.label\": \"Bar Chart\",\n \"chart.bar.description\": \"Compare values across categories\",\n \"chart.bar.useCase\": \"Best for comparing discrete categories, showing rankings, or displaying changes over time\",\n \"chart.line.label\": \"Line Chart\",\n \"chart.line.description\": \"Show trends and changes over time\",\n \"chart.line.useCase\": \"Best for continuous data, trends, time series, and showing relationships between multiple series\",\n \"chart.area.label\": \"Area Chart\",\n \"chart.area.description\": \"Emphasise magnitude of change over time\",\n \"chart.area.useCase\": \"Best for showing cumulative totals, volume changes, or stacked comparisons over time\",\n \"chart.pie.label\": \"Pie Chart\",\n \"chart.pie.description\": \"Show proportions of a whole\",\n \"chart.pie.useCase\": \"Best for showing percentage distribution or composition of a total (limit to 5-7 slices)\",\n \"chart.scatter.label\": \"Scatter Plot\",\n \"chart.scatter.description\": \"Reveal correlations between variables\",\n \"chart.scatter.useCase\": \"Best for identifying patterns, correlations, outliers, and relationships between two measures\",\n \"chart.bubble.label\": \"Bubble Chart\",\n \"chart.bubble.description\": \"Compare three dimensions of data\",\n \"chart.bubble.useCase\": \"Best for showing relationships between three variables (X, Y, and size), market analysis\",\n \"chart.radar.label\": \"Radar Chart\",\n \"chart.radar.description\": \"Compare multiple metrics across categories\",\n \"chart.radar.useCase\": \"Best for multivariate comparisons, performance metrics, strengths/weaknesses analysis\",\n \"chart.radialBar.label\": \"Radial Bar Chart\",\n \"chart.radialBar.description\": \"Circular progress and KPI visualisation\",\n \"chart.radialBar.useCase\": \"Best for showing progress toward goals, KPIs, or comparing percentages in a compact form\",\n \"chart.treemap.label\": \"TreeMap\",\n \"chart.treemap.description\": \"Visualise hierarchical data with nested rectangles\",\n \"chart.treemap.useCase\": \"Best for showing part-to-whole relationships in hierarchical data, disk usage, budget allocation\",\n \"chart.table.label\": \"Data Table\",\n \"chart.table.description\": \"Display detailed tabular data\",\n \"chart.table.useCase\": \"Best for precise values, detailed analysis, sortable/filterable data exploration\",\n \"chart.activityGrid.label\": \"Activity Grid\",\n \"chart.activityGrid.description\": \"GitHub-style activity grid showing temporal patterns across different time scales\",\n \"chart.activityGrid.useCase\": \"Best for visualising activity patterns over time. Supports hour (3hr blocks × days), day (days × weeks), week (weeks × months), month (months × quarters), and quarter (quarters × years) granularities\",\n \"chart.kpiNumber.label\": \"KPI Number\",\n \"chart.kpiNumber.description\": \"Display key performance indicators as large numbers\",\n \"chart.kpiNumber.useCase\": \"Perfect for showing important metrics like revenue, user count, or other key business metrics in a prominent, easy-to-read format\",\n \"chart.kpiDelta.label\": \"KPI Delta\",\n \"chart.kpiDelta.description\": \"Display change between latest and previous values with trend indicators\",\n \"chart.kpiDelta.useCase\": \"Perfect for showing performance changes over time, such as revenue growth, user acquisition changes, or other metrics where the trend and delta are more important than the absolute value\",\n \"chart.kpiText.label\": \"KPI Text\",\n \"chart.kpiText.description\": \"Display key performance indicators as customisable text\",\n \"chart.kpiText.useCase\": \"Perfect for showing metrics with custom formatting, combining multiple values, or displaying contextual KPI information using templates\",\n \"chart.markdown.label\": \"Markdown\",\n \"chart.markdown.description\": \"Display custom markdown content with formatting\",\n \"chart.markdown.useCase\": \"Perfect for adding documentation, notes, section headers, instructions, or formatted text to dashboards\",\n \"chart.funnel.label\": \"Funnel Chart\",\n \"chart.funnel.description\": \"Show conversion through sequential steps\",\n \"chart.funnel.useCase\": \"Best for visualising user journey funnels, sales pipelines, or multi-step processes\",\n \"chart.sankey.label\": \"Sankey Chart\",\n \"chart.sankey.description\": \"Show flow between states or steps\",\n \"chart.sankey.useCase\": \"Best for visualising user journey flows, path analysis, or state transitions\",\n \"chart.sunburst.label\": \"Sunburst Chart\",\n \"chart.sunburst.description\": \"Show hierarchical flow as radial rings\",\n \"chart.sunburst.useCase\": \"Best for visualising forward paths from a starting event in a compact radial layout\",\n \"chart.heatmap.label\": \"Heatmap\",\n \"chart.heatmap.description\": \"Visualise intensity across two dimensions\",\n \"chart.heatmap.useCase\": \"Best for showing patterns in matrix data like correlations, schedules, or category comparisons\",\n \"chart.retentionHeatmap.label\": \"Retention Matrix\",\n \"chart.retentionHeatmap.description\": \"Cohort retention matrix visualisation\",\n \"chart.retentionHeatmap.useCase\": \"Visualise user retention over time by cohort\",\n \"chart.retentionCombined.label\": \"Retention Chart\",\n \"chart.retentionCombined.description\": \"Combined retention visualisation with line chart and heatmap modes\",\n \"chart.retentionCombined.useCase\": \"Visualise user retention over time with optional breakdown segmentation\",\n \"chart.boxPlot.label\": \"Box Plot\",\n \"chart.boxPlot.description\": \"Show statistical distribution (median, IQR, whiskers) across categories\",\n \"chart.boxPlot.useCase\": \"Best for P&L spread per symbol, trade size distribution, latency distribution across platforms\",\n \"chart.waterfall.label\": \"Waterfall Chart\",\n \"chart.waterfall.description\": \"Show cumulative effect of sequential positive and negative values\",\n \"chart.waterfall.useCase\": \"Best for P&L decomposition, cash flow analysis, budget variance, or any sequential contribution breakdown\",\n \"chart.candlestick.label\": \"Candlestick Chart\",\n \"chart.candlestick.description\": \"Financial candlestick chart showing open/close body and high/low wicks\",\n \"chart.candlestick.useCase\": \"Best for EOD quotes (bid/ask spread per date/symbol), markout distribution bands, or OHLC price data\",\n \"chart.measureProfile.label\": \"Measure Profile\",\n \"chart.measureProfile.description\": \"Plot N measures as sequential X-axis points to visualise a profile or shape across intervals\",\n \"chart.measureProfile.useCase\": \"Best for markout interval analysis (e.g. avgMinus2m → avgAtEvent → avgPlus2h), metric profiles, or any pattern across ordered measures\",\n \"chart.gauge.label\": \"Gauge Chart\",\n \"chart.gauge.description\": \"Half-circle arc gauge for a single KPI value versus a maximum target\",\n \"chart.gauge.useCase\": \"Best for high-water marks vs equity, margin utilisation, or any single value progress toward a goal\",\n\n \"chart.config.chartType\": \"Chart Type\",\n \"chart.config.loading\": \"Loading chart configuration...\",\n \"chart.config.axisConfig\": \"Chart Configuration\",\n \"chart.config.unassigned\": \"Unassigned Fields\",\n \"chart.config.unassignedHint\": \"Drag fields to chart axes above\",\n \"chart.config.noFields\": \"Add metrics and breakdowns in the Query tab to configure chart axes\",\n\n \"chart.dropZone.xAxis.label\": \"X-Axis (Categories)\",\n \"chart.dropZone.xAxis.description\": \"Dimensions and time dimensions for grouping\",\n \"chart.dropZone.xAxis.empty\": \"Drop dimensions & time dimensions here\",\n \"chart.dropZone.yAxis.label\": \"Y-Axis (Values)\",\n \"chart.dropZone.yAxis.description\": \"Measures for values or dimensions for series\",\n \"chart.dropZone.yAxis.empty\": \"Drop measures or dimensions here\",\n \"chart.dropZone.series.label\": \"Series (Split into Multiple Series)\",\n \"chart.dropZone.series.description\": \"Dimensions to create separate data series\",\n \"chart.dropZone.series.empty\": \"Drop dimensions here to split data into series\",\n \"chart.dropZone.maxReached\": \"Maximum items reached\",\n \"chart.dropZone.default.empty\": \"Drop fields here\",\n\n \"chart.bar.dropZone.xAxis.empty\": \"Drop dimensions & time dimensions here\",\n \"chart.bar.dropZone.yAxis.empty\": \"Drop measures here\",\n \"chart.bar.dropZone.series.empty\": \"Drop dimensions here to split data into series\",\n \"chart.line.dropZone.xAxis.empty\": \"Drop time dimensions or dimensions here\",\n \"chart.line.dropZone.yAxis.empty\": \"Drop measures here\",\n \"chart.line.dropZone.series.empty\": \"Drop dimensions here for multiple lines\",\n \"chart.area.dropZone.xAxis.empty\": \"Drop time dimensions or dimensions here\",\n \"chart.area.dropZone.yAxis.empty\": \"Drop measures here\",\n \"chart.area.dropZone.series.empty\": \"Drop dimensions here for stacked areas\",\n \"chart.pie.dropZone.xAxis.empty\": \"Drop a dimension for categories\",\n \"chart.pie.dropZone.yAxis.empty\": \"Drop a measure for values\",\n \"chart.scatter.dropZone.xAxis.empty\": \"Drop a field for X-axis\",\n \"chart.scatter.dropZone.yAxis.empty\": \"Drop a measure for Y-axis\",\n \"chart.scatter.dropZone.series.empty\": \"Drop a dimension to colour points\",\n \"chart.bubble.dropZone.xAxis.empty\": \"Drop a field for X-axis position\",\n \"chart.bubble.dropZone.yAxis.empty\": \"Drop a measure for Y-axis position\",\n \"chart.bubble.dropZone.sizeField.empty\": \"Drop a measure for bubble size\",\n \"chart.bubble.dropZone.series.empty\": \"Drop a dimension for bubble labels\",\n \"chart.bubble.dropZone.colorField.empty\": \"Drop a field for bubble colour (optional)\",\n \"chart.radar.dropZone.xAxis.empty\": \"Drop dimensions for radar axes\",\n \"chart.radar.dropZone.yAxis.empty\": \"Drop measures for values\",\n \"chart.radar.dropZone.series.empty\": \"Drop dimensions for multiple shapes\",\n \"chart.radialBar.dropZone.xAxis.empty\": \"Drop dimensions for categories\",\n \"chart.radialBar.dropZone.yAxis.empty\": \"Drop a measure for values\",\n \"chart.treemap.dropZone.xAxis.empty\": \"Drop dimensions for categories\",\n \"chart.treemap.dropZone.yAxis.empty\": \"Drop a measure for size\",\n \"chart.treemap.dropZone.series.empty\": \"Drop a dimension for colour grouping\",\n \"chart.table.dropZone.xAxis.empty\": \"Drop fields to display as columns (or leave empty for all)\",\n \"chart.activityGrid.dropZone.dateField.empty\": \"Drop a time dimension (granularity affects grid structure)\",\n \"chart.activityGrid.dropZone.valueField.empty\": \"Drop a measure for activity intensity\",\n \"chart.kpiNumber.dropZone.yAxis.empty\": \"Drop a measure here\",\n \"chart.kpiDelta.dropZone.yAxis.empty\": \"Drop a measure here\",\n \"chart.kpiDelta.dropZone.xAxis.empty\": \"Drop a dimension for ordering\",\n \"chart.kpiText.dropZone.yAxis.empty\": \"Drop a measure here\",\n \"chart.funnel.dropZone.xAxis.empty\": \"Steps defined in funnel config\",\n \"chart.funnel.dropZone.yAxis.empty\": \"Counts calculated from funnel execution\",\n \"chart.sankey.dropZone.xAxis.empty\": \"Auto-populated from flow config\",\n \"chart.sankey.dropZone.yAxis.empty\": \"Calculated from flow execution\",\n \"chart.sunburst.dropZone.xAxis.empty\": \"Auto-populated from flow config\",\n \"chart.sunburst.dropZone.yAxis.empty\": \"Calculated from flow execution\",\n \"chart.heatmap.dropZone.xAxis.empty\": \"Drop one dimension here\",\n \"chart.heatmap.dropZone.yAxis.empty\": \"Drop one dimension here\",\n \"chart.heatmap.dropZone.valueField.empty\": \"Drop one measure here\",\n \"chart.boxPlot.dropZone.xAxis.empty\": \"Drop a dimension here\",\n \"chart.boxPlot.dropZone.yAxis.empty\": \"Drop 1, 3, or 5 measures here\",\n \"chart.waterfall.dropZone.xAxis.empty\": \"Drop a dimension here\",\n \"chart.waterfall.dropZone.yAxis.empty\": \"Drop a measure here\",\n \"chart.candlestick.dropZone.xAxis.empty\": \"Drop a time or dimension here\",\n \"chart.candlestick.dropZone.yAxis.empty\": \"Drop 2+ measures here\",\n \"chart.measureProfile.dropZone.yAxis.empty\": \"Drop 2+ measures here (displayed left → right)\",\n \"chart.measureProfile.dropZone.series.empty\": \"Drop a dimension here to create multiple lines\",\n \"chart.gauge.dropZone.yAxis.empty\": \"Drop 1 measure here (optional 2nd for dynamic max)\",\n\n \"chart.activityGrid.validation.timeDimensionRequired\": \"Time dimension is required for activity grid\",\n \"chart.activityGrid.validation.measureRequired\": \"Activity measure is required for intensity mapping\",\n \"chart.kpiDelta.validation.measureRequired\": \"A measure is required for KPI Delta charts\",\n \"chart.heatmap.validation.xAxisRequired\": \"X-axis dimension required\",\n \"chart.heatmap.validation.yAxisRequired\": \"Y-axis dimension required\",\n \"chart.heatmap.validation.valueRequired\": \"Value measure required\",\n\n \"chart.option.stacking.label\": \"Stacking\",\n \"chart.option.stacking.none\": \"None\",\n \"chart.option.stacking.stacked\": \"Stacked\",\n \"chart.option.stacking.percent\": \"Stacked 100%\",\n \"chart.option.stacking.description\": \"How to stack multiple series\",\n \"chart.option.target.label\": \"Target Values\",\n \"chart.option.target.description\": \"Single value or comma-separated values to spread across X-axis\",\n \"chart.option.connectNulls.label\": \"Connect Nulls\",\n \"chart.option.connectNulls.description\": \"Draw continuous line through missing data points\",\n \"chart.option.showAllXLabels.label\": \"Show All X Labels\",\n \"chart.option.showAllXLabels.description\": \"Display every label on the X-axis instead of auto-hiding overlapping labels\",\n \"chart.option.leftYAxisFormat.label\": \"Left Y-Axis Format\",\n \"chart.option.leftYAxisFormat.description\": \"Number formatting for left Y-axis\",\n \"chart.option.rightYAxisFormat.label\": \"Right Y-Axis Format\",\n \"chart.option.rightYAxisFormat.description\": \"Number formatting for right Y-axis\",\n \"chart.option.xAxisFormat.label\": \"X-Axis Format\",\n \"chart.option.xAxisFormat.description\": \"Number formatting for X-axis\",\n \"chart.option.yAxisFormat.label\": \"Y-Axis Format\",\n \"chart.option.yAxisFormat.description\": \"Number formatting for Y-axis\",\n \"chart.option.valueFormat.label\": \"Value Format\",\n \"chart.option.valueFormat.description\": \"Number formatting for values\",\n \"chart.option.innerRadius.label\": \"Inner Radius\",\n \"chart.option.innerRadius.description\": \"Hollow centre size (0% = solid pie, higher = donut style)\",\n \"chart.option.showLabels.label\": \"Show Cell Values\",\n \"chart.option.showLabels.description\": \"Display values inside each cell\",\n \"chart.option.cellShape.label\": \"Cell Shape\",\n \"chart.option.cellShape.rectangle\": \"Rectangle\",\n \"chart.option.cellShape.circle\": \"Circle\",\n \"chart.option.funnelStyle.label\": \"Funnel Style\",\n \"chart.option.funnelStyle.bars\": \"Bars\",\n \"chart.option.funnelStyle.funnel\": \"Funnel\",\n \"chart.option.funnelOrientation.label\": \"Orientation\",\n \"chart.option.funnelOrientation.horizontal\": \"Horizontal\",\n \"chart.option.funnelOrientation.vertical\": \"Vertical\",\n \"chart.option.hideSummaryFooter.label\": \"Hide Summary Footer\",\n \"chart.option.hideSummaryFooter.description\": \"Hide the summary footer showing steps count and overall conversion\",\n \"chart.option.showConversion.label\": \"Show Conversion Rate\",\n \"chart.option.showConversion.description\": \"Display step-to-step conversion percentage\",\n \"chart.option.showAvgTime.label\": \"Show Avg Time\",\n \"chart.option.showAvgTime.description\": \"Display average time to convert\",\n \"chart.option.showMedianTime.label\": \"Show Median Time\",\n \"chart.option.showMedianTime.description\": \"Display median time to convert\",\n \"chart.option.showP90Time.label\": \"Show P90 Time\",\n \"chart.option.showP90Time.description\": \"Display 90th percentile time to convert\",\n \"chart.option.linkOpacity.label\": \"Link Opacity\",\n \"chart.option.linkOpacity.light\": \"Light\",\n \"chart.option.linkOpacity.medium\": \"Medium\",\n \"chart.option.linkOpacity.dark\": \"Dark\",\n \"chart.option.showNodeLabels.label\": \"Show Node Labels\",\n \"chart.option.showNodeLabels.description\": \"Display labels on flow nodes\",\n \"chart.option.prefix.label\": \"Prefix\",\n \"chart.option.prefix.description\": \"Text to display before the number\",\n \"chart.option.suffix.label\": \"Suffix\",\n \"chart.option.suffix.description\": \"Text to display after the number\",\n \"chart.option.decimals.label\": \"Decimal Places\",\n \"chart.option.decimals.description\": \"Number of decimal places to display\",\n \"chart.option.showHistogram.label\": \"Show Variance Histogram\",\n \"chart.option.showHistogram.description\": \"Display historical variance chart below the delta\",\n \"chart.option.useLastCompletePeriod.label\": \"Use Last Complete Period\",\n \"chart.option.useLastCompletePeriod.description\": \"Exclude current incomplete period from calculation (e.g., partial week/month)\",\n \"chart.option.skipLastPeriod.label\": \"Skip Last Period\",\n \"chart.option.skipLastPeriod.description\": \"Always exclude the last period regardless of completeness\",\n \"chart.option.retentionDisplayMode.label\": \"Display Mode\",\n \"chart.option.retentionDisplayMode.lineChart\": \"Line Chart\",\n \"chart.option.retentionDisplayMode.heatmapTable\": \"Heatmap Table\",\n \"chart.option.retentionDisplayMode.combined\": \"Combined\",\n \"chart.option.showLegend.label\": \"Show Legend\",\n \"chart.option.showLegend.description\": \"Show the legend\",\n \"chart.option.showGrid.label\": \"Show Grid\",\n \"chart.option.showGrid.description\": \"Show grid lines on the chart\",\n \"chart.option.showTooltip.label\": \"Show Tooltip\",\n \"chart.option.showTooltip.description\": \"Show tooltip on hover with detailed stats\",\n \"chart.option.priorPeriodStyle.label\": \"Prior Period Line Style\",\n \"chart.option.priorPeriodStyle.dashed\": \"Dashed\",\n \"chart.option.priorPeriodStyle.dotted\": \"Dotted\",\n \"chart.option.priorPeriodStyle.solid\": \"Solid\",\n \"chart.option.priorPeriodStyle.description\": \"Line style for prior period in comparison mode\",\n \"chart.option.priorPeriodOpacity.label\": \"Prior Period Opacity\",\n \"chart.option.priorPeriodOpacity.description\": \"Opacity for prior period lines (0.1 to 1)\",\n \"chart.option.showTotal.label\": \"Show Total Bar\",\n \"chart.option.showTotal.description\": \"Append a final bar showing the running total\",\n \"chart.option.showConnectorLine.label\": \"Show Connector Line\",\n \"chart.option.showConnectorLine.description\": \"Draw a dashed step-line connecting bar tops\",\n \"chart.option.showDataLabels.label\": \"Show Data Labels\",\n \"chart.option.showDataLabels.description\": \"Display the value at each data point\",\n \"chart.option.showReferenceLineAtZero.label\": \"Show Zero Reference Line\",\n \"chart.option.showReferenceLineAtZero.description\": \"Draw a dashed line at Y = 0\",\n \"chart.option.lineType.label\": \"Line Interpolation\",\n \"chart.option.lineType.smooth\": \"Smooth (monotone)\",\n \"chart.option.lineType.linear\": \"Linear\",\n \"chart.option.lineType.step\": \"Step\",\n \"chart.option.lineType.description\": \"How data points are connected\",\n \"chart.option.rangeMode.label\": \"Chart Mode\",\n \"chart.option.rangeMode.ohlc\": \"OHLC (open, close, high, low)\",\n \"chart.option.rangeMode.range\": \"Range (high, low / bid, ask)\",\n \"chart.option.rangeMode.description\": \"OHLC: 4 measures. Range: 2 measures (high + low).\",\n \"chart.option.bullColor.label\": \"Bullish Colour\",\n \"chart.option.bullColor.description\": \"Candle colour when close ≥ open\",\n \"chart.option.bearColor.label\": \"Bearish Colour\",\n \"chart.option.bearColor.description\": \"Candle colour when close < open\",\n \"chart.option.showWicks.label\": \"Show Wicks\",\n \"chart.option.showWicks.description\": \"Draw high/low wicks above and below the body\",\n \"chart.option.minValue.label\": \"Minimum Value\",\n \"chart.option.minValue.description\": \"Lower bound of the gauge arc (default 0)\",\n \"chart.option.maxValue.label\": \"Maximum Value (static)\",\n \"chart.option.maxValue.description\": \"Upper bound of the gauge. Leave empty to use yAxis[1] or default 100\",\n \"chart.option.showCentreLabel.label\": \"Show Centre Label\",\n \"chart.option.showCentreLabel.description\": \"Display current value and field name in the centre of the gauge\",\n \"chart.option.showPercentage.label\": \"Show as Percentage\",\n \"chart.option.showPercentage.description\": \"Display value as % of max instead of raw number\",\n \"chart.option.fitToWidth.label\": \"Fit to Width\",\n \"chart.option.fitToWidth.description\": \"Automatically size blocks to fill portlet width and height while maintaining aspect ratio\",\n \"chart.option.fontSize.label\": \"Font Size\",\n \"chart.option.fontSize.small\": \"Small\",\n \"chart.option.fontSize.medium\": \"Medium\",\n \"chart.option.fontSize.large\": \"Large\",\n \"chart.option.alignment.label\": \"Text Alignment\",\n \"chart.option.alignment.left\": \"Left\",\n \"chart.option.alignment.center\": \"Center\",\n \"chart.option.alignment.right\": \"Right\",\n \"chart.option.hideHeader.label\": \"Hide Header\",\n \"chart.option.hideHeader.description\": \"Hide the portlet header bar (title and action buttons)\",\n \"chart.option.transparentBackground.label\": \"Transparent Background\",\n \"chart.option.transparentBackground.description\": \"Remove card background, border, and shadow for seamless integration as section headers\",\n \"chart.option.autoHeight.label\": \"Auto Height\",\n \"chart.option.autoHeight.description\": \"In row and mobile layouts, size to markdown content instead of fixed row height\",\n \"chart.option.accentBorder.label\": \"Accent Border\",\n \"chart.option.accentBorder.none\": \"None\",\n \"chart.option.accentBorder.left\": \"Left\",\n \"chart.option.accentBorder.top\": \"Top\",\n \"chart.option.accentBorder.bottom\": \"Bottom\",\n \"chart.option.accentBorder.description\": \"Add an accent-coloured border on one side of the content\",\n\n \"chart.configText.20_percent\": \"20%\",\n \"chart.configText.40_percent\": \"40%\",\n \"chart.configText.60_percent\": \"60%\",\n \"chart.configText.80_percent\": \"80%\",\n \"chart.configText.accent_color\": \"Accent Color\",\n \"chart.configText.activity_measure\": \"Activity Measure\",\n \"chart.configText.add_2_or_more_measures_they_become_the_x_axis_categories_in_the_order_li\": \"Add 2 or more measures — they become the X-axis categories in the order listed\",\n \"chart.configText.add_an_accent_colored_border_on_one_side_of_the_content\": \"Add an accent-colored border on one side of the content\",\n \"chart.configText.all_fields_to_display_as_columns\": \"All fields to display as columns\",\n \"chart.configText.array_of_value_0_1_fraction_color_bands_shown_as_outer_arc_markers\": \"Array of {value (0–1 fraction), color} bands shown as outer arc markers\",\n \"chart.configText.axes_categories\": \"Axes (Categories)\",\n \"chart.configText.bubble_colour\": \"Bubble Colour\",\n \"chart.configText.bubble_labels\": \"Bubble Labels\",\n \"chart.configText.bubble_radius\": \"Bubble Radius\",\n \"chart.configText.categories\": \"Categories\",\n \"chart.configText.choose_how_to_visualize_retention_data\": \"Choose how to visualize retention data\",\n \"chart.configText.color_bubbles_by_this_field_optional\": \"Color bubbles by this field (optional)\",\n \"chart.configText.color_for_negative_changes_decreases\": \"Color for negative changes (decreases)\",\n \"chart.configText.color_for_positive_changes_increases\": \"Color for positive changes (increases)\",\n \"chart.configText.color_from_the_dashboard_palette_for_headers_bullets_and_links\": \"Color from the dashboard palette for headers, bullets, and links\",\n \"chart.configText.color_from_the_dashboard_palette_for_the_kpi_value_text\": \"Color from the dashboard palette for the KPI value text\",\n \"chart.configText.color_groups\": \"Color Groups\",\n \"chart.configText.columns\": \"Columns\",\n \"chart.configText.columns_x_axis\": \"Columns (X-Axis)\",\n \"chart.configText.count_at_each_step_auto_calculated\": \"Count at each step (auto-calculated)\",\n \"chart.configText.count_of_entities_following_each_path\": \"Count of entities following each path\",\n \"chart.configText.current_value_to_display_on_the_gauge_e_g_current_equity_margin_used\": \"Current value to display on the gauge (e.g. current equity, margin used)\",\n \"chart.configText.dimension_for_column_categories\": \"Dimension for column categories\",\n \"chart.configText.dimension_for_ordering_data_typically_time\": \"Dimension for ordering data (typically time)\",\n \"chart.configText.dimension_for_pie_slices\": \"Dimension for pie slices\",\n \"chart.configText.dimension_for_row_categories\": \"Dimension for row categories\",\n \"chart.configText.dimension_labels_for_each_bar_segment_e_g_symbol_transaction_type\": \"Dimension labels for each bar segment (e.g. symbol, transaction type)\",\n \"chart.configText.dimension_optional\": \"Dimension (optional)\",\n \"chart.configText.dimension_to_color_points_by_category\": \"Dimension to color points by category\",\n \"chart.configText.dimension_to_color_rectangles_by_category\": \"Dimension to color rectangles by category\",\n \"chart.configText.dimension_to_group_boxes_by_e_g_symbol_platform\": \"Dimension to group boxes by (e.g. symbol, platform)\",\n \"chart.configText.dimension_to_split_data_into_separate_profile_lines_e_g_symbol_platform\": \"Dimension to split data into separate profile lines (e.g. symbol, platform)\",\n \"chart.configText.dimensions_for_radar_axes\": \"Dimensions for radar axes\",\n \"chart.configText.dimensions_for_radial_segments\": \"Dimensions for radial segments\",\n \"chart.configText.dimensions_for_treemap_rectangles\": \"Dimensions for treemap rectangles\",\n \"chart.configText.dimensions_to_create_multiple_radar_shapes\": \"Dimensions to create multiple radar shapes\",\n \"chart.configText.dimensions_to_create_separate_lines\": \"Dimensions to create separate lines\",\n \"chart.configText.dimensions_to_create_stacked_areas\": \"Dimensions to create stacked areas\",\n \"chart.configText.display_the_value_above_each_bar_segment\": \"Display the value above each bar segment\",\n \"chart.configText.display_value_at_each_data_point\": \"Display value at each data point\",\n \"chart.configText.drop_1_measure_for_auto_mode_3_for_avg_stddev_median_mode_or_5_for_min_q\": \"Drop 1 measure for auto mode, 3 for avg/stddev/median mode, or 5 for min/q1/median/q3/max mode\",\n \"chart.configText.drop_2_4_measures_in_order_open_close_high_low_ohlc_mode_for_range_mode_\": \"Drop 2–4 measures in order: open, close, high, low (OHLC mode). For range mode drop 2: high, low.\",\n \"chart.configText.enter_markdown_text_supports_headers_bold_text_italic_text_links_text_ur\": \"Enter markdown text. Supports headers (#), bold (**text**), italic (*text*), links ([text](url)), lists (- item), and horizontal rules (---).\",\n \"chart.configText.event_dimension_that_categorizes_flow_nodes\": \"Event dimension that categorizes flow nodes\",\n \"chart.configText.event_type\": \"Event Type\",\n \"chart.configText.exclude_current_incomplete_period_from_aggregation_e_g_partial_week_mont\": \"Exclude current incomplete period from aggregation (e.g., partial week/month)\",\n \"chart.configText.exclude_current_incomplete_period_from_delta_calculation_e_g_partial_wee\": \"Exclude current incomplete period from delta calculation (e.g., partial week/month)\",\n \"chart.configText.field_to_use_for_bubble_labels_and_identification\": \"Field to use for bubble labels and identification\",\n \"chart.configText.flow_count\": \"Flow Count\",\n \"chart.configText.hide_the_statistics_footer_below_the_chart\": \"Hide the statistics footer below the chart\",\n \"chart.configText.hollow_center_size_0_percent_solid_pie_higher_donut_style\": \"Hollow center size (0% = solid pie, higher = donut style)\",\n \"chart.configText.horizontal_alignment_of_the_markdown_content\": \"Horizontal alignment of the markdown content\",\n \"chart.configText.horizontal_axis_position\": \"Horizontal axis position\",\n \"chart.configText.how_to_stack_multiple_area_series\": \"How to stack multiple area series\",\n \"chart.configText.how_to_stack_multiple_bar_series\": \"How to stack multiple bar series\",\n \"chart.configText.markdown_content\": \"Markdown Content\",\n \"chart.configText.measure_for_rectangle_sizes\": \"Measure for rectangle sizes\",\n \"chart.configText.measure_for_slice_sizes\": \"Measure for slice sizes\",\n \"chart.configText.measure_for_y_position\": \"Measure for Y position\",\n \"chart.configText.measure_or_dimension_for_x_position\": \"Measure or dimension for X position\",\n \"chart.configText.measure_that_determines_cell_color\": \"Measure that determines cell color\",\n \"chart.configText.measure_to_display_as_kpi_number\": \"Measure to display as KPI number\",\n \"chart.configText.measure_to_display_in_the_kpi_text_template\": \"Measure to display in the KPI text template\",\n \"chart.configText.measure_to_track_changes_for\": \"Measure to track changes for\",\n \"chart.configText.measure_used_for_activity_intensity_color_coding\": \"Measure used for activity intensity (color coding)\",\n \"chart.configText.measures_for_area_values\": \"Measures for area values\",\n \"chart.configText.measures_for_bar_heights\": \"Measures for bar heights\",\n \"chart.configText.measures_for_line_values\": \"Measures for line values\",\n \"chart.configText.measures_for_radar_values\": \"Measures for radar values\",\n \"chart.configText.measures_for_radial_bar_lengths\": \"Measures for radial bar lengths\",\n \"chart.configText.measures_x_axis_order\": \"Measures (X-Axis Order)\",\n \"chart.configText.negative_change_color\": \"Negative Change Color\",\n \"chart.configText.none_pie\": \"None (Pie)\",\n \"chart.configText.number_formatting_for_cell_values_and_legend\": \"Number formatting for cell values and legend\",\n \"chart.configText.number_formatting_for_numeric_values\": \"Number formatting for numeric values\",\n \"chart.configText.number_formatting_for_size_values\": \"Number formatting for size values\",\n \"chart.configText.number_formatting_for_the_displayed_value_and_axis_labels\": \"Number formatting for the displayed value and axis labels\",\n \"chart.configText.number_formatting_for_the_price_axis\": \"Number formatting for the price axis\",\n \"chart.configText.number_formatting_for_the_value_axis\": \"Number formatting for the value axis\",\n \"chart.configText.number_formatting_for_the_y_axis\": \"Number formatting for the Y-axis\",\n \"chart.configText.number_formatting_for_x_axis_labels\": \"Number formatting for X-axis labels\",\n \"chart.configText.number_formatting_for_y_axis_and_values\": \"Number formatting for Y-axis and values\",\n \"chart.configText.number_formatting_for_y_axis_labels\": \"Number formatting for Y-axis labels\",\n \"chart.configText.number_of_decimal_places_to_display_for_numeric_values\": \"Number of decimal places to display for numeric values\",\n \"chart.configText.ohlc_measures_open_close_high_low\": \"OHLC Measures (open, close, high, low)\",\n \"chart.configText.opacity_of_flow_links\": \"Opacity of flow links\",\n \"chart.configText.overall_text_size_for_the_markdown_content\": \"Overall text size for the markdown content\",\n \"chart.configText.positive_change_color\": \"Positive Change Color\",\n \"chart.configText.rows_y_axis\": \"Rows (Y-Axis)\",\n \"chart.configText.series_color_groups\": \"Series (Color Groups)\",\n \"chart.configText.series_multiple_lines\": \"Series (Multiple Lines)\",\n \"chart.configText.series_multiple_shapes\": \"Series (Multiple Shapes)\",\n \"chart.configText.series_split_into_multiple_lines\": \"Series (Split into Multiple Lines)\",\n \"chart.configText.series_stack_areas\": \"Series (Stack Areas)\",\n \"chart.configText.show_series_legend_only_visible_with_a_series_dimension\": \"Show series legend (only visible with a Series dimension)\",\n \"chart.configText.show_the_color_intensity_legend\": \"Show the color intensity legend\",\n \"chart.configText.show_the_legend_for_breakdown_segments\": \"Show the legend for breakdown segments\",\n \"chart.configText.single_measure_whose_values_are_summed_cumulatively\": \"Single measure whose values are summed cumulatively\",\n \"chart.configText.size\": \"Size\",\n \"chart.configText.size_of_bubbles_based_on_this_measure\": \"Size of bubbles based on this measure\",\n \"chart.configText.size_of_the_center_hole_0_for_full_circle\": \"Size of the center hole (0 for full circle)\",\n \"chart.configText.step_count\": \"Step Count\",\n \"chart.configText.step_name\": \"Step Name\",\n \"chart.configText.step_names_auto_populated_from_funnel_steps\": \"Step names (auto-populated from funnel steps)\",\n \"chart.configText.target_value_to_compare_against_first_value_used_if_multiple_provided\": \"Target value to compare against (first value used if multiple provided)\",\n \"chart.configText.template_for_displaying_the_text_use_value_to_insert_the_measure_value\": \"Template for displaying the text. Use ${value} to insert the measure value.\",\n \"chart.configText.text_template\": \"Text Template\",\n \"chart.configText.threshold_bands\": \"Threshold Bands\",\n \"chart.configText.time_dimension\": \"Time Dimension\",\n \"chart.configText.time_dimension_or_category_for_each_candle_e_g_date_symbol\": \"Time dimension or category for each candle (e.g. date, symbol)\",\n \"chart.configText.time_dimensions_or_dimensions_for_x_axis\": \"Time dimensions or dimensions for X-axis\",\n \"chart.configText.time_field_that_determines_grid_structure_granularity_affects_layout\": \"Time field that determines grid structure (granularity affects layout)\",\n \"chart.configText.value\": \"Value\",\n \"chart.configText.value_color\": \"Value Color\",\n \"chart.configText.value_color_intensity\": \"Value (Color Intensity)\",\n \"chart.configText.value_measure\": \"Value Measure\",\n \"chart.configText.values\": \"Values\",\n \"chart.configText.vertical_axis_position\": \"Vertical axis position\",\n \"chart.configText.visualization_style\": \"Visualization style\",\n \"chart.configText.x_axis_groups\": \"X-Axis (Groups)\",\n \"chart.configText.x_axis_time_categories\": \"X-Axis (Time/Categories)\",\n \"chart.configText.x_axis_time_category\": \"X-Axis (Time / Category)\",\n \"chart.configText.y_axis\": \"Y-Axis\",\n \"chart.configText.y_axis_measures\": \"Y-Axis (Measures)\",\n \"chart.configText.y_axis_value\": \"Y-Axis (Value)\",\n\n \"display.loading\": \"Loading display options...\",\n \"display.noOptions\": \"No display options available for this chart type.\",\n \"display.heading\": \"Display Options\",\n\n \"results.loading.title\": \"Executing Query...\",\n \"results.loading.subtitle\": \"Running your query against the cube API\",\n \"results.error.title\": \"Query Execution Failed\",\n \"results.error.subtitle\": \"There was an error executing your query. Please check the query and try again.\",\n \"results.waiting.title\": \"Preparing Query...\",\n \"results.waiting.subtitle\": \"Your query will execute shortly\",\n \"results.needsRefresh.title\": \"Ready to Execute\",\n \"results.needsRefresh.subtitle\": \"Click refresh to run your query\",\n \"results.needsRefresh.runButton\": \"Run Query\",\n \"results.empty.query\": \"Add metrics or breakdowns from the panel on the right to see results\",\n \"results.empty.retention\": \"Select a cube and configure retention settings to see results\",\n \"results.empty.funnel\": \"Add funnel steps to see conversion analysis\",\n \"results.empty.flow\": \"Configure flow analysis to see user journey paths\",\n \"results.empty.title\": \"No Results Yet\",\n \"results.ai.button\": \"Analyse with AI\",\n \"results.noData.title\": \"Query Successful\",\n \"results.noData.subtitle\": \"No data returned from the query\",\n \"results.chart.noData\": \"No data to display\",\n \"results.chart.noDataHint\": \"Run a query to see chart visualisation\",\n \"results.chart.unsupported\": \"Unsupported chart type\",\n \"results.refreshing\": \"Refreshing results...\",\n \"results.header.rows\": \"rows\",\n \"results.header.row\": \"row\",\n \"results.header.stale\": \"Results may be outdated\",\n \"results.header.failed\": \"Query failed\",\n \"results.header.executing\": \"Executing...\",\n \"results.header.noResults\": \"No results\",\n \"results.view.chart\": \"Chart\",\n \"results.view.merged\": \"Merged\",\n \"results.warning.largeDataset\": \"Large dataset:\",\n \"results.warning.configChanged\": \"Query configuration changed. Results may be outdated.\",\n \"results.warning.refreshNow\": \"Refresh Now\",\n \"results.share.copied\": \"Copied!\",\n \"results.share.noChart\": \"(no chart)\",\n\n \"server.errors.dbNotConfigured\": \"Database executor not configured\",\n \"server.errors.cubeNotFound\": \"Cube '{cubeName}' not found\",\n \"server.errors.noCubesInQuery\": \"No cubes found in query\",\n \"server.errors.dbAdapterRequired\": \"DatabaseExecutor must have a databaseAdapter property\",\n \"server.errors.rlsRequiresTransactions\": \"rlsSetup requires a database driver that supports transactions (db.transaction)\",\n \"server.errors.queryExecutionFailed\": \"Query execution failed: {message}\",\n \"server.errors.queryExecutionUnknown\": \"Query execution failed: Unknown error\",\n \"server.errors.noCompareDateRange\": \"No compareDateRange found in query\",\n \"server.errors.compareDateRangeInvalid\": \"compareDateRange requires at least 2 periods\",\n \"server.errors.funnelValidationFailed\": \"Funnel validation failed: {errors}\",\n \"server.errors.flowValidationFailed\": \"Flow validation failed: {errors}\",\n \"server.errors.retentionValidationFailed\": \"Retention validation failed: {errors}\",\n \"server.errors.cubeRefUnresolved\": \"{cubeName}.joins.{joinName}: target cube '{targetCube}' is not registered\",\n \"server.errors.unresolvedCubeRefs\": \"Unresolved cube references:\\n{details}\",\n \"server.errors.calculatedMeasureValidation\": \"Calculated measure validation failed for cube '{cubeName}':\\n{details}\",\n \"server.errors.queryValidationFailed\": \"Query validation failed: {errors}\",\n \"server.errors.queryContainsMultipleModes\": \"Query contains multiple query modes: {modes}\",\n \"server.errors.primaryCubeNotFound\": \"Primary cube '{cubeName}' not found\",\n \"server.errors.noJoinPath\": \"No join path found from '{fromCube}' to '{toCube}'\",\n \"server.errors.cubeNotFoundForMeasure\": \"Cube '{cubeName}' not found for measure '{measure}'\",\n \"server.errors.cubeNotFoundForDimension\": \"Cube '{cubeName}' not found for dimension '{dimension}'\",\n \"server.errors.cubeNotFoundForTimeDimension\": \"Cube '{cubeName}' not found for time dimension '{timeDimension}'\",\n \"server.errors.invalidFunnelConfig\": \"Query does not contain a valid funnel configuration\",\n \"server.errors.invalidFlowConfig\": \"Query does not contain a valid flow configuration\",\n \"server.errors.invalidRetentionConfig\": \"Query does not contain a valid retention configuration\",\n \"server.errors.llmInitFailed\": \"Failed to initialize LLM provider\",\n\n \"server.validation.ai.bindingKeyRequired.flow\": \"flow.bindingKey is required\",\n \"server.validation.ai.bindingKeyRequired.funnel\": \"funnel.bindingKey is required\",\n \"server.validation.ai.bindingKeyRequired.retention\": \"retention.bindingKey is required\",\n \"server.validation.ai.cubeNotFoundInFilter\": \"Cube '{cubeName}' not found in filter\",\n \"server.validation.ai.cubeNotFoundWithAvailable\": \"Cube '{cubeName}' not found\",\n \"server.validation.ai.cubeNotFoundWithSuggestion\": \"Cube '{cubeName}' not found\",\n \"server.validation.ai.dimensionNotFoundWithAvailable\": \"Dimension '{dimensionName}' not found on cube '{cubeName}'\",\n \"server.validation.ai.dimensionNotFoundWithSuggestion\": \"Dimension '{dimensionName}' not found on cube '{cubeName}'\",\n \"server.validation.ai.dimensionNotTimeType\": \"Dimension '{dimension}' is not a time type (it's '{type}')\",\n \"server.validation.ai.emptyQuery\": \"Query must have at least one measure or dimension\",\n \"server.validation.ai.eventDimensionRequired\": \"flow.eventDimension is required\",\n \"server.validation.ai.filterFieldNotFound\": \"Filter field '{fieldName}' not found on cube '{cubeName}'\",\n \"server.validation.ai.filterFieldNotFoundWithSuggestion\": \"Filter field '{fieldName}' not found on cube '{cubeName}'\",\n \"server.validation.ai.funnelRequiresSteps\": \"funnel requires at least 2 steps\",\n \"server.validation.ai.funnelStepsRequired\": \"funnel.steps array is required\",\n \"server.validation.ai.granularityNotSpecified\": \"retention.granularity not specified\",\n \"server.validation.ai.invalidDimensionFormat\": \"Invalid dimension format: '{dimension}'. Expected 'CubeName.dimensionName'\",\n \"server.validation.ai.invalidFilterMemberFormat\": \"Invalid filter member format: '{member}'\",\n \"server.validation.ai.invalidMeasureFormat\": \"Invalid measure format: '{measure}'. Expected 'CubeName.measureName'\",\n \"server.validation.ai.measureNotFoundWithAvailable\": \"Measure '{measureName}' not found on cube '{cubeName}'\",\n \"server.validation.ai.measureNotFoundWithSuggestion\": \"Measure '{measureName}' not found on cube '{cubeName}'\",\n \"server.validation.ai.performanceManyDimensions\": \"Query has {count} dimensions, which may produce many rows\",\n \"server.validation.ai.performanceManyMeasures\": \"Query has {count} measures, which may impact performance\",\n \"server.validation.ai.periodsNotSpecified\": \"retention.periods not specified\",\n \"server.validation.ai.retentionTimeDimensionRequired\": \"retention.timeDimension is required\",\n \"server.validation.ai.stepMissingName\": \"Step {step} is missing a name\",\n \"server.validation.ai.stepsBothMissing\": \"Neither stepsBefore nor stepsAfter specified\",\n \"server.validation.ai.suggestAddDimensionFilters\": \"Consider adding filters or reducing dimensions\",\n \"server.validation.ai.suggestAddStepNames\": \"Add descriptive names to funnel steps\",\n \"server.validation.ai.suggestSetSteps\": \"Set stepsBefore and/or stepsAfter to see event sequences\",\n \"server.validation.ai.suggestSpecifyGranularity\": \"Specify granularity: \\\"day\\\", \\\"week\\\", or \\\"month\\\"\",\n \"server.validation.ai.suggestSpecifyPeriods\": \"Specify number of periods to analyze\",\n \"server.validation.ai.suggestSplitQueries\": \"Consider splitting into multiple queries\",\n \"server.validation.ai.suggestUseTimeDimension\": \"Use a dimension with type \\\"time\\\" for timeDimensions\",\n \"server.validation.ai.timeDimensionRequired.flow\": \"flow.timeDimension is required\",\n \"server.validation.ai.timeDimensionRequired.funnel\": \"funnel.timeDimension is required\",\n\n \"server.validation.chart.barNeedsDimension\": \"Bar charts need an xAxis dimension for category labels. Add a dimension to the query or use \\\"table\\\" chart type instead.\",\n \"server.validation.chart.barXAxisRequired\": \"chartConfig.xAxis is required for bar charts. Put a dimension in xAxis so bars have category labels.\",\n \"server.validation.chart.dropZoneRequired\": \"chartConfig.{key} is required for {chartType} chart ({label}). Accepts: {acceptDesc}.\",\n \"server.validation.chart.seriesDuplicatesXAxis\": \"chartConfig.series must not contain the same field as xAxis (found: {duplicates}). The series field is only for splitting into grouped/stacked sub-series by a DIFFERENT dimension. Remove the duplicate from series.\",\n\n \"server.validation.flow.bindingKeyCubeNotFound\": \"Binding key cube not found: {cubeName}\",\n \"server.validation.flow.bindingKeyDimNotFound\": \"Binding key dimension not found: {dimName} in cube {cubeName}\",\n \"server.validation.flow.bindingKeyMappingCubeNotFound\": \"Binding key mapping cube not found: {cubeName}\",\n \"server.validation.flow.eventDimCubeNotFound\": \"Event dimension cube not found: {cubeName}\",\n \"server.validation.flow.eventDimNotFound\": \"Event dimension not found: {dimName} in cube {cubeName}\",\n \"server.validation.flow.eventDimRequired\": \"Event dimension is required for flow analysis\",\n \"server.validation.flow.highStepDepthWarning\": \"High step depth (4-5) may impact query performance on large datasets\",\n \"server.validation.flow.invalidBindingKeyFormat\": \"Invalid binding key format: {bindingKey}. Expected 'CubeName.dimensionName'\",\n \"server.validation.flow.invalidEventDimFormat\": \"Invalid event dimension format: {eventDimension}. Expected 'CubeName.dimensionName'\",\n \"server.validation.flow.invalidJoinStrategy\": \"Invalid joinStrategy: {joinStrategy}\",\n \"server.validation.flow.invalidTimeDimFormat\": \"Invalid time dimension format: {timeDimension}. Expected 'CubeName.dimensionName'\",\n \"server.validation.flow.lateralNotSupported\": \"Lateral joins are not supported on this database\",\n \"server.validation.flow.lateralNotSupportedExec\": \"Lateral joins with CTE references are not supported on this database\",\n \"server.validation.flow.sqliteNotSupported\": \"Flow queries are not supported on SQLite. Use PostgreSQL or MySQL for flow analysis.\",\n \"server.validation.flow.startingStepFilterRequired\": \"Starting step must have at least one filter\",\n \"server.validation.flow.startingStepNameMissing\": \"Starting step has no name - using default\",\n \"server.validation.flow.startingStepRequired\": \"Starting step is required for flow analysis\",\n \"server.validation.flow.stepsBeforeRange\": \"stepsBefore must be between 0 and 5, got: {value}\",\n \"server.validation.flow.stepsAfterRange\": \"stepsAfter must be between 0 and 5, got: {value}\",\n \"server.validation.flow.timeDimCubeNotFound\": \"Time dimension cube not found: {cubeName}\",\n \"server.validation.flow.timeDimNotFound\": \"Time dimension not found: {dimName} in cube {cubeName}\",\n\n \"server.validation.funnel.bindingKeyCubeNotFound\": \"Binding key cube not found: {cubeName}\",\n \"server.validation.funnel.bindingKeyDimNotFound\": \"Binding key dimension not found: {dimName} in cube {cubeName}\",\n \"server.validation.funnel.bindingKeyMappingCubeNotFound\": \"Binding key mapping cube not found: {cubeName}\",\n \"server.validation.funnel.invalidBindingKeyFormat\": \"Invalid binding key format: {bindingKey}. Expected 'CubeName.dimensionName'\",\n \"server.validation.funnel.invalidTimeDimFormat\": \"Invalid time dimension format: {timeDimension}. Expected 'CubeName.dimensionName'\",\n \"server.validation.funnel.minSteps\": \"Funnel must have at least 2 steps\",\n \"server.validation.funnel.stepCubeNotFound\": \"Step {step} cube not found: {cube}\",\n \"server.validation.funnel.stepFilterCubeNotFound\": \"Step {step} filter cube not found: {cubeName}\",\n \"server.validation.funnel.stepFilterIsMeasure\": \"Step {step} filter '{member}' is a measure. Funnel step filters only support dimensions, not measures.\",\n \"server.validation.funnel.stepFilterMemberNotFound\": \"Step {step} filter member not found: {field} in cube {cubeName}\",\n \"server.validation.funnel.stepFilterNoJoinPath\": \"Step {step} filter '{member}' requires a join from '{stepCube}' but no join path was found. Define a join relationship between these cubes.\",\n \"server.validation.funnel.stepMustHaveName\": \"Step {step} must have a name\",\n \"server.validation.funnel.stepTimeToConvertFormat\": \"Step {step} timeToConvert must be ISO 8601 duration format: {value}\",\n \"server.validation.funnel.timeDimCubeNotFound\": \"Time dimension cube not found: {cubeName}\",\n \"server.validation.funnel.timeDimNotFound\": \"Time dimension not found: {dimName} in cube {cubeName}\",\n\n \"server.validation.retention.bindingKeyCubeNotFound\": \"Binding key cube not found: {cubeName}\",\n \"server.validation.retention.bindingKeyDimNotFound\": \"Binding key dimension not found: {dimName} in cube {cubeName}\",\n \"server.validation.retention.bindingKeyMappingCubeNotFound\": \"Binding key mapping cube not found: {cubeName}\",\n \"server.validation.retention.breakdownDimCubeNotFound\": \"Breakdown dimension cube not found: {cubeName}\",\n \"server.validation.retention.breakdownDimNotFound\": \"Breakdown dimension not found: {dimName} in cube {cubeName}\",\n \"server.validation.retention.cubeNotFound\": \"Cube not found: {cubeName}\",\n \"server.validation.retention.dateRangeEndRequired\": \"Date range end is required\",\n \"server.validation.retention.dateRangeInvalidEnd\": \"Invalid date range end format\",\n \"server.validation.retention.dateRangeInvalidStart\": \"Invalid date range start format\",\n \"server.validation.retention.dateRangeRequired\": \"Date range is required\",\n \"server.validation.retention.dateRangeStartBeforeEnd\": \"Date range start must be before or equal to end\",\n \"server.validation.retention.dateRangeStartRequired\": \"Date range start is required\",\n \"server.validation.retention.invalidBindingKeyFormat\": \"Invalid binding key format: {bindingKey}. Expected 'CubeName.dimensionName'\",\n \"server.validation.retention.invalidBreakdownDimFormat\": \"Invalid breakdown dimension format: {dimension}. Expected 'CubeName.dimensionName'\",\n \"server.validation.retention.invalidGranularity\": \"Invalid granularity: {granularity}\",\n \"server.validation.retention.invalidRetentionType\": \"Invalid retention type: {retentionType}\",\n \"server.validation.retention.invalidTimeDimFormat\": \"Invalid time dimension format: {timeDimension}\",\n \"server.validation.retention.noBindingKeyMapping\": \"No binding key mapping found for cube: {cubeName}\",\n \"server.validation.retention.periodsMax\": \"Periods cannot exceed 52 (performance limit)\",\n \"server.validation.retention.periodsMin\": \"Periods must be at least 1\",\n \"server.validation.retention.timeDimNotFound\": \"Time dimension not found: {dimName}\",\n\n \"server.validation.calculatedMeasure.mustHaveCalculatedSql\": \"Calculated measure '{cubeName}.{fieldName}' must have calculatedSql property\",\n \"server.validation.calculatedMeasure.invalidSyntax\": \"Invalid calculatedSql syntax in '{cubeName}.{fieldName}': {errors}\",\n \"server.validation.calculatedMeasure.circularDependency\": \"Circular dependency detected in calculated measures: {cycle}\",\n\n \"server.validation.query.multipleQueryModes\": \"Query contains multiple query modes: {modes}\",\n \"server.validation.query.funnelBindingKeyCubeNotFound\": \"Funnel binding key cube not found: {cubeName}\",\n \"server.validation.query.flowBindingKeyCubeNotFound\": \"Flow binding key cube not found: {cubeName}\",\n \"server.validation.query.retentionCubeNotFound\": \"Retention cube not found: {cubeName}\",\n \"server.validation.query.retentionBindingKeyCubeNotFound\": \"Retention binding key cube not found: {cubeName}\",\n \"server.validation.query.retentionBreakdownCubeNotFound\": \"Retention breakdown cube not found: {cubeName}\",\n \"server.validation.query.invalidMeasureFormat\": \"Invalid measure format: {measure}. Expected format: 'CubeName.fieldName'\",\n \"server.validation.query.cubeNotFoundForMeasure\": \"Cube '{cubeName}' not found (referenced in measure '{measure}')\",\n \"server.validation.query.measureNotFound\": \"Measure '{fieldName}' not found on cube '{cubeName}'{hint}\",\n \"server.validation.query.invalidDimensionFormat\": \"Invalid dimension format: {dimension}. Expected format: 'CubeName.fieldName'\",\n \"server.validation.query.cubeNotFoundForDimension\": \"Cube '{cubeName}' not found (referenced in dimension '{dimension}')\",\n \"server.validation.query.dimensionNotFound\": \"Dimension '{fieldName}' not found on cube '{cubeName}'{hint}\",\n \"server.validation.query.invalidTimeDimensionFormat\": \"Invalid timeDimension format: {dimension}. Expected format: 'CubeName.fieldName'\",\n \"server.validation.query.cubeNotFoundForTimeDimension\": \"Cube '{cubeName}' not found (referenced in timeDimension '{dimension}')\",\n \"server.validation.query.timeDimensionNotFound\": \"TimeDimension '{fieldName}' not found on cube '{cubeName}' (must be a dimension with time type)\",\n \"server.validation.query.mustReferenceAtLeastOneCube\": \"Query must reference at least one cube through measures, dimensions, or filters\",\n \"server.validation.query.ungroupedRequiresDimension\": \"Ungrouped queries require at least one dimension or time dimension\",\n \"server.validation.query.ungroupedIncompatibleFunnel\": \"Ungrouped queries are incompatible with funnel analysis\",\n \"server.validation.query.ungroupedIncompatibleFlow\": \"Ungrouped queries are incompatible with flow analysis\",\n \"server.validation.query.ungroupedIncompatibleRetention\": \"Ungrouped queries are incompatible with retention analysis\",\n \"server.validation.query.ungroupedIncompatibleCompareDateRange\": \"Ungrouped queries are incompatible with compareDateRange\",\n \"server.validation.query.ungroupedIncompatibleFillMissingDates\": \"Ungrouped queries are incompatible with fillMissingDates\",\n \"server.validation.query.filterMustHaveMember\": \"Filter must have a member field\",\n \"server.validation.query.invalidFilterMemberFormat\": \"Invalid filter member format: {member}. Expected format: 'CubeName.fieldName'\",\n \"server.validation.query.cubeNotFoundForFilter\": \"Cube '{cubeName}' not found (referenced in filter '{member}')\",\n \"server.validation.query.filterFieldNotFound\": \"Filter field '{fieldName}' not found on cube '{cubeName}' (must be a dimension or measure){hint}\",\n\n \"server.errors.funnel.cubeNotFoundForStep\": \"Cube not found for step: {cube}\",\n \"server.errors.funnel.cubeNotFoundForBindingKey\": \"Cube not found for binding key: {bindingKey}\",\n \"server.errors.funnel.cannotResolveCubeForStep\": \"Cannot resolve cube for step - multi-cube funnel requires cube specification in each step\",\n \"server.errors.funnel.bindingKeyDimNotFound\": \"Binding key dimension not found: {bindingKey}\",\n \"server.errors.funnel.noBindingKeyMapping\": \"No binding key mapping found for cube: {cubeName}\",\n \"server.errors.funnel.bindingKeyMappingDimNotFound\": \"Binding key dimension not found: {dimension}\",\n \"server.errors.funnel.timeDimNotFound\": \"Time dimension not found: {timeDimension}\",\n \"server.errors.funnel.noTimeDimMapping\": \"No time dimension mapping found for cube: {cubeName}\",\n \"server.errors.funnel.timeDimMappingNotFound\": \"Time dimension not found: {dimension}\",\n\n \"server.errors.flow.cannotResolveCube\": \"Cannot resolve cube for flow query\",\n \"server.errors.flow.cubeNotFound\": \"Cube not found: {cubeName}\",\n \"server.errors.flow.bindingKeyDimNotFound\": \"Binding key dimension not found: {bindingKey}\",\n \"server.errors.flow.noBindingKeyMapping\": \"No binding key mapping found for cube: {cubeName}\",\n \"server.errors.flow.bindingKeyMappingDimNotFound\": \"Binding key dimension not found: {dimension}\",\n \"server.errors.flow.timeDimNotFound\": \"Time dimension not found: {timeDimension}\",\n \"server.errors.flow.noTimeDimMapping\": \"No time dimension mapping found for cube: {cubeName}\",\n \"server.errors.flow.timeDimMappingNotFound\": \"Time dimension not found: {dimension}\",\n \"server.errors.flow.eventDimNotFound\": \"Event dimension not found: {eventDimension}\",\n\n \"server.validation.template.emptyReference\": \"Empty member reference {} found in template\",\n \"server.validation.template.invalidMemberReference\": \"Invalid member reference {ref}: must start with letter or underscore, and contain only letters, numbers, underscores, and dots\",\n \"server.validation.template.multipleDots\": \"Invalid member reference {ref}: only one dot allowed (Cube.measure format)\",\n \"server.validation.template.nestedBraces\": \"Nested braces are not allowed in member references\",\n \"server.validation.template.substituteTargetCubeNotFound\": \"Cannot substitute {ref}: cube '{cubeName}' not found\",\n \"server.validation.template.substituteMeasureNotResolved\": \"Cannot substitute {ref}: measure '{measureName}' not resolved yet. Ensure measures are resolved in dependency order.\",\n \"server.validation.template.unmatchedClosingBrace\": \"Unmatched closing brace at position {position}\",\n \"server.validation.template.unmatchedOpeningBrace\": \"Unmatched opening brace in template\",\n\n \"notebook.aiAssistant\": \"AI Assistant\",\n \"notebook.saveAsDashboard\": \"Save as Dashboard\",\n \"notebook.saveAsDashboardTitle\": \"Save notebook as a dashboard\",\n \"notebook.clearTitle\": \"Clear notebook and chat\",\n \"notebook.feedbackThanks\": \"Thanks for your feedback!\",\n \"notebook.feedbackQuestion\": \"Was this helpful?\",\n \"notebook.feedbackYes\": \"Yes\",\n \"notebook.feedbackNo\": \"No\",\n \"notebook.thinking\": \"Thinking...\",\n \"notebook.emptyState.title\": \"Data Analysis Assistant\",\n \"notebook.emptyState.description\": \"Ask me about your data and I'll create visualizations and insights.\",\n \"notebook.emptyState.example1\": \"\\\"Show me employee productivity trends\\\"\",\n \"notebook.emptyState.example2\": \"\\\"What are the top departments by headcount?\\\"\",\n \"notebook.emptyState.example3\": \"\\\"Compare revenue across product categories\\\"\",\n \"notebook.saveAsDashboardPrompt\": \"Save the current notebook as a dashboard with a professional layout, section headers, and appropriate filters.\",\n \"notebook.chatInput.placeholder\": \"Ask about your data...\",\n \"notebook.chatInput.stop\": \"Stop\",\n \"notebook.chatInput.continue\": \"Continue\",\n \"notebook.chatInput.send\": \"Send\",\n \"notebook.canvas.emptyTitle\": \"Your notebook is empty\",\n \"notebook.canvas.emptyDescription\": \"Ask the AI assistant a question about your data. Charts and insights will appear here as the assistant analyzes your data.\",\n \"notebook.canvas.editVisualization\": \"Edit Visualization\",\n \"notebook.canvas.update\": \"Update\",\n \"notebook.collapsed.noBlocks\": \"No blocks\",\n \"notebook.collapsed.expandNotebook\": \"Expand notebook\",\n \"notebook.collapsed.expandChat\": \"Expand AI chat\",\n \"notebook.collapsed.aiChat\": \"AI Chat\",\n \"notebook.collapsed.markdown\": \"Markdown\",\n\n \"schema.loading\": \"Loading cube schema...\",\n \"schema.error\": \"Failed to load cube schema\",\n \"schema.noCubes\": \"No cubes found\",\n \"schema.noCubesHint\": \"Register some cubes to see the relationship diagram\",\n \"schema.computingLayout\": \"Computing layout...\",\n \"schema.searchPlaceholder\": \"Search cubes and fields...\",\n \"schema.autoLayout\": \"Auto Layout\",\n \"schema.missingDeps.title\": \"Schema Visualization requires additional packages\",\n \"schema.missingDeps.description\": \"Install the required dependencies to enable the interactive schema diagram:\",\n \"schema.loadingVisualization\": \"Loading schema visualization...\",\n \"schema.measures\": \"Measures ({count})\",\n \"schema.timeDimensions\": \"Time Dimensions ({count})\",\n \"schema.dimensions\": \"Dimensions ({count})\",\n \"schema.cubeInfo\": \"Cube info\",\n\n \"dataBrowser.selectCube\": \"Select a cube\",\n \"dataBrowser.selectCubeHint\": \"Choose a cube from the sidebar to browse its data\",\n \"dataBrowser.loadingData\": \"Loading data...\",\n \"dataBrowser.noData\": \"No data\",\n \"dataBrowser.noRows\": \"No rows returned for this query\",\n \"dataBrowser.toolbar.filters\": \"Filters\",\n \"dataBrowser.toolbar.columns\": \"Columns\",\n \"dataBrowser.toolbar.rows\": \"{count} rows\",\n \"dataBrowser.sidebar.cubes\": \"Cubes\",\n \"dataBrowser.sidebar.searchPlaceholder\": \"Search...\",\n \"dataBrowser.sidebar.noCubes\": \"No cubes found\",\n\n \"queryAnalysis.summary\": \"Query Summary\",\n \"queryAnalysis.summary.type\": \"Type\",\n \"queryAnalysis.summary.cubes\": \"Cubes\",\n \"queryAnalysis.summary.joins\": \"Joins\",\n \"queryAnalysis.summary.ctes\": \"CTEs\",\n \"queryAnalysis.summary.strategy\": \"Strategy\",\n \"queryAnalysis.primaryCube\": \"Primary Cube (FROM table)\",\n \"queryAnalysis.primaryCube.showCandidates\": \"Show candidates ({count})\",\n \"queryAnalysis.primaryCube.reachable\": \"reachable\",\n \"queryAnalysis.primaryCube.cannotReachAll\": \"cannot reach all\",\n \"queryAnalysis.joinPaths\": \"Join Paths\",\n \"queryAnalysis.joinPaths.steps\": \"{count} step\",\n \"queryAnalysis.joinPaths.stepsPlural\": \"{count} steps\",\n \"queryAnalysis.joinPaths.noPath\": \"No path\",\n \"queryAnalysis.joinPaths.selection\": \"Selection:\",\n \"queryAnalysis.joinPaths.pathCandidates\": \"Path scoring candidates ({count})\",\n \"queryAnalysis.joinPaths.visitedCubes\": \"Cubes visited during search ({count})\",\n \"queryAnalysis.preAggregations\": \"Pre-Aggregation CTEs\",\n \"queryAnalysis.preAggregations.measures\": \"Measures:\",\n \"queryAnalysis.preAggregations.joinKeys\": \"Join keys:\",\n \"queryAnalysis.warnings\": \"Warnings\",\n \"queryAnalysis.cubesInvolved\": \"Cubes involved:\",\n\n \"common.actions.copied\": \"Copied\",\n \"common.actions.copyToClipboard\": \"Copy to clipboard\",\n\n \"chart.availability.requiresMeasure\": \"Requires at least 1 measure\",\n \"chart.availability.requiresTwoMeasures\": \"Requires at least 2 measures\",\n \"chart.availability.requiresDimension\": \"Requires at least 1 dimension\",\n \"chart.availability.requiresTwoDimensions\": \"Requires at least 2 dimensions\",\n \"chart.availability.requiresTimeDimension\": \"Requires a time dimension\",\n \"chart.availability.scatter\": \"Requires 2 measures, or 1 measure plus 1 dimension\",\n \"chart.availability.bubble\": \"Requires at least 2 measures and 1 dimension\",\n\n \"chart.runtime.noData\": \"No data available\",\n \"chart.runtime.noDataHint.bar\": \"No data points to display in bar chart\",\n \"chart.runtime.noDataHint.line\": \"No data points to display in line chart\",\n \"chart.runtime.noDataHint.area\": \"No data points to display in area chart\",\n \"chart.runtime.noDataHint.pie\": \"No data points to display in pie chart\",\n \"chart.runtime.noDataHint.scatter\": \"No data points to display in scatter chart\",\n \"chart.runtime.noDataHint.radar\": \"No data points to display in radar chart\",\n \"chart.runtime.noDataHint.radialBar\": \"No data points to display in radial bar chart\",\n \"chart.runtime.noDataHint.treemap\": \"No data points to display in treemap chart\",\n \"chart.runtime.noDataHint.bubble\": \"No data points to display in bubble chart\",\n \"chart.runtime.noDataHint.boxPlot\": \"No data points to display in box plot chart\",\n \"chart.runtime.noDataHint.waterfall\": \"No data points to display in waterfall chart\",\n \"chart.runtime.noDataHint.candlestick\": \"No data points to display in candlestick chart\",\n \"chart.runtime.noDataHint.gauge\": \"No data points to display in gauge chart\",\n \"chart.runtime.noDataHint.measureProfile\": \"No data points to display in measure profile chart\",\n \"chart.runtime.noDataHint.activityGrid\": \"No data points to display in activity grid\",\n \"chart.runtime.noDataHint.heatmap\": \"Run a query to see heatmap visualization\",\n \"chart.runtime.noDataHint.table\": \"No data to display in table\",\n \"chart.runtime.noDataHint.kpi\": \"No data points to display\",\n \"chart.runtime.noDataHint.funnel\": \"Configure a funnel with at least 2 steps and a binding key\",\n \"chart.runtime.noDataHint.flow\": \"Configure a flow analysis with a starting step and event dimension\",\n \"chart.runtime.noDataHint.retention\": \"Configure retention analysis to see results\",\n\n \"chart.runtime.noValidData\": \"No valid data\",\n \"chart.runtime.noValidDataHint.bar\": \"No valid data points for bar chart after transformation\",\n \"chart.runtime.noValidDataHint.line\": \"No valid data points for line chart after transformation\",\n \"chart.runtime.noValidDataHint.area\": \"No valid data points for area chart after transformation\",\n \"chart.runtime.noValidDataHint.scatter\": \"No valid data points for scatter chart after transformation\",\n \"chart.runtime.noValidDataHint.radar\": \"No valid data points for radar chart after transformation\",\n \"chart.runtime.noValidDataHint.radialBar\": \"No valid data points for radial bar chart after transformation\",\n \"chart.runtime.noValidDataHint.treemap\": \"No valid data points for treemap chart after transformation\",\n \"chart.runtime.noValidDataHint.pie\": \"No data points to display in pie chart\",\n \"chart.runtime.noValidDataHint.pieFiltered\": \"Filtered out {count} data points (zero or invalid values)\",\n \"chart.runtime.noValidDataHint.boxPlot\": \"Could not compute box plot statistics from the provided data\",\n \"chart.runtime.noValidDataHint.gauge\": \"Gauge value is not a valid number\",\n \"chart.runtime.noValidDataHint.kpiText\": \"All values are null or invalid\",\n\n \"chart.runtime.configError\": \"Configuration Error\",\n \"chart.runtime.configErrorHint.axisInvalid\": \"Invalid or missing chart axis configuration\",\n \"chart.runtime.configErrorHint.axisFields\": \"Missing required X-axis or Y-axis fields\",\n \"chart.runtime.configErrorHint.pieAxis\": \"chartConfig.x/y or chartConfig.xAxis/yAxis required for pie chart\",\n \"chart.runtime.configErrorHint.radarNumeric\": \"No numeric fields found for radar chart values\",\n \"chart.runtime.configErrorHint.radialBarNumeric\": \"No numeric field found for radial bar chart values\",\n \"chart.runtime.configErrorHint.treemapNumeric\": \"No numeric field found for treemap chart size\",\n \"chart.runtime.configErrorHint.noMeasure\": \"No measure field configured\",\n \"chart.runtime.configErrorHint.noMeasures\": \"No measure fields configured\",\n \"chart.runtime.configErrorHint.bubbleRequired\": \"Bubble chart requires xAxis, yAxis, series, and sizeField dimensions\",\n \"chart.runtime.configErrorHint.bubbleOptional\": \"Optional: colorField for bubble coloring\",\n \"chart.runtime.configErrorHint.activityGridRequired\": \"Activity grid requires a time dimension and a measure\",\n\n \"chart.runtime.chartError\": \"{chartType} Error\",\n \"chart.runtime.unknownError\": \"Unknown rendering error\",\n \"chart.runtime.checkConfig\": \"Check the data and configuration\",\n\n \"chart.runtime.unableToRender\": \"Unable to render retention data\",\n \"chart.runtime.dataFormatIncorrect\": \"Data format may be incorrect\",\n\n \"chart.runtime.measuringDimensions\": \"Measuring chart dimensions...\",\n \"chart.runtime.unableToDisplay\": \"Unable to display chart\",\n \"chart.runtime.responsiveContainerError\": \"Failed to create responsive container\",\n\n \"chart.runtime.noDataToDisplay\": \"No data to display\",\n \"chart.runtime.table.invalidStructure\": \"Data structure is invalid\",\n \"chart.runtime.heatmapNoResults\": \"The query returned no results for the heatmap\",\n \"chart.runtime.heatmapConfigRequired\": \"Configuration required\",\n \"chart.runtime.heatmapXRequired\": \"X-axis dimension required. \",\n \"chart.runtime.heatmapYRequired\": \"Y-axis dimension required. \",\n \"chart.runtime.heatmapValueRequired\": \"Value measure required.\",\n \"chart.runtime.heatmapTruncated\": \"Data truncated to {maxRows}x{maxCols} cells (original: {originalRows}x{originalCols}). Add filters to reduce dimensions.\",\n\n \"chart.runtime.activityGridGranularityTooHigh\": \"Granularity Too High\",\n \"chart.runtime.activityGridGranularityHint\": \"Activity grids work best with hour, day, week, month, or quarter granularity\",\n \"chart.runtime.activityGridGranularityAction\": \"Please choose a lower granularity for your time dimension\",\n \"chart.runtime.activityGridConfigRequired\": \"Configuration Required\",\n\n \"chart.runtime.retention.cohort\": \"Cohort\",\n \"chart.runtime.retention.segment\": \"Segment\",\n \"chart.runtime.retention.users\": \"Users\",\n \"chart.runtime.retention.cohortSize\": \"Cohort Size: {count}\",\n \"chart.runtime.retention.retained\": \"Retained: {count}\",\n \"chart.runtime.retention.rate\": \"Rate: {rate}\",\n \"chart.runtime.retention.retentionPercent\": \"Retention %\",\n \"chart.runtime.retention.periodLabel\": \"{cohort} - Period {period}\",\n \"chart.runtime.retention.noData\": \"No data\",\n \"chart.runtime.retention.total\": \"Total\",\n \"chart.runtime.retention.retention\": \"Retention\",\n\n \"chart.runtime.funnel.noData\": \"No funnel data\",\n \"chart.runtime.funnel.steps\": \"steps\",\n \"chart.runtime.funnel.overall\": \"Overall:\",\n \"chart.runtime.funnel.completed\": \"{completed} / {total} completed\",\n\n \"chart.runtime.flow.noData\": \"No flow data\",\n \"chart.runtime.flow.events\": \"events\",\n \"chart.runtime.flow.eventsAfter\": \"events (after)\",\n \"chart.runtime.flow.paths\": \"Paths:\",\n \"chart.runtime.flow.startingEntities\": \"starting entities\",\n \"chart.runtime.flow.entities\": \"entities\",\n\n \"chart.runtime.kpiDelta.insufficientData\": \"Insufficient Data\",\n \"chart.runtime.kpiDelta.requiresTwoPoints\": \"Delta calculation requires at least 2 data points\",\n \"chart.runtime.kpiDelta.currentPoints\": \"Current data points: {count}\",\n \"chart.runtime.kpiDelta.noVariance\": \"No variance data\",\n\n \"chart.runtime.kpiNumber.noData\": \"No data\",\n\n \"chart.runtime.markdown.noContent\": \"No content\",\n \"chart.runtime.markdown.addContent\": \"Add markdown content in the chart configuration\",\n\n \"chart.runtime.axisFormat.label\": \"Label\",\n \"chart.runtime.axisFormat.autoLabel\": \"Auto-generated label\",\n \"chart.runtime.axisFormat.unit\": \"Unit\",\n \"chart.runtime.axisFormat.custom\": \"Custom\",\n \"chart.runtime.axisFormat.prefix\": \"Prefix\",\n \"chart.runtime.axisFormat.prefixExample\": \"e.g., $\",\n \"chart.runtime.axisFormat.suffix\": \"Suffix\",\n \"chart.runtime.axisFormat.suffixExample\": \"e.g., units\",\n \"chart.runtime.axisFormat.abbreviation\": \"Abbreviation\",\n \"chart.runtime.axisFormat.yes\": \"Yes\",\n \"chart.runtime.axisFormat.no\": \"No\",\n \"chart.runtime.axisFormat.decimals\": \"Decimals\",\n \"chart.runtime.axisFormat.preview\": \"Preview\",\n \"chart.runtime.axisFormat.leftYAxis\": \"Left Y-Axis\",\n \"chart.runtime.axisFormat.rightYAxis\": \"Right Y-Axis\",\n \"chart.runtime.axisFormat.xAxis\": \"X-Axis\",\n\n \"chart.runtime.missingDep.title\": \"Missing Dependency\",\n \"chart.runtime.missingDep.description\": \"The {chartType} chart requires the {packageName} package.\",\n \"chart.runtime.missingDep.restartHint\": \"After installing, restart your development server.\",\n\n \"chart.runtime.unknownChartType\": \"Unknown chart type\",\n \"chart.runtime.unknownChartTypeHint\": \"\\\"{chartType}\\\" is not registered\",\n\n \"chart.runtime.boxPlot.truncated\": \"Data truncated to {max} groups (original: {total})\",\n \"chart.runtime.candlestick.truncated\": \"Showing first {max} candles (total: {total})\",\n \"chart.runtime.bar.hiddenPoints\": \"{count} data point(s) with no values hidden\",\n\n \"chart.runtime.waterfall.increase\": \"Increase\",\n \"chart.runtime.waterfall.decrease\": \"Decrease\",\n \"chart.runtime.waterfall.total\": \"Total\",\n \"chart.runtime.tooltip.noData\": \"No data\",\n \"chart.runtime.tooltip.targetValue\": \"Target Value\",\n\n \"results.toolbar.refreshing\": \"Refreshing\",\n \"results.toolbar.refresh\": \"Refresh\",\n \"results.toolbar.refreshTitle\": \"Refresh data (Shift+click to bypass cache)\",\n \"results.toolbar.refreshingTitle\": \"Refreshing...\",\n \"results.toolbar.cacheBustTitle\": \"Click to refresh and bypass cache\",\n \"results.toolbar.clear\": \"Clear\",\n \"results.toolbar.clearFunnel\": \"Clear funnel\",\n \"results.toolbar.clearQuery\": \"Clear all query data\",\n \"results.toolbar.aiClose\": \"Close AI assistant\",\n \"results.toolbar.aiOpen\": \"Analyse with AI\",\n \"results.toolbar.shareTitle\": \"Share this analysis\",\n \"results.toolbar.shareCopied\": \"Link copied!\",\n \"results.toolbar.schemaHide\": \"Hide schema diagram\",\n \"results.toolbar.schemaShow\": \"Show schema diagram\",\n \"results.toolbar.debugHide\": \"Hide debug info\",\n \"results.toolbar.debugShow\": \"Show debug info\",\n \"results.toolbar.chartView\": \"Chart view\",\n \"results.toolbar.chartDisabled\": \"Add metrics to enable chart view\",\n \"results.toolbar.tableView\": \"Table view\",\n \"results.toolbar.mergedTableView\": \"Merged table view\",\n \"results.view.table\": \"Table\",\n \"results.warning.filterHint\": \"Consider adding filters to improve performance.\",\n\n \"results.debug.query\": \"Query:\",\n \"results.debug.queryAnalysis\": \"Query Analysis\",\n \"results.debug.copyMarkdownTitle\": \"Copy query, analysis, and SQL as markdown\",\n \"results.debug.copyAsMarkdown\": \"Copy as Markdown\",\n \"results.debug.analysisError\": \"Analysis unavailable due to error\",\n \"results.debug.analysisEmpty\": \"Add metrics to see analysis\",\n \"results.debug.cubeQuery\": \"Cube Query\",\n \"results.debug.cubeQueryExecuted\": \"Executed Query (with funnel filters)\",\n \"results.debug.funnelFilterHint\": \"This query includes an IN filter with binding key values from the previous step\",\n \"results.debug.noQuery\": \"No query\",\n \"results.debug.serverResponse\": \"Server Response\",\n \"results.debug.noResults\": \"No results yet\",\n \"results.debug.chartConfig\": \"Chart Config\",\n \"results.debug.displayConfig\": \"Display Config\",\n \"results.debug.generatedSql\": \"Generated SQL\",\n \"results.debug.loadingSql\": \"Loading SQL...\",\n \"results.debug.executionError\": \"Execution Error\",\n\n \"results.confirm.clearFunnel\": \"Clear Funnel\",\n \"results.confirm.clearQuery\": \"Clear Query\",\n \"results.confirm.clearFunnelMessage\": \"Are you sure you want to clear this funnel? This action cannot be undone.\",\n \"results.confirm.clearQueryMessage\": \"Are you sure you want to clear this query? This action cannot be undone.\",\n\n \"results.table.noData\": \"No data to display\",\n \"results.table.noDataHint\": \"Run a query to see table data\",\n\n \"results.flow.noData\": \"No flow data to display\",\n \"results.flow.noDataHint\": \"Configure flow analysis to see results\",\n \"results.flow.nodes\": \"Nodes ({count})\",\n \"results.flow.transitions\": \"Transitions ({count})\",\n \"results.flow.layer\": \"Layer\",\n \"results.flow.name\": \"Name\",\n \"results.flow.count\": \"Count\",\n \"results.flow.from\": \"From\",\n \"results.flow.to\": \"To\",\n\n \"results.debug.funnel.label\": \"Funnel Query\",\n \"results.debug.funnel.steps\": \"{count} steps\",\n \"results.debug.funnel.serverQuery\": \"Funnel Server Query\",\n \"results.debug.funnel.noQuery\": \"No funnel query configured\",\n \"results.debug.funnel.sqlPlaceholder\": \"Configure funnel binding key to generate SQL\",\n \"results.debug.funnel.stepsTitle\": \"Funnel Steps\",\n\n \"results.debug.flow.label\": \"Flow Query\",\n \"results.debug.flow.badge\": \"{before} before, {after} after\",\n \"results.debug.flow.serverQuery\": \"Flow Server Query\",\n \"results.debug.flow.noQuery\": \"No flow query configured\",\n \"results.debug.flow.sqlPlaceholder\": \"Configure flow to generate SQL\",\n \"results.debug.flow.configTitle\": \"Flow Configuration\",\n \"results.debug.flow.startingStep\": \"Starting Step:\",\n \"results.debug.flow.eventDimension\": \"Event Dimension:\",\n \"results.debug.flow.stepsBefore\": \"Steps Before:\",\n \"results.debug.flow.stepsAfter\": \"Steps After:\",\n \"results.debug.flow.notSet\": \"Not set\",\n \"results.debug.flow.responseTitle\": \"Server Response (Sankey Data)\",\n\n \"results.debug.retention.label\": \"Retention Query\",\n \"results.debug.retention.badge\": \"{segments} segment(s), {users} users\",\n \"results.debug.retention.serverQuery\": \"Retention Server Query\",\n \"results.debug.retention.configIncomplete\": \"Configuration Incomplete\",\n \"results.debug.retention.configHint\": \"Configure the retention analysis settings to generate a query.\",\n \"results.debug.retention.sqlPlaceholder\": \"Configure retention to generate SQL\",\n \"results.debug.retention.configTitle\": \"Retention Configuration\",\n \"results.debug.retention.summaryTitle\": \"Retention Summary\",\n \"results.debug.retention.retentionType\": \"Retention Type:\",\n \"results.debug.retention.periods\": \"Periods:\",\n \"results.debug.retention.granularity\": \"Granularity:\",\n \"results.debug.retention.segments\": \"Segments:\",\n \"results.debug.retention.avgPeriod1\": \"Avg Period 1:\",\n \"results.debug.retention.maxPeriod1\": \"Max Period 1:\",\n \"results.debug.retention.minPeriod1\": \"Min Period 1:\",\n\n \"results.debug.standard.sqlPlaceholder\": \"Add metrics to generate SQL\",\n\n \"flow.tabs.flow\": \"Flow\",\n \"flow.tabs.display\": \"Display\",\n \"flow.tabs.displayUnavailable\": \"Display options not available\",\n \"flow.tabs.displayTitle\": \"Display options\",\n \"flow.visualization.title\": \"Visualization\",\n \"flow.visualization.description\": \"Choose how to visualize the flow data. This affects how data is aggregated.\",\n \"flow.visualization.sankey\": \"Sankey\",\n \"flow.visualization.sankeyHint\": \"Paths can converge\",\n \"flow.visualization.sunburst\": \"Sunburst\",\n \"flow.visualization.sunburstHint\": \"Unique paths only\",\n \"flow.startingStep.title\": \"Starting Step\",\n \"flow.startingStep.description\": \"Define the anchor event from which paths will be explored in both directions.\",\n \"flow.startingStep.filterLabel\": \"Filter Conditions\",\n \"flow.depth.title\": \"Exploration Depth\",\n \"flow.depth.descriptionSankey\": \"How many steps to explore before and after the starting step.\",\n \"flow.depth.descriptionSunburst\": \"How many steps to explore after the starting step.\",\n \"flow.depth.stepsBefore\": \"Steps Before\",\n \"flow.depth.stepsBeforeNA\": \"(N/A)\",\n \"flow.depth.stepsAfter\": \"Steps After\",\n \"flow.depth.performanceWarning\": \"High step depth (4-5) may impact query performance on large datasets.\",\n \"flow.joinStrategy.title\": \"Join Strategy\",\n \"flow.joinStrategy.description\": \"Control how before/after steps are fetched. Switch to window if lateral is slower on your DB.\",\n \"flow.joinStrategy.auto\": \"Auto (prefer lateral if available)\",\n \"flow.joinStrategy.lateral\": \"Lateral (index seeks)\",\n \"flow.joinStrategy.window\": \"Window (ROW_NUMBER)\",\n\n \"retention.tabs.retention\": \"Retention\",\n \"retention.tabs.display\": \"Display\",\n \"retention.tabs.displayUnavailable\": \"Display options not available\",\n \"retention.tabs.displayTitle\": \"Display options\",\n \"retention.dateRange.title\": \"Date Range\",\n \"retention.dateRange.description\": \"Select the date range for cohort entry. Users who first appear within this range will be analyzed.\",\n \"retention.dateRange.label\": \"Date Range\",\n \"retention.dateRange.selectRange\": \"Select date range\",\n \"retention.dateRange.customRange\": \"Custom Range\",\n \"retention.dateRange.applyCustom\": \"Apply Custom Range\",\n \"retention.cohortFilter.title\": \"Cohort Filter\",\n \"retention.cohortFilter.description\": \"Define who enters the cohort. Users whose first event matches these filters within the date range are included.\",\n \"retention.returnFilter.title\": \"Return Filter\",\n \"retention.returnFilter.description\": \"Define what counts as a return. Events matching these filters in subsequent periods count as retention.\",\n \"retention.breakdown.title\": \"Breakdown\",\n \"retention.breakdown.description\": \"Optionally segment retention by dimensions (e.g., country, plan type).\",\n \"retention.settings.title\": \"Settings\",\n \"retention.settings.description\": \"Configure how retention is calculated and displayed.\",\n \"retention.settings.granularityLabel\": \"Period Granularity\",\n \"retention.settings.periodsLabel\": \"Number of Periods ({min}-{max})\",\n \"retention.settings.periodsWarning\": \"High period count may impact query performance.\",\n \"retention.settings.retentionTypeLabel\": \"Retention Type\",\n\n \"debug.explainPlan\": \"Explain Plan\",\n \"debug.explainRunning\": \"Running...\",\n \"debug.explainIncludeTiming\": \"Include timing\",\n \"debug.explainRunningAnalyze\": \"Running EXPLAIN ANALYZE...\",\n \"debug.explainRunningBasic\": \"Running EXPLAIN...\",\n \"debug.explainError\": \"Explain Error:\",\n \"debug.sequentialScans\": \"Sequential Scans Detected\",\n \"debug.indexesUsed\": \"{count} Index Used\",\n \"debug.indexesUsedPlural\": \"{count} Indexes Used\",\n \"debug.executionTime\": \"Execution: {time}ms\",\n \"debug.planningTime\": \"Planning: {time}ms\",\n \"debug.cost\": \"Cost: {cost}\",\n \"debug.indexes\": \"Indexes:\",\n \"debug.executionPlanTitle\": \"Execution Plan ({database})\",\n \"debug.aiAnalyzing\": \"Analyzing...\",\n \"debug.aiAnalysis\": \"AI Analysis\",\n \"debug.aiAnalysisError\": \"AI Analysis Error:\",\n\n \"explainAI.title\": \"AI Performance Analysis\",\n \"explainAI.assessment.good\": \"Good\",\n \"explainAI.assessment.warning\": \"Warning\",\n \"explainAI.assessment.critical\": \"Critical\",\n \"explainAI.summary\": \"Summary\",\n \"explainAI.queryAnalysis\": \"Query Analysis\",\n \"explainAI.issuesFound\": \"Issues Found ({count})\",\n \"explainAI.recommendations\": \"Recommendations ({count})\",\n \"explainAI.noRecommendations\": \"No specific recommendations. The query appears to be well-optimized.\",\n \"explainAI.expectedImpact\": \"Expected impact:\",\n \"explainAI.addToCube\": \"Add to {cubeName} cube:\",\n \"explainAI.modelLabel\": \"Model:\",\n \"explainAI.usingUserKey\": \"(using your API key)\",\n \"explainAI.copied\": \"Copied!\",\n \"explainAI.copy\": \"Copy\",\n \"explainAI.type.index\": \"INDEX\",\n \"explainAI.type.table\": \"TABLE\",\n \"explainAI.type.cube\": \"CUBE\",\n \"explainAI.type.general\": \"TIP\",\n\n \"errorBoundary.modeError\": \"Mode Error\",\n \"errorBoundary.modeErrorDescription\": \"There was a problem with the {mode} mode. This might be due to invalid configuration data.\",\n \"errorBoundary.showDetails\": \"Show error details\",\n \"errorBoundary.unknownError\": \"Unknown error\",\n \"errorBoundary.tryAgain\": \"Try Again\",\n \"errorBoundary.switchToQuery\": \"Switch to Query Mode\",\n\n \"funnel.tabs.steps\": \"Steps\",\n \"funnel.tabs.display\": \"Display\",\n \"funnel.tabs.displayUnavailable\": \"Display options not available\",\n \"funnel.tabs.displayTitle\": \"Display options\",\n \"funnel.steps.title\": \"Funnel Steps\",\n \"funnel.steps.emptyMessage\": \"No steps defined. Add at least 2 steps to create a funnel.\",\n \"funnel.steps.addFirst\": \"Add First Step\",\n \"funnel.steps.addStep\": \"Add Step\",\n \"funnel.steps.validationHint\": \"Add at least one more step to create a valid funnel\",\n \"funnel.step.removeTitle\": \"Remove step\",\n \"funnel.step.editNameTitle\": \"Click to edit name\",\n \"funnel.step.placeholder\": \"Step name\",\n \"funnel.step.timeWindow\": \"Time Window\",\n \"funnel.step.timeWindowHelp\": \"Max time from previous step to qualify\",\n \"funnel.step.filters\": \"{count} filter\",\n \"funnel.step.filtersPlural\": \"{count} filters\",\n \"funnel.step.within\": \"within {time}\",\n \"funnel.step.noFilters\": \"No filters configured\",\n\n \"funnel.config.configuration\": \"Configuration\",\n \"funnel.config.cube\": \"Cube\",\n \"funnel.config.cubeHelp\": \"Select a cube configured for funnel analysis\",\n \"funnel.config.cubePlaceholder\": \"Select event stream cube\",\n \"funnel.config.bindingKey\": \"Binding Key\",\n \"funnel.config.bindingKeyHelp\": \"Entity that connects steps (e.g., user ID, order ID)\",\n \"funnel.config.bindingKeyPlaceholder\": \"Select binding key\",\n \"funnel.config.selectCubeFirst\": \"Select cube first\",\n \"funnel.config.timeDimension\": \"Time Dimension\",\n \"funnel.config.timeDimensionHelp\": \"Timestamp field for step ordering\",\n \"funnel.config.timeDimensionPlaceholder\": \"Select time dimension\",\n \"funnel.config.noMatchingFields\": \"No matching fields found\",\n\n \"funnel.bindingKey.searchPlaceholder\": \"Search dimensions...\",\n \"funnel.bindingKey.noMatching\": \"No matching dimensions found\",\n \"funnel.bindingKey.helpText\": \"Select a dimension that identifies entities across funnel steps (e.g., user ID, order ID)\",\n \"funnel.bindingKey.clearTitle\": \"Clear binding key\",\n\n \"flow.config.configuration\": \"Configuration\",\n \"flow.config.cube\": \"Cube\",\n \"flow.config.cubeHelp\": \"Select a cube configured for flow analysis\",\n \"flow.config.cubePlaceholder\": \"Select event stream cube\",\n \"flow.config.bindingKey\": \"Binding Key\",\n \"flow.config.bindingKeyHelp\": \"Entity that links events together (e.g., user ID)\",\n \"flow.config.bindingKeyPlaceholder\": \"Select binding key\",\n \"flow.config.selectCubeFirst\": \"Select cube first\",\n \"flow.config.timeDimension\": \"Time Dimension\",\n \"flow.config.timeDimensionHelp\": \"Timestamp field for event ordering\",\n \"flow.config.timeDimensionPlaceholder\": \"Select time dimension\",\n \"flow.config.eventDimension\": \"Event Dimension\",\n \"flow.config.eventDimensionHelp\": \"Dimension that categorizes events (node labels in Sankey)\",\n \"flow.config.eventDimensionPlaceholder\": \"Select event dimension\",\n \"flow.config.noMatchingFields\": \"No matching fields found\",\n\n \"retention.config.configuration\": \"Configuration\",\n \"retention.config.cube\": \"Cube\",\n \"retention.config.cubeHelp\": \"Select the cube containing your user events\",\n \"retention.config.cubePlaceholder\": \"Select cube\",\n \"retention.config.bindingKey\": \"Binding Key\",\n \"retention.config.bindingKeyHelp\": \"Dimension that identifies entities across events (e.g., user ID, customer ID)\",\n \"retention.config.bindingKeyPlaceholder\": \"Select user identifier\",\n \"retention.config.selectCubeFirst\": \"Select cube first\",\n \"retention.config.timestamp\": \"Timestamp\",\n \"retention.config.timestampHelp\": \"Timestamp field for cohort entry and activity\",\n \"retention.config.timestampPlaceholder\": \"Select timestamp\",\n \"retention.config.noMatchingFields\": \"No matching fields found\",\n \"retention.config.searchPlaceholder\": \"Search...\",\n\n \"display.showLegend\": \"Show Legend\",\n \"display.showGrid\": \"Show Grid\",\n \"display.showTooltip\": \"Show Tooltip\",\n \"display.stacked\": \"Stacked\",\n \"display.hideHeader\": \"Hide Header\",\n\n \"fieldSearch.aria.closeDialog\": \"Close dialog\",\n \"fieldSearch.aria.filterByCube\": \"Filter by cube\",\n \"fieldSearch.aria.cubeCategories\": \"Cube categories\",\n \"fieldSearch.aria.availableFields\": \"Available fields\",\n\n \"chart.dropZone.required\": \"This field is required\",\n\n \"common.actions.apply\": \"Apply\",\n \"common.actions.done\": \"Done\",\n \"common.actions.update\": \"Update\",\n \"common.actions.retry\": \"Retry\",\n \"common.actions.exit\": \"Exit\",\n \"common.actions.selectAll\": \"Select All\",\n \"common.saving\": \"Saving...\",\n \"common.labels.title\": \"Title\",\n\n \"dashboardFilter.editFilter\": \"Edit Filter\",\n \"dashboardFilter.filterLabel\": \"Filter Label\",\n \"dashboardFilter.enterFilterLabel\": \"Enter filter label\",\n \"dashboardFilter.universalTimeFilter\": \"Universal Time Filter\",\n \"dashboardFilter.universalTimeDescription\": \"This filter applies to all time dimensions in mapped portlets. Users can select the date range when viewing the dashboard.\",\n \"dashboardFilter.field\": \"Field\",\n \"dashboardFilter.showDashboardFields\": \"Show dashboard fields only\",\n \"dashboardFilter.showAllFields\": \"Show all fields\",\n \"dashboardFilter.dashboard\": \"Dashboard\",\n \"dashboardFilter.all\": \"All\",\n \"dashboardFilter.clickToSelectField\": \"Click to select a field\",\n \"dashboardFilter.operator\": \"Operator\",\n \"dashboardFilter.defaultValue\": \"Default Value\",\n \"dashboardFilter.deleteFilter\": \"Delete Filter\",\n \"dashboardFilter.noValueRequired\": \"No value required\",\n \"dashboardFilter.to\": \"to\",\n \"dashboardFilter.selectValue\": \"Select value...\",\n \"dashboardFilter.search\": \"Search...\",\n \"dashboardFilter.errorPrefix\": \"Error: \",\n \"dashboardFilter.noValuesFound\": \"No values found\",\n \"dashboardFilter.enterValue\": \"Enter value...\",\n \"dashboardFilter.enterNumber\": \"Enter number\",\n \"dashboardFilter.min\": \"Min\",\n \"dashboardFilter.max\": \"Max\",\n \"dashboardFilter.filterLabelRequired\": \"Filter label is required\",\n \"dashboardFilter.selectFieldRequired\": \"Please select a field for the filter\",\n \"dashboardFilter.notSet\": \"(not set)\",\n \"dashboardFilter.clickToConfigure\": \"Click to configure\",\n\n \"dashboardFilter.customDate.startDate\": \"Start Date\",\n \"dashboardFilter.customDate.endDate\": \"End Date\",\n \"dashboardFilter.customDate.sinceDate\": \"Since Date\",\n \"dashboardFilter.customDate.fromSelectedToToday\": \"From selected date to today\",\n \"dashboardFilter.customDate.number\": \"Number\",\n \"dashboardFilter.customDate.unit\": \"Unit\",\n \"dashboardFilter.customDate.lastNPreview\": \"Last {number} {unit}\",\n\n \"dashboardFilter.editMode.filters\": \"Filters\",\n \"dashboardFilter.editMode.noFilters\": \"No filters configured. Click \\\"Add\\\" to create one.\",\n \"dashboardFilter.editMode.dateRange\": \"Date Range\",\n \"dashboardFilter.editMode.filter\": \"Filter\",\n \"dashboardFilter.editMode.editFilter\": \"Edit filter\",\n \"dashboardFilter.editMode.removeFilter\": \"Remove filter\",\n\n \"dashboardFilter.filterValue.editValue\": \"Edit value\",\n\n \"dashboardFilter.readOnly.filters\": \"Filters\",\n\n \"filter.shared.fieldsInQuery\": \"Fields in Query ({count})\",\n \"filter.shared.allAvailableFields\": \"All Available Fields ({count})\",\n \"filter.shared.noFieldsMatch\": \"No fields found matching \\\"{searchTerm}\\\"\",\n \"filter.shared.searchFields\": \"Search fields...\",\n \"filter.shared.selectField\": \"Select field...\",\n \"filter.shared.schemaNotLoaded\": \"Schema not loaded\",\n\n \"filter.shared.group.addFilter\": \"Add Filter\",\n \"filter.shared.group.addAndGroup\": \"Add AND Group\",\n \"filter.shared.group.addOrGroup\": \"Add OR Group\",\n \"filter.shared.group.addCondition\": \"Add condition\",\n \"filter.shared.group.noConditions\": \"No conditions in this group.\",\n \"filter.shared.group.addFilterLink\": \"Add a filter\",\n\n \"filter.shared.builder.filters\": \"Filters ({count})\",\n \"filter.shared.builder.clearAll\": \"Clear all\",\n \"filter.shared.builder.addFilter\": \"Add Filter\",\n\n \"filter.shared.dateRange.title\": \"Date Ranges ({count})\",\n \"filter.shared.dateRange.clearAll\": \"Clear all\",\n \"filter.shared.dateRange.addDateRange\": \"Add Date Range\",\n \"filter.shared.dateRange.allHaveDateRanges\": \"All time dimensions already have date ranges\",\n\n \"filter.shared.valueSelector.noValueRequired\": \"No value required\",\n \"filter.shared.valueSelector.to\": \"to\",\n \"filter.shared.valueSelector.min\": \"Min\",\n \"filter.shared.valueSelector.max\": \"Max\",\n \"filter.shared.valueSelector.enterNumber\": \"Enter number\",\n \"filter.shared.valueSelector.loadingValues\": \"Loading values...\",\n \"filter.shared.valueSelector.selectValue\": \"Select value...\",\n \"filter.shared.valueSelector.searchValues\": \"Search values...\",\n \"filter.shared.valueSelector.searching\": \"Searching...\",\n \"filter.shared.valueSelector.errorLoading\": \"Error loading values: {error}\",\n \"filter.shared.valueSelector.noMatchingValues\": \"No matching values\",\n \"filter.shared.valueSelector.noValuesAvailable\": \"No values available\",\n \"filter.shared.valueSelector.enterValue\": \"Enter {type} value\",\n\n \"dashboard.noPortlets\": \"No Portlets\",\n \"dashboard.noPortletsDescription\": \"Add your first portlet to start visualizing your data\",\n \"dashboard.addText\": \"Add Text\",\n \"dashboard.addPortlet\": \"Add Portlet\",\n \"dashboard.finishEditing\": \"Finish Editing\",\n \"dashboard.edit\": \"Edit\",\n \"dashboard.grid\": \"Grid\",\n \"dashboard.rows\": \"Rows\",\n \"dashboard.desktopRequired\": \"Desktop view required for editing\",\n \"dashboard.editModeHint\": \"Drag \\u2022 Resize \\u2022 Auto-save\",\n \"dashboard.filterSelectionMode\": \"Filter Selection Mode - Click portlets to toggle '{filterLabel}'\",\n \"dashboard.filterSelectionEscHint\": \"\\u2022 Press ESC to exit\",\n \"dashboard.editPortlet\": \"Edit Portlet\",\n \"dashboard.addNewPortlet\": \"Add New Portlet\",\n \"dashboard.updatePortlet\": \"Update Portlet\",\n \"dashboard.deletePortlet\": \"Delete Portlet\",\n \"dashboard.deletePortletConfirm\": \"Are you sure you want to delete\",\n \"dashboard.deletePortletSuffix\": \"? This action cannot be undone.\",\n \"dashboard.thisPortlet\": \"this portlet\",\n\n \"dashboard.editModal.dashboardName\": \"Dashboard Name\",\n \"dashboard.editModal.enterDashboardName\": \"Enter dashboard name...\",\n \"dashboard.editModal.descriptionOptional\": \"Description (optional)\",\n \"dashboard.editModal.enterDescription\": \"Enter description...\",\n\n \"portlet.configRequired\": \"Configuration Required\",\n \"portlet.configRequiredHint\": \"Please configure this chart\",\n \"portlet.queryError\": \"Query Error\",\n \"portlet.queryWithFilters\": \"Query (with filters applied)\",\n \"portlet.chartConfig\": \"Chart Config\",\n \"portlet.noDataAvailable\": \"No data available\",\n \"portlet.noDataDrilled\": \"No data points to display for the current filter\",\n \"portlet.invalidQuery\": \"Invalid query or no results\",\n \"portlet.unsupportedChartType\": \"Unsupported chart type\",\n \"portlet.unableToRender\": \"Unable to render chart\",\n \"portlet.enterTitle\": \"Enter portlet title...\",\n \"portlet.enterPortletTitle\": \"Please enter a title for the portlet.\",\n \"portlet.configureQuery\": \"Please configure a query before saving.\",\n \"portlet.configureFlow\": \"Please configure the flow analysis (binding key, time dimension, event dimension, and starting step filter).\",\n \"portlet.configureRetention\": \"Please configure the retention analysis (binding key, time dimension, and date range).\",\n \"portlet.configureFunnel\": \"Please add at least two funnel steps.\",\n \"portlet.addMetricOrBreakdown\": \"Please add at least one metric or breakdown to your query.\",\n\n \"portlet.filterConfig.title\": \"Configure Dashboard Filters\",\n \"portlet.filterConfig.subtitle\": \"Choose which dashboard filters apply to \\\"{portletTitle}\\\"\",\n \"portlet.filterConfig.noFilters\": \"No dashboard filters available\",\n \"portlet.filterConfig.noFiltersHint\": \"Add filters at the dashboard level first\",\n \"portlet.filterConfig.availableFilters\": \"Available Filters\",\n \"portlet.filterConfig.selectedCount\": \"{selected} of {total} selected\",\n \"portlet.filterConfig.applied\": \"Applied\",\n \"portlet.filterConfig.noValue\": \"no value\",\n \"portlet.filterConfig.complexFilter\": \"Complex filter\",\n \"portlet.filterConfig.groupFilter\": \"{type} group with {count} filter\",\n \"portlet.filterConfig.groupFilterPlural\": \"{type} group with {count} filters\",\n \"portlet.filterConfig.applyFilters\": \"Apply Filters\",\n\n \"textPortlet.editText\": \"Edit Text\",\n \"textPortlet.addText\": \"Add Text\",\n \"textPortlet.markdownContent\": \"Markdown Content\",\n \"textPortlet.markdownHint\": \"Supports headers (#), bold (**text**), italic (*text*), links ([text](url)), lists (- item), and horizontal rules (---).\",\n \"textPortlet.preview\": \"Preview\",\n\n \"debug.title\": \"Chart Debug Information\",\n \"debug.chartType\": \"Chart Type\",\n \"debug.fieldAnalysis\": \"Field Analysis\",\n \"debug.chartConfig\": \"Chart Config\",\n \"debug.displayConfig\": \"Display Config\",\n \"debug.queryObject\": \"Query Object\",\n \"debug.dataSample\": \"Data Sample (first 3 rows)\",\n \"debug.cacheStatus\": \"Cache Status\",\n \"debug.cacheHit\": \"Cache Hit\",\n \"debug.cachedAt\": \"Cached At:\",\n \"debug.ttl\": \"TTL:\",\n \"debug.ttlRemaining\": \"TTL Remaining:\",\n \"debug.freshQuery\": \"Fresh Query\",\n \"debug.notFromCache\": \"Result not served from cache\",\n \"debug.escToClose\": \"Press\",\n \"debug.escKey\": \"ESC\",\n \"debug.toClose\": \"to close\",\n \"debug.tooltip\": \"Debug chart configuration\",\n\n \"error.unableToRender\": \"Unable to render chart\",\n \"error.unableToRenderNamed\": \"Unable to render {title}\",\n \"error.renderDescription\": \"There was an error rendering this chart component. The error details are shown below.\",\n \"error.errorLabel\": \"Error:\",\n \"error.typeLabel\": \"Type:\",\n \"error.portletConfig\": \"Portlet Configuration\",\n \"error.cubeQuery\": \"Cube Query\",\n \"error.componentStack\": \"Component Stack\",\n \"error.tryAgain\": \"Try Again\",\n\n \"drill.back\": \"Back\",\n \"drill.goBackOneLevel\": \"Go back one level\",\n \"drill.returnToTop\": \"Return to top level\",\n \"drill.navigateTo\": \"Navigate to {label}\",\n \"drill.empty\": \"(empty)\",\n\n \"analyticsPage.title\": \"Analytics Page - Coming in Phase 4\",\n\n \"dataHistogram.average\": \"Average of {count} values\",\n \"dataHistogram.valuesInRange\": \"{count} values in this range\",\n\n \"mcp.status.connecting\": \"Connecting...\",\n \"mcp.status.loading\": \"Loading...\",\n \"mcp.status.waiting\": \"Waiting for query results...\",\n \"mcp.error.connectionLabel\": \"Connection error:\",\n \"mcp.error.errorLabel\": \"Error:\",\n \"mcp.error.noTextContent\": \"No text content in result\",\n \"mcp.error.invalidResultFormat\": \"Invalid result format: missing data array\",\n \"mcp.error.parseFailed\": \"Failed to parse result: {message}\",\n \"mcp.error.queryFailed\": \"Query failed: {message}\",\n \"mcp.footer.rows\": \"{count} row\",\n \"mcp.footer.rowsPlural\": \"{count} rows\",\n \"mcp.footer.measures\": \"{count} measure\",\n \"mcp.footer.measuresPlural\": \"{count} measures\",\n \"mcp.footer.dimensions\": \"{count} dimension\",\n \"mcp.footer.dimensionsPlural\": \"{count} dimensions\"\n}\n","import en from './locales/en.json'\nimport type { TranslationKey, TranslationParams } from './types'\n\nlet currentLocale = 'en-GB'\nlet messages: Record<string, string> = en\nlet debugMode = false\n\n/**\n * Enable or disable i18n debug mode. When enabled, console.warn is\n * emitted for missing translation keys — helps catch bare strings\n * accidentally passed to t() and genuinely missing keys.\n */\nexport function setDebugMode(enabled: boolean): void {\n debugMode = enabled\n}\n\n/**\n * Translate a key, optionally interpolating ICU {var} parameters.\n * Returns the key path itself if no translation is found (debuggable).\n */\nexport function t(key: TranslationKey, params?: TranslationParams): string {\n const template = messages[key as string]\n if (!template) {\n if (debugMode && typeof console !== 'undefined') {\n console.warn(`[drizzle-cube i18n] Missing translation key: \"${key}\"`)\n }\n return key\n }\n if (!params) return template\n // Simple ICU {var} interpolation — sufficient for Phase 1.\n // For plurals/gender, upgrade to Intl.MessageFormat later.\n return template.replace(/\\{(\\w+)\\}/g, (_, name) => {\n const val = params[name]\n return val !== undefined ? String(val) : `{${name}}`\n })\n}\n\n/**\n * Load a locale dynamically. Each locale JSON becomes a separate chunk\n * via dynamic import() — only the active locale is fetched at runtime.\n * Falls back silently to en-GB if the locale file doesn't exist.\n */\nexport async function loadLocale(locale: string): Promise<void> {\n if (locale === 'nl') {\n locale = 'nl-NL'\n }\n if (locale === 'en-GB' || locale === 'en') {\n currentLocale = 'en-GB'\n messages = en\n return\n }\n try {\n const dict = await import(`./locales/${locale}.json`)\n currentLocale = locale\n messages = { ...en, ...dict.default }\n } catch {\n if (typeof console !== 'undefined') {\n console.warn(`[drizzle-cube] Failed to load locale \"${locale}\", falling back to en-GB`)\n }\n currentLocale = 'en-GB'\n messages = en\n }\n}\n\n/**\n * Set translations directly — for consumer-provided overrides or\n * fully custom locales not shipped with drizzle-cube.\n */\nexport function setTranslations(locale: string, dict: Record<string, string>): void {\n currentLocale = locale\n messages = { ...en, ...dict }\n}\n\n/** Get the current active locale string */\nexport function getLocale(): string {\n return currentLocale\n}\n\n/** Get the current merged messages (for React context) */\nexport function getMessages(): Record<string, string> {\n return messages\n}\n\n/**\n * Create a namespace-scoped translator. Keys are prefix-filtered:\n * `createTranslator('chart.bar')` → `t('label')` resolves to `chart.bar.label`\n */\nexport function createTranslator(ns: string) {\n return (key: string, params?: TranslationParams): string =>\n t(`${ns}.${key}` as TranslationKey, params)\n}\n","import { createContext, useEffect, useState, type ReactNode } from 'react'\nimport { loadLocale, setTranslations, getLocale, getMessages, t as globalT } from '../../i18n/runtime'\nimport type { TranslationKey, TranslationParams } from '../../i18n/types'\n\nexport interface I18nContextValue {\n t: (key: TranslationKey, params?: TranslationParams) => string\n locale: string\n}\n\nexport const I18nContext = createContext<I18nContextValue>({\n t: globalT,\n locale: 'en-GB',\n})\n\ninterface I18nProviderProps {\n locale?: string\n translations?: Record<string, string>\n children: ReactNode\n}\n\n/**\n * I18nProvider — loads the requested locale and optionally merges consumer overrides.\n * Wraps children with a React context providing `t()` and `locale`.\n */\nexport function I18nProvider({ locale = 'en-GB', translations, children }: I18nProviderProps) {\n const [ready, setReady] = useState(false)\n const [currentLocale, setCurrentLocale] = useState(getLocale())\n\n useEffect(() => {\n let cancelled = false\n\n const init = async () => {\n await loadLocale(locale)\n if (translations && !cancelled) {\n // Merge consumer overrides on top of the loaded locale\n const merged = { ...getMessages(), ...translations }\n setTranslations(locale, merged)\n }\n if (!cancelled) {\n setCurrentLocale(getLocale())\n setReady(true)\n }\n }\n init()\n\n return () => { cancelled = true }\n }, [locale, translations])\n\n // Render children immediately with current translations — no loading spinner.\n // The default locale (en-GB) is statically bundled and always available.\n const value: I18nContextValue = {\n t: globalT,\n locale: ready ? currentLocale : locale,\n }\n\n return (\n <I18nContext.Provider value={value}>\n {children}\n </I18nContext.Provider>\n )\n}\n","import { useContext, useCallback } from 'react'\nimport { I18nContext } from '../providers/I18nProvider'\nimport type { TranslationKey, TranslationParams } from '../../i18n/types'\n\n/**\n * Hook to access translations within React components.\n *\n * @param namespace - Optional namespace prefix for scoped translations.\n * When provided, keys are prefixed: `useTranslation('chart.bar')` →\n * `t('label')` resolves to `chart.bar.label`.\n *\n * @example\n * ```tsx\n * const { t, locale } = useTranslation()\n * t('common.actions.save') // → \"Save\"\n *\n * const { t } = useTranslation('chart.bar')\n * t('label') // → \"Bar Chart\"\n * ```\n */\nexport function useTranslation(namespace?: string) {\n const { t: globalT, locale } = useContext(I18nContext)\n\n const t = useCallback(\n (key: string, params?: TranslationParams): string => {\n const fullKey = namespace ? `${namespace}.${key}` : key\n return globalT(fullKey as TranslationKey, params)\n },\n [globalT, namespace]\n )\n\n return { t, locale }\n}\n","import type { FieldLabelMap, AxisFormatConfig } from '../types'\n\n// Utility function to check if a value is a valid numeric value (not null, undefined, or NaN)\n// This is used to preserve null values instead of converting them to 0\nexport function isValidNumericValue(value: any): boolean {\n return value !== null && value !== undefined && !isNaN(Number(value))\n}\n\n// Utility function to parse numeric value from data, preserving nulls\n// Returns null for null/undefined/NaN values, otherwise returns the numeric value\nexport function parseNumericValue(value: any): number | null {\n if (value === null || value === undefined) return null\n const num = typeof value === 'string' ? parseFloat(value) : Number(value)\n return isNaN(num) ? null : num\n}\n\n// Utility function to format numeric values for display in charts\n// Rounds to at most 2 decimal places, preserves integers\nexport function formatNumericValue(value: any): string {\n if (value === null || value === undefined) return 'No data'\n const num = typeof value === 'number' ? value : parseFloat(value)\n if (isNaN(num)) return String(value)\n if (Number.isInteger(num)) return num.toLocaleString()\n // Round to at most 2 decimal places, remove trailing zeros\n return parseFloat(num.toFixed(2)).toLocaleString()\n}\n\n/**\n * Format a numeric value for axis/tooltip display with configurable formatting\n *\n * @param value - The numeric value to format\n * @param config - Optional formatting configuration\n * @param locale - Optional locale string (defaults to browser locale)\n * @returns Formatted string representation of the value\n *\n * @example\n * formatAxisValue(1250000, { unit: 'currency', abbreviate: true }) // \"$1.25M\"\n * formatAxisValue(0.75, { unit: 'percent', decimals: 1 }) // \"75.0%\"\n * formatAxisValue(1234567, { abbreviate: true, decimals: 2 }) // \"1.23M\"\n */\nexport function formatAxisValue(\n value: number | null | undefined,\n config?: AxisFormatConfig,\n locale?: string\n): string {\n // Handle null/undefined\n if (value === null || value === undefined) {\n return 'No data'\n }\n\n // Handle non-numeric values\n const num = typeof value === 'number' ? value : parseFloat(String(value))\n if (isNaN(num)) {\n return String(value)\n }\n\n // Handle special cases\n if (!isFinite(num)) {\n return num > 0 ? '∞' : '-∞'\n }\n\n // Get locale (default to browser locale)\n const effectiveLocale = locale || (typeof navigator !== 'undefined' ? navigator.language : 'en-US')\n\n // If no config provided, use default formatting\n if (!config) {\n return formatNumericValue(value)\n }\n\n const { unit, abbreviate = true, decimals, customPrefix, customSuffix } = config\n\n // Calculate the display value and suffix for abbreviation\n // Default to true for abbreviation when config is provided\n let displayValue = num\n let abbreviationSuffix = ''\n\n if (abbreviate) {\n const absNum = Math.abs(num)\n if (absNum >= 1_000_000_000) {\n displayValue = num / 1_000_000_000\n abbreviationSuffix = 'B'\n } else if (absNum >= 1_000_000) {\n displayValue = num / 1_000_000\n abbreviationSuffix = 'M'\n } else if (absNum >= 1_000) {\n displayValue = num / 1_000\n abbreviationSuffix = 'K'\n }\n }\n\n // Determine decimal places\n // If decimals is undefined, use auto (2 for non-integers, 0 for integers after abbreviation)\n const effectiveDecimals = decimals !== undefined\n ? decimals\n : (Number.isInteger(displayValue) ? 0 : 2)\n\n // Format based on unit type\n switch (unit) {\n case 'currency': {\n // Use Intl.NumberFormat for currency\n // Currency code is determined by locale (USD for en-US, EUR for de-DE, etc.)\n const currencyCode = getCurrencyCodeForLocale(effectiveLocale)\n\n if (abbreviate && abbreviationSuffix) {\n // For abbreviated currency, format the number part and add suffix\n const formatted = new Intl.NumberFormat(effectiveLocale, {\n style: 'currency',\n currency: currencyCode,\n minimumFractionDigits: effectiveDecimals,\n maximumFractionDigits: effectiveDecimals,\n }).format(displayValue)\n // Insert abbreviation suffix before any trailing currency symbol or at end\n // Handle both \"$1.25\" -> \"$1.25M\" and \"1.25 €\" -> \"1.25M €\"\n const parts = new Intl.NumberFormat(effectiveLocale, {\n style: 'currency',\n currency: currencyCode,\n }).formatToParts(displayValue)\n const hasTrailingCurrency = parts[parts.length - 1]?.type === 'currency'\n if (hasTrailingCurrency) {\n // Currency symbol is at the end (e.g., \"1.25 €\")\n return formatted.replace(/(\\s*[^\\d\\s]+)$/, abbreviationSuffix + '$1')\n }\n return formatted + abbreviationSuffix\n }\n\n return new Intl.NumberFormat(effectiveLocale, {\n style: 'currency',\n currency: currencyCode,\n minimumFractionDigits: effectiveDecimals,\n maximumFractionDigits: effectiveDecimals,\n }).format(displayValue)\n }\n\n case 'percent': {\n // Format as percentage (multiply by 100 if value is 0-1 range, otherwise use as-is)\n // Assume values > 1 are already percentages, values <= 1 need multiplication\n const percentValue = Math.abs(displayValue) <= 1 && !abbreviate ? displayValue * 100 : displayValue\n const formatted = new Intl.NumberFormat(effectiveLocale, {\n minimumFractionDigits: effectiveDecimals,\n maximumFractionDigits: effectiveDecimals,\n }).format(percentValue)\n return formatted + abbreviationSuffix + '%'\n }\n\n case 'custom': {\n // Apply custom prefix/suffix\n const prefix = customPrefix || ''\n const suffix = customSuffix || ''\n const formatted = new Intl.NumberFormat(effectiveLocale, {\n minimumFractionDigits: effectiveDecimals,\n maximumFractionDigits: effectiveDecimals,\n }).format(displayValue)\n return prefix + formatted + abbreviationSuffix + suffix\n }\n\n case 'number':\n default: {\n // Standard number formatting with locale-aware grouping\n const formatted = new Intl.NumberFormat(effectiveLocale, {\n minimumFractionDigits: effectiveDecimals,\n maximumFractionDigits: effectiveDecimals,\n }).format(displayValue)\n return formatted + abbreviationSuffix\n }\n }\n}\n\n/**\n * Get the currency code for a given locale\n * Maps common locales to their default currency\n */\nfunction getCurrencyCodeForLocale(locale: string): string {\n // Extract language and region from locale (e.g., \"en-US\" -> [\"en\", \"US\"])\n const parts = locale.split('-')\n const region = parts[1]?.toUpperCase()\n\n // Map regions to currencies\n const currencyMap: Record<string, string> = {\n 'US': 'USD',\n 'CA': 'CAD',\n 'GB': 'GBP',\n 'UK': 'GBP',\n 'AU': 'AUD',\n 'NZ': 'NZD',\n 'EU': 'EUR',\n 'DE': 'EUR',\n 'FR': 'EUR',\n 'IT': 'EUR',\n 'ES': 'EUR',\n 'NL': 'EUR',\n 'BE': 'EUR',\n 'AT': 'EUR',\n 'IE': 'EUR',\n 'PT': 'EUR',\n 'FI': 'EUR',\n 'JP': 'JPY',\n 'CN': 'CNY',\n 'KR': 'KRW',\n 'IN': 'INR',\n 'BR': 'BRL',\n 'MX': 'MXN',\n 'CH': 'CHF',\n 'SE': 'SEK',\n 'NO': 'NOK',\n 'DK': 'DKK',\n 'PL': 'PLN',\n 'RU': 'RUB',\n 'ZA': 'ZAR',\n 'SG': 'SGD',\n 'HK': 'HKD',\n 'TW': 'TWD',\n 'TH': 'THB',\n 'MY': 'MYR',\n 'PH': 'PHP',\n 'ID': 'IDR',\n 'VN': 'VND',\n 'AE': 'AED',\n 'SA': 'SAR',\n 'IL': 'ILS',\n 'TR': 'TRY',\n }\n\n return currencyMap[region] || 'USD'\n}\n\n/**\n * Create a tick formatter function for Recharts axes\n * Returns a function that can be used as tickFormatter prop\n */\nexport function createAxisTickFormatter(config?: AxisFormatConfig): (value: any) => string {\n return (value: any) => formatAxisValue(value, config)\n}\n\n// Utility function to get field label from field name\nexport function getFieldLabel(fieldName: string, labelMap: FieldLabelMap): string {\n return labelMap[fieldName] || fieldName\n}\n\n// Utility function to transform series keys to use labels\nexport function transformSeriesKeysWithLabels(seriesKeys: string[], labelMap: FieldLabelMap): string[] {\n return seriesKeys.map(key => getFieldLabel(key, labelMap))\n}\n\n// Utility function to format time values for better display using known granularity\nexport function formatTimeValue(value: any, granularity?: string): string {\n\n if (!value) return 'Unknown'\n \n const str = String(value)\n \n // Check if it's a timestamp (ISO format or PostgreSQL format)\n // Handles formats like: \"2025-04-01T00:00:00.000\" or \"2023-02-01 00:00:00+00\"\n if (str.match(/^\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}/)) {\n // Convert PostgreSQL format to ISO format if needed\n let isoStr = str\n if (str.includes(' ')) {\n // Convert \"2023-02-01 00:00:00+00\" to \"2023-02-01T00:00:00Z\"\n isoStr = str.replace(' ', 'T').replace('+00', 'Z').replace(/\\+\\d{2}:\\d{2}$/, 'Z')\n }\n // Ensure the timestamp ends with 'Z' if not present\n if (!isoStr.endsWith('Z') && !isoStr.includes('+')) {\n isoStr = isoStr + 'Z'\n }\n const date = new Date(isoStr)\n \n // Ensure we're working with valid date\n if (isNaN(date.getTime())) {\n return str\n }\n \n // Use UTC methods on the properly UTC-parsed date\n const year = date.getUTCFullYear()\n const month = String(date.getUTCMonth() + 1).padStart(2, '0')\n const day = String(date.getUTCDate()).padStart(2, '0')\n const hours = date.getUTCHours()\n const minutes = date.getUTCMinutes()\n \n // Format based on known granularity if provided\n if (granularity) {\n switch (granularity.toLowerCase()) {\n case 'year':\n return `${year}`\n case 'quarter': {\n const quarter = Math.floor(date.getUTCMonth() / 3) + 1\n return `${year}-Q${quarter}`\n }\n case 'month':\n return `${year}-${month}`\n case 'week':\n // For week, we could calculate week number, but let's use date for simplicity\n return `${year}-${month}-${day}`\n case 'day':\n return `${year}-${month}-${day}`\n case 'hour':\n return `${year}-${month}-${day} ${String(hours).padStart(2, '0')}:00`\n case 'minute':\n return `${year}-${month}-${day} ${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`\n default:\n // Unknown granularity, fall back to heuristic\n break\n }\n }\n \n // Fallback heuristic if granularity not provided or unknown\n const seconds = date.getUTCSeconds()\n const milliseconds = date.getUTCMilliseconds()\n \n // If it's the first day of the month at exactly midnight UTC, it's likely a month granularity\n if (day === '01' && hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 0) {\n // Check if it's also first month of a quarter (quarter granularity)\n if (month === '01' || month === '04' || month === '07' || month === '10') {\n const quarter = Math.floor(date.getUTCMonth() / 3) + 1\n return `${year}-Q${quarter}`\n }\n // Month granularity\n return `${year}-${month}`\n }\n \n // If it's exactly midnight UTC, it's likely a day granularity\n if (hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 0) {\n return `${year}-${month}-${day}`\n }\n \n // If it has time components, include them (hour/minute granularity)\n if (minutes === 0 && seconds === 0 && milliseconds === 0) {\n // Hour granularity\n return `${year}-${month}-${day} ${String(hours).padStart(2, '0')}:00`\n }\n \n // Full timestamp\n return `${year}-${month}-${day} ${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`\n }\n \n // Return as-is if not a timestamp\n return str\n}\n\n// Helper function to get granularity for a field from the query timeDimensions\nexport function getFieldGranularity(queryObject: any, fieldName: string): string | undefined {\n try {\n if (queryObject?.timeDimensions) {\n // Find the timeDimension that matches this field\n const timeDim = queryObject.timeDimensions.find((td: any) => {\n // Check if field name matches the dimension or dimension with granularity suffix\n return fieldName === td.dimension || \n fieldName.startsWith(td.dimension.replace('.', '_')) ||\n fieldName === `${td.dimension}_${td.granularity}`\n })\n \n if (timeDim?.granularity) {\n return timeDim.granularity\n }\n }\n \n // Fallback: extract granularity from field name suffix if present\n const granularityMatch = fieldName.match(/_([a-z]+)$/)\n if (granularityMatch) {\n const suffix = granularityMatch[1]\n // Only return if it's a valid granularity\n if (['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second'].includes(suffix)) {\n return suffix\n }\n }\n \n return undefined\n } catch {\n return undefined\n }\n}\n\n// Transform data for charts with proper type handling\n// NOTE: Preserves null values to allow charts to handle gaps/missing data appropriately\nexport function transformChartData(\n data: any[],\n xAxisField: string,\n yAxisFields: string[],\n queryObject: any,\n getFieldLabelFn: (fieldName: string) => string = (fieldName) => fieldName\n) {\n if (!data || data.length === 0) return []\n\n const granularity = getFieldGranularity(queryObject, xAxisField)\n\n return data.map((row: any) => {\n const transformed: any = {\n name: formatTimeValue(row[xAxisField], granularity) || row[xAxisField] || 'Unknown',\n }\n\n yAxisFields.forEach(field => {\n const displayName = getFieldLabelFn(field)\n if (displayName === '__proto__' || displayName === 'constructor' || displayName === 'prototype') return\n // Preserve null values instead of converting to 0\n transformed[displayName] = parseNumericValue(row[field])\n })\n\n return transformed\n })\n}\n\nexport interface ChartSeriesResult {\n data: any[]\n seriesKeys: string[]\n hasDimensions: boolean\n}\n\n// Advanced data transformation that handles both measures and dimensions on Y-axis\n// NOTE: Preserves null values to allow charts to handle gaps/missing data appropriately\nexport function transformChartDataWithSeries(\n data: any[],\n xAxisField: string,\n yAxisFields: string[],\n queryObject: any,\n seriesFields?: string[], // New optional parameter for explicit series fields\n getFieldLabelFn: (fieldName: string) => string = (fieldName) => fieldName // Function to get field labels\n): ChartSeriesResult {\n if (!data || data.length === 0) {\n return { data: [], seriesKeys: [], hasDimensions: false }\n }\n\n const originalQuery = queryObject || {}\n const queryDimensions = [\n ...(originalQuery.dimensions || []),\n ...(originalQuery.timeDimensions?.map((td: any) => td.dimension) || [])\n ]\n const queryMeasures = originalQuery.measures || []\n\n // Use explicit series fields if provided, otherwise no dimension-based series\n const yAxisMeasures = yAxisFields.filter(field => queryMeasures.includes(field))\n const yAxisDimensions = (seriesFields || []).filter(field => queryDimensions.includes(field))\n\n // Handle complex case with dimensions on Y-axis\n if (yAxisDimensions.length > 0) {\n // Group data by X-axis field and create separate series for dimension values\n const groupedData: { [key: string]: any } = {}\n\n data.forEach((row: any) => {\n const granularity = getFieldGranularity(queryObject, xAxisField)\n const xValue = formatTimeValue(row[xAxisField], granularity) || row[xAxisField] || 'Unknown'\n if (xValue === '__proto__' || xValue === 'constructor' || xValue === 'prototype') return\n if (!groupedData[xValue]) {\n groupedData[xValue] = { name: String(xValue) }\n }\n\n // Add measures - preserve nulls for individual measures\n yAxisMeasures.forEach(measure => {\n const displayName = getFieldLabelFn(measure)\n if (displayName === '__proto__' || displayName === 'constructor' || displayName === 'prototype') return\n const measureValue = parseNumericValue(row[measure])\n\n // For aggregation: sum non-null values, preserve null if all are null\n if (measureValue !== null) {\n const currentValue = groupedData[xValue][displayName]\n\n groupedData[xValue][displayName] = (currentValue === null || currentValue === undefined)\n ? measureValue\n : currentValue + measureValue\n } else if (!(displayName in groupedData[xValue])) {\n // Only set to null if no value exists yet\n groupedData[xValue][displayName] = null\n }\n })\n\n // Add dimensions as separate series (aggregate measure values by dimension)\n yAxisDimensions.forEach(dimension => {\n const dimValue = row[dimension]\n if (dimValue !== undefined && dimValue !== null) {\n const seriesName = String(dimValue)\n // Aggregate the first measure for this dimension value, or use totalCost if available\n const measureToAggregate = yAxisMeasures[0] || queryMeasures.find((m: string) =>\n m.includes('totalCost') || m.includes('count') || m.includes('sum')\n ) || queryMeasures[0]\n\n if (measureToAggregate) {\n if (seriesName === '__proto__' || seriesName === 'constructor' || seriesName === 'prototype') return\n const measureValue = parseNumericValue(row[measureToAggregate])\n\n // For dimension series: sum non-null values, preserve null if all are null\n if (measureValue !== null) {\n const currentValue = groupedData[xValue][seriesName]\n\n groupedData[xValue][seriesName] = (currentValue === null || currentValue === undefined)\n ? measureValue\n : currentValue + measureValue\n } else if (!(seriesName in groupedData[xValue])) {\n // Only set to null if no value exists yet\n groupedData[xValue][seriesName] = null\n }\n }\n }\n })\n })\n \n const chartData = Object.values(groupedData)\n \n // Get all series keys for rendering\n // When dimensions are on Y-axis, only show dimension series, not measures\n // The measures are the values being aggregated for each dimension series\n const dimensionSeries = Array.from(new Set(\n data.flatMap((row: any) => \n yAxisDimensions.map(dimension => {\n const value = row[dimension]\n return value !== undefined && value !== null \n ? String(value)\n : null\n }).filter((value): value is string => value !== null)\n )\n ))\n \n return {\n data: chartData,\n seriesKeys: dimensionSeries,\n hasDimensions: true\n }\n }\n \n // Standard measures-only path\n const chartData = transformChartData(data, xAxisField, yAxisFields, queryObject, getFieldLabelFn)\n const seriesKeys = yAxisFields.map(field => getFieldLabelFn(field))\n \n return {\n data: chartData,\n seriesKeys,\n hasDimensions: false\n }\n}","/**\n * Optimized hook that only subscribes to field label functionality\n * from CubeMeta context. This prevents re-renders when unrelated\n * contexts (CubeApi, Features) change.\n *\n * Use this instead of useCubeContext() when you only need getFieldLabel.\n */\n\nimport { useContext, useMemo } from 'react'\nimport { CubeMetaContext, type CubeMetaContextValue } from '../providers/CubeMetaContext'\n\n/**\n * Returns a stable reference to the getFieldLabel function.\n * Components using this hook will only re-render when the field label\n * mapping actually changes, not when unrelated API or feature contexts update.\n *\n * @returns Function to get human-readable label for a field name\n * @throws Error if used outside CubeProvider\n */\nexport function useCubeFieldLabel(): (fieldName: string) => string {\n const context = useContext(CubeMetaContext) as CubeMetaContextValue | null\n\n if (!context) {\n throw new Error('useCubeFieldLabel must be used within CubeProvider')\n }\n\n // Return stable reference - only changes when getFieldLabel itself changes\n return useMemo(() => context.getFieldLabel, [context.getFieldLabel])\n}\n","/**\n * Theme utilities and TypeScript types for drizzle-cube theming system\n */\n\n/**\n * Semantic color tokens used throughout drizzle-cube components\n */\nexport interface ThemeColorTokens {\n // Surface colors\n surface: string\n surfaceSecondary: string\n surfaceTertiary: string\n surfaceHover: string\n\n // Text colors\n text: string\n textSecondary: string\n textMuted: string\n textDisabled: string\n\n // Border colors\n border: string\n borderSecondary: string\n borderHover: string\n\n // Primary colors\n primary: string\n primaryHover: string\n primaryContent: string\n\n // Semantic state colors\n success: string\n successBg: string\n successBorder: string\n\n warning: string\n warningBg: string\n warningBorder: string\n\n error: string\n errorBg: string\n errorBorder: string\n\n info: string\n infoBg: string\n infoBorder: string\n\n // Danger colors\n danger: string\n dangerHover: string\n dangerBg: string\n\n // Overlay colors\n overlay: string\n overlayLight: string\n}\n\n/**\n * Theme configuration interface\n */\nexport interface ThemeConfig {\n name: string\n colors: Partial<ThemeColorTokens>\n}\n\n/**\n * Get the current value of a theme CSS variable\n */\nexport function getThemeVariable(variableName: string): string {\n if (typeof window === 'undefined') return ''\n\n return getComputedStyle(document.documentElement)\n .getPropertyValue(`--dc-${variableName}`)\n .trim()\n}\n\n/**\n * Set a theme CSS variable\n */\nexport function setThemeVariable(variableName: string, value: string): void {\n if (typeof window === 'undefined') return\n\n document.documentElement.style.setProperty(`--dc-${variableName}`, value)\n}\n\n/**\n * Apply a complete theme configuration\n */\nexport function applyTheme(theme: ThemeConfig): void {\n if (typeof window === 'undefined') return\n\n Object.entries(theme.colors).forEach(([key, value]) => {\n if (value) {\n // Convert camelCase to kebab-case\n const cssVarName = key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)\n setThemeVariable(cssVarName, value)\n }\n })\n}\n\n/**\n * Reset theme to defaults by removing custom properties\n */\nexport function resetTheme(): void {\n if (typeof window === 'undefined') return\n\n const styleElement = document.documentElement.style\n const properties = Array.from(styleElement)\n\n properties.forEach(prop => {\n if (prop.startsWith('--dc-')) {\n styleElement.removeProperty(prop)\n }\n })\n}\n\n/**\n * Theme type definition\n */\nexport type Theme = 'light' | 'dark' | 'neon'\n\n/**\n * Get the current theme\n */\nexport function getTheme(): Theme {\n if (typeof window === 'undefined') return 'light'\n\n // Check localStorage first\n const stored = localStorage.getItem('theme')\n if (stored === 'dark' || stored === 'neon' || stored === 'light') {\n return stored\n }\n\n // Check for data-theme attribute\n const dataTheme = document.documentElement.getAttribute('data-theme')\n if (dataTheme === 'dark' || dataTheme === 'neon') {\n return dataTheme\n }\n\n // Check for dark class\n if (document.documentElement.classList.contains('dark') ||\n document.body.classList.contains('dark')) {\n return 'dark'\n }\n\n // Check for neon class\n if (document.documentElement.classList.contains('neon') ||\n document.body.classList.contains('neon')) {\n return 'neon'\n }\n\n // Check system preference\n if (window.matchMedia('(prefers-color-scheme: dark)').matches) {\n return 'dark'\n }\n\n return 'light'\n}\n\n/**\n * Set the theme\n */\nexport function setTheme(theme: Theme): void {\n if (typeof window === 'undefined') return\n\n // Remove all theme classes\n document.documentElement.classList.remove('dark', 'neon')\n\n // Set data-theme attribute\n document.documentElement.setAttribute('data-theme', theme)\n\n // Add appropriate class for backwards compatibility\n if (theme === 'dark' || theme === 'neon') {\n document.documentElement.classList.add(theme)\n }\n\n // Persist to localStorage\n localStorage.setItem('theme', theme)\n}\n\n/**\n * Detect if dark mode is currently active (backwards compatibility)\n * @deprecated Use getTheme() instead\n */\nexport function isDarkMode(): boolean {\n const theme = getTheme()\n return theme === 'dark' || theme === 'neon'\n}\n\n/**\n * Watch for theme changes\n */\nexport function watchThemeChanges(callback: (theme: Theme) => void): () => void {\n if (typeof window === 'undefined') return () => {}\n\n // Watch for class changes on html element\n const observer = new MutationObserver(() => {\n callback(getTheme())\n })\n\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'data-theme']\n })\n\n // Watch for system preference changes\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n const mediaListener = () => callback(getTheme())\n mediaQuery.addEventListener('change', mediaListener)\n\n // Return cleanup function\n return () => {\n observer.disconnect()\n mediaQuery.removeEventListener('change', mediaListener)\n }\n}\n\n/**\n * Example theme configurations\n */\nexport const THEME_PRESETS = {\n light: {\n name: 'light' as const,\n colors: {\n surface: '#ffffff',\n surfaceSecondary: '#f9fafb',\n text: '#111827',\n textSecondary: '#374151',\n textMuted: '#6b7280',\n border: '#e5e7eb',\n primary: '#3b82f6',\n primaryHover: '#2563eb',\n }\n },\n dark: {\n name: 'dark' as const,\n colors: {\n surface: '#1e293b',\n surfaceSecondary: '#334155',\n text: '#f1f5f9',\n textSecondary: '#e2e8f0',\n textMuted: '#cbd5e1',\n border: '#475569',\n primary: '#60a5fa',\n primaryHover: '#3b82f6',\n }\n },\n neon: {\n name: 'neon' as const,\n colors: {\n surface: '#0a0118',\n surfaceSecondary: '#1a0f2e',\n surfaceTertiary: '#2a1f3e',\n text: '#ffffff',\n textSecondary: '#e0e0ff',\n textMuted: '#b0b0d0',\n border: '#ff00ff',\n borderSecondary: '#00ffff',\n primary: '#00ffff',\n primaryHover: '#00cccc',\n primaryContent: '#000000',\n success: '#00ff00',\n warning: '#ffff00',\n error: '#ff0066',\n info: '#00ffff',\n danger: '#ff1493',\n }\n }\n} as const\n","// Default color palette for charts - used as fallback when no dashboard palette is specified\n// These are now part of the 'default' palette in the unified color palette system\nexport const CHART_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\n// Default gradient colors for continuous numeric scales - used as fallback\n// These are now part of the 'default' palette in the unified color palette system\nexport const CHART_COLORS_GRADIENT = [\n '#440154', // dark purple\n '#414487', // purple-blue\n '#2a788e', // teal\n '#22a884', // green-teal \n '#7ad151', // green\n '#fde725', // yellow\n]\n\n// Colors for positive/negative values\nexport const POSITIVE_COLOR = '#10b981' // green\nexport const NEGATIVE_COLOR = '#ef4444' // red\n\nexport const CHART_MARGINS = {\n top: 5,\n right: 30,\n left: 20,\n bottom: 5\n}\n\nexport const RESPONSIVE_CHART_MARGINS = {\n top: 5,\n right: 30,\n left: 20,\n bottom: 60 // Extra space for rotated labels\n}","import React, { useEffect, useLayoutEffect, useRef, useState, useMemo, useCallback } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation'\nimport { select, scaleQuantize, max, min, transition as _transition } from 'd3'\n// _transition import is for side effects only - it extends Selection.prototype with .transition() method\nimport { CHART_COLORS_GRADIENT, CHART_MARGINS } from '../../utils/chartConstants'\nimport { formatTimeValue } from '../../utils/chartUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport { getTheme, watchThemeChanges, type Theme } from '../../theme'\nimport type { ChartProps } from '../../types'\n\ninterface GridCell {\n x: number\n y: number\n value: number\n date: Date\n label: string\n}\n\ninterface GridMapping {\n extractX: (date: Date) => number\n extractY: (date: Date) => number\n xLabels: string[]\n yLabels: string[]\n xFormat: (value: number) => string\n yFormat: (value: number) => string\n cellWidth: number\n cellHeight: number\n hasHierarchicalLabels?: boolean\n getYearFromX?: (value: number) => number\n}\n\nconst ActivityGridChart = React.memo(function ActivityGridChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const { t } = useTranslation()\n const svgRef = useRef<SVGSVGElement | null>(null)\n const containerRef = useRef<HTMLDivElement | null>(null)\n const [dimensions, setDimensions] = useState({ width: 0, height: 0 })\n const [dimensionsReady, setDimensionsReady] = useState(false)\n const [currentTheme, setCurrentTheme] = useState<Theme>('light')\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n\n // Watch for theme changes\n useEffect(() => {\n setCurrentTheme(getTheme())\n const unwatch = watchThemeChanges((theme) => {\n setCurrentTheme(theme)\n })\n return unwatch\n }, [])\n\n const safeDisplayConfig = useMemo(() => ({\n showTooltip: displayConfig?.showTooltip ?? true,\n showLabels: displayConfig?.showLabels ?? true,\n fitToWidth: displayConfig?.fitToWidth ?? false\n }), [displayConfig?.showTooltip, displayConfig?.showLabels, displayConfig?.fitToWidth])\n\n // Enhanced dimension measurement with retry mechanism\n useLayoutEffect(() => {\n let retryCount = 0\n const maxRetries = 10\n let rafId: number\n let timeoutId: ReturnType<typeof setTimeout>\n \n const updateDimensions = () => {\n if (containerRef.current) {\n const { width, height } = containerRef.current.getBoundingClientRect()\n \n if (width > 0 && height > 0) {\n setDimensions({ width, height })\n setDimensionsReady(true)\n return true\n }\n }\n return false\n }\n \n const success = updateDimensions()\n \n if (!success && retryCount < maxRetries) {\n const retryWithRaf = () => {\n const rafSuccess = updateDimensions()\n \n if (!rafSuccess && retryCount < maxRetries) {\n retryCount++\n timeoutId = setTimeout(() => {\n rafId = requestAnimationFrame(retryWithRaf)\n }, 50 * retryCount)\n }\n }\n \n rafId = requestAnimationFrame(retryWithRaf)\n }\n \n return () => {\n if (rafId) cancelAnimationFrame(rafId)\n if (timeoutId) clearTimeout(timeoutId)\n }\n }, [])\n\n // ResizeObserver for dynamic resizing\n useEffect(() => {\n let resizeObserver: ResizeObserver | null = null\n \n const updateDimensions = () => {\n if (containerRef.current) {\n const { width, height } = containerRef.current.getBoundingClientRect()\n if (width > 0 && height > 0) {\n setDimensions({ width, height })\n if (!dimensionsReady) {\n setDimensionsReady(true)\n }\n }\n }\n }\n \n if (containerRef.current) {\n resizeObserver = new ResizeObserver(() => updateDimensions())\n resizeObserver.observe(containerRef.current)\n updateDimensions()\n }\n\n window.addEventListener('resize', updateDimensions)\n \n return () => {\n if (resizeObserver) {\n resizeObserver.disconnect()\n }\n window.removeEventListener('resize', updateDimensions)\n }\n }, [dimensionsReady])\n\n // Helper functions for grid coordinate extraction\n const getQuarter = (date: Date): number => {\n return Math.floor(date.getMonth() / 3) + 1 // 1-4\n }\n\n const getMonthOfQuarter = (date: Date): number => {\n return (date.getMonth() % 3) + 1 // 1-3\n }\n\n const getWeekOfMonth = (date: Date): number => {\n // Always start from week 1 for the first week of the month, regardless of day offset\n const dayOfMonth = date.getDate()\n return Math.floor((dayOfMonth - 1) / 7) + 1 // 1-5 typically\n }\n\n // Get granularity mapping based on time dimension\n const getGridMapping = useCallback((granularity: string): GridMapping | null => {\n switch (granularity?.toLowerCase()) {\n case 'year':\n // Year granularity is not useful for activity grids\n return null\n \n case 'quarter':\n // Quarter granularity: years × quarters\n return {\n extractX: (date: Date) => date.getFullYear(),\n extractY: (date: Date) => getQuarter(date) - 1, // 0-3 for indexing\n xLabels: [], // Will be determined from data\n yLabels: ['Q1', 'Q2', 'Q3', 'Q4'],\n xFormat: (value: number) => `'${value.toString().slice(-2)}`, // '24 instead of 2024\n yFormat: (value: number) => ['Q1', 'Q2', 'Q3', 'Q4'][value] || '',\n cellWidth: 16,\n cellHeight: 16\n }\n \n case 'month':\n // Month granularity: quarters × months of quarter\n // Show years above and quarters (Q1, Q2, Q3, Q4) as columns with hierarchical labels\n return {\n extractX: (date: Date) => {\n const year = date.getFullYear()\n const quarter = getQuarter(date) // 1-4\n return year * 10 + quarter // e.g., 20241, 20242, 20243, 20244 (only 4 per year)\n },\n extractY: (date: Date) => getMonthOfQuarter(date) - 1, // 0-2 for indexing\n xLabels: [], // Will be determined from data\n yLabels: ['Month 1', 'Month 2', 'Month 3'],\n xFormat: (value: number) => {\n const quarter = value % 10\n return `Q${quarter}` // Just show Q1, Q2, Q3, Q4 for individual columns\n },\n yFormat: (value: number) => ['Month 1', 'Month 2', 'Month 3'][value] || '',\n cellWidth: 16,\n cellHeight: 16,\n hasHierarchicalLabels: true, // Flag to indicate we need special handling\n getYearFromX: (value: number) => Math.floor(value / 10) // Helper to get year for grouping\n }\n \n case 'week':\n // Week granularity: months × weeks of month (same structure as quarters but with months as columns)\n return {\n extractX: (date: Date) => {\n const year = date.getFullYear()\n const month = date.getMonth() + 1\n return year * 100 + month // e.g., 202401, 202402, etc.\n },\n extractY: (date: Date) => getWeekOfMonth(date) - 1, // 0-5 for indexing\n xLabels: [], // Will be determined from data\n yLabels: ['Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5'],\n xFormat: (value: number) => {\n const month = value % 100\n const monthNames = ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D']\n return monthNames[month - 1] || ''\n },\n yFormat: (value: number) => ['Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5'][value] || '',\n cellWidth: 16,\n cellHeight: 16,\n hasHierarchicalLabels: true, // Add hierarchical labels like month view\n getYearFromX: (value: number) => Math.floor(value / 100) // Helper to get year for grouping\n }\n \n case 'day':\n // Day granularity: weeks × days of week with hierarchical year/week labels\n return {\n extractX: (date: Date) => {\n const { year, week } = getWeekOfYear(date)\n return year * 100 + week // e.g., 202401, 202402, etc.\n },\n extractY: (date: Date) => date.getDay(), // 0-6 (Sun-Sat)\n xLabels: [], // Will be determined from data\n yLabels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],\n xFormat: (value: number) => {\n const week = value % 100\n return `${week}`\n },\n yFormat: (value: number) => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][value] || '',\n cellWidth: 16,\n cellHeight: 16,\n hasHierarchicalLabels: true, // Add hierarchical labels\n getYearFromX: (value: number) => Math.floor(value / 100) // Helper to get year for grouping\n }\n\n case 'hour':\n // Hour granularity: days × 3-hour blocks with hierarchical year/month labels\n return {\n extractX: (date: Date) => {\n // Get day as YYYYMMDD number for unique day identification\n const year = date.getFullYear()\n const month = date.getMonth() + 1\n const day = date.getDate()\n return year * 10000 + month * 100 + day // e.g., 20240115\n },\n extractY: (date: Date) => Math.floor(date.getHours() / 3), // 0-7 for 8 three-hour blocks\n xLabels: [], // Will be determined from data\n yLabels: ['00-03', '03-06', '06-09', '09-12', '12-15', '15-18', '18-21', '21-00'],\n xFormat: (value: number) => {\n // Format YYYYMMDD as just the day number\n const day = value % 100\n return `${day}`\n },\n yFormat: (value: number) => ['00-03', '03-06', '06-09', '09-12', '12-15', '15-18', '18-21', '21-00'][value] || '',\n cellWidth: 16,\n cellHeight: 16,\n hasHierarchicalLabels: true, // Show year/month grouping above\n getYearFromX: (value: number) => Math.floor(value / 100) // Extract YYYYMM for month grouping\n }\n\n default:\n return null\n }\n }, [])\n\n // Helper function to get week of year with correct year (1-53)\n const getWeekOfYear = (date: Date): { year: number, week: number } => {\n const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))\n const dayNum = d.getUTCDay() || 7\n d.setUTCDate(d.getUTCDate() + 4 - dayNum)\n const year = d.getUTCFullYear()\n const yearStart = new Date(Date.UTC(year, 0, 1))\n const week = Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7)\n return { year, week }\n }\n\n useEffect(() => {\n if (!data || data.length === 0 || !svgRef.current || !dimensionsReady || dimensions.width === 0) {\n return\n }\n\n // Clear previous chart\n select(svgRef.current).selectAll('*').remove()\n\n // Validate chartConfig\n if (!chartConfig?.dateField || !chartConfig?.valueField) {\n return\n }\n\n const dateField = Array.isArray(chartConfig.dateField) ? chartConfig.dateField[0] : chartConfig.dateField\n const valueField = Array.isArray(chartConfig.valueField) ? chartConfig.valueField[0] : chartConfig.valueField\n\n\n if (!dateField || !valueField) {\n return\n }\n\n // Get granularity directly from the query's time dimensions\n const getQueryGranularity = () => {\n if (!queryObject?.timeDimensions || queryObject.timeDimensions.length === 0) {\n return 'day'\n }\n \n // Find the time dimension that matches our dateField\n const timeDim = queryObject.timeDimensions.find((td: any) => \n td.dimension === dateField || td.dimension.includes(dateField)\n )\n \n if (timeDim && timeDim.granularity) {\n return timeDim.granularity\n }\n \n // Fallback to first time dimension's granularity\n const firstTimeDim = queryObject.timeDimensions[0]\n if (firstTimeDim && firstTimeDim.granularity) {\n return firstTimeDim.granularity\n }\n \n return 'day'\n }\n \n const queryGranularity = getQueryGranularity()\n const gridMapping = getGridMapping(queryGranularity)\n \n // Handle unsupported granularity\n if (!gridMapping) {\n return\n }\n \n\n // Transform data for grid\n const gridData: GridCell[] = data.map(item => {\n const dateValue = item[dateField]\n const value = typeof item[valueField] === 'string' \n ? parseFloat(item[valueField]) \n : (item[valueField] || 0)\n \n // Parse the date\n let date: Date\n if (typeof dateValue === 'string') {\n // Handle different date formats\n let isoStr = dateValue\n if (dateValue.includes(' ')) {\n isoStr = dateValue.replace(' ', 'T').replace('+00', 'Z').replace(/\\+\\d{2}:\\d{2}$/, 'Z')\n }\n if (!isoStr.endsWith('Z') && !isoStr.includes('+')) {\n isoStr = isoStr + 'Z'\n }\n date = new Date(isoStr)\n } else {\n date = new Date(dateValue)\n }\n\n // Skip invalid dates\n if (isNaN(date.getTime())) {\n return null\n }\n\n const x = gridMapping.extractX(date)\n const y = gridMapping.extractY(date)\n \n return {\n x,\n y,\n value,\n date,\n label: formatTimeValue(dateValue, queryGranularity)\n }\n }).filter((cell): cell is GridCell => cell !== null)\n\n\n if (gridData.length === 0) return\n\n // Calculate grid dimensions\n const maxY = max(gridData, d => d.y) || 0\n const minY = min(gridData, d => d.y) || 0\n\n\n // Generate complete X range first so we can calculate proper grid width\n const getCompleteXRange = (): number[] => {\n const dataXValues = [...new Set(gridData.map(cell => cell.x))].sort()\n \n if (queryGranularity === 'quarter') {\n // For quarters: only show quarters that have data to avoid gaps\n return dataXValues\n }\n \n if (queryGranularity === 'month') {\n // For months: only show quarters that have data to avoid gaps\n return dataXValues\n }\n \n if (queryGranularity === 'week') {\n // For weeks: only show months that have data to avoid gaps\n return dataXValues\n }\n \n if (queryGranularity === 'day') {\n // For days: only show the actual weeks that have data to avoid discontinuities\n return dataXValues\n }\n \n // For other granularities, use the actual data values\n return dataXValues\n }\n \n const completeXRange = getCompleteXRange()\n \n // Calculate grid dimensions based on complete X range\n const gridWidth = completeXRange.length * gridMapping.cellWidth + (completeXRange.length - 1) * 4\n const gridHeight = (maxY - minY + 1) * gridMapping.cellHeight + (maxY - minY) * 4\n\n const margin = { \n ...CHART_MARGINS, \n left: 60, // Space for Y-axis labels\n bottom: 10, // Reduced since labels are at top\n top: gridMapping.hasHierarchicalLabels ? 40 : 25, // Extra space for hierarchical labels\n right: 10\n }\n \n const availableWidth = dimensions.width - margin.left - margin.right\n const availableHeight = dimensions.height - margin.top - margin.bottom\n\n // Scale the grid to fit the available space\n let finalCellWidth: number\n let finalCellHeight: number\n\n if (safeDisplayConfig.fitToWidth) {\n // Fit to Width mode: Calculate cell size to perfectly fit both dimensions\n // Remove min/max constraints and let blocks scale to optimal size\n const scaleX = availableWidth / gridWidth\n const scaleY = availableHeight / gridHeight\n const scale = Math.min(scaleX, scaleY)\n\n // Use the scale to calculate cell size, maintaining 1:1 aspect ratio\n finalCellWidth = gridMapping.cellWidth * scale\n finalCellHeight = gridMapping.cellHeight * scale\n } else {\n // Default mode: Use minimum/maximum size constraints with scrolling\n const getMinCellSize = () => {\n return { width: 16, height: 16 } // Same size for all views\n }\n\n const minCellSize = getMinCellSize()\n const maxCellSize = 24\n\n const scaleX = availableWidth / gridWidth\n const scaleY = availableHeight / gridHeight\n const scale = Math.min(scaleX, scaleY)\n\n // Calculate final cell size with constraints\n finalCellWidth = Math.max(minCellSize.width, Math.min(maxCellSize, gridMapping.cellWidth * scale))\n finalCellHeight = Math.max(minCellSize.height, Math.min(maxCellSize, gridMapping.cellHeight * scale))\n\n // For week view, prefer scrolling over tiny cells\n if (queryGranularity === 'week' && finalCellWidth < minCellSize.width) {\n finalCellWidth = minCellSize.width\n }\n }\n \n // Calculate actual grid dimensions with the final cell sizes\n const actualGridWidth = completeXRange.length * finalCellWidth + (completeXRange.length - 1) * 4\n \n // Determine if we need horizontal scrolling\n const needsHorizontalScroll = actualGridWidth > availableWidth\n const svgWidth = needsHorizontalScroll ? actualGridWidth + margin.left + margin.right : dimensions.width\n \n\n const svg = select(svgRef.current)\n .attr('width', svgWidth)\n .attr('height', dimensions.height)\n\n const g = svg.append('g')\n .attr('transform', `translate(${margin.left},${margin.top})`)\n\n // Set up color scale\n const values = gridData.map(d => d.value)\n const minValue = min(values) || 0\n const maxValue = max(values) || 1\n\n const colorScale = scaleQuantize<string>()\n .domain([minValue, maxValue])\n .range(colorPalette?.gradient || CHART_COLORS_GRADIENT)\n\n // Create grid data map for quick lookup\n const gridMap = new Map<string, GridCell>()\n gridData.forEach(cell => {\n const key = `${cell.x}-${cell.y}`\n gridMap.set(key, cell)\n })\n\n // Get theme colors from CSS variables\n const getThemeColor = (varName: string, fallback: string) => {\n const value = getComputedStyle(document.documentElement).getPropertyValue(varName).trim()\n return value || fallback\n }\n\n const isDark = currentTheme !== 'light'\n const textColor = isDark\n ? getThemeColor('--dc-text-muted', '#cbd5e1') // Lighter text for dark mode\n : getThemeColor('--dc-text-secondary', '#374151') // Darker text for light mode\n const lineColor = getThemeColor('--dc-border', '#e5e7eb')\n // Use theme-aware colors for empty cells: light gray in light mode, slightly lighter than bg in dark mode\n const emptyCellColor = isDark\n ? getThemeColor('--dc-bg-secondary', '#1e293b') // Slightly lighter than dark background\n : getThemeColor('--dc-bg-secondary', '#f3f4f6') // Very light gray in light mode\n const cellStrokeColor = isDark\n ? getThemeColor('--dc-border', '#334155') // Subtle border in dark mode\n : getThemeColor('--dc-bg', '#ffffff') // White border in light mode\n\n // Create tooltip\n const tooltip = select('body').append('div')\n .attr('class', 'activity-grid-tooltip')\n .style('position', 'absolute')\n .style('padding', '8px')\n .style('background', 'rgba(0, 0, 0, 0.8)')\n .style('color', 'white')\n .style('border-radius', '4px')\n .style('font-size', '12px')\n .style('pointer-events', 'none')\n .style('opacity', 0)\n .style('z-index', 1000)\n\n // Create a mapping from X values to their position indices for proper spacing\n const xValueToIndex = new Map<number, number>()\n completeXRange.forEach((x, index) => {\n xValueToIndex.set(x, index)\n })\n \n // Render grid cells for the complete X range\n for (const x of completeXRange) {\n for (let y = minY; y <= maxY; y++) {\n const key = `${x}-${y}`\n const cell = gridMap.get(key)\n const xIndex = xValueToIndex.get(x) || 0\n \n const rect = g.append('rect')\n .attr('x', xIndex * (finalCellWidth + 4))\n .attr('y', (y - minY) * (finalCellHeight + 4))\n .attr('width', finalCellWidth)\n .attr('height', finalCellHeight)\n .attr('rx', 2)\n .attr('ry', 2)\n .style('fill', cell ? colorScale(cell.value) : emptyCellColor)\n .style('stroke', cellStrokeColor)\n .style('stroke-width', 1)\n\n // Add hover effects and tooltips\n if (safeDisplayConfig.showTooltip) {\n rect\n .style('cursor', drillEnabled ? 'pointer' : 'default')\n .on('mouseover', function(event) {\n select(this)\n .style('stroke', '#000')\n .style('stroke-width', 2)\n\n if (cell) {\n const tooltipContent = [\n `<strong>${cell.label}</strong>`,\n `${getFieldLabel(valueField)}: ${cell.value}`\n ].join('<br>')\n\n tooltip\n .html(tooltipContent)\n .style('left', (event.pageX + 10) + 'px')\n .style('top', (event.pageY - 10) + 'px')\n .transition()\n .duration(200)\n .style('opacity', 1)\n }\n })\n .on('mousemove', function(event) {\n tooltip\n .style('left', (event.pageX + 10) + 'px')\n .style('top', (event.pageY - 10) + 'px')\n })\n .on('mouseout', function() {\n select(this)\n .style('stroke', cellStrokeColor)\n .style('stroke-width', 1)\n\n tooltip\n .transition()\n .duration(200)\n .style('opacity', 0)\n })\n } else if (drillEnabled) {\n // Set cursor for drill even without tooltips\n rect.style('cursor', 'pointer')\n }\n\n // Add drill click handler\n if (drillEnabled && onDataPointClick && cell) {\n rect.on('click', function(event) {\n onDataPointClick({\n dataPoint: { [valueField]: cell.value, [dateField]: cell.date },\n clickedField: valueField,\n xValue: cell.label,\n position: { x: event.pageX, y: event.pageY },\n nativeEvent: event as unknown as React.MouseEvent\n })\n })\n }\n }\n }\n\n // Add axis labels if enabled\n if (safeDisplayConfig.showLabels) {\n if (gridMapping.hasHierarchicalLabels && gridMapping.getYearFromX) {\n // Special handling for quarter view with hierarchical labels\n // Use the complete X range to show all quarters/months\n \n // Group quarters by year\n const yearGroups = new Map<number, number[]>()\n for (const x of completeXRange) {\n const year = gridMapping.getYearFromX(x)\n if (!yearGroups.has(year)) {\n yearGroups.set(year, [])\n }\n yearGroups.get(year)!.push(x)\n }\n \n \n // Draw column labels (Q1-Q4 for months, Jan-Dec for weeks)\n for (const x of completeXRange) {\n const xIndex = xValueToIndex.get(x) || 0\n g.append('text')\n .attr('x', xIndex * (finalCellWidth + 4) + finalCellWidth / 2)\n .attr('y', -8)\n .attr('text-anchor', 'middle')\n .style('font-size', '10px')\n .style('fill', textColor)\n .text(gridMapping.xFormat(x))\n }\n\n // Draw year group labels above quarters\n for (const [year, xValues] of yearGroups) {\n if (xValues.length > 0) {\n // Get the index positions for proper spacing\n const startIndex = Math.min(...xValues.map(x => xValueToIndex.get(x) || 0))\n const endIndex = Math.max(...xValues.map(x => xValueToIndex.get(x) || 0))\n const centerIndex = (startIndex + endIndex) / 2\n\n // Year label (or year/month for hour granularity)\n let labelText: string\n if (year > 9999) {\n // For hour granularity, year is encoded as YYYYMM\n const actualYear = Math.floor(year / 100)\n const month = year % 100\n const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']\n labelText = `${monthNames[month - 1]} '${actualYear.toString().slice(-2)}`\n } else {\n // For other granularities, just show the year\n labelText = `'${year.toString().slice(-2)}`\n }\n\n g.append('text')\n .attr('x', centerIndex * (finalCellWidth + 4) + finalCellWidth / 2)\n .attr('y', -25)\n .attr('text-anchor', 'middle')\n .style('font-size', '12px')\n .style('font-weight', 'bold')\n .style('fill', textColor)\n .text(labelText)\n\n // Optional: Add a subtle line to group quarters under the year\n if (xValues.length > 1) {\n g.append('line')\n .attr('x1', startIndex * (finalCellWidth + 4))\n .attr('x2', endIndex * (finalCellWidth + 4) + finalCellWidth)\n .attr('y1', -20)\n .attr('y2', -20)\n .style('stroke', lineColor)\n .style('stroke-width', 1)\n .style('opacity', 0.3)\n }\n }\n }\n } else {\n // Regular X-axis labels for other granularities\n const xLabelStep = Math.max(1, Math.floor(completeXRange.length / 10))\n for (let i = 0; i < completeXRange.length; i += xLabelStep) {\n const x = completeXRange[i]\n g.append('text')\n .attr('x', i * (finalCellWidth + 4) + finalCellWidth / 2)\n .attr('y', -8)\n .attr('text-anchor', 'middle')\n .style('font-size', '10px')\n .style('fill', textColor)\n .text(gridMapping.xFormat(x))\n }\n }\n\n // Y-axis labels (left) - same for all granularities\n for (let y = minY; y <= maxY; y++) {\n g.append('text')\n .attr('x', -8)\n .attr('y', (y - minY) * (finalCellHeight + 4) + finalCellHeight / 2)\n .attr('text-anchor', 'end')\n .attr('dy', '.35em')\n .style('font-size', '10px')\n .style('fill', textColor)\n .text(gridMapping.yFormat(y))\n }\n }\n\n // Cleanup function\n return () => {\n tooltip.remove()\n }\n }, [data, chartConfig, displayConfig, queryObject, dimensions, dimensionsReady, safeDisplayConfig, colorPalette, currentTheme, getFieldLabel, getGridMapping, drillEnabled, onDataPointClick])\n\n if (!data || data.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full\"\n style={{ height }}\n >\n <div className=\"dc:text-center text-dc-text-muted\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('chart.runtime.noData')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">{t('chart.runtime.noDataHint.activityGrid')}</div>\n </div>\n </div>\n )\n }\n\n // Validate that we have required fields\n const hasValidConfig = chartConfig?.dateField && chartConfig?.valueField\n if (!hasValidConfig) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full\"\n style={{ height }}\n >\n <div className=\"dc:text-center text-dc-text-muted\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('chart.runtime.activityGridConfigRequired')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">{t('chart.runtime.configErrorHint.activityGridRequired')}</div>\n </div>\n </div>\n )\n }\n\n // Check if granularity is supported\n const dateField = Array.isArray(chartConfig.dateField) ? chartConfig.dateField[0] : chartConfig.dateField\n const granularityFromQuery = queryObject?.timeDimensions?.find((td: any) => \n td.dimension === dateField || td.dimension.includes(dateField)\n )?.granularity || 'day'\n \n if (granularityFromQuery?.toLowerCase() === 'year') {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full\"\n style={{ height }}\n >\n <div className=\"dc:text-center text-dc-text-muted\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('chart.runtime.activityGridGranularityTooHigh')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">{t('chart.runtime.activityGridGranularityHint')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary dc:mt-1\">{t('chart.runtime.activityGridGranularityAction')}</div>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"dc:w-full dc:flex dc:flex-col dc:relative\" style={{ height, minHeight: '250px', overflow: 'hidden', width: '100%' }}>\n <div ref={containerRef} className=\"dc:w-full dc:h-full dc:relative dc:overflow-x-auto\" style={{ width: '100%' }}>\n <svg ref={svgRef} className=\"dc:h-full\" />\n {!dimensionsReady && (\n <div className=\"dc:absolute dc:inset-0 dc:flex dc:items-center dc:justify-center\">\n <div className=\"text-dc-text-muted dc:text-sm\">{t('chart.runtime.measuringDimensions')}</div>\n </div>\n )}\n </div>\n </div>\n )\n})\n\nexport default ActivityGridChart"],"mappings":"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"}
|