drizzle-cube 0.6.7 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/ActivityGridChart.config-DaYILvFZ.cjs +1 -0
- package/dist/adapters/ActivityGridChart.config-_SyTBEyR.js +2 -0
- package/dist/adapters/AreaChart.config-BKh6fY1_.cjs +1 -0
- package/dist/adapters/AreaChart.config-CJoxLP3Y.js +2 -0
- package/dist/adapters/BarChart.config-Bm1bfRbI.cjs +1 -0
- package/dist/adapters/BarChart.config-CGu2Bqrx.js +2 -0
- package/dist/adapters/BoxPlotChart.config-B7M9CQaE.js +2 -0
- package/dist/adapters/BoxPlotChart.config-CXQel8Hk.cjs +1 -0
- package/dist/adapters/BubbleChart.config-BJ36IuVS.cjs +1 -0
- package/dist/adapters/BubbleChart.config-CWoXeLdm.js +2 -0
- package/dist/adapters/CandlestickChart.config-CGMLZVIy.cjs +1 -0
- package/dist/adapters/CandlestickChart.config-CzOcfF1e.js +2 -0
- package/dist/adapters/DataTable.config-CdAzueTs.cjs +1 -0
- package/dist/adapters/DataTable.config-D_KmPyxV.js +2 -0
- package/dist/adapters/FunnelChart.config-D1QAUuYl.cjs +1 -0
- package/dist/adapters/FunnelChart.config-DCRHQoFc.js +2 -0
- package/dist/adapters/GaugeChart.config-DAybRXP3.js +2 -0
- package/dist/adapters/GaugeChart.config-WUPfyLP-.cjs +1 -0
- package/dist/adapters/HeatMapChart.config-CziltPnC.cjs +1 -0
- package/dist/adapters/HeatMapChart.config-DHpXNONW.js +2 -0
- package/dist/adapters/KpiDelta.config-Bw5o0ocY.js +2 -0
- package/dist/adapters/KpiDelta.config-CdAmE1pw.cjs +1 -0
- package/dist/adapters/KpiNumber.config-CQ_wq2gx.js +2 -0
- package/dist/adapters/KpiNumber.config-wxN8x4DC.cjs +1 -0
- package/dist/adapters/KpiText.config-DIRIC97F.cjs +1 -0
- package/dist/adapters/KpiText.config-eODcP6Hn.js +2 -0
- package/dist/adapters/LineChart.config-32-9zA2V.js +2 -0
- package/dist/adapters/LineChart.config-BukeFp6z.cjs +1 -0
- package/dist/adapters/MarkdownChart.config-BVsR5LDy.js +2 -0
- package/dist/adapters/MarkdownChart.config-j4veYjcZ.cjs +1 -0
- package/dist/adapters/MeasureProfileChart.config-BRDYx1hw.js +2 -0
- package/dist/adapters/MeasureProfileChart.config-CIosBxco.cjs +1 -0
- package/dist/adapters/PieChart.config-Bj8KiA0n.cjs +1 -0
- package/dist/adapters/PieChart.config-DgAj3y-I.js +2 -0
- package/dist/adapters/ProportionBarChart.config-BfFOppHY.js +2 -0
- package/dist/adapters/ProportionBarChart.config-c8GGE1aK.cjs +1 -0
- package/dist/adapters/RadarChart.config-D7iHY1v9.js +2 -0
- package/dist/adapters/RadarChart.config-ghlANG2-.cjs +1 -0
- package/dist/adapters/RadialBarChart.config-CsXIz-0v.js +2 -0
- package/dist/adapters/RadialBarChart.config-Dsvu2CPV.cjs +1 -0
- package/dist/adapters/RetentionCombinedChart.config-CdT1kXC7.js +2 -0
- package/dist/adapters/RetentionCombinedChart.config-sDjFKH9K.cjs +1 -0
- package/dist/adapters/RetentionHeatmap.config-CvKBvQmu.cjs +1 -0
- package/dist/adapters/RetentionHeatmap.config-DbM3ICwc.js +2 -0
- package/dist/adapters/SankeyChart.config-BNBcIHaD.js +2 -0
- package/dist/adapters/SankeyChart.config-DZscz3kh.cjs +1 -0
- package/dist/adapters/ScatterChart.config-BNYIddyM.cjs +1 -0
- package/dist/adapters/ScatterChart.config-CEhtoLWc.js +2 -0
- package/dist/adapters/SunburstChart.config-ClseCzji.cjs +1 -0
- package/dist/adapters/SunburstChart.config-nnphNoVO.js +2 -0
- package/dist/adapters/TreeMapChart.config-C8-75muA.js +2 -0
- package/dist/adapters/TreeMapChart.config-DoXeUg9Y.cjs +1 -0
- package/dist/adapters/WaterfallChart.config-BOF1qxY3.cjs +1 -0
- package/dist/adapters/WaterfallChart.config-uW5YyOji.js +2 -0
- package/dist/adapters/{compiler-CGJQlQ6Q.cjs → compiler-DAyodAnH.cjs} +1 -1
- package/dist/adapters/{compiler-mz2Uudwd.js → compiler-Dq04wAjm.js} +1 -1
- package/dist/adapters/core/index.cjs +1 -1
- package/dist/adapters/core/index.js +1 -1
- package/dist/adapters/{core-4WY4M7hp.js → core-BjxfG6Uf.js} +2 -2
- package/dist/adapters/{core-FpI2NfHt.cjs → core-zV2w0hO2.cjs} +1 -1
- package/dist/adapters/express/index.cjs +1 -1
- package/dist/adapters/express/index.js +5 -5
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/fastify/index.js +5 -5
- package/dist/adapters/{handler-g8k2CZG2.cjs → handler-C6Hj6_Yk.cjs} +3 -3
- package/dist/adapters/{handler-PjEeNmXY.js → handler-ox6qxLw5.js} +29 -29
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/hono/index.js +4 -4
- package/dist/adapters/mcp-tools.cjs +1 -1
- package/dist/adapters/mcp-tools.js +2 -2
- package/dist/adapters/mcp-transport-C_t_p0wU.cjs +40 -0
- package/dist/adapters/mcp-transport-DFH9e6de.js +603 -0
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.js +5 -5
- package/dist/adapters/{openai-DnWF0wCU.cjs → openai-BffpdO4n.cjs} +1 -1
- package/dist/adapters/{openai-DHjN2OwG.js → openai-C9LNhHfK.js} +1 -1
- package/dist/adapters/{openai-CcY2fW3E.js → openai-CmyXJWti.js} +1 -1
- package/dist/adapters/{openai-7xjDI7R2.cjs → openai-DlT-0VRC.cjs} +1 -1
- package/dist/adapters/utils-C5FLHK04.cjs +128 -0
- package/dist/adapters/{utils-G52pPkce.js → utils-tjiwXFnP.js} +18 -0
- package/dist/adapters/utils.cjs +1 -1
- package/dist/adapters/utils.js +1 -1
- package/dist/cjs/client/components/DashboardPortletCard.d.ts +1 -1
- package/dist/cjs/client/components/PortletGroupCard.d.ts +18 -0
- package/dist/cjs/client/components/RowManagedLayout.d.ts +15 -3
- package/dist/cjs/client/components/dashboard/DashboardContext.d.ts +5 -1
- package/dist/cjs/client/components/dashboard/dashboardGridUtils.d.ts +2 -4
- package/dist/cjs/client/components/dashboardPortletCard/EditActionButtons.d.ts +34 -0
- package/dist/cjs/client/components/dashboardPortletCard/PortletCardHeader.d.ts +2 -12
- package/dist/cjs/client/components/dashboardPortletCard/PortletFloatingActions.d.ts +24 -0
- package/dist/cjs/client/components/dashboardPortletCard/cardStyles.d.ts +5 -0
- package/dist/cjs/client/components/dashboardPortletCard/propsEqual.d.ts +2 -0
- package/dist/cjs/client/components/mobileStackedLayout/memberDisplay.d.ts +21 -0
- package/dist/cjs/client/components.d.ts +1 -1
- package/dist/cjs/client/hooks/dashboard/groupGeometry.d.ts +23 -0
- package/dist/cjs/client/hooks/dashboard/groupUtils.d.ts +57 -0
- package/dist/cjs/client/hooks/dashboard/layoutUtils.d.ts +34 -3
- package/dist/cjs/client/hooks/dashboard/useDashboardController.d.ts +11 -3
- package/dist/cjs/client/hooks/dashboard/useRowLayoutEngine.d.ts +12 -2
- package/dist/cjs/client/hooks/useDashboardHook.d.ts +14 -1
- package/dist/cjs/client/index.d.ts +1 -1
- package/dist/cjs/client/stores/dashboardStore.d.ts +11 -1
- package/dist/cjs/client/types.d.ts +30 -1
- package/dist/client/charts.js +12 -12
- package/dist/client/chunks/{DashboardEditModal-BEG0d7wI.js → DashboardEditModal-ByXLlXZD.js} +3289 -2370
- package/dist/client/chunks/DashboardEditModal-ByXLlXZD.js.map +1 -0
- package/dist/client/chunks/{ProportionBarChart-xLX6XD4W.js → ProportionBarChart-lOeVz8ZC.js} +3 -3
- package/dist/client/chunks/{ProportionBarChart-xLX6XD4W.js.map → ProportionBarChart-lOeVz8ZC.js.map} +1 -1
- package/dist/client/chunks/{RetentionCombinedChart-DeFz5wJh.js → RetentionCombinedChart-Mcm7zHFx.js} +3 -3
- package/dist/client/chunks/{RetentionCombinedChart-DeFz5wJh.js.map → RetentionCombinedChart-Mcm7zHFx.js.map} +1 -1
- package/dist/client/chunks/{RetentionHeatmap-DkJOiNwz.js → RetentionHeatmap-4VJx5KDM.js} +2 -2
- package/dist/client/chunks/{RetentionHeatmap-DkJOiNwz.js.map → RetentionHeatmap-4VJx5KDM.js.map} +1 -1
- package/dist/client/chunks/{analysis-builder-BWLL6ztg.js → analysis-builder-CWTJQQtL.js} +9 -9
- package/dist/client/chunks/{analysis-builder-BWLL6ztg.js.map → analysis-builder-CWTJQQtL.js.map} +1 -1
- package/dist/client/chunks/{analysis-builder-shared-Ba3JsRNw.js → analysis-builder-shared-l-4WKVS1.js} +9 -9
- package/dist/client/chunks/{analysis-builder-shared-Ba3JsRNw.js.map → analysis-builder-shared-l-4WKVS1.js.map} +1 -1
- package/dist/client/chunks/{chart-activity-grid-CyVDJSAI.js → chart-activity-grid-DVOhLgxv.js} +20 -2
- package/dist/client/chunks/{chart-activity-grid-CyVDJSAI.js.map → chart-activity-grid-DVOhLgxv.js.map} +1 -1
- package/dist/client/chunks/{chart-area-b5UAS6NZ.js → chart-area-C-5ATxvi.js} +2 -2
- package/dist/client/chunks/{chart-area-b5UAS6NZ.js.map → chart-area-C-5ATxvi.js.map} +1 -1
- package/dist/client/chunks/{chart-bar-aVNonvWw.js → chart-bar-CPiLaXHP.js} +3 -3
- package/dist/client/chunks/{chart-bar-aVNonvWw.js.map → chart-bar-CPiLaXHP.js.map} +1 -1
- package/dist/client/chunks/{chart-box-plot-Bx6M97VL.js → chart-box-plot-Bae5O9xC.js} +2 -2
- package/dist/client/chunks/{chart-box-plot-Bx6M97VL.js.map → chart-box-plot-Bae5O9xC.js.map} +1 -1
- package/dist/client/chunks/{chart-bubble-EWd4_ucG.js → chart-bubble-BFN5WIpf.js} +2 -2
- package/dist/client/chunks/{chart-bubble-EWd4_ucG.js.map → chart-bubble-BFN5WIpf.js.map} +1 -1
- package/dist/client/chunks/{chart-candlestick-CG5EX67b.js → chart-candlestick-PBQKp_z7.js} +2 -2
- package/dist/client/chunks/{chart-candlestick-CG5EX67b.js.map → chart-candlestick-PBQKp_z7.js.map} +1 -1
- package/dist/client/chunks/{chart-data-table-D6Cu86m_.js → chart-data-table-SNSqZj9B.js} +2 -2
- package/dist/client/chunks/{chart-data-table-D6Cu86m_.js.map → chart-data-table-SNSqZj9B.js.map} +1 -1
- package/dist/client/chunks/{chart-funnel-CLRYMbFj.js → chart-funnel-CkHSXp6h.js} +2 -2
- package/dist/client/chunks/{chart-funnel-CLRYMbFj.js.map → chart-funnel-CkHSXp6h.js.map} +1 -1
- package/dist/client/chunks/{chart-gauge-BRqb8eNU.js → chart-gauge-DHIGq3TH.js} +3 -3
- package/dist/client/chunks/{chart-gauge-BRqb8eNU.js.map → chart-gauge-DHIGq3TH.js.map} +1 -1
- package/dist/client/chunks/{chart-heat-map-BCfr3nYJ.js → chart-heat-map-Bb8L1XQr.js} +3 -3
- package/dist/client/chunks/{chart-heat-map-BCfr3nYJ.js.map → chart-heat-map-Bb8L1XQr.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-delta-ISxzG6g_.js → chart-kpi-delta-BJCOXE-F.js} +3 -3
- package/dist/client/chunks/{chart-kpi-delta-ISxzG6g_.js.map → chart-kpi-delta-BJCOXE-F.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-number-OmRCPf7D.js → chart-kpi-number-Cdqgv20g.js} +5 -5
- package/dist/client/chunks/{chart-kpi-number-OmRCPf7D.js.map → chart-kpi-number-Cdqgv20g.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-text-BUm932QA.js → chart-kpi-text-B10ZHCsI.js} +3 -3
- package/dist/client/chunks/{chart-kpi-text-BUm932QA.js.map → chart-kpi-text-B10ZHCsI.js.map} +1 -1
- package/dist/client/chunks/{chart-line-BM_DrpQW.js → chart-line-CzvVL3Be.js} +3 -3
- package/dist/client/chunks/{chart-line-BM_DrpQW.js.map → chart-line-CzvVL3Be.js.map} +1 -1
- package/dist/client/chunks/{chart-markdown-BNrsZpqM.js → chart-markdown-C0xxBW81.js} +2 -2
- package/dist/client/chunks/{chart-markdown-BNrsZpqM.js.map → chart-markdown-C0xxBW81.js.map} +1 -1
- package/dist/client/chunks/{chart-measure-profile-DttUWxIu.js → chart-measure-profile-BSUPFZCR.js} +3 -3
- package/dist/client/chunks/{chart-measure-profile-DttUWxIu.js.map → chart-measure-profile-BSUPFZCR.js.map} +1 -1
- package/dist/client/chunks/{chart-pie-D7pxCMll.js → chart-pie-CwRfwWWZ.js} +3 -3
- package/dist/client/chunks/{chart-pie-D7pxCMll.js.map → chart-pie-CwRfwWWZ.js.map} +1 -1
- package/dist/client/chunks/{chart-radar-sUnyHn3a.js → chart-radar-Dfql2E7i.js} +3 -3
- package/dist/client/chunks/{chart-radar-sUnyHn3a.js.map → chart-radar-Dfql2E7i.js.map} +1 -1
- package/dist/client/chunks/{chart-radial-bar-Df9zbhst.js → chart-radial-bar-oE9OZ1IX.js} +3 -3
- package/dist/client/chunks/{chart-radial-bar-Df9zbhst.js.map → chart-radial-bar-oE9OZ1IX.js.map} +1 -1
- package/dist/client/chunks/{chart-sankey-d_iS5fjB.js → chart-sankey-tK7Oz1bC.js} +2 -2
- package/dist/client/chunks/{chart-sankey-d_iS5fjB.js.map → chart-sankey-tK7Oz1bC.js.map} +1 -1
- package/dist/client/chunks/{chart-scatter-DD5v7r4K.js → chart-scatter-BtLfAsZb.js} +3 -3
- package/dist/client/chunks/{chart-scatter-DD5v7r4K.js.map → chart-scatter-BtLfAsZb.js.map} +1 -1
- package/dist/client/chunks/{chart-sunburst-jL5f9PiB.js → chart-sunburst-B7ryWwQ1.js} +3 -3
- package/dist/client/chunks/{chart-sunburst-jL5f9PiB.js.map → chart-sunburst-B7ryWwQ1.js.map} +1 -1
- package/dist/client/chunks/{chart-tree-map-DK-pNvh4.js → chart-tree-map-DU5M3-qe.js} +3 -3
- package/dist/client/chunks/{chart-tree-map-DK-pNvh4.js.map → chart-tree-map-DU5M3-qe.js.map} +1 -1
- package/dist/client/chunks/{chart-waterfall-CBGewe2Z.js → chart-waterfall-CAwutpzd.js} +3 -3
- package/dist/client/chunks/{chart-waterfall-CBGewe2Z.js.map → chart-waterfall-CAwutpzd.js.map} +1 -1
- package/dist/client/chunks/{charts-core-BHjcM-rm.js → charts-core-BxBB2zBE.js} +2 -2
- package/dist/client/chunks/{charts-core-BHjcM-rm.js.map → charts-core-BxBB2zBE.js.map} +1 -1
- package/dist/client/chunks/charts-loader-0avVzeDd.js +85 -0
- package/dist/client/chunks/{charts-loader-FXj1JiQZ.js.map → charts-loader-0avVzeDd.js.map} +1 -1
- package/dist/client/chunks/{nl-NL-B1MNDJT-.js → nl-NL-BSMhprPr.js} +19 -1
- package/dist/client/chunks/{nl-NL-B1MNDJT-.js.map → nl-NL-BSMhprPr.js.map} +1 -1
- package/dist/client/chunks/{schema-visualization-BXdd320w.js → schema-visualization-CGbnbzj_.js} +3 -3
- package/dist/client/chunks/{schema-visualization-BXdd320w.js.map → schema-visualization-CGbnbzj_.js.map} +1 -1
- package/dist/client/chunks/{useDirtyStateTracking-CFgSTyny.js → useDirtyStateTracking-Dc-Si4db.js} +4 -4
- package/dist/client/chunks/{useDirtyStateTracking-CFgSTyny.js.map → useDirtyStateTracking-Dc-Si4db.js.map} +1 -1
- package/dist/client/chunks/{useExplainAI-CisDjZNp.js → useExplainAI-DJbc7Lu4.js} +4 -4
- package/dist/client/chunks/{useExplainAI-CisDjZNp.js.map → useExplainAI-DJbc7Lu4.js.map} +1 -1
- package/dist/client/chunks/{vendor-D5xADz_A.js → vendor-cTfe9Kho.js} +2 -2
- package/dist/client/chunks/{vendor-D5xADz_A.js.map → vendor-cTfe9Kho.js.map} +1 -1
- package/dist/client/components/DashboardPortletCard.d.ts +1 -1
- package/dist/client/components/PortletGroupCard.d.ts +18 -0
- package/dist/client/components/RowManagedLayout.d.ts +15 -3
- package/dist/client/components/dashboard/DashboardContext.d.ts +5 -1
- package/dist/client/components/dashboard/dashboardGridUtils.d.ts +2 -4
- package/dist/client/components/dashboardPortletCard/EditActionButtons.d.ts +34 -0
- package/dist/client/components/dashboardPortletCard/PortletCardHeader.d.ts +2 -12
- package/dist/client/components/dashboardPortletCard/PortletFloatingActions.d.ts +24 -0
- package/dist/client/components/dashboardPortletCard/cardStyles.d.ts +5 -0
- package/dist/client/components/dashboardPortletCard/propsEqual.d.ts +2 -0
- package/dist/client/components/mobileStackedLayout/memberDisplay.d.ts +21 -0
- package/dist/client/components.d.ts +1 -1
- package/dist/client/components.js +1 -1
- package/dist/client/hooks/dashboard/groupGeometry.d.ts +23 -0
- package/dist/client/hooks/dashboard/groupUtils.d.ts +57 -0
- package/dist/client/hooks/dashboard/layoutUtils.d.ts +34 -3
- package/dist/client/hooks/dashboard/useDashboardController.d.ts +11 -3
- package/dist/client/hooks/dashboard/useRowLayoutEngine.d.ts +12 -2
- package/dist/client/hooks/useDashboardHook.d.ts +14 -1
- package/dist/client/hooks.js +3 -3
- package/dist/client/icons.js +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +13 -13
- package/dist/client/providers.js +1 -1
- package/dist/client/schema.js +1 -1
- package/dist/client/stores/dashboardStore.d.ts +11 -1
- package/dist/client/styles.css +1 -1
- package/dist/client/types.d.ts +30 -1
- package/dist/client/utils.js +4 -4
- package/dist/client-bundle-stats.html +1 -1
- package/dist/mcp-app/mcp-app.html +3 -3
- package/dist/server/index.cjs +1 -1
- package/dist/server/index.js +18 -0
- package/package.json +1 -1
- package/dist/adapters/ActivityGridChart.config-CA4DWvoH.js +0 -2
- package/dist/adapters/ActivityGridChart.config-DC2PXV56.cjs +0 -1
- package/dist/adapters/AreaChart.config-BY4hUUJY.js +0 -2
- package/dist/adapters/AreaChart.config-CPwW53vL.cjs +0 -1
- package/dist/adapters/BarChart.config-Be-PGzp5.js +0 -2
- package/dist/adapters/BarChart.config-CfdsJtHY.cjs +0 -1
- package/dist/adapters/BoxPlotChart.config-BYwJZm4S.js +0 -2
- package/dist/adapters/BoxPlotChart.config-BoZN94lR.cjs +0 -1
- package/dist/adapters/BubbleChart.config-DX9aSqMD.js +0 -2
- package/dist/adapters/BubbleChart.config-ijpB_fme.cjs +0 -1
- package/dist/adapters/CandlestickChart.config-C3iEXUN1.js +0 -2
- package/dist/adapters/CandlestickChart.config-DIGeboSZ.cjs +0 -1
- package/dist/adapters/DataTable.config-BjFTY-Md.js +0 -2
- package/dist/adapters/DataTable.config-ClJZRtsp.cjs +0 -1
- package/dist/adapters/FunnelChart.config-DYDd9Y2z.cjs +0 -1
- package/dist/adapters/FunnelChart.config-cVxnY9hD.js +0 -2
- package/dist/adapters/GaugeChart.config-BxWKW2b5.js +0 -2
- package/dist/adapters/GaugeChart.config-DsmevM9y.cjs +0 -1
- package/dist/adapters/HeatMapChart.config-BhO-Wj54.js +0 -2
- package/dist/adapters/HeatMapChart.config-HWcgNRuG.cjs +0 -1
- package/dist/adapters/KpiDelta.config-D3JOnd5n.js +0 -2
- package/dist/adapters/KpiDelta.config-DmLmTPSH.cjs +0 -1
- package/dist/adapters/KpiNumber.config-CVghzBbF.cjs +0 -1
- package/dist/adapters/KpiNumber.config-aK-Ueuhk.js +0 -2
- package/dist/adapters/KpiText.config-DLl8NWit.js +0 -2
- package/dist/adapters/KpiText.config-DbWJ42vd.cjs +0 -1
- package/dist/adapters/LineChart.config-C3i3Tgro.cjs +0 -1
- package/dist/adapters/LineChart.config-El3XPCFT.js +0 -2
- package/dist/adapters/MarkdownChart.config-D39QzWHW.js +0 -2
- package/dist/adapters/MarkdownChart.config-e_9gUs93.cjs +0 -1
- package/dist/adapters/MeasureProfileChart.config-BWQ82qcV.js +0 -2
- package/dist/adapters/MeasureProfileChart.config-l9WITF80.cjs +0 -1
- package/dist/adapters/PieChart.config-CO3Gib0h.js +0 -2
- package/dist/adapters/PieChart.config-yqXTNkix.cjs +0 -1
- package/dist/adapters/ProportionBarChart.config-B7CtjFwg.cjs +0 -1
- package/dist/adapters/ProportionBarChart.config-tGjQou_c.js +0 -2
- package/dist/adapters/RadarChart.config-B2YZnD_e.js +0 -2
- package/dist/adapters/RadarChart.config-DwJmHK8y.cjs +0 -1
- package/dist/adapters/RadialBarChart.config-B9XP-cQ1.cjs +0 -1
- package/dist/adapters/RadialBarChart.config-y2riBDwJ.js +0 -2
- package/dist/adapters/RetentionCombinedChart.config-Fzxwm_WV.cjs +0 -1
- package/dist/adapters/RetentionCombinedChart.config-ohy-zQbu.js +0 -2
- package/dist/adapters/RetentionHeatmap.config-0ykBWgUp.js +0 -2
- package/dist/adapters/RetentionHeatmap.config-DF_4ARr0.cjs +0 -1
- package/dist/adapters/SankeyChart.config-ChdkKqAn.cjs +0 -1
- package/dist/adapters/SankeyChart.config-Cl8kxpKq.js +0 -2
- package/dist/adapters/ScatterChart.config-BIqh_Z8u.cjs +0 -1
- package/dist/adapters/ScatterChart.config-D4v0Z_w4.js +0 -2
- package/dist/adapters/SunburstChart.config-CGMw6hSr.cjs +0 -1
- package/dist/adapters/SunburstChart.config-CmK6DVYQ.js +0 -2
- package/dist/adapters/TreeMapChart.config-B6WC-TVm.js +0 -2
- package/dist/adapters/TreeMapChart.config-q-kBddZC.cjs +0 -1
- package/dist/adapters/WaterfallChart.config-BiOMGjjF.cjs +0 -1
- package/dist/adapters/WaterfallChart.config-C_K8zN09.js +0 -2
- package/dist/adapters/mcp-transport-OBc8jVG5.js +0 -603
- package/dist/adapters/mcp-transport-n1r1Kqzn.cjs +0 -40
- package/dist/adapters/utils-DJ0_72_G.cjs +0 -128
- package/dist/client/chunks/DashboardEditModal-BEG0d7wI.js.map +0 -1
- package/dist/client/chunks/charts-loader-FXj1JiQZ.js +0 -85
package/dist/client/chunks/{ProportionBarChart-xLX6XD4W.js → ProportionBarChart-lOeVz8ZC.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as e, S as t, w as n } from "./chart-activity-grid-
|
|
2
|
-
import { D as r, O as i, k as a, s as o } from "./chart-area-
|
|
1
|
+
import { P as e, S as t, w as n } from "./chart-activity-grid-DVOhLgxv.js";
|
|
2
|
+
import { D as r, O as i, k as a, s as o } from "./chart-area-C-5ATxvi.js";
|
|
3
3
|
import s, { useMemo as c } from "react";
|
|
4
4
|
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
5
5
|
//#region src/client/components/charts/ProportionBarChart.tsx
|
|
@@ -106,4 +106,4 @@ var d = s.memo(function({ data: s, chartConfig: d, displayConfig: f = {}, height
|
|
|
106
106
|
//#endregion
|
|
107
107
|
export { d as default };
|
|
108
108
|
|
|
109
|
-
//# sourceMappingURL=ProportionBarChart-
|
|
109
|
+
//# sourceMappingURL=ProportionBarChart-lOeVz8ZC.js.map
|
package/dist/client/chunks/{ProportionBarChart-xLX6XD4W.js.map → ProportionBarChart-lOeVz8ZC.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProportionBarChart-
|
|
1
|
+
{"version":3,"file":"ProportionBarChart-lOeVz8ZC.js","names":[],"sources":["../../../src/client/components/charts/ProportionBarChart.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel.js'\nimport { ChartEmptyState, ChartConfigError, ChartRenderError } from './ChartStates.js'\nimport { getSeriesColor } from './cartesianChartHelpers.js'\nimport { formatAxisValue } from '../../utils/chartUtils.js'\nimport type { ChartProps } from '../../types.js'\n\ninterface Segment {\n /**\n * React key. Category labels are not reliably unique - a query can return\n * duplicates, and every null collapses to the same empty string - so the key\n * is tied to the source row instead. Stable across sorting, which reorders\n * the segments under the same identity.\n */\n id: string\n label: string\n value: number\n share: number\n color: string\n}\n\n/**\n * A single 100%-wide stacked bar plus a labelled percentage legend.\n *\n * A flatter alternative to a pie chart for part-to-whole breakdowns: shares are\n * far easier to compare along one axis than as angles, and it costs a fraction\n * of the vertical space. Deliberately plain HTML rather than Recharts — there is\n * no axis, scale or interaction to justify an SVG chart.\n */\nconst ProportionBarChart = React.memo(function ProportionBarChart({\n data,\n chartConfig,\n displayConfig = {},\n height = '100%',\n colorPalette\n}: ChartProps) {\n const { t } = useTranslation()\n const getFieldLabel = useCubeFieldLabel()\n\n const categoryField = useMemo(() => {\n const x = chartConfig?.xAxis\n return (Array.isArray(x) ? x[0] : x) || ''\n }, [chartConfig?.xAxis])\n\n const valueField = useMemo(() => {\n const y = chartConfig?.yAxis\n return (Array.isArray(y) ? y[0] : y) || ''\n }, [chartConfig?.yAxis])\n\n const sortSegments = displayConfig.sortSegments ?? false\n\n const { segments, total } = useMemo(() => {\n if (!data || !categoryField || !valueField) return { segments: [] as Segment[], total: 0 }\n\n const rows = data\n .map((row: any, sourceIndex: number) => {\n const raw = row?.[valueField]\n const num = typeof raw === 'number' ? raw : parseFloat(String(raw))\n const label = String(row?.[categoryField] ?? '')\n return {\n id: `${sourceIndex}:${label}`,\n label,\n // Negative shares cannot be drawn in a part-to-whole bar.\n value: !isNaN(num) && isFinite(num) && num > 0 ? num : 0\n }\n })\n .filter((row) => row.value > 0)\n\n if (sortSegments) rows.sort((a, b) => b.value - a.value)\n\n const sum = rows.reduce((acc, row) => acc + row.value, 0)\n return {\n total: sum,\n segments: rows.map((row, index) => ({\n ...row,\n share: sum > 0 ? (row.value / sum) * 100 : 0,\n color: getSeriesColor(colorPalette, index)\n }))\n }\n }, [data, categoryField, valueField, colorPalette, sortSegments])\n\n try {\n if (!data || data.length === 0) {\n return <ChartEmptyState height={height} hint={t('chart.runtime.noDataHint.proportionBar')} />\n }\n\n if (!categoryField || !valueField) {\n return <ChartConfigError height={height} hint={t('chart.runtime.configErrorHint.proportionBar')} />\n }\n\n if (segments.length === 0 || total === 0) {\n return (\n <ChartEmptyState\n height={height}\n titleKey=\"chart.runtime.noValidData\"\n hint={t('chart.runtime.noValidDataHint.proportionBar')}\n />\n )\n }\n\n const showLabels = displayConfig.showLabels ?? true\n const showPercentages = displayConfig.showPercentages ?? true\n const decimals = displayConfig.decimals ?? 0\n\n return (\n <div\n className=\"dc:w-full dc:h-full dc:flex dc:flex-col dc:justify-center dc:gap-3 dc:px-3 dc:py-2 dc:overflow-hidden\"\n // Fill the parent rather than the `height` prop, which is a pixel value\n // that can exceed the visible panel and push this short content low.\n style={{ height: '100%', minHeight: '80px' }}\n data-testid=\"proportion-bar\"\n >\n <div className=\"dc:flex dc:w-full dc:overflow-hidden dc:rounded-sm\" style={{ height: 10 }}>\n {segments.map((segment) => (\n <div\n key={segment.id}\n data-testid=\"proportion-bar-segment\"\n title={`${segment.label}: ${formatAxisValue(segment.value, displayConfig.leftYAxisFormat)}`}\n style={{ width: `${segment.share}%`, backgroundColor: segment.color }}\n />\n ))}\n </div>\n\n {(showLabels || showPercentages) && (\n <div className=\"dc:flex dc:flex-wrap dc:gap-x-6 dc:gap-y-2\">\n {segments.map((segment) => (\n <div key={segment.id} className=\"dc:flex dc:flex-col dc:gap-0.5 dc:min-w-0\">\n {showLabels && (\n <span\n className=\"text-dc-text-muted dc:uppercase dc:truncate dc:font-semibold\"\n style={{ fontSize: '11px', letterSpacing: '0.06em' }}\n >\n {segment.label}\n </span>\n )}\n {showPercentages && (\n <span\n className=\"dc:font-semibold dc:leading-none\"\n style={{ fontSize: '18px', color: segment.color }}\n >\n {segment.share.toFixed(decimals)}%\n </span>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n )\n } catch (error) {\n return <ChartRenderError height={height} chartType={getFieldLabel(valueField)} error={error} />\n }\n})\n\nexport default ProportionBarChart\n"],"mappings":"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"}
|
package/dist/client/chunks/{RetentionCombinedChart-DeFz5wJh.js → RetentionCombinedChart-Mcm7zHFx.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as e } from "./retention-ChW9jYdy.js";
|
|
2
|
-
import { P as t, l as n, s as r } from "./chart-activity-grid-
|
|
3
|
-
import { M as i, j as a } from "./chart-area-
|
|
2
|
+
import { P as t, l as n, s as r } from "./chart-activity-grid-DVOhLgxv.js";
|
|
3
|
+
import { M as i, j as a } from "./chart-area-C-5ATxvi.js";
|
|
4
4
|
import o, { useMemo as s, useState as c } from "react";
|
|
5
5
|
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
6
6
|
import { CartesianGrid as d, ComposedChart as f, Legend as p, Line as m, XAxis as h, YAxis as g } from "recharts";
|
|
@@ -267,4 +267,4 @@ var C = o.memo(function({ data: o, height: x = "100%", displayConfig: C, colorPa
|
|
|
267
267
|
//#endregion
|
|
268
268
|
export { C as default };
|
|
269
269
|
|
|
270
|
-
//# sourceMappingURL=RetentionCombinedChart-
|
|
270
|
+
//# sourceMappingURL=RetentionCombinedChart-Mcm7zHFx.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RetentionCombinedChart-DeFz5wJh.js","names":[],"sources":["../../../src/client/components/charts/RetentionCombinedChart.tsx"],"sourcesContent":["/**\n * RetentionCombinedChart Component\n *\n * Combined visualization for retention analysis data.\n * Supports multiple display modes: heatmap, line chart, or combined view.\n *\n * Features:\n * - X-axis: Period numbers (P0, P1, P2...)\n * - Y-axis: Retention % (0-100%)\n * - Lines: One per breakdown value (or single if no breakdown)\n * - Display modes: 'heatmap' | 'line' | 'combined'\n * - Heatmap shows color-coded retention matrix\n * - Line chart shows retention curves over periods\n */\n\nimport React, { useMemo, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport {\n ComposedChart,\n Line,\n XAxis,\n YAxis,\n CartesianGrid,\n Legend,\n} from 'recharts'\nimport ChartContainer from './ChartContainer.js'\nimport ChartTooltip from './ChartTooltip.js'\nimport { CHART_COLORS, CHART_MARGINS } from '../../utils/chartConstants.js'\nimport type { ChartProps } from '../../types.js'\nimport type { RetentionChartData, RetentionResultRow, RetentionGranularity } from '../../types/retention.js'\nimport { isRetentionData } from '../../types/retention.js'\n\n/**\n * Retention display mode\n * - 'heatmap': Show retention as color-coded bars\n * - 'line': Show retention as line curves\n * - 'combined': Show both heatmap background and line overlay\n */\nexport type RetentionDisplayMode = 'heatmap' | 'line' | 'combined'\n\n/**\n * Get color with opacity based on retention rate for heatmap cells\n * Uses a green gradient: higher retention = more saturated green\n */\nfunction getRetentionColor(rate: number): string {\n const clampedRate = Math.max(0, Math.min(1, rate))\n const alpha = 0.1 + clampedRate * 0.7\n return `rgba(34, 197, 94, ${alpha})`\n}\n\n/**\n * Format percentage for display\n */\nfunction formatPercentage(rate: number): string {\n return `${Math.round(rate * 100)}%`\n}\n\n/**\n * Format period label based on granularity\n * Period 0 shows \"< 1 Day\" / \"< 1 Week\" etc. to indicate the initial cohort\n * e.g., Period 0 with 'week' granularity → \"< 1 Week\", Period 1 → \"Week 1\"\n */\nfunction formatPeriodLabel(period: number, granularity?: RetentionGranularity): string {\n const prefix = granularity === 'day' ? 'Day'\n : granularity === 'week' ? 'Week'\n : granularity === 'month' ? 'Month'\n : 'P' // Fallback to P0, P1, etc.\n\n // Period 0 is special - shows \"< 1 Day\" / \"< 1 Week\" etc.\n if (period === 0) {\n return granularity ? `< 1 ${prefix}` : 'P0'\n }\n\n return granularity ? `${prefix} ${period}` : `P${period}`\n}\n\n/**\n * Get display label for the cohort total column\n * Shows \"Total\" regardless of binding key - it's the cohort size count\n */\nfunction getCohortLabel(_bindingKeyLabel?: string): string {\n return 'Total'\n}\n\n/**\n * Get default series name based on binding key\n * e.g., \"userId\" → \"userId Retention\", null → \"Retention\"\n */\nfunction getDefaultSeriesName(bindingKeyLabel?: string): string {\n if (!bindingKeyLabel) return 'Retention'\n return `${bindingKeyLabel} Retention`\n}\n\n/**\n * Transform retention data for chart display\n * Groups data by period with breakdown values as series\n */\nfunction transformRetentionData(\n rows: RetentionResultRow[],\n periods: number[],\n breakdownValues?: string[],\n granularity?: RetentionGranularity,\n bindingKeyLabel?: string\n): { chartData: any[]; seriesKeys: string[]; defaultSeriesName: string } {\n const defaultSeriesName = getDefaultSeriesName(bindingKeyLabel)\n\n // If no breakdown, single series\n if (!breakdownValues || breakdownValues.length === 0) {\n const chartData = periods.map((period) => {\n const row = rows.find((r) => r.period === period && !r.breakdownValue)\n return {\n period,\n periodLabel: formatPeriodLabel(period, granularity),\n [defaultSeriesName]: row ? row.retentionRate : null,\n cohortSize: row?.cohortSize ?? 0,\n retainedUsers: row?.retainedUsers ?? 0,\n }\n })\n return { chartData, seriesKeys: [defaultSeriesName], defaultSeriesName }\n }\n\n // With breakdown, create series per breakdown value\n const chartData = periods.map((period) => {\n const dataPoint: any = {\n period,\n periodLabel: formatPeriodLabel(period, granularity),\n }\n\n breakdownValues.forEach((bv) => {\n const row = rows.find((r) => r.period === period && r.breakdownValue === bv)\n dataPoint[bv] = row ? row.retentionRate : null\n dataPoint[`${bv}_cohortSize`] = row?.cohortSize ?? 0\n dataPoint[`${bv}_retainedUsers`] = row?.retainedUsers ?? 0\n })\n\n return dataPoint\n })\n\n return { chartData, seriesKeys: breakdownValues, defaultSeriesName }\n}\n\ninterface TooltipData {\n period: number\n breakdownValue?: string | null\n cohortSize: number\n retainedUsers: number\n retentionRate: number\n x: number\n y: number\n}\n\n/**\n * RetentionCombinedChart Component\n */\nconst RetentionCombinedChart = React.memo(function RetentionCombinedChart({\n data,\n height = '100%',\n displayConfig,\n colorPalette,\n}: ChartProps) {\n const { t } = useTranslation()\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n const [heatmapTooltip, setHeatmapTooltip] = useState<TooltipData | null>(null)\n\n // Parse retention data\n const retentionData = useMemo<RetentionChartData | null>(() => {\n if (!data) return null\n\n // Check if data is already in RetentionChartData format\n if (isRetentionData(data)) {\n return data\n }\n\n // If data is an array of RetentionResultRow, convert it\n if (Array.isArray(data) && data.length > 0) {\n const rows = data as RetentionResultRow[]\n const periods = [...new Set(rows.map((r) => r.period))].sort((a, b) => a - b)\n const breakdownValues = [\n ...new Set(rows.filter((r) => r.breakdownValue).map((r) => r.breakdownValue!)),\n ]\n\n return {\n rows,\n periods,\n breakdownValues: breakdownValues.length > 0 ? breakdownValues : undefined,\n }\n }\n\n return null\n }, [data])\n\n // Transform data for chart\n const { chartData, seriesKeys, defaultSeriesName } = useMemo(() => {\n if (!retentionData) {\n return { chartData: [], seriesKeys: [], defaultSeriesName: 'Retention' }\n }\n return transformRetentionData(\n retentionData.rows,\n retentionData.periods,\n retentionData.breakdownValues,\n retentionData.granularity,\n retentionData.bindingKeyLabel\n )\n }, [retentionData])\n\n // Get cohort label for heatmap column header\n const cohortLabel = getCohortLabel(retentionData?.bindingKeyLabel)\n\n // Display mode from config\n const displayMode: RetentionDisplayMode =\n (displayConfig as any)?.retentionDisplayMode || 'line'\n\n const showLegend = displayConfig?.showLegend ?? true\n const showGrid = displayConfig?.showGrid ?? true\n const showTooltip = displayConfig?.showTooltip ?? true\n\n // Handle empty/loading states\n if (!data || (Array.isArray(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\">{t('chart.runtime.noData')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {t('chart.runtime.noDataHint.retention')}\n </div>\n </div>\n </div>\n )\n }\n\n if (!chartData || chartData.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\">{t('chart.runtime.unableToRender')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">{t('chart.runtime.dataFormatIncorrect')}</div>\n </div>\n </div>\n )\n }\n\n // Render line chart component (reused in line and combined modes)\n const renderLineChart = (chartHeight: string | number) => {\n const chartMargins = {\n ...CHART_MARGINS,\n left: 50,\n right: 20,\n }\n\n return (\n <ChartContainer height={chartHeight}>\n <ComposedChart data={chartData} margin={chartMargins} accessibilityLayer={false}>\n {showGrid && <CartesianGrid strokeDasharray=\"3 3\" />}\n <XAxis\n dataKey=\"periodLabel\"\n tick={{ fontSize: 12 }}\n axisLine={{ stroke: 'var(--dc-border)' }}\n tickLine={{ stroke: 'var(--dc-border)' }}\n />\n <YAxis\n domain={[0, 1]}\n tickFormatter={(value) => formatPercentage(value)}\n tick={{ fontSize: 12 }}\n axisLine={{ stroke: 'var(--dc-border)' }}\n tickLine={{ stroke: 'var(--dc-border)' }}\n label={{\n value: 'Retention %',\n angle: -90,\n position: 'insideLeft',\n style: { textAnchor: 'middle', fontSize: '12px', fill: 'var(--dc-text-secondary)' },\n }}\n />\n {showTooltip && (\n <ChartTooltip\n formatter={(value: any, name: string) => {\n if (value === null || value === undefined) {\n return ['No data', name]\n }\n return [formatPercentage(value), name]\n }}\n labelFormatter={(label: string) => label}\n />\n )}\n {showLegend && (\n <Legend\n wrapperStyle={{ fontSize: '12px', paddingTop: '10px' }}\n iconType=\"line\"\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => setHoveredLegend(String(o.dataKey || ''))}\n onMouseLeave={() => setHoveredLegend(null)}\n />\n )}\n\n {/* Render lines */}\n {seriesKeys.map((seriesKey, index) => (\n <Line\n key={seriesKey}\n type=\"monotone\"\n dataKey={seriesKey}\n stroke={\n (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) ||\n CHART_COLORS[index % CHART_COLORS.length]\n }\n strokeWidth={2}\n dot={{ r: 4, strokeWidth: 2 }}\n activeDot={{ r: 6 }}\n strokeOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 1 : 0.3) : 1}\n connectNulls={false}\n />\n ))}\n </ComposedChart>\n </ChartContainer>\n )\n }\n\n // Render heatmap table component (reused in heatmap and combined modes)\n const renderHeatmapTable = () => (\n <table className=\"dc:w-full dc:border-collapse dc:text-sm\">\n <thead className=\"dc:sticky dc:top-0 bg-dc-bg dc:z-10\">\n <tr>\n <th className=\"dc:text-left dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[100px] dc:whitespace-nowrap\">\n {retentionData?.breakdownValues?.length ? 'Segment' : 'Cohort'}\n </th>\n <th className=\"dc:text-right dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[60px] dc:whitespace-nowrap\">\n {cohortLabel}\n </th>\n {retentionData?.periods.map((period) => (\n <th\n key={period}\n className=\"dc:text-center dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[70px] dc:whitespace-nowrap\"\n >\n {formatPeriodLabel(period, retentionData?.granularity)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {seriesKeys.map((seriesKey, rowIndex) => {\n const period0Data = chartData.find((d) => d.period === 0)\n const isDefaultSeries = seriesKey === defaultSeriesName\n const cohortSize = isDefaultSeries\n ? period0Data?.cohortSize ?? 0\n : period0Data?.[`${seriesKey}_cohortSize`] ?? 0\n\n return (\n <tr\n key={seriesKey}\n className={rowIndex % 2 === 0 ? 'bg-dc-bg' : 'bg-dc-surface-secondary'}\n >\n <td className=\"dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:whitespace-nowrap\">\n {seriesKey}\n </td>\n <td className=\"dc:p-2 dc:text-right text-dc-text-secondary dc:border-b border-dc-border\">\n {cohortSize.toLocaleString()}\n </td>\n {retentionData?.periods.map((period) => {\n const dataPoint = chartData.find((d) => d.period === period)\n const rate = dataPoint?.[seriesKey] ?? 0\n const bgColor = rate > 0 ? getRetentionColor(rate) : 'transparent'\n const textColor = rate > 0.5 ? '#ffffff' : 'var(--dc-text)'\n\n return (\n <td\n key={period}\n className=\"dc:p-2 dc:text-center dc:border-b border-dc-border dc:cursor-default dc:transition-opacity dc:hover:opacity-80\"\n style={{ backgroundColor: bgColor, color: textColor }}\n onMouseEnter={(e) => {\n const rect = e.currentTarget.getBoundingClientRect()\n const retainedUsers = isDefaultSeries\n ? dataPoint?.retainedUsers ?? 0\n : dataPoint?.[`${seriesKey}_retainedUsers`] ?? 0\n setHeatmapTooltip({\n period,\n breakdownValue: isDefaultSeries ? null : seriesKey,\n cohortSize,\n retainedUsers,\n retentionRate: rate,\n x: rect.left + rect.width / 2,\n y: rect.top,\n })\n }}\n onMouseLeave={() => setHeatmapTooltip(null)}\n >\n {rate > 0 ? formatPercentage(rate) : '-'}\n </td>\n )\n })}\n </tr>\n )\n })}\n </tbody>\n </table>\n )\n\n // Render heatmap tooltip (shared between heatmap and combined modes)\n const renderHeatmapTooltip = () =>\n heatmapTooltip && (\n <div\n className=\"dc:fixed dc:z-50 dc:px-3 dc:py-2 bg-dc-surface dc:border border-dc-border dc:rounded-sm dc:shadow-lg dc:text-sm dc:pointer-events-none\"\n style={{\n left: heatmapTooltip.x,\n top: heatmapTooltip.y - 10,\n transform: 'translate(-50%, -100%)',\n }}\n >\n <div className=\"dc:font-medium text-dc-text dc:mb-1\">\n {heatmapTooltip.breakdownValue\n ? `${heatmapTooltip.breakdownValue} - ${formatPeriodLabel(heatmapTooltip.period, retentionData?.granularity)}`\n : formatPeriodLabel(heatmapTooltip.period, retentionData?.granularity)}\n </div>\n <div className=\"text-dc-text-secondary dc:space-y-0.5\">\n <div>{t('chart.runtime.retention.cohortSize', { count: heatmapTooltip.cohortSize.toLocaleString() })}</div>\n <div>{t('chart.runtime.retention.retained', { count: heatmapTooltip.retainedUsers.toLocaleString() })}</div>\n <div className=\"dc:font-medium text-dc-text\">\n {t('chart.runtime.retention.rate', { rate: formatPercentage(heatmapTooltip.retentionRate) })}\n </div>\n </div>\n </div>\n )\n\n // Render heatmap mode (table-based only)\n if (displayMode === 'heatmap') {\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:overflow-auto\" style={{ height }}>\n {renderHeatmapTable()}\n {renderHeatmapTooltip()}\n </div>\n )\n }\n\n // Combined mode: line chart on top, heatmap table below\n if (displayMode === 'combined') {\n return (\n <div className=\"dc:flex dc:flex-col dc:w-full dc:h-full\" style={{ height }}>\n {/* Line chart - takes remaining space after heatmap */}\n <div className=\"dc:flex-1 dc:min-h-[200px]\">\n {renderLineChart('100%')}\n </div>\n {/* Heatmap table - auto-height based on content, scrolls if needed */}\n <div className=\"dc:flex-shrink-0 dc:max-h-[40%] dc:overflow-auto dc:border-t border-dc-border\">\n {renderHeatmapTable()}\n </div>\n {/* Shared heatmap tooltip */}\n {renderHeatmapTooltip()}\n </div>\n )\n }\n\n // Line mode: just the line chart\n return renderLineChart(height)\n})\n\nexport default RetentionCombinedChart\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RetentionCombinedChart-Mcm7zHFx.js","names":[],"sources":["../../../src/client/components/charts/RetentionCombinedChart.tsx"],"sourcesContent":["/**\n * RetentionCombinedChart Component\n *\n * Combined visualization for retention analysis data.\n * Supports multiple display modes: heatmap, line chart, or combined view.\n *\n * Features:\n * - X-axis: Period numbers (P0, P1, P2...)\n * - Y-axis: Retention % (0-100%)\n * - Lines: One per breakdown value (or single if no breakdown)\n * - Display modes: 'heatmap' | 'line' | 'combined'\n * - Heatmap shows color-coded retention matrix\n * - Line chart shows retention curves over periods\n */\n\nimport React, { useMemo, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport {\n ComposedChart,\n Line,\n XAxis,\n YAxis,\n CartesianGrid,\n Legend,\n} from 'recharts'\nimport ChartContainer from './ChartContainer.js'\nimport ChartTooltip from './ChartTooltip.js'\nimport { CHART_COLORS, CHART_MARGINS } from '../../utils/chartConstants.js'\nimport type { ChartProps } from '../../types.js'\nimport type { RetentionChartData, RetentionResultRow, RetentionGranularity } from '../../types/retention.js'\nimport { isRetentionData } from '../../types/retention.js'\n\n/**\n * Retention display mode\n * - 'heatmap': Show retention as color-coded bars\n * - 'line': Show retention as line curves\n * - 'combined': Show both heatmap background and line overlay\n */\nexport type RetentionDisplayMode = 'heatmap' | 'line' | 'combined'\n\n/**\n * Get color with opacity based on retention rate for heatmap cells\n * Uses a green gradient: higher retention = more saturated green\n */\nfunction getRetentionColor(rate: number): string {\n const clampedRate = Math.max(0, Math.min(1, rate))\n const alpha = 0.1 + clampedRate * 0.7\n return `rgba(34, 197, 94, ${alpha})`\n}\n\n/**\n * Format percentage for display\n */\nfunction formatPercentage(rate: number): string {\n return `${Math.round(rate * 100)}%`\n}\n\n/**\n * Format period label based on granularity\n * Period 0 shows \"< 1 Day\" / \"< 1 Week\" etc. to indicate the initial cohort\n * e.g., Period 0 with 'week' granularity → \"< 1 Week\", Period 1 → \"Week 1\"\n */\nfunction formatPeriodLabel(period: number, granularity?: RetentionGranularity): string {\n const prefix = granularity === 'day' ? 'Day'\n : granularity === 'week' ? 'Week'\n : granularity === 'month' ? 'Month'\n : 'P' // Fallback to P0, P1, etc.\n\n // Period 0 is special - shows \"< 1 Day\" / \"< 1 Week\" etc.\n if (period === 0) {\n return granularity ? `< 1 ${prefix}` : 'P0'\n }\n\n return granularity ? `${prefix} ${period}` : `P${period}`\n}\n\n/**\n * Get display label for the cohort total column\n * Shows \"Total\" regardless of binding key - it's the cohort size count\n */\nfunction getCohortLabel(_bindingKeyLabel?: string): string {\n return 'Total'\n}\n\n/**\n * Get default series name based on binding key\n * e.g., \"userId\" → \"userId Retention\", null → \"Retention\"\n */\nfunction getDefaultSeriesName(bindingKeyLabel?: string): string {\n if (!bindingKeyLabel) return 'Retention'\n return `${bindingKeyLabel} Retention`\n}\n\n/**\n * Transform retention data for chart display\n * Groups data by period with breakdown values as series\n */\nfunction transformRetentionData(\n rows: RetentionResultRow[],\n periods: number[],\n breakdownValues?: string[],\n granularity?: RetentionGranularity,\n bindingKeyLabel?: string\n): { chartData: any[]; seriesKeys: string[]; defaultSeriesName: string } {\n const defaultSeriesName = getDefaultSeriesName(bindingKeyLabel)\n\n // If no breakdown, single series\n if (!breakdownValues || breakdownValues.length === 0) {\n const chartData = periods.map((period) => {\n const row = rows.find((r) => r.period === period && !r.breakdownValue)\n return {\n period,\n periodLabel: formatPeriodLabel(period, granularity),\n [defaultSeriesName]: row ? row.retentionRate : null,\n cohortSize: row?.cohortSize ?? 0,\n retainedUsers: row?.retainedUsers ?? 0,\n }\n })\n return { chartData, seriesKeys: [defaultSeriesName], defaultSeriesName }\n }\n\n // With breakdown, create series per breakdown value\n const chartData = periods.map((period) => {\n const dataPoint: any = {\n period,\n periodLabel: formatPeriodLabel(period, granularity),\n }\n\n breakdownValues.forEach((bv) => {\n const row = rows.find((r) => r.period === period && r.breakdownValue === bv)\n dataPoint[bv] = row ? row.retentionRate : null\n dataPoint[`${bv}_cohortSize`] = row?.cohortSize ?? 0\n dataPoint[`${bv}_retainedUsers`] = row?.retainedUsers ?? 0\n })\n\n return dataPoint\n })\n\n return { chartData, seriesKeys: breakdownValues, defaultSeriesName }\n}\n\ninterface TooltipData {\n period: number\n breakdownValue?: string | null\n cohortSize: number\n retainedUsers: number\n retentionRate: number\n x: number\n y: number\n}\n\n/**\n * RetentionCombinedChart Component\n */\nconst RetentionCombinedChart = React.memo(function RetentionCombinedChart({\n data,\n height = '100%',\n displayConfig,\n colorPalette,\n}: ChartProps) {\n const { t } = useTranslation()\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n const [heatmapTooltip, setHeatmapTooltip] = useState<TooltipData | null>(null)\n\n // Parse retention data\n const retentionData = useMemo<RetentionChartData | null>(() => {\n if (!data) return null\n\n // Check if data is already in RetentionChartData format\n if (isRetentionData(data)) {\n return data\n }\n\n // If data is an array of RetentionResultRow, convert it\n if (Array.isArray(data) && data.length > 0) {\n const rows = data as RetentionResultRow[]\n const periods = [...new Set(rows.map((r) => r.period))].sort((a, b) => a - b)\n const breakdownValues = [\n ...new Set(rows.filter((r) => r.breakdownValue).map((r) => r.breakdownValue!)),\n ]\n\n return {\n rows,\n periods,\n breakdownValues: breakdownValues.length > 0 ? breakdownValues : undefined,\n }\n }\n\n return null\n }, [data])\n\n // Transform data for chart\n const { chartData, seriesKeys, defaultSeriesName } = useMemo(() => {\n if (!retentionData) {\n return { chartData: [], seriesKeys: [], defaultSeriesName: 'Retention' }\n }\n return transformRetentionData(\n retentionData.rows,\n retentionData.periods,\n retentionData.breakdownValues,\n retentionData.granularity,\n retentionData.bindingKeyLabel\n )\n }, [retentionData])\n\n // Get cohort label for heatmap column header\n const cohortLabel = getCohortLabel(retentionData?.bindingKeyLabel)\n\n // Display mode from config\n const displayMode: RetentionDisplayMode =\n (displayConfig as any)?.retentionDisplayMode || 'line'\n\n const showLegend = displayConfig?.showLegend ?? true\n const showGrid = displayConfig?.showGrid ?? true\n const showTooltip = displayConfig?.showTooltip ?? true\n\n // Handle empty/loading states\n if (!data || (Array.isArray(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\">{t('chart.runtime.noData')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {t('chart.runtime.noDataHint.retention')}\n </div>\n </div>\n </div>\n )\n }\n\n if (!chartData || chartData.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\">{t('chart.runtime.unableToRender')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">{t('chart.runtime.dataFormatIncorrect')}</div>\n </div>\n </div>\n )\n }\n\n // Render line chart component (reused in line and combined modes)\n const renderLineChart = (chartHeight: string | number) => {\n const chartMargins = {\n ...CHART_MARGINS,\n left: 50,\n right: 20,\n }\n\n return (\n <ChartContainer height={chartHeight}>\n <ComposedChart data={chartData} margin={chartMargins} accessibilityLayer={false}>\n {showGrid && <CartesianGrid strokeDasharray=\"3 3\" />}\n <XAxis\n dataKey=\"periodLabel\"\n tick={{ fontSize: 12 }}\n axisLine={{ stroke: 'var(--dc-border)' }}\n tickLine={{ stroke: 'var(--dc-border)' }}\n />\n <YAxis\n domain={[0, 1]}\n tickFormatter={(value) => formatPercentage(value)}\n tick={{ fontSize: 12 }}\n axisLine={{ stroke: 'var(--dc-border)' }}\n tickLine={{ stroke: 'var(--dc-border)' }}\n label={{\n value: 'Retention %',\n angle: -90,\n position: 'insideLeft',\n style: { textAnchor: 'middle', fontSize: '12px', fill: 'var(--dc-text-secondary)' },\n }}\n />\n {showTooltip && (\n <ChartTooltip\n formatter={(value: any, name: string) => {\n if (value === null || value === undefined) {\n return ['No data', name]\n }\n return [formatPercentage(value), name]\n }}\n labelFormatter={(label: string) => label}\n />\n )}\n {showLegend && (\n <Legend\n wrapperStyle={{ fontSize: '12px', paddingTop: '10px' }}\n iconType=\"line\"\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => setHoveredLegend(String(o.dataKey || ''))}\n onMouseLeave={() => setHoveredLegend(null)}\n />\n )}\n\n {/* Render lines */}\n {seriesKeys.map((seriesKey, index) => (\n <Line\n key={seriesKey}\n type=\"monotone\"\n dataKey={seriesKey}\n stroke={\n (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) ||\n CHART_COLORS[index % CHART_COLORS.length]\n }\n strokeWidth={2}\n dot={{ r: 4, strokeWidth: 2 }}\n activeDot={{ r: 6 }}\n strokeOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 1 : 0.3) : 1}\n connectNulls={false}\n />\n ))}\n </ComposedChart>\n </ChartContainer>\n )\n }\n\n // Render heatmap table component (reused in heatmap and combined modes)\n const renderHeatmapTable = () => (\n <table className=\"dc:w-full dc:border-collapse dc:text-sm\">\n <thead className=\"dc:sticky dc:top-0 bg-dc-bg dc:z-10\">\n <tr>\n <th className=\"dc:text-left dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[100px] dc:whitespace-nowrap\">\n {retentionData?.breakdownValues?.length ? 'Segment' : 'Cohort'}\n </th>\n <th className=\"dc:text-right dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[60px] dc:whitespace-nowrap\">\n {cohortLabel}\n </th>\n {retentionData?.periods.map((period) => (\n <th\n key={period}\n className=\"dc:text-center dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[70px] dc:whitespace-nowrap\"\n >\n {formatPeriodLabel(period, retentionData?.granularity)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {seriesKeys.map((seriesKey, rowIndex) => {\n const period0Data = chartData.find((d) => d.period === 0)\n const isDefaultSeries = seriesKey === defaultSeriesName\n const cohortSize = isDefaultSeries\n ? period0Data?.cohortSize ?? 0\n : period0Data?.[`${seriesKey}_cohortSize`] ?? 0\n\n return (\n <tr\n key={seriesKey}\n className={rowIndex % 2 === 0 ? 'bg-dc-bg' : 'bg-dc-surface-secondary'}\n >\n <td className=\"dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:whitespace-nowrap\">\n {seriesKey}\n </td>\n <td className=\"dc:p-2 dc:text-right text-dc-text-secondary dc:border-b border-dc-border\">\n {cohortSize.toLocaleString()}\n </td>\n {retentionData?.periods.map((period) => {\n const dataPoint = chartData.find((d) => d.period === period)\n const rate = dataPoint?.[seriesKey] ?? 0\n const bgColor = rate > 0 ? getRetentionColor(rate) : 'transparent'\n const textColor = rate > 0.5 ? '#ffffff' : 'var(--dc-text)'\n\n return (\n <td\n key={period}\n className=\"dc:p-2 dc:text-center dc:border-b border-dc-border dc:cursor-default dc:transition-opacity dc:hover:opacity-80\"\n style={{ backgroundColor: bgColor, color: textColor }}\n onMouseEnter={(e) => {\n const rect = e.currentTarget.getBoundingClientRect()\n const retainedUsers = isDefaultSeries\n ? dataPoint?.retainedUsers ?? 0\n : dataPoint?.[`${seriesKey}_retainedUsers`] ?? 0\n setHeatmapTooltip({\n period,\n breakdownValue: isDefaultSeries ? null : seriesKey,\n cohortSize,\n retainedUsers,\n retentionRate: rate,\n x: rect.left + rect.width / 2,\n y: rect.top,\n })\n }}\n onMouseLeave={() => setHeatmapTooltip(null)}\n >\n {rate > 0 ? formatPercentage(rate) : '-'}\n </td>\n )\n })}\n </tr>\n )\n })}\n </tbody>\n </table>\n )\n\n // Render heatmap tooltip (shared between heatmap and combined modes)\n const renderHeatmapTooltip = () =>\n heatmapTooltip && (\n <div\n className=\"dc:fixed dc:z-50 dc:px-3 dc:py-2 bg-dc-surface dc:border border-dc-border dc:rounded-sm dc:shadow-lg dc:text-sm dc:pointer-events-none\"\n style={{\n left: heatmapTooltip.x,\n top: heatmapTooltip.y - 10,\n transform: 'translate(-50%, -100%)',\n }}\n >\n <div className=\"dc:font-medium text-dc-text dc:mb-1\">\n {heatmapTooltip.breakdownValue\n ? `${heatmapTooltip.breakdownValue} - ${formatPeriodLabel(heatmapTooltip.period, retentionData?.granularity)}`\n : formatPeriodLabel(heatmapTooltip.period, retentionData?.granularity)}\n </div>\n <div className=\"text-dc-text-secondary dc:space-y-0.5\">\n <div>{t('chart.runtime.retention.cohortSize', { count: heatmapTooltip.cohortSize.toLocaleString() })}</div>\n <div>{t('chart.runtime.retention.retained', { count: heatmapTooltip.retainedUsers.toLocaleString() })}</div>\n <div className=\"dc:font-medium text-dc-text\">\n {t('chart.runtime.retention.rate', { rate: formatPercentage(heatmapTooltip.retentionRate) })}\n </div>\n </div>\n </div>\n )\n\n // Render heatmap mode (table-based only)\n if (displayMode === 'heatmap') {\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:overflow-auto\" style={{ height }}>\n {renderHeatmapTable()}\n {renderHeatmapTooltip()}\n </div>\n )\n }\n\n // Combined mode: line chart on top, heatmap table below\n if (displayMode === 'combined') {\n return (\n <div className=\"dc:flex dc:flex-col dc:w-full dc:h-full\" style={{ height }}>\n {/* Line chart - takes remaining space after heatmap */}\n <div className=\"dc:flex-1 dc:min-h-[200px]\">\n {renderLineChart('100%')}\n </div>\n {/* Heatmap table - auto-height based on content, scrolls if needed */}\n <div className=\"dc:flex-shrink-0 dc:max-h-[40%] dc:overflow-auto dc:border-t border-dc-border\">\n {renderHeatmapTable()}\n </div>\n {/* Shared heatmap tooltip */}\n {renderHeatmapTooltip()}\n </div>\n )\n }\n\n // Line mode: just the line chart\n return renderLineChart(height)\n})\n\nexport default RetentionCombinedChart\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as e } from "./retention-ChW9jYdy.js";
|
|
2
|
-
import { P as t } from "./chart-activity-grid-
|
|
2
|
+
import { P as t } from "./chart-activity-grid-DVOhLgxv.js";
|
|
3
3
|
import n, { useMemo as r, useState as i } from "react";
|
|
4
4
|
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
5
5
|
//#region src/client/components/charts/RetentionHeatmap.tsx
|
|
@@ -190,4 +190,4 @@ var d = n.memo(function({ data: n, height: d = "100%", displayConfig: f }) {
|
|
|
190
190
|
//#endregion
|
|
191
191
|
export { d as default };
|
|
192
192
|
|
|
193
|
-
//# sourceMappingURL=RetentionHeatmap-
|
|
193
|
+
//# sourceMappingURL=RetentionHeatmap-4VJx5KDM.js.map
|
package/dist/client/chunks/{RetentionHeatmap-DkJOiNwz.js.map → RetentionHeatmap-4VJx5KDM.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RetentionHeatmap-DkJOiNwz.js","names":[],"sources":["../../../src/client/components/charts/RetentionHeatmap.tsx"],"sourcesContent":["/**\n * RetentionHeatmap Component\n *\n * Visualizes retention analysis data as a cohort × period matrix.\n * Displays retention rates with color intensity based on percentage.\n *\n * Features:\n * - Cohort labels in first column (e.g., \"2024-01\", \"2024-02\")\n * - Cohort size in second column\n * - Period columns (P0, P1, P2, ... PN)\n * - Cell background color intensity based on retention rate\n * - Hover tooltip with detailed stats\n */\n\nimport React, { useMemo, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport type { ChartProps } from '../../types.js'\nimport type { RetentionChartData, RetentionResultRow } from '../../types/retention.js'\nimport { isRetentionData } from '../../types/retention.js'\n\n/**\n * Get color with opacity based on retention rate\n * Uses a green gradient: higher retention = more saturated green\n */\nfunction getRetentionColor(rate: number): string {\n // Clamp rate between 0 and 1\n const clampedRate = Math.max(0, Math.min(1, rate))\n\n // Use CSS variable for theming support\n // Fallback to a green color if CSS var not available\n const alpha = 0.1 + clampedRate * 0.7 // Range from 0.1 to 0.8 opacity\n\n // Green color (success color)\n return `rgba(34, 197, 94, ${alpha})`\n}\n\n/**\n * Get text color that contrasts with background\n */\nfunction getTextColor(rate: number): string {\n // Use dark text for lower rates, light text for higher rates\n return rate > 0.5 ? '#ffffff' : 'var(--dc-text)'\n}\n\n/**\n * Format cohort period for display\n * Converts date strings to readable format\n */\nfunction formatCohortPeriod(cohort: string): string {\n // If it's already in YYYY-MM format, return as-is\n if (/^\\d{4}-\\d{2}$/.test(cohort)) {\n return cohort\n }\n\n // Try to parse as date\n const date = new Date(cohort)\n if (!isNaN(date.getTime())) {\n return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' })\n }\n\n return cohort\n}\n\n/**\n * Format percentage for display\n */\nfunction formatPercentage(rate: number): string {\n return `${Math.round(rate * 100)}%`\n}\n\ninterface TooltipData {\n cohort: string\n period: number\n cohortSize: number\n retainedUsers: number\n retentionRate: number\n x: number\n y: number\n}\n\n/**\n * RetentionHeatmap Component\n */\nconst RetentionHeatmap = React.memo(function RetentionHeatmap({\n data,\n height = '100%',\n displayConfig,\n}: ChartProps) {\n const { t } = useTranslation()\n const [tooltip, setTooltip] = useState<TooltipData | null>(null)\n\n // Parse retention data\n const retentionData = useMemo<RetentionChartData | null>(() => {\n if (!data) return null\n\n // Check if data is already in RetentionChartData format\n if (isRetentionData(data)) {\n return data\n }\n\n // If data is an array of RetentionResultRow, convert it\n if (Array.isArray(data) && data.length > 0) {\n const rows = data as RetentionResultRow[]\n const breakdownValues = [...new Set(rows.map(r => r.breakdownValue || 'All Users'))].sort()\n const periods = [...new Set(rows.map(r => r.period))].sort((a, b) => a - b)\n\n return {\n rows,\n breakdownValues: breakdownValues.length > 1 || breakdownValues[0] !== 'All Users' ? breakdownValues : undefined,\n periods,\n }\n }\n\n return null\n }, [data])\n\n // Build matrix for display\n // In the new simplified format, rows are grouped by breakdownValue (or 'All Users' if no breakdown)\n const matrix = useMemo(() => {\n if (!retentionData) return null\n\n const { rows, breakdownValues, periods } = retentionData\n\n // Determine segments: use breakdownValues if available, otherwise single 'All Users' segment\n const segments = breakdownValues || ['All Users']\n\n // Create a lookup map for quick access: segment:period -> row\n const lookup = new Map<string, RetentionResultRow>()\n for (const row of rows) {\n const segment = row.breakdownValue || 'All Users'\n lookup.set(`${segment}:${row.period}`, row)\n }\n\n // Build matrix structure - one row per segment\n return segments.map((segment: string) => {\n const segmentRows = periods.map(period => {\n const row = lookup.get(`${segment}:${period}`)\n return row || null\n })\n\n // Get cohort size from period 0\n const period0 = segmentRows[0]\n const cohortSize = period0?.cohortSize ?? 0\n\n return {\n cohort: segment, // Keep 'cohort' key for compatibility with rendering\n cohortSize,\n periods: segmentRows,\n }\n })\n }, [retentionData])\n\n // Handle mouse enter on cell\n const handleMouseEnter = (\n event: React.MouseEvent,\n cohort: string,\n period: number,\n row: RetentionResultRow | null\n ) => {\n if (!row) return\n\n const rect = event.currentTarget.getBoundingClientRect()\n setTooltip({\n cohort,\n period,\n cohortSize: row.cohortSize,\n retainedUsers: row.retainedUsers,\n retentionRate: row.retentionRate,\n x: rect.left + rect.width / 2,\n y: rect.top,\n })\n }\n\n const handleMouseLeave = () => {\n setTooltip(null)\n }\n\n // Handle empty/loading states\n if (!data || (Array.isArray(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\">{t('chart.runtime.noData')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {t('chart.runtime.noDataHint.retention')}\n </div>\n </div>\n </div>\n )\n }\n\n if (!matrix || matrix.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\">{t('chart.runtime.unableToRender')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {t('chart.runtime.dataFormatIncorrect')}\n </div>\n </div>\n </div>\n )\n }\n\n const periods = retentionData?.periods ?? []\n const showLegend = displayConfig?.showLegend ?? true\n\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:overflow-auto\" style={{ height }}>\n {/* Retention Matrix Table */}\n <table className=\"dc:w-full dc:border-collapse dc:text-sm\">\n <thead className=\"dc:sticky dc:top-0 bg-dc-bg dc:z-10\">\n <tr>\n <th className=\"dc:text-left dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[100px]\">\n {t('chart.runtime.retention.cohort')}\n </th>\n <th className=\"dc:text-right dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[80px]\">\n {t('chart.runtime.retention.users')}\n </th>\n {periods.map(period => (\n <th\n key={period}\n className=\"dc:text-center dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[60px]\"\n >\n P{period}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {matrix.map((row, rowIndex) => (\n <tr key={row.cohort} className={rowIndex % 2 === 0 ? 'bg-dc-bg' : 'bg-dc-surface-secondary'}>\n <td className=\"dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:whitespace-nowrap\">\n {formatCohortPeriod(row.cohort)}\n </td>\n <td className=\"dc:p-2 dc:text-right text-dc-text-secondary dc:border-b border-dc-border\">\n {row.cohortSize.toLocaleString()}\n </td>\n {row.periods.map((cell, periodIndex) => {\n const period = periods[periodIndex]\n const rate = cell?.retentionRate ?? 0\n const bgColor = cell ? getRetentionColor(rate) : 'transparent'\n const textColor = cell ? getTextColor(rate) : 'var(--dc-text-muted)'\n\n return (\n <td\n key={period}\n className=\"dc:p-2 dc:text-center dc:border-b border-dc-border dc:cursor-default dc:transition-opacity dc:hover:opacity-80\"\n style={{ backgroundColor: bgColor, color: textColor }}\n onMouseEnter={(e) => handleMouseEnter(e, row.cohort, period, cell)}\n onMouseLeave={handleMouseLeave}\n >\n {cell ? formatPercentage(rate) : '-'}\n </td>\n )\n })}\n </tr>\n ))}\n </tbody>\n </table>\n\n {/* Legend */}\n {showLegend && (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:mt-4 dc:gap-2 dc:text-xs text-dc-text-secondary\">\n <span>0%</span>\n <div className=\"dc:flex dc:h-4\">\n {[0, 0.2, 0.4, 0.6, 0.8, 1].map(rate => (\n <div\n key={rate}\n className=\"dc:w-6 dc:h-4\"\n style={{ backgroundColor: getRetentionColor(rate) }}\n />\n ))}\n </div>\n <span>100%</span>\n </div>\n )}\n\n {/* Tooltip */}\n {tooltip && (\n <div\n className=\"dc:fixed dc:z-50 dc:px-3 dc:py-2 bg-dc-surface dc:border border-dc-border dc:rounded-sm dc:shadow-lg dc:text-sm dc:pointer-events-none\"\n style={{\n left: tooltip.x,\n top: tooltip.y - 10,\n transform: 'translate(-50%, -100%)',\n }}\n >\n <div className=\"dc:font-medium text-dc-text dc:mb-1\">\n {t('chart.runtime.retention.periodLabel', {\n cohort: formatCohortPeriod(tooltip.cohort),\n period: tooltip.period\n })}\n </div>\n <div className=\"text-dc-text-secondary dc:space-y-0.5\">\n <div>{t('chart.runtime.retention.cohortSize', { count: tooltip.cohortSize.toLocaleString() })}</div>\n <div>{t('chart.runtime.retention.retained', { count: tooltip.retainedUsers.toLocaleString() })}</div>\n <div className=\"dc:font-medium text-dc-text\">\n {t('chart.runtime.retention.rate', { rate: formatPercentage(tooltip.retentionRate) })}\n </div>\n </div>\n </div>\n )}\n </div>\n )\n})\n\nexport default RetentionHeatmap\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RetentionHeatmap-4VJx5KDM.js","names":[],"sources":["../../../src/client/components/charts/RetentionHeatmap.tsx"],"sourcesContent":["/**\n * RetentionHeatmap Component\n *\n * Visualizes retention analysis data as a cohort × period matrix.\n * Displays retention rates with color intensity based on percentage.\n *\n * Features:\n * - Cohort labels in first column (e.g., \"2024-01\", \"2024-02\")\n * - Cohort size in second column\n * - Period columns (P0, P1, P2, ... PN)\n * - Cell background color intensity based on retention rate\n * - Hover tooltip with detailed stats\n */\n\nimport React, { useMemo, useState } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport type { ChartProps } from '../../types.js'\nimport type { RetentionChartData, RetentionResultRow } from '../../types/retention.js'\nimport { isRetentionData } from '../../types/retention.js'\n\n/**\n * Get color with opacity based on retention rate\n * Uses a green gradient: higher retention = more saturated green\n */\nfunction getRetentionColor(rate: number): string {\n // Clamp rate between 0 and 1\n const clampedRate = Math.max(0, Math.min(1, rate))\n\n // Use CSS variable for theming support\n // Fallback to a green color if CSS var not available\n const alpha = 0.1 + clampedRate * 0.7 // Range from 0.1 to 0.8 opacity\n\n // Green color (success color)\n return `rgba(34, 197, 94, ${alpha})`\n}\n\n/**\n * Get text color that contrasts with background\n */\nfunction getTextColor(rate: number): string {\n // Use dark text for lower rates, light text for higher rates\n return rate > 0.5 ? '#ffffff' : 'var(--dc-text)'\n}\n\n/**\n * Format cohort period for display\n * Converts date strings to readable format\n */\nfunction formatCohortPeriod(cohort: string): string {\n // If it's already in YYYY-MM format, return as-is\n if (/^\\d{4}-\\d{2}$/.test(cohort)) {\n return cohort\n }\n\n // Try to parse as date\n const date = new Date(cohort)\n if (!isNaN(date.getTime())) {\n return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' })\n }\n\n return cohort\n}\n\n/**\n * Format percentage for display\n */\nfunction formatPercentage(rate: number): string {\n return `${Math.round(rate * 100)}%`\n}\n\ninterface TooltipData {\n cohort: string\n period: number\n cohortSize: number\n retainedUsers: number\n retentionRate: number\n x: number\n y: number\n}\n\n/**\n * RetentionHeatmap Component\n */\nconst RetentionHeatmap = React.memo(function RetentionHeatmap({\n data,\n height = '100%',\n displayConfig,\n}: ChartProps) {\n const { t } = useTranslation()\n const [tooltip, setTooltip] = useState<TooltipData | null>(null)\n\n // Parse retention data\n const retentionData = useMemo<RetentionChartData | null>(() => {\n if (!data) return null\n\n // Check if data is already in RetentionChartData format\n if (isRetentionData(data)) {\n return data\n }\n\n // If data is an array of RetentionResultRow, convert it\n if (Array.isArray(data) && data.length > 0) {\n const rows = data as RetentionResultRow[]\n const breakdownValues = [...new Set(rows.map(r => r.breakdownValue || 'All Users'))].sort()\n const periods = [...new Set(rows.map(r => r.period))].sort((a, b) => a - b)\n\n return {\n rows,\n breakdownValues: breakdownValues.length > 1 || breakdownValues[0] !== 'All Users' ? breakdownValues : undefined,\n periods,\n }\n }\n\n return null\n }, [data])\n\n // Build matrix for display\n // In the new simplified format, rows are grouped by breakdownValue (or 'All Users' if no breakdown)\n const matrix = useMemo(() => {\n if (!retentionData) return null\n\n const { rows, breakdownValues, periods } = retentionData\n\n // Determine segments: use breakdownValues if available, otherwise single 'All Users' segment\n const segments = breakdownValues || ['All Users']\n\n // Create a lookup map for quick access: segment:period -> row\n const lookup = new Map<string, RetentionResultRow>()\n for (const row of rows) {\n const segment = row.breakdownValue || 'All Users'\n lookup.set(`${segment}:${row.period}`, row)\n }\n\n // Build matrix structure - one row per segment\n return segments.map((segment: string) => {\n const segmentRows = periods.map(period => {\n const row = lookup.get(`${segment}:${period}`)\n return row || null\n })\n\n // Get cohort size from period 0\n const period0 = segmentRows[0]\n const cohortSize = period0?.cohortSize ?? 0\n\n return {\n cohort: segment, // Keep 'cohort' key for compatibility with rendering\n cohortSize,\n periods: segmentRows,\n }\n })\n }, [retentionData])\n\n // Handle mouse enter on cell\n const handleMouseEnter = (\n event: React.MouseEvent,\n cohort: string,\n period: number,\n row: RetentionResultRow | null\n ) => {\n if (!row) return\n\n const rect = event.currentTarget.getBoundingClientRect()\n setTooltip({\n cohort,\n period,\n cohortSize: row.cohortSize,\n retainedUsers: row.retainedUsers,\n retentionRate: row.retentionRate,\n x: rect.left + rect.width / 2,\n y: rect.top,\n })\n }\n\n const handleMouseLeave = () => {\n setTooltip(null)\n }\n\n // Handle empty/loading states\n if (!data || (Array.isArray(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\">{t('chart.runtime.noData')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {t('chart.runtime.noDataHint.retention')}\n </div>\n </div>\n </div>\n )\n }\n\n if (!matrix || matrix.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\">{t('chart.runtime.unableToRender')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {t('chart.runtime.dataFormatIncorrect')}\n </div>\n </div>\n </div>\n )\n }\n\n const periods = retentionData?.periods ?? []\n const showLegend = displayConfig?.showLegend ?? true\n\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:overflow-auto\" style={{ height }}>\n {/* Retention Matrix Table */}\n <table className=\"dc:w-full dc:border-collapse dc:text-sm\">\n <thead className=\"dc:sticky dc:top-0 bg-dc-bg dc:z-10\">\n <tr>\n <th className=\"dc:text-left dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[100px]\">\n {t('chart.runtime.retention.cohort')}\n </th>\n <th className=\"dc:text-right dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[80px]\">\n {t('chart.runtime.retention.users')}\n </th>\n {periods.map(period => (\n <th\n key={period}\n className=\"dc:text-center dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:min-w-[60px]\"\n >\n P{period}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {matrix.map((row, rowIndex) => (\n <tr key={row.cohort} className={rowIndex % 2 === 0 ? 'bg-dc-bg' : 'bg-dc-surface-secondary'}>\n <td className=\"dc:p-2 dc:font-medium text-dc-text dc:border-b border-dc-border dc:whitespace-nowrap\">\n {formatCohortPeriod(row.cohort)}\n </td>\n <td className=\"dc:p-2 dc:text-right text-dc-text-secondary dc:border-b border-dc-border\">\n {row.cohortSize.toLocaleString()}\n </td>\n {row.periods.map((cell, periodIndex) => {\n const period = periods[periodIndex]\n const rate = cell?.retentionRate ?? 0\n const bgColor = cell ? getRetentionColor(rate) : 'transparent'\n const textColor = cell ? getTextColor(rate) : 'var(--dc-text-muted)'\n\n return (\n <td\n key={period}\n className=\"dc:p-2 dc:text-center dc:border-b border-dc-border dc:cursor-default dc:transition-opacity dc:hover:opacity-80\"\n style={{ backgroundColor: bgColor, color: textColor }}\n onMouseEnter={(e) => handleMouseEnter(e, row.cohort, period, cell)}\n onMouseLeave={handleMouseLeave}\n >\n {cell ? formatPercentage(rate) : '-'}\n </td>\n )\n })}\n </tr>\n ))}\n </tbody>\n </table>\n\n {/* Legend */}\n {showLegend && (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:mt-4 dc:gap-2 dc:text-xs text-dc-text-secondary\">\n <span>0%</span>\n <div className=\"dc:flex dc:h-4\">\n {[0, 0.2, 0.4, 0.6, 0.8, 1].map(rate => (\n <div\n key={rate}\n className=\"dc:w-6 dc:h-4\"\n style={{ backgroundColor: getRetentionColor(rate) }}\n />\n ))}\n </div>\n <span>100%</span>\n </div>\n )}\n\n {/* Tooltip */}\n {tooltip && (\n <div\n className=\"dc:fixed dc:z-50 dc:px-3 dc:py-2 bg-dc-surface dc:border border-dc-border dc:rounded-sm dc:shadow-lg dc:text-sm dc:pointer-events-none\"\n style={{\n left: tooltip.x,\n top: tooltip.y - 10,\n transform: 'translate(-50%, -100%)',\n }}\n >\n <div className=\"dc:font-medium text-dc-text dc:mb-1\">\n {t('chart.runtime.retention.periodLabel', {\n cohort: formatCohortPeriod(tooltip.cohort),\n period: tooltip.period\n })}\n </div>\n <div className=\"text-dc-text-secondary dc:space-y-0.5\">\n <div>{t('chart.runtime.retention.cohortSize', { count: tooltip.cohortSize.toLocaleString() })}</div>\n <div>{t('chart.runtime.retention.retained', { count: tooltip.retainedUsers.toLocaleString() })}</div>\n <div className=\"dc:font-medium text-dc-text\">\n {t('chart.runtime.retention.rate', { rate: formatPercentage(tooltip.retentionRate) })}\n </div>\n </div>\n </div>\n )}\n </div>\n )\n})\n\nexport default RetentionHeatmap\n"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { S as e, g as t, k as n, l as r, mt as i, u as a, v as o } from "./chart-data-table-
|
|
2
|
-
import { T as s, j as c, o as l, s as u, v as d, x as f } from "./DashboardEditModal-
|
|
1
|
+
import { S as e, g as t, k as n, l as r, mt as i, u as a, v as o } from "./chart-data-table-SNSqZj9B.js";
|
|
2
|
+
import { T as s, j as c, o as l, s as u, v as d, x as f } from "./DashboardEditModal-ByXLlXZD.js";
|
|
3
3
|
import { i as p, n as m, o as h, r as g, s as _ } from "./retention-ChW9jYdy.js";
|
|
4
|
-
import { P as v, z as y } from "./chart-activity-grid-
|
|
5
|
-
import { r as b, t as x } from "./useExplainAI-
|
|
6
|
-
import { a as S, t as C } from "./charts-loader-
|
|
7
|
-
import { F as w, M as T, N as E, S as D, c as O, n as k, p as A, t as j, u as M } from "./analysis-builder-shared-
|
|
8
|
-
import { n as N } from "./charts-core-
|
|
4
|
+
import { P as v, z as y } from "./chart-activity-grid-DVOhLgxv.js";
|
|
5
|
+
import { r as b, t as x } from "./useExplainAI-DJbc7Lu4.js";
|
|
6
|
+
import { a as S, t as C } from "./charts-loader-0avVzeDd.js";
|
|
7
|
+
import { F as w, M as T, N as E, S as D, c as O, n as k, p as A, t as j, u as M } from "./analysis-builder-shared-l-4WKVS1.js";
|
|
8
|
+
import { n as N } from "./charts-core-BxBB2zBE.js";
|
|
9
9
|
import P, { Component as F, forwardRef as I, memo as L, useCallback as R, useEffect as z, useImperativeHandle as ee, useMemo as B, useRef as V, useState as H, useSyncExternalStore as te } from "react";
|
|
10
10
|
import { Fragment as U, jsx as W, jsxs as G } from "react/jsx-runtime";
|
|
11
11
|
//#region src/client/shared/components/QueryAnalysisPanel.sections.tsx
|
|
@@ -671,7 +671,7 @@ function We(e, t, n) {
|
|
|
671
671
|
}
|
|
672
672
|
//#endregion
|
|
673
673
|
//#region src/client/components/AnalysisBuilder/AnalysisResultsPanel.tsx
|
|
674
|
-
var Ge = P.lazy(() => import("./schema-visualization-
|
|
674
|
+
var Ge = P.lazy(() => import("./schema-visualization-CGbnbzj_.js").then((e) => e.n).then((e) => ({ default: e.SchemaVisualizationLazy }))), Ke = L(function({ executionStatus: e, executionResults: t, executionError: r, totalRowCount: i, resultsStale: a = !1, chartType: s = "line", chartConfig: u = {}, displayConfig: d = {}, colorPalette: f, currentPaletteName: p, onColorPaletteChange: m, allQueries: h, funnelExecutedQueries: g, activeView: _ = "chart", onActiveViewChange: y, displayLimit: w = 100, onDisplayLimitChange: T, chartAvailability: D, debugDataPerQuery: O = [], onShareClick: k, canShare: A = !1, shareButtonState: j = "idle", onRefreshClick: M, canRefresh: N = !1, isRefreshing: F = !1, needsRefresh: I = !1, onClearClick: L, canClear: ee = !1, enableAI: te = !1, isAIOpen: ne = !1, onAIToggle: re, queryCount: ie = 1, perQueryResults: ae, activeTableIndex: oe = 0, onActiveTableChange: se, analysisType: K, isFunnelMode: ce = !1, funnelServerQuery: le, funnelDebugData: ue, flowServerQuery: de, flowDebugData: fe, retentionServerQuery: pe, retentionDebugData: me, retentionChartData: q, retentionValidation: ge, warnings: _e, highlightedFields: ve, onSchemaFieldClick: ye }) {
|
|
675
675
|
let { t: J } = v(), Y = K === "funnel" || ce, X = K === "flow", Z = K === "retention", { features: be } = n(), [xe, Se] = H(!1), [Ce, we] = H(!1), [Q, Ee] = H(0), [De, Oe] = H(!1), [ke, Ae] = H(!1), [je, Me] = H(!1), [Ne, Pe] = H("idle");
|
|
676
676
|
z(() => {
|
|
677
677
|
let e = (e) => {
|
|
@@ -5410,4 +5410,4 @@ vr.displayName = "AnalysisBuilder";
|
|
|
5410
5410
|
//#endregion
|
|
5411
5411
|
export { vr as default };
|
|
5412
5412
|
|
|
5413
|
-
//# sourceMappingURL=analysis-builder-
|
|
5413
|
+
//# sourceMappingURL=analysis-builder-CWTJQQtL.js.map
|