drizzle-cube 0.4.53 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/express/index.cjs +2 -2
- package/dist/adapters/express/index.js +109 -96
- package/dist/adapters/fastify/index.cjs +2 -2
- package/dist/adapters/fastify/index.js +148 -134
- package/dist/adapters/{handler-RItnSaEl.js → handler-3LGcjLtr.js} +617 -612
- package/dist/adapters/handler-BzzbVpcl.cjs +25 -0
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/hono/index.js +103 -99
- package/dist/adapters/{compiler-S6KHiOY6.js → locale-DTnJrxm1.js} +1700 -1563
- package/dist/adapters/locale-DueXjqMh.cjs +198 -0
- package/dist/adapters/locale.d.ts +8 -0
- package/dist/adapters/mcp-tools.cjs +1 -1
- package/dist/adapters/mcp-tools.d.ts +7 -3
- package/dist/adapters/mcp-tools.js +27 -27
- package/dist/adapters/mcp-transport-C7VLf4T5.js +579 -0
- package/dist/adapters/mcp-transport-poPHl_2j.cjs +39 -0
- package/dist/adapters/mcp-transport.d.ts +25 -2
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.js +152 -132
- package/dist/adapters/utils-DNrj-ryp.cjs +17 -0
- package/dist/adapters/{utils-IH1ePsBd.js → utils-DOg9oGdt.js} +2341 -819
- package/dist/adapters/utils.cjs +1 -1
- package/dist/adapters/utils.d.ts +45 -2
- package/dist/adapters/utils.js +1 -1
- package/dist/client/charts.js +12 -12
- package/dist/client/chunks/{DashboardEditModal-BTdV528l.js → DashboardEditModal-cSSIAZGy.js} +1968 -1973
- package/dist/client/chunks/DashboardEditModal-cSSIAZGy.js.map +1 -0
- package/dist/client/chunks/{FieldSearchModal-D75vy4Wb.js → FieldSearchModal-CZNo4pNK.js} +550 -536
- package/dist/client/chunks/FieldSearchModal-CZNo4pNK.js.map +1 -0
- package/dist/client/chunks/KpiDelta-Dll_eCV1.js +2 -0
- package/dist/client/chunks/KpiNumber-BPlR92hI.js +2 -0
- package/dist/client/chunks/KpiText-BIxq7Jso.js +2 -0
- package/dist/client/chunks/{RetentionCombinedChart-DIhK5pD8.js → RetentionCombinedChart-BD8tGeM_.js} +96 -96
- package/dist/client/chunks/RetentionCombinedChart-BD8tGeM_.js.map +1 -0
- package/dist/client/chunks/{RetentionHeatmap-CyREolyP.js → RetentionHeatmap-B_5sewwi.js} +77 -77
- package/dist/client/chunks/RetentionHeatmap-B_5sewwi.js.map +1 -0
- package/dist/client/chunks/SchemaVisualization-CCICjhvv.js +2 -0
- package/dist/client/chunks/SchemaVisualizationLazy-DraGsMx6.js +2 -0
- package/dist/client/chunks/af-ZA-xDmO5F0s.js +1431 -0
- package/dist/client/chunks/af-ZA-xDmO5F0s.js.map +1 -0
- package/dist/client/chunks/{analysis-builder-C1CJ0c7L.js → analysis-builder-BeVZhiQ5.js} +1519 -1507
- package/dist/client/chunks/analysis-builder-BeVZhiQ5.js.map +1 -0
- package/dist/client/chunks/{analysis-builder-shared-rkjJfWLT.js → analysis-builder-shared-BWc7ZZnG.js} +925 -954
- package/dist/client/chunks/analysis-builder-shared-BWc7ZZnG.js.map +1 -0
- package/dist/client/chunks/chart-activity-grid-CWT0gLv4.js +2376 -0
- package/dist/client/chunks/chart-activity-grid-CWT0gLv4.js.map +1 -0
- package/dist/client/chunks/{chart-area-BwYaflNk.js → chart-area-D63kG8OT.js} +157 -157
- package/dist/client/chunks/chart-area-D63kG8OT.js.map +1 -0
- package/dist/client/chunks/{chart-bar-BiENfFgE.js → chart-bar-BEfsCLjl.js} +78 -78
- package/dist/client/chunks/chart-bar-BEfsCLjl.js.map +1 -0
- package/dist/client/chunks/{chart-box-plot-BJF1tBXC.js → chart-box-plot-o-h9MRX5.js} +111 -114
- package/dist/client/chunks/chart-box-plot-o-h9MRX5.js.map +1 -0
- package/dist/client/chunks/{chart-bubble-DQQhGVDJ.js → chart-bubble-CMDp4Pfm.js} +121 -121
- package/dist/client/chunks/chart-bubble-CMDp4Pfm.js.map +1 -0
- package/dist/client/chunks/chart-candlestick-WyANJ0Ky.js +303 -0
- package/dist/client/chunks/chart-candlestick-WyANJ0Ky.js.map +1 -0
- package/dist/client/chunks/chart-config-activity-grid-C-EkgYoa.js +51 -0
- package/dist/client/chunks/chart-config-activity-grid-C-EkgYoa.js.map +1 -0
- package/dist/client/chunks/chart-config-area-CMZpbIah.js +93 -0
- package/dist/client/chunks/chart-config-area-CMZpbIah.js.map +1 -0
- package/dist/client/chunks/chart-config-bar-B8_V4YLg.js +87 -0
- package/dist/client/chunks/chart-config-bar-B8_V4YLg.js.map +1 -0
- package/dist/client/chunks/chart-config-box-plot-Dwj7sEbU.js +35 -0
- package/dist/client/chunks/chart-config-box-plot-Dwj7sEbU.js.map +1 -0
- package/dist/client/chunks/chart-config-bubble-B0w0ZVp4.js +82 -0
- package/dist/client/chunks/chart-config-bubble-B0w0ZVp4.js.map +1 -0
- package/dist/client/chunks/chart-config-candlestick-Bvo3zeIn.js +72 -0
- package/dist/client/chunks/chart-config-candlestick-Bvo3zeIn.js.map +1 -0
- package/dist/client/chunks/{chart-config-data-table-Bhdx5Hem.js → chart-config-data-table-BQXSn4b_.js} +9 -9
- package/dist/client/chunks/chart-config-data-table-BQXSn4b_.js.map +1 -0
- package/dist/client/chunks/chart-config-funnel-BzEsHmjR.js +93 -0
- package/dist/client/chunks/chart-config-funnel-BzEsHmjR.js.map +1 -0
- package/dist/client/chunks/chart-config-gauge-C5ZiyZy7.js +64 -0
- package/dist/client/chunks/chart-config-gauge-C5ZiyZy7.js.map +1 -0
- package/dist/client/chunks/chart-config-heat-map-Cv8qNnVP.js +91 -0
- package/dist/client/chunks/chart-config-heat-map-Cv8qNnVP.js.map +1 -0
- package/dist/client/chunks/chart-config-kpi-delta-BraHQc2E.js +94 -0
- package/dist/client/chunks/chart-config-kpi-delta-BraHQc2E.js.map +1 -0
- package/dist/client/chunks/chart-config-kpi-number-CeCkx7mC.js +75 -0
- package/dist/client/chunks/chart-config-kpi-number-CeCkx7mC.js.map +1 -0
- package/dist/client/chunks/chart-config-kpi-text-CImM3SvH.js +47 -0
- package/dist/client/chunks/chart-config-kpi-text-CImM3SvH.js.map +1 -0
- package/dist/client/chunks/chart-config-line-BVKapAQK.js +104 -0
- package/dist/client/chunks/chart-config-line-BVKapAQK.js.map +1 -0
- package/dist/client/chunks/chart-config-markdown-C-_g_8te.js +117 -0
- package/dist/client/chunks/chart-config-markdown-C-_g_8te.js.map +1 -0
- package/dist/client/chunks/chart-config-measure-profile-KTVV1gO3.js +82 -0
- package/dist/client/chunks/chart-config-measure-profile-KTVV1gO3.js.map +1 -0
- package/dist/client/chunks/chart-config-pie-BZxVl25X.js +68 -0
- package/dist/client/chunks/chart-config-pie-BZxVl25X.js.map +1 -0
- package/dist/client/chunks/chart-config-radar-B7FByX3t.js +49 -0
- package/dist/client/chunks/chart-config-radar-B7FByX3t.js.map +1 -0
- package/dist/client/chunks/chart-config-radial-bar-UfW_3yyX.js +38 -0
- package/dist/client/chunks/chart-config-radial-bar-UfW_3yyX.js.map +1 -0
- package/dist/client/chunks/chart-config-sankey-DGAThN9i.js +66 -0
- package/dist/client/chunks/chart-config-sankey-DGAThN9i.js.map +1 -0
- package/dist/client/chunks/chart-config-scatter-BVVJuOnt.js +61 -0
- package/dist/client/chunks/chart-config-scatter-BVVJuOnt.js.map +1 -0
- package/dist/client/chunks/chart-config-sunburst-utejM2YS.js +45 -0
- package/dist/client/chunks/chart-config-sunburst-utejM2YS.js.map +1 -0
- package/dist/client/chunks/chart-config-tree-map-IHp97OyV.js +51 -0
- package/dist/client/chunks/chart-config-tree-map-IHp97OyV.js.map +1 -0
- package/dist/client/chunks/chart-config-waterfall-BdqG1V-x.js +59 -0
- package/dist/client/chunks/chart-config-waterfall-BdqG1V-x.js.map +1 -0
- package/dist/client/chunks/{chart-data-table-2iCsn0CF.js → chart-data-table-C3Xh9jwL.js} +1083 -1086
- package/dist/client/chunks/chart-data-table-C3Xh9jwL.js.map +1 -0
- package/dist/client/chunks/{chart-funnel-poyOf7-e.js → chart-funnel-C7pgktN5.js} +132 -132
- package/dist/client/chunks/chart-funnel-C7pgktN5.js.map +1 -0
- package/dist/client/chunks/{chart-gauge-D5J4gRky.js → chart-gauge-D2r2B_AR.js} +150 -150
- package/dist/client/chunks/chart-gauge-D2r2B_AR.js.map +1 -0
- package/dist/client/chunks/{chart-heat-map-BAMVhLGG.js → chart-heat-map-Dw2yjwfO.js} +75 -80
- package/dist/client/chunks/chart-heat-map-Dw2yjwfO.js.map +1 -0
- package/dist/client/chunks/{chart-kpi-delta-KQjUIeal.js → chart-kpi-delta-CYE0S1x_.js} +117 -117
- package/dist/client/chunks/chart-kpi-delta-CYE0S1x_.js.map +1 -0
- package/dist/client/chunks/chart-kpi-number-BlZ79xHW.js +321 -0
- package/dist/client/chunks/chart-kpi-number-BlZ79xHW.js.map +1 -0
- package/dist/client/chunks/chart-kpi-text-DY1BnxPe.js +148 -0
- package/dist/client/chunks/chart-kpi-text-DY1BnxPe.js.map +1 -0
- package/dist/client/chunks/{chart-line-B5_WntY5.js → chart-line-CBsTThTv.js} +123 -123
- package/dist/client/chunks/chart-line-CBsTThTv.js.map +1 -0
- package/dist/client/chunks/chart-markdown-BWaWVkuz.js +3474 -0
- package/dist/client/chunks/chart-markdown-BWaWVkuz.js.map +1 -0
- package/dist/client/chunks/chart-measure-profile-B41qCTBG.js +179 -0
- package/dist/client/chunks/chart-measure-profile-B41qCTBG.js.map +1 -0
- package/dist/client/chunks/chart-pie-Djbu8x2v.js +172 -0
- package/dist/client/chunks/chart-pie-Djbu8x2v.js.map +1 -0
- package/dist/client/chunks/chart-radar-BsTcKV0K.js +154 -0
- package/dist/client/chunks/chart-radar-BsTcKV0K.js.map +1 -0
- package/dist/client/chunks/chart-radial-bar-Du7XNnwE.js +148 -0
- package/dist/client/chunks/chart-radial-bar-Du7XNnwE.js.map +1 -0
- package/dist/client/chunks/{chart-sankey-BOyxfG1Q.js → chart-sankey-WwkZAhLy.js} +73 -73
- package/dist/client/chunks/chart-sankey-WwkZAhLy.js.map +1 -0
- package/dist/client/chunks/chart-scatter-D8krEYsA.js +255 -0
- package/dist/client/chunks/chart-scatter-D8krEYsA.js.map +1 -0
- package/dist/client/chunks/{chart-sunburst-D9lGEOCc.js → chart-sunburst-CIDB_pTl.js} +66 -66
- package/dist/client/chunks/chart-sunburst-CIDB_pTl.js.map +1 -0
- package/dist/client/chunks/chart-tree-map-C5C2iaWM.js +298 -0
- package/dist/client/chunks/chart-tree-map-C5C2iaWM.js.map +1 -0
- package/dist/client/chunks/{chart-waterfall-BCdUx4DC.js → chart-waterfall-BGdPrJ5Y.js} +80 -80
- package/dist/client/chunks/chart-waterfall-BGdPrJ5Y.js.map +1 -0
- package/dist/client/chunks/{charts-core-C5Yokk-x.js → charts-core-BXOqaYFn.js} +92 -92
- package/dist/client/chunks/charts-core-BXOqaYFn.js.map +1 -0
- package/dist/client/chunks/en-US-5xPTdCXg.js +35 -0
- package/dist/client/chunks/en-US-5xPTdCXg.js.map +1 -0
- package/dist/client/chunks/nl-NL-vCifBijs.js +1491 -0
- package/dist/client/chunks/nl-NL-vCifBijs.js.map +1 -0
- package/dist/client/chunks/{schema-visualization-t1KiOORo.js → schema-visualization-Xp60Ff2W.js} +352 -364
- package/dist/client/chunks/schema-visualization-Xp60Ff2W.js.map +1 -0
- package/dist/client/chunks/{useDebounce-CKqkM42n.js → useDebounce-CfmUMFau.js} +85 -85
- package/dist/client/chunks/useDebounce-CfmUMFau.js.map +1 -0
- package/dist/client/chunks/{useExplainAI-DBIfYwz-.js → useExplainAI-BKGmejIj.js} +4 -4
- package/dist/client/chunks/{useExplainAI-DBIfYwz-.js.map → useExplainAI-BKGmejIj.js.map} +1 -1
- package/dist/client/chunks/{utils--qCr8Yn5.js → utils-BldkcRHv.js} +2 -2
- package/dist/client/chunks/{utils--qCr8Yn5.js.map → utils-BldkcRHv.js.map} +1 -1
- package/dist/client/chunks/{vendor-BRlsCGnK.js → vendor-ClXpIiea.js} +2 -2
- package/dist/client/chunks/{vendor-BRlsCGnK.js.map → vendor-ClXpIiea.js.map} +1 -1
- package/dist/client/components/AnalysisBuilder/types.d.ts +6 -6
- package/dist/client/components.js +3 -3
- package/dist/client/hooks/useTranslation.d.ts +21 -0
- package/dist/client/hooks.js +3 -3
- package/dist/client/icons.js +1 -1
- package/dist/client/index.js +493 -488
- package/dist/client/index.js.map +1 -1
- package/dist/client/providers/CubeApiProvider.d.ts +2 -1
- package/dist/client/providers/CubeProvider.d.ts +7 -1
- package/dist/client/providers/I18nProvider.d.ts +18 -0
- package/dist/client/providers.js +1 -1
- package/dist/client/schema.js +1 -1
- package/dist/client/shared/types.d.ts +5 -20
- package/dist/client/styles.css +1 -1
- package/dist/client/utils.js +5 -5
- package/dist/client-bundle-stats.html +1 -1
- package/dist/mcp-app/mcp-app.html +40 -36
- package/dist/server/index.cjs +42 -40
- package/dist/server/index.js +3311 -1672
- package/package.json +5 -2
- package/dist/adapters/compiler-CRgLzmSn.cjs +0 -198
- package/dist/adapters/handler-DumFgnNM.cjs +0 -25
- package/dist/adapters/mcp-transport-C6bsIOSV.js +0 -545
- package/dist/adapters/mcp-transport-DMhuYmFp.cjs +0 -39
- package/dist/adapters/utils-CyBt-as9.cjs +0 -15
- package/dist/client/chunks/DashboardEditModal-BTdV528l.js.map +0 -1
- package/dist/client/chunks/FieldSearchModal-D75vy4Wb.js.map +0 -1
- package/dist/client/chunks/KpiDelta-Bk8bzKYM.js +0 -2
- package/dist/client/chunks/KpiNumber-CKF-8e_T.js +0 -2
- package/dist/client/chunks/KpiText-Iz1vIvu_.js +0 -2
- package/dist/client/chunks/RetentionCombinedChart-DIhK5pD8.js.map +0 -1
- package/dist/client/chunks/RetentionHeatmap-CyREolyP.js.map +0 -1
- package/dist/client/chunks/SchemaVisualization-B1GUT-FM.js +0 -2
- package/dist/client/chunks/SchemaVisualizationLazy-DymwT34e.js +0 -2
- package/dist/client/chunks/analysis-builder-C1CJ0c7L.js.map +0 -1
- package/dist/client/chunks/analysis-builder-shared-rkjJfWLT.js.map +0 -1
- package/dist/client/chunks/chart-activity-grid-DLktOINm.js +0 -806
- package/dist/client/chunks/chart-activity-grid-DLktOINm.js.map +0 -1
- package/dist/client/chunks/chart-area-BwYaflNk.js.map +0 -1
- package/dist/client/chunks/chart-bar-BiENfFgE.js.map +0 -1
- package/dist/client/chunks/chart-box-plot-BJF1tBXC.js.map +0 -1
- package/dist/client/chunks/chart-bubble-DQQhGVDJ.js.map +0 -1
- package/dist/client/chunks/chart-candlestick-C2UuXbLe.js +0 -306
- package/dist/client/chunks/chart-candlestick-C2UuXbLe.js.map +0 -1
- package/dist/client/chunks/chart-config-activity-grid-DJOU3TEz.js +0 -51
- package/dist/client/chunks/chart-config-activity-grid-DJOU3TEz.js.map +0 -1
- package/dist/client/chunks/chart-config-area-CWnWVT6a.js +0 -93
- package/dist/client/chunks/chart-config-area-CWnWVT6a.js.map +0 -1
- package/dist/client/chunks/chart-config-bar-C-7Dr1Ho.js +0 -87
- package/dist/client/chunks/chart-config-bar-C-7Dr1Ho.js.map +0 -1
- package/dist/client/chunks/chart-config-box-plot-mVOwmLdu.js +0 -35
- package/dist/client/chunks/chart-config-box-plot-mVOwmLdu.js.map +0 -1
- package/dist/client/chunks/chart-config-bubble-BPE2CeiD.js +0 -82
- package/dist/client/chunks/chart-config-bubble-BPE2CeiD.js.map +0 -1
- package/dist/client/chunks/chart-config-candlestick-BSB9DRy2.js +0 -72
- package/dist/client/chunks/chart-config-candlestick-BSB9DRy2.js.map +0 -1
- package/dist/client/chunks/chart-config-data-table-Bhdx5Hem.js.map +0 -1
- package/dist/client/chunks/chart-config-funnel-Cl-v-bm1.js +0 -93
- package/dist/client/chunks/chart-config-funnel-Cl-v-bm1.js.map +0 -1
- package/dist/client/chunks/chart-config-gauge-CdrUTJMJ.js +0 -64
- package/dist/client/chunks/chart-config-gauge-CdrUTJMJ.js.map +0 -1
- package/dist/client/chunks/chart-config-heat-map-DGE3NzoF.js +0 -91
- package/dist/client/chunks/chart-config-heat-map-DGE3NzoF.js.map +0 -1
- package/dist/client/chunks/chart-config-kpi-delta-DMrQerUW.js +0 -94
- package/dist/client/chunks/chart-config-kpi-delta-DMrQerUW.js.map +0 -1
- package/dist/client/chunks/chart-config-kpi-number-DCytCytn.js +0 -75
- package/dist/client/chunks/chart-config-kpi-number-DCytCytn.js.map +0 -1
- package/dist/client/chunks/chart-config-kpi-text-KdKQUvHo.js +0 -47
- package/dist/client/chunks/chart-config-kpi-text-KdKQUvHo.js.map +0 -1
- package/dist/client/chunks/chart-config-line-Bl9VDAdz.js +0 -104
- package/dist/client/chunks/chart-config-line-Bl9VDAdz.js.map +0 -1
- package/dist/client/chunks/chart-config-markdown-BX26b94i.js +0 -117
- package/dist/client/chunks/chart-config-markdown-BX26b94i.js.map +0 -1
- package/dist/client/chunks/chart-config-measure-profile-DwtRhEFh.js +0 -82
- package/dist/client/chunks/chart-config-measure-profile-DwtRhEFh.js.map +0 -1
- package/dist/client/chunks/chart-config-pie-BzBcqPMJ.js +0 -68
- package/dist/client/chunks/chart-config-pie-BzBcqPMJ.js.map +0 -1
- package/dist/client/chunks/chart-config-radar-6ZOgt__z.js +0 -49
- package/dist/client/chunks/chart-config-radar-6ZOgt__z.js.map +0 -1
- package/dist/client/chunks/chart-config-radial-bar-Df6Eta7N.js +0 -38
- package/dist/client/chunks/chart-config-radial-bar-Df6Eta7N.js.map +0 -1
- package/dist/client/chunks/chart-config-sankey-DgqKBFvN.js +0 -66
- package/dist/client/chunks/chart-config-sankey-DgqKBFvN.js.map +0 -1
- package/dist/client/chunks/chart-config-scatter-D5nVLDvi.js +0 -61
- package/dist/client/chunks/chart-config-scatter-D5nVLDvi.js.map +0 -1
- package/dist/client/chunks/chart-config-sunburst-Ca3FX9nW.js +0 -45
- package/dist/client/chunks/chart-config-sunburst-Ca3FX9nW.js.map +0 -1
- package/dist/client/chunks/chart-config-tree-map-Bjy4QNa3.js +0 -51
- package/dist/client/chunks/chart-config-tree-map-Bjy4QNa3.js.map +0 -1
- package/dist/client/chunks/chart-config-waterfall-C5K2eqR7.js +0 -59
- package/dist/client/chunks/chart-config-waterfall-C5K2eqR7.js.map +0 -1
- package/dist/client/chunks/chart-data-table-2iCsn0CF.js.map +0 -1
- package/dist/client/chunks/chart-funnel-poyOf7-e.js.map +0 -1
- package/dist/client/chunks/chart-gauge-D5J4gRky.js.map +0 -1
- package/dist/client/chunks/chart-heat-map-BAMVhLGG.js.map +0 -1
- package/dist/client/chunks/chart-kpi-delta-KQjUIeal.js.map +0 -1
- package/dist/client/chunks/chart-kpi-number-CsQgV_x3.js +0 -325
- package/dist/client/chunks/chart-kpi-number-CsQgV_x3.js.map +0 -1
- package/dist/client/chunks/chart-kpi-text-BR0IyeUU.js +0 -148
- package/dist/client/chunks/chart-kpi-text-BR0IyeUU.js.map +0 -1
- package/dist/client/chunks/chart-line-B5_WntY5.js.map +0 -1
- package/dist/client/chunks/chart-markdown-B6bENbel.js +0 -3473
- package/dist/client/chunks/chart-markdown-B6bENbel.js.map +0 -1
- package/dist/client/chunks/chart-measure-profile-yWk-obNb.js +0 -179
- package/dist/client/chunks/chart-measure-profile-yWk-obNb.js.map +0 -1
- package/dist/client/chunks/chart-pie-BodrUoHv.js +0 -172
- package/dist/client/chunks/chart-pie-BodrUoHv.js.map +0 -1
- package/dist/client/chunks/chart-radar-gG3zfLud.js +0 -154
- package/dist/client/chunks/chart-radar-gG3zfLud.js.map +0 -1
- package/dist/client/chunks/chart-radial-bar-C2IPCV8c.js +0 -148
- package/dist/client/chunks/chart-radial-bar-C2IPCV8c.js.map +0 -1
- package/dist/client/chunks/chart-sankey-BOyxfG1Q.js.map +0 -1
- package/dist/client/chunks/chart-scatter-B8OwlsAX.js +0 -255
- package/dist/client/chunks/chart-scatter-B8OwlsAX.js.map +0 -1
- package/dist/client/chunks/chart-sunburst-D9lGEOCc.js.map +0 -1
- package/dist/client/chunks/chart-tree-map-DZaKy9he.js +0 -298
- package/dist/client/chunks/chart-tree-map-DZaKy9he.js.map +0 -1
- package/dist/client/chunks/chart-waterfall-BCdUx4DC.js.map +0 -1
- package/dist/client/chunks/charts-core-C5Yokk-x.js.map +0 -1
- package/dist/client/chunks/schema-visualization-t1KiOORo.js.map +0 -1
- package/dist/client/chunks/useDebounce-CKqkM42n.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chart-funnel-poyOf7-e.js","names":[],"sources":["../../../src/client/utils/funnelExecution.ts","../../../src/client/components/charts/FunnelChart.tsx"],"sourcesContent":["/**\n * Funnel Query Execution Utilities\n *\n * Handles sequential execution of funnel queries where each step\n * filters based on binding key values from the previous step.\n */\n\nimport type { CubeQuery, SimpleFilter } from '../types'\n\n/**\n * Default maximum number of binding key values to pass between funnel steps.\n * Limits the size of the IN clause to prevent performance issues.\n */\nexport const DEFAULT_BINDING_KEY_LIMIT = 500\nimport type {\n FunnelBindingKey,\n FunnelBindingKeyMapping,\n FunnelConfig,\n FunnelStep,\n FunnelStepResult,\n FunnelExecutionResult,\n FunnelChartData,\n} from '../types/funnel'\n\n/**\n * Extract the binding key field name for a specific query/cube.\n *\n * For simple binding keys (string), returns the dimension directly.\n * For cross-cube mappings, finds the mapping for the cube used in the query.\n *\n * @param bindingKey - The funnel binding key configuration\n * @param query - The cube query (used to determine which cube is being queried)\n * @returns The binding key field name (e.g., \"Users.userId\")\n */\nexport function getBindingKeyField(\n bindingKey: FunnelBindingKey,\n query: CubeQuery\n): string {\n // Simple case: single dimension name\n if (typeof bindingKey.dimension === 'string') {\n return bindingKey.dimension\n }\n\n // Cross-cube case: find the mapping for this query's cube\n const mappings = bindingKey.dimension as FunnelBindingKeyMapping[]\n\n // Extract cube name from query fields\n const cubeName = getCubeNameFromQuery(query)\n if (!cubeName) {\n // If we can't determine the cube, use the first mapping\n return mappings[0]?.dimension || ''\n }\n\n // Find matching mapping\n const mapping = mappings.find(m => m.cube === cubeName)\n if (mapping) {\n return mapping.dimension\n }\n\n // Fallback to first mapping if no match found\n return mappings[0]?.dimension || ''\n}\n\n/**\n * Extract the cube name from a query by looking at measures/dimensions\n */\nexport function getCubeNameFromQuery(query: CubeQuery): string | null {\n // Check measures first\n if (query.measures && query.measures.length > 0) {\n const parts = query.measures[0].split('.')\n if (parts.length >= 2) {\n return parts[0]\n }\n }\n\n // Then check dimensions\n if (query.dimensions && query.dimensions.length > 0) {\n const parts = query.dimensions[0].split('.')\n if (parts.length >= 2) {\n return parts[0]\n }\n }\n\n // Then check time dimensions\n if (query.timeDimensions && query.timeDimensions.length > 0) {\n const parts = query.timeDimensions[0].dimension.split('.')\n if (parts.length >= 2) {\n return parts[0]\n }\n }\n\n return null\n}\n\n/**\n * Result from extracting binding key values\n */\nexport interface ExtractedBindingKeyValues {\n /** Binding key values (limited to max if specified) */\n values: (string | number)[]\n /** Total count of unique values before limiting */\n totalCount: number\n /** Whether values were truncated due to limit */\n wasTruncated: boolean\n}\n\n/**\n * Extract unique values for the binding key from query result data\n *\n * @deprecated This function was used for client-side funnel execution.\n * Server-side funnel execution is now the default approach.\n *\n * @param data - Raw data from query result\n * @param bindingKeyField - The field name to extract values from\n * @param limit - Maximum number of values to return (default: DEFAULT_BINDING_KEY_LIMIT)\n * @returns Object with values array, total count, and truncation flag\n */\nexport function extractBindingKeyValues(\n data: unknown[],\n bindingKeyField: string,\n limit: number = DEFAULT_BINDING_KEY_LIMIT\n): ExtractedBindingKeyValues {\n const values = new Set<string | number>()\n\n for (const row of data) {\n if (typeof row === 'object' && row !== null) {\n const value = (row as Record<string, unknown>)[bindingKeyField]\n if (value !== null && value !== undefined) {\n // Convert to string or number\n if (typeof value === 'number') {\n values.add(value)\n } else {\n values.add(String(value))\n }\n }\n }\n }\n\n const allValues = Array.from(values)\n const totalCount = allValues.length\n\n // Sort values for consistent ordering (enables query cache hits)\n // Numbers sort numerically, strings sort alphabetically\n allValues.sort((a, b) => {\n if (typeof a === 'number' && typeof b === 'number') {\n return a - b\n }\n return String(a).localeCompare(String(b))\n })\n\n const limitedValues = limit > 0 ? allValues.slice(0, limit) : allValues\n\n return {\n values: limitedValues,\n totalCount,\n wasTruncated: limitedValues.length < totalCount,\n }\n}\n\n/**\n * Build a step query with binding key filter applied.\n *\n * @deprecated This function was used for client-side funnel execution.\n * Server-side funnel execution is now the default approach.\n *\n * For the first step (no previous values), returns the original query\n * with the binding key dimension ensured.\n *\n * For subsequent steps, adds an IN filter for the previous step's\n * binding key values.\n *\n * @param step - The funnel step configuration\n * @param bindingKey - The funnel binding key configuration\n * @param previousBindingKeyValues - Binding key values from previous step (null for first step)\n * @returns Modified CubeQuery with binding key filter\n */\nexport function buildStepQuery(\n step: FunnelStep,\n bindingKey: FunnelBindingKey,\n previousBindingKeyValues: (string | number)[] | null\n): CubeQuery {\n const query: CubeQuery = { ...step.query }\n\n // Get binding key field for this query\n const bindingKeyField = getBindingKeyField(bindingKey, query)\n\n // Ensure binding key dimension is in the query (needed to extract values for next step)\n if (!query.dimensions?.includes(bindingKeyField)) {\n query.dimensions = [...(query.dimensions || []), bindingKeyField]\n }\n\n // Add IN filter for previous step's binding key values (except for first step)\n if (previousBindingKeyValues && previousBindingKeyValues.length > 0) {\n const inFilter: SimpleFilter = {\n member: bindingKeyField,\n operator: 'in',\n values: previousBindingKeyValues,\n }\n query.filters = [...(query.filters || []), inFilter]\n }\n\n return query\n}\n\n/**\n * Calculate conversion metrics for a step\n *\n * @deprecated This function was used for client-side funnel execution.\n * Server-side funnel execution now calculates metrics directly.\n */\nexport function calculateStepMetrics(\n currentCount: number,\n previousCount: number | null,\n firstStepCount: number\n): {\n conversionRate: number | null\n cumulativeConversionRate: number\n} {\n const conversionRate =\n previousCount !== null && previousCount > 0\n ? currentCount / previousCount\n : null\n\n const cumulativeConversionRate =\n firstStepCount > 0 ? currentCount / firstStepCount : 1.0\n\n return { conversionRate, cumulativeConversionRate }\n}\n\n/**\n * Build a funnel step result from query execution\n *\n * @deprecated This function was used for client-side funnel execution.\n * Server-side funnel execution now returns results directly.\n */\nexport function buildStepResult(\n step: FunnelStep,\n stepIndex: number,\n data: unknown[],\n bindingKeyField: string,\n countUnique: boolean,\n previousCount: number | null,\n firstStepCount: number,\n executionTime: number,\n error: Error | null = null,\n bindingKeyLimit: number = DEFAULT_BINDING_KEY_LIMIT\n): FunnelStepResult {\n // Extract binding key values (with limit applied)\n const extracted = error\n ? { values: [], totalCount: 0, wasTruncated: false }\n : extractBindingKeyValues(data, bindingKeyField, bindingKeyLimit)\n\n // Calculate count based on total unique values (before truncation)\n const count = error\n ? 0\n : countUnique\n ? extracted.totalCount\n : data.length\n\n // Calculate conversion metrics\n const { conversionRate, cumulativeConversionRate } = calculateStepMetrics(\n count,\n previousCount,\n firstStepCount || count // Use current count as first step count if this is step 0\n )\n\n return {\n stepIndex,\n stepName: step.name,\n stepId: step.id,\n data,\n bindingKeyValues: extracted.values,\n bindingKeyTotalCount: extracted.totalCount,\n count,\n conversionRate,\n cumulativeConversionRate,\n executionTime,\n error,\n }\n}\n\n/**\n * Convert step results to chart-ready data format\n *\n * @deprecated Use transformServerFunnelResult() instead.\n * Server-side funnel execution returns data in a compatible format.\n */\nexport function buildFunnelChartData(\n stepResults: FunnelStepResult[]\n): FunnelChartData[] {\n if (stepResults.length === 0) return []\n\n const firstStepCount = stepResults[0]?.count || 0\n\n return stepResults.map((result, index) => ({\n name: result.stepName,\n value: result.count,\n percentage: firstStepCount > 0 ? (result.count / firstStepCount) * 100 : 0,\n conversionRate: result.conversionRate,\n stepIndex: index,\n }))\n}\n\n/**\n * Build the complete funnel execution result\n *\n * @deprecated This function was used for client-side funnel execution.\n * Server-side funnel execution now builds results directly.\n */\nexport function buildFunnelResult(\n config: FunnelConfig,\n stepResults: FunnelStepResult[],\n status: FunnelExecutionResult['status'],\n error: Error | null = null,\n currentStepIndex: number | null = null\n): FunnelExecutionResult {\n const firstStepCount = stepResults[0]?.count || 0\n const lastStepCount = stepResults[stepResults.length - 1]?.count || 0\n const totalExecutionTime = stepResults.reduce((sum, r) => sum + r.executionTime, 0)\n\n return {\n config,\n steps: stepResults,\n summary: {\n totalEntries: firstStepCount,\n totalCompletions: lastStepCount,\n overallConversionRate: firstStepCount > 0 ? lastStepCount / firstStepCount : 0,\n totalExecutionTime,\n },\n chartData: buildFunnelChartData(stepResults),\n status,\n error,\n currentStepIndex,\n }\n}\n\n/**\n * Create an empty/initial funnel result\n *\n * @deprecated This function was used for client-side funnel execution.\n * Server-side funnel execution manages state differently.\n */\nexport function createEmptyFunnelResult(\n config: FunnelConfig\n): FunnelExecutionResult {\n return {\n config,\n steps: [],\n summary: {\n totalEntries: 0,\n totalCompletions: 0,\n overallConversionRate: 0,\n totalExecutionTime: 0,\n },\n chartData: [],\n status: 'idle',\n error: null,\n currentStepIndex: null,\n }\n}\n\n/**\n * Build a FunnelConfig from multi-query config state.\n * Used to convert AnalysisBuilder state to funnel configuration.\n *\n * @deprecated Use the dedicated funnel mode (analysisType === 'funnel') instead.\n * This function supports backward compatibility with legacy multi-query funnel mode\n * (mergeStrategy === 'funnel'). For new implementations, use the dedicated funnel\n * state in the store which provides better UX with step-based configuration.\n *\n * @param queries - Array of CubeQuery objects (one per step)\n * @param bindingKey - The binding key configuration\n * @param stepLabels - Optional labels for each step\n * @param stepTimeToConvert - Optional time window for each step\n * @returns FunnelConfig ready for execution\n */\nexport function buildFunnelConfigFromQueries(\n queries: CubeQuery[],\n bindingKey: FunnelBindingKey,\n stepLabels?: string[],\n stepTimeToConvert?: (string | null)[]\n): FunnelConfig {\n const steps: FunnelStep[] = queries.map((query, index) => ({\n id: `step-${index}`,\n name: stepLabels?.[index] || `Step ${index + 1}`,\n query,\n timeToConvert: stepTimeToConvert?.[index] || undefined,\n }))\n\n return {\n id: `funnel-${Date.now()}`,\n name: 'Funnel Analysis',\n bindingKey,\n steps,\n countUnique: true,\n }\n}\n\n/**\n * Check if funnel execution should stop early\n * (e.g., when no binding key values remain)\n */\nexport function shouldStopFunnelExecution(\n stepResult: FunnelStepResult\n): boolean {\n return stepResult.bindingKeyValues.length === 0 || stepResult.error !== null\n}\n\n/**\n * Metadata fields injected into funnel result data\n * (for compatibility with existing chart/table components)\n */\nexport interface FunnelMetadata {\n __stepIndex: number\n __stepName: string\n __stepId: string\n __conversionRate: number | null\n __cumulativeRate: number\n}\n\n/**\n * Check if data contains funnel metadata\n */\nexport function isFunnelData(data: unknown[]): boolean {\n return (\n data.length > 0 &&\n typeof data[0] === 'object' &&\n data[0] !== null &&\n '__stepIndex' in data[0]\n )\n}\n\n// =============================================================================\n// Server-Side Funnel Support\n// =============================================================================\n\n/**\n * Server-side funnel step definition (for FunnelQueryConfig)\n */\nexport interface ServerFunnelStep {\n name: string\n filter?: SimpleFilter | SimpleFilter[]\n timeToConvert?: string\n}\n\n/**\n * Server-side funnel query configuration\n */\nexport interface ServerFunnelQueryConfig {\n bindingKey: string | FunnelBindingKeyMapping[]\n timeDimension: string | Array<{ cube: string; dimension: string }>\n steps: ServerFunnelStep[]\n includeTimeMetrics?: boolean\n globalTimeWindow?: string\n}\n\n/**\n * Server-side funnel result row\n */\nexport interface ServerFunnelResultRow {\n step: string\n stepIndex: number\n count: number\n conversionRate: number | null\n cumulativeConversionRate: number\n avgSecondsToConvert?: number | null\n medianSecondsToConvert?: number | null\n p90SecondsToConvert?: number | null\n minSecondsToConvert?: number | null\n maxSecondsToConvert?: number | null\n}\n\n/**\n * Query shape for server-side funnel execution\n */\nexport interface ServerFunnelQuery {\n funnel: ServerFunnelQueryConfig\n}\n\n/**\n * Build a server-side funnel query from client state.\n *\n * Transforms the client-side FunnelConfig format into a SemanticQuery\n * with `funnel` property that the server can execute.\n *\n * @param queries - Array of CubeQuery objects (one per step)\n * @param bindingKey - The binding key configuration\n * @param stepLabels - Optional labels for each step\n * @param stepTimeToConvert - Optional time window for each step (ISO 8601 duration)\n * @param includeTimeMetrics - Whether to include time-to-convert metrics\n * @returns Query object with funnel configuration for server execution\n */\nexport function buildServerFunnelQuery(\n queries: CubeQuery[],\n bindingKey: FunnelBindingKey,\n stepLabels?: string[],\n stepTimeToConvert?: (string | null)[],\n includeTimeMetrics: boolean = true\n): ServerFunnelQuery {\n // 1. Unwrap binding key from client format (client wraps in { dimension: ... })\n const serverBindingKey = bindingKey.dimension\n\n // 2. Extract time dimension from queries\n const timeDimension = extractTimeDimensionFromQueries(queries, bindingKey)\n\n // 3. Convert each CubeQuery to a server FunnelStep\n const steps: ServerFunnelStep[] = queries.map((query, index) => {\n const step: ServerFunnelStep = {\n name: stepLabels?.[index] || `Step ${index + 1}`,\n }\n\n // Extract filters from query\n const filter = extractFiltersFromQuery(query)\n if (filter) {\n step.filter = filter\n }\n\n // Add time-to-convert constraint if specified\n const timeConstraint = stepTimeToConvert?.[index]\n if (timeConstraint) {\n step.timeToConvert = timeConstraint\n }\n\n return step\n })\n\n return {\n funnel: {\n bindingKey: serverBindingKey,\n timeDimension,\n steps,\n includeTimeMetrics,\n },\n }\n}\n\n/**\n * Extract time dimension from queries.\n *\n * For single-cube funnels, returns the first time dimension found.\n * For cross-cube funnels (with binding key mappings), builds a mapping array.\n *\n * @param queries - Array of CubeQuery objects\n * @param bindingKey - The binding key configuration (to detect cross-cube)\n * @returns Time dimension string or mapping array\n */\nfunction extractTimeDimensionFromQueries(\n queries: CubeQuery[],\n bindingKey: FunnelBindingKey\n): string | Array<{ cube: string; dimension: string }> {\n // Check if this is a cross-cube funnel\n const isCrossCube = Array.isArray(bindingKey.dimension)\n\n if (!isCrossCube) {\n // Single-cube: find first time dimension\n for (const query of queries) {\n if (query.timeDimensions?.length) {\n return query.timeDimensions[0].dimension\n }\n }\n // Fallback: try to infer from measures/dimensions\n const cubeName = getCubeNameFromQuery(queries[0])\n if (cubeName) {\n return `${cubeName}.createdAt` // Common convention\n }\n throw new Error('Funnel requires at least one time dimension in step queries')\n }\n\n // Cross-cube: build mapping for each cube\n const mappings: Array<{ cube: string; dimension: string }> = []\n const bindingMappings = bindingKey.dimension as FunnelBindingKeyMapping[]\n\n for (const mapping of bindingMappings) {\n // Find time dimension for this cube from queries\n const cubeQuery = queries.find(q => getCubeNameFromQuery(q) === mapping.cube)\n if (cubeQuery?.timeDimensions?.length) {\n mappings.push({\n cube: mapping.cube,\n dimension: cubeQuery.timeDimensions[0].dimension,\n })\n } else {\n // Fallback to common convention\n mappings.push({\n cube: mapping.cube,\n dimension: `${mapping.cube}.createdAt`,\n })\n }\n }\n\n return mappings\n}\n\n/**\n * Extract filters from a CubeQuery for server funnel step.\n *\n * @param query - The CubeQuery\n * @returns Filter or array of filters, or undefined if no filters\n */\nfunction extractFiltersFromQuery(query: CubeQuery): SimpleFilter | SimpleFilter[] | undefined {\n if (!query.filters?.length) {\n return undefined\n }\n\n // Filter out any binding key filters (server handles those)\n const filters = query.filters.filter(f => {\n // Keep only SimpleFilter with member (not grouped filters for now)\n if ('member' in f) {\n return true\n }\n return false\n }) as SimpleFilter[]\n\n if (filters.length === 0) {\n return undefined\n }\n\n if (filters.length === 1) {\n return filters[0]\n }\n\n return filters\n}\n\n/**\n * Transform server funnel result rows to client FunnelChartData format.\n *\n * Maps the server's FunnelResultRow[] to the client's FunnelChartData[]\n * format expected by FunnelChart component.\n *\n * @param serverResult - Raw data from server (FunnelResultRow[])\n * @param stepNames - Optional step names to override server names\n * @returns FunnelChartData[] ready for FunnelChart component\n */\nexport function transformServerFunnelResult(\n serverResult: unknown[],\n stepNames?: string[]\n): FunnelChartData[] {\n if (!serverResult?.length) {\n return []\n }\n\n const rows = serverResult as ServerFunnelResultRow[]\n\n return rows.map((row, index) => ({\n // Map server fields to client format\n name: stepNames?.[index] || row.step,\n value: row.count,\n // Convert cumulative rate to percentage\n percentage: row.cumulativeConversionRate * 100,\n // Convert step-to-step rate to percentage (null for first step)\n conversionRate: row.conversionRate !== null ? row.conversionRate * 100 : null,\n stepIndex: row.stepIndex,\n // Include time metrics for optional display\n avgSecondsToConvert: row.avgSecondsToConvert,\n medianSecondsToConvert: row.medianSecondsToConvert,\n p90SecondsToConvert: row.p90SecondsToConvert,\n }))\n}\n\n/**\n * Format a duration in seconds to a human-readable string.\n *\n * @param seconds - Duration in seconds\n * @returns Formatted string (e.g., \"45s\", \"2.5m\", \"1.3h\", \"3.2d\")\n */\nexport function formatDuration(seconds: number | null | undefined): string {\n if (seconds === null || seconds === undefined) {\n return '-'\n }\n\n if (seconds < 60) {\n return `${Math.round(seconds)}s`\n }\n\n if (seconds < 3600) {\n const minutes = seconds / 60\n return minutes < 10 ? `${minutes.toFixed(1)}m` : `${Math.round(minutes)}m`\n }\n\n if (seconds < 86400) {\n const hours = seconds / 3600\n return hours < 10 ? `${hours.toFixed(1)}h` : `${Math.round(hours)}h`\n }\n\n const days = seconds / 86400\n return days < 10 ? `${days.toFixed(1)}d` : `${Math.round(days)}d`\n}\n","/**\n * FunnelChart Component\n *\n * Visualizes funnel data showing conversion rates between steps.\n * Uses horizontal bars with percentage widths to represent the funnel shape.\n * Works with data from useFunnelQuery hook which provides FunnelChartData.\n *\n * When displayConfig.showFunnelTimeMetrics is enabled, displays time-to-convert\n * metrics from server-side funnel execution (avg, median, P90 seconds).\n */\n\nimport React, { useMemo } from 'react'\nimport { FunnelChart as RechartsFunnelChart, Funnel, LabelList, Tooltip, Cell, ResponsiveContainer } from 'recharts'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport type { ChartProps } from '../../types'\nimport type { FunnelChartData } from '../../types/funnel'\nimport { formatDuration } from '../../utils/funnelExecution'\n\n// Color gradient for funnel steps (darker to lighter)\nconst FUNNEL_COLORS = [\n '#3B82F6', // blue-500\n '#60A5FA', // blue-400\n '#93C5FD', // blue-300\n '#BFDBFE', // blue-200\n '#DBEAFE', // blue-100\n]\n\n/**\n * Get color for a funnel step\n */\nfunction getStepColor(index: number, colorPalette?: string[]): string {\n const colors = colorPalette || FUNNEL_COLORS\n return colors[index % colors.length]\n}\n\n/**\n * Check if data is funnel data format\n */\nfunction isFunnelData(data: unknown[]): data is FunnelChartData[] {\n if (!data || data.length === 0) return false\n const first = data[0]\n return (\n typeof first === 'object' &&\n first !== null &&\n 'name' in first &&\n 'value' in first &&\n 'percentage' in first\n )\n}\n\n/**\n * Render time metrics based on config options - returns array of lines for vertical stacking\n */\nfunction getTimeMetricsLines(\n step: FunnelChartData,\n showAvg: boolean,\n showMedian: boolean,\n showP90: boolean\n): string[] {\n const lines: string[] = []\n if (showAvg && step.avgSecondsToConvert != null) {\n lines.push(`Avg: ${formatDuration(step.avgSecondsToConvert)}`)\n }\n if (showMedian && step.medianSecondsToConvert != null) {\n lines.push(`Med: ${formatDuration(step.medianSecondsToConvert)}`)\n }\n if (showP90 && step.p90SecondsToConvert != null) {\n lines.push(`P90: ${formatDuration(step.p90SecondsToConvert)}`)\n }\n return lines\n}\n\n/**\n * FunnelChart Component\n *\n * Renders a funnel visualization from FunnelChartData array.\n * Shows each step as a horizontal bar with width proportional to count.\n * Displays conversion rates between steps.\n */\nconst FunnelChart = React.memo(function FunnelChart({\n data,\n height = '100%',\n colorPalette,\n displayConfig,\n}: ChartProps) {\n // Get display config options\n const customStepLabels = displayConfig?.funnelStepLabels\n const orientation = displayConfig?.funnelOrientation || 'horizontal'\n const isVertical = orientation === 'vertical'\n const funnelStyle = displayConfig?.funnelStyle ?? 'bars'\n const showConversion = displayConfig?.showFunnelConversion ?? true\n\n // Time metrics - individual toggles with backward compat for showFunnelTimeMetrics\n const showAvgTime = displayConfig?.showFunnelAvgTime ??\n (displayConfig?.showFunnelTimeMetrics ?? false) // backward compat\n const showMedianTime = displayConfig?.showFunnelMedianTime ?? false\n const showP90Time = displayConfig?.showFunnelP90Time ?? false\n\n // Transform data if needed\n const funnelData = useMemo<FunnelChartData[]>(() => {\n if (!data || data.length === 0) return []\n\n // If already funnel data format, use directly\n if (isFunnelData(data)) {\n return data\n }\n\n // Try to convert from raw query results\n // Look for common patterns: step/name, count/value, percentage\n return data.map((row, index) => {\n const record = row as Record<string, unknown>\n\n // Find name field\n const nameField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('step') || k.toLowerCase().includes('name') || k === '__stepName'\n )\n const name = nameField ? String(record[nameField]) : `Step ${index + 1}`\n\n // Find value field\n const valueField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('count') || k.toLowerCase().includes('value') || k === '__count'\n )\n const value = valueField ? Number(record[valueField]) || 0 : 0\n\n // Find percentage field\n const percentField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('percent') || k === '__percentage'\n )\n const percentage = percentField ? Number(record[percentField]) || 0 : 0\n\n // Find conversion rate field\n const convRateField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('conversion') || k === '__conversionRate'\n )\n const conversionRate = convRateField ? Number(record[convRateField]) || null : null\n\n return {\n name,\n value,\n percentage,\n conversionRate,\n stepIndex: index,\n }\n })\n }, [data])\n\n // Calculate first step value for percentage calculations\n const firstStepValue = funnelData[0]?.value || 0\n\n // Handle no data\n if (!data || data.length === 0 || funnelData.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No funnel data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n Configure a funnel with at least 2 steps and a binding key\n </div>\n </div>\n </div>\n )\n }\n\n const paletteColors = colorPalette?.colors || CHART_COLORS\n\n // Render Recharts Funnel style (trapezoid shape)\n if (funnelStyle === 'funnel') {\n // Recharts FunnelChart layout: 'horizontal' = funnel flows left-to-right, 'vertical' = top-to-bottom (default)\n // Our config: 'horizontal' orientation = standard top-to-bottom funnel\n // 'vertical' orientation = left-to-right funnel\n const rechartsLayout: 'horizontal' | 'vertical' = isVertical ? 'horizontal' : 'vertical'\n\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n <div className=\"dc:flex-1\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsFunnelChart layout={rechartsLayout} accessibilityLayer={false}>\n <Tooltip\n formatter={(value) => typeof value === 'number' ? value.toLocaleString() : String(value)}\n contentStyle={{\n backgroundColor: 'var(--dc-surface)',\n border: '1px solid var(--dc-border)',\n borderRadius: '4px',\n }}\n />\n <Funnel\n dataKey=\"value\"\n nameKey=\"name\"\n data={funnelData}\n isAnimationActive\n >\n {funnelData.map((_, index) => (\n <Cell key={`cell-${index}`} fill={getStepColor(index, paletteColors)} />\n ))}\n <LabelList\n position=\"right\"\n dataKey=\"name\"\n fill=\"var(--dc-text)\"\n style={{ fontSize: '12px' }}\n />\n <LabelList\n position=\"center\"\n dataKey=\"percentage\"\n formatter={(v) => typeof v === 'number' ? `${v.toFixed(1)}%` : String(v)}\n fill=\"#fff\"\n style={{ fontSize: '11px', fontWeight: 500 }}\n />\n </Funnel>\n </RechartsFunnelChart>\n </ResponsiveContainer>\n </div>\n {/* Summary Footer */}\n {!displayConfig?.hideSummaryFooter && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{funnelData.length}</span> steps\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Overall:</span>{' '}\n <span className=\"dc:font-medium\">\n {firstStepValue > 0\n ? `${((funnelData[funnelData.length - 1]?.value || 0) / firstStepValue * 100).toFixed(1)}%`\n : '0%'}\n </span>\n </div>\n <div className=\"text-dc-text-muted\">\n {funnelData[funnelData.length - 1]?.value.toLocaleString() || 0} / {firstStepValue.toLocaleString()} completed\n </div>\n </div>\n </div>\n )}\n </div>\n )\n }\n\n // Render vertical orientation (bars grow from bottom to top, steps laid out horizontally)\n if (isVertical) {\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n {/* Funnel Steps - Vertical Layout */}\n <div className=\"dc:flex-1 dc:flex dc:items-end dc:justify-center dc:gap-4 dc:px-4 dc:py-3 dc:overflow-hidden\">\n {funnelData.map((step, index) => {\n const heightPercent = firstStepValue > 0 ? (step.value / firstStepValue) * 100 : 0\n const prevStep = index > 0 ? funnelData[index - 1] : null\n const stepConversionRate = prevStep && prevStep.value > 0\n ? (step.value / prevStep.value) * 100\n : null\n\n // Use custom label if provided, otherwise fall back to step name\n const displayName = customStepLabels?.[index] || step.name\n\n const timeMetricsLines = getTimeMetricsLines(step, showAvgTime, showMedianTime, showP90Time)\n const metricsCount = timeMetricsLines.length\n\n return (\n <div key={step.name} className=\"dc:flex dc:flex-col dc:items-center dc:gap-2 dc:flex-1 dc:max-w-32 dc:h-full\">\n {/* Conversion Rate from Previous (top) */}\n <div className={`${metricsCount > 0 ? (metricsCount > 1 ? 'dc:min-h-16' : 'dc:min-h-10') : 'dc:h-5'} dc:flex-shrink-0 dc:text-center`}>\n {stepConversionRate !== null ? (\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {showConversion && <span>→ {stepConversionRate.toFixed(1)}%</span>}\n {/* Time metrics (when enabled) */}\n {metricsCount > 0 && (\n <div className=\"text-dc-text-muted dc:mt-0.5 dc:space-y-0.5\">\n {timeMetricsLines.map((line, i) => (\n <div key={i}>⏱ {line}</div>\n ))}\n </div>\n )}\n </div>\n ) : (\n <div className=\"dc:text-xs text-dc-text-muted\">—</div>\n )}\n </div>\n\n {/* Bar Container */}\n <div className=\"dc:flex-1 dc:w-full dc:relative dc:min-h-12\">\n {/* Background Track */}\n <div className=\"dc:absolute dc:inset-0 bg-dc-surface-secondary dc:rounded-sm\" />\n\n {/* Filled Bar (grows from bottom) */}\n <div\n className=\"dc:absolute dc:bottom-0 dc:left-0 dc:right-0 dc:rounded-sm dc:transition-all dc:duration-300\"\n style={{\n height: `${Math.max(heightPercent, 5)}%`,\n backgroundColor: getStepColor(index, paletteColors),\n }}\n />\n\n {/* Percentage Label on Bar */}\n <div\n className=\"dc:absolute dc:bottom-0 dc:left-0 dc:right-0 dc:flex dc:items-end dc:justify-center dc:pb-1 dc:pointer-events-none\"\n style={{ height: `${Math.max(heightPercent, 20)}%` }}\n >\n <span className=\"dc:text-xs dc:font-medium text-white dc:drop-shadow-sm\">\n {step.percentage?.toFixed(1) ?? heightPercent.toFixed(1)}%\n </span>\n </div>\n </div>\n\n {/* Step Label (bottom) */}\n <div className=\"dc:flex-shrink-0 dc:text-center\">\n <div className=\"dc:text-sm dc:font-medium text-dc-text dc:truncate\" title={displayName}>\n {displayName}\n </div>\n <div className=\"dc:text-xs text-dc-text-muted\">\n {step.value.toLocaleString()}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n\n {/* Summary Footer */}\n {!displayConfig?.hideSummaryFooter && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{funnelData.length}</span> steps\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Overall:</span>{' '}\n <span className=\"dc:font-medium\">\n {firstStepValue > 0\n ? `${((funnelData[funnelData.length - 1]?.value || 0) / firstStepValue * 100).toFixed(1)}%`\n : '0%'}\n </span>\n </div>\n <div className=\"text-dc-text-muted\">\n {funnelData[funnelData.length - 1]?.value.toLocaleString() || 0} / {firstStepValue.toLocaleString()} completed\n </div>\n </div>\n </div>\n )}\n </div>\n )\n }\n\n // Render horizontal orientation (default - bars grow left to right, steps stacked vertically)\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n {/* Funnel Steps - Horizontal Layout */}\n <div className=\"dc:flex-1 dc:flex dc:flex-col dc:justify-center dc:gap-2 dc:px-4 dc:py-3 dc:overflow-hidden\">\n {funnelData.map((step, index) => {\n const widthPercent = firstStepValue > 0 ? (step.value / firstStepValue) * 100 : 0\n const prevStep = index > 0 ? funnelData[index - 1] : null\n const stepConversionRate = prevStep && prevStep.value > 0\n ? (step.value / prevStep.value) * 100\n : null\n\n // Use custom label if provided, otherwise fall back to step name\n const displayName = customStepLabels?.[index] || step.name\n const timeMetricsLines = getTimeMetricsLines(step, showAvgTime, showMedianTime, showP90Time)\n const metricsCount = timeMetricsLines.length\n\n return (\n <div key={step.name} className=\"dc:flex dc:items-center dc:gap-3\">\n {/* Step Label */}\n <div className=\"dc:w-24 dc:flex-shrink-0 dc:text-right\">\n <div className=\"dc:text-sm dc:font-medium text-dc-text dc:truncate\" title={displayName}>\n {displayName}\n </div>\n <div className=\"dc:text-xs text-dc-text-muted\">\n {step.value.toLocaleString()}\n </div>\n </div>\n\n {/* Bar Container */}\n <div className=\"dc:flex-1 dc:relative\">\n {/* Background Track */}\n <div className=\"dc:w-full dc:h-8 bg-dc-surface-secondary dc:rounded-sm\" />\n\n {/* Filled Bar */}\n <div\n className=\"dc:absolute dc:top-0 dc:left-0 dc:h-8 dc:rounded-sm dc:transition-all dc:duration-300\"\n style={{\n width: `${Math.max(widthPercent, 2)}%`,\n backgroundColor: getStepColor(index, paletteColors),\n }}\n />\n\n {/* Percentage Label on Bar */}\n <div\n className=\"dc:absolute dc:top-0 dc:left-0 dc:h-8 dc:flex dc:items-center dc:px-2 dc:pointer-events-none\"\n style={{ width: `${Math.max(widthPercent, 20)}%` }}\n >\n <span className=\"dc:text-xs dc:font-medium text-white dc:drop-shadow-sm\">\n {step.percentage?.toFixed(1) ?? widthPercent.toFixed(1)}%\n </span>\n </div>\n </div>\n\n {/* Conversion Rate from Previous */}\n <div className={`${metricsCount > 0 ? (metricsCount > 1 ? 'dc:w-36' : 'dc:w-28') : 'dc:w-16'} dc:flex-shrink-0 dc:text-left`}>\n {stepConversionRate !== null ? (\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {showConversion && <span>↓ {stepConversionRate.toFixed(1)}%</span>}\n {/* Time metrics (when enabled) */}\n {metricsCount > 0 && (\n <div className=\"text-dc-text-muted dc:mt-0.5 dc:space-y-0.5\">\n {timeMetricsLines.map((line, i) => (\n <div key={i}>⏱ {line}</div>\n ))}\n </div>\n )}\n </div>\n ) : (\n <div className=\"dc:text-xs text-dc-text-muted\">—</div>\n )}\n </div>\n </div>\n )\n })}\n </div>\n\n {/* Summary Footer */}\n {!displayConfig?.hideSummaryFooter && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{funnelData.length}</span> steps\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Overall:</span>{' '}\n <span className=\"dc:font-medium\">\n {firstStepValue > 0\n ? `${((funnelData[funnelData.length - 1]?.value || 0) / firstStepValue * 100).toFixed(1)}%`\n : '0%'}\n </span>\n </div>\n <div className=\"text-dc-text-muted\">\n {funnelData[funnelData.length - 1]?.value.toLocaleString() || 0} / {firstStepValue.toLocaleString()} completed\n </div>\n </div>\n </div>\n )}\n </div>\n )\n})\n\nexport default FunnelChart\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chart-gauge-D5J4gRky.js","names":["epsilon","pi","tau","constant","tau","pi"],"sources":["../../../node_modules/d3-shape/src/constant.js","../../../node_modules/d3-shape/src/math.js","../../../node_modules/d3-path/src/path.js","../../../node_modules/d3-shape/src/path.js","../../../node_modules/d3-shape/src/arc.js","../../../src/client/components/charts/GaugeChart.tsx"],"sourcesContent":["export default function(x) {\n return function constant() {\n return x;\n };\n}\n","export const abs = Math.abs;\nexport const atan2 = Math.atan2;\nexport const cos = Math.cos;\nexport const max = Math.max;\nexport const min = Math.min;\nexport const sin = Math.sin;\nexport const sqrt = Math.sqrt;\n\nexport const epsilon = 1e-12;\nexport const pi = Math.PI;\nexport const halfPi = pi / 2;\nexport const tau = 2 * pi;\n\nexport function acos(x) {\n return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);\n}\n\nexport function asin(x) {\n return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);\n}\n","const pi = Math.PI,\n tau = 2 * pi,\n epsilon = 1e-6,\n tauEpsilon = tau - epsilon;\n\nfunction append(strings) {\n this._ += strings[0];\n for (let i = 1, n = strings.length; i < n; ++i) {\n this._ += arguments[i] + strings[i];\n }\n}\n\nfunction appendRound(digits) {\n let d = Math.floor(digits);\n if (!(d >= 0)) throw new Error(`invalid digits: ${digits}`);\n if (d > 15) return append;\n const k = 10 ** d;\n return function(strings) {\n this._ += strings[0];\n for (let i = 1, n = strings.length; i < n; ++i) {\n this._ += Math.round(arguments[i] * k) / k + strings[i];\n }\n };\n}\n\nexport class Path {\n constructor(digits) {\n this._x0 = this._y0 = // start of current subpath\n this._x1 = this._y1 = null; // end of current subpath\n this._ = \"\";\n this._append = digits == null ? append : appendRound(digits);\n }\n moveTo(x, y) {\n this._append`M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}`;\n }\n closePath() {\n if (this._x1 !== null) {\n this._x1 = this._x0, this._y1 = this._y0;\n this._append`Z`;\n }\n }\n lineTo(x, y) {\n this._append`L${this._x1 = +x},${this._y1 = +y}`;\n }\n quadraticCurveTo(x1, y1, x, y) {\n this._append`Q${+x1},${+y1},${this._x1 = +x},${this._y1 = +y}`;\n }\n bezierCurveTo(x1, y1, x2, y2, x, y) {\n this._append`C${+x1},${+y1},${+x2},${+y2},${this._x1 = +x},${this._y1 = +y}`;\n }\n arcTo(x1, y1, x2, y2, r) {\n x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(`negative radius: ${r}`);\n\n let x0 = this._x1,\n y0 = this._y1,\n x21 = x2 - x1,\n y21 = y2 - y1,\n x01 = x0 - x1,\n y01 = y0 - y1,\n l01_2 = x01 * x01 + y01 * y01;\n\n // Is this path empty? Move to (x1,y1).\n if (this._x1 === null) {\n this._append`M${this._x1 = x1},${this._y1 = y1}`;\n }\n\n // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.\n else if (!(l01_2 > epsilon));\n\n // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?\n // Equivalently, is (x1,y1) coincident with (x2,y2)?\n // Or, is the radius zero? Line to (x1,y1).\n else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {\n this._append`L${this._x1 = x1},${this._y1 = y1}`;\n }\n\n // Otherwise, draw an arc!\n else {\n let x20 = x2 - x0,\n y20 = y2 - y0,\n l21_2 = x21 * x21 + y21 * y21,\n l20_2 = x20 * x20 + y20 * y20,\n l21 = Math.sqrt(l21_2),\n l01 = Math.sqrt(l01_2),\n l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),\n t01 = l / l01,\n t21 = l / l21;\n\n // If the start tangent is not coincident with (x0,y0), line to.\n if (Math.abs(t01 - 1) > epsilon) {\n this._append`L${x1 + t01 * x01},${y1 + t01 * y01}`;\n }\n\n this._append`A${r},${r},0,0,${+(y01 * x20 > x01 * y20)},${this._x1 = x1 + t21 * x21},${this._y1 = y1 + t21 * y21}`;\n }\n }\n arc(x, y, r, a0, a1, ccw) {\n x = +x, y = +y, r = +r, ccw = !!ccw;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(`negative radius: ${r}`);\n\n let dx = r * Math.cos(a0),\n dy = r * Math.sin(a0),\n x0 = x + dx,\n y0 = y + dy,\n cw = 1 ^ ccw,\n da = ccw ? a0 - a1 : a1 - a0;\n\n // Is this path empty? Move to (x0,y0).\n if (this._x1 === null) {\n this._append`M${x0},${y0}`;\n }\n\n // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).\n else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {\n this._append`L${x0},${y0}`;\n }\n\n // Is this arc empty? We’re done.\n if (!r) return;\n\n // Does the angle go the wrong way? Flip the direction.\n if (da < 0) da = da % tau + tau;\n\n // Is this a complete circle? Draw two arcs to complete the circle.\n if (da > tauEpsilon) {\n this._append`A${r},${r},0,1,${cw},${x - dx},${y - dy}A${r},${r},0,1,${cw},${this._x1 = x0},${this._y1 = y0}`;\n }\n\n // Is this arc non-empty? Draw an arc!\n else if (da > epsilon) {\n this._append`A${r},${r},0,${+(da >= pi)},${cw},${this._x1 = x + r * Math.cos(a1)},${this._y1 = y + r * Math.sin(a1)}`;\n }\n }\n rect(x, y, w, h) {\n this._append`M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}h${w = +w}v${+h}h${-w}Z`;\n }\n toString() {\n return this._;\n }\n}\n\nexport function path() {\n return new Path;\n}\n\n// Allow instanceof d3.path\npath.prototype = Path.prototype;\n\nexport function pathRound(digits = 3) {\n return new Path(+digits);\n}\n","import {Path} from \"d3-path\";\n\nexport function withPath(shape) {\n let digits = 3;\n\n shape.digits = function(_) {\n if (!arguments.length) return digits;\n if (_ == null) {\n digits = null;\n } else {\n const d = Math.floor(_);\n if (!(d >= 0)) throw new RangeError(`invalid digits: ${_}`);\n digits = d;\n }\n return shape;\n };\n\n return () => new Path(digits);\n}\n","import constant from \"./constant.js\";\nimport {abs, acos, asin, atan2, cos, epsilon, halfPi, max, min, pi, sin, sqrt, tau} from \"./math.js\";\nimport {withPath} from \"./path.js\";\n\nfunction arcInnerRadius(d) {\n return d.innerRadius;\n}\n\nfunction arcOuterRadius(d) {\n return d.outerRadius;\n}\n\nfunction arcStartAngle(d) {\n return d.startAngle;\n}\n\nfunction arcEndAngle(d) {\n return d.endAngle;\n}\n\nfunction arcPadAngle(d) {\n return d && d.padAngle; // Note: optional!\n}\n\nfunction intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n var x10 = x1 - x0, y10 = y1 - y0,\n x32 = x3 - x2, y32 = y3 - y2,\n t = y32 * x10 - x32 * y10;\n if (t * t < epsilon) return;\n t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / t;\n return [x0 + t * x10, y0 + t * y10];\n}\n\n// Compute perpendicular offset line of length rc.\n// http://mathworld.wolfram.com/Circle-LineIntersection.html\nfunction cornerTangents(x0, y0, x1, y1, r1, rc, cw) {\n var x01 = x0 - x1,\n y01 = y0 - y1,\n lo = (cw ? rc : -rc) / sqrt(x01 * x01 + y01 * y01),\n ox = lo * y01,\n oy = -lo * x01,\n x11 = x0 + ox,\n y11 = y0 + oy,\n x10 = x1 + ox,\n y10 = y1 + oy,\n x00 = (x11 + x10) / 2,\n y00 = (y11 + y10) / 2,\n dx = x10 - x11,\n dy = y10 - y11,\n d2 = dx * dx + dy * dy,\n r = r1 - rc,\n D = x11 * y10 - x10 * y11,\n d = (dy < 0 ? -1 : 1) * sqrt(max(0, r * r * d2 - D * D)),\n cx0 = (D * dy - dx * d) / d2,\n cy0 = (-D * dx - dy * d) / d2,\n cx1 = (D * dy + dx * d) / d2,\n cy1 = (-D * dx + dy * d) / d2,\n dx0 = cx0 - x00,\n dy0 = cy0 - y00,\n dx1 = cx1 - x00,\n dy1 = cy1 - y00;\n\n // Pick the closer of the two intersection points.\n // TODO Is there a faster way to determine which intersection to use?\n if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;\n\n return {\n cx: cx0,\n cy: cy0,\n x01: -ox,\n y01: -oy,\n x11: cx0 * (r1 / r - 1),\n y11: cy0 * (r1 / r - 1)\n };\n}\n\nexport default function() {\n var innerRadius = arcInnerRadius,\n outerRadius = arcOuterRadius,\n cornerRadius = constant(0),\n padRadius = null,\n startAngle = arcStartAngle,\n endAngle = arcEndAngle,\n padAngle = arcPadAngle,\n context = null,\n path = withPath(arc);\n\n function arc() {\n var buffer,\n r,\n r0 = +innerRadius.apply(this, arguments),\n r1 = +outerRadius.apply(this, arguments),\n a0 = startAngle.apply(this, arguments) - halfPi,\n a1 = endAngle.apply(this, arguments) - halfPi,\n da = abs(a1 - a0),\n cw = a1 > a0;\n\n if (!context) context = buffer = path();\n\n // Ensure that the outer radius is always larger than the inner radius.\n if (r1 < r0) r = r1, r1 = r0, r0 = r;\n\n // Is it a point?\n if (!(r1 > epsilon)) context.moveTo(0, 0);\n\n // Or is it a circle or annulus?\n else if (da > tau - epsilon) {\n context.moveTo(r1 * cos(a0), r1 * sin(a0));\n context.arc(0, 0, r1, a0, a1, !cw);\n if (r0 > epsilon) {\n context.moveTo(r0 * cos(a1), r0 * sin(a1));\n context.arc(0, 0, r0, a1, a0, cw);\n }\n }\n\n // Or is it a circular or annular sector?\n else {\n var a01 = a0,\n a11 = a1,\n a00 = a0,\n a10 = a1,\n da0 = da,\n da1 = da,\n ap = padAngle.apply(this, arguments) / 2,\n rp = (ap > epsilon) && (padRadius ? +padRadius.apply(this, arguments) : sqrt(r0 * r0 + r1 * r1)),\n rc = min(abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),\n rc0 = rc,\n rc1 = rc,\n t0,\n t1;\n\n // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.\n if (rp > epsilon) {\n var p0 = asin(rp / r0 * sin(ap)),\n p1 = asin(rp / r1 * sin(ap));\n if ((da0 -= p0 * 2) > epsilon) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;\n else da0 = 0, a00 = a10 = (a0 + a1) / 2;\n if ((da1 -= p1 * 2) > epsilon) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;\n else da1 = 0, a01 = a11 = (a0 + a1) / 2;\n }\n\n var x01 = r1 * cos(a01),\n y01 = r1 * sin(a01),\n x10 = r0 * cos(a10),\n y10 = r0 * sin(a10);\n\n // Apply rounded corners?\n if (rc > epsilon) {\n var x11 = r1 * cos(a11),\n y11 = r1 * sin(a11),\n x00 = r0 * cos(a00),\n y00 = r0 * sin(a00),\n oc;\n\n // Restrict the corner radius according to the sector angle. If this\n // intersection fails, it’s probably because the arc is too small, so\n // disable the corner radius entirely.\n if (da < pi) {\n if (oc = intersect(x01, y01, x00, y00, x11, y11, x10, y10)) {\n var ax = x01 - oc[0],\n ay = y01 - oc[1],\n bx = x11 - oc[0],\n by = y11 - oc[1],\n kc = 1 / sin(acos((ax * bx + ay * by) / (sqrt(ax * ax + ay * ay) * sqrt(bx * bx + by * by))) / 2),\n lc = sqrt(oc[0] * oc[0] + oc[1] * oc[1]);\n rc0 = min(rc, (r0 - lc) / (kc - 1));\n rc1 = min(rc, (r1 - lc) / (kc + 1));\n } else {\n rc0 = rc1 = 0;\n }\n }\n }\n\n // Is the sector collapsed to a line?\n if (!(da1 > epsilon)) context.moveTo(x01, y01);\n\n // Does the sector’s outer ring have rounded corners?\n else if (rc1 > epsilon) {\n t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);\n t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);\n\n context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc1, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r1, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw);\n context.arc(t1.cx, t1.cy, rc1, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the outer ring just a circular arc?\n else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);\n\n // Is there no inner ring, and it’s a circular sector?\n // Or perhaps it’s an annular sector collapsed due to padding?\n if (!(r0 > epsilon) || !(da0 > epsilon)) context.lineTo(x10, y10);\n\n // Does the sector’s inner ring (or point) have rounded corners?\n else if (rc0 > epsilon) {\n t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);\n t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);\n\n context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc0, atan2(t0.y01, t0.x01), atan2(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r0, atan2(t0.cy + t0.y11, t0.cx + t0.x11), atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw);\n context.arc(t1.cx, t1.cy, rc0, atan2(t1.y11, t1.x11), atan2(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the inner ring just a circular arc?\n else context.arc(0, 0, r0, a10, a00, cw);\n }\n\n context.closePath();\n\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n arc.centroid = function() {\n var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,\n a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi / 2;\n return [cos(a) * r, sin(a) * r];\n };\n\n arc.innerRadius = function(_) {\n return arguments.length ? (innerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : innerRadius;\n };\n\n arc.outerRadius = function(_) {\n return arguments.length ? (outerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : outerRadius;\n };\n\n arc.cornerRadius = function(_) {\n return arguments.length ? (cornerRadius = typeof _ === \"function\" ? _ : constant(+_), arc) : cornerRadius;\n };\n\n arc.padRadius = function(_) {\n return arguments.length ? (padRadius = _ == null ? null : typeof _ === \"function\" ? _ : constant(+_), arc) : padRadius;\n };\n\n arc.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : startAngle;\n };\n\n arc.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : endAngle;\n };\n\n arc.padAngle = function(_) {\n return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : constant(+_), arc) : padAngle;\n };\n\n arc.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), arc) : context;\n };\n\n return arc;\n}\n","import React, { useLayoutEffect, useMemo, useRef, useState } from 'react'\nimport { arc } from 'd3-shape'\nimport { formatAxisValue } from '../../utils/chartUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps, ThresholdBand } from '../../types'\n\nconst START_ANGLE = -Math.PI * 0.75\nconst END_ANGLE = Math.PI * 0.75\nconst TRACK_COLOR = '#e2e8f0'\nconst DEFAULT_FILL = '#6366f1'\n\nfunction parseNum(v: unknown): number | null {\n if (v === undefined || v === null) return null\n const n = typeof v === 'number' ? v : parseFloat(String(v))\n return isNaN(n) ? null : n\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, v))\n}\n\nfunction valueToAngle(value: number, min: number, max: number): number {\n const span = max === min ? 1 : max - min\n const t = clamp((value - min) / span, 0, 1)\n return START_ANGLE + t * (END_ANGLE - START_ANGLE)\n}\n\nfunction resolveColor(fraction: number, thresholds: ThresholdBand[]): string {\n const sorted = [...thresholds].sort((a, b) => a.value - b.value)\n let color = DEFAULT_FILL\n for (const t of sorted) {\n if (fraction >= t.value) color = t.color\n }\n return color\n}\n\nfunction buildArcPath(\n innerRadius: number,\n outerRadius: number,\n startAngle: number,\n endAngle: number\n): string {\n const pathFn = arc()\n return pathFn({ innerRadius, outerRadius, startAngle, endAngle }) ?? ''\n}\n\nfunction Needle({ angle, radius }: { angle: number; radius: number }) {\n const needleLen = radius * 0.72\n const needleBase = radius * 0.06\n const x = Math.cos(angle - Math.PI / 2) * needleLen\n const y = Math.sin(angle - Math.PI / 2) * needleLen\n return (\n <g data-testid=\"gauge-needle\">\n <circle r={needleBase} fill=\"currentColor\" className=\"text-dc-text-secondary\" />\n <line\n x1={0}\n y1={0}\n x2={x}\n y2={y}\n stroke=\"currentColor\"\n strokeWidth={3}\n strokeLinecap=\"round\"\n className=\"text-dc-text-secondary\"\n />\n </g>\n )\n}\n\nconst GaugeChart = React.memo(function GaugeChart({\n data,\n chartConfig,\n displayConfig = {},\n height = '100%',\n}: ChartProps) {\n const getFieldLabel = useCubeFieldLabel()\n const containerRef = useRef<HTMLDivElement>(null)\n const [dimensions, setDimensions] = useState({ width: 0, height: 0 })\n\n useLayoutEffect(() => {\n const el = containerRef.current\n if (!el) return\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height: h } = entry.contentRect\n if (width > 0 && h > 0) setDimensions({ width, height: h })\n }\n })\n observer.observe(el)\n const rect = el.getBoundingClientRect()\n if (rect.width > 0 && rect.height > 0) setDimensions({ width: rect.width, height: rect.height })\n return () => observer.disconnect()\n }, [])\n\n const { valueField, maxField, configError } = useMemo(() => {\n const yAxis: string[] = Array.isArray(chartConfig?.yAxis)\n ? chartConfig.yAxis\n : []\n\n const valueField = yAxis[0] ?? ''\n const maxField = yAxis[1] ?? ''\n\n if (!valueField) {\n return { valueField, maxField, configError: 'Gauge requires at least 1 measure in Y-Axis (current value)' }\n }\n return { valueField, maxField, configError: null }\n }, [chartConfig])\n\n const thresholds: ThresholdBand[] = useMemo(() => {\n const raw = displayConfig?.thresholds\n let arr: unknown[] | null = null\n if (Array.isArray(raw)) {\n arr = raw\n } else if (typeof raw === 'string' && raw.trim()) {\n try {\n const parsed = JSON.parse(raw)\n if (Array.isArray(parsed)) arr = parsed\n } catch (e) {\n console.warn('GaugeChart: invalid threshold JSON', e)\n return []\n }\n }\n if (!arr) return []\n return arr.filter(\n (entry): entry is ThresholdBand =>\n entry !== null &&\n typeof entry === 'object' &&\n typeof (entry as ThresholdBand).value === 'number' &&\n typeof (entry as ThresholdBand).color === 'string'\n )\n }, [displayConfig?.thresholds])\n\n const thresholdBands = thresholds.length === 0\n ? []\n : [...thresholds].sort((a, b) => a.value - b.value).map((t, i, sorted) => {\n const bandStart = i === 0 ? START_ANGLE : valueToAngle(sorted[i - 1].value, 0, 1)\n const bandEnd = valueToAngle(t.value, 0, 1)\n return { color: t.color, start: bandStart, end: bandEnd }\n })\n\n try {\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in gauge chart</div>\n </div>\n </div>\n )\n }\n\n if (configError) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">{configError}</div>\n </div>\n </div>\n )\n }\n\n const row = (data as Record<string, unknown>[])[0]\n const rawValue = parseNum(row[valueField])\n if (rawValue === null) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No valid data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">Gauge value is not a valid number</div>\n </div>\n </div>\n )\n }\n const minValue = displayConfig?.minValue ?? 0\n const maxFieldValue = maxField ? parseNum(row[maxField]) : null\n const maxValue = displayConfig?.maxValue ?? (maxFieldValue ?? 100)\n\n const effectiveMax = maxValue === minValue ? minValue + 1 : maxValue\n const clampedValue = clamp(rawValue, minValue, effectiveMax)\n const fraction = (clampedValue - minValue) / (effectiveMax - minValue)\n\n const fillColor = thresholds.length > 0 ? resolveColor(fraction, thresholds) : DEFAULT_FILL\n\n const showCenterLabel = displayConfig?.showCenterLabel ?? true\n const showPercentage = displayConfig?.showPercentage ?? false\n const yAxisFormat = displayConfig?.leftYAxisFormat\n\n const containerW = dimensions.width || 300\n const containerH = typeof height === 'number' ? height : (dimensions.height || 200)\n\n const radius = Math.min(containerW / 2, containerH * 0.9) * 0.85\n const outerR = radius\n const innerR = radius * 0.6\n const cx = containerW / 2\n const cy = containerH * 0.7\n\n const trackPath = buildArcPath(innerR, outerR, START_ANGLE, END_ANGLE)\n const fillAngle = valueToAngle(clampedValue, minValue, effectiveMax)\n const fillPath = buildArcPath(innerR, outerR, START_ANGLE, fillAngle)\n\n const needleAngle = valueToAngle(clampedValue, minValue, effectiveMax)\n\n const displayValue = yAxisFormat\n ? formatAxisValue(rawValue, yAxisFormat)\n : rawValue.toLocaleString()\n\n const valueLabel = showPercentage\n ? `${(fraction * 100).toFixed(1)}%`\n : displayValue\n\n const fieldLabel = getFieldLabel(valueField)\n return (\n <div ref={containerRef} className=\"dc:relative dc:w-full\" style={{ height }}>\n <svg\n width=\"100%\"\n height=\"100%\"\n viewBox={`0 0 ${containerW} ${containerH}`}\n preserveAspectRatio=\"xMidYMid meet\"\n data-testid=\"gauge-svg\"\n >\n <g transform={`translate(${cx}, ${cy})`}>\n <path\n d={trackPath}\n fill={TRACK_COLOR}\n data-testid=\"gauge-track\"\n />\n\n {thresholdBands.map((band, i) => (\n <path\n key={i}\n d={buildArcPath(outerR + 4, outerR + 8, band.start, band.end)}\n fill={band.color}\n data-testid={`gauge-band-${i}`}\n />\n ))}\n\n <path\n d={fillPath}\n fill={fillColor}\n data-testid=\"gauge-fill\"\n data-fraction={fraction.toFixed(4)}\n />\n\n <Needle angle={needleAngle} radius={radius} />\n\n {showCenterLabel && (\n <g data-testid=\"gauge-label\">\n <text\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fontSize={radius * 0.22}\n fontWeight=\"bold\"\n fill=\"currentColor\"\n dy={radius * 0.12}\n data-testid=\"gauge-value-text\"\n >\n {valueLabel}\n </text>\n <text\n textAnchor=\"middle\"\n fontSize={radius * 0.13}\n fill=\"currentColor\"\n className=\"text-dc-text-secondary\"\n dy={radius * 0.34}\n >\n {fieldLabel}\n </text>\n </g>\n )}\n\n <text\n x={Math.cos(START_ANGLE - Math.PI / 2) * (outerR + 14)}\n y={Math.sin(START_ANGLE - Math.PI / 2) * (outerR + 14)}\n textAnchor=\"middle\"\n fontSize={radius * 0.12}\n fill=\"currentColor\"\n className=\"text-dc-text-secondary\"\n data-testid=\"gauge-min-label\"\n >\n {yAxisFormat ? formatAxisValue(minValue, yAxisFormat) : minValue.toLocaleString()}\n </text>\n <text\n x={Math.cos(END_ANGLE - Math.PI / 2) * (outerR + 14)}\n y={Math.sin(END_ANGLE - Math.PI / 2) * (outerR + 14)}\n textAnchor=\"middle\"\n fontSize={radius * 0.12}\n fill=\"currentColor\"\n className=\"text-dc-text-secondary\"\n data-testid=\"gauge-max-label\"\n >\n {yAxisFormat ? formatAxisValue(effectiveMax, yAxisFormat) : effectiveMax.toLocaleString()}\n </text>\n </g>\n </svg>\n </div>\n )\n } catch (error) {\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Gauge Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default GaugeChart\n"],"x_google_ignoreList":[0,1,2,3,4],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chart-heat-map-BAMVhLGG.js","names":[],"sources":["../../../src/client/components/charts/HeatMapChart.tsx"],"sourcesContent":["/**\n * HeatMapChart Component\n *\n * Visualizes intensity across two categorical dimensions using a color matrix.\n * Uses @nivo/heatmap for rendering.\n *\n * The chart displays:\n * - Rows: Y-axis dimension values\n * - Columns: X-axis dimension values\n * - Cell color: Intensity based on measure value\n */\n\nimport React, { useMemo } from 'react'\nimport { ResponsiveHeatMap } from '@nivo/heatmap'\nimport { formatTimeValue, getFieldGranularity, formatAxisValue } from '../../utils/chartUtils'\nimport type { AxisFormatConfig } from '../../types'\nimport type { ChartProps } from '../../types'\n\n/**\n * Parse color string (hex or rgb) to RGB values\n */\nfunction parseColor(color: string): { r: number; g: number; b: number } | null {\n // Handle hex colors\n if (color.startsWith('#')) {\n const hex = color.slice(1)\n return {\n r: parseInt(hex.substring(0, 2), 16),\n g: parseInt(hex.substring(2, 4), 16),\n b: parseInt(hex.substring(4, 6), 16),\n }\n }\n\n // Handle rgb/rgba colors\n const rgbMatch = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/)\n if (rgbMatch) {\n return {\n r: parseInt(rgbMatch[1], 10),\n g: parseInt(rgbMatch[2], 10),\n b: parseInt(rgbMatch[3], 10),\n }\n }\n\n return null\n}\n\n/**\n * Calculate relative luminance of a color\n * Returns value between 0 (black) and 1 (white)\n */\nfunction getLuminance(color: string): number {\n const rgb = parseColor(color)\n if (!rgb) return 0.5 // Default to mid-gray if parsing fails\n\n const r = rgb.r / 255\n const g = rgb.g / 255\n const b = rgb.b / 255\n\n // Apply gamma correction\n const rLinear = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4)\n const gLinear = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4)\n const bLinear = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4)\n\n // Calculate luminance using WCAG formula\n return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear\n}\n\n/**\n * Get contrasting text color (white or dark) based on background color\n */\nfunction getContrastingTextColor(bgColor: string): string {\n const luminance = getLuminance(bgColor)\n // Use white text on dark backgrounds, dark text on light backgrounds\n return luminance < 0.4 ? '#ffffff' : '#1f2937'\n}\n\n/**\n * Maximum dimensions for heatmap to prevent browser lockup\n * 50x50 = 2500 cells max\n */\nconst MAX_HEATMAP_ROWS = 50\nconst MAX_HEATMAP_COLS = 50\n\n/**\n * Nivo heatmap data format\n */\ninterface HeatMapDatum {\n x: string\n y: number | null\n}\n\ninterface HeatMapSerie {\n id: string\n data: HeatMapDatum[]\n}\n\n/**\n * Result of heatmap transformation including truncation info\n */\ninterface HeatMapTransformResult {\n data: HeatMapSerie[]\n truncated: boolean\n originalRows: number\n originalCols: number\n}\n\n/**\n * Transform drizzle-cube flat query results to nivo heatmap format\n *\n * Input (drizzle-cube):\n * [\n * { \"Region.name\": \"East\", \"Product.category\": \"Electronics\", \"Sales.total\": 1500 },\n * { \"Region.name\": \"East\", \"Product.category\": \"Clothing\", \"Sales.total\": 800 },\n * ]\n *\n * Output (nivo format):\n * [\n * { id: \"East\", data: [{ x: \"Electronics\", y: 1500 }, { x: \"Clothing\", y: 800 }] }\n * ]\n *\n * Data is truncated to MAX_HEATMAP_ROWS x MAX_HEATMAP_COLS to prevent browser lockup\n */\nfunction transformToHeatMapFormat(\n data: Record<string, unknown>[],\n xAxisField: string | undefined,\n yAxisField: string | undefined,\n valueField: string | undefined,\n xGranularity?: string,\n yGranularity?: string\n): HeatMapTransformResult {\n if (!xAxisField || !yAxisField || !valueField) {\n return { data: [], truncated: false, originalRows: 0, originalCols: 0 }\n }\n\n // Group data by Y-axis dimension\n const groupedByY = new Map<string, Map<string, number>>()\n const allXValues = new Set<string>()\n // Keep original values for sorting timestamps correctly\n const xValueOriginals = new Map<string, unknown>()\n\n for (const row of data) {\n const rawYValue = row[yAxisField]\n const rawXValue = row[xAxisField]\n\n // Format time values based on granularity\n const yValue = formatTimeValue(rawYValue, yGranularity) || String(rawYValue ?? '(empty)')\n const xValue = formatTimeValue(rawXValue, xGranularity) || String(rawXValue ?? '(empty)')\n const value = Number(row[valueField]) || 0\n\n allXValues.add(xValue)\n // Store original for sorting\n if (!xValueOriginals.has(xValue)) {\n xValueOriginals.set(xValue, rawXValue)\n }\n\n if (!groupedByY.has(yValue)) {\n groupedByY.set(yValue, new Map())\n }\n groupedByY.get(yValue)!.set(xValue, value)\n }\n\n // Sort X values - try to sort by original timestamp if available\n const xValueArray = Array.from(allXValues).sort((a, b) => {\n const origA = xValueOriginals.get(a)\n const origB = xValueOriginals.get(b)\n // If both are date strings, sort chronologically\n if (typeof origA === 'string' && typeof origB === 'string' &&\n origA.match(/^\\d{4}-\\d{2}-\\d{2}/) && origB.match(/^\\d{4}-\\d{2}-\\d{2}/)) {\n return origA.localeCompare(origB)\n }\n // Otherwise sort alphabetically by formatted value\n return a.localeCompare(b)\n })\n\n // Track original dimensions for truncation warning\n const originalRows = groupedByY.size\n const originalCols = xValueArray.length\n const truncated = originalRows > MAX_HEATMAP_ROWS || originalCols > MAX_HEATMAP_COLS\n\n // Truncate X values if needed\n const limitedXValues = xValueArray.slice(0, MAX_HEATMAP_COLS)\n\n // Build result with truncation\n const result: HeatMapSerie[] = []\n let rowCount = 0\n for (const [yValue, xMap] of groupedByY) {\n if (rowCount >= MAX_HEATMAP_ROWS) break\n result.push({\n id: yValue,\n data: limitedXValues.map((x) => ({\n x,\n y: xMap.get(x) ?? null,\n })),\n })\n rowCount++\n }\n\n return { data: result, truncated, originalRows, originalCols }\n}\n\n/**\n * HeatMapChart Component\n *\n * Renders a heatmap visualization from query results.\n * Shows intensity patterns across two categorical dimensions.\n */\nconst HeatMapChart = React.memo(function HeatMapChart({\n data,\n height = '100%',\n chartConfig,\n colorPalette,\n displayConfig,\n queryObject,\n}: ChartProps) {\n // Get display config options\n const displayConfigAny = displayConfig as Record<string, unknown> | undefined\n const showLabels = (displayConfigAny?.showLabels as boolean) ?? false\n const cellShape = (displayConfigAny?.cellShape as 'rect' | 'circle') ?? 'rect'\n const showLegend = (displayConfigAny?.showLegend as boolean) ?? true\n const xAxisFormat = displayConfigAny?.xAxisFormat as AxisFormatConfig | undefined\n const yAxisFormat = displayConfigAny?.yAxisFormat as AxisFormatConfig | undefined\n const valueFormat = displayConfigAny?.valueFormat as AxisFormatConfig | undefined\n\n // Extract field names from chartConfig (handle both array and string formats)\n const xAxisField = chartConfig?.xAxis\n ? (Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis)\n : undefined\n const yAxisField = chartConfig?.yAxis\n ? (Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis[0] : chartConfig.yAxis)\n : undefined\n const valueField = chartConfig?.valueField\n ? (Array.isArray(chartConfig.valueField) ? chartConfig.valueField[0] : chartConfig.valueField)\n : undefined\n\n // Get granularity for time dimensions (only if field is defined)\n const xGranularity = xAxisField ? getFieldGranularity(queryObject, xAxisField) : undefined\n const yGranularity = yAxisField ? getFieldGranularity(queryObject, yAxisField) : undefined\n\n // Transform data to nivo format\n const { data: heatmapData, truncated, originalRows, originalCols } = useMemo(() => {\n if (!data || data.length === 0) {\n return { data: [], truncated: false, originalRows: 0, originalCols: 0 }\n }\n return transformToHeatMapFormat(\n data as Record<string, unknown>[],\n xAxisField,\n yAxisField,\n valueField,\n xGranularity,\n yGranularity\n )\n }, [data, xAxisField, yAxisField, valueField, xGranularity, yGranularity])\n\n // Handle no data or missing config\n if (!data || data.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n Run a query to see heatmap visualization\n </div>\n </div>\n </div>\n )\n }\n\n if (!xAxisField || !yAxisField || !valueField) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration required</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {!xAxisField && 'X-axis dimension required. '}\n {!yAxisField && 'Y-axis dimension required. '}\n {!valueField && 'Value measure required.'}\n </div>\n </div>\n </div>\n )\n }\n\n if (heatmapData.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data to display</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n The query returned no results for the heatmap\n </div>\n </div>\n </div>\n )\n }\n\n // Use gradient colors from palette, or default sequential blue gradient\n // Sequential single-hue gradients are ideal for heatmaps showing magnitude/intensity\n const colors = colorPalette?.gradient || [\n '#eff3ff', // lightest blue\n '#c6dbef',\n '#9ecae1',\n '#6baed6',\n '#3182bd',\n '#08519c', // darkest blue\n ]\n\n return (\n <div className=\"dc:relative dc:w-full dc:h-full\" style={{ height }}>\n {truncated && (\n <div className=\"dc:absolute dc:top-0 dc:left-0 dc:right-0 dc:z-10 dc:px-3 dc:py-1.5 dc:text-xs bg-dc-warning-bg text-dc-warning dc:border-b border-dc-border\">\n Data truncated to {MAX_HEATMAP_ROWS}x{MAX_HEATMAP_COLS} cells (original: {originalRows}x{originalCols}). Add filters to reduce dimensions.\n </div>\n )}\n <ResponsiveHeatMap\n data={heatmapData}\n margin={{ top: truncated ? 40 : 20, right: 20, bottom: 120, left: 120 }}\n valueFormat={valueFormat ? (v) => formatAxisValue(v, valueFormat) : '>-.2s'}\n axisTop={null}\n axisRight={null}\n axisBottom={{\n tickSize: 5,\n tickPadding: 5,\n tickRotation: -45,\n legend: xAxisFormat?.label || xAxisField?.split('.').pop() || 'X Axis',\n legendPosition: 'middle',\n legendOffset: 70,\n format: xAxisFormat\n ? (v) => {\n const num = parseFloat(String(v))\n return isNaN(num) ? String(v) : formatAxisValue(num, xAxisFormat)\n }\n : undefined,\n }}\n axisLeft={{\n tickSize: 5,\n tickPadding: 5,\n tickRotation: 0,\n legend: yAxisFormat?.label || yAxisField?.split('.').pop() || 'Y Axis',\n legendPosition: 'middle',\n legendOffset: -80,\n format: yAxisFormat\n ? (v) => {\n const num = parseFloat(String(v))\n return isNaN(num) ? String(v) : formatAxisValue(num, yAxisFormat)\n }\n : undefined,\n }}\n colors={\n colors.length >= 2\n // Sequential scale needs [minColor, maxColor] tuple\n // Use first color for low values, last color for high values\n ? { type: 'sequential', colors: [colors[0], colors[colors.length - 1]] as [string, string] }\n : { type: 'sequential', scheme: 'greens' } // Fallback to built-in scheme\n }\n emptyColor=\"var(--dc-surface-tertiary)\"\n cellComponent={cellShape === 'circle' ? 'circle' : 'rect'}\n enableLabels={showLabels}\n labelTextColor={({ color }) => getContrastingTextColor(color)}\n legends={\n showLegend\n ? [\n {\n anchor: 'bottom',\n translateX: 0,\n translateY: 95,\n length: 400,\n thickness: 8,\n direction: 'row',\n tickPosition: 'after',\n tickSize: 3,\n tickSpacing: 4,\n tickOverlap: false,\n title: valueField?.split('.').pop() || 'Value',\n titleAlign: 'start',\n titleOffset: 4,\n },\n ]\n : []\n }\n annotations={[]}\n theme={{\n text: {\n fill: 'var(--dc-text)',\n },\n axis: {\n legend: {\n text: {\n fill: 'var(--dc-text)',\n },\n },\n ticks: {\n text: {\n fill: 'var(--dc-text-secondary)',\n },\n },\n },\n legends: {\n text: {\n fill: 'var(--dc-text-secondary)',\n },\n title: {\n text: {\n fill: 'var(--dc-text)',\n },\n },\n },\n tooltip: {\n container: {\n background: 'var(--dc-surface)',\n color: 'var(--dc-text)',\n borderRadius: '4px',\n boxShadow: '0 1px 2px rgba(0, 0, 0, 0.25)',\n },\n },\n }}\n />\n </div>\n )\n})\n\nexport default HeatMapChart\n"],"mappings":";;;;;;;AAqBA,SAAS,EAAW,GAA2D;AAE7E,KAAI,EAAM,WAAW,IAAI,EAAE;EACzB,IAAM,IAAM,EAAM,MAAM,EAAE;AAC1B,SAAO;GACL,GAAG,SAAS,EAAI,UAAU,GAAG,EAAE,EAAE,GAAG;GACpC,GAAG,SAAS,EAAI,UAAU,GAAG,EAAE,EAAE,GAAG;GACpC,GAAG,SAAS,EAAI,UAAU,GAAG,EAAE,EAAE,GAAG;GACrC;;CAIH,IAAM,IAAW,EAAM,MAAM,iCAAiC;AAS9D,QARI,IACK;EACL,GAAG,SAAS,EAAS,IAAI,GAAG;EAC5B,GAAG,SAAS,EAAS,IAAI,GAAG;EAC5B,GAAG,SAAS,EAAS,IAAI,GAAG;EAC7B,GAGI;;AAOT,SAAS,EAAa,GAAuB;CAC3C,IAAM,IAAM,EAAW,EAAM;AAC7B,KAAI,CAAC,EAAK,QAAO;CAEjB,IAAM,IAAI,EAAI,IAAI,KACZ,IAAI,EAAI,IAAI,KACZ,IAAI,EAAI,IAAI,KAGZ,IAAU,KAAK,SAAU,IAAI,UAAkB,IAAI,QAAS,UAAO,KACnE,IAAU,KAAK,SAAU,IAAI,UAAkB,IAAI,QAAS,UAAO,KACnE,IAAU,KAAK,SAAU,IAAI,UAAkB,IAAI,QAAS,UAAO;AAGzE,QAAO,QAAS,IAAU,QAAS,IAAU,QAAS;;AAMxD,SAAS,EAAwB,GAAyB;AAGxD,QAFkB,EAAa,EAAQ,GAEpB,KAAM,YAAY;;AAOvC,IAAM,IAAmB,IACnB,IAAmB;AAyCzB,SAAS,EACP,GACA,GACA,GACA,GACA,GACA,GACwB;AACxB,KAAI,CAAC,KAAc,CAAC,KAAc,CAAC,EACjC,QAAO;EAAE,MAAM,EAAE;EAAE,WAAW;EAAO,cAAc;EAAG,cAAc;EAAG;CAIzE,IAAM,oBAAa,IAAI,KAAkC,EACnD,oBAAa,IAAI,KAAa,EAE9B,oBAAkB,IAAI,KAAsB;AAElD,MAAK,IAAM,KAAO,GAAM;EACtB,IAAM,IAAY,EAAI,IAChB,IAAY,EAAI,IAGhB,IAAS,EAAgB,GAAW,EAAa,IAAI,OAAO,KAAa,UAAU,EACnF,IAAS,EAAgB,GAAW,EAAa,IAAI,OAAO,KAAa,UAAU,EACnF,IAAQ,OAAO,EAAI,GAAY,IAAI;AAWzC,EATA,EAAW,IAAI,EAAO,EAEjB,EAAgB,IAAI,EAAO,IAC9B,EAAgB,IAAI,GAAQ,EAAU,EAGnC,EAAW,IAAI,EAAO,IACzB,EAAW,IAAI,mBAAQ,IAAI,KAAK,CAAC,EAEnC,EAAW,IAAI,EAAO,CAAE,IAAI,GAAQ,EAAM;;CAI5C,IAAM,IAAc,MAAM,KAAK,EAAW,CAAC,MAAM,GAAG,MAAM;EACxD,IAAM,IAAQ,EAAgB,IAAI,EAAE,EAC9B,IAAQ,EAAgB,IAAI,EAAE;AAOpC,SALI,OAAO,KAAU,YAAY,OAAO,KAAU,YAC9C,EAAM,MAAM,qBAAqB,IAAI,EAAM,MAAM,qBAAqB,GACjE,EAAM,cAAc,EAAM,GAG5B,EAAE,cAAc,EAAE;GACzB,EAGI,IAAe,EAAW,MAC1B,IAAe,EAAY,QAC3B,IAAY,IAAe,KAAoB,IAAe,GAG9D,IAAiB,EAAY,MAAM,GAAG,EAAiB,EAGvD,IAAyB,EAAE,EAC7B,IAAW;AACf,MAAK,IAAM,CAAC,GAAQ,MAAS,GAAY;AACvC,MAAI,KAAY,EAAkB;AAQlC,EAPA,EAAO,KAAK;GACV,IAAI;GACJ,MAAM,EAAe,KAAK,OAAO;IAC/B;IACA,GAAG,EAAK,IAAI,EAAE,IAAI;IACnB,EAAE;GACJ,CAAC,EACF;;AAGF,QAAO;EAAE,MAAM;EAAQ;EAAW;EAAc;EAAc;;AAShE,IAAM,IAAe,EAAM,KAAK,SAAsB,EACpD,SACA,YAAS,QACT,gBACA,iBACA,kBACA,kBACa;CAEb,IAAM,IAAmB,GACnB,IAAc,GAAkB,cAA0B,IAC1D,IAAa,GAAkB,aAAmC,QAClE,IAAc,GAAkB,cAA0B,IAC1D,IAAc,GAAkB,aAChC,IAAc,GAAkB,aAChC,IAAc,GAAkB,aAGhC,IAAa,GAAa,QAC3B,MAAM,QAAQ,EAAY,MAAM,GAAG,EAAY,MAAM,KAAK,EAAY,QACvE,KAAA,GACE,IAAa,GAAa,QAC3B,MAAM,QAAQ,EAAY,MAAM,GAAG,EAAY,MAAM,KAAK,EAAY,QACvE,KAAA,GACE,IAAa,GAAa,aAC3B,MAAM,QAAQ,EAAY,WAAW,GAAG,EAAY,WAAW,KAAK,EAAY,aACjF,KAAA,GAGE,IAAe,IAAa,EAAoB,GAAa,EAAW,GAAG,KAAA,GAC3E,IAAe,IAAa,EAAoB,GAAa,EAAW,GAAG,KAAA,GAG3E,EAAE,MAAM,GAAa,cAAW,iBAAc,oBAAiB,QAC/D,CAAC,KAAQ,EAAK,WAAW,IACpB;EAAE,MAAM,EAAE;EAAE,WAAW;EAAO,cAAc;EAAG,cAAc;EAAG,GAElE,EACL,GACA,GACA,GACA,GACA,GACA,EACD,EACA;EAAC;EAAM;EAAY;EAAY;EAAY;EAAc;EAAa,CAAC;AAG1E,KAAI,CAAC,KAAQ,EAAK,WAAW,EAC3B,QACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO,EAAE,WAAQ;YAEjB,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAsC;IAAuB,CAAA,EAC5E,kBAAC,OAAD;IAAK,WAAU;cAAoC;IAE7C,CAAA,CACF;;EACF,CAAA;AAIV,KAAI,CAAC,KAAc,CAAC,KAAc,CAAC,EACjC,QACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO,EAAE,WAAQ;YAEjB,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAsC;IAA4B,CAAA,EACjF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,CAAC,KAAc;KACf,CAAC,KAAc;KACf,CAAC,KAAc;KACZ;MACF;;EACF,CAAA;AAIV,KAAI,EAAY,WAAW,EACzB,QACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO,EAAE,WAAQ;YAEjB,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAsC;IAAwB,CAAA,EAC7E,kBAAC,OAAD;IAAK,WAAU;cAAoC;IAE7C,CAAA,CACF;;EACF,CAAA;CAMV,IAAM,IAAS,GAAc,YAAY;EACvC;EACA;EACA;EACA;EACA;EACA;EACD;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;EAAkC,OAAO,EAAE,WAAQ;YAAlE,CACG,KACC,kBAAC,OAAD;GAAK,WAAU;aAAf;IAA8J;IACzI;IAAiB;IAAE;IAAiB;IAAmB;IAAa;IAAE;IAAa;IAClG;MAER,kBAAC,GAAD;GACE,MAAM;GACN,QAAQ;IAAE,KAAK,IAAY,KAAK;IAAI,OAAO;IAAI,QAAQ;IAAK,MAAM;IAAK;GACvE,aAAa,KAAe,MAAM,EAAgB,GAAG,EAAY,GAAG;GACpE,SAAS;GACT,WAAW;GACX,YAAY;IACV,UAAU;IACV,aAAa;IACb,cAAc;IACd,QAAQ,GAAa,SAAS,GAAY,MAAM,IAAI,CAAC,KAAK,IAAI;IAC9D,gBAAgB;IAChB,cAAc;IACd,QAAQ,KACH,MAAM;KACL,IAAM,IAAM,WAAW,OAAO,EAAE,CAAC;AACjC,YAAO,MAAM,EAAI,GAAG,OAAO,EAAE,GAAG,EAAgB,GAAK,EAAY;QAEnE,KAAA;IACL;GACD,UAAU;IACR,UAAU;IACV,aAAa;IACb,cAAc;IACd,QAAQ,GAAa,SAAS,GAAY,MAAM,IAAI,CAAC,KAAK,IAAI;IAC9D,gBAAgB;IAChB,cAAc;IACd,QAAQ,KACH,MAAM;KACL,IAAM,IAAM,WAAW,OAAO,EAAE,CAAC;AACjC,YAAO,MAAM,EAAI,GAAG,OAAO,EAAE,GAAG,EAAgB,GAAK,EAAY;QAEnE,KAAA;IACL;GACD,QACE,EAAO,UAAU,IAGb;IAAE,MAAM;IAAc,QAAQ,CAAC,EAAO,IAAI,EAAO,EAAO,SAAS,GAAG;IAAsB,GAC1F;IAAE,MAAM;IAAc,QAAQ;IAAU;GAE9C,YAAW;GACX,eAAe,MAAc,WAAW,WAAW;GACnD,cAAc;GACd,iBAAiB,EAAE,eAAY,EAAwB,EAAM;GAC7D,SACE,IACI,CACE;IACE,QAAQ;IACR,YAAY;IACZ,YAAY;IACZ,QAAQ;IACR,WAAW;IACX,WAAW;IACX,cAAc;IACd,UAAU;IACV,aAAa;IACb,aAAa;IACb,OAAO,GAAY,MAAM,IAAI,CAAC,KAAK,IAAI;IACvC,YAAY;IACZ,aAAa;IACd,CACF,GACD,EAAE;GAER,aAAa,EAAE;GACf,OAAO;IACL,MAAM,EACJ,MAAM,kBACP;IACD,MAAM;KACJ,QAAQ,EACN,MAAM,EACJ,MAAM,kBACP,EACF;KACD,OAAO,EACL,MAAM,EACJ,MAAM,4BACP,EACF;KACF;IACD,SAAS;KACP,MAAM,EACJ,MAAM,4BACP;KACD,OAAO,EACL,MAAM,EACJ,MAAM,kBACP,EACF;KACF;IACD,SAAS,EACP,WAAW;KACT,YAAY;KACZ,OAAO;KACP,cAAc;KACd,WAAW;KACZ,EACF;IACF;GACD,CAAA,CACE;;EAER"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chart-kpi-delta-KQjUIeal.js","names":[],"sources":["../../../src/client/utils/periodUtils.ts","../../../src/client/components/charts/KpiDelta.tsx"],"sourcesContent":["/**\n * Utility functions for detecting incomplete time periods in KPI charts\n */\n\n/**\n * Get the end date of a period based on granularity\n * @param date - The date within the period\n * @param granularity - The time granularity (day, week, month, quarter, year)\n * @returns The end date of the period (end of day)\n */\nexport function getPeriodEndDate(date: Date, granularity: string): Date {\n const endDate = new Date(date)\n\n switch (granularity.toLowerCase()) {\n case 'day':\n // End of the same day\n endDate.setHours(23, 59, 59, 999)\n break\n\n case 'week': {\n // End of the week (Saturday, assuming week starts Sunday)\n const dayOfWeek = endDate.getDay()\n const daysUntilSaturday = 6 - dayOfWeek\n endDate.setDate(endDate.getDate() + daysUntilSaturday)\n endDate.setHours(23, 59, 59, 999)\n break\n }\n\n case 'month':\n // Last day of the month\n endDate.setMonth(endDate.getMonth() + 1, 0) // Set to day 0 of next month = last day of current month\n endDate.setHours(23, 59, 59, 999)\n break\n\n case 'quarter': {\n // Last day of the quarter\n const currentMonth = endDate.getMonth()\n const quarterEndMonth = Math.floor(currentMonth / 3) * 3 + 2 // 0->2, 3->5, 6->8, 9->11\n endDate.setMonth(quarterEndMonth + 1, 0) // Last day of quarter end month\n endDate.setHours(23, 59, 59, 999)\n break\n }\n\n case 'year':\n // December 31st\n endDate.setMonth(11, 31) // December 31\n endDate.setHours(23, 59, 59, 999)\n break\n\n default:\n // Unknown granularity - treat as complete\n endDate.setHours(23, 59, 59, 999)\n }\n\n return endDate\n}\n\n/**\n * Check if the last period in the data is complete based on granularity\n * @param lastDataPoint - The last data point in the sorted dataset\n * @param timeDimensionField - The field name containing the time value\n * @param granularity - The time granularity\n * @returns true if the period is complete, false if it's incomplete\n */\nexport function isLastPeriodComplete(\n lastDataPoint: any,\n timeDimensionField: string,\n granularity: string\n): boolean {\n if (!lastDataPoint || !timeDimensionField || !granularity) {\n return true // Assume complete if we can't determine\n }\n\n const timeValue = lastDataPoint[timeDimensionField]\n if (!timeValue) {\n return true // Assume complete if no time value\n }\n\n // Parse the time value\n const date = new Date(timeValue)\n if (isNaN(date.getTime())) {\n return true // Assume complete if invalid date\n }\n\n // Get the end of this period\n const periodEnd = getPeriodEndDate(date, granularity)\n\n // Compare with current time\n const now = new Date()\n\n // If the period end is in the future, the period is incomplete\n return periodEnd <= now\n}\n\n/**\n * Extract granularity from a query object\n * @param queryObject - The CubeQuery object\n * @param dimensionField - Optional specific dimension field to match\n * @returns The granularity string or null if not found\n */\nexport function getQueryGranularity(\n queryObject: any,\n dimensionField?: string\n): string | null {\n if (!queryObject?.timeDimensions || queryObject.timeDimensions.length === 0) {\n return null\n }\n\n // If a specific dimension field is provided, try to find its granularity\n if (dimensionField) {\n const matchingDim = queryObject.timeDimensions.find((td: any) =>\n td.dimension === dimensionField ||\n td.dimension?.includes(dimensionField) ||\n dimensionField?.includes(td.dimension)\n )\n if (matchingDim?.granularity) {\n return matchingDim.granularity\n }\n }\n\n // Fallback to first time dimension's granularity\n const firstTimeDim = queryObject.timeDimensions[0]\n return firstTimeDim?.granularity || null\n}\n\n/**\n * Filter data to exclude incomplete or last period\n * @param data - The data array sorted by time\n * @param timeDimensionField - The field containing time values\n * @param queryObject - The query object containing timeDimensions\n * @param useLastCompletePeriod - Whether to check for incomplete periods\n * @param skipLastPeriod - Whether to always skip the last period (overrides useLastCompletePeriod)\n * @returns Object with filtered data and whether filtering was applied\n */\nexport function filterIncompletePeriod(\n data: any[],\n timeDimensionField: string | undefined,\n queryObject: any,\n useLastCompletePeriod: boolean,\n skipLastPeriod: boolean = false\n): { filteredData: any[]; excludedIncompletePeriod: boolean; skippedLastPeriod: boolean; granularity: string | null } {\n // Default return - no filtering\n const noFilter = {\n filteredData: data,\n excludedIncompletePeriod: false,\n skippedLastPeriod: false,\n granularity: null\n }\n\n // Need at least 2 data points to filter\n if (data.length < 2) {\n return noFilter\n }\n\n const granularity = getQueryGranularity(queryObject, timeDimensionField)\n\n // If skipLastPeriod is enabled, always skip the last period\n if (skipLastPeriod) {\n return {\n filteredData: data.slice(0, -1),\n excludedIncompletePeriod: false,\n skippedLastPeriod: true,\n granularity\n }\n }\n\n // Skip incomplete period check if feature is disabled\n if (!useLastCompletePeriod) {\n return { ...noFilter, granularity }\n }\n\n // Skip if no time dimension field\n if (!timeDimensionField) {\n return { ...noFilter, granularity }\n }\n\n // Skip if no time dimensions in query\n if (!queryObject?.timeDimensions || queryObject.timeDimensions.length === 0) {\n return { ...noFilter, granularity }\n }\n\n if (!granularity) {\n return noFilter\n }\n\n // Check if last period is incomplete\n const lastRow = data[data.length - 1]\n if (!isLastPeriodComplete(lastRow, timeDimensionField, granularity)) {\n return {\n filteredData: data.slice(0, -1),\n excludedIncompletePeriod: true,\n skippedLastPeriod: false,\n granularity\n }\n }\n\n return { ...noFilter, granularity }\n}\n","import React, { useState, useRef, useEffect } from \"react\";\nimport { Icon } from \"@iconify/react\";\nimport infoCircleIcon from \"@iconify-icons/tabler/info-circle\";\nimport { useCubeFieldLabel } from \"../../hooks/useCubeFieldLabel\";\nimport { filterIncompletePeriod } from \"../../utils/periodUtils\";\nimport type { ChartProps } from \"../../types\";\n\ninterface VarianceHistogramProps {\n values: number[];\n lastValue: number;\n positiveColor: string;\n negativeColor: string;\n formatValue: (value: number) => string;\n width: number;\n height: number;\n}\n\nfunction VarianceHistogram({\n values,\n lastValue,\n positiveColor,\n negativeColor,\n formatValue,\n width,\n height,\n}: VarianceHistogramProps) {\n // Limit to most recent N values to fit in the histogram\n // Calculate max bars based on width (minimum 8px per bar including gap)\n const maxBars = Math.max(10, Math.floor(width / 10));\n const limitedValues =\n values.length > maxBars\n ? values.slice(-maxBars) // Take the most recent values\n : values;\n\n // Calculate variance (difference) from current/last value for each point\n const variances = limitedValues.map((value) => value - lastValue);\n\n // Find min/max variance for scaling (include 0 as baseline)\n const minVariance = Math.min(...variances, 0);\n const maxVariance = Math.max(...variances, 0);\n const range = Math.max(Math.abs(minVariance), Math.abs(maxVariance));\n\n if (range === 0 || variances.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center bg-dc-bg-secondary dc:rounded-sm dc:border border-dc-border\"\n style={{ width: `${width}px`, height: `${height}px` }}\n >\n <span className=\"dc:text-xs text-dc-text-muted\">No variance data</span>\n </div>\n );\n }\n\n // Calculate bar dimensions\n const barGap = 2;\n const availableWidth = width - (limitedValues.length - 1) * barGap;\n const barWidth = Math.max(4, availableWidth / limitedValues.length);\n\n // Calculate where zero line should be positioned (as percentage from top)\n // If maxVariance = 67 and minVariance = -24, total range = 91\n // Zero should be at 67/91 = 73.6% from top\n const totalRange = maxVariance - minVariance;\n const zeroLinePercent =\n totalRange > 0 ? (maxVariance / totalRange) * 100 : 50;\n\n return (\n <div className=\"dc:flex dc:items-center dc:space-x-2\">\n {/* Histogram bars */}\n <div\n className=\"dc:relative\"\n style={{\n width: `${width}px`,\n height: `${height}px`,\n }}\n >\n {/* Zero line (represents current value) */}\n <div\n className=\"dc:absolute dc:left-0 dc:right-0\"\n style={{\n height: \"1px\",\n top: `${zeroLinePercent}%`,\n backgroundColor: \"var(--dc-border)\",\n zIndex: 1,\n }}\n />\n\n {/* Variance bars - oldest to newest (left to right) */}\n {variances.map((variance, index) => {\n // Calculate bar height as proportion of total range\n const normalizedHeight = Math.abs(variance) / totalRange;\n const barHeight = Math.max(2, normalizedHeight * (height - 4));\n const isPositive = variance >= 0;\n const isLastValue = index === limitedValues.length - 1;\n const color = isPositive ? positiveColor : negativeColor;\n const xPosition = index * (barWidth + barGap);\n\n return (\n <div\n key={index}\n className=\"dc:absolute rounded-xs\"\n style={{\n left: `${xPosition}px`,\n width: `${barWidth}px`,\n height: `${barHeight}px`,\n backgroundColor: color,\n opacity: isLastValue ? 1 : 0.6,\n // Position bar relative to zero line\n ...(isPositive\n ? { bottom: `${100 - zeroLinePercent}%` }\n : { top: `${zeroLinePercent}%` }),\n zIndex: 2,\n }}\n title={`${formatValue(limitedValues[index])}: ${variance >= 0 ? \"+\" : \"\"}${formatValue(variance)} vs current`}\n />\n );\n })}\n </div>\n\n {/* Variance labels on the right - show actual value difference */}\n <div\n className=\"dc:flex dc:flex-col dc:justify-between dc:text-xs text-dc-text-muted\"\n style={{ height: `${height}px` }}\n >\n <span>+{formatValue(maxVariance)}</span>\n <span>\n {minVariance < 0 ? \"\" : \"\"}\n {formatValue(minVariance)}\n </span>\n </div>\n </div>\n );\n}\n\nconst KpiDelta = React.memo(function KpiDelta({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n}: ChartProps) {\n const [fontSize, setFontSize] = useState(32);\n const [textWidth, setTextWidth] = useState(250);\n const containerRef = useRef<HTMLDivElement>(null);\n const valueRef = useRef<HTMLDivElement>(null);\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel();\n\n // Calculate font size and text width based on container dimensions\n useEffect(() => {\n const updateDimensions = () => {\n if (containerRef.current) {\n const container = containerRef.current;\n const rect = container.getBoundingClientRect();\n const containerWidth = rect.width;\n const containerHeight = rect.height;\n\n if (containerWidth > 0 && containerHeight > 0) {\n const widthBasedSize = containerWidth / 4;\n const heightBasedSize = containerHeight / 4;\n const baseFontSize = Math.min(widthBasedSize, heightBasedSize);\n const clampedFontSize = Math.max(28, Math.min(baseFontSize, 140));\n setFontSize(clampedFontSize);\n\n setTimeout(() => {\n if (valueRef.current) {\n const textRect = valueRef.current.getBoundingClientRect();\n const measuredWidth = textRect.width;\n // Scale histogram width with container, accounting for labels on the right (~60px)\n const maxHistogramWidth = containerWidth - 100; // Leave room for padding and labels\n const effectiveWidth = Math.max(\n measuredWidth,\n Math.min(maxHistogramWidth, containerWidth * 0.7),\n );\n setTextWidth(Math.max(100, effectiveWidth)); // Minimum 100px\n }\n }, 10);\n }\n }\n };\n\n const timer = setTimeout(updateDimensions, 50);\n\n const resizeObserver = new ResizeObserver(() => {\n setTimeout(updateDimensions, 10);\n });\n\n if (containerRef.current) {\n resizeObserver.observe(containerRef.current);\n }\n\n return () => {\n clearTimeout(timer);\n resizeObserver.disconnect();\n };\n }, [data, chartConfig]);\n\n if (!data || data.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n }}\n >\n <div className=\"dc:text-center text-dc-text-muted\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n No data points to display\n </div>\n </div>\n </div>\n );\n }\n\n // Extract value and dimension fields from chart config\n let valueFields: string[] = [];\n let dimensionFields: string[] = [];\n\n if (chartConfig?.yAxis) {\n valueFields = Array.isArray(chartConfig.yAxis)\n ? chartConfig.yAxis\n : [chartConfig.yAxis];\n }\n\n if (chartConfig?.xAxis) {\n dimensionFields = Array.isArray(chartConfig.xAxis)\n ? chartConfig.xAxis\n : [chartConfig.xAxis];\n }\n\n if (valueFields.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n backgroundColor: \"var(--dc-danger-bg)\",\n color: \"var(--dc-danger)\",\n borderColor: \"var(--dc-danger-border)\",\n }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">No measure field configured</div>\n </div>\n </div>\n );\n }\n\n const valueField = valueFields[0];\n const dimensionField = dimensionFields[0]; // Optional\n\n // Sort data by dimension if available (for time series)\n let sortedData = [...data];\n if (dimensionField) {\n sortedData = sortedData.sort((a, b) => {\n const aVal = a[dimensionField];\n const bVal = b[dimensionField];\n if (aVal < bVal) return -1;\n if (aVal > bVal) return 1;\n return 0;\n });\n }\n\n // Filter out incomplete or last period if enabled\n const { useLastCompletePeriod = true, skipLastPeriod = false } =\n displayConfig;\n const {\n filteredData,\n excludedIncompletePeriod,\n skippedLastPeriod,\n granularity,\n } = filterIncompletePeriod(\n sortedData,\n dimensionField,\n queryObject,\n useLastCompletePeriod,\n skipLastPeriod,\n );\n\n // Use filtered data for calculations\n const dataToUse = filteredData;\n\n // Extract values from filtered data\n const values = dataToUse\n .map((row) => row[valueField])\n .filter((val) => val !== null && val !== undefined && !isNaN(Number(val)))\n .map((val) => Number(val));\n\n if (values.length < 2) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n backgroundColor: \"var(--dc-warning-bg)\",\n color: \"var(--dc-warning)\",\n borderColor: \"var(--dc-warning-border)\",\n }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Insufficient Data</div>\n <div className=\"dc:text-xs\">\n Delta calculation requires at least 2 data points\n </div>\n <div className=\"dc:text-xs\">Current data points: {values.length}</div>\n </div>\n </div>\n );\n }\n\n // Calculate delta between last and second-last values\n const lastValue = values[values.length - 1];\n const secondLastValue = values[values.length - 2];\n const absoluteChange = lastValue - secondLastValue;\n const percentageChange =\n secondLastValue !== 0\n ? (absoluteChange / Math.abs(secondLastValue)) * 100\n : 0;\n\n const isPositiveChange = absoluteChange >= 0;\n\n // Format number with appropriate units and decimals\n const formatNumber = (value: number | null | undefined): string => {\n // If custom formatValue is provided, use it exclusively\n if (displayConfig.formatValue) {\n return displayConfig.formatValue(value);\n }\n\n // Null handling: Show placeholder for missing data\n if (value === null || value === undefined) {\n return \"—\";\n }\n\n const decimals = displayConfig.decimals ?? 0;\n const prefix = displayConfig.prefix ?? \"\";\n\n let formattedValue: string;\n\n if (Math.abs(value) >= 1e9) {\n formattedValue = (value / 1e9).toFixed(decimals) + \"B\";\n } else if (Math.abs(value) >= 1e6) {\n formattedValue = (value / 1e6).toFixed(decimals) + \"M\";\n } else if (Math.abs(value) >= 1e3) {\n formattedValue = (value / 1e3).toFixed(decimals) + \"K\";\n } else {\n formattedValue = value.toFixed(decimals);\n }\n\n return prefix + formattedValue;\n };\n\n // Get colors from palette\n const getPositiveColor = (): string => {\n if (\n displayConfig.positiveColorIndex !== undefined &&\n colorPalette?.colors\n ) {\n const colorIndex = displayConfig.positiveColorIndex;\n if (colorIndex >= 0 && colorIndex < colorPalette.colors.length) {\n return colorPalette.colors[colorIndex];\n }\n }\n return \"#10b981\"; // Default green\n };\n\n const getNegativeColor = (): string => {\n if (\n displayConfig.negativeColorIndex !== undefined &&\n colorPalette?.colors\n ) {\n const colorIndex = displayConfig.negativeColorIndex;\n if (colorIndex >= 0 && colorIndex < colorPalette.colors.length) {\n return colorPalette.colors[colorIndex];\n }\n }\n return \"#ef4444\"; // Default red\n };\n\n const positiveColor = getPositiveColor();\n const negativeColor = getNegativeColor();\n const currentColor = isPositiveChange ? positiveColor : negativeColor;\n\n return (\n <div\n ref={containerRef}\n className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full dc:h-full dc:p-4\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n }}\n >\n {/* Field Label */}\n <div\n className=\"text-dc-text-secondary dc:font-bold dc:text-center dc:mb-2 dc:flex dc:items-center dc:justify-center dc:gap-1\"\n style={{\n fontSize: \"14px\",\n lineHeight: \"1.2\",\n }}\n >\n <span>\n {(() => {\n const label = getFieldLabel(valueField);\n return label && label.length > 1 ? label : valueField;\n })()}\n </span>\n {(excludedIncompletePeriod || skippedLastPeriod) && (\n <span\n title={\n skippedLastPeriod\n ? `Excludes last ${granularity || \"period\"}`\n : `Excludes current incomplete ${granularity}`\n }\n className=\"dc:cursor-help\"\n >\n <Icon\n icon={infoCircleIcon}\n className=\"dc:w-4 dc:h-4 text-dc-text-muted dc:opacity-70\"\n />\n </span>\n )}\n </div>\n\n {/* Main KPI Value and Delta */}\n <div className=\"dc:flex dc:items-center dc:justify-center dc:space-x-4 dc:mb-2\">\n {/* Main KPI Value */}\n <div\n ref={valueRef}\n className=\"dc:font-bold dc:leading-none\"\n style={{\n fontSize: `${fontSize}px`,\n color: \"var(--dc-text)\", // Keep main value neutral\n }}\n >\n {formatNumber(lastValue)}\n </div>\n\n {/* Delta Information */}\n <div className=\"dc:flex dc:items-center dc:space-x-1\">\n {/* Arrow */}\n <div\n className=\"dc:font-bold\"\n style={{\n color: currentColor,\n fontSize: `${fontSize * 0.35}px`,\n }}\n >\n {isPositiveChange ? \"▲\" : \"▼\"}\n </div>\n\n {/* Delta Values */}\n <div className=\"dc:text-left\">\n <div\n className=\"dc:font-bold dc:leading-tight\"\n style={{\n fontSize: `${fontSize * 0.35}px`,\n color: currentColor,\n }}\n >\n {isPositiveChange ? \"+\" : \"\"}\n {formatNumber(absoluteChange)}\n </div>\n <div\n className=\"dc:font-semibold dc:leading-tight\"\n style={{\n fontSize: `${fontSize * 0.28}px`,\n color: currentColor,\n opacity: 0.8,\n }}\n >\n {isPositiveChange ? \"+\" : \"\"}\n {percentageChange.toFixed(1)}%\n </div>\n </div>\n </div>\n </div>\n\n {/* Unit/Suffix (hidden when formatValue is provided) */}\n {displayConfig.suffix && !displayConfig.formatValue && (\n <div\n className=\"text-dc-text-muted dc:text-center dc:mb-3\"\n style={{\n fontSize: \"14px\",\n lineHeight: \"1.2\",\n opacity: 0.8,\n }}\n >\n {displayConfig.suffix}\n </div>\n )}\n\n {/* Variance Histogram */}\n {displayConfig.showHistogram !== false && values.length > 2 && (\n <div className=\"dc:mt-2 dc:w-full dc:flex dc:justify-center dc:overflow-hidden\">\n <VarianceHistogram\n values={values}\n lastValue={lastValue}\n positiveColor={positiveColor}\n negativeColor={negativeColor}\n formatValue={formatNumber}\n width={textWidth}\n height={64}\n />\n </div>\n )}\n </div>\n );\n})\n\nexport default KpiDelta\n"],"mappings":";;;;;AAUA,SAAgB,EAAiB,GAAY,GAA2B;CACtE,IAAM,IAAU,IAAI,KAAK,EAAK;AAE9B,SAAQ,EAAY,aAAa,EAAjC;EACE,KAAK;AAEH,KAAQ,SAAS,IAAI,IAAI,IAAI,IAAI;AACjC;EAEF,KAAK,QAAQ;GAGX,IAAM,IAAoB,IADR,EAAQ,QAAQ;AAGlC,GADA,EAAQ,QAAQ,EAAQ,SAAS,GAAG,EAAkB,EACtD,EAAQ,SAAS,IAAI,IAAI,IAAI,IAAI;AACjC;;EAGF,KAAK;AAGH,GADA,EAAQ,SAAS,EAAQ,UAAU,GAAG,GAAG,EAAE,EAC3C,EAAQ,SAAS,IAAI,IAAI,IAAI,IAAI;AACjC;EAEF,KAAK,WAAW;GAEd,IAAM,IAAe,EAAQ,UAAU,EACjC,IAAkB,KAAK,MAAM,IAAe,EAAE,GAAG,IAAI;AAE3D,GADA,EAAQ,SAAS,IAAkB,GAAG,EAAE,EACxC,EAAQ,SAAS,IAAI,IAAI,IAAI,IAAI;AACjC;;EAGF,KAAK;AAGH,GADA,EAAQ,SAAS,IAAI,GAAG,EACxB,EAAQ,SAAS,IAAI,IAAI,IAAI,IAAI;AACjC;EAEF,QAEE,GAAQ,SAAS,IAAI,IAAI,IAAI,IAAI;;AAGrC,QAAO;;AAUT,SAAgB,EACd,GACA,GACA,GACS;AACT,KAAI,CAAC,KAAiB,CAAC,KAAsB,CAAC,EAC5C,QAAO;CAGT,IAAM,IAAY,EAAc;AAChC,KAAI,CAAC,EACH,QAAO;CAIT,IAAM,IAAO,IAAI,KAAK,EAAU;AAYhC,QAXI,MAAM,EAAK,SAAS,CAAC,GAChB,KAIS,EAAiB,GAAM,EAAY,oBAGzC,IAAI,MAAM;;AAYxB,SAAgB,EACd,GACA,GACe;AACf,KAAI,CAAC,GAAa,kBAAkB,EAAY,eAAe,WAAW,EACxE,QAAO;AAIT,KAAI,GAAgB;EAClB,IAAM,IAAc,EAAY,eAAe,MAAM,MACnD,EAAG,cAAc,KACjB,EAAG,WAAW,SAAS,EAAe,IACtC,GAAgB,SAAS,EAAG,UAAU,CACvC;AACD,MAAI,GAAa,YACf,QAAO,EAAY;;AAMvB,QADqB,EAAY,eAAe,IAC3B,eAAe;;AAYtC,SAAgB,EACd,GACA,GACA,GACA,GACA,IAA0B,IAC0F;CAEpH,IAAM,IAAW;EACf,cAAc;EACd,0BAA0B;EAC1B,mBAAmB;EACnB,aAAa;EACd;AAGD,KAAI,EAAK,SAAS,EAChB,QAAO;CAGT,IAAM,IAAc,EAAoB,GAAa,EAAmB;AAGxE,KAAI,EACF,QAAO;EACL,cAAc,EAAK,MAAM,GAAG,GAAG;EAC/B,0BAA0B;EAC1B,mBAAmB;EACnB;EACD;AAcH,KAVI,CAAC,KAKD,CAAC,KAKD,CAAC,GAAa,kBAAkB,EAAY,eAAe,WAAW,EACxE,QAAO;EAAE,GAAG;EAAU;EAAa;AAGrC,KAAI,CAAC,EACH,QAAO;CAIT,IAAM,IAAU,EAAK,EAAK,SAAS;AAUnC,QATK,EAAqB,GAAS,GAAoB,EAAY,GAS5D;EAAE,GAAG;EAAU;EAAa,GAR1B;EACL,cAAc,EAAK,MAAM,GAAG,GAAG;EAC/B,0BAA0B;EAC1B,mBAAmB;EACnB;EACD;;;;AChLL,SAAS,EAAkB,EACzB,WACA,cACA,kBACA,kBACA,gBACA,UACA,aACyB;CAGzB,IAAM,IAAU,KAAK,IAAI,IAAI,KAAK,MAAM,IAAQ,GAAG,CAAC,EAC9C,IACJ,EAAO,SAAS,IACZ,EAAO,MAAM,CAAC,EAAQ,GACtB,GAGA,IAAY,EAAc,KAAK,MAAU,IAAQ,EAAU,EAG3D,IAAc,KAAK,IAAI,GAAG,GAAW,EAAE,EACvC,IAAc,KAAK,IAAI,GAAG,GAAW,EAAE;AAG7C,KAFc,KAAK,IAAI,KAAK,IAAI,EAAY,EAAE,KAAK,IAAI,EAAY,CAAC,KAEtD,KAAK,EAAU,WAAW,EACtC,QACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO;GAAE,OAAO,GAAG,EAAM;GAAK,QAAQ,GAAG,EAAO;GAAK;YAErD,kBAAC,QAAD;GAAM,WAAU;aAAgC;GAAuB,CAAA;EACnE,CAAA;CAKV,IACM,IAAiB,KAAS,EAAc,SAAS,KAAK,GACtD,IAAW,KAAK,IAAI,GAAG,IAAiB,EAAc,OAAO,EAK7D,IAAa,IAAc,GAC3B,IACJ,IAAa,IAAK,IAAc,IAAc,MAAM;AAEtD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CAEE,kBAAC,OAAD;GACE,WAAU;GACV,OAAO;IACL,OAAO,GAAG,EAAM;IAChB,QAAQ,GAAG,EAAO;IACnB;aALH,CAQE,kBAAC,OAAD;IACE,WAAU;IACV,OAAO;KACL,QAAQ;KACR,KAAK,GAAG,EAAgB;KACxB,iBAAiB;KACjB,QAAQ;KACT;IACD,CAAA,EAGD,EAAU,KAAK,GAAU,MAAU;IAElC,IAAM,IAAmB,KAAK,IAAI,EAAS,GAAG,GACxC,IAAY,KAAK,IAAI,GAAG,KAAoB,IAAS,GAAG,EACxD,IAAa,KAAY,GACzB,IAAc,MAAU,EAAc,SAAS,GAC/C,IAAQ,IAAa,IAAgB;AAG3C,WACE,kBAAC,OAAD;KAEE,WAAU;KACV,OAAO;MACL,MAAM,GAPM,KAAS,IAAW,GAOb;MACnB,OAAO,GAAG,EAAS;MACnB,QAAQ,GAAG,EAAU;MACrB,iBAAiB;MACjB,SAAS,IAAc,IAAI;MAE3B,GAAI,IACA,EAAE,QAAQ,GAAG,MAAM,EAAgB,IAAI,GACvC,EAAE,KAAK,GAAG,EAAgB,IAAI;MAClC,QAAQ;MACT;KACD,OAAO,GAAG,EAAY,EAAc,GAAO,CAAC,IAAI,KAAY,IAAI,MAAM,KAAK,EAAY,EAAS,CAAC;KACjG,EAfK,EAeL;KAEJ,CACE;MAGN,kBAAC,OAAD;GACE,WAAU;GACV,OAAO,EAAE,QAAQ,GAAG,EAAO,KAAK;aAFlC,CAIE,kBAAC,QAAD,EAAA,UAAA,CAAM,KAAE,EAAY,EAAY,CAAQ,EAAA,CAAA,EACxC,kBAAC,QAAD,EAAA,UAAA,CACqB,IAClB,EAAY,EAAY,CACpB,EAAA,CAAA,CACH;KACF;;;AAIV,IAAM,IAAW,EAAM,KAAK,SAAkB,EAC5C,MAAA,GACA,gBACA,mBAAgB,EAAE,EAClB,gBACA,YAAS,QACT,mBACa;CACb,IAAM,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAW,KAAgB,EAAS,IAAI,EACzC,IAAe,EAAuB,KAAK,EAC3C,IAAW,EAAuB,KAAK,EAEvC,IAAgB,GAAmB;AAmDzC,KAhDA,QAAgB;EACd,IAAM,UAAyB;AAC7B,OAAI,EAAa,SAAS;IAExB,IAAM,IADY,EAAa,QACR,uBAAuB,EACxC,IAAiB,EAAK,OACtB,IAAkB,EAAK;AAE7B,QAAI,IAAiB,KAAK,IAAkB,GAAG;KAC7C,IAAM,IAAiB,IAAiB,GAClC,IAAkB,IAAkB;AAK1C,KAFA,EADwB,KAAK,IAAI,IAAI,KAAK,IADrB,KAAK,IAAI,GAAgB,EAAgB,EACF,IAAI,CAAC,CACrC,EAE5B,iBAAiB;AACf,UAAI,EAAS,SAAS;OAEpB,IAAM,IADW,EAAS,QAAQ,uBAAuB,CAC1B,OAEzB,IAAoB,IAAiB,KACrC,IAAiB,KAAK,IAC1B,GACA,KAAK,IAAI,GAAmB,IAAiB,GAAI,CAClD;AACD,SAAa,KAAK,IAAI,KAAK,EAAe,CAAC;;QAE5C,GAAG;;;KAKN,IAAQ,WAAW,GAAkB,GAAG,EAExC,IAAiB,IAAI,qBAAqB;AAC9C,cAAW,GAAkB,GAAG;IAChC;AAMF,SAJI,EAAa,WACf,EAAe,QAAQ,EAAa,QAAQ,QAGjC;AAEX,GADA,aAAa,EAAM,EACnB,EAAe,YAAY;;IAE5B,CAAC,GAAM,EAAY,CAAC,EAEnB,CAAC,KAAQ,EAAK,WAAW,EAC3B,QACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO;GACL,QAAQ,MAAW,SAAS,SAAS;GACrC,WAAW,MAAW,SAAS,UAAU,KAAA;GAC1C;YAED,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAsC;IAAuB,CAAA,EAC5E,kBAAC,OAAD;IAAK,WAAU;cAAoC;IAE7C,CAAA,CACF;;EACF,CAAA;CAKV,IAAI,IAAwB,EAAE,EAC1B,IAA4B,EAAE;AAclC,KAZI,GAAa,UACf,IAAc,MAAM,QAAQ,EAAY,MAAM,GAC1C,EAAY,QACZ,CAAC,EAAY,MAAM,GAGrB,GAAa,UACf,IAAkB,MAAM,QAAQ,EAAY,MAAM,GAC9C,EAAY,QACZ,CAAC,EAAY,MAAM,GAGrB,EAAY,WAAW,EACzB,QACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO;GACL,QAAQ,MAAW,SAAS,SAAS;GACrC,WAAW,MAAW,SAAS,UAAU,KAAA;GACzC,iBAAiB;GACjB,OAAO;GACP,aAAa;GACd;YAED,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAsC;IAAyB,CAAA,EAC9E,kBAAC,OAAD;IAAK,WAAU;cAAa;IAAiC,CAAA,CACzD;;EACF,CAAA;CAIV,IAAM,IAAa,EAAY,IACzB,IAAiB,EAAgB,IAGnC,IAAa,CAAC,GAAG,EAAK;AAC1B,CAAI,MACF,IAAa,EAAW,MAAM,GAAG,MAAM;EACrC,IAAM,IAAO,EAAE,IACT,IAAO,EAAE;AAGf,SAFI,IAAO,IAAa,KACpB,IAAO,IAAa,IACjB;GACP;CAIJ,IAAM,EAAE,2BAAwB,IAAM,oBAAiB,OACrD,GACI,EACJ,iBACA,6BACA,sBACA,mBACE,EACF,GACA,GACA,GACA,GACA,EACD,EAMK,IAHY,EAIf,KAAK,MAAQ,EAAI,GAAY,CAC7B,QAAQ,MAAQ,KAAQ,QAA6B,CAAC,MAAM,OAAO,EAAI,CAAC,CAAC,CACzE,KAAK,MAAQ,OAAO,EAAI,CAAC;AAE5B,KAAI,EAAO,SAAS,EAClB,QACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO;GACL,QAAQ,MAAW,SAAS,SAAS;GACrC,WAAW,MAAW,SAAS,UAAU,KAAA;GACzC,iBAAiB;GACjB,OAAO;GACP,aAAa;GACd;YAED,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAsC;KAAuB,CAAA;IAC5E,kBAAC,OAAD;KAAK,WAAU;eAAa;KAEtB,CAAA;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CAA4B,yBAAsB,EAAO,OAAa;;IAClE;;EACF,CAAA;CAKV,IAAM,IAAY,EAAO,EAAO,SAAS,IACnC,IAAkB,EAAO,EAAO,SAAS,IACzC,IAAiB,IAAY,GAC7B,IACJ,MAAoB,IAEhB,IADC,IAAiB,KAAK,IAAI,EAAgB,GAAI,KAG/C,IAAmB,KAAkB,GAGrC,KAAgB,MAA6C;AAEjE,MAAI,EAAc,YAChB,QAAO,EAAc,YAAY,EAAM;AAIzC,MAAI,KAAU,KACZ,QAAO;EAGT,IAAM,IAAW,EAAc,YAAY,GACrC,IAAS,EAAc,UAAU,IAEnC;AAYJ,SAVA,AAOE,IAPE,KAAK,IAAI,EAAM,IAAI,OACH,IAAQ,KAAK,QAAQ,EAAS,GAAG,MAC1C,KAAK,IAAI,EAAM,IAAI,OACV,IAAQ,KAAK,QAAQ,EAAS,GAAG,MAC1C,KAAK,IAAI,EAAM,IAAI,OACV,IAAQ,KAAK,QAAQ,EAAS,GAAG,MAElC,EAAM,QAAQ,EAAS,EAGnC,IAAS;IAIZ,UAAiC;AACrC,MACE,EAAc,uBAAuB,KAAA,KACrC,GAAc,QACd;GACA,IAAM,IAAa,EAAc;AACjC,OAAI,KAAc,KAAK,IAAa,EAAa,OAAO,OACtD,QAAO,EAAa,OAAO;;AAG/B,SAAO;IAGH,UAAiC;AACrC,MACE,EAAc,uBAAuB,KAAA,KACrC,GAAc,QACd;GACA,IAAM,IAAa,EAAc;AACjC,OAAI,KAAc,KAAK,IAAa,EAAa,OAAO,OACtD,QAAO,EAAa,OAAO;;AAG/B,SAAO;IAGH,IAAgB,GAAkB,EAClC,IAAgB,GAAkB,EAClC,IAAe,IAAmB,IAAgB;AAExD,QACE,kBAAC,OAAD;EACE,KAAK;EACL,WAAU;EACV,OAAO;GACL,QAAQ,MAAW,SAAS,SAAS;GACrC,WAAW,MAAW,SAAS,UAAU,KAAA;GAC1C;YANH;GASE,kBAAC,OAAD;IACE,WAAU;IACV,OAAO;KACL,UAAU;KACV,YAAY;KACb;cALH,CAOE,kBAAC,QAAD,EAAA,iBACU;KACN,IAAM,IAAQ,EAAc,EAAW;AACvC,YAAO,KAAS,EAAM,SAAS,IAAI,IAAQ;QACzC,EACC,CAAA,GACL,KAA4B,MAC5B,kBAAC,QAAD;KACE,OACE,IACI,iBAAiB,KAAe,aAChC,+BAA+B;KAErC,WAAU;eAEV,kBAAC,GAAD;MACE,MAAM;MACN,WAAU;MACV,CAAA;KACG,CAAA,CAEL;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KACE,KAAK;KACL,WAAU;KACV,OAAO;MACL,UAAU,GAAG,EAAS;MACtB,OAAO;MACR;eAEA,EAAa,EAAU;KACpB,CAAA,EAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,OAAD;MACE,WAAU;MACV,OAAO;OACL,OAAO;OACP,UAAU,GAAG,IAAW,IAAK;OAC9B;gBAEA,IAAmB,MAAM;MACtB,CAAA,EAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OACE,WAAU;OACV,OAAO;QACL,UAAU,GAAG,IAAW,IAAK;QAC7B,OAAO;QACR;iBALH,CAOG,IAAmB,MAAM,IACzB,EAAa,EAAe,CACzB;UACN,kBAAC,OAAD;OACE,WAAU;OACV,OAAO;QACL,UAAU,GAAG,IAAW,IAAK;QAC7B,OAAO;QACP,SAAS;QACV;iBANH;QAQG,IAAmB,MAAM;QACzB,EAAiB,QAAQ,EAAE;QAAC;QACzB;SACF;QACF;OACF;;GAGL,EAAc,UAAU,CAAC,EAAc,eACtC,kBAAC,OAAD;IACE,WAAU;IACV,OAAO;KACL,UAAU;KACV,YAAY;KACZ,SAAS;KACV;cAEA,EAAc;IACX,CAAA;GAIP,EAAc,kBAAkB,MAAS,EAAO,SAAS,KACxD,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACU;KACG;KACI;KACA;KACf,aAAa;KACb,OAAO;KACP,QAAQ;KACR,CAAA;IACE,CAAA;GAEJ;;EAER"}
|