drizzle-cube 0.2.2 → 0.2.3

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 (108) hide show
  1. package/dist/client/charts.js +11 -11
  2. package/dist/client/chunks/chart-activitygridchart-BzL97Vnm.js +2510 -0
  3. package/dist/client/chunks/chart-activitygridchart-BzL97Vnm.js.map +1 -0
  4. package/dist/client/chunks/chart-activitygridchart-config-48WqIofo.js +230 -0
  5. package/dist/client/chunks/chart-activitygridchart-config-48WqIofo.js.map +1 -0
  6. package/dist/client/chunks/{chart-areachart-C_luzpU9.js → chart-areachart-BtH3SvAM.js} +3 -3
  7. package/dist/client/chunks/{chart-areachart-C_luzpU9.js.map → chart-areachart-BtH3SvAM.js.map} +1 -1
  8. package/dist/client/chunks/{chart-areachart-config-InZgxubz.js → chart-areachart-config-D8taXdn1.js} +5 -6
  9. package/dist/client/chunks/chart-areachart-config-D8taXdn1.js.map +1 -0
  10. package/dist/client/chunks/{chart-barchart-CJzMunji.js → chart-barchart-DX6DIoIp.js} +4 -4
  11. package/dist/client/chunks/{chart-barchart-CJzMunji.js.map → chart-barchart-DX6DIoIp.js.map} +1 -1
  12. package/dist/client/chunks/{chart-barchart-config-DxatOnVV.js → chart-barchart-config-DjRZBtLb.js} +5 -6
  13. package/dist/client/chunks/chart-barchart-config-DjRZBtLb.js.map +1 -0
  14. package/dist/client/chunks/{chart-bubblechart-DPyYu4B-.js → chart-bubblechart-Lj_PnppP.js} +2 -2
  15. package/dist/client/chunks/{chart-bubblechart-DPyYu4B-.js.map → chart-bubblechart-Lj_PnppP.js.map} +1 -1
  16. package/dist/client/chunks/{chart-bubblechart-config-CcZTMTCx.js → chart-bubblechart-config-C6ck2qmg.js} +5 -6
  17. package/dist/client/chunks/chart-bubblechart-config-C6ck2qmg.js.map +1 -0
  18. package/dist/client/chunks/{chart-charttooltip-CyhJAnW_.js → chart-charttooltip-D9BKscsw.js} +2 -2
  19. package/dist/client/chunks/{chart-charttooltip-CyhJAnW_.js.map → chart-charttooltip-D9BKscsw.js.map} +1 -1
  20. package/dist/client/chunks/{chart-datatable-xonQ9udl.js → chart-datatable-BaECYDn2.js} +2 -2
  21. package/dist/client/chunks/{chart-datatable-xonQ9udl.js.map → chart-datatable-BaECYDn2.js.map} +1 -1
  22. package/dist/client/chunks/{chart-datatable-config-D7mojhYA.js → chart-datatable-config-B5z0SlOJ.js} +5 -6
  23. package/dist/client/chunks/chart-datatable-config-B5z0SlOJ.js.map +1 -0
  24. package/dist/client/chunks/{chart-kpidelta-DTq92DyT.js → chart-kpidelta-Bx-stIIk.js} +23 -23
  25. package/dist/client/chunks/{chart-kpidelta-DTq92DyT.js.map → chart-kpidelta-Bx-stIIk.js.map} +1 -1
  26. package/dist/client/chunks/{chart-kpidelta-config-icJXuFxe.js → chart-kpidelta-config-CnZfi_P2.js} +5 -6
  27. package/dist/client/chunks/chart-kpidelta-config-CnZfi_P2.js.map +1 -0
  28. package/dist/client/chunks/{chart-kpinumber-CiOvJAlz.js → chart-kpinumber-BrXgyKGP.js} +23 -23
  29. package/dist/client/chunks/{chart-kpinumber-CiOvJAlz.js.map → chart-kpinumber-BrXgyKGP.js.map} +1 -1
  30. package/dist/client/chunks/{chart-kpinumber-config-T39g03ud.js → chart-kpinumber-config-CVikHojM.js} +5 -6
  31. package/dist/client/chunks/chart-kpinumber-config-CVikHojM.js.map +1 -0
  32. package/dist/client/chunks/{chart-kpitext-D5kNrcSl.js → chart-kpitext-_2tnYN_z.js} +3 -3
  33. package/dist/client/chunks/{chart-kpitext-D5kNrcSl.js.map → chart-kpitext-_2tnYN_z.js.map} +1 -1
  34. package/dist/client/chunks/{chart-kpitext-config-BbJGXAVk.js → chart-kpitext-config-B2MovXSw.js} +5 -6
  35. package/dist/client/chunks/chart-kpitext-config-B2MovXSw.js.map +1 -0
  36. package/dist/client/chunks/{chart-linechart-4AF7Q_zF.js → chart-linechart-DBdIULsM.js} +4 -4
  37. package/dist/client/chunks/{chart-linechart-4AF7Q_zF.js.map → chart-linechart-DBdIULsM.js.map} +1 -1
  38. package/dist/client/chunks/{chart-linechart-config-BS1qVC8K.js → chart-linechart-config-CVBVB7nS.js} +5 -6
  39. package/dist/client/chunks/chart-linechart-config-CVBVB7nS.js.map +1 -0
  40. package/dist/client/chunks/{chart-markdownchart-config-DMCD8phf.js → chart-markdownchart-config-Cq5Sxrgq.js} +5 -6
  41. package/dist/client/chunks/chart-markdownchart-config-Cq5Sxrgq.js.map +1 -0
  42. package/dist/client/chunks/{chart-piechart-Bf4TMIJP.js → chart-piechart-CzesCpZ_.js} +3 -3
  43. package/dist/client/chunks/{chart-piechart-Bf4TMIJP.js.map → chart-piechart-CzesCpZ_.js.map} +1 -1
  44. package/dist/client/chunks/{chart-piechart-config-6ZK8XaSX.js → chart-piechart-config-Bdd31Gdi.js} +5 -6
  45. package/dist/client/chunks/chart-piechart-config-Bdd31Gdi.js.map +1 -0
  46. package/dist/client/chunks/{chart-radarchart-DYar9VDH.js → chart-radarchart-B3WUEiTh.js} +3 -3
  47. package/dist/client/chunks/{chart-radarchart-DYar9VDH.js.map → chart-radarchart-B3WUEiTh.js.map} +1 -1
  48. package/dist/client/chunks/{chart-radarchart-config-CCWOkkwO.js → chart-radarchart-config-BWyfn0ij.js} +5 -6
  49. package/dist/client/chunks/chart-radarchart-config-BWyfn0ij.js.map +1 -0
  50. package/dist/client/chunks/{chart-radialbarchart-BzRlxNfh.js → chart-radialbarchart-DBiRcLd9.js} +3 -3
  51. package/dist/client/chunks/{chart-radialbarchart-BzRlxNfh.js.map → chart-radialbarchart-DBiRcLd9.js.map} +1 -1
  52. package/dist/client/chunks/{chart-radialbarchart-config-DicXYrMW.js → chart-radialbarchart-config-lU2_NQCD.js} +5 -6
  53. package/dist/client/chunks/chart-radialbarchart-config-lU2_NQCD.js.map +1 -0
  54. package/dist/client/chunks/{chart-scatterchart-DNVKc7nQ.js → chart-scatterchart-C_D9OqA1.js} +2 -2
  55. package/dist/client/chunks/{chart-scatterchart-DNVKc7nQ.js.map → chart-scatterchart-C_D9OqA1.js.map} +1 -1
  56. package/dist/client/chunks/{chart-scatterchart-config-DgYa-5vm.js → chart-scatterchart-config-DtLnut1M.js} +5 -6
  57. package/dist/client/chunks/chart-scatterchart-config-DtLnut1M.js.map +1 -0
  58. package/dist/client/chunks/{chart-treemapchart-CworQneQ.js → chart-treemapchart-DJqy3Sy7.js} +3 -3
  59. package/dist/client/chunks/{chart-treemapchart-CworQneQ.js.map → chart-treemapchart-DJqy3Sy7.js.map} +1 -1
  60. package/dist/client/chunks/{chart-treemapchart-config-DGhteyBe.js → chart-treemapchart-config-BEiIWQnR.js} +5 -6
  61. package/dist/client/chunks/chart-treemapchart-config-BEiIWQnR.js.map +1 -0
  62. package/dist/client/chunks/{charts-Cjl5gNIQ.js → charts-DFbr7EGW.js} +45 -45
  63. package/dist/client/chunks/{charts-Cjl5gNIQ.js.map → charts-DFbr7EGW.js.map} +1 -1
  64. package/dist/client/chunks/icons-D6z-_9sa.js +1400 -0
  65. package/dist/client/chunks/icons-D6z-_9sa.js.map +1 -0
  66. package/dist/client/chunks/index-_pVwmFVW.js +551 -0
  67. package/dist/client/chunks/index-_pVwmFVW.js.map +1 -0
  68. package/dist/client/components.js +3149 -3132
  69. package/dist/client/components.js.map +1 -1
  70. package/dist/client/hooks.js +5 -5
  71. package/dist/client/icons/defaultIcons.d.ts +5 -0
  72. package/dist/client/icons/index.d.ts +22 -0
  73. package/dist/client/icons/registry.d.ts +58 -0
  74. package/dist/client/icons/types.d.ts +111 -0
  75. package/dist/client/icons.d.ts +2 -0
  76. package/dist/client/icons.js +15 -0
  77. package/dist/client/icons.js.map +1 -0
  78. package/dist/client/index.d.ts +2 -0
  79. package/dist/client/index.js +44 -32
  80. package/dist/client/index.js.map +1 -1
  81. package/dist/client/providers/CubeProvider.d.ts +1 -1
  82. package/dist/client/providers.js +1 -1
  83. package/dist/client/styles.css +1 -1
  84. package/dist/client/types.d.ts +1 -0
  85. package/dist/client-bundle-stats.html +1 -1
  86. package/package.json +21 -9
  87. package/dist/client/chunks/chart-activitygridchart--4a5X5eV.js +0 -3713
  88. package/dist/client/chunks/chart-activitygridchart--4a5X5eV.js.map +0 -1
  89. package/dist/client/chunks/chart-activitygridchart-config-D9CgNH02.js +0 -51
  90. package/dist/client/chunks/chart-activitygridchart-config-D9CgNH02.js.map +0 -1
  91. package/dist/client/chunks/chart-areachart-config-InZgxubz.js.map +0 -1
  92. package/dist/client/chunks/chart-barchart-config-DxatOnVV.js.map +0 -1
  93. package/dist/client/chunks/chart-bubblechart-config-CcZTMTCx.js.map +0 -1
  94. package/dist/client/chunks/chart-datatable-config-D7mojhYA.js.map +0 -1
  95. package/dist/client/chunks/chart-kpidelta-config-icJXuFxe.js.map +0 -1
  96. package/dist/client/chunks/chart-kpinumber-config-T39g03ud.js.map +0 -1
  97. package/dist/client/chunks/chart-kpitext-config-BbJGXAVk.js.map +0 -1
  98. package/dist/client/chunks/chart-linechart-config-BS1qVC8K.js.map +0 -1
  99. package/dist/client/chunks/chart-markdownchart-config-DMCD8phf.js.map +0 -1
  100. package/dist/client/chunks/chart-piechart-config-6ZK8XaSX.js.map +0 -1
  101. package/dist/client/chunks/chart-radarchart-config-CCWOkkwO.js.map +0 -1
  102. package/dist/client/chunks/chart-radialbarchart-config-DicXYrMW.js.map +0 -1
  103. package/dist/client/chunks/chart-scatterchart-config-DgYa-5vm.js.map +0 -1
  104. package/dist/client/chunks/chart-treemapchart-config-DGhteyBe.js.map +0 -1
  105. package/dist/client/chunks/icons-D-n_woAP.js +0 -2386
  106. package/dist/client/chunks/icons-D-n_woAP.js.map +0 -1
  107. package/dist/client/chunks/index-Cn50vSlx.js +0 -8707
  108. package/dist/client/chunks/index-Cn50vSlx.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"chart-kpidelta-DTq92DyT.js","sources":["../../../src/client/utils/periodUtils.ts","../../../src/client/components/charts/KpiDelta.tsx"],"sourcesContent":["/**\n * Utility functions for detecting incomplete time periods in KPI charts\n */\n\n/**\n * Get the end date of a period based on granularity\n * @param date - The date within the period\n * @param granularity - The time granularity (day, week, month, quarter, year)\n * @returns The end date of the period (end of day)\n */\nexport function getPeriodEndDate(date: Date, granularity: string): Date {\n const endDate = new Date(date)\n\n switch (granularity.toLowerCase()) {\n case 'day':\n // End of the same day\n endDate.setHours(23, 59, 59, 999)\n break\n\n case 'week':\n // End of the week (Saturday, assuming week starts Sunday)\n const dayOfWeek = endDate.getDay()\n const daysUntilSaturday = 6 - dayOfWeek\n endDate.setDate(endDate.getDate() + daysUntilSaturday)\n endDate.setHours(23, 59, 59, 999)\n break\n\n 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 case 'year':\n // December 31st\n endDate.setMonth(11, 31) // December 31\n endDate.setHours(23, 59, 59, 999)\n break\n\n default:\n // Unknown granularity - treat as complete\n endDate.setHours(23, 59, 59, 999)\n }\n\n return endDate\n}\n\n/**\n * Check if the last period in the data is complete based on granularity\n * @param lastDataPoint - The last data point in the sorted dataset\n * @param timeDimensionField - The field name containing the time value\n * @param granularity - The time granularity\n * @returns true if the period is complete, false if it's incomplete\n */\nexport function isLastPeriodComplete(\n lastDataPoint: any,\n timeDimensionField: string,\n granularity: string\n): boolean {\n if (!lastDataPoint || !timeDimensionField || !granularity) {\n return true // Assume complete if we can't determine\n }\n\n const timeValue = lastDataPoint[timeDimensionField]\n if (!timeValue) {\n return true // Assume complete if no time value\n }\n\n // Parse the time value\n const date = new Date(timeValue)\n if (isNaN(date.getTime())) {\n return true // Assume complete if invalid date\n }\n\n // Get the end of this period\n const periodEnd = getPeriodEndDate(date, granularity)\n\n // Compare with current time\n const now = new Date()\n\n // If the period end is in the future, the period is incomplete\n return periodEnd <= now\n}\n\n/**\n * Extract granularity from a query object\n * @param queryObject - The CubeQuery object\n * @param dimensionField - Optional specific dimension field to match\n * @returns The granularity string or null if not found\n */\nexport function getQueryGranularity(\n queryObject: any,\n dimensionField?: string\n): string | null {\n if (!queryObject?.timeDimensions || queryObject.timeDimensions.length === 0) {\n return null\n }\n\n // If a specific dimension field is provided, try to find its granularity\n if (dimensionField) {\n const matchingDim = queryObject.timeDimensions.find((td: any) =>\n td.dimension === dimensionField ||\n td.dimension?.includes(dimensionField) ||\n dimensionField?.includes(td.dimension)\n )\n if (matchingDim?.granularity) {\n return matchingDim.granularity\n }\n }\n\n // Fallback to first time dimension's granularity\n const firstTimeDim = queryObject.timeDimensions[0]\n return firstTimeDim?.granularity || null\n}\n\n/**\n * Filter data to exclude incomplete or last period\n * @param data - The data array sorted by time\n * @param timeDimensionField - The field containing time values\n * @param queryObject - The query object containing timeDimensions\n * @param useLastCompletePeriod - Whether to check for incomplete periods\n * @param skipLastPeriod - Whether to always skip the last period (overrides useLastCompletePeriod)\n * @returns Object with filtered data and whether filtering was applied\n */\nexport function filterIncompletePeriod(\n data: any[],\n timeDimensionField: string | undefined,\n queryObject: any,\n useLastCompletePeriod: boolean,\n skipLastPeriod: boolean = false\n): { filteredData: any[]; excludedIncompletePeriod: boolean; skippedLastPeriod: boolean; granularity: string | null } {\n // Default return - no filtering\n const noFilter = {\n filteredData: data,\n excludedIncompletePeriod: false,\n skippedLastPeriod: false,\n granularity: null\n }\n\n // Need at least 2 data points to filter\n if (data.length < 2) {\n return noFilter\n }\n\n const granularity = getQueryGranularity(queryObject, timeDimensionField)\n\n // If skipLastPeriod is enabled, always skip the last period\n if (skipLastPeriod) {\n return {\n filteredData: data.slice(0, -1),\n excludedIncompletePeriod: false,\n skippedLastPeriod: true,\n granularity\n }\n }\n\n // Skip incomplete period check if feature is disabled\n if (!useLastCompletePeriod) {\n return { ...noFilter, granularity }\n }\n\n // Skip if no time dimension field\n if (!timeDimensionField) {\n return { ...noFilter, granularity }\n }\n\n // Skip if no time dimensions in query\n if (!queryObject?.timeDimensions || queryObject.timeDimensions.length === 0) {\n return { ...noFilter, granularity }\n }\n\n if (!granularity) {\n return noFilter\n }\n\n // Check if last period is incomplete\n const lastRow = data[data.length - 1]\n if (!isLastPeriodComplete(lastRow, timeDimensionField, granularity)) {\n return {\n filteredData: data.slice(0, -1),\n excludedIncompletePeriod: true,\n skippedLastPeriod: false,\n granularity\n }\n }\n\n return { ...noFilter, granularity }\n}\n","import { useState, useRef, useEffect } from \"react\";\nimport { Icon } from \"@iconify/react\";\nimport infoCircleIcon from \"@iconify-icons/tabler/info-circle\";\nimport { useCubeContext } from \"../../providers/CubeProvider\";\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=\"flex items-center justify-center bg-gray-50 rounded-sm border\"\n style={{ width: `${width}px`, height: `${height}px` }}\n >\n <span className=\"text-xs text-gray-500\">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=\"flex items-center space-x-2\">\n {/* Histogram bars */}\n <div\n className=\"relative\"\n style={{\n width: `${width}px`,\n height: `${height}px`,\n }}\n >\n {/* Zero line (represents current value) */}\n <div\n className=\"absolute left-0 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=\"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=\"flex flex-col justify-between 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\nexport default 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 const { getFieldLabel } = useCubeContext();\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=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">No data available</div>\n <div className=\"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=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">Configuration Error</div>\n <div className=\"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=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">Insufficient Data</div>\n <div className=\"text-xs\">\n Delta calculation requires at least 2 data points\n </div>\n <div className=\"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=\"flex flex-col items-center justify-center w-full h-full 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 font-bold text-center mb-2 flex items-center justify-center 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=\"cursor-help\"\n >\n <Icon\n icon={infoCircleIcon}\n className=\"w-4 h-4 text-dc-text-muted opacity-70\"\n />\n </span>\n )}\n </div>\n\n {/* Main KPI Value and Delta */}\n <div className=\"flex items-center justify-center space-x-4 mb-2\">\n {/* Main KPI Value */}\n <div\n ref={valueRef}\n className=\"font-bold 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=\"flex items-center space-x-1\">\n {/* Arrow */}\n <div\n className=\"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=\"font-bold 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=\"font-semibold 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 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=\"mt-2 w-full flex justify-center 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"],"names":["getPeriodEndDate","date","granularity","endDate","daysUntilSaturday","currentMonth","quarterEndMonth","isLastPeriodComplete","lastDataPoint","timeDimensionField","timeValue","getQueryGranularity","queryObject","dimensionField","matchingDim","td","filterIncompletePeriod","data","useLastCompletePeriod","skipLastPeriod","noFilter","lastRow","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","chartConfig","displayConfig","colorPalette","fontSize","setFontSize","useState","textWidth","setTextWidth","containerRef","useRef","valueRef","getFieldLabel","useCubeContext","useEffect","updateDimensions","rect","containerWidth","containerHeight","widthBasedSize","heightBasedSize","baseFontSize","clampedFontSize","measuredWidth","maxHistogramWidth","effectiveWidth","timer","resizeObserver","valueFields","dimensionFields","valueField","sortedData","a","b","aVal","bVal","filteredData","excludedIncompletePeriod","skippedLastPeriod","row","val","secondLastValue","absoluteChange","percentageChange","isPositiveChange","formatNumber","decimals","prefix","formattedValue","getPositiveColor","colorIndex","getNegativeColor","currentColor","label","Icon","infoCircleIcon"],"mappings":"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"}
