drizzle-cube 0.3.27 → 0.3.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/adapters/utils.cjs +1 -1
  2. package/dist/adapters/utils.js +5 -5
  3. package/dist/client/charts.js +12 -12
  4. package/dist/client/chunks/{DashboardEditModal-CVMSvpJ-.js → DashboardEditModal-t5XeTZom.js} +45 -45
  5. package/dist/client/chunks/DashboardEditModal-t5XeTZom.js.map +1 -0
  6. package/dist/client/chunks/{RetentionCombinedChart-BVKWmxc-.js → RetentionCombinedChart-SsBGHJWi.js} +9 -9
  7. package/dist/client/chunks/RetentionCombinedChart-SsBGHJWi.js.map +1 -0
  8. package/dist/client/chunks/{RetentionHeatmap-BiqfhGYk.js → RetentionHeatmap-BHYU8MXY.js} +8 -8
  9. package/dist/client/chunks/RetentionHeatmap-BHYU8MXY.js.map +1 -0
  10. package/dist/client/chunks/{analysis-builder-CuTR61Ct.js → analysis-builder-CKVYG9jJ.js} +67 -67
  11. package/dist/client/chunks/analysis-builder-CKVYG9jJ.js.map +1 -0
  12. package/dist/client/chunks/{analysis-builder-shared-D7iqklYk.js → analysis-builder-shared-CF8Vx1oD.js} +8 -8
  13. package/dist/client/chunks/analysis-builder-shared-CF8Vx1oD.js.map +1 -0
  14. package/dist/client/chunks/{chart-activity-grid-BcMRkaq4.js → chart-activity-grid-Bpu_-8uT.js} +5 -5
  15. package/dist/client/chunks/{chart-activity-grid-BcMRkaq4.js.map → chart-activity-grid-Bpu_-8uT.js.map} +1 -1
  16. package/dist/client/chunks/{chart-area-KlBSb_ur.js → chart-area-DcvLkvGb.js} +8 -8
  17. package/dist/client/chunks/chart-area-DcvLkvGb.js.map +1 -0
  18. package/dist/client/chunks/{chart-bar-D1i2jmIg.js → chart-bar-CNfm5iMY.js} +7 -7
  19. package/dist/client/chunks/chart-bar-CNfm5iMY.js.map +1 -0
  20. package/dist/client/chunks/{chart-bubble-BFjnOwNZ.js → chart-bubble-KA-RYWR8.js} +4 -4
  21. package/dist/client/chunks/{chart-bubble-BFjnOwNZ.js.map → chart-bubble-KA-RYWR8.js.map} +1 -1
  22. package/dist/client/chunks/{chart-data-table-CYMMAHau.js → chart-data-table-Cq14arji.js} +9 -9
  23. package/dist/client/chunks/chart-data-table-Cq14arji.js.map +1 -0
  24. package/dist/client/chunks/{chart-funnel-BH7r4HWZ.js → chart-funnel-DFKWS49U.js} +7 -7
  25. package/dist/client/chunks/chart-funnel-DFKWS49U.js.map +1 -0
  26. package/dist/client/chunks/{chart-heat-map-DyGtODLE.js → chart-heat-map-D2g0dbKz.js} +5 -5
  27. package/dist/client/chunks/chart-heat-map-D2g0dbKz.js.map +1 -0
  28. package/dist/client/chunks/{chart-kpi-delta-Bk396suk.js → chart-kpi-delta-Cc-jiBd0.js} +30 -30
  29. package/dist/client/chunks/chart-kpi-delta-Cc-jiBd0.js.map +1 -0
  30. package/dist/client/chunks/{chart-kpi-number-CIGqZ5Dw.js → chart-kpi-number-BXolYaZA.js} +8 -8
  31. package/dist/client/chunks/chart-kpi-number-BXolYaZA.js.map +1 -0
  32. package/dist/client/chunks/{chart-kpi-text-iwlaSQCi.js → chart-kpi-text-PA8oyypA.js} +7 -7
  33. package/dist/client/chunks/chart-kpi-text-PA8oyypA.js.map +1 -0
  34. package/dist/client/chunks/{chart-line-BKqhoW9A.js → chart-line-BOrtUdOD.js} +8 -8
  35. package/dist/client/chunks/chart-line-BOrtUdOD.js.map +1 -0
  36. package/dist/client/chunks/{chart-markdown-CiPhRY9s.js → chart-markdown-Cget3iEq.js} +6 -6
  37. package/dist/client/chunks/chart-markdown-Cget3iEq.js.map +1 -0
  38. package/dist/client/chunks/{chart-pie-BNr-GgTh.js → chart-pie-B-Sbsvd2.js} +7 -7
  39. package/dist/client/chunks/chart-pie-B-Sbsvd2.js.map +1 -0
  40. package/dist/client/chunks/{chart-radar-CYrGVYEQ.js → chart-radar-CezgvXhm.js} +6 -6
  41. package/dist/client/chunks/chart-radar-CezgvXhm.js.map +1 -0
  42. package/dist/client/chunks/{chart-radial-bar-2PR3ucIR.js → chart-radial-bar-COBPiBxm.js} +6 -6
  43. package/dist/client/chunks/chart-radial-bar-COBPiBxm.js.map +1 -0
  44. package/dist/client/chunks/{chart-sankey-C7w7h2ZV.js → chart-sankey-O4WQBRDk.js} +27 -27
  45. package/dist/client/chunks/{chart-sankey-C7w7h2ZV.js.map → chart-sankey-O4WQBRDk.js.map} +1 -1
  46. package/dist/client/chunks/{chart-scatter-D3Z9bl8H.js → chart-scatter-BVF0n9DR.js} +22 -22
  47. package/dist/client/chunks/chart-scatter-BVF0n9DR.js.map +1 -0
  48. package/dist/client/chunks/{chart-sunburst-C4ydFeaK.js → chart-sunburst-DZzVj_6S.js} +4 -4
  49. package/dist/client/chunks/{chart-sunburst-C4ydFeaK.js.map → chart-sunburst-DZzVj_6S.js.map} +1 -1
  50. package/dist/client/chunks/{chart-tree-map-DCCmMyz1.js → chart-tree-map-CeEtNuo6.js} +6 -6
  51. package/dist/client/chunks/chart-tree-map-CeEtNuo6.js.map +1 -0
  52. package/dist/client/chunks/{charts-core-DIW3Dd7n.js → charts-core-DF99lItO.js} +4 -4
  53. package/dist/client/chunks/charts-core-DF99lItO.js.map +1 -0
  54. package/dist/client/chunks/{charts-loader-BCBnMYjH.js → charts-loader-AkRviD9H.js} +22 -22
  55. package/dist/client/chunks/{charts-loader-BCBnMYjH.js.map → charts-loader-AkRviD9H.js.map} +1 -1
  56. package/dist/client/chunks/{core-BITzuqYm.js → core-Bso4ultM.js} +2 -2
  57. package/dist/client/chunks/{core-BITzuqYm.js.map → core-Bso4ultM.js.map} +1 -1
  58. package/dist/client/chunks/{hooks-Dd_nnv0J.js → hooks-B69r8rwU.js} +4 -4
  59. package/dist/client/chunks/{hooks-Dd_nnv0J.js.map → hooks-B69r8rwU.js.map} +1 -1
  60. package/dist/client/chunks/{providers-BjxD1ZmC.js → providers-CxlSRYvE.js} +2 -2
  61. package/dist/client/chunks/{providers-BjxD1ZmC.js.map → providers-CxlSRYvE.js.map} +1 -1
  62. package/dist/client/chunks/{syntaxHighlighting-Cmqp7_Mx.js → syntaxHighlighting-yTdSle2t.js} +2 -2
  63. package/dist/client/chunks/{syntaxHighlighting-Cmqp7_Mx.js.map → syntaxHighlighting-yTdSle2t.js.map} +1 -1
  64. package/dist/client/chunks/{useDirtyStateTracking-B5g-bw7a.js → useDirtyStateTracking-GZtwGRu7.js} +3 -3
  65. package/dist/client/chunks/{useDirtyStateTracking-B5g-bw7a.js.map → useDirtyStateTracking-GZtwGRu7.js.map} +1 -1
  66. package/dist/client/chunks/{vendor-CJRtj0__.js → vendor-B_H-VRhj.js} +97 -99
  67. package/dist/client/chunks/vendor-B_H-VRhj.js.map +1 -0
  68. package/dist/client/components.js +4 -4
  69. package/dist/client/components.js.map +1 -1
  70. package/dist/client/hooks.js +3 -3
  71. package/dist/client/index.js +10 -10
  72. package/dist/client/index.js.map +1 -1
  73. package/dist/client/providers.js +1 -1
  74. package/dist/client/styles.css +1 -1
  75. package/dist/client/utils.js +6 -6
  76. package/dist/client-bundle-stats.html +1 -1
  77. package/dist/server/index.cjs +1 -1
  78. package/dist/server/index.js +482 -482
  79. package/package.json +1 -1
  80. package/dist/client/chunks/DashboardEditModal-CVMSvpJ-.js.map +0 -1
  81. package/dist/client/chunks/RetentionCombinedChart-BVKWmxc-.js.map +0 -1
  82. package/dist/client/chunks/RetentionHeatmap-BiqfhGYk.js.map +0 -1
  83. package/dist/client/chunks/analysis-builder-CuTR61Ct.js.map +0 -1
  84. package/dist/client/chunks/analysis-builder-shared-D7iqklYk.js.map +0 -1
  85. package/dist/client/chunks/chart-area-KlBSb_ur.js.map +0 -1
  86. package/dist/client/chunks/chart-bar-D1i2jmIg.js.map +0 -1
  87. package/dist/client/chunks/chart-data-table-CYMMAHau.js.map +0 -1
  88. package/dist/client/chunks/chart-funnel-BH7r4HWZ.js.map +0 -1
  89. package/dist/client/chunks/chart-heat-map-DyGtODLE.js.map +0 -1
  90. package/dist/client/chunks/chart-kpi-delta-Bk396suk.js.map +0 -1
  91. package/dist/client/chunks/chart-kpi-number-CIGqZ5Dw.js.map +0 -1
  92. package/dist/client/chunks/chart-kpi-text-iwlaSQCi.js.map +0 -1
  93. package/dist/client/chunks/chart-line-BKqhoW9A.js.map +0 -1
  94. package/dist/client/chunks/chart-markdown-CiPhRY9s.js.map +0 -1
  95. package/dist/client/chunks/chart-pie-BNr-GgTh.js.map +0 -1
  96. package/dist/client/chunks/chart-radar-CYrGVYEQ.js.map +0 -1
  97. package/dist/client/chunks/chart-radial-bar-2PR3ucIR.js.map +0 -1
  98. package/dist/client/chunks/chart-scatter-D3Z9bl8H.js.map +0 -1
  99. package/dist/client/chunks/chart-tree-map-DCCmMyz1.js.map +0 -1
  100. package/dist/client/chunks/charts-core-DIW3Dd7n.js.map +0 -1
  101. package/dist/client/chunks/vendor-CJRtj0__.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"chart-area-KlBSb_ur.js","sources":["../../../src/client/components/charts/AreaChart.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport { ComposedChart, Area, Line, XAxis, YAxis, CartesianGrid, Legend } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport AngledXAxisTick from './AngledXAxisTick'\nimport { CHART_COLORS, CHART_MARGINS } from '../../utils/chartConstants'\nimport { transformChartDataWithSeries, formatAxisValue } from '../../utils/chartUtils'\nimport { parseTargetValues, spreadTargetValues } from '../../utils/targetUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps } from '../../types'\n\nconst AreaChart = React.memo(function AreaChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n \n try {\n // Determine stacking from stackType (new) or stacked (legacy)\n const stackType = displayConfig?.stackType ?? (displayConfig?.stacked ? 'normal' : 'none')\n const shouldStack = stackType !== 'none'\n const isPercentStack = stackType === 'percent'\n\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showGrid: displayConfig?.showGrid ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n connectNulls: displayConfig?.connectNulls ?? false\n }\n\n // Extract axis format configs\n const leftYAxisFormat = displayConfig?.leftYAxisFormat\n const rightYAxisFormat = displayConfig?.rightYAxisFormat\n\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=\"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 area chart</div>\n </div>\n </div>\n )\n }\n\n // Validate chartConfig - support both legacy and new formats\n let xAxisField: string\n let yAxisFields: string[]\n let seriesFields: string[] = []\n \n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format\n xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis\n yAxisFields = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis : [chartConfig.yAxis]\n seriesFields = chartConfig.series || []\n } else if (chartConfig?.x && chartConfig?.y) {\n // Legacy format\n xAxisField = chartConfig.x\n yAxisFields = Array.isArray(chartConfig.y) ? chartConfig.y : [chartConfig.y]\n } else {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">Invalid or missing chart axis configuration</div>\n </div>\n </div>\n )\n }\n\n if (!xAxisField || !yAxisFields || yAxisFields.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">Missing required X-axis or Y-axis fields</div>\n </div>\n </div>\n )\n }\n\n // Use shared function to transform data and handle series\n const { data: chartData, seriesKeys } = transformChartDataWithSeries(\n data,\n xAxisField,\n yAxisFields,\n queryObject,\n seriesFields,\n getFieldLabel\n )\n\n // Dual Y-axis support: extract yAxisAssignment from chartConfig\n const yAxisAssignment = chartConfig?.yAxisAssignment || {}\n\n // Build mapping from series key (label) to original field name\n const seriesKeyToField: Record<string, string> = {}\n yAxisFields.forEach((field) => {\n const label = getFieldLabel(field)\n seriesKeyToField[label] = field\n })\n\n // Determine if we need a right Y-axis\n const hasRightAxis = yAxisFields.some((field) => yAxisAssignment[field] === 'right')\n\n // Get fields for left and right axes for labels\n const leftAxisFields = yAxisFields.filter((f) => (yAxisAssignment[f] || 'left') === 'left')\n const rightAxisFields = yAxisFields.filter((f) => yAxisAssignment[f] === 'right')\n\n // Disable stacking when dual Y-axis is used (areas on different axes can't be stacked)\n const effectiveShouldStack = shouldStack && !hasRightAxis\n const effectiveIsPercentStack = isPercentStack && !hasRightAxis\n\n // Determine if legend will be shown\n const showLegend = safeDisplayConfig.showLegend\n\n // Use custom chart margins with extra space for Y-axis labels\n const chartMargins = {\n ...CHART_MARGINS,\n left: 40, // Space for left Y-axis label\n right: hasRightAxis ? 40 : 20 // Extra space for right Y-axis label if needed\n }\n \n // Process target values and add to chart data\n const targetValues = parseTargetValues(displayConfig?.target || '')\n const spreadTargets = spreadTargetValues(targetValues, chartData.length)\n \n // Add target data to chart data if targets exist\n let enhancedChartData = chartData\n if (spreadTargets.length > 0) {\n enhancedChartData = chartData.map((dataPoint, index) => ({\n ...dataPoint,\n __target: spreadTargets[index] || null\n }))\n }\n \n // Validate transformed data\n if (!chartData || chartData.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=\"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\">No valid data points for area chart after transformation</div>\n </div>\n </div>\n )\n }\n\n // Determine stack offset for percentage stacking\n const stackOffset = effectiveIsPercentStack ? ('expand' as const) : undefined\n\n return (\n <ChartContainer height={height}>\n <ComposedChart data={enhancedChartData} margin={chartMargins} stackOffset={stackOffset} accessibilityLayer={false}>\n {safeDisplayConfig.showGrid && <CartesianGrid strokeDasharray=\"3 3\" style={{ pointerEvents: 'none' }} />}\n <XAxis dataKey=\"name\" type=\"category\" tick={<AngledXAxisTick />} height={60} />\n <YAxis\n yAxisId=\"left\"\n orientation=\"left\"\n tick={{ fontSize: 12 }}\n tickFormatter={\n effectiveIsPercentStack\n ? (v) => `${(v * 100).toFixed(0)}%`\n : leftYAxisFormat\n ? (value) => formatAxisValue(value, leftYAxisFormat)\n : undefined\n }\n domain={effectiveIsPercentStack ? [0, 1] : undefined}\n label={\n effectiveIsPercentStack\n ? undefined\n : leftAxisFields.length > 0\n ? {\n value: leftYAxisFormat?.label || getFieldLabel(leftAxisFields[0]),\n angle: -90,\n position: 'left',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n {hasRightAxis && (\n <YAxis\n yAxisId=\"right\"\n orientation=\"right\"\n tick={{ fontSize: 12 }}\n tickFormatter={rightYAxisFormat ? (value) => formatAxisValue(value, rightYAxisFormat) : undefined}\n label={\n rightAxisFields.length > 0\n ? {\n value: rightYAxisFormat?.label || getFieldLabel(rightAxisFields[0]),\n angle: 90,\n position: 'right',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n )}\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={(value: any, name: any) => {\n // Handle null values in tooltip\n if (value === null || value === undefined) {\n return ['No data', name]\n }\n if (name === 'Target') {\n // Use left Y-axis format for target values\n return [formatAxisValue(value, leftYAxisFormat), 'Target Value']\n }\n // Format as percentage when using percent stacking\n if (effectiveIsPercentStack && typeof value === 'number') {\n return [`${(value * 100).toFixed(1)}%`, name]\n }\n // Determine which axis format to use based on series name\n const originalField = seriesKeyToField[name]\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n const formatConfig = axisId === 'right' ? rightYAxisFormat : leftYAxisFormat\n return [formatAxisValue(value, formatConfig), name]\n }}\n />\n )}\n {showLegend && (\n <Legend \n wrapperStyle={{ fontSize: '12px', paddingTop: '10px' }}\n iconType=\"rect\"\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 {seriesKeys.map((seriesKey, index) => {\n // Look up the original field name to get its axis assignment\n const originalField = seriesKeyToField[seriesKey]\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n // When drill is enabled, show persistent dots for better click targets\n const areaColor = (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) ||\n CHART_COLORS[index % CHART_COLORS.length]\n\n return (\n <Area\n key={seriesKey}\n type=\"monotone\"\n dataKey={seriesKey}\n yAxisId={axisId}\n stackId={effectiveShouldStack ? 'stack' : undefined}\n stroke={areaColor}\n fill={areaColor}\n fillOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 0.6 : 0.1) : 0.3}\n strokeWidth={2}\n strokeOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 1 : 0.3) : 1}\n connectNulls={safeDisplayConfig.connectNulls}\n dot={(props: any) => {\n const { cx, cy, payload, key } = props\n if (!drillEnabled || cx === undefined || cy === undefined) return null\n\n const handleClick = (event: React.MouseEvent) => {\n event.stopPropagation()\n event.preventDefault()\n if (onDataPointClick) {\n onDataPointClick({\n dataPoint: payload,\n clickedField: originalField || seriesKey,\n xValue: payload.name,\n position: { x: event.clientX, y: event.clientY },\n nativeEvent: event\n })\n }\n }\n\n return (\n <g key={key}>\n {/* Background to mask grid lines - uses theme surface color */}\n <circle\n cx={cx}\n cy={cy}\n r={6}\n fill=\"var(--dc-surface)\"\n style={{ pointerEvents: 'none' }}\n />\n {/* Visible dot with click handler */}\n <circle\n cx={cx}\n cy={cy}\n r={4}\n fill=\"var(--dc-surface)\"\n stroke={areaColor}\n strokeWidth={2}\n cursor=\"pointer\"\n onClick={(e: React.MouseEvent<SVGCircleElement>) => {\n handleClick(e as unknown as React.MouseEvent)\n }}\n />\n </g>\n )\n }}\n activeDot={false}\n />\n )\n })}\n {spreadTargets.length > 0 && (\n <>\n {/* White background line */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n stroke=\"#ffffff\"\n strokeWidth={2}\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n {/* Grey dashed line on top */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n name=\"Target\"\n stroke=\"#8B5CF6\"\n strokeWidth={2}\n strokeDasharray=\"2 3\"\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n </>\n )}\n </ComposedChart>\n </ChartContainer>\n )\n } catch (error) {\n // 'AreaChart rendering 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=\"text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Area 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 AreaChart"],"names":["AreaChart","React","data","chartConfig","displayConfig","queryObject","height","colorPalette","onDataPointClick","drillEnabled","hoveredLegend","setHoveredLegend","useState","getFieldLabel","useCubeFieldLabel","stackType","shouldStack","isPercentStack","safeDisplayConfig","leftYAxisFormat","rightYAxisFormat","jsx","jsxs","xAxisField","yAxisFields","seriesFields","chartData","seriesKeys","transformChartDataWithSeries","yAxisAssignment","seriesKeyToField","field","label","hasRightAxis","leftAxisFields","f","rightAxisFields","effectiveShouldStack","effectiveIsPercentStack","showLegend","chartMargins","CHART_MARGINS","targetValues","parseTargetValues","spreadTargets","spreadTargetValues","enhancedChartData","dataPoint","index","ChartContainer","ComposedChart","CartesianGrid","XAxis","AngledXAxisTick","YAxis","v","value","formatAxisValue","ChartTooltip","name","originalField","formatConfig","Legend","o","seriesKey","axisId","areaColor","CHART_COLORS","Area","props","cx","cy","payload","key","handleClick","event","e","Fragment","Line","error"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chart-bar-D1i2jmIg.js","sources":["../../../src/client/components/charts/AngledXAxisTick.tsx","../../../src/client/components/charts/BarChart.tsx"],"sourcesContent":["import React from 'react'\n\ninterface AngledXAxisTickProps {\n x?: number\n y?: number\n payload?: {\n value: string | number\n }\n tickFormatter?: (value: string | number, index: number) => string\n index?: number\n visibleTicksCount?: number\n // Allow any additional props recharts might pass\n [key: string]: unknown\n}\n\n/**\n * Custom XAxis tick component for properly aligned angled labels.\n * Fixes alignment issues in recharts 3.7+ where angled labels don't\n * center properly under data points.\n */\nconst AngledXAxisTick: React.FC<AngledXAxisTickProps> = ({\n x = 0,\n y = 0,\n payload,\n tickFormatter,\n index = 0\n}) => {\n if (!payload) return null\n\n const displayValue = tickFormatter\n ? tickFormatter(payload.value, index)\n : String(payload.value)\n\n return (\n <g transform={`translate(${x},${y})`}>\n <text\n x={0}\n y={0}\n dy={16}\n textAnchor=\"end\"\n fill=\"currentColor\"\n fontSize={12}\n transform=\"rotate(-45)\"\n >\n {displayValue}\n </text>\n </g>\n )\n}\n\nexport default AngledXAxisTick\n","import React, { useState, useMemo } from 'react'\nimport { ComposedChart, Bar, Line, XAxis, YAxis, CartesianGrid, Cell, Legend } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport AngledXAxisTick from './AngledXAxisTick'\nimport { CHART_COLORS, POSITIVE_COLOR, NEGATIVE_COLOR, CHART_MARGINS } from '../../utils/chartConstants'\nimport { transformChartDataWithSeries, isValidNumericValue, formatAxisValue } from '../../utils/chartUtils'\nimport { parseTargetValues, spreadTargetValues } from '../../utils/targetUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps } from '../../types'\n\nconst BarChart = React.memo(function BarChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n\n // Determine stacking from stackType (new) or stacked (legacy)\n const stackType = displayConfig?.stackType ?? (displayConfig?.stacked ? 'normal' : 'none')\n const shouldStack = stackType !== 'none'\n const isPercentStack = stackType === 'percent'\n\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showGrid: displayConfig?.showGrid ?? true,\n showTooltip: displayConfig?.showTooltip ?? true\n }\n\n // Extract axis format configs\n const leftYAxisFormat = displayConfig?.leftYAxisFormat\n const rightYAxisFormat = displayConfig?.rightYAxisFormat\n\n // Validate chartConfig - support both legacy and new formats\n // Do validation but don't early return yet (hooks must come first)\n const { xAxisField, yAxisFields, seriesFields, configError } = useMemo(() => {\n let xAxisField: string | undefined\n let yAxisFields: string[] = []\n let seriesFields: string[] = []\n let configError: string | null = null\n\n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format\n xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis\n yAxisFields = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis : [chartConfig.yAxis]\n seriesFields = chartConfig.series || []\n } else if (chartConfig?.x && chartConfig?.y) {\n // Legacy format\n xAxisField = chartConfig.x\n yAxisFields = Array.isArray(chartConfig.y) ? chartConfig.y : [chartConfig.y]\n } else {\n configError = 'Invalid or missing chart axis configuration'\n }\n\n if (!configError && (!xAxisField || !yAxisFields || yAxisFields.length === 0)) {\n configError = 'Missing required X-axis or Y-axis fields'\n }\n\n return { xAxisField, yAxisFields, seriesFields, configError }\n }, [chartConfig])\n\n // Transform data (will be empty arrays if config is invalid)\n const { data: transformedData, seriesKeys } = useMemo(() => {\n if (configError || !data || data.length === 0 || !xAxisField) {\n return { data: [], seriesKeys: [] }\n }\n return transformChartDataWithSeries(\n data,\n xAxisField,\n yAxisFields,\n queryObject,\n seriesFields,\n getFieldLabel\n )\n }, [data, xAxisField, yAxisFields, queryObject, seriesFields, getFieldLabel, configError])\n\n // Dual Y-axis support: extract yAxisAssignment from chartConfig (memoized to prevent object recreation)\n const yAxisAssignment = useMemo(() =>\n chartConfig?.yAxisAssignment || {},\n [chartConfig?.yAxisAssignment]\n )\n\n // Build mapping from series key (label) to original field name\n const seriesKeyToField: Record<string, string> = useMemo(() => {\n const mapping: Record<string, string> = {}\n yAxisFields.forEach((field) => {\n const label = getFieldLabel(field)\n mapping[label] = field\n })\n return mapping\n }, [yAxisFields, getFieldLabel])\n\n // Determine if we need a right Y-axis\n const hasRightAxis = yAxisFields.some((field) => yAxisAssignment[field] === 'right')\n\n // Get fields for left and right axes for labels\n const leftAxisFields = yAxisFields.filter((f) => (yAxisAssignment[f] || 'left') === 'left')\n const rightAxisFields = yAxisFields.filter((f) => yAxisAssignment[f] === 'right')\n\n // Null handling: Filter out data points where ALL measure values are null\n // This prevents rendering empty bars and makes the chart clearer\n const { chartData, skippedCount } = useMemo(() => {\n if (transformedData.length === 0 || seriesKeys.length === 0) {\n return { chartData: [], skippedCount: 0 }\n }\n const filtered = transformedData.filter(row => {\n // Keep the row if at least one series has a valid numeric value\n return seriesKeys.some(key => isValidNumericValue(row[key]))\n })\n const skipped = transformedData.length - filtered.length\n return { chartData: filtered, skippedCount: skipped }\n }, [transformedData, seriesKeys])\n\n // Now handle early returns AFTER all hooks\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=\"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 bar 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=\"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 // Determine stack offset for percentage stacking\n // Disable stacking when dual Y-axis is used (bars on different axes can't be stacked)\n const effectiveShouldStack = shouldStack && !hasRightAxis\n const effectiveIsPercentStack = isPercentStack && !hasRightAxis\n const stackOffset = effectiveIsPercentStack ? 'expand' as const : undefined\n\n // Check if we should use positive/negative coloring\n // This is enabled when we have single series data with mixed positive/negative values\n const usePositiveNegativeColoring = seriesKeys.length === 1 && chartData.some(row => {\n const value = row[seriesKeys[0]]\n return typeof value === 'number' && value < 0\n })\n\n // Determine if legend will be shown\n const showLegend = safeDisplayConfig.showLegend\n\n // Use custom chart margins with extra space for Y-axis labels\n const chartMargins = {\n ...CHART_MARGINS,\n left: 40, // Space for left Y-axis label\n right: hasRightAxis ? 40 : 20 // Extra space for right Y-axis label if needed\n }\n \n // Process target values and add to chart data\n const targetValues = parseTargetValues(displayConfig?.target || '')\n const spreadTargets = spreadTargetValues(targetValues, chartData.length)\n \n // Add target data to chart data if targets exist\n let enhancedChartData = chartData\n if (spreadTargets.length > 0) {\n enhancedChartData = chartData.map((dataPoint, index) => ({\n ...dataPoint,\n __target: spreadTargets[index] || null\n }))\n }\n \n // Validate transformed data\n if (!chartData || chartData.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=\"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\">No valid data points for bar chart after transformation</div>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"dc:relative dc:w-full\" style={{ height }}>\n <ChartContainer height={skippedCount > 0 ? `calc(100% - 20px)` : \"100%\"}>\n <ComposedChart data={enhancedChartData} margin={chartMargins} stackOffset={stackOffset} accessibilityLayer={false}>\n {safeDisplayConfig.showGrid && (\n <CartesianGrid strokeDasharray=\"3 3\" style={{ pointerEvents: 'none' }} />\n )}\n <XAxis\n dataKey=\"name\"\n type=\"category\"\n tick={<AngledXAxisTick />}\n height={60}\n />\n <YAxis\n yAxisId=\"left\"\n orientation=\"left\"\n tick={{ fontSize: 12 }}\n tickFormatter={\n effectiveIsPercentStack\n ? (v) => `${(v * 100).toFixed(0)}%`\n : leftYAxisFormat\n ? (value) => formatAxisValue(value, leftYAxisFormat)\n : undefined\n }\n domain={effectiveIsPercentStack ? [0, 1] : undefined}\n label={\n effectiveIsPercentStack\n ? undefined\n : leftAxisFields.length > 0\n ? {\n value: leftYAxisFormat?.label || getFieldLabel(leftAxisFields[0]),\n angle: -90,\n position: 'left',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n {hasRightAxis && (\n <YAxis\n yAxisId=\"right\"\n orientation=\"right\"\n tick={{ fontSize: 12 }}\n tickFormatter={rightYAxisFormat ? (value) => formatAxisValue(value, rightYAxisFormat) : undefined}\n label={\n rightAxisFields.length > 0\n ? {\n value: rightYAxisFormat?.label || getFieldLabel(rightAxisFields[0]),\n angle: 90,\n position: 'right',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n )}\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={(value: any, name: any) => {\n // Handle null values in tooltip\n if (value === null || value === undefined) {\n return ['No data', name]\n }\n if (name === 'Target') {\n // Use left Y-axis format for target values\n return [formatAxisValue(value, leftYAxisFormat), 'Target Value']\n }\n // Format as percentage when using percent stacking\n if (effectiveIsPercentStack && typeof value === 'number') {\n return [`${(value * 100).toFixed(1)}%`, name]\n }\n // Determine which axis format to use based on series name\n const originalField = seriesKeyToField[name]\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n const formatConfig = axisId === 'right' ? rightYAxisFormat : leftYAxisFormat\n return [formatAxisValue(value, formatConfig), name]\n }}\n />\n )}\n {showLegend && (\n <Legend \n wrapperStyle={{ fontSize: '12px', paddingTop: '25px' }}\n iconType=\"rect\"\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 {seriesKeys.map((seriesKey, index) => {\n // Look up the original field name to get its axis assignment\n const originalField = seriesKeyToField[seriesKey]\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n return (\n <Bar\n key={seriesKey}\n dataKey={seriesKey}\n yAxisId={axisId}\n stackId={effectiveShouldStack ? 'stack' : undefined}\n fill={\n usePositiveNegativeColoring\n ? POSITIVE_COLOR\n : (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) ||\n CHART_COLORS[index % CHART_COLORS.length]\n }\n fillOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 1 : 0.3) : 1}\n cursor={drillEnabled ? 'pointer' : undefined}\n onClick={(barData: any, dataIndex: number, event: React.MouseEvent) => {\n if (onDataPointClick && drillEnabled && barData) {\n onDataPointClick({\n dataPoint: enhancedChartData[dataIndex] || barData,\n clickedField: originalField || seriesKey,\n xValue: barData.name,\n position: { x: event.clientX, y: event.clientY },\n nativeEvent: event\n })\n }\n }}\n >\n {usePositiveNegativeColoring &&\n chartData.map((entry, entryIndex) => {\n const value = entry[seriesKey]\n const fillColor = typeof value === 'number' && value < 0 ? NEGATIVE_COLOR : POSITIVE_COLOR\n return (\n <Cell\n key={`cell-${entryIndex}`}\n fill={fillColor}\n fillOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 1 : 0.3) : 1}\n />\n )\n })}\n </Bar>\n )\n })}\n {spreadTargets.length > 0 && (\n <>\n {/* White background line */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n stroke=\"#ffffff\"\n strokeWidth={2}\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n {/* Grey dashed line on top */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n name=\"Target\"\n stroke=\"#8B5CF6\"\n strokeWidth={2}\n strokeDasharray=\"2 3\"\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n </>\n )}\n </ComposedChart>\n </ChartContainer>\n {skippedCount > 0 && (\n <div className=\"dc:text-xs text-dc-text-muted text-center dc:mt-1\">\n {skippedCount} data point{skippedCount !== 1 ? 's' : ''} with no values hidden\n </div>\n )}\n </div>\n )\n } catch (error) {\n // 'BarChart rendering 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=\"text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Bar 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 BarChart"],"names":["AngledXAxisTick","x","y","payload","tickFormatter","index","displayValue","jsx","BarChart","React","data","chartConfig","displayConfig","queryObject","height","colorPalette","onDataPointClick","drillEnabled","hoveredLegend","setHoveredLegend","useState","getFieldLabel","useCubeFieldLabel","stackType","shouldStack","isPercentStack","safeDisplayConfig","leftYAxisFormat","rightYAxisFormat","xAxisField","yAxisFields","seriesFields","configError","useMemo","transformedData","seriesKeys","transformChartDataWithSeries","yAxisAssignment","seriesKeyToField","mapping","field","label","hasRightAxis","leftAxisFields","f","rightAxisFields","chartData","skippedCount","filtered","row","key","isValidNumericValue","skipped","jsxs","effectiveShouldStack","effectiveIsPercentStack","stackOffset","usePositiveNegativeColoring","value","showLegend","chartMargins","CHART_MARGINS","targetValues","parseTargetValues","spreadTargets","spreadTargetValues","enhancedChartData","dataPoint","ChartContainer","ComposedChart","CartesianGrid","XAxis","YAxis","v","formatAxisValue","ChartTooltip","name","originalField","formatConfig","Legend","o","seriesKey","axisId","Bar","POSITIVE_COLOR","CHART_COLORS","barData","dataIndex","event","entry","entryIndex","fillColor","NEGATIVE_COLOR","Cell","Fragment","Line","error"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chart-data-table-CYMMAHau.js","sources":["../../../src/client/utils/pivotUtils.ts","../../../src/client/components/charts/DataTable.tsx"],"sourcesContent":["/**\n * Pivot utilities for DataTable time dimension pivoting\n *\n * When a query includes a time dimension with granularity, transforms flat data\n * into a pivoted structure with time periods as columns.\n */\n\nimport type { CubeQuery, CubeMeta } from '../types'\nimport { formatTimeValue } from './chartUtils'\n\n/**\n * Derives ordered column list from a CubeQuery object.\n * Order: dimensions, timeDimensions (as field names), measures\n *\n * @param queryObject The CubeQuery object\n * @returns Array of field names in order\n */\nexport function getOrderedColumnsFromQuery(queryObject?: CubeQuery): string[] {\n if (!queryObject) return []\n\n const columns: string[] = []\n\n // Add regular dimensions first\n if (queryObject.dimensions) {\n columns.push(...queryObject.dimensions)\n }\n\n // Add time dimensions\n if (queryObject.timeDimensions) {\n queryObject.timeDimensions.forEach(td => {\n if (!columns.includes(td.dimension)) {\n columns.push(td.dimension)\n }\n })\n }\n\n // Add measures last\n if (queryObject.measures) {\n columns.push(...queryObject.measures)\n }\n\n return columns\n}\n\n/**\n * Configuration for pivoting based on query structure\n */\nexport interface PivotConfig {\n /** Time dimension field name (e.g., \"Sales.date\") */\n timeDimension: string\n /** Time granularity (e.g., \"month\", \"day\", \"year\") */\n granularity: string\n /** Non-time dimension fields */\n dimensions: string[]\n /** Measure fields */\n measures: string[]\n}\n\n/**\n * Column definition for pivoted table\n */\nexport interface PivotColumn {\n /** Column key for data access */\n key: string\n /** Display label for column header */\n label: string\n /** Whether this is a time column (vs row identifier column) */\n isTimeColumn: boolean\n /** Whether this is the measure column */\n isMeasureColumn?: boolean\n}\n\n/**\n * Row in the pivoted table\n */\nexport interface PivotRow {\n /** Unique row identifier */\n id: string\n /** The measure field name (for icon lookup) */\n measureField: string\n /** Values keyed by column key */\n values: Record<string, any>\n /** Whether this is the first row in a dimension group (for row spanning) */\n isFirstInGroup?: boolean\n /** Number of rows to span for dimension cells (only set on first row in group) */\n dimensionRowSpan?: number\n}\n\n/**\n * Result of pivoting table data\n */\nexport interface PivotedTableData {\n /** Whether data was pivoted (has time dimension with granularity) */\n isPivoted: boolean\n /** Column definitions for the pivoted table */\n columns: PivotColumn[]\n /** Rows of pivoted data */\n rows: PivotRow[]\n}\n\n/**\n * Determines if a query has a time dimension with granularity that should trigger pivoting\n * @param queryObject The CubeQuery object\n * @param xAxisOverride Optional array of fields to use for filtering dimensions and measures\n * (for respecting chartConfig.xAxis configuration)\n * @returns PivotConfig if pivoting should occur, null otherwise\n */\nexport function hasTimeDimensionForPivot(\n queryObject?: CubeQuery,\n xAxisOverride?: string[]\n): PivotConfig | null {\n if (!queryObject?.timeDimensions?.length) return null\n\n // Find the first time dimension with granularity\n const timeDim = queryObject.timeDimensions.find(td => td.granularity)\n if (!timeDim?.granularity) return null\n\n // Must have at least one measure to pivot\n if (!queryObject.measures?.length) return null\n\n let dimensions: string[]\n let measures: string[]\n\n if (xAxisOverride && xAxisOverride.length > 0) {\n // Filter dimensions from xAxisOverride (excluding time dimension and measures)\n dimensions = xAxisOverride.filter(field => {\n // Exclude the time dimension being pivoted (it becomes columns)\n if (field === timeDim.dimension) return false\n // Exclude measures (they become row values, not dimension columns)\n if (queryObject.measures?.includes(field)) return false\n return true\n })\n\n // Filter measures from xAxisOverride - only show measures that are in xAxis\n const measuresInXAxis = xAxisOverride.filter(field =>\n queryObject.measures?.includes(field)\n )\n // If xAxis contains measures, use only those; otherwise fall back to all measures\n measures = measuresInXAxis.length > 0 ? measuresInXAxis : queryObject.measures\n } else {\n dimensions = queryObject.dimensions || []\n measures = queryObject.measures\n }\n\n // Must have at least one measure after filtering\n if (measures.length === 0) return null\n\n return {\n timeDimension: timeDim.dimension,\n granularity: timeDim.granularity,\n dimensions,\n measures\n }\n}\n\n/**\n * Extracts unique time values from data and sorts them chronologically\n */\nfunction extractAndSortTimeValues(\n data: any[],\n timeDimension: string,\n granularity: string\n): string[] {\n const timeSet = new Set<string>()\n\n data.forEach(row => {\n const rawTime = row[timeDimension]\n if (rawTime != null) {\n const formattedTime = formatTimeValue(rawTime, granularity)\n timeSet.add(formattedTime)\n }\n })\n\n // Sort chronologically - ISO date strings sort correctly alphabetically\n return Array.from(timeSet).sort()\n}\n\n/**\n * Builds column definitions for pivoted table\n * Order: Measure (if multiple) -> Dimensions -> Time columns\n */\nfunction buildColumns(\n config: PivotConfig,\n timeValues: string[],\n getFieldLabel: (field: string) => string\n): PivotColumn[] {\n const columns: PivotColumn[] = []\n\n // Add measure column first (if multiple measures)\n if (config.measures.length > 1) {\n columns.push({\n key: '__measure__',\n label: 'Measure',\n isTimeColumn: false,\n isMeasureColumn: true\n })\n }\n\n // Add dimension columns\n config.dimensions.forEach(dim => {\n columns.push({\n key: dim,\n label: getFieldLabel(dim),\n isTimeColumn: false\n })\n })\n\n // Add time columns (already sorted chronologically)\n timeValues.forEach(timeVal => {\n columns.push({\n key: timeVal,\n label: timeVal,\n isTimeColumn: true\n })\n })\n\n return columns\n}\n\n/**\n * Groups data by dimension values, then by time period\n * Returns Map<dimensionKey, Map<formattedTime, dataRow>>\n */\nfunction groupByDimensions(\n data: any[],\n config: PivotConfig\n): Map<string, Map<string, any>> {\n const grouped = new Map<string, Map<string, any>>()\n\n data.forEach(row => {\n // Build dimension key from all dimension values\n const dimKey = config.dimensions.length > 0\n ? config.dimensions.map(dim => String(row[dim] ?? '')).join('|')\n : '__all__'\n\n // Get or create time map for this dimension combo\n if (!grouped.has(dimKey)) {\n grouped.set(dimKey, new Map())\n }\n\n // Store data point by formatted time value\n const rawTime = row[config.timeDimension]\n if (rawTime != null) {\n const formattedTime = formatTimeValue(rawTime, config.granularity)\n grouped.get(dimKey)!.set(formattedTime, row)\n }\n })\n\n return grouped\n}\n\n/**\n * Builds pivoted rows from grouped data\n * Rows are grouped by measure first (all Revenue rows together, all Quantity rows together)\n * Then within each measure group, rows are ordered by dimension values\n */\nfunction buildPivotedRows(\n data: any[],\n config: PivotConfig,\n timeValues: string[],\n getFieldLabel: (field: string) => string\n): PivotRow[] {\n // Group data by dimensions and time\n const grouped = groupByDimensions(data, config)\n const dimensionKeys = Array.from(grouped.keys())\n\n const rows: PivotRow[] = []\n const measureCount = config.measures.length\n const dimensionCount = dimensionKeys.length\n\n // Iterate over measures first, then dimensions\n // This groups all rows for one measure together\n config.measures.forEach((measure) => {\n dimensionKeys.forEach((dimensionKey, dimIndex) => {\n const timeData = grouped.get(dimensionKey)!\n const rowId = measureCount > 1\n ? `${measure}|${dimensionKey}`\n : dimensionKey\n\n const values: Record<string, any> = {}\n\n // Add dimension values\n const dimValues = dimensionKey === '__all__' ? [] : dimensionKey.split('|')\n config.dimensions.forEach((dim, idx) => {\n values[dim] = dimValues[idx] ?? ''\n })\n\n // Add measure name if multiple measures (for display in Measure column)\n if (measureCount > 1) {\n values['__measure__'] = getFieldLabel(measure)\n }\n\n // Add time period values\n timeValues.forEach(timeVal => {\n const dataPoint = timeData.get(timeVal)\n values[timeVal] = dataPoint?.[measure] ?? null\n })\n\n // Track grouping for row spanning (when multiple measures)\n // First row of each measure group should span all dimension rows\n const isFirstInGroup = dimIndex === 0\n const measureRowSpan = isFirstInGroup && measureCount > 1 ? dimensionCount : undefined\n\n rows.push({\n id: rowId,\n measureField: measure,\n values,\n isFirstInGroup,\n dimensionRowSpan: measureRowSpan\n })\n })\n })\n\n return rows\n}\n\n/**\n * Pivots flat data into time-columned table structure\n *\n * @param data Raw data array from query result\n * @param config Pivot configuration\n * @param getFieldLabel Function to get display label for a field\n * @param meta Optional cube metadata for looking up measure types\n * @returns Pivoted table data structure\n */\nexport function pivotTableData(\n data: any[],\n config: PivotConfig,\n getFieldLabel: (field: string) => string,\n _meta?: CubeMeta | null\n): PivotedTableData {\n if (!data || data.length === 0) {\n return { isPivoted: true, columns: [], rows: [] }\n }\n\n // Extract unique time values and sort chronologically\n const timeValues = extractAndSortTimeValues(data, config.timeDimension, config.granularity)\n\n // Build column definitions\n const columns = buildColumns(config, timeValues, getFieldLabel)\n\n // Build pivoted rows\n const rows = buildPivotedRows(data, config, timeValues, getFieldLabel)\n\n return { isPivoted: true, columns, rows }\n}\n\n/**\n * Looks up the measure type from metadata\n *\n * @param measureField The fully qualified measure field name (e.g., \"Sales.revenue\")\n * @param meta Cube metadata\n * @returns The measure type (e.g., \"sum\", \"count\", \"avg\") or undefined\n */\nexport function getMeasureType(measureField: string, meta: CubeMeta | null): string | undefined {\n if (!meta?.cubes) return undefined\n\n for (const cube of meta.cubes) {\n const measure = cube.measures.find(m => m.name === measureField)\n if (measure) {\n return measure.type\n }\n }\n\n return undefined\n}\n","/**\n * Data Table component with time dimension pivoting\n *\n * When a query includes a time dimension with granularity, the table\n * automatically pivots to show time periods as columns.\n */\n\nimport React, { useMemo } from 'react'\nimport { useCubeMeta } from '../../providers/CubeProvider'\nimport { getMeasureTypeIcon } from '../../icons'\nimport {\n hasTimeDimensionForPivot,\n pivotTableData,\n getMeasureType,\n getOrderedColumnsFromQuery,\n type PivotedTableData,\n type PivotColumn,\n type PivotRow\n} from '../../utils/pivotUtils'\nimport { formatAxisValue } from '../../utils/chartUtils'\nimport type { ChartProps, AxisFormatConfig } from '../../types'\n\nconst DataTable = React.memo(function DataTable({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = 300\n}: ChartProps) {\n const { getFieldLabel, meta } = useCubeMeta()\n\n // Detect if we should pivot based on query structure\n // Pass chartConfig.xAxis to respect user-configured column ordering\n const pivotConfig = useMemo(\n () => hasTimeDimensionForPivot(queryObject, chartConfig?.xAxis),\n [queryObject, chartConfig?.xAxis]\n )\n\n // Check if pivoting is enabled (default: true when time dimension present)\n const enablePivot = displayConfig?.pivotTimeDimension !== false\n\n // Compute pivoted data if applicable\n const pivotedData = useMemo<PivotedTableData | null>(() => {\n if (!pivotConfig || !enablePivot) return null\n return pivotTableData(data, pivotConfig, getFieldLabel, meta)\n }, [data, pivotConfig, enablePivot, getFieldLabel, meta])\n\n // Empty state\n if (!data || data.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full\"\n style={{ height }}\n >\n <div className=\"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\">No data to display in table</div>\n </div>\n </div>\n )\n }\n\n // Render pivoted table if applicable\n if (pivotedData?.isPivoted && pivotedData.columns.length > 0) {\n return (\n <PivotedTable\n pivotedData={pivotedData}\n height={height}\n meta={meta}\n leftYAxisFormat={displayConfig?.leftYAxisFormat}\n />\n )\n }\n\n // Fallback to flat table rendering\n return (\n <FlatTable\n data={data}\n chartConfig={chartConfig}\n queryObject={queryObject}\n height={height}\n getFieldLabel={getFieldLabel}\n leftYAxisFormat={displayConfig?.leftYAxisFormat}\n />\n )\n})\n\nexport default DataTable\n\n/**\n * Pivoted table component - renders time dimension as columns\n */\nfunction PivotedTable({\n pivotedData,\n height,\n meta,\n leftYAxisFormat\n}: {\n pivotedData: PivotedTableData\n height: number | string\n meta: any\n leftYAxisFormat?: AxisFormatConfig\n}) {\n const { columns, rows } = pivotedData\n\n if (columns.length === 0 || rows.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full\"\n style={{ height }}\n >\n <div className=\"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\">No data to display in table</div>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"dc:w-full dc:overflow-auto\" style={{ height }}>\n <table className=\"dc:min-w-full dc:divide-y border-dc-border\">\n <thead className=\"bg-dc-surface-secondary dc:sticky dc:top-0\">\n <tr>\n {columns.map((col: PivotColumn) => (\n <th\n key={col.key}\n className={`dc:px-3 dc:py-2 dc:text-xs dc:font-medium text-dc-text-muted dc:uppercase dc:tracking-wider dc:whitespace-nowrap ${\n col.isTimeColumn ? 'text-right' : 'text-left'\n }`}\n >\n {col.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"bg-dc-surface dc:divide-y border-dc-border\">\n {rows.map((row: PivotRow) => (\n <PivotedTableRow\n key={row.id}\n row={row}\n columns={columns}\n meta={meta}\n leftYAxisFormat={leftYAxisFormat}\n />\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\n/**\n * Single row in the pivoted table\n * Supports row spanning for measure cells when grouped by measure\n */\nfunction PivotedTableRow({\n row,\n columns,\n meta,\n leftYAxisFormat\n}: {\n row: PivotRow\n columns: PivotColumn[]\n meta: any\n leftYAxisFormat?: AxisFormatConfig\n}) {\n // Get measure icon for this row\n const measureType = getMeasureType(row.measureField, meta)\n const MeasureIcon = getMeasureTypeIcon(measureType)\n\n return (\n <tr className=\"hover:bg-dc-surface-secondary\">\n {columns.map((col: PivotColumn) => {\n const value = row.values[col.key]\n\n // Measure column - show icon + label with row spanning\n if (col.isMeasureColumn) {\n // Skip this cell if not first in measure group\n if (row.isFirstInGroup === false) {\n return null\n }\n\n return (\n <td\n key={col.key}\n className=\"dc:px-3 dc:py-2 dc:whitespace-nowrap dc:text-sm text-dc-text dc:align-top\"\n rowSpan={row.dimensionRowSpan}\n >\n <div className=\"dc:flex dc:items-center\">\n <MeasureIcon className=\"dc:w-3.5 dc:h-3.5 dc:mr-1.5 text-dc-text-muted dc:shrink-0\" />\n <span>{value}</span>\n </div>\n </td>\n )\n }\n\n // Time column - right-aligned, formatted values (these are measure values)\n if (col.isTimeColumn) {\n return (\n <td\n key={col.key}\n className=\"dc:px-3 dc:py-2 dc:whitespace-nowrap dc:text-sm text-right text-dc-text\"\n >\n {formatPivotCellValue(value, leftYAxisFormat)}\n </td>\n )\n }\n\n // Dimension column - left-aligned (no row spanning) - no number formatting\n return (\n <td\n key={col.key}\n className=\"dc:px-3 dc:py-2 dc:whitespace-nowrap dc:text-sm text-dc-text\"\n >\n {formatPivotCellValue(value)}\n </td>\n )\n })}\n </tr>\n )\n}\n\n/**\n * Format cell value for pivoted table display\n * Shows \"-\" for null/undefined values\n */\nfunction formatPivotCellValue(value: any, formatConfig?: AxisFormatConfig): string {\n if (value === null || value === undefined) return '-'\n if (typeof value === 'number') {\n // Use formatAxisValue if config provided\n if (formatConfig) {\n return formatAxisValue(value, formatConfig)\n }\n // Format with locale and up to 2 decimal places\n if (Number.isInteger(value)) {\n return value.toLocaleString()\n }\n return parseFloat(value.toFixed(2)).toLocaleString()\n }\n if (typeof value === 'boolean') {\n return value ? 'Yes' : 'No'\n }\n return String(value)\n}\n\n/**\n * Flat table component - original non-pivoted table rendering\n */\nfunction FlatTable({\n data,\n chartConfig,\n queryObject,\n height,\n getFieldLabel,\n leftYAxisFormat\n}: {\n data: any[]\n chartConfig?: { xAxis?: string[] }\n queryObject?: ChartProps['queryObject']\n height: number | string\n getFieldLabel: (field: string) => string\n leftYAxisFormat?: AxisFormatConfig\n}) {\n // Get all columns available in data\n const allColumns = Object.keys(data[0] || {})\n\n // Determine column order with proper fallback:\n // 1. If xAxis is configured, use that order\n // 2. Otherwise, derive order from queryObject (dimensions + timeDimensions + measures)\n // 3. Fallback: Object.keys order\n const columns = useMemo(() => {\n // If xAxis is configured, use that order (filtered to existing columns)\n if (chartConfig?.xAxis && chartConfig.xAxis.length > 0) {\n return chartConfig.xAxis.filter(col => allColumns.includes(col))\n }\n\n // Derive order from queryObject\n const queryOrder = getOrderedColumnsFromQuery(queryObject)\n if (queryOrder.length > 0) {\n // Filter to only columns that exist in data\n const ordered = queryOrder.filter(col => allColumns.includes(col))\n // Add any columns in data that weren't in query (edge case)\n const remaining = allColumns.filter(col => !ordered.includes(col))\n return [...ordered, ...remaining]\n }\n\n // Final fallback: Object.keys order\n return allColumns\n }, [chartConfig?.xAxis, queryObject, allColumns])\n\n if (columns.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full\"\n style={{ height }}\n >\n <div className=\"text-center text-dc-text-muted\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No columns available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">Data structure is invalid</div>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"dc:w-full dc:overflow-auto\" style={{ height }}>\n <table className=\"dc:min-w-full dc:divide-y border-dc-border\">\n <thead className=\"bg-dc-surface-secondary dc:sticky dc:top-0\">\n <tr>\n {columns.map((column) => (\n <th\n key={column}\n className=\"dc:px-3 dc:py-2 text-left dc:text-xs dc:font-medium text-dc-text-muted dc:uppercase dc:tracking-wider\"\n >\n {getFieldLabel(column)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"bg-dc-surface dc:divide-y border-dc-border\">\n {data.map((row, index) => (\n <tr key={index} className=\"hover:bg-dc-surface-secondary\">\n {columns.map((column) => (\n <td\n key={column}\n className=\"dc:px-3 dc:py-2 dc:whitespace-nowrap dc:text-sm text-dc-text\"\n >\n {formatCellValue(row[column], leftYAxisFormat)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n\n/**\n * Format cell value for flat table display\n */\nfunction formatCellValue(value: any, formatConfig?: AxisFormatConfig): string {\n if (value == null) return ''\n if (typeof value === 'number') {\n // Use formatAxisValue if config provided\n if (formatConfig) {\n return formatAxisValue(value, formatConfig)\n }\n return value.toLocaleString()\n }\n if (typeof value === 'boolean') {\n return value ? 'Yes' : 'No'\n }\n return String(value)\n}\n"],"names":["getOrderedColumnsFromQuery","queryObject","columns","td","hasTimeDimensionForPivot","xAxisOverride","timeDim","dimensions","measures","field","measuresInXAxis","extractAndSortTimeValues","data","timeDimension","granularity","timeSet","row","rawTime","formattedTime","formatTimeValue","buildColumns","config","timeValues","getFieldLabel","dim","timeVal","groupByDimensions","grouped","dimKey","buildPivotedRows","dimensionKeys","rows","measureCount","dimensionCount","measure","dimensionKey","dimIndex","timeData","rowId","values","dimValues","idx","dataPoint","isFirstInGroup","measureRowSpan","pivotTableData","_meta","getMeasureType","measureField","meta","cube","m","DataTable","React","chartConfig","displayConfig","height","useCubeMeta","pivotConfig","useMemo","enablePivot","pivotedData","jsx","jsxs","PivotedTable","FlatTable","leftYAxisFormat","col","PivotedTableRow","measureType","MeasureIcon","getMeasureTypeIcon","value","formatPivotCellValue","formatConfig","formatAxisValue","allColumns","queryOrder","ordered","remaining","column","index","formatCellValue"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chart-funnel-BH7r4HWZ.js","sources":["../../../src/client/components/charts/FunnelChart.tsx"],"sourcesContent":["/**\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=\"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 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 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 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 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"],"names":["FUNNEL_COLORS","getStepColor","index","colorPalette","colors","isFunnelData","data","first","getTimeMetricsLines","step","showAvg","showMedian","showP90","lines","formatDuration","FunnelChart","React","height","displayConfig","customStepLabels","isVertical","funnelStyle","showConversion","showAvgTime","showMedianTime","showP90Time","funnelData","useMemo","row","record","nameField","k","name","valueField","value","percentField","percentage","convRateField","conversionRate","firstStepValue","jsx","jsxs","paletteColors","CHART_COLORS","ResponsiveContainer","RechartsFunnelChart","Tooltip","Funnel","_","Cell","LabelList","v","heightPercent","prevStep","stepConversionRate","displayName","timeMetricsLines","metricsCount","line","i","widthPercent"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chart-heat-map-DyGtODLE.js","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=\"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=\"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=\"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"],"names":["parseColor","color","hex","rgbMatch","getLuminance","rgb","r","g","b","rLinear","gLinear","bLinear","getContrastingTextColor","bgColor","MAX_HEATMAP_ROWS","MAX_HEATMAP_COLS","transformToHeatMapFormat","data","xAxisField","yAxisField","valueField","xGranularity","yGranularity","groupedByY","allXValues","xValueOriginals","row","rawYValue","rawXValue","yValue","formatTimeValue","xValue","value","xValueArray","origA","origB","originalRows","originalCols","truncated","limitedXValues","result","rowCount","xMap","x","HeatMapChart","React","height","chartConfig","colorPalette","displayConfig","queryObject","displayConfigAny","showLabels","cellShape","showLegend","xAxisFormat","yAxisFormat","valueFormat","getFieldGranularity","heatmapData","useMemo","jsx","jsxs","colors","ResponsiveHeatMap","v","formatAxisValue","num"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chart-kpi-delta-Bk396suk.js","sources":["../../../src/client/components/charts/KpiDelta.tsx"],"sourcesContent":["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=\"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=\"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=\"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 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=\"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 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"],"names":["VarianceHistogram","values","lastValue","positiveColor","negativeColor","formatValue","width","height","maxBars","limitedValues","variances","value","minVariance","maxVariance","jsx","barGap","availableWidth","barWidth","totalRange","zeroLinePercent","jsxs","variance","index","normalizedHeight","barHeight","isPositive","isLastValue","color","xPosition","KpiDelta","React","data","chartConfig","displayConfig","queryObject","colorPalette","fontSize","setFontSize","useState","textWidth","setTextWidth","containerRef","useRef","valueRef","getFieldLabel","useCubeFieldLabel","useEffect","updateDimensions","rect","containerWidth","containerHeight","widthBasedSize","heightBasedSize","baseFontSize","clampedFontSize","measuredWidth","maxHistogramWidth","effectiveWidth","timer","resizeObserver","valueFields","dimensionFields","valueField","dimensionField","sortedData","a","b","aVal","bVal","useLastCompletePeriod","skipLastPeriod","filteredData","excludedIncompletePeriod","skippedLastPeriod","granularity","filterIncompletePeriod","row","val","secondLastValue","absoluteChange","percentageChange","isPositiveChange","formatNumber","decimals","prefix","formattedValue","getPositiveColor","colorIndex","getNegativeColor","currentColor","label","Icon","infoCircleIcon"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chart-kpi-number-CIGqZ5Dw.js","sources":["../../../src/client/utils/periodUtils.ts","../../../src/client/components/DataHistogram.tsx","../../../src/client/components/charts/KpiNumber.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","\ninterface DataHistogramProps {\n /** Array of numeric values to create histogram from */\n values: number[]\n /** Minimum value in the dataset */\n min: number\n /** Maximum value in the dataset */\n max: number\n /** Color for the histogram bars */\n color?: string\n /** Number of buckets/bars to create (default: 12) */\n bucketCount?: number\n /** Height of the histogram in pixels (default: 32) */\n height?: number\n /** Format function for min/max labels */\n formatValue?: (value: number) => string\n /** Width of the histogram to match text above */\n width?: number\n /** Whether to show average indicator line (default: true) */\n showAverageIndicator?: boolean\n /** Target value to show as green line */\n targetValue?: number\n}\n\n/**\n * Reusable histogram component that shows the distribution of actual data values\n */\nexport default function DataHistogram({\n values,\n min,\n max,\n color = '#1f2937',\n bucketCount = 12,\n height = 32,\n formatValue = (val) => val.toString(),\n width,\n showAverageIndicator = true,\n targetValue\n}: DataHistogramProps) {\n // Create histogram buckets from actual data\n const buckets = new Array(bucketCount).fill(0)\n const range = max - min\n \n // Distribute actual values into buckets\n values.forEach(value => {\n if (range === 0) {\n // All values are the same, put everything in middle bucket\n buckets[Math.floor(bucketCount / 2)]++\n } else {\n // Calculate which bucket this value belongs to\n let bucketIndex = Math.floor(((value - min) / range) * (bucketCount - 1))\n // Clamp to valid bucket range\n bucketIndex = Math.max(0, Math.min(bucketCount - 1, bucketIndex))\n buckets[bucketIndex]++\n }\n })\n \n // Find max bucket count for normalization\n const maxBucketCount = Math.max(...buckets)\n \n // Calculate average for indicator positioning\n const average = values.reduce((sum, val) => sum + val, 0) / values.length\n \n // Calculate average position as percentage of histogram width\n const averagePosition = range === 0 ? 50 : ((average - min) / range) * 100\n \n // Calculate target position if target value is provided\n const targetPosition = targetValue !== undefined && range > 0 \n ? ((targetValue - min) / range) * 100 \n : null\n\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center\">\n {/* Horizontal bars representing actual data distribution */}\n <div \n className=\"dc:relative dc:flex dc:items-end dc:justify-center dc:space-x-0.5\" \n style={{ \n height: `${height}px`,\n width: width ? `${width}px` : '200px',\n minWidth: '200px'\n }}\n >\n {buckets.map((count, i) => {\n // Normalize height based on actual data frequency\n const normalizedHeight = maxBucketCount > 0 ? count / maxBucketCount : 0\n const minHeight = 0.1 // minimum height for empty buckets\n const displayHeight = count > 0 ? Math.max(minHeight, normalizedHeight) : minHeight\n \n return (\n <div\n key={i}\n className=\"dc:flex-1 dc:rounded-t-sm dc:transition-all dc:duration-300 dc:ease-out\"\n style={{\n height: `${displayHeight * height}px`,\n backgroundColor: color,\n opacity: count > 0 ? 0.7 + (normalizedHeight * 0.3) : 0.2 // higher opacity for buckets with data\n }}\n title={`${count} values in this range`} // tooltip showing actual count\n />\n )\n })}\n \n {/* Average indicator line */}\n {showAverageIndicator && (\n <div\n className=\"dc:absolute dc:top-0 dc:bottom-0 dc:pointer-events-none\"\n style={{\n left: `${averagePosition}%`,\n transform: 'translateX(-50%)',\n width: '2px',\n backgroundColor: '#ef4444',\n opacity: 0.8,\n zIndex: 10\n }}\n title={`Average: ${formatValue(average)}`}\n >\n {/* Small triangle at top to indicate average */}\n <div\n className=\"dc:absolute dc:-top-1\"\n style={{\n left: '50%',\n transform: 'translateX(-50%)',\n width: '0',\n height: '0',\n borderLeft: '4px solid transparent',\n borderRight: '4px solid transparent',\n borderTop: '6px solid #ef4444'\n }}\n />\n </div>\n )}\n \n {/* Target indicator line */}\n {targetPosition !== null && targetValue !== undefined && (\n <div\n className=\"dc:absolute dc:top-0 dc:bottom-0 dc:pointer-events-none\"\n style={{\n left: `${Math.max(0, Math.min(100, targetPosition))}%`,\n transform: 'translateX(-50%)',\n width: '2px',\n backgroundColor: '#10b981',\n opacity: 0.8,\n zIndex: 11\n }}\n title={`Target: ${formatValue(targetValue)}`}\n >\n {/* Small triangle at top to indicate target */}\n <div\n className=\"dc:absolute dc:-top-1\"\n style={{\n left: '50%',\n transform: 'translateX(-50%)',\n width: '0',\n height: '0',\n borderLeft: '4px solid transparent',\n borderRight: '4px solid transparent',\n borderTop: '6px solid #10b981'\n }}\n />\n </div>\n )}\n </div>\n \n {/* Min/Max values aligned with histogram width */}\n <div\n className=\"dc:flex dc:justify-between dc:mt-2 dc:text-xs text-dc-text-muted\"\n style={{\n width: width ? `${width}px` : '200px',\n minWidth: '200px'\n }}\n >\n <span>{formatValue(min)}</span>\n <span>{formatValue(max)}</span>\n </div>\n\n {/* Average indicator */}\n <div className=\"text-center dc:mt-1 dc:text-xs text-dc-text-muted\">\n Average of {values.length} values\n </div>\n </div>\n )\n}","import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react'\nimport { Icon } from '@iconify/react'\nimport infoCircleIcon from '@iconify-icons/tabler/info-circle'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport DataHistogram from '../DataHistogram'\nimport { parseTargetValues, calculateVariance, formatVariance } from '../../utils/targetUtils'\nimport { filterIncompletePeriod } from '../../utils/periodUtils'\nimport type { ChartProps } from '../../types'\n\nconst KpiNumber = React.memo(function KpiNumber({\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\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n\n // Extract value field from chart config - handle both string and array formats\n const valueFields = useMemo(() => {\n if (!chartConfig?.yAxis) return []\n if (typeof chartConfig.yAxis === 'string') return [chartConfig.yAxis]\n if (Array.isArray(chartConfig.yAxis)) return chartConfig.yAxis\n return []\n }, [chartConfig?.yAxis])\n\n const valueField = valueFields[0] || '' // Use first measure field\n\n // Get time dimension field if present (for incomplete period filtering)\n const timeDimensionField = queryObject?.timeDimensions?.[0]?.dimension || undefined\n\n // Memoize sorted data to prevent recalculation on every render\n const sortedData = useMemo(() => {\n if (!data || data.length === 0) return []\n let sorted = [...data]\n if (timeDimensionField) {\n sorted = sorted.sort((a, b) => {\n const aVal = a[timeDimensionField]\n const bVal = b[timeDimensionField]\n if (aVal < bVal) return -1\n if (aVal > bVal) return 1\n return 0\n })\n }\n return sorted\n }, [data, timeDimensionField])\n\n // Filter out incomplete or last period if enabled\n const { useLastCompletePeriod = true, skipLastPeriod = false } = displayConfig\n\n // Memoize filtered data\n const {\n filteredData,\n excludedIncompletePeriod,\n skippedLastPeriod,\n granularity\n } = useMemo(() => {\n if (sortedData.length === 0) {\n return { filteredData: [], excludedIncompletePeriod: false, skippedLastPeriod: false, granularity: undefined }\n }\n return filterIncompletePeriod(sortedData, timeDimensionField, queryObject, useLastCompletePeriod, skipLastPeriod)\n }, [sortedData, timeDimensionField, queryObject, useLastCompletePeriod, skipLastPeriod])\n\n // Use filtered data for calculations\n const dataToUse = filteredData\n\n // Memoize value extraction to prevent recalculation\n const values = useMemo(() => {\n if (!valueField || dataToUse.length === 0) return []\n\n const rawValues = dataToUse.map(row => {\n // Try direct field access first\n if (row[valueField] !== undefined) {\n return row[valueField]\n }\n\n // If not found, try finding the first numeric field as fallback\n const numericFields = Object.keys(row).filter(key =>\n typeof row[key] === 'number' && !isNaN(row[key])\n )\n\n if (numericFields.length > 0) {\n return row[numericFields[0]]\n }\n\n return undefined\n })\n\n return rawValues\n .filter(val => val !== null && val !== undefined && !isNaN(Number(val)))\n .map(val => Number(val))\n }, [dataToUse, valueField])\n\n // Memoize statistics calculations\n const { avg, min, max } = useMemo(() => {\n if (values.length === 0) return { avg: 0, min: 0, max: 0 }\n const sum = values.reduce((acc, val) => acc + val, 0)\n const avg = sum / values.length\n const min = Math.min(...values)\n const max = Math.max(...values)\n return { avg, min, max }\n }, [values])\n\n // Memoize format function to prevent re-creating on every render\n const formatNumber = useCallback((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 }, [displayConfig])\n\n const mainValue = values.length === 1 ? values[0] : avg\n const showStats = values.length > 1\n\n // Memoize color calculation to prevent re-creating function on every render\n const valueColor = useMemo((): string => {\n if (displayConfig.valueColorIndex !== undefined && colorPalette?.colors) {\n const colorIndex = displayConfig.valueColorIndex\n if (colorIndex >= 0 && colorIndex < colorPalette.colors.length) {\n return colorPalette.colors[colorIndex]\n }\n }\n // Default to first color in palette if available, otherwise fallback to dark gray\n return colorPalette?.colors?.[0] || '#1f2937'\n }, [displayConfig.valueColorIndex, colorPalette?.colors])\n\n // Process target values for variance calculation\n const targetValues = useMemo(() => parseTargetValues(displayConfig?.target || ''), [displayConfig?.target])\n const targetValue = targetValues.length > 0 ? targetValues[0] : null // Use first target value\n const variance = targetValue !== null && values.length > 0 ? calculateVariance(mainValue, targetValue) : null\n\n // Memoize variance color calculation\n const varianceColor = useMemo((): string => {\n if (variance === null) return '#6B7280' // Gray for no target\n\n if (variance >= 0) {\n // Positive variance - use positive color from palette\n const positiveIndex = displayConfig.positiveColorIndex ?? 1\n return colorPalette?.colors?.[positiveIndex] || '#10B981' // Green fallback\n } else {\n // Negative variance - use negative color from palette\n const negativeIndex = displayConfig.negativeColorIndex ?? 7\n return colorPalette?.colors?.[negativeIndex] || '#EF4444' // Red fallback\n }\n }, [variance, displayConfig.positiveColorIndex, displayConfig.negativeColorIndex, colorPalette?.colors])\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 // Calculate font size based on container dimensions\n // For KPI displays, we want the text to be large and prominent\n // Reserve space for the label by using more conservative sizing\n const widthBasedSize = containerWidth / 5\n const heightBasedSize = containerHeight / 4 // More conservative to leave room for label\n const baseFontSize = Math.min(widthBasedSize, heightBasedSize)\n const clampedFontSize = Math.max(24, Math.min(baseFontSize, 120)) // Lower max to ensure label fits\n setFontSize(clampedFontSize)\n\n // Use a timeout to measure text width after font size is applied\n setTimeout(() => {\n if (valueRef.current) {\n const textRect = valueRef.current.getBoundingClientRect()\n const measuredWidth = textRect.width\n // Ensure we have a minimum width and use container width as fallback\n const effectiveWidth = Math.max(measuredWidth, Math.min(containerWidth * 0.6, 300))\n setTextWidth(effectiveWidth)\n }\n }, 10)\n }\n }\n }\n\n // Initial calculation - reduce delay for faster initial render\n const timer = setTimeout(updateDimensions, 50)\n\n const resizeObserver = new ResizeObserver(() => {\n // Debounce the resize updates\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 // Early returns AFTER all hooks\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=\"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\">No data points to display</div>\n </div>\n </div>\n )\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=\"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 fields configured</div>\n </div>\n </div>\n )\n }\n\n // Null handling: If all values are null, show placeholder instead of error\n if (values.length === 0) {\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 text-center dc:mb-3\"\n style={{\n fontSize: '14px',\n lineHeight: '1.2'\n }}\n >\n {getFieldLabel(valueField)}\n </div>\n\n {/* No Data Placeholder */}\n <div\n className=\"dc:font-bold dc:leading-none text-dc-text-muted\"\n style={{\n fontSize: `${fontSize}px`\n }}\n >\n —\n </div>\n\n <div className=\"dc:text-xs text-dc-text-muted dc:mt-2\">No data</div>\n </div>\n )\n }\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 - Bolder and bigger */}\n <div\n className=\"text-dc-text-secondary dc:font-bold text-center dc:mb-3 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 // Temporary fix: if label seems wrong, use the field name directly\n const displayLabel = (label && label.length > 1) ? label : valueField\n return displayLabel\n })()}\n </span>\n {(excludedIncompletePeriod || skippedLastPeriod) && (\n <span\n title={skippedLastPeriod\n ? `Excludes last ${granularity || 'period'}`\n : `Excludes current incomplete ${granularity}`}\n className=\"dc:cursor-help\"\n >\n <Icon icon={infoCircleIcon} className=\"dc:w-4 dc:h-4 text-dc-text-muted dc:opacity-70\" />\n </span>\n )}\n </div>\n\n {/* Main KPI Value and Variance - Horizontal layout */}\n <div className=\"dc:flex dc:items-center dc:justify-center dc:gap-4 dc:mb-3\">\n <div\n ref={valueRef}\n className=\"dc:font-bold dc:leading-none\"\n style={{\n fontSize: `${fontSize}px`,\n color: valueColor\n }}\n >\n {formatNumber(mainValue)}\n </div>\n\n {/* Target Variance Display - To the right of main value */}\n {targetValue !== null && variance !== null && (\n <div className=\"dc:flex dc:flex-col dc:items-start\">\n <div\n className=\"dc:font-semibold\"\n style={{\n fontSize: `${Math.max(12, fontSize * 0.3)}px`,\n color: varianceColor,\n lineHeight: '1.2'\n }}\n >\n {formatVariance(variance, 1)}\n </div>\n <div\n className=\"text-dc-text-muted dc:text-xs\"\n style={{\n opacity: 0.7,\n fontSize: `${Math.max(10, fontSize * 0.2)}px`\n }}\n >\n vs {formatNumber(targetValue)}\n </div>\n </div>\n )}\n </div>\n\n {/* Unit/Suffix - Larger, not bold (hidden when formatValue is provided) */}\n {displayConfig.suffix && !displayConfig.formatValue && (\n <div\n className=\"text-dc-text-muted text-center\"\n style={{\n fontSize: '14px',\n lineHeight: '1.2',\n opacity: 0.8\n }}\n >\n {displayConfig.suffix}\n </div>\n )}\n\n {/* Data Histogram for multiple values */}\n {showStats && (\n <div className=\"dc:mt-4\">\n <DataHistogram\n values={values}\n min={min}\n max={max}\n color={valueColor}\n formatValue={formatNumber}\n height={24}\n width={textWidth}\n targetValue={targetValue || undefined}\n />\n </div>\n )}\n </div>\n )\n})\n\nexport default KpiNumber\n"],"names":["getPeriodEndDate","date","granularity","endDate","daysUntilSaturday","currentMonth","quarterEndMonth","isLastPeriodComplete","lastDataPoint","timeDimensionField","timeValue","getQueryGranularity","queryObject","dimensionField","matchingDim","td","filterIncompletePeriod","data","useLastCompletePeriod","skipLastPeriod","noFilter","lastRow","DataHistogram","values","min","max","color","bucketCount","height","formatValue","val","width","showAverageIndicator","targetValue","buckets","range","value","bucketIndex","maxBucketCount","average","sum","averagePosition","targetPosition","jsxs","count","i","normalizedHeight","minHeight","displayHeight","jsx","KpiNumber","React","chartConfig","displayConfig","colorPalette","fontSize","setFontSize","useState","textWidth","setTextWidth","containerRef","useRef","valueRef","getFieldLabel","useCubeFieldLabel","valueFields","useMemo","valueField","sortedData","sorted","a","b","aVal","bVal","filteredData","excludedIncompletePeriod","skippedLastPeriod","dataToUse","row","numericFields","key","avg","acc","formatNumber","useCallback","decimals","prefix","formattedValue","mainValue","showStats","valueColor","colorIndex","targetValues","parseTargetValues","variance","calculateVariance","varianceColor","positiveIndex","negativeIndex","useEffect","updateDimensions","rect","containerWidth","containerHeight","widthBasedSize","heightBasedSize","baseFontSize","clampedFontSize","measuredWidth","effectiveWidth","timer","resizeObserver","label","Icon","infoCircleIcon","formatVariance"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"chart-kpi-text-iwlaSQCi.js","sources":["../../../src/client/components/charts/KpiText.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect } from 'react'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport DataHistogram from '../DataHistogram'\nimport type { ChartProps } from '../../types'\n\nconst KpiText = React.memo(function KpiText({ \n data, \n chartConfig, \n displayConfig = {},\n height = \"100%\",\n colorPalette\n}: ChartProps) {\n const [fontSize, setFontSize] = useState(28)\n const [textWidth, setTextWidth] = useState(0)\n const containerRef = useRef<HTMLDivElement>(null)\n const textRef = 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 // Calculate font size based on container dimensions\n // For KPI Text displays, text might be longer so use more conservative sizing\n // Also reserve space for potential statistics display\n const widthBasedSize = containerWidth / 8\n const heightBasedSize = containerHeight / 5\n const baseFontSize = Math.min(widthBasedSize, heightBasedSize)\n const clampedFontSize = Math.max(18, Math.min(baseFontSize, 80))\n setFontSize(clampedFontSize)\n }\n }\n \n // Measure the text width after font size is set\n if (textRef.current) {\n const textRect = textRef.current.getBoundingClientRect()\n setTextWidth(textRect.width)\n }\n }\n\n // Initial calculation after a short delay to ensure the container is fully rendered\n const timer = setTimeout(updateDimensions, 100)\n \n const resizeObserver = new ResizeObserver(() => {\n // Debounce the resize updates\n clearTimeout(timer)\n setTimeout(updateDimensions, 50)\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=\"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\">No data points to display</div>\n </div>\n </div>\n )\n }\n\n // Extract value field from chart config - handle both string and array formats\n let valueFields: string[] = []\n if (chartConfig?.yAxis) {\n // Handle both string and array formats\n if (typeof chartConfig.yAxis === 'string') {\n valueFields = [chartConfig.yAxis]\n } else if (Array.isArray(chartConfig.yAxis)) {\n valueFields = chartConfig.yAxis\n }\n }\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=\"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 fields configured</div>\n </div>\n </div>\n )\n }\n\n const valueField = valueFields[0] // Use first measure field\n \n // Debug logging (remove in production)\n \n // Extract values for the selected field\n const values = data\n .map(row => {\n // Try direct field access first\n if (row[valueField] !== undefined) {\n return row[valueField]\n }\n \n // If not found, try finding the first available field as fallback\n const availableFields = Object.keys(row)\n if (availableFields.length > 0) {\n // Field not found, using fallback\n return row[availableFields[0]]\n }\n \n return undefined\n })\n .filter(val => val !== null && val !== undefined)\n \n\n if (values.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-warning-bg)',\n color: 'var(--dc-warning)',\n borderColor: 'var(--dc-warning-border)'\n }}\n >\n <div className=\"text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No valid data</div>\n <div className=\"dc:text-xs\">All values are null or invalid</div>\n </div>\n </div>\n )\n }\n\n // Calculate statistics for numeric values\n const numericValues = values\n .map(val => Number(val))\n .filter(val => !isNaN(val))\n\n let mainValue: any\n let min: number | null = null\n let max: number | null = null\n let showStats = false\n\n if (numericValues.length > 0) {\n if (values.length === 1) {\n mainValue = values[0]\n } else {\n // Calculate average for multiple numeric values\n const sum = numericValues.reduce((acc, val) => acc + val, 0)\n const avg = sum / numericValues.length\n mainValue = avg\n min = Math.min(...numericValues)\n max = Math.max(...numericValues)\n showStats = true\n }\n } else {\n // Non-numeric values - just use the first one or concatenate if multiple\n mainValue = values.length === 1 ? values[0] : values.join(', ')\n }\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 ?? 2\n\n if (Math.abs(value) >= 1e9) {\n return (value / 1e9).toFixed(decimals) + 'B'\n } else if (Math.abs(value) >= 1e6) {\n return (value / 1e6).toFixed(decimals) + 'M'\n } else if (Math.abs(value) >= 1e3) {\n return (value / 1e3).toFixed(decimals) + 'K'\n } else {\n return value.toFixed(decimals)\n }\n }\n\n // Process template string\n const processTemplate = (template: string, value: any): string => {\n try {\n // Create template variables\n const templateVars = {\n value: typeof value === 'number' ? formatNumber(value) : String(value),\n rawValue: value,\n field: valueField,\n fieldLabel: getFieldLabel(valueField),\n min: min !== null ? formatNumber(min) : '',\n max: max !== null ? formatNumber(max) : '',\n count: values.length\n }\n\n // Simple template replacement using ${variable} syntax\n return template.replace(/\\$\\{(\\w+)\\}/g, (match, varName) => {\n if (varName in templateVars) {\n return String(templateVars[varName as keyof typeof templateVars])\n }\n return match\n })\n } catch {\n // Error processing template\n return String(value)\n }\n }\n\n const template = displayConfig.template || '${fieldLabel}: ${value}'\n const displayText = processTemplate(template, mainValue)\n\n // Get color from palette by index, default to first color in palette\n const getValueColor = (): string => {\n if (displayConfig.valueColorIndex !== undefined && colorPalette?.colors) {\n const colorIndex = displayConfig.valueColorIndex\n if (colorIndex >= 0 && colorIndex < colorPalette.colors.length) {\n return colorPalette.colors[colorIndex]\n }\n }\n // Default to first color in palette if available, otherwise fallback to dark gray\n return colorPalette?.colors?.[0] || '#1f2937'\n }\n\n const valueColor = getValueColor()\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 {/* Main KPI Text */}\n <div \n ref={textRef}\n className=\"dc:font-bold dc:leading-tight text-center\"\n style={{ \n fontSize: `${fontSize}px`,\n color: valueColor \n }}\n >\n {displayText}\n </div>\n\n {/* Data Histogram for multiple values */}\n {showStats && min !== null && max !== null && (\n <div className=\"dc:mt-4\">\n <DataHistogram\n values={values}\n min={min}\n max={max}\n color={valueColor}\n formatValue={formatNumber}\n height={24}\n width={textWidth || 200}\n />\n </div>\n )}\n </div>\n )\n})\n\nexport default KpiText"],"names":["KpiText","React","data","chartConfig","displayConfig","height","colorPalette","fontSize","setFontSize","useState","textWidth","setTextWidth","containerRef","useRef","textRef","getFieldLabel","useCubeFieldLabel","useEffect","updateDimensions","rect","containerWidth","containerHeight","widthBasedSize","heightBasedSize","baseFontSize","clampedFontSize","textRect","timer","resizeObserver","jsx","jsxs","valueFields","valueField","values","row","availableFields","val","numericValues","mainValue","min","max","showStats","acc","formatNumber","value","decimals","processTemplate","template","templateVars","match","varName","displayText","valueColor","colorIndex","DataHistogram"],"mappings":"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"}