1
+ {"version":3,"file":"chart-kpidelta-Bx-stIIk.js","sources":["../../../src/client/utils/periodUtils.ts","../../../src/client/components/charts/KpiDelta.tsx"],"sourcesContent":["/**\n * Utility functions for detecting incomplete time periods in KPI charts\n */\n\n/**\n * Get the end date of a period based on granularity\n * @param date - The date within the period\n * @param granularity - The time granularity (day, week, month, quarter, year)\n * @returns The end date of the period (end of day)\n */\nexport function getPeriodEndDate(date: Date, granularity: string): Date {\n const endDate = new Date(date)\n\n switch (granularity.toLowerCase()) {\n case 'day':\n // End of the same day\n endDate.setHours(23, 59, 59, 999)\n break\n\n case 'week':\n // End of the week (Saturday, assuming week starts Sunday)\n const dayOfWeek = endDate.getDay()\n const daysUntilSaturday = 6 - dayOfWeek\n endDate.setDate(endDate.getDate() + daysUntilSaturday)\n endDate.setHours(23, 59, 59, 999)\n break\n\n 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 case 'year':\n // December 31st\n endDate.setMonth(11, 31) // December 31\n endDate.setHours(23, 59, 59, 999)\n break\n\n default:\n // Unknown granularity - treat as complete\n endDate.setHours(23, 59, 59, 999)\n }\n\n return endDate\n}\n\n/**\n * Check if the last period in the data is complete based on granularity\n * @param lastDataPoint - The last data point in the sorted dataset\n * @param timeDimensionField - The field name containing the time value\n * @param granularity - The time granularity\n * @returns true if the period is complete, false if it's incomplete\n */\nexport function isLastPeriodComplete(\n lastDataPoint: any,\n timeDimensionField: string,\n granularity: string\n): boolean {\n if (!lastDataPoint || !timeDimensionField || !granularity) {\n return true // Assume complete if we can't determine\n }\n\n const timeValue = lastDataPoint[timeDimensionField]\n if (!timeValue) {\n return true // Assume complete if no time value\n }\n\n // Parse the time value\n const date = new Date(timeValue)\n if (isNaN(date.getTime())) {\n return true // Assume complete if invalid date\n }\n\n // Get the end of this period\n const periodEnd = getPeriodEndDate(date, granularity)\n\n // Compare with current time\n const now = new Date()\n\n // If the period end is in the future, the period is incomplete\n return periodEnd <= now\n}\n\n/**\n * Extract granularity from a query object\n * @param queryObject - The CubeQuery object\n * @param dimensionField - Optional specific dimension field to match\n * @returns The granularity string or null if not found\n */\nexport function getQueryGranularity(\n queryObject: any,\n dimensionField?: string\n): string | null {\n if (!queryObject?.timeDimensions || queryObject.timeDimensions.length === 0) {\n return null\n }\n\n // If a specific dimension field is provided, try to find its granularity\n if (dimensionField) {\n const matchingDim = queryObject.timeDimensions.find((td: any) =>\n td.dimension === dimensionField ||\n td.dimension?.includes(dimensionField) ||\n dimensionField?.includes(td.dimension)\n )\n if (matchingDim?.granularity) {\n return matchingDim.granularity\n }\n }\n\n // Fallback to first time dimension's granularity\n const firstTimeDim = queryObject.timeDimensions[0]\n return firstTimeDim?.granularity || null\n}\n\n/**\n * Filter data to exclude incomplete or last period\n * @param data - The data array sorted by time\n * @param timeDimensionField - The field containing time values\n * @param queryObject - The query object containing timeDimensions\n * @param useLastCompletePeriod - Whether to check for incomplete periods\n * @param skipLastPeriod - Whether to always skip the last period (overrides useLastCompletePeriod)\n * @returns Object with filtered data and whether filtering was applied\n */\nexport function filterIncompletePeriod(\n data: any[],\n timeDimensionField: string | undefined,\n queryObject: any,\n useLastCompletePeriod: boolean,\n skipLastPeriod: boolean = false\n): { filteredData: any[]; excludedIncompletePeriod: boolean; skippedLastPeriod: boolean; granularity: string | null } {\n // Default return - no filtering\n const noFilter = {\n filteredData: data,\n excludedIncompletePeriod: false,\n skippedLastPeriod: false,\n granularity: null\n }\n\n // Need at least 2 data points to filter\n if (data.length < 2) {\n return noFilter\n }\n\n const granularity = getQueryGranularity(queryObject, timeDimensionField)\n\n // If skipLastPeriod is enabled, always skip the last period\n if (skipLastPeriod) {\n return {\n filteredData: data.slice(0, -1),\n excludedIncompletePeriod: false,\n skippedLastPeriod: true,\n granularity\n }\n }\n\n // Skip incomplete period check if feature is disabled\n if (!useLastCompletePeriod) {\n return { ...noFilter, granularity }\n }\n\n // Skip if no time dimension field\n if (!timeDimensionField) {\n return { ...noFilter, granularity }\n }\n\n // Skip if no time dimensions in query\n if (!queryObject?.timeDimensions || queryObject.timeDimensions.length === 0) {\n return { ...noFilter, granularity }\n }\n\n if (!granularity) {\n return noFilter\n }\n\n // Check if last period is incomplete\n const lastRow = data[data.length - 1]\n if (!isLastPeriodComplete(lastRow, timeDimensionField, granularity)) {\n return {\n filteredData: data.slice(0, -1),\n excludedIncompletePeriod: true,\n skippedLastPeriod: false,\n granularity\n }\n }\n\n return { ...noFilter, granularity }\n}\n","import { useState, useRef, useEffect } from \"react\";\nimport { Icon } from \"@iconify/react\";\nimport infoCircleIcon from \"@iconify-icons/tabler/info-circle\";\nimport { useCubeContext } from \"../../providers/CubeProvider\";\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=\"flex items-center justify-center bg-gray-50 rounded-sm border\"\n style={{ width: `${width}px`, height: `${height}px` }}\n >\n <span className=\"text-xs text-gray-500\">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=\"flex items-center space-x-2\">\n {/* Histogram bars */}\n <div\n className=\"relative\"\n style={{\n width: `${width}px`,\n height: `${height}px`,\n }}\n >\n {/* Zero line (represents current value) */}\n <div\n className=\"absolute left-0 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=\"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=\"flex flex-col justify-between 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\nexport default 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 const { getFieldLabel } = useCubeContext();\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=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">No data available</div>\n <div className=\"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=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">Configuration Error</div>\n <div className=\"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=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">Insufficient Data</div>\n <div className=\"text-xs\">\n Delta calculation requires at least 2 data points\n </div>\n <div className=\"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=\"flex flex-col items-center justify-center w-full h-full 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 font-bold text-center mb-2 flex items-center justify-center 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=\"cursor-help\"\n >\n <Icon\n icon={infoCircleIcon}\n className=\"w-4 h-4 text-dc-text-muted opacity-70\"\n />\n </span>\n )}\n </div>\n\n {/* Main KPI Value and Delta */}\n <div className=\"flex items-center justify-center space-x-4 mb-2\">\n {/* Main KPI Value */}\n <div\n ref={valueRef}\n className=\"font-bold 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=\"flex items-center space-x-1\">\n {/* Arrow */}\n <div\n className=\"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=\"font-bold 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=\"font-semibold 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 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=\"mt-2 w-full flex justify-center 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"],"names":["getPeriodEndDate","date","granularity","endDate","daysUntilSaturday","currentMonth","quarterEndMonth","isLastPeriodComplete","lastDataPoint","timeDimensionField","timeValue","getQueryGranularity","queryObject","dimensionField","matchingDim","td","filterIncompletePeriod","data","useLastCompletePeriod","skipLastPeriod","noFilter","lastRow","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","chartConfig","displayConfig","colorPalette","fontSize","setFontSize","useState","textWidth","setTextWidth","containerRef","useRef","valueRef","getFieldLabel","useCubeContext","useEffect","updateDimensions","rect","containerWidth","containerHeight","widthBasedSize","heightBasedSize","baseFontSize","clampedFontSize","measuredWidth","maxHistogramWidth","effectiveWidth","timer","resizeObserver","valueFields","dimensionFields","valueField","sortedData","a","b","aVal","bVal","filteredData","excludedIncompletePeriod","skippedLastPeriod","row","val","secondLastValue","absoluteChange","percentageChange","isPositiveChange","formatNumber","decimals","prefix","formattedValue","getPositiveColor","colorIndex","getNegativeColor","currentColor","label","Icon","infoCircleIcon"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
- import { jsx as a } from "react/jsx-runtime";
2
- import { I as t, g as i } from "./icons-D-n_woAP.js";
3
- const s = {
4
- icon: ({ className: e }) => /* @__PURE__ */ a(t, { icon: i, className: e }),
1
+ import { g as a } from "./chart-activitygridchart-config-48WqIofo.js";
2
+ const i = {
3
+ icon: a("kpiDelta"),
5
4
  description: "Display change between latest and previous values with trend indicators",
6
5
  useCase: "Perfect for showing performance changes over time, such as revenue growth, user acquisition changes, or other metrics where the trend and delta are more important than the absolute value",
7
6
  dropZones: [
@@ -94,6 +93,6 @@ const s = {
94
93
  } : { isValid: !0 }
95
94
  };
96
95
  export {
97
- s as kpiDeltaConfig
96
+ i as kpiDeltaConfig
98
97
  };
99
- //# sourceMappingURL=chart-kpidelta-config-icJXuFxe.js.map
98
+ //# sourceMappingURL=chart-kpidelta-config-CnZfi_P2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-kpidelta-config-CnZfi_P2.js","sources":["../../../src/client/components/charts/KpiDelta.config.tsx"],"sourcesContent":["import type { ChartTypeConfig } from '../../charts/chartConfigs'\nimport { getChartTypeIcon } from '../../icons'\n\n/**\n * Configuration for the KPI Delta chart type\n */\nexport const kpiDeltaConfig: ChartTypeConfig = {\n icon: getChartTypeIcon('kpiDelta'),\n description: 'Display change between latest and previous values with trend indicators',\n useCase: 'Perfect for showing performance changes over time, such as revenue growth, user acquisition changes, or other metrics where the trend and delta are more important than the absolute value',\n dropZones: [\n {\n key: 'yAxis',\n label: 'Value',\n description: 'Measure to track changes for',\n mandatory: true,\n maxItems: 1,\n acceptTypes: ['measure'],\n emptyText: 'Drop a measure here'\n },\n {\n key: 'xAxis',\n label: 'Dimension (optional)',\n description: 'Dimension for ordering data (typically time)',\n mandatory: false,\n maxItems: 1,\n acceptTypes: ['dimension', 'timeDimension'],\n emptyText: 'Drop a dimension for ordering'\n }\n ],\n displayOptionsConfig: [\n {\n key: 'prefix',\n label: 'Prefix',\n type: 'string',\n placeholder: 'e.g., $, €, #',\n description: 'Text to display before the number'\n },\n {\n key: 'suffix',\n label: 'Suffix',\n type: 'string',\n placeholder: 'e.g., %, units, items',\n description: 'Text to display after the number'\n },\n {\n key: 'decimals',\n label: 'Decimal Places',\n type: 'number',\n defaultValue: 1,\n min: 0,\n max: 10,\n step: 1,\n description: 'Number of decimal places to display'\n },\n {\n key: 'positiveColorIndex',\n label: 'Positive Change Color',\n type: 'paletteColor',\n defaultValue: 2, // Typically green in most palettes\n description: 'Color for positive changes (increases)'\n },\n {\n key: 'negativeColorIndex',\n label: 'Negative Change Color', \n type: 'paletteColor',\n defaultValue: 3, // Typically red in most palettes\n description: 'Color for negative changes (decreases)'\n },\n {\n key: 'showHistogram',\n label: 'Show Variance Histogram',\n type: 'boolean',\n defaultValue: true,\n description: 'Display historical variance chart below the delta'\n },\n {\n key: 'useLastCompletePeriod',\n label: 'Use Last Complete Period',\n type: 'boolean',\n defaultValue: true,\n description: 'Exclude current incomplete period from delta calculation (e.g., partial week/month)'\n },\n {\n key: 'skipLastPeriod',\n label: 'Skip Last Period',\n type: 'boolean',\n defaultValue: false,\n description: 'Always exclude the last period regardless of completeness'\n }\n ],\n displayOptions: ['hideHeader'],\n validate: (config: any) => {\n if (!config.yAxis || (Array.isArray(config.yAxis) && config.yAxis.length === 0)) {\n return {\n isValid: false,\n message: 'A measure is required for KPI Delta charts'\n }\n }\n \n return { isValid: true }\n }\n}"],"names":["kpiDeltaConfig","getChartTypeIcon","config"],"mappings":";AAMO,MAAMA,IAAkC;AAAA,EAC7C,MAAMC,EAAiB,UAAU;AAAA,EACjC,aAAa;AAAA,EACb,SAAS;AAAA,EACT,WAAW;AAAA,IACT;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,aAAa;AAAA,MACb,WAAW;AAAA,MACX,UAAU;AAAA,MACV,aAAa,CAAC,SAAS;AAAA,MACvB,WAAW;AAAA,IAAA;AAAA,IAEb;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,aAAa;AAAA,MACb,WAAW;AAAA,MACX,UAAU;AAAA,MACV,aAAa,CAAC,aAAa,eAAe;AAAA,MAC1C,WAAW;AAAA,IAAA;AAAA,EACb;AAAA,EAEF,sBAAsB;AAAA,IACpB;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA,MACd,KAAK;AAAA,MACL,KAAK;AAAA,MACL,MAAM;AAAA,MACN,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA;AAAA,MACd,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA;AAAA,MACd,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA,MACd,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA,MACd,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA,MACd,aAAa;AAAA,IAAA;AAAA,EACf;AAAA,EAEF,gBAAgB,CAAC,YAAY;AAAA,EAC7B,UAAU,CAACC,MACL,CAACA,EAAO,SAAU,MAAM,QAAQA,EAAO,KAAK,KAAKA,EAAO,MAAM,WAAW,IACpE;AAAA,IACL,SAAS;AAAA,IACT,SAAS;AAAA,EAAA,IAIN,EAAE,SAAS,GAAA;AAEtB;"}
@@ -1,9 +1,9 @@
1
1
  import { jsxs as a, jsx as t } from "react/jsx-runtime";
2
- import { useState as k, useRef as E, useEffect as Z } from "react";
3
- import { I as q, f as ee } from "./icons-D-n_woAP.js";
4
- import { u as te } from "./chart-activitygridchart--4a5X5eV.js";
5
- import { p as se, f as ne, c as ie } from "./chart-areachart-C_luzpU9.js";
6
- import { f as re } from "./chart-kpidelta-DTq92DyT.js";
2
+ import { useState as W, useRef as E, useEffect as Z } from "react";
3
+ import { a6 as q, k as ee } from "./icons-D6z-_9sa.js";
4
+ import { u as te } from "./chart-activitygridchart-BzL97Vnm.js";
5
+ import { p as se, f as ne, c as ie } from "./chart-areachart-BtH3SvAM.js";
6
+ import { f as re } from "./chart-kpidelta-Bx-stIIk.js";
7
7
  function oe({
8
8
  values: m,
9
9
  min: o,
@@ -13,7 +13,7 @@ function oe({
13
13
  height: x = 32,
14
14
  formatValue: u = (r) => r.toString(),
15
15
  width: N,
16
- showAverageIndicator: H = !0,
16
+ showAverageIndicator: I = !0,
17
17
  targetValue: y
18
18
  }) {
19
19
  const r = new Array(s).fill(0), c = n - o;
@@ -38,7 +38,7 @@ function oe({
38
38
  },
39
39
  children: [
40
40
  r.map((l, f) => {
41
- const I = M > 0 ? l / M : 0, w = 0.1, V = l > 0 ? Math.max(w, I) : w;
41
+ const w = M > 0 ? l / M : 0, F = 0.1, V = l > 0 ? Math.max(F, w) : F;
42
42
  return /* @__PURE__ */ t(
43
43
  "div",
44
44
  {
@@ -46,7 +46,7 @@ function oe({
46
46
  style: {
47
47
  height: `${V * x}px`,
48
48
  backgroundColor: S,
49
- opacity: l > 0 ? 0.7 + I * 0.3 : 0.2
49
+ opacity: l > 0 ? 0.7 + w * 0.3 : 0.2
50
50
  // higher opacity for buckets with data
51
51
  },
52
52
  title: `${l} values in this range`
@@ -54,7 +54,7 @@ function oe({
54
54
  f
55
55
  );
56
56
  }),
57
- H && /* @__PURE__ */ t(
57
+ I && /* @__PURE__ */ t(
58
58
  "div",
59
59
  {
60
60
  className: "absolute top-0 bottom-0 pointer-events-none",
@@ -146,13 +146,13 @@ function le({
146
146
  height: s = "100%",
147
147
  colorPalette: x
148
148
  }) {
149
- const [u, N] = k(32), [H, y] = k(250), r = E(null), c = E(null), { getFieldLabel: M } = te();
149
+ const [u, N] = W(32), [I, y] = W(250), r = E(null), c = E(null), { getFieldLabel: M } = te();
150
150
  if (Z(() => {
151
151
  const e = () => {
152
152
  if (r.current) {
153
- const D = r.current.getBoundingClientRect(), j = D.width, W = D.height;
154
- if (j > 0 && W > 0) {
155
- const U = j / 5, C = W / 4, G = Math.min(U, C), J = Math.max(24, Math.min(G, 120));
153
+ const k = r.current.getBoundingClientRect(), j = k.width, D = k.height;
154
+ if (j > 0 && D > 0) {
155
+ const U = j / 5, C = D / 4, G = Math.min(U, C), J = Math.max(24, Math.min(G, 120));
156
156
  N(J), setTimeout(() => {
157
157
  if (c.current) {
158
158
  const Q = c.current.getBoundingClientRect().width, Y = Math.max(Q, Math.min(j * 0.6, 300));
@@ -207,12 +207,12 @@ function le({
207
207
  const d = e[g], p = i[g];
208
208
  return d < p ? -1 : d > p ? 1 : 0;
209
209
  }));
210
- const { useLastCompletePeriod: f = !0, skipLastPeriod: I = !1 } = n, {
211
- filteredData: w,
210
+ const { useLastCompletePeriod: f = !0, skipLastPeriod: w = !1 } = n, {
211
+ filteredData: F,
212
212
  excludedIncompletePeriod: V,
213
213
  skippedLastPeriod: T,
214
214
  granularity: A
215
- } = re(l, g, S, f, I), h = w.map((e) => {
215
+ } = re(l, g, S, f, w), h = F.map((e) => {
216
216
  if (e[b] !== void 0)
217
217
  return e[b];
218
218
  const i = Object.keys(e).filter(
@@ -272,9 +272,9 @@ function le({
272
272
  return x.colors[e];
273
273
  }
274
274
  return x?.colors?.[0] || "#1f2937";
275
- })(), L = se(n?.target || ""), z = L.length > 0 ? L[0] : null, F = z !== null ? ie(R, z) : null, P = () => {
276
- if (F === null) return "#6B7280";
277
- if (F >= 0) {
275
+ })(), L = se(n?.target || ""), z = L.length > 0 ? L[0] : null, H = z !== null ? ie(R, z) : null, P = () => {
276
+ if (H === null) return "#6B7280";
277
+ if (H >= 0) {
278
278
  const e = n.positiveColorIndex ?? 1;
279
279
  return x?.colors?.[e] || "#10B981";
280
280
  } else {
@@ -329,7 +329,7 @@ function le({
329
329
  children: $(R)
330
330
  }
331
331
  ),
332
- z !== null && F !== null && /* @__PURE__ */ a("div", { className: "flex flex-col items-start", children: [
332
+ z !== null && H !== null && /* @__PURE__ */ a("div", { className: "flex flex-col items-start", children: [
333
333
  /* @__PURE__ */ t(
334
334
  "div",
335
335
  {
@@ -339,7 +339,7 @@ function le({
339
339
  color: P(),
340
340
  lineHeight: "1.2"
341
341
  },
342
- children: ne(F, 1)
342
+ children: ne(H, 1)
343
343
  }
344
344
  ),
345
345
  /* @__PURE__ */ a(
@@ -379,7 +379,7 @@ function le({
379
379
  color: B,
380
380
  formatValue: $,
381
381
  height: 24,
382
- width: H,
382
+ width: I,
383
383
  targetValue: z || void 0
384
384
  }
385
385
  ) })
@@ -395,4 +395,4 @@ export {
395
395
  oe as D,
396
396
  ge as K
397
397
  };
398
- //# sourceMappingURL=chart-kpinumber-CiOvJAlz.js.map
398
+ //# sourceMappingURL=chart-kpinumber-BrXgyKGP.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chart-kpinumber-CiOvJAlz.js","sources":["../../../src/client/components/DataHistogram.tsx","../../../src/client/components/charts/KpiNumber.tsx"],"sourcesContent":["\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=\"flex flex-col items-center\">\n {/* Horizontal bars representing actual data distribution */}\n <div \n className=\"relative flex items-end justify-center 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=\"flex-1 rounded-t-sm transition-all duration-300 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=\"absolute top-0 bottom-0 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=\"absolute -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=\"absolute top-0 bottom-0 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=\"absolute -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=\"flex justify-between mt-2 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 mt-1 text-xs text-dc-text-muted\">\n Average of {values.length} values\n </div>\n </div>\n )\n}","import { useState, useRef, useEffect } from 'react'\nimport { Icon } from '@iconify/react'\nimport infoCircleIcon from '@iconify-icons/tabler/info-circle'\nimport { useCubeContext } from '../../providers/CubeProvider'\nimport DataHistogram from '../DataHistogram'\nimport { parseTargetValues, calculateVariance, formatVariance } from '../../utils/targetUtils'\nimport { filterIncompletePeriod } from '../../utils/periodUtils'\nimport type { ChartProps } from '../../types'\n\nexport default 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 const { getFieldLabel } = useCubeContext()\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 if (!data || data.length === 0) {\n return (\n <div\n className=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">No data available</div>\n <div className=\"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=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">Configuration Error</div>\n <div className=\"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 // Get time dimension field if present (for incomplete period filtering)\n const timeDimensionField = queryObject?.timeDimensions?.[0]?.dimension || undefined\n\n // Sort data by time dimension if available (for proper filtering)\n let sortedData = [...data]\n if (timeDimensionField) {\n sortedData = sortedData.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\n // Filter out incomplete or last period if enabled\n const { useLastCompletePeriod = true, skipLastPeriod = false } = displayConfig\n const {\n filteredData,\n excludedIncompletePeriod,\n skippedLastPeriod,\n granularity\n } = filterIncompletePeriod(sortedData, timeDimensionField, queryObject, useLastCompletePeriod, skipLastPeriod)\n\n // Use filtered data for calculations\n const dataToUse = filteredData\n\n // Extract values for the selected field\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 const values = rawValues\n .filter(val => val !== null && val !== undefined && !isNaN(Number(val)))\n .map(val => Number(val))\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=\"flex flex-col items-center justify-center w-full h-full 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 font-bold text-center 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=\"font-bold leading-none text-dc-text-muted\"\n style={{\n fontSize: `${fontSize}px`\n }}\n >\n —\n </div>\n\n <div className=\"text-xs text-dc-text-muted mt-2\">No data</div>\n </div>\n )\n }\n\n // Calculate statistics\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\n // Format number with appropriate units and decimals (no prefix/suffix here, handled separately)\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 const mainValue = values.length === 1 ? values[0] : avg\n const showStats = values.length > 1\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 // Process target values for variance calculation\n const targetValues = parseTargetValues(displayConfig?.target || '')\n const targetValue = targetValues.length > 0 ? targetValues[0] : null // Use first target value\n const variance = targetValue !== null ? calculateVariance(mainValue, targetValue) : null\n \n // Get colors for variance display (similar to KpiDelta)\n const getVarianceColor = (): 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 }\n\n return (\n <div \n ref={containerRef}\n className=\"flex flex-col items-center justify-center w-full h-full 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 font-bold text-center mb-3 flex items-center justify-center 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=\"cursor-help\"\n >\n <Icon icon={infoCircleIcon} className=\"w-4 h-4 text-dc-text-muted opacity-70\" />\n </span>\n )}\n </div>\n\n {/* Main KPI Value and Variance - Horizontal layout */}\n <div className=\"flex items-center justify-center gap-4 mb-3\">\n <div \n ref={valueRef}\n className=\"font-bold 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=\"flex flex-col items-start\">\n <div \n className=\"font-semibold\"\n style={{ \n fontSize: `${Math.max(12, fontSize * 0.3)}px`,\n color: getVarianceColor(),\n lineHeight: '1.2'\n }}\n >\n {formatVariance(variance, 1)}\n </div>\n <div\n className=\"text-dc-text-muted 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=\"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}"],"names":["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","data","chartConfig","displayConfig","queryObject","colorPalette","fontSize","setFontSize","useState","textWidth","setTextWidth","containerRef","useRef","valueRef","getFieldLabel","useCubeContext","useEffect","updateDimensions","rect","containerWidth","containerHeight","widthBasedSize","heightBasedSize","baseFontSize","clampedFontSize","measuredWidth","effectiveWidth","timer","resizeObserver","valueFields","valueField","timeDimensionField","sortedData","a","b","aVal","bVal","useLastCompletePeriod","skipLastPeriod","filteredData","excludedIncompletePeriod","skippedLastPeriod","granularity","filterIncompletePeriod","row","numericFields","key","avg","acc","formatNumber","decimals","prefix","formattedValue","mainValue","showStats","valueColor","colorIndex","targetValues","parseTargetValues","variance","calculateVariance","getVarianceColor","positiveIndex","negativeIndex","label","Icon","infoCircleIcon","formatVariance"],"mappings":"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"}
1
+ {"version":3,"file":"chart-kpinumber-BrXgyKGP.js","sources":["../../../src/client/components/DataHistogram.tsx","../../../src/client/components/charts/KpiNumber.tsx"],"sourcesContent":["\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=\"flex flex-col items-center\">\n {/* Horizontal bars representing actual data distribution */}\n <div \n className=\"relative flex items-end justify-center 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=\"flex-1 rounded-t-sm transition-all duration-300 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=\"absolute top-0 bottom-0 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=\"absolute -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=\"absolute top-0 bottom-0 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=\"absolute -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=\"flex justify-between mt-2 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 mt-1 text-xs text-dc-text-muted\">\n Average of {values.length} values\n </div>\n </div>\n )\n}","import { useState, useRef, useEffect } from 'react'\nimport { Icon } from '@iconify/react'\nimport infoCircleIcon from '@iconify-icons/tabler/info-circle'\nimport { useCubeContext } from '../../providers/CubeProvider'\nimport DataHistogram from '../DataHistogram'\nimport { parseTargetValues, calculateVariance, formatVariance } from '../../utils/targetUtils'\nimport { filterIncompletePeriod } from '../../utils/periodUtils'\nimport type { ChartProps } from '../../types'\n\nexport default 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 const { getFieldLabel } = useCubeContext()\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 if (!data || data.length === 0) {\n return (\n <div\n className=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">No data available</div>\n <div className=\"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=\"flex items-center justify-center w-full 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=\"text-sm font-semibold mb-1\">Configuration Error</div>\n <div className=\"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 // Get time dimension field if present (for incomplete period filtering)\n const timeDimensionField = queryObject?.timeDimensions?.[0]?.dimension || undefined\n\n // Sort data by time dimension if available (for proper filtering)\n let sortedData = [...data]\n if (timeDimensionField) {\n sortedData = sortedData.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\n // Filter out incomplete or last period if enabled\n const { useLastCompletePeriod = true, skipLastPeriod = false } = displayConfig\n const {\n filteredData,\n excludedIncompletePeriod,\n skippedLastPeriod,\n granularity\n } = filterIncompletePeriod(sortedData, timeDimensionField, queryObject, useLastCompletePeriod, skipLastPeriod)\n\n // Use filtered data for calculations\n const dataToUse = filteredData\n\n // Extract values for the selected field\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 const values = rawValues\n .filter(val => val !== null && val !== undefined && !isNaN(Number(val)))\n .map(val => Number(val))\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=\"flex flex-col items-center justify-center w-full h-full 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 font-bold text-center 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=\"font-bold leading-none text-dc-text-muted\"\n style={{\n fontSize: `${fontSize}px`\n }}\n >\n —\n </div>\n\n <div className=\"text-xs text-dc-text-muted mt-2\">No data</div>\n </div>\n )\n }\n\n // Calculate statistics\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\n // Format number with appropriate units and decimals (no prefix/suffix here, handled separately)\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 const mainValue = values.length === 1 ? values[0] : avg\n const showStats = values.length > 1\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 // Process target values for variance calculation\n const targetValues = parseTargetValues(displayConfig?.target || '')\n const targetValue = targetValues.length > 0 ? targetValues[0] : null // Use first target value\n const variance = targetValue !== null ? calculateVariance(mainValue, targetValue) : null\n \n // Get colors for variance display (similar to KpiDelta)\n const getVarianceColor = (): 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 }\n\n return (\n <div \n ref={containerRef}\n className=\"flex flex-col items-center justify-center w-full h-full 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 font-bold text-center mb-3 flex items-center justify-center 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=\"cursor-help\"\n >\n <Icon icon={infoCircleIcon} className=\"w-4 h-4 text-dc-text-muted opacity-70\" />\n </span>\n )}\n </div>\n\n {/* Main KPI Value and Variance - Horizontal layout */}\n <div className=\"flex items-center justify-center gap-4 mb-3\">\n <div \n ref={valueRef}\n className=\"font-bold 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=\"flex flex-col items-start\">\n <div \n className=\"font-semibold\"\n style={{ \n fontSize: `${Math.max(12, fontSize * 0.3)}px`,\n color: getVarianceColor(),\n lineHeight: '1.2'\n }}\n >\n {formatVariance(variance, 1)}\n </div>\n <div\n className=\"text-dc-text-muted 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=\"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}"],"names":["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","data","chartConfig","displayConfig","queryObject","colorPalette","fontSize","setFontSize","useState","textWidth","setTextWidth","containerRef","useRef","valueRef","getFieldLabel","useCubeContext","useEffect","updateDimensions","rect","containerWidth","containerHeight","widthBasedSize","heightBasedSize","baseFontSize","clampedFontSize","measuredWidth","effectiveWidth","timer","resizeObserver","valueFields","valueField","timeDimensionField","sortedData","a","b","aVal","bVal","useLastCompletePeriod","skipLastPeriod","filteredData","excludedIncompletePeriod","skippedLastPeriod","granularity","filterIncompletePeriod","row","numericFields","key","avg","acc","formatNumber","decimals","prefix","formattedValue","mainValue","showStats","valueColor","colorIndex","targetValues","parseTargetValues","variance","calculateVariance","getVarianceColor","positiveIndex","negativeIndex","label","Icon","infoCircleIcon","formatVariance"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
- import { jsx as t } from "react/jsx-runtime";
2
- import { I as a, h as r } from "./icons-D-n_woAP.js";
3
- const l = {
4
- icon: ({ className: e }) => /* @__PURE__ */ t(a, { icon: r, className: e }),
1
+ import { g as e } from "./chart-activitygridchart-config-48WqIofo.js";
2
+ const a = {
3
+ icon: e("kpiNumber"),
5
4
  description: "Display key performance indicators as large numbers",
6
5
  useCase: "Perfect for showing important metrics like revenue, user count, or other key business metrics in a prominent, easy-to-read format",
7
6
  dropZones: [
@@ -72,6 +71,6 @@ const l = {
72
71
  displayOptions: ["hideHeader"]
73
72
  };
74
73
  export {
75
- l as kpiNumberConfig
74
+ a as kpiNumberConfig
76
75
  };
77
- //# sourceMappingURL=chart-kpinumber-config-T39g03ud.js.map
76
+ //# sourceMappingURL=chart-kpinumber-config-CVikHojM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart-kpinumber-config-CVikHojM.js","sources":["../../../src/client/components/charts/KpiNumber.config.tsx"],"sourcesContent":["import type { ChartTypeConfig } from '../../charts/chartConfigs'\nimport { getChartTypeIcon } from '../../icons'\n\n/**\n * Configuration for the KPI Number chart type\n */\nexport const kpiNumberConfig: ChartTypeConfig = {\n icon: getChartTypeIcon('kpiNumber'),\n description: 'Display key performance indicators as large numbers',\n useCase: 'Perfect for showing important metrics like revenue, user count, or other key business metrics in a prominent, easy-to-read format',\n dropZones: [\n {\n key: 'yAxis',\n label: 'Value',\n description: 'Measure to display as KPI number',\n mandatory: true,\n maxItems: 1,\n acceptTypes: ['measure'],\n emptyText: 'Drop a measure here'\n }\n ],\n displayOptionsConfig: [\n {\n key: 'target',\n label: 'Target Value',\n type: 'string',\n placeholder: 'e.g., 100',\n description: 'Target value to compare against (first value used if multiple provided)'\n },\n {\n key: 'prefix',\n label: 'Prefix',\n type: 'string',\n placeholder: 'e.g., $, €, #',\n description: 'Text to display before the number'\n },\n {\n key: 'suffix',\n label: 'Suffix',\n type: 'string',\n placeholder: 'e.g., %, units, items',\n description: 'Text to display after the number'\n },\n {\n key: 'decimals',\n label: 'Decimal Places',\n type: 'number',\n defaultValue: 0,\n min: 0,\n max: 10,\n step: 1,\n description: 'Number of decimal places to display'\n },\n {\n key: 'valueColorIndex',\n label: 'Value Color',\n type: 'paletteColor',\n defaultValue: 0,\n description: 'Color from the dashboard palette for the KPI value text'\n },\n {\n key: 'useLastCompletePeriod',\n label: 'Use Last Complete Period',\n type: 'boolean',\n defaultValue: true,\n description: 'Exclude current incomplete period from aggregation (e.g., partial week/month)'\n },\n {\n key: 'skipLastPeriod',\n label: 'Skip Last Period',\n type: 'boolean',\n defaultValue: false,\n description: 'Always exclude the last period regardless of completeness'\n }\n ],\n displayOptions: ['hideHeader']\n}"],"names":["kpiNumberConfig","getChartTypeIcon"],"mappings":";AAMO,MAAMA,IAAmC;AAAA,EAC9C,MAAMC,EAAiB,WAAW;AAAA,EAClC,aAAa;AAAA,EACb,SAAS;AAAA,EACT,WAAW;AAAA,IACT;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,aAAa;AAAA,MACb,WAAW;AAAA,MACX,UAAU;AAAA,MACV,aAAa,CAAC,SAAS;AAAA,MACvB,WAAW;AAAA,IAAA;AAAA,EACb;AAAA,EAEF,sBAAsB;AAAA,IACpB;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA,MACd,KAAK;AAAA,MACL,KAAK;AAAA,MACL,MAAM;AAAA,MACN,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA,MACd,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA,MACd,aAAa;AAAA,IAAA;AAAA,IAEf;AAAA,MACE,KAAK;AAAA,MACL,OAAO;AAAA,MACP,MAAM;AAAA,MACN,cAAc;AAAA,MACd,aAAa;AAAA,IAAA;AAAA,EACf;AAAA,EAEF,gBAAgB,CAAC,YAAY;AAC/B;"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as n, jsxs as N } from "react/jsx-runtime";
2
2
  import { useState as M, useRef as V, useEffect as O } from "react";
3
- import { u as W } from "./chart-activitygridchart--4a5X5eV.js";
4
- import { D as E } from "./chart-kpinumber-CiOvJAlz.js";
3
+ import { u as W } from "./chart-activitygridchart-BzL97Vnm.js";
4
+ import { D as E } from "./chart-kpinumber-BrXgyKGP.js";
5
5
  function U({
6
6
  data: x,
7
7
  chartConfig: s,
@@ -162,4 +162,4 @@ function U({
162
162
  export {
163
163
  U as default
164
164
  };
165
- //# sourceMappingURL=chart-kpitext-D5kNrcSl.js.map
165
+ //# sourceMappingURL=chart-kpitext-_2tnYN_z.js.map