drizzle-cube 0.4.30 → 0.4.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/express/index.cjs +1 -1
- package/dist/adapters/express/index.d.ts +1 -1
- package/dist/adapters/express/index.js +7 -7
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/fastify/index.d.ts +1 -1
- package/dist/adapters/fastify/index.js +7 -7
- package/dist/adapters/{handler-D-2-6uLM.js → handler-BiVT5Sa4.js} +4 -4
- package/dist/adapters/{handler-B8vuFQYP.cjs → handler-GLMhjuwG.cjs} +2 -2
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/hono/index.d.ts +1 -1
- package/dist/adapters/hono/index.js +3 -3
- package/dist/adapters/{mcp-transport-CuugoG8t.js → mcp-transport-Cs6gEFCi.js} +1254 -948
- package/dist/adapters/mcp-transport-nyTtswLM.cjs +254 -0
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.d.ts +1 -1
- package/dist/adapters/nextjs/index.js +3 -3
- package/dist/adapters/{openai-DhLE0A9Z.cjs → openai-C5Brybjm.cjs} +1 -1
- package/dist/adapters/openai-D1zCEi6A.cjs +16 -0
- package/dist/adapters/{openai-CjBvA6mK.js → openai-DPuFtYsB.js} +659 -637
- package/dist/adapters/{openai-Zjw4Zo4R.js → openai-wU05wYGL.js} +1 -1
- package/dist/adapters/{utils-ChhNGUOF.js → utils-Dj1adfDc.js} +6 -3
- package/dist/adapters/{utils-CwJplXR5.cjs → utils-cb6OWP7J.cjs} +1 -1
- package/dist/adapters/utils.cjs +1 -1
- package/dist/adapters/utils.d.ts +1 -1
- package/dist/adapters/utils.js +1 -1
- package/dist/client/charts.js +13 -13
- package/dist/client/chunks/{DashboardEditModal-C076pscL.js → DashboardEditModal-DenRJiYr.js} +11 -11
- package/dist/client/chunks/{DashboardEditModal-C076pscL.js.map → DashboardEditModal-DenRJiYr.js.map} +1 -1
- package/dist/client/chunks/{FieldSearchModal-C0DjSWk3.js → FieldSearchModal-BQS1v1up.js} +3 -3
- package/dist/client/chunks/{FieldSearchModal-C0DjSWk3.js.map → FieldSearchModal-BQS1v1up.js.map} +1 -1
- package/dist/client/chunks/{RetentionCombinedChart-DuGXc-AP.js → RetentionCombinedChart-D4Yf1TnQ.js} +3 -3
- package/dist/client/chunks/{RetentionCombinedChart-DuGXc-AP.js.map → RetentionCombinedChart-D4Yf1TnQ.js.map} +1 -1
- package/dist/client/chunks/{analysis-builder-DF0XntqC.js → analysis-builder-DG38V1gO.js} +14 -20
- package/dist/client/chunks/{analysis-builder-DF0XntqC.js.map → analysis-builder-DG38V1gO.js.map} +1 -1
- package/dist/client/chunks/{analysis-builder-shared-2QhKYbs6.js → analysis-builder-shared-DaFu78dk.js} +19 -17
- package/dist/client/chunks/analysis-builder-shared-DaFu78dk.js.map +1 -0
- package/dist/client/chunks/{chart-activity-grid-CNES9VBk.js → chart-activity-grid-wR2Twpo7.js} +9 -6
- package/dist/client/chunks/chart-activity-grid-wR2Twpo7.js.map +1 -0
- package/dist/client/chunks/{chart-area-CRJc3KOu.js → chart-area-e9ysnatQ.js} +2 -2
- package/dist/client/chunks/{chart-area-CRJc3KOu.js.map → chart-area-e9ysnatQ.js.map} +1 -1
- package/dist/client/chunks/{chart-bar-DD2PjJ5n.js → chart-bar-Bx4oKlqo.js} +3 -3
- package/dist/client/chunks/{chart-bar-DD2PjJ5n.js.map → chart-bar-Bx4oKlqo.js.map} +1 -1
- package/dist/client/chunks/{chart-box-plot-BcqleldJ.js → chart-box-plot-CVIi1aM5.js} +2 -2
- package/dist/client/chunks/{chart-box-plot-BcqleldJ.js.map → chart-box-plot-CVIi1aM5.js.map} +1 -1
- package/dist/client/chunks/{chart-bubble-BmQkVk4K.js → chart-bubble-DvyG15UB.js} +2 -2
- package/dist/client/chunks/{chart-bubble-BmQkVk4K.js.map → chart-bubble-DvyG15UB.js.map} +1 -1
- package/dist/client/chunks/{chart-candlestick-C3Rep469.js → chart-candlestick-caHyxB9O.js} +2 -2
- package/dist/client/chunks/{chart-candlestick-C3Rep469.js.map → chart-candlestick-caHyxB9O.js.map} +1 -1
- package/dist/client/chunks/{chart-data-table-DehW1C1G.js → chart-data-table-B2m_6EZe.js} +2 -2
- package/dist/client/chunks/{chart-data-table-DehW1C1G.js.map → chart-data-table-B2m_6EZe.js.map} +1 -1
- package/dist/client/chunks/{chart-funnel-BjkpnG5g.js → chart-funnel-C9JRW79j.js} +2 -2
- package/dist/client/chunks/{chart-funnel-BjkpnG5g.js.map → chart-funnel-C9JRW79j.js.map} +1 -1
- package/dist/client/chunks/{chart-gauge-BWW_HEfg.js → chart-gauge-BLLJqeXo.js} +2 -2
- package/dist/client/chunks/{chart-gauge-BWW_HEfg.js.map → chart-gauge-BLLJqeXo.js.map} +1 -1
- package/dist/client/chunks/{chart-heat-map-BWuOuDcm.js → chart-heat-map-f2fM2mDC.js} +2 -2
- package/dist/client/chunks/{chart-heat-map-BWuOuDcm.js.map → chart-heat-map-f2fM2mDC.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-delta-D5OHtDJx.js → chart-kpi-delta-S6qDEoJO.js} +3 -3
- package/dist/client/chunks/{chart-kpi-delta-D5OHtDJx.js.map → chart-kpi-delta-S6qDEoJO.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-number-C9zH-aKC.js → chart-kpi-number-CEUBsVW2.js} +5 -5
- package/dist/client/chunks/{chart-kpi-number-C9zH-aKC.js.map → chart-kpi-number-CEUBsVW2.js.map} +1 -1
- package/dist/client/chunks/{chart-kpi-text-ChVn3S7j.js → chart-kpi-text-DgRig_jQ.js} +3 -3
- package/dist/client/chunks/{chart-kpi-text-ChVn3S7j.js.map → chart-kpi-text-DgRig_jQ.js.map} +1 -1
- package/dist/client/chunks/{chart-line-DOIMkP0b.js → chart-line-CPhQRMZ7.js} +4 -3
- package/dist/client/chunks/chart-line-CPhQRMZ7.js.map +1 -0
- package/dist/client/chunks/{chart-measure-profile-C6wrr9il.js → chart-measure-profile-CVlqGslU.js} +3 -3
- package/dist/client/chunks/{chart-measure-profile-C6wrr9il.js.map → chart-measure-profile-CVlqGslU.js.map} +1 -1
- package/dist/client/chunks/{chart-pie-BU_FgwDc.js → chart-pie-DafSc9sE.js} +3 -3
- package/dist/client/chunks/{chart-pie-BU_FgwDc.js.map → chart-pie-DafSc9sE.js.map} +1 -1
- package/dist/client/chunks/{chart-radar-DlufwnAX.js → chart-radar-Dz9F5k-B.js} +3 -3
- package/dist/client/chunks/{chart-radar-DlufwnAX.js.map → chart-radar-Dz9F5k-B.js.map} +1 -1
- package/dist/client/chunks/{chart-radial-bar-B5vS_Aw5.js → chart-radial-bar-N3MNUL7o.js} +3 -3
- package/dist/client/chunks/{chart-radial-bar-B5vS_Aw5.js.map → chart-radial-bar-N3MNUL7o.js.map} +1 -1
- package/dist/client/chunks/{chart-sankey-FChb26UX.js → chart-sankey-8nRYnupt.js} +2 -2
- package/dist/client/chunks/{chart-sankey-FChb26UX.js.map → chart-sankey-8nRYnupt.js.map} +1 -1
- package/dist/client/chunks/{chart-scatter-DW0cAZ2H.js → chart-scatter-J2JNi88S.js} +3 -3
- package/dist/client/chunks/{chart-scatter-DW0cAZ2H.js.map → chart-scatter-J2JNi88S.js.map} +1 -1
- package/dist/client/chunks/{chart-sunburst-Clf-6WxW.js → chart-sunburst-D1NFQjqk.js} +3 -3
- package/dist/client/chunks/{chart-sunburst-Clf-6WxW.js.map → chart-sunburst-D1NFQjqk.js.map} +1 -1
- package/dist/client/chunks/{chart-tree-map-p_VwUJPF.js → chart-tree-map-CbYjko2s.js} +3 -3
- package/dist/client/chunks/{chart-tree-map-p_VwUJPF.js.map → chart-tree-map-CbYjko2s.js.map} +1 -1
- package/dist/client/chunks/{chart-waterfall-jt44IQ-w.js → chart-waterfall-Z65TGMUO.js} +3 -3
- package/dist/client/chunks/{chart-waterfall-jt44IQ-w.js.map → chart-waterfall-Z65TGMUO.js.map} +1 -1
- package/dist/client/chunks/{charts-core-CUVzf4cV.js → charts-core-CJlGzwsW.js} +2 -2
- package/dist/client/chunks/{charts-core-CUVzf4cV.js.map → charts-core-CJlGzwsW.js.map} +1 -1
- package/dist/client/chunks/{charts-loader-AGpph8_I.js → charts-loader-BI07yxaQ.js} +25 -25
- package/dist/client/chunks/{charts-loader-AGpph8_I.js.map → charts-loader-BI07yxaQ.js.map} +1 -1
- package/dist/client/chunks/{schema-visualization-ZugB4Io9.js → schema-visualization-DPL5_Tkh.js} +2 -2
- package/dist/client/chunks/{schema-visualization-ZugB4Io9.js.map → schema-visualization-DPL5_Tkh.js.map} +1 -1
- package/dist/client/chunks/{useDebounce-BQjNWndQ.js → useDebounce-DkuM7nQk.js} +4 -4
- package/dist/client/chunks/{useDebounce-BQjNWndQ.js.map → useDebounce-DkuM7nQk.js.map} +1 -1
- package/dist/client/chunks/{useExplainAI-c_bHxZe5.js → useExplainAI-RflLTDVL.js} +7 -7
- package/dist/client/chunks/useExplainAI-RflLTDVL.js.map +1 -0
- package/dist/client/chunks/{utils-A54Ny29G.js → utils--32ZtfbZ.js} +2 -2
- package/dist/client/chunks/{utils-A54Ny29G.js.map → utils--32ZtfbZ.js.map} +1 -1
- package/dist/client/chunks/{vendor-CoPBRumI.js → vendor-CZX7UVzM.js} +2 -2
- package/dist/client/chunks/{vendor-CoPBRumI.js.map → vendor-CZX7UVzM.js.map} +1 -1
- package/dist/client/components/AnalysisBuilder/AnalysisModeErrorBoundary.d.ts +0 -1
- package/dist/client/components.js +3 -3
- package/dist/client/hooks.js +3 -3
- package/dist/client/icons.js +1 -1
- package/dist/client/index.js +15 -15
- package/dist/client/providers.js +1 -1
- package/dist/client/utils.js +5 -5
- package/dist/client-bundle-stats.html +1 -1
- package/dist/server/index.cjs +41 -40
- package/dist/server/index.d.ts +43 -9
- package/dist/server/index.js +1421 -1112
- package/dist/server/openai-CucrxPHM.cjs +16 -0
- package/dist/server/{openai-CLsoLaue.cjs → openai-DiyuZ3ds.cjs} +1 -1
- package/dist/server/{openai-BPhmb8mi.js → openai-nZJcAzON.js} +659 -637
- package/dist/server/{openai-D1kZ5sdM.js → openai-owPdq1yN.js} +1 -1
- package/package.json +14 -4
- package/dist/adapters/mcp-transport-Dpp6hdZe.cjs +0 -253
- package/dist/adapters/openai-eJBw3LfQ.cjs +0 -16
- package/dist/client/chunks/analysis-builder-shared-2QhKYbs6.js.map +0 -1
- package/dist/client/chunks/chart-activity-grid-CNES9VBk.js.map +0 -1
- package/dist/client/chunks/chart-line-DOIMkP0b.js.map +0 -1
- package/dist/client/chunks/useExplainAI-c_bHxZe5.js.map +0 -1
- package/dist/server/openai-DkOKbVLC.cjs +0 -16
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-line-CPhQRMZ7.js","names":[],"sources":["../../../src/client/utils/comparisonUtils.ts","../../../src/client/components/charts/LineChart.tsx"],"sourcesContent":["/**\n * Comparison Data Utilities\n * Handles period-over-period comparison data transformation for charts\n *\n * These utilities detect comparison query results (with __periodIndex metadata)\n * and transform them for visualization in either 'separate' or 'overlay' mode.\n */\n\n/**\n * Check if data contains comparison period metadata\n */\nexport function isComparisonData(data: any[]): boolean {\n return data.length > 0 && '__periodIndex' in data[0]\n}\n\n/**\n * Get unique period labels from comparison data\n */\nexport function getPeriodLabels(data: any[]): string[] {\n if (!isComparisonData(data)) return []\n\n const labels = new Set<string>()\n for (const row of data) {\n if (row.__period) {\n labels.add(row.__period)\n }\n }\n return Array.from(labels)\n}\n\n/**\n * Get period indices from comparison data\n */\nexport function getPeriodIndices(data: any[]): number[] {\n if (!isComparisonData(data)) return []\n\n const indices = new Set<number>()\n for (const row of data) {\n if (typeof row.__periodIndex === 'number') {\n indices.add(row.__periodIndex)\n }\n }\n return Array.from(indices).sort((a, b) => a - b)\n}\n\n/**\n * Generate a short label for a period (used in legends)\n * Uses simple \"Current\" / \"Prior\" labels for clarity\n */\nexport function generatePeriodShortLabel(_periodLabel: string, index: number): string {\n return index === 0 ? 'Current' : 'Prior'\n}\n\n/**\n * Transform comparison data for 'separate' mode\n * Returns data with each row having its original measure values\n * Series are created per period (e.g., \"totalLinesOfCode (Current)\", \"totalLinesOfCode (Prior)\")\n */\nexport function transformForSeparateMode(\n data: any[],\n measures: string[],\n _timeDimensionKey: string\n): { data: any[], seriesKeys: string[] } {\n if (!isComparisonData(data)) {\n return { data, seriesKeys: measures }\n }\n\n const periodLabels = getPeriodLabels(data)\n const seriesKeys: string[] = []\n\n // Create series key for each measure + period combination\n for (const measure of measures) {\n for (let i = 0; i < periodLabels.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i], i)\n const key = `${measure} (${shortLabel})`\n seriesKeys.push(key)\n }\n }\n\n // Data already has period info, just return as-is for separate mode\n // The chart will group by time dimension and show multiple lines\n return { data, seriesKeys }\n}\n\n/**\n * Detect dimension fields in comparison data\n * Returns fields that are not measures, time dimension, or metadata fields\n */\nfunction detectDimensionFields(\n data: any[],\n measures: string[],\n timeDimensionKey: string\n): string[] {\n if (data.length === 0) return []\n\n const metadataFields = ['__period', '__periodIndex', '__periodDayIndex']\n const firstRow = data[0]\n const dimensions: string[] = []\n\n for (const key of Object.keys(firstRow)) {\n if (\n !measures.includes(key) &&\n key !== timeDimensionKey &&\n !metadataFields.includes(key)\n ) {\n dimensions.push(key)\n }\n }\n\n return dimensions\n}\n\n/**\n * Get unique dimension values from data for a set of dimension fields\n */\nfunction getUniqueDimensionValues(\n data: any[],\n dimensionFields: string[]\n): Map<string, Set<any>> {\n const values = new Map<string, Set<any>>()\n\n for (const field of dimensionFields) {\n values.set(field, new Set())\n }\n\n for (const row of data) {\n for (const field of dimensionFields) {\n if (row[field] !== undefined && row[field] !== null) {\n values.get(field)!.add(row[field])\n }\n }\n }\n\n return values\n}\n\n/**\n * Transform comparison data for 'overlay' mode\n * Pivots data so each row represents a day-of-period index,\n * with separate columns for each period's values.\n *\n * When dimensions are present, creates series keys that include\n * dimension values: \"Engineering - Total Lines of Code (Current)\"\n *\n * Input (without dimensions):\n * [\n * { date: '2024-01-01', value: 100, __periodIndex: 0, __periodDayIndex: 0 },\n * { date: '2024-01-02', value: 110, __periodIndex: 0, __periodDayIndex: 1 },\n * { date: '2023-01-01', value: 80, __periodIndex: 1, __periodDayIndex: 0 },\n * { date: '2023-01-02', value: 85, __periodIndex: 1, __periodDayIndex: 1 },\n * ]\n *\n * Output (without dimensions):\n * [\n * { __periodDayIndex: 0, 'value (Current)': 100, 'value (Prior)': 80 },\n * { __periodDayIndex: 1, 'value (Current)': 110, 'value (Prior)': 85 },\n * ]\n *\n * Input (with dimensions):\n * [\n * { date: '2024-01-01', 'Dept.name': 'Engineering', value: 100, __periodIndex: 0, __periodDayIndex: 0 },\n * { date: '2024-01-01', 'Dept.name': 'Sales', value: 50, __periodIndex: 0, __periodDayIndex: 0 },\n * { date: '2023-01-01', 'Dept.name': 'Engineering', value: 80, __periodIndex: 1, __periodDayIndex: 0 },\n * { date: '2023-01-01', 'Dept.name': 'Sales', value: 40, __periodIndex: 1, __periodDayIndex: 0 },\n * ]\n *\n * Output (with dimensions):\n * [\n * {\n * __periodDayIndex: 0,\n * 'Engineering - value (Current)': 100,\n * 'Engineering - value (Prior)': 80,\n * 'Sales - value (Current)': 50,\n * 'Sales - value (Prior)': 40,\n * },\n * ]\n */\nexport function transformForOverlayMode(\n data: any[],\n measures: string[],\n timeDimensionKey: string,\n getFieldLabel?: (fieldName: string) => string\n): { data: any[], seriesKeys: string[], xAxisKey: string } {\n if (!isComparisonData(data)) {\n return { data, seriesKeys: measures, xAxisKey: timeDimensionKey }\n }\n\n const periodLabels = getPeriodLabels(data)\n const periodIndices = getPeriodIndices(data)\n\n // Detect dimension fields in the data\n const dimensionFields = detectDimensionFields(data, measures, timeDimensionKey)\n const hasDimensions = dimensionFields.length > 0\n\n // Get unique dimension values for generating series keys\n const uniqueDimensionValues = hasDimensions\n ? getUniqueDimensionValues(data, dimensionFields)\n : null\n\n // Group data by __periodDayIndex\n const groupedByDayIndex = new Map<number, Record<string, any>>()\n\n for (const row of data) {\n const dayIndex = row.__periodDayIndex\n const periodIndex = row.__periodIndex\n\n if (!groupedByDayIndex.has(dayIndex)) {\n groupedByDayIndex.set(dayIndex, {\n __periodDayIndex: dayIndex,\n // Store the date from the first period (index 0) for reference\n __displayDate: periodIndex === 0 ? row[timeDimensionKey] : undefined\n })\n }\n\n const groupedRow = groupedByDayIndex.get(dayIndex)!\n\n // If this is the first period and we don't have a display date yet, add it\n if (!groupedRow.__displayDate && row[timeDimensionKey]) {\n groupedRow.__displayDate = row[timeDimensionKey]\n }\n\n // Build dimension prefix if dimensions are present\n let dimensionPrefix = ''\n if (hasDimensions) {\n const dimensionValues = dimensionFields.map(field => {\n const value = row[field]\n return getFieldLabel ? getFieldLabel(String(value)) : String(value)\n })\n dimensionPrefix = dimensionValues.join(' / ')\n }\n\n // Add measure values with optional dimension prefix and period suffix\n const shortLabel = generatePeriodShortLabel(periodLabels[periodIndex] || '', periodIndex)\n for (const measure of measures) {\n const displayName = getFieldLabel ? getFieldLabel(measure) : measure\n const key = dimensionPrefix\n ? `${dimensionPrefix} - ${displayName} (${shortLabel})`\n : `${displayName} (${shortLabel})`\n groupedRow[key] = row[measure]\n }\n }\n\n // Convert to array and sort by day index\n const transformedData = Array.from(groupedByDayIndex.values())\n .sort((a, b) => a.__periodDayIndex - b.__periodDayIndex)\n\n // Generate series keys (using display names to match the data keys)\n const seriesKeys: string[] = []\n\n if (hasDimensions && uniqueDimensionValues) {\n // Generate all combinations of dimension values\n const dimensionCombinations = generateDimensionCombinations(\n dimensionFields,\n uniqueDimensionValues,\n getFieldLabel\n )\n\n // For each dimension combination, create series keys for each measure + period\n for (const dimCombo of dimensionCombinations) {\n for (const measure of measures) {\n const displayName = getFieldLabel ? getFieldLabel(measure) : measure\n for (let i = 0; i < periodIndices.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i] || '', i)\n seriesKeys.push(`${dimCombo} - ${displayName} (${shortLabel})`)\n }\n }\n }\n } else {\n // No dimensions - generate simple measure + period keys\n for (const measure of measures) {\n const displayName = getFieldLabel ? getFieldLabel(measure) : measure\n for (let i = 0; i < periodIndices.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i] || '', i)\n seriesKeys.push(`${displayName} (${shortLabel})`)\n }\n }\n }\n\n return {\n data: transformedData,\n seriesKeys,\n xAxisKey: '__periodDayIndex'\n }\n}\n\n/**\n * Generate all combinations of dimension values as formatted strings\n */\nfunction generateDimensionCombinations(\n dimensionFields: string[],\n uniqueValues: Map<string, Set<any>>,\n getFieldLabel?: (fieldName: string) => string\n): string[] {\n if (dimensionFields.length === 0) return []\n\n // For single dimension, just return the values\n if (dimensionFields.length === 1) {\n const field = dimensionFields[0]\n const values = Array.from(uniqueValues.get(field) || [])\n return values.map(v => getFieldLabel ? getFieldLabel(String(v)) : String(v))\n }\n\n // For multiple dimensions, generate cartesian product\n const result: string[] = []\n const fieldValues = dimensionFields.map(field =>\n Array.from(uniqueValues.get(field) || [])\n )\n\n function generateCombos(index: number, current: string[]): void {\n if (index === fieldValues.length) {\n const formatted = current.map(v =>\n getFieldLabel ? getFieldLabel(String(v)) : String(v)\n )\n result.push(formatted.join(' / '))\n return\n }\n\n for (const value of fieldValues[index]) {\n generateCombos(index + 1, [...current, value])\n }\n }\n\n generateCombos(0, [])\n return result\n}\n\n/**\n * Format the period day index for display on X-axis\n * Can show as \"Day 1\", \"Day 2\", etc., or as the original date\n */\nexport function formatPeriodDayIndex(\n dayIndex: number,\n displayDate?: string | Date,\n options?: {\n showDayNumber?: boolean\n dateFormat?: 'short' | 'long'\n }\n): string {\n if (options?.showDayNumber) {\n return `Day ${dayIndex + 1}`\n }\n\n if (displayDate) {\n const date = typeof displayDate === 'string' ? new Date(displayDate) : displayDate\n if (!isNaN(date.getTime())) {\n return date.toLocaleDateString('en-US', {\n month: options?.dateFormat === 'long' ? 'long' : 'short',\n day: 'numeric'\n })\n }\n }\n\n return `${dayIndex + 1}`\n}\n\n/**\n * Check if a series key represents a prior period (not the first period)\n * Used for applying different styling to prior period lines\n */\nexport function isPriorPeriodSeries(seriesKey: string, periodLabels: string[]): boolean {\n if (periodLabels.length < 2) return false\n\n // Check if the series key contains any label other than the first period's\n for (let i = 1; i < periodLabels.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i], i)\n if (seriesKey.includes(`(${shortLabel})`)) {\n return true\n }\n }\n\n // Also check for \"(Prior\" pattern\n return seriesKey.includes('(Prior')\n}\n\n/**\n * Get the stroke dash array for prior period styling\n */\nexport function getPriorPeriodStrokeDashArray(\n style: 'solid' | 'dashed' | 'dotted' = 'dashed'\n): string | undefined {\n switch (style) {\n case 'solid':\n return undefined\n case 'dashed':\n return '5 5'\n case 'dotted':\n return '2 2'\n default:\n return '5 5'\n }\n}\n","import React, { useState, useMemo } from 'react'\nimport { LineChart as RechartsLineChart, Line, XAxis, YAxis, CartesianGrid, Legend } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport AngledXAxisTick from './AngledXAxisTick'\nimport { CHART_COLORS, CHART_MARGINS } from '../../utils/chartConstants'\nimport { transformChartDataWithSeries, formatAxisValue, formatTimeValue, getFieldGranularity } from '../../utils/chartUtils'\nimport { parseTargetValues, spreadTargetValues } from '../../utils/targetUtils'\nimport {\n isComparisonData,\n getPeriodLabels,\n transformForOverlayMode,\n isPriorPeriodSeries,\n getPriorPeriodStrokeDashArray\n} from '../../utils/comparisonUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps } from '../../types'\n\nconst LineChart = React.memo(function LineChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n\n // Extract Y-axis fields early for use in useMemo hooks\n // This ensures hooks are always called in the same order\n const yAxisFields = useMemo(() => {\n if (chartConfig?.yAxis) {\n return Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis : [chartConfig.yAxis]\n }\n if (chartConfig?.y) {\n return Array.isArray(chartConfig.y) ? chartConfig.y : [chartConfig.y]\n }\n return []\n }, [chartConfig?.yAxis, chartConfig?.y])\n\n // Dual Y-axis support: extract yAxisAssignment from chartConfig (memoized to prevent object recreation)\n // MUST be called before any early returns to satisfy React hooks rules\n const yAxisAssignment = useMemo(() =>\n chartConfig?.yAxisAssignment || {},\n [chartConfig?.yAxisAssignment]\n )\n\n // Build mapping from series key (label) to original field name (memoized to prevent object recreation)\n // This is needed because seriesKeys use display labels, not field names\n // MUST be called before any early returns to satisfy React hooks rules\n const seriesKeyToField = useMemo(() => {\n const mapping: Record<string, string> = {}\n yAxisFields.forEach((field) => {\n const label = getFieldLabel(field)\n mapping[label] = field\n })\n return mapping\n }, [yAxisFields, getFieldLabel])\n\n try {\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showGrid: displayConfig?.showGrid ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n connectNulls: displayConfig?.connectNulls ?? false\n }\n\n // Extract axis format configs\n const leftYAxisFormat = displayConfig?.leftYAxisFormat\n const rightYAxisFormat = displayConfig?.rightYAxisFormat\n\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in line chart</div>\n </div>\n </div>\n )\n }\n\n // Validate chartConfig - support both legacy and new formats\n let xAxisField: string\n let seriesFields: string[] = []\n\n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format\n xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis\n seriesFields = chartConfig.series || []\n } else if (chartConfig?.x && chartConfig?.y) {\n // Legacy format\n xAxisField = chartConfig.x\n } else {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">Invalid or missing chart axis configuration</div>\n </div>\n </div>\n )\n }\n\n if (!xAxisField || yAxisFields.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">Missing required X-axis or Y-axis fields</div>\n </div>\n </div>\n )\n }\n\n // Check if this is comparison data (has __periodIndex metadata)\n const hasComparisonData = isComparisonData(data)\n const priorPeriodStyle = displayConfig?.priorPeriodStyle || 'dashed'\n const priorPeriodOpacity = displayConfig?.priorPeriodOpacity ?? 0.5\n const periodLabels = hasComparisonData ? getPeriodLabels(data) : []\n\n // Transform data based on comparison mode\n let chartData: any[]\n let seriesKeys: string[]\n let effectiveXAxisKey = 'name' // Default X-axis key after transformation\n\n if (hasComparisonData) {\n // For comparison data, always use overlay transformation to align by period day index\n // Both 'separate' and 'overlay' modes use the same data transformation,\n // they differ only in styling (dashed lines, opacity for prior periods in overlay mode)\n const overlayResult = transformForOverlayMode(data, yAxisFields, xAxisField, getFieldLabel)\n chartData = overlayResult.data\n seriesKeys = overlayResult.seriesKeys\n effectiveXAxisKey = '__periodDayIndex'\n } else {\n // Standard mode: use normal transformation\n const standardResult = transformChartDataWithSeries(\n data,\n xAxisField,\n yAxisFields,\n queryObject,\n seriesFields,\n getFieldLabel\n )\n chartData = standardResult.data\n seriesKeys = standardResult.seriesKeys\n }\n\n // Helper to find field from series key, handling comparison suffixes like \"(Current)\" and \"(Prior)\"\n const findFieldFromSeriesKey = (seriesKey: string): string | undefined => {\n // Direct match first\n if (seriesKeyToField[seriesKey]) {\n return seriesKeyToField[seriesKey]\n }\n // For comparison data, strip the period suffix and any dimension prefix\n // Series keys look like: \"Label (Current)\", \"Label (Prior)\", or \"DimValue - Label (Current)\"\n\n // Guard against excessive input length to prevent ReDoS\n if (seriesKey.length > 1000) return undefined\n const withoutSuffix = seriesKey.replace(/\\s*\\((Current|Prior)\\)$/, '')\n // Check if it has a dimension prefix (contains \" - \")\n const parts = withoutSuffix.split(' - ')\n const measureLabel = parts[parts.length - 1] // Last part is the measure label\n return seriesKeyToField[measureLabel]\n }\n\n // Determine if we need a right Y-axis\n const hasRightAxis = yAxisFields.some((field) => yAxisAssignment[field] === 'right')\n\n // Get fields for left and right axes for labels\n const leftAxisFields = yAxisFields.filter((f) => (yAxisAssignment[f] || 'left') === 'left')\n const rightAxisFields = yAxisFields.filter((f) => yAxisAssignment[f] === 'right')\n\n // Determine if legend will be shown\n const showLegend = safeDisplayConfig.showLegend\n\n // Use custom chart margins with extra space for Y-axis labels\n const chartMargins = {\n ...CHART_MARGINS,\n left: 40, // Space for left Y-axis label\n right: hasRightAxis ? 40 : 20 // Extra space for right Y-axis label if needed\n }\n\n // Process target values and add to chart data\n const targetValues = parseTargetValues(displayConfig?.target || '')\n const spreadTargets = spreadTargetValues(targetValues, chartData.length)\n\n // Add target data to chart data if targets exist\n let enhancedChartData = chartData\n if (spreadTargets.length > 0) {\n enhancedChartData = chartData.map((dataPoint, index) => ({\n ...dataPoint,\n __target: spreadTargets[index] || null\n }))\n }\n\n // Validate transformed data\n if (!chartData || chartData.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No valid data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No valid data points for line chart after transformation</div>\n </div>\n </div>\n )\n }\n\n return (\n <ChartContainer height={height}>\n <RechartsLineChart data={enhancedChartData} margin={chartMargins} accessibilityLayer={false}>\n {safeDisplayConfig.showGrid && (\n <CartesianGrid strokeDasharray=\"3 3\" style={{ pointerEvents: 'none' }} />\n )}\n <XAxis\n dataKey={effectiveXAxisKey}\n type=\"category\"\n tick={<AngledXAxisTick tickFormatter={\n hasComparisonData\n ? (value: string | number, index: number) => {\n // For comparison data, show the date from the current period\n // formatted according to the query's granularity\n const row = chartData[index]\n if (row?.__displayDate) {\n const granularity = getFieldGranularity(queryObject, xAxisField)\n return formatTimeValue(row.__displayDate, granularity)\n }\n return `Period ${Number(value) + 1}`\n }\n : undefined\n } />}\n height={60}\n />\n <YAxis\n yAxisId=\"left\"\n orientation=\"left\"\n tick={{ fontSize: 12 }}\n tickFormatter={leftYAxisFormat ? (value) => formatAxisValue(value, leftYAxisFormat) : undefined}\n label={\n leftAxisFields.length > 0\n ? {\n value: leftYAxisFormat?.label || getFieldLabel(leftAxisFields[0]),\n angle: -90,\n position: 'left',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n {hasRightAxis && (\n <YAxis\n yAxisId=\"right\"\n orientation=\"right\"\n tick={{ fontSize: 12 }}\n tickFormatter={rightYAxisFormat ? (value) => formatAxisValue(value, rightYAxisFormat) : undefined}\n label={\n rightAxisFields.length > 0\n ? {\n value: rightYAxisFormat?.label || getFieldLabel(rightAxisFields[0]),\n angle: 90,\n position: 'right',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n )}\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={(value: any, name: any) => {\n // Handle null values in tooltip\n if (value === null || value === undefined) {\n return ['No data', name]\n }\n if (name === 'Target') {\n // Use left Y-axis format for target values\n return [formatAxisValue(value, leftYAxisFormat), 'Target Value']\n }\n\n // Determine which axis format to use based on series name\n const originalField = findFieldFromSeriesKey(name)\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n const formatConfig = axisId === 'right' ? rightYAxisFormat : leftYAxisFormat\n // Series name is already formatted (e.g., \"Total Lines of Code (Current)\")\n return [formatAxisValue(value, formatConfig), name]\n }}\n labelFormatter={\n hasComparisonData\n ? (label: any, payload: any) => {\n // For comparison data, show the date from the current period\n // formatted according to the query's granularity\n if (payload && payload.length > 0) {\n const row = payload[0]?.payload\n if (row?.__displayDate) {\n const granularity = getFieldGranularity(queryObject, xAxisField)\n return formatTimeValue(row.__displayDate, granularity)\n }\n }\n return `Period ${Number(label) + 1}`\n }\n : undefined\n }\n />\n )}\n {showLegend && (\n <Legend\n wrapperStyle={{ fontSize: '12px', paddingTop: '25px' }}\n iconType=\"line\"\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => setHoveredLegend(String(o.dataKey || ''))}\n onMouseLeave={() => setHoveredLegend(null)}\n />\n )}\n {seriesKeys.map((seriesKey, index) => {\n // Look up the original field name to get its axis assignment\n const originalField = findFieldFromSeriesKey(seriesKey)\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n\n // Determine if this is a prior period series (for styling)\n const isPriorPeriod = hasComparisonData && isPriorPeriodSeries(seriesKey, periodLabels)\n const strokeDashArray = isPriorPeriod ? getPriorPeriodStrokeDashArray(priorPeriodStyle) : undefined\n const opacity = isPriorPeriod ? priorPeriodOpacity : 1\n\n // When drill is enabled, show persistent dots for better click targets\n const lineColor = (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) ||\n CHART_COLORS[index % CHART_COLORS.length]\n\n return (\n <Line\n key={seriesKey}\n type=\"monotone\"\n dataKey={seriesKey}\n yAxisId={axisId}\n stroke={lineColor}\n strokeWidth={isPriorPeriod ? 1.5 : 2}\n strokeDasharray={strokeDashArray}\n dot={isPriorPeriod ? false : (props: any) => {\n const { cx, cy, payload, key } = props\n if (cx === undefined || cy === undefined) return null\n\n const handleClick = (event: React.MouseEvent) => {\n event.stopPropagation()\n event.preventDefault()\n if (onDataPointClick) {\n onDataPointClick({\n dataPoint: payload,\n clickedField: originalField || seriesKey,\n xValue: payload.name,\n position: { x: event.clientX, y: event.clientY },\n nativeEvent: event\n })\n }\n }\n\n // When drill is enabled, render clickable dots with background to mask grid\n if (drillEnabled && onDataPointClick) {\n return (\n <g key={key}>\n {/* Background to mask grid lines - uses theme surface color */}\n <circle\n cx={cx}\n cy={cy}\n r={6}\n fill=\"var(--dc-surface)\"\n style={{ pointerEvents: 'none' }}\n />\n {/* Visible dot with click handler */}\n <circle\n cx={cx}\n cy={cy}\n r={4}\n fill=\"var(--dc-surface)\"\n stroke={lineColor}\n strokeWidth={2}\n cursor=\"pointer\"\n onClick={(e: React.MouseEvent<SVGCircleElement>) => {\n handleClick(e as unknown as React.MouseEvent)\n }}\n />\n </g>\n )\n }\n\n // Non-drill mode: simple small dot\n return (\n <circle\n key={key}\n cx={cx}\n cy={cy}\n r={3}\n fill={lineColor}\n />\n )\n }}\n activeDot={false}\n strokeOpacity={\n hoveredLegend\n ? (hoveredLegend === seriesKey ? 1 : 0.3)\n : opacity\n }\n connectNulls={safeDisplayConfig.connectNulls}\n />\n )\n })}\n {spreadTargets.length > 0 && (\n <>\n {/* White background line */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n stroke=\"#ffffff\"\n strokeWidth={2}\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n {/* Grey dashed line on top */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n name=\"Target\"\n stroke=\"#8B5CF6\"\n strokeWidth={2}\n strokeDasharray=\"2 3\"\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n </>\n )}\n </RechartsLineChart>\n </ChartContainer>\n )\n } catch (error) {\n // 'LineChart rendering error\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Line Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default LineChart\n"],"mappings":"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"}
|
package/dist/client/chunks/{chart-measure-profile-C6wrr9il.js → chart-measure-profile-CVlqGslU.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as e } from "./rolldown-runtime-lc2dmIiU.js";
|
|
2
|
-
import { _ as t, i as n, n as r, y as i } from "./chart-activity-grid-
|
|
3
|
-
import { c as a, l as o, s } from "./chart-area-
|
|
2
|
+
import { _ as t, i as n, n as r, y as i } from "./chart-activity-grid-wR2Twpo7.js";
|
|
3
|
+
import { c as a, l as o, s } from "./chart-area-e9ysnatQ.js";
|
|
4
4
|
import c, { useMemo as l } from "react";
|
|
5
5
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
6
6
|
import { CartesianGrid as f, Legend as p, Line as m, LineChart as h, ReferenceLine as g, XAxis as _, YAxis as v } from "recharts";
|
|
@@ -176,4 +176,4 @@ var x = c.memo(function({ data: e, chartConfig: c, displayConfig: y = {}, height
|
|
|
176
176
|
//#endregion
|
|
177
177
|
export { y as t };
|
|
178
178
|
|
|
179
|
-
//# sourceMappingURL=chart-measure-profile-
|
|
179
|
+
//# sourceMappingURL=chart-measure-profile-CVlqGslU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-measure-profile-C6wrr9il.js","names":[],"sources":["../../../src/client/components/charts/MeasureProfileChart.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\nimport { LineChart, Line, XAxis, YAxis, CartesianGrid, ReferenceLine, Legend } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport AngledXAxisTick from './AngledXAxisTick'\nimport { CHART_COLORS, CHART_MARGINS } from '../../utils/chartConstants'\nimport { formatAxisValue } from '../../utils/chartUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps } from '../../types'\n\n/**\n * Pivots N measures from chartConfig.yAxis[] into sequential X-axis points.\n * Each measure becomes one X-axis category; its numeric value is Y.\n * When a series dimension is provided, values are averaged across all rows\n * that share the same series value.\n *\n * Input (normal cube rows):\n * [{ 'Markouts.avgMinus2m': 10, 'Markouts.avgAtEvent': 0, 'Trades.symbol': 'AAPL' },\n * { 'Markouts.avgMinus2m': 12, 'Markouts.avgAtEvent': 1, 'Trades.symbol': 'AAPL' }]\n *\n * Output (one row per measure, values averaged per series):\n * [{ measureKey: 'Markouts.avgMinus2m', measureLabel: <getFieldLabel result>, AAPL: 11 },\n * { measureKey: 'Markouts.avgAtEvent', measureLabel: <getFieldLabel result>, AAPL: 0.5 }]\n */\nfunction pivotMeasuresToProfile(\n data: Record<string, unknown>[],\n yAxisFields: string[],\n seriesField: string | undefined,\n getFieldLabel: (field: string) => string\n): { profileData: Record<string, unknown>[]; seriesKeys: string[] } {\n if (!data || data.length === 0 || yAxisFields.length === 0) {\n return { profileData: [], seriesKeys: [] }\n }\n\n if (seriesField) {\n const seriesValues = Array.from(new Set(data.map((row) => String(row[seriesField] ?? 'Unknown'))))\n const profileData = yAxisFields.map((field) => {\n const base: Record<string, unknown> = {\n measureKey: field,\n measureLabel: getFieldLabel(field),\n }\n for (const seriesVal of seriesValues) {\n const matchingRows = data.filter((row) => String(row[seriesField] ?? 'Unknown') === seriesVal)\n const values = matchingRows\n .map((r) => {\n const v = r[field]\n return typeof v === 'number' ? v : parseFloat(String(v ?? ''))\n })\n .filter((v) => !isNaN(v))\n base[seriesVal] = values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : null\n }\n return base\n })\n return { profileData, seriesKeys: seriesValues }\n } else {\n const VALUE_KEY = '_value'\n const profileData = yAxisFields.map((field) => {\n const values = data\n .map((r) => {\n const v = r[field]\n return typeof v === 'number' ? v : parseFloat(String(v ?? ''))\n })\n .filter((v) => !isNaN(v))\n return {\n measureKey: field,\n measureLabel: getFieldLabel(field),\n [VALUE_KEY]: values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : null,\n }\n })\n return { profileData, seriesKeys: [VALUE_KEY] }\n }\n}\n\nconst MeasureProfileChart = React.memo(function MeasureProfileChart({\n data,\n chartConfig,\n displayConfig = {},\n height = '100%',\n colorPalette,\n drillEnabled,\n}: ChartProps) {\n const getFieldLabel = useCubeFieldLabel()\n\n const showReferenceLineAtZero = displayConfig?.showReferenceLineAtZero ?? true\n const showDataLabels = displayConfig?.showDataLabels ?? false\n const lineType = displayConfig?.lineType ?? 'monotone'\n const yAxisFormat = displayConfig?.leftYAxisFormat\n\n const { yAxisFields, seriesField, configError } = useMemo(() => {\n const yAxisFields: string[] = Array.isArray(chartConfig?.yAxis)\n ? chartConfig.yAxis\n : []\n const seriesField = Array.isArray(chartConfig?.series)\n ? chartConfig.series[0]\n : chartConfig?.series ?? undefined\n const configError =\n yAxisFields.length < 2 ? 'Measure Profile chart requires at least 2 measures in Y-Axis' : null\n return { yAxisFields, seriesField, configError }\n }, [chartConfig])\n\n const { profileData, seriesKeys } = useMemo(() => {\n if (configError || !data || data.length === 0) return { profileData: [], seriesKeys: [] }\n return pivotMeasuresToProfile(\n data as Record<string, unknown>[],\n yAxisFields,\n seriesField,\n getFieldLabel\n )\n }, [data, yAxisFields, seriesField, getFieldLabel, configError])\n\n const showLegend = (displayConfig?.showLegend ?? true) && seriesKeys.length > 1\n\n try {\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in measure profile chart</div>\n </div>\n </div>\n )\n }\n\n if (configError) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">{configError}</div>\n </div>\n </div>\n )\n }\n return (\n <div className=\"dc:relative dc:w-full\" style={{ height }}>\n <ChartContainer height=\"100%\">\n <LineChart data={profileData} margin={{ ...CHART_MARGINS, left: 40 }} accessibilityLayer={false}>\n <CartesianGrid strokeDasharray=\"3 3\" style={{ pointerEvents: 'none' }} />\n <XAxis dataKey=\"measureLabel\" type=\"category\" tick={<AngledXAxisTick />} height={60} />\n <YAxis\n tick={{ fontSize: 12 }}\n tickFormatter={yAxisFormat ? (v) => formatAxisValue(v, yAxisFormat) : undefined}\n />\n <ChartTooltip\n formatter={(value: any, name: any) => {\n if (value === null || value === undefined) return ['No data', name]\n const formatted = yAxisFormat ? formatAxisValue(value, yAxisFormat) : value?.toLocaleString?.() ?? value\n const displayName = name === '_value' ? (getFieldLabel(yAxisFields[0]?.split('.')[0]) || 'Value') : name\n return [formatted, displayName]\n }}\n />\n {showReferenceLineAtZero && (\n <ReferenceLine y={0} stroke=\"var(--dc-border, #94a3b8)\" strokeDasharray=\"4 2\" />\n )}\n {showLegend && (\n <Legend wrapperStyle={{ fontSize: '12px', paddingTop: '8px' }} />\n )}\n {seriesKeys.map((seriesKey, index) => (\n <Line\n key={seriesKey}\n type={lineType}\n dataKey={seriesKey}\n name={seriesKey === '_value' ? (getFieldLabel(yAxisFields[0]?.split('.')[0]) || 'Value') : seriesKey}\n stroke={\n (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) ||\n CHART_COLORS[index % CHART_COLORS.length]\n }\n strokeWidth={2}\n dot={showDataLabels ? { r: 4 } : { r: 3 }}\n activeDot={{ r: 5 }}\n label={showDataLabels ? { position: 'top', fontSize: 10 } : undefined}\n isAnimationActive={false}\n cursor={drillEnabled ? 'pointer' : undefined}\n />\n ))}\n </LineChart>\n </ChartContainer>\n </div>\n )\n } catch (error) {\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Measure Profile Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default MeasureProfileChart\n"],"mappings":";;;;;;;;AAwBA,SAAS,EACP,GACA,GACA,GACA,GACkE;AAClE,KAAI,CAAC,KAAQ,EAAK,WAAW,KAAK,EAAY,WAAW,EACvD,QAAO;EAAE,aAAa,EAAE;EAAE,YAAY,EAAE;EAAE;AAG5C,KAAI,GAAa;EACf,IAAM,IAAe,MAAM,KAAK,IAAI,IAAI,EAAK,KAAK,MAAQ,OAAO,EAAI,MAAgB,UAAU,CAAC,CAAC,CAAC;AAkBlG,SAAO;GAAE,aAjBW,EAAY,KAAK,MAAU;IAC7C,IAAM,IAAgC;KACpC,YAAY;KACZ,cAAc,EAAc,EAAM;KACnC;AACD,SAAK,IAAM,KAAa,GAAc;KAEpC,IAAM,IADe,EAAK,QAAQ,MAAQ,OAAO,EAAI,MAAgB,UAAU,KAAK,EAAU,CAE3F,KAAK,MAAM;MACV,IAAM,IAAI,EAAE;AACZ,aAAO,OAAO,KAAM,WAAW,IAAI,WAAW,OAAO,KAAK,GAAG,CAAC;OAC9D,CACD,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;AAC3B,OAAK,KAAa,EAAO,SAAS,IAAI,EAAO,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE,GAAG,EAAO,SAAS;;AAE5F,WAAO;KACP;GACoB,YAAY;GAAc;QAC3C;EACL,IAAM,IAAY;AAclB,SAAO;GAAE,aAbW,EAAY,KAAK,MAAU;IAC7C,IAAM,IAAS,EACZ,KAAK,MAAM;KACV,IAAM,IAAI,EAAE;AACZ,YAAO,OAAO,KAAM,WAAW,IAAI,WAAW,OAAO,KAAK,GAAG,CAAC;MAC9D,CACD,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;AAC3B,WAAO;KACL,YAAY;KACZ,cAAc,EAAc,EAAM;MACjC,IAAY,EAAO,SAAS,IAAI,EAAO,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE,GAAG,EAAO,SAAS;KACtF;KACD;GACoB,YAAY,CAAC,EAAU;GAAE;;;AAInD,IAAM,IAAsB,EAAM,KAAK,SAA6B,EAClE,SACA,gBACA,mBAAgB,EAAE,EAClB,YAAS,QACT,iBACA,mBACa;CACb,IAAM,IAAgB,GAAmB,EAEnC,IAA0B,GAAe,2BAA2B,IACpE,IAAiB,GAAe,kBAAkB,IAClD,IAAW,GAAe,YAAY,YACtC,IAAc,GAAe,iBAE7B,EAAE,gBAAa,gBAAa,mBAAgB,QAAc;EAC9D,IAAM,IAAwB,MAAM,QAAQ,GAAa,MAAM,GAC3D,EAAY,QACZ,EAAE;AAMN,SAAO;GAAE;GAAa,aALF,MAAM,QAAQ,GAAa,OAAO,GAClD,EAAY,OAAO,KACnB,GAAa,UAAU,KAAA;GAGQ,aADjC,EAAY,SAAS,IAAI,iEAAiE;GAC5C;IAC/C,CAAC,EAAY,CAAC,EAEX,EAAE,gBAAa,kBAAe,QAC9B,KAAe,CAAC,KAAQ,EAAK,WAAW,IAAU;EAAE,aAAa,EAAE;EAAE,YAAY,EAAE;EAAE,GAClF,EACL,GACA,GACA,GACA,EACD,EACA;EAAC;EAAM;EAAa;EAAa;EAAe;EAAY,CAAC,EAE1D,KAAc,GAAe,cAAc,OAAS,EAAW,SAAS;AAE9E,KAAI;AAsBF,SArBI,CAAC,KAAQ,EAAK,WAAW,IAEzB,kBAAC,OAAD;GAAK,WAAU;GAAyE,OAAO,EAAE,WAAQ;aACvG,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAsC;KAAuB,CAAA,EAC5E,kBAAC,OAAD;KAAK,WAAU;eAAoC;KAAwD,CAAA,CACvG;;GACF,CAAA,GAIN,IAEA,kBAAC,OAAD;GAAK,WAAU;GAAsE,OAAO,EAAE,WAAQ;aACpG,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAsC;KAAyB,CAAA,EAC9E,kBAAC,OAAD;KAAK,WAAU;eAAc;KAAkB,CAAA,CAC3C;;GACF,CAAA,GAIR,kBAAC,OAAD;GAAK,WAAU;GAAwB,OAAO,EAAE,WAAQ;aACtD,kBAAC,GAAD;IAAgB,QAAO;cACrB,kBAAC,GAAD;KAAW,MAAM;KAAa,QAAQ;MAAE,GAAG;MAAe,MAAM;MAAI;KAAE,oBAAoB;eAA1F;MACE,kBAAC,GAAD;OAAe,iBAAgB;OAAM,OAAO,EAAE,eAAe,QAAQ;OAAI,CAAA;MACzE,kBAAC,GAAD;OAAO,SAAQ;OAAe,MAAK;OAAW,MAAM,kBAAC,GAAD,EAAmB,CAAA;OAAE,QAAQ;OAAM,CAAA;MACvF,kBAAC,GAAD;OACE,MAAM,EAAE,UAAU,IAAI;OACtB,eAAe,KAAe,MAAM,EAAgB,GAAG,EAAY,GAAG,KAAA;OACtE,CAAA;MACF,kBAAC,GAAD,EACE,YAAY,GAAY,MAClB,KAAU,OAAoC,CAAC,WAAW,EAAK,GAG5D,CAFW,IAAc,EAAgB,GAAO,EAAY,GAAG,GAAO,kBAAkB,IAAI,GAC/E,MAAS,WAAY,EAAc,EAAY,IAAI,MAAM,IAAI,CAAC,GAAG,IAAI,UAAW,EACrE,EAEjC,CAAA;MACD,KACC,kBAAC,GAAD;OAAe,GAAG;OAAG,QAAO;OAA4B,iBAAgB;OAAQ,CAAA;MAEjF,KACC,kBAAC,GAAD,EAAQ,cAAc;OAAE,UAAU;OAAQ,YAAY;OAAO,EAAI,CAAA;MAElE,EAAW,KAAK,GAAW,MAC1B,kBAAC,GAAD;OAEE,MAAM;OACN,SAAS;OACT,MAAM,MAAc,WAAY,EAAc,EAAY,IAAI,MAAM,IAAI,CAAC,GAAG,IAAI,UAAW;OAC3F,QACG,GAAc,UAAU,EAAa,OAAO,IAAQ,EAAa,OAAO,WACzE,EAAa,IAAQ,EAAa;OAEpC,aAAa;OACb,KAAK,IAAiB,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,GAAG;OACzC,WAAW,EAAE,GAAG,GAAG;OACnB,OAAO,IAAiB;QAAE,UAAU;QAAO,UAAU;QAAI,GAAG,KAAA;OAC5D,mBAAmB;OACnB,QAAQ,IAAe,YAAY,KAAA;OACnC,EAdK,EAcL,CACF;MACQ;;IACG,CAAA;GACb,CAAA;UAED,GAAO;AACd,SACE,kBAAC,OAAD;GAAK,WAAU;GAAuF,OAAO,EAAE,WAAQ;aACrH,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAsC;MAAiC,CAAA;KACtF,kBAAC,OAAD;MAAK,WAAU;gBAAsB,aAAiB,QAAQ,EAAM,UAAU;MAAgC,CAAA;KAC9G,kBAAC,OAAD;MAAK,WAAU;gBAAgC;MAAsC,CAAA;KACjF;;GACF,CAAA;;EAGV"}
|
|
1
|
+
{"version":3,"file":"chart-measure-profile-CVlqGslU.js","names":[],"sources":["../../../src/client/components/charts/MeasureProfileChart.tsx"],"sourcesContent":["import React, { useMemo } from 'react'\nimport { LineChart, Line, XAxis, YAxis, CartesianGrid, ReferenceLine, Legend } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport AngledXAxisTick from './AngledXAxisTick'\nimport { CHART_COLORS, CHART_MARGINS } from '../../utils/chartConstants'\nimport { formatAxisValue } from '../../utils/chartUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps } from '../../types'\n\n/**\n * Pivots N measures from chartConfig.yAxis[] into sequential X-axis points.\n * Each measure becomes one X-axis category; its numeric value is Y.\n * When a series dimension is provided, values are averaged across all rows\n * that share the same series value.\n *\n * Input (normal cube rows):\n * [{ 'Markouts.avgMinus2m': 10, 'Markouts.avgAtEvent': 0, 'Trades.symbol': 'AAPL' },\n * { 'Markouts.avgMinus2m': 12, 'Markouts.avgAtEvent': 1, 'Trades.symbol': 'AAPL' }]\n *\n * Output (one row per measure, values averaged per series):\n * [{ measureKey: 'Markouts.avgMinus2m', measureLabel: <getFieldLabel result>, AAPL: 11 },\n * { measureKey: 'Markouts.avgAtEvent', measureLabel: <getFieldLabel result>, AAPL: 0.5 }]\n */\nfunction pivotMeasuresToProfile(\n data: Record<string, unknown>[],\n yAxisFields: string[],\n seriesField: string | undefined,\n getFieldLabel: (field: string) => string\n): { profileData: Record<string, unknown>[]; seriesKeys: string[] } {\n if (!data || data.length === 0 || yAxisFields.length === 0) {\n return { profileData: [], seriesKeys: [] }\n }\n\n if (seriesField) {\n const seriesValues = Array.from(new Set(data.map((row) => String(row[seriesField] ?? 'Unknown'))))\n const profileData = yAxisFields.map((field) => {\n const base: Record<string, unknown> = {\n measureKey: field,\n measureLabel: getFieldLabel(field),\n }\n for (const seriesVal of seriesValues) {\n const matchingRows = data.filter((row) => String(row[seriesField] ?? 'Unknown') === seriesVal)\n const values = matchingRows\n .map((r) => {\n const v = r[field]\n return typeof v === 'number' ? v : parseFloat(String(v ?? ''))\n })\n .filter((v) => !isNaN(v))\n base[seriesVal] = values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : null\n }\n return base\n })\n return { profileData, seriesKeys: seriesValues }\n } else {\n const VALUE_KEY = '_value'\n const profileData = yAxisFields.map((field) => {\n const values = data\n .map((r) => {\n const v = r[field]\n return typeof v === 'number' ? v : parseFloat(String(v ?? ''))\n })\n .filter((v) => !isNaN(v))\n return {\n measureKey: field,\n measureLabel: getFieldLabel(field),\n [VALUE_KEY]: values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : null,\n }\n })\n return { profileData, seriesKeys: [VALUE_KEY] }\n }\n}\n\nconst MeasureProfileChart = React.memo(function MeasureProfileChart({\n data,\n chartConfig,\n displayConfig = {},\n height = '100%',\n colorPalette,\n drillEnabled,\n}: ChartProps) {\n const getFieldLabel = useCubeFieldLabel()\n\n const showReferenceLineAtZero = displayConfig?.showReferenceLineAtZero ?? true\n const showDataLabels = displayConfig?.showDataLabels ?? false\n const lineType = displayConfig?.lineType ?? 'monotone'\n const yAxisFormat = displayConfig?.leftYAxisFormat\n\n const { yAxisFields, seriesField, configError } = useMemo(() => {\n const yAxisFields: string[] = Array.isArray(chartConfig?.yAxis)\n ? chartConfig.yAxis\n : []\n const seriesField = Array.isArray(chartConfig?.series)\n ? chartConfig.series[0]\n : chartConfig?.series ?? undefined\n const configError =\n yAxisFields.length < 2 ? 'Measure Profile chart requires at least 2 measures in Y-Axis' : null\n return { yAxisFields, seriesField, configError }\n }, [chartConfig])\n\n const { profileData, seriesKeys } = useMemo(() => {\n if (configError || !data || data.length === 0) return { profileData: [], seriesKeys: [] }\n return pivotMeasuresToProfile(\n data as Record<string, unknown>[],\n yAxisFields,\n seriesField,\n getFieldLabel\n )\n }, [data, yAxisFields, seriesField, getFieldLabel, configError])\n\n const showLegend = (displayConfig?.showLegend ?? true) && seriesKeys.length > 1\n\n try {\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in measure profile chart</div>\n </div>\n </div>\n )\n }\n\n if (configError) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">{configError}</div>\n </div>\n </div>\n )\n }\n return (\n <div className=\"dc:relative dc:w-full\" style={{ height }}>\n <ChartContainer height=\"100%\">\n <LineChart data={profileData} margin={{ ...CHART_MARGINS, left: 40 }} accessibilityLayer={false}>\n <CartesianGrid strokeDasharray=\"3 3\" style={{ pointerEvents: 'none' }} />\n <XAxis dataKey=\"measureLabel\" type=\"category\" tick={<AngledXAxisTick />} height={60} />\n <YAxis\n tick={{ fontSize: 12 }}\n tickFormatter={yAxisFormat ? (v) => formatAxisValue(v, yAxisFormat) : undefined}\n />\n <ChartTooltip\n formatter={(value: any, name: any) => {\n if (value === null || value === undefined) return ['No data', name]\n const formatted = yAxisFormat ? formatAxisValue(value, yAxisFormat) : value?.toLocaleString?.() ?? value\n const displayName = name === '_value' ? (getFieldLabel(yAxisFields[0]?.split('.')[0]) || 'Value') : name\n return [formatted, displayName]\n }}\n />\n {showReferenceLineAtZero && (\n <ReferenceLine y={0} stroke=\"var(--dc-border, #94a3b8)\" strokeDasharray=\"4 2\" />\n )}\n {showLegend && (\n <Legend wrapperStyle={{ fontSize: '12px', paddingTop: '8px' }} />\n )}\n {seriesKeys.map((seriesKey, index) => (\n <Line\n key={seriesKey}\n type={lineType}\n dataKey={seriesKey}\n name={seriesKey === '_value' ? (getFieldLabel(yAxisFields[0]?.split('.')[0]) || 'Value') : seriesKey}\n stroke={\n (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) ||\n CHART_COLORS[index % CHART_COLORS.length]\n }\n strokeWidth={2}\n dot={showDataLabels ? { r: 4 } : { r: 3 }}\n activeDot={{ r: 5 }}\n label={showDataLabels ? { position: 'top', fontSize: 10 } : undefined}\n isAnimationActive={false}\n cursor={drillEnabled ? 'pointer' : undefined}\n />\n ))}\n </LineChart>\n </ChartContainer>\n </div>\n )\n } catch (error) {\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Measure Profile Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default MeasureProfileChart\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as e } from "./rolldown-runtime-lc2dmIiU.js";
|
|
2
|
-
import { D as t, S as n, _ as r, n as i, x as a, y as o } from "./chart-activity-grid-
|
|
3
|
-
import { c as s, l as c } from "./chart-area-
|
|
2
|
+
import { D as t, S as n, _ as r, n as i, x as a, y as o } from "./chart-activity-grid-wR2Twpo7.js";
|
|
3
|
+
import { c as s, l as c } from "./chart-area-e9ysnatQ.js";
|
|
4
4
|
import l, { useState as u } from "react";
|
|
5
5
|
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
6
6
|
import { Cell as p, Legend as m, Pie as h, PieChart as g } from "recharts";
|
|
@@ -169,4 +169,4 @@ var _ = /* @__PURE__ */ e({ default: () => v }), v = l.memo(function({ data: e,
|
|
|
169
169
|
//#endregion
|
|
170
170
|
export { _ as n, v as t };
|
|
171
171
|
|
|
172
|
-
//# sourceMappingURL=chart-pie-
|
|
172
|
+
//# sourceMappingURL=chart-pie-DafSc9sE.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-pie-BU_FgwDc.js","names":[],"sources":["../../../src/client/components/charts/PieChart.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport { PieChart as RechartsPieChart, Pie, Cell, Legend } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport { transformChartDataWithSeries, formatTimeValue, getFieldGranularity, formatAxisValue } from '../../utils/chartUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps } from '../../types'\n\nconst PieChart = React.memo(function PieChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n \n try {\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n leftYAxisFormat: displayConfig?.leftYAxisFormat,\n innerRadius: displayConfig?.innerRadius || '0%'\n }\n\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in pie chart</div>\n </div>\n </div>\n )\n }\n\n let pieData: Array<{name: string, value: number}>\n\n // Validate chartConfig - support both legacy and new formats\n let xAxisField: string\n let yAxisFields: string[]\n let seriesFields: string[] = []\n \n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format\n xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis\n yAxisFields = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis : [chartConfig.yAxis]\n seriesFields = chartConfig.series || []\n } else if (chartConfig?.x && chartConfig?.y) {\n // Legacy format\n xAxisField = chartConfig.x\n yAxisFields = Array.isArray(chartConfig.y) ? chartConfig.y : [chartConfig.y]\n } else {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">chartConfig.x/y or chartConfig.xAxis/yAxis required for pie chart</div>\n </div>\n </div>\n )\n }\n\n if (!xAxisField || !yAxisFields || yAxisFields.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">Missing required X-axis or Y-axis fields</div>\n </div>\n </div>\n )\n }\n\n if (seriesFields.length > 0) {\n // Use series-based transformation for dimension-based pie slices\n const { data: chartData } = transformChartDataWithSeries(\n data,\n xAxisField,\n yAxisFields,\n queryObject,\n seriesFields,\n getFieldLabel\n )\n \n // Convert series data to pie format\n pieData = []\n if (chartData.length > 0) {\n const firstRow = chartData[0]\n Object.keys(firstRow).forEach(key => {\n if (key !== 'name' && typeof firstRow[key] === 'number') {\n pieData.push({\n name: String(key),\n value: firstRow[key]\n })\n }\n })\n }\n } else {\n // Standard measure-based pie chart\n const granularity = getFieldGranularity(queryObject, xAxisField)\n pieData = data.map(item => {\n let name = formatTimeValue(item[xAxisField], granularity) || String(item[xAxisField]) || 'Unknown'\n // Handle boolean values with better labels\n if (typeof item[xAxisField] === 'boolean') {\n name = item[xAxisField] ? 'Active' : 'Inactive'\n } else if (name === 'true' || name === 'false') {\n name = name === 'true' ? 'Active' : 'Inactive'\n }\n return {\n name,\n value: typeof item[yAxisFields[0]] === 'string' \n ? parseFloat(item[yAxisFields[0]]) \n : (item[yAxisFields[0]] || 0)\n }\n })\n }\n\n // Filter out invalid values (null, undefined, NaN, or zero)\n const originalLength = pieData.length\n pieData = pieData.filter(item => \n item.value != null && \n !isNaN(item.value) && \n item.value !== 0 && \n item.value > 0\n )\n \n if (pieData.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No valid data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {originalLength > 0\n ? `Filtered out ${originalLength} data points (zero or invalid values)`\n : 'No data points to display in pie chart'\n }\n </div>\n </div>\n </div>\n )\n }\n \n return (\n <ChartContainer height={height}>\n <RechartsPieChart accessibilityLayer={false}>\n <Pie\n data={pieData}\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={safeDisplayConfig.innerRadius !== '0%' ? safeDisplayConfig.innerRadius : undefined}\n outerRadius=\"70%\"\n dataKey=\"value\"\n label={!safeDisplayConfig.showLegend ? ({ name, percent }) =>\n `${name} ${((percent || 0) * 100).toFixed(0)}%`\n : undefined}\n cursor={drillEnabled ? 'pointer' : undefined}\n onClick={(sliceData: any, _index: number, event: React.MouseEvent) => {\n if (onDataPointClick && drillEnabled && sliceData) {\n onDataPointClick({\n dataPoint: sliceData,\n clickedField: yAxisFields[0],\n xValue: sliceData.name,\n position: { x: event.clientX, y: event.clientY },\n nativeEvent: event\n })\n }\n }}\n >\n {pieData.map((_entry, index) => (\n <Cell\n key={`cell-${index}`}\n fill={(colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]}\n fillOpacity={hoveredLegend ? (hoveredLegend === pieData[index].name ? 1 : 0.3) : 1}\n />\n ))}\n </Pie>\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={safeDisplayConfig.leftYAxisFormat\n ? (value: any, name: string) => [formatAxisValue(value, safeDisplayConfig.leftYAxisFormat), name]\n : undefined\n }\n />\n )}\n {safeDisplayConfig.showLegend && (\n <Legend \n wrapperStyle={{ fontSize: '12px', paddingTop: '10px' }}\n iconType=\"circle\"\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => setHoveredLegend(String(o.value || ''))}\n onMouseLeave={() => setHoveredLegend(null)}\n />\n )}\n </RechartsPieChart>\n </ChartContainer>\n )\n } catch (error) {\n // 'PieChart rendering error\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Pie Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default PieChart"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chart-pie-DafSc9sE.js","names":[],"sources":["../../../src/client/components/charts/PieChart.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport { PieChart as RechartsPieChart, Pie, Cell, Legend } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport { transformChartDataWithSeries, formatTimeValue, getFieldGranularity, formatAxisValue } from '../../utils/chartUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps } from '../../types'\n\nconst PieChart = React.memo(function PieChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n \n try {\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n leftYAxisFormat: displayConfig?.leftYAxisFormat,\n innerRadius: displayConfig?.innerRadius || '0%'\n }\n\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in pie chart</div>\n </div>\n </div>\n )\n }\n\n let pieData: Array<{name: string, value: number}>\n\n // Validate chartConfig - support both legacy and new formats\n let xAxisField: string\n let yAxisFields: string[]\n let seriesFields: string[] = []\n \n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format\n xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis\n yAxisFields = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis : [chartConfig.yAxis]\n seriesFields = chartConfig.series || []\n } else if (chartConfig?.x && chartConfig?.y) {\n // Legacy format\n xAxisField = chartConfig.x\n yAxisFields = Array.isArray(chartConfig.y) ? chartConfig.y : [chartConfig.y]\n } else {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">chartConfig.x/y or chartConfig.xAxis/yAxis required for pie chart</div>\n </div>\n </div>\n )\n }\n\n if (!xAxisField || !yAxisFields || yAxisFields.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">Missing required X-axis or Y-axis fields</div>\n </div>\n </div>\n )\n }\n\n if (seriesFields.length > 0) {\n // Use series-based transformation for dimension-based pie slices\n const { data: chartData } = transformChartDataWithSeries(\n data,\n xAxisField,\n yAxisFields,\n queryObject,\n seriesFields,\n getFieldLabel\n )\n \n // Convert series data to pie format\n pieData = []\n if (chartData.length > 0) {\n const firstRow = chartData[0]\n Object.keys(firstRow).forEach(key => {\n if (key !== 'name' && typeof firstRow[key] === 'number') {\n pieData.push({\n name: String(key),\n value: firstRow[key]\n })\n }\n })\n }\n } else {\n // Standard measure-based pie chart\n const granularity = getFieldGranularity(queryObject, xAxisField)\n pieData = data.map(item => {\n let name = formatTimeValue(item[xAxisField], granularity) || String(item[xAxisField]) || 'Unknown'\n // Handle boolean values with better labels\n if (typeof item[xAxisField] === 'boolean') {\n name = item[xAxisField] ? 'Active' : 'Inactive'\n } else if (name === 'true' || name === 'false') {\n name = name === 'true' ? 'Active' : 'Inactive'\n }\n return {\n name,\n value: typeof item[yAxisFields[0]] === 'string' \n ? parseFloat(item[yAxisFields[0]]) \n : (item[yAxisFields[0]] || 0)\n }\n })\n }\n\n // Filter out invalid values (null, undefined, NaN, or zero)\n const originalLength = pieData.length\n pieData = pieData.filter(item => \n item.value != null && \n !isNaN(item.value) && \n item.value !== 0 && \n item.value > 0\n )\n \n if (pieData.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No valid data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {originalLength > 0\n ? `Filtered out ${originalLength} data points (zero or invalid values)`\n : 'No data points to display in pie chart'\n }\n </div>\n </div>\n </div>\n )\n }\n \n return (\n <ChartContainer height={height}>\n <RechartsPieChart accessibilityLayer={false}>\n <Pie\n data={pieData}\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={safeDisplayConfig.innerRadius !== '0%' ? safeDisplayConfig.innerRadius : undefined}\n outerRadius=\"70%\"\n dataKey=\"value\"\n label={!safeDisplayConfig.showLegend ? ({ name, percent }) =>\n `${name} ${((percent || 0) * 100).toFixed(0)}%`\n : undefined}\n cursor={drillEnabled ? 'pointer' : undefined}\n onClick={(sliceData: any, _index: number, event: React.MouseEvent) => {\n if (onDataPointClick && drillEnabled && sliceData) {\n onDataPointClick({\n dataPoint: sliceData,\n clickedField: yAxisFields[0],\n xValue: sliceData.name,\n position: { x: event.clientX, y: event.clientY },\n nativeEvent: event\n })\n }\n }}\n >\n {pieData.map((_entry, index) => (\n <Cell\n key={`cell-${index}`}\n fill={(colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]}\n fillOpacity={hoveredLegend ? (hoveredLegend === pieData[index].name ? 1 : 0.3) : 1}\n />\n ))}\n </Pie>\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={safeDisplayConfig.leftYAxisFormat\n ? (value: any, name: string) => [formatAxisValue(value, safeDisplayConfig.leftYAxisFormat), name]\n : undefined\n }\n />\n )}\n {safeDisplayConfig.showLegend && (\n <Legend \n wrapperStyle={{ fontSize: '12px', paddingTop: '10px' }}\n iconType=\"circle\"\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => setHoveredLegend(String(o.value || ''))}\n onMouseLeave={() => setHoveredLegend(null)}\n />\n )}\n </RechartsPieChart>\n </ChartContainer>\n )\n } catch (error) {\n // 'PieChart rendering error\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Pie Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default PieChart"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as e } from "./rolldown-runtime-lc2dmIiU.js";
|
|
2
|
-
import { D as t, S as n, n as r, x as i, y as a } from "./chart-activity-grid-
|
|
3
|
-
import { c as o, l as s } from "./chart-area-
|
|
2
|
+
import { D as t, S as n, n as r, x as i, y as a } from "./chart-activity-grid-wR2Twpo7.js";
|
|
3
|
+
import { c as o, l as s } from "./chart-area-e9ysnatQ.js";
|
|
4
4
|
import c, { useState as l } from "react";
|
|
5
5
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
6
6
|
import { Legend as f, PolarAngleAxis as p, PolarGrid as m, PolarRadiusAxis as h, Radar as g, RadarChart as _ } from "recharts";
|
|
@@ -151,4 +151,4 @@ var v = /* @__PURE__ */ e({ default: () => y }), y = c.memo(function({ data: e,
|
|
|
151
151
|
//#endregion
|
|
152
152
|
export { v as n, y as t };
|
|
153
153
|
|
|
154
|
-
//# sourceMappingURL=chart-radar-
|
|
154
|
+
//# sourceMappingURL=chart-radar-Dz9F5k-B.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-radar-DlufwnAX.js","names":[],"sources":["../../../src/client/components/charts/RadarChart.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport { RadarChart as RechartsRadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Radar, Legend } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport { transformChartDataWithSeries, formatTimeValue, getFieldGranularity, formatAxisValue } from '../../utils/chartUtils'\nimport type { ChartProps } from '../../types'\n\nconst RadarChart = React.memo(function RadarChart({ \n data, \n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette\n}: ChartProps) {\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n \n try {\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n showGrid: displayConfig?.showGrid ?? true,\n leftYAxisFormat: displayConfig?.leftYAxisFormat\n }\n\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in radar chart</div>\n </div>\n </div>\n )\n }\n\n let radarData: any[]\n let seriesKeys: string[] = []\n\n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format - use chart config\n const xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis // Subject/category field\n const yAxisFields = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis : [chartConfig.yAxis] // Value fields\n const seriesFields = chartConfig.series || []\n\n // Use shared function to transform data and handle series\n const { data: chartData, seriesKeys: transformedSeriesKeys } = transformChartDataWithSeries(\n data, \n xAxisField, \n yAxisFields, \n queryObject,\n seriesFields\n )\n \n radarData = chartData\n seriesKeys = transformedSeriesKeys\n } else {\n // Legacy format or auto-detection - try to find suitable fields\n const firstRow = data[0]\n const keys = Object.keys(firstRow)\n \n // Try to find subject/category field\n const subjectField = keys.find(key => \n typeof firstRow[key] === 'string' ||\n key.toLowerCase().includes('subject') ||\n key.toLowerCase().includes('name') ||\n key.toLowerCase().includes('category')\n ) || keys[0]\n\n // Find numeric fields for values\n const valueFields = keys.filter(key => \n typeof firstRow[key] === 'number' && key !== subjectField\n )\n\n if (valueFields.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">No numeric fields found for radar chart values</div>\n </div>\n </div>\n )\n }\n\n // Transform data for radar chart\n if (subjectField) {\n // Use subject field for radar categories\n const granularity = getFieldGranularity(queryObject, subjectField)\n radarData = data.map(item => {\n const transformedItem: any = {\n name: formatTimeValue(item[subjectField], granularity) || String(item[subjectField]) || 'Unknown'\n }\n \n valueFields.forEach(field => {\n const displayName = field.split('.').pop() || field\n transformedItem[displayName] = typeof item[field] === 'string' \n ? parseFloat(item[field]) \n : (item[field] || 0)\n })\n \n return transformedItem\n })\n \n seriesKeys = valueFields.map(field => field.split('.').pop() || field)\n } else {\n // Fallback - use first value field only\n radarData = data.map(item => ({\n name: String(item[keys[0]] || 'Unknown'),\n value: typeof item[valueFields[0]] === 'string' \n ? parseFloat(item[valueFields[0]]) \n : (item[valueFields[0]] || 0)\n }))\n seriesKeys = ['value']\n }\n }\n \n // Validate transformed data\n if (!radarData || radarData.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No valid data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No valid data points for radar chart after transformation</div>\n </div>\n </div>\n )\n }\n\n return (\n <ChartContainer height={height}>\n <RechartsRadarChart data={radarData} margin={{ top: 20, right: 80, bottom: 20, left: 80 }} accessibilityLayer={false}>\n {safeDisplayConfig.showGrid && (\n <PolarGrid />\n )}\n <PolarAngleAxis\n dataKey=\"name\"\n tick={{ fontSize: 12 }}\n className=\"text-dc-text-muted\"\n />\n <PolarRadiusAxis\n tick={{ fontSize: 10 }}\n className=\"text-dc-text-muted\"\n tickFormatter={safeDisplayConfig.leftYAxisFormat\n ? (value: any) => formatAxisValue(value, safeDisplayConfig.leftYAxisFormat)\n : undefined\n }\n />\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={safeDisplayConfig.leftYAxisFormat\n ? (value: any, name: string) => [formatAxisValue(value, safeDisplayConfig.leftYAxisFormat), name]\n : undefined\n }\n />\n )}\n {(safeDisplayConfig.showLegend && seriesKeys.length > 1) && (\n <Legend \n wrapperStyle={{ fontSize: '12px', paddingTop: '10px' }}\n iconType=\"rect\"\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => setHoveredLegend(String(o.dataKey || ''))}\n onMouseLeave={() => setHoveredLegend(null)}\n />\n )}\n {seriesKeys.map((seriesKey, index) => (\n <Radar\n key={seriesKey}\n name={seriesKey}\n dataKey={seriesKey}\n stroke={(colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]}\n fill={(colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]}\n fillOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 0.6 : 0.1) : 0.3}\n strokeOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 1 : 0.3) : 1}\n strokeWidth={2}\n />\n ))}\n </RechartsRadarChart>\n </ChartContainer>\n )\n } catch (error) {\n // 'RadarChart rendering error\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Radar Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default RadarChart"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chart-radar-Dz9F5k-B.js","names":[],"sources":["../../../src/client/components/charts/RadarChart.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport { RadarChart as RechartsRadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Radar, Legend } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport { transformChartDataWithSeries, formatTimeValue, getFieldGranularity, formatAxisValue } from '../../utils/chartUtils'\nimport type { ChartProps } from '../../types'\n\nconst RadarChart = React.memo(function RadarChart({ \n data, \n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette\n}: ChartProps) {\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n \n try {\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n showGrid: displayConfig?.showGrid ?? true,\n leftYAxisFormat: displayConfig?.leftYAxisFormat\n }\n\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in radar chart</div>\n </div>\n </div>\n )\n }\n\n let radarData: any[]\n let seriesKeys: string[] = []\n\n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format - use chart config\n const xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis // Subject/category field\n const yAxisFields = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis : [chartConfig.yAxis] // Value fields\n const seriesFields = chartConfig.series || []\n\n // Use shared function to transform data and handle series\n const { data: chartData, seriesKeys: transformedSeriesKeys } = transformChartDataWithSeries(\n data, \n xAxisField, \n yAxisFields, \n queryObject,\n seriesFields\n )\n \n radarData = chartData\n seriesKeys = transformedSeriesKeys\n } else {\n // Legacy format or auto-detection - try to find suitable fields\n const firstRow = data[0]\n const keys = Object.keys(firstRow)\n \n // Try to find subject/category field\n const subjectField = keys.find(key => \n typeof firstRow[key] === 'string' ||\n key.toLowerCase().includes('subject') ||\n key.toLowerCase().includes('name') ||\n key.toLowerCase().includes('category')\n ) || keys[0]\n\n // Find numeric fields for values\n const valueFields = keys.filter(key => \n typeof firstRow[key] === 'number' && key !== subjectField\n )\n\n if (valueFields.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">No numeric fields found for radar chart values</div>\n </div>\n </div>\n )\n }\n\n // Transform data for radar chart\n if (subjectField) {\n // Use subject field for radar categories\n const granularity = getFieldGranularity(queryObject, subjectField)\n radarData = data.map(item => {\n const transformedItem: any = {\n name: formatTimeValue(item[subjectField], granularity) || String(item[subjectField]) || 'Unknown'\n }\n \n valueFields.forEach(field => {\n const displayName = field.split('.').pop() || field\n transformedItem[displayName] = typeof item[field] === 'string' \n ? parseFloat(item[field]) \n : (item[field] || 0)\n })\n \n return transformedItem\n })\n \n seriesKeys = valueFields.map(field => field.split('.').pop() || field)\n } else {\n // Fallback - use first value field only\n radarData = data.map(item => ({\n name: String(item[keys[0]] || 'Unknown'),\n value: typeof item[valueFields[0]] === 'string' \n ? parseFloat(item[valueFields[0]]) \n : (item[valueFields[0]] || 0)\n }))\n seriesKeys = ['value']\n }\n }\n \n // Validate transformed data\n if (!radarData || radarData.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No valid data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No valid data points for radar chart after transformation</div>\n </div>\n </div>\n )\n }\n\n return (\n <ChartContainer height={height}>\n <RechartsRadarChart data={radarData} margin={{ top: 20, right: 80, bottom: 20, left: 80 }} accessibilityLayer={false}>\n {safeDisplayConfig.showGrid && (\n <PolarGrid />\n )}\n <PolarAngleAxis\n dataKey=\"name\"\n tick={{ fontSize: 12 }}\n className=\"text-dc-text-muted\"\n />\n <PolarRadiusAxis\n tick={{ fontSize: 10 }}\n className=\"text-dc-text-muted\"\n tickFormatter={safeDisplayConfig.leftYAxisFormat\n ? (value: any) => formatAxisValue(value, safeDisplayConfig.leftYAxisFormat)\n : undefined\n }\n />\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={safeDisplayConfig.leftYAxisFormat\n ? (value: any, name: string) => [formatAxisValue(value, safeDisplayConfig.leftYAxisFormat), name]\n : undefined\n }\n />\n )}\n {(safeDisplayConfig.showLegend && seriesKeys.length > 1) && (\n <Legend \n wrapperStyle={{ fontSize: '12px', paddingTop: '10px' }}\n iconType=\"rect\"\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => setHoveredLegend(String(o.dataKey || ''))}\n onMouseLeave={() => setHoveredLegend(null)}\n />\n )}\n {seriesKeys.map((seriesKey, index) => (\n <Radar\n key={seriesKey}\n name={seriesKey}\n dataKey={seriesKey}\n stroke={(colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]}\n fill={(colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]}\n fillOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 0.6 : 0.1) : 0.3}\n strokeOpacity={hoveredLegend ? (hoveredLegend === seriesKey ? 1 : 0.3) : 1}\n strokeWidth={2}\n />\n ))}\n </RechartsRadarChart>\n </ChartContainer>\n )\n } catch (error) {\n // 'RadarChart rendering error\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Radar Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default RadarChart"],"mappings":";;;;;;;iDAQM,IAAa,EAAM,KAAK,SAAoB,EAChD,SACA,gBACA,mBAAgB,EAAE,EAClB,gBACA,YAAS,QACT,mBACa;CACb,IAAM,CAAC,GAAe,KAAoB,EAAwB,KAAK;AAEvE,KAAI;EACF,IAAM,IAAoB;GACxB,YAAY,GAAe,cAAc;GACzC,aAAa,GAAe,eAAe;GAC3C,UAAU,GAAe,YAAY;GACrC,iBAAiB,GAAe;GACjC;AAED,MAAI,CAAC,KAAQ,EAAK,WAAW,EAC3B,QACE,kBAAC,OAAD;GAAK,WAAU;GAAyE,OAAO,EAAE,WAAQ;aACvG,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAsC;KAAuB,CAAA,EAC5E,kBAAC,OAAD;KAAK,WAAU;eAAoC;KAA8C,CAAA,CAC7F;;GACF,CAAA;EAIV,IAAI,GACA,IAAuB,EAAE;AAE7B,MAAI,GAAa,SAAS,GAAa,OAAO;GAO5C,IAAM,EAAE,MAAM,GAAW,YAAY,MAA0B,EAC7D,GANiB,MAAM,QAAQ,EAAY,MAAM,GAAG,EAAY,MAAM,KAAK,EAAY,OACrE,MAAM,QAAQ,EAAY,MAAM,GAAG,EAAY,QAAQ,CAAC,EAAY,MAAM,EAQ5F,GAPmB,EAAY,UAAU,EAAE,CAS5C;AAGD,GADA,IAAY,GACZ,IAAa;SACR;GAEL,IAAM,IAAW,EAAK,IAChB,IAAO,OAAO,KAAK,EAAS,EAG5B,IAAe,EAAK,MAAK,MAC7B,OAAO,EAAS,MAAS,YACzB,EAAI,aAAa,CAAC,SAAS,UAAU,IACrC,EAAI,aAAa,CAAC,SAAS,OAAO,IAClC,EAAI,aAAa,CAAC,SAAS,WAAW,CACvC,IAAI,EAAK,IAGJ,IAAc,EAAK,QAAO,MAC9B,OAAO,EAAS,MAAS,YAAY,MAAQ,EAC9C;AAED,OAAI,EAAY,WAAW,EACzB,QACE,kBAAC,OAAD;IAAK,WAAU;IAAsE,OAAO,EAAE,WAAQ;cACpG,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAsC;MAAyB,CAAA,EAC9E,kBAAC,OAAD;MAAK,WAAU;gBAAa;MAAoD,CAAA,CAC5E;;IACF,CAAA;AAKV,OAAI,GAAc;IAEhB,IAAM,IAAc,EAAoB,GAAa,EAAa;AAgBlE,IAfA,IAAY,EAAK,KAAI,MAAQ;KAC3B,IAAM,IAAuB,EAC3B,MAAM,EAAgB,EAAK,IAAe,EAAY,IAAI,OAAO,EAAK,GAAc,IAAI,WACzF;AASD,YAPA,EAAY,SAAQ,MAAS;MAC3B,IAAM,IAAc,EAAM,MAAM,IAAI,CAAC,KAAK,IAAI;AAC9C,QAAgB,KAAe,OAAO,EAAK,MAAW,WAClD,WAAW,EAAK,GAAO,GACtB,EAAK,MAAU;OACpB,EAEK;MACP,EAEF,IAAa,EAAY,KAAI,MAAS,EAAM,MAAM,IAAI,CAAC,KAAK,IAAI,EAAM;SAStE,CANA,IAAY,EAAK,KAAI,OAAS;IAC5B,MAAM,OAAO,EAAK,EAAK,OAAO,UAAU;IACxC,OAAO,OAAO,EAAK,EAAY,OAAQ,WACnC,WAAW,EAAK,EAAY,IAAI,GAC/B,EAAK,EAAY,OAAO;IAC9B,EAAE,EACH,IAAa,CAAC,QAAQ;;AAgB1B,SAXI,CAAC,KAAa,EAAU,WAAW,IAEnC,kBAAC,OAAD;GAAK,WAAU;GAAyE,OAAO,EAAE,WAAQ;aACvG,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAsC;KAAmB,CAAA,EACxE,kBAAC,OAAD;KAAK,WAAU;eAAoC;KAA+D,CAAA,CAC9G;;GACF,CAAA,GAKR,kBAAC,GAAD;GAAwB;aACtB,kBAAC,GAAD;IAAoB,MAAM;IAAW,QAAQ;KAAE,KAAK;KAAI,OAAO;KAAI,QAAQ;KAAI,MAAM;KAAI;IAAE,oBAAoB;cAA/G;KACG,EAAkB,YACjB,kBAAC,GAAD,EAAa,CAAA;KAEf,kBAAC,GAAD;MACE,SAAQ;MACR,MAAM,EAAE,UAAU,IAAI;MACtB,WAAU;MACV,CAAA;KACF,kBAAC,GAAD;MACE,MAAM,EAAE,UAAU,IAAI;MACtB,WAAU;MACV,eAAe,EAAkB,mBAC5B,MAAe,EAAgB,GAAO,EAAkB,gBAAgB,GACzE,KAAA;MAEJ,CAAA;KACD,EAAkB,eACjB,kBAAC,GAAD,EACE,WAAW,EAAkB,mBACxB,GAAY,MAAiB,CAAC,EAAgB,GAAO,EAAkB,gBAAgB,EAAE,EAAK,GAC/F,KAAA,GAEJ,CAAA;KAEF,EAAkB,cAAc,EAAW,SAAS,KACpD,kBAAC,GAAD;MACE,cAAc;OAAE,UAAU;OAAQ,YAAY;OAAQ;MACtD,UAAS;MACT,UAAU;MACV,QAAO;MACP,OAAM;MACN,eAAc;MACd,eAAe,MAAM,EAAiB,OAAO,EAAE,WAAW,GAAG,CAAC;MAC9D,oBAAoB,EAAiB,KAAK;MAC1C,CAAA;KAEH,EAAW,KAAK,GAAW,MAC1B,kBAAC,GAAD;MAEE,MAAM;MACN,SAAS;MACT,QAAS,GAAc,UAAU,EAAa,OAAO,IAAQ,EAAa,OAAO,WAAY,EAAa,IAAQ,EAAa;MAC/H,MAAO,GAAc,UAAU,EAAa,OAAO,IAAQ,EAAa,OAAO,WAAY,EAAa,IAAQ,EAAa;MAC7H,aAAa,IAAiB,MAAkB,IAAY,KAAM,KAAO;MACzE,eAAe,IAAiB,MAAkB,IAAY,IAAI,KAAO;MACzE,aAAa;MACb,EARK,EAQL,CACF;KACiB;;GACN,CAAA;UAEZ,GAAO;AAEd,SACE,kBAAC,OAAD;GAAK,WAAU;GAAuF,OAAO,EAAE,WAAQ;aACrH,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAsC;MAAuB,CAAA;KAC5E,kBAAC,OAAD;MAAK,WAAU;gBAAsB,aAAiB,QAAQ,EAAM,UAAU;MAAgC,CAAA;KAC9G,kBAAC,OAAD;MAAK,WAAU;gBAAgC;MAAsC,CAAA;KACjF;;GACF,CAAA;;EAGV"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as e } from "./rolldown-runtime-lc2dmIiU.js";
|
|
2
|
-
import { S as t, n, x as r, y as i } from "./chart-activity-grid-
|
|
3
|
-
import { c as a, l as o } from "./chart-area-
|
|
2
|
+
import { S as t, n, x as r, y as i } from "./chart-activity-grid-wR2Twpo7.js";
|
|
3
|
+
import { c as a, l as o } from "./chart-area-e9ysnatQ.js";
|
|
4
4
|
import s, { useState as c } from "react";
|
|
5
5
|
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
6
6
|
import { Cell as d, Legend as f, RadialBar as p, RadialBarChart as m } from "recharts";
|
|
@@ -145,4 +145,4 @@ var h = /* @__PURE__ */ e({ default: () => g }), g = s.memo(function({ data: e,
|
|
|
145
145
|
//#endregion
|
|
146
146
|
export { h as n, g as t };
|
|
147
147
|
|
|
148
|
-
//# sourceMappingURL=chart-radial-bar-
|
|
148
|
+
//# sourceMappingURL=chart-radial-bar-N3MNUL7o.js.map
|
package/dist/client/chunks/{chart-radial-bar-B5vS_Aw5.js.map → chart-radial-bar-N3MNUL7o.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-radial-bar-B5vS_Aw5.js","names":[],"sources":["../../../src/client/components/charts/RadialBarChart.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport { RadialBarChart as RechartsRadialBarChart, RadialBar, Legend, Cell } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport { formatTimeValue, getFieldGranularity, formatAxisValue } from '../../utils/chartUtils'\nimport type { ChartProps } from '../../types'\n\nconst RadialBarChart = React.memo(function RadialBarChart({ \n data, \n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette\n}: ChartProps) {\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n \n try {\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n leftYAxisFormat: displayConfig?.leftYAxisFormat\n }\n\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in radial bar chart</div>\n </div>\n </div>\n )\n }\n\n let radialData: Array<{name: string, value: number, fill?: string}>\n\n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format - use chart config\n const xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis // Name/category field\n const yAxisField = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis[0] : chartConfig.yAxis // Value field\n\n const granularity = getFieldGranularity(queryObject, xAxisField)\n radialData = data.map((item, index) => ({\n name: formatTimeValue(item[xAxisField], granularity) || String(item[xAxisField]) || 'Unknown',\n value: typeof item[yAxisField] === 'string' \n ? parseFloat(item[yAxisField]) \n : (item[yAxisField] || 0),\n fill: (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]\n }))\n } else {\n // Legacy format or auto-detection\n const firstRow = data[0]\n const keys = Object.keys(firstRow)\n \n // Try to find name/label field\n const nameField = keys.find(key => \n typeof firstRow[key] === 'string' ||\n key.toLowerCase().includes('name') ||\n key.toLowerCase().includes('label') ||\n key.toLowerCase().includes('category')\n ) || keys[0]\n\n // Find a numeric field for values\n const valueField = keys.find(key => \n typeof firstRow[key] === 'number' && key !== nameField\n ) || keys[1]\n\n if (!valueField) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">No numeric field found for radial bar chart values</div>\n </div>\n </div>\n )\n }\n\n // Transform data for radial bar chart\n radialData = data.map((item, index) => {\n let name = item[nameField]\n // Handle boolean values with better labels\n if (typeof name === 'boolean') {\n name = name ? 'Active' : 'Inactive'\n } else if (name === 'true' || name === 'false') {\n name = name === 'true' ? 'Active' : 'Inactive'\n } else {\n name = String(name)\n }\n return {\n name,\n value: typeof item[valueField] === 'string' \n ? parseFloat(item[valueField]) \n : (item[valueField] || 0),\n fill: (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]\n }\n })\n }\n\n // Filter out zero/null values\n radialData = radialData.filter(item => item.value != null && item.value !== 0)\n \n if (radialData.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No valid data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No valid data points for radial bar chart after transformation</div>\n </div>\n </div>\n )\n }\n\n return (\n <ChartContainer height={height}>\n <RechartsRadialBarChart\n data={radialData}\n innerRadius=\"10%\"\n outerRadius=\"80%\"\n margin={{ top: 20, right: 30, bottom: 20, left: 30 }}\n accessibilityLayer={false}\n >\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={safeDisplayConfig.leftYAxisFormat\n ? (value: any, name: string) => [formatAxisValue(value, safeDisplayConfig.leftYAxisFormat), name]\n : undefined\n }\n />\n )}\n {safeDisplayConfig.showLegend && (\n <Legend \n wrapperStyle={{ fontSize: '12px', paddingTop: '10px' }}\n iconType=\"circle\"\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => setHoveredLegend(String(o.value || ''))}\n onMouseLeave={() => setHoveredLegend(null)}\n />\n )}\n <RadialBar\n dataKey=\"value\"\n cornerRadius={4}\n label={{\n position: 'insideStart',\n fill: '#fff',\n fontSize: 12,\n formatter: safeDisplayConfig.leftYAxisFormat\n ? (value: any) => formatAxisValue(value, safeDisplayConfig.leftYAxisFormat)\n : undefined\n }}\n >\n {radialData.map((entry, index) => (\n <Cell \n key={`cell-${index}`} \n fill={entry.fill}\n fillOpacity={hoveredLegend ? (hoveredLegend === entry.name ? 1 : 0.3) : 1}\n />\n ))}\n </RadialBar>\n </RechartsRadialBarChart>\n </ChartContainer>\n )\n } catch (error) {\n // 'RadialBarChart rendering error\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Radial Bar Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default RadialBarChart"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chart-radial-bar-N3MNUL7o.js","names":[],"sources":["../../../src/client/components/charts/RadialBarChart.tsx"],"sourcesContent":["import React, { useState } from 'react'\nimport { RadialBarChart as RechartsRadialBarChart, RadialBar, Legend, Cell } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport { formatTimeValue, getFieldGranularity, formatAxisValue } from '../../utils/chartUtils'\nimport type { ChartProps } from '../../types'\n\nconst RadialBarChart = React.memo(function RadialBarChart({ \n data, \n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette\n}: ChartProps) {\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n \n try {\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n leftYAxisFormat: displayConfig?.leftYAxisFormat\n }\n\n if (!data || data.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No data points to display in radial bar chart</div>\n </div>\n </div>\n )\n }\n\n let radialData: Array<{name: string, value: number, fill?: string}>\n\n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format - use chart config\n const xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis // Name/category field\n const yAxisField = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis[0] : chartConfig.yAxis // Value field\n\n const granularity = getFieldGranularity(queryObject, xAxisField)\n radialData = data.map((item, index) => ({\n name: formatTimeValue(item[xAxisField], granularity) || String(item[xAxisField]) || 'Unknown',\n value: typeof item[yAxisField] === 'string' \n ? parseFloat(item[yAxisField]) \n : (item[yAxisField] || 0),\n fill: (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]\n }))\n } else {\n // Legacy format or auto-detection\n const firstRow = data[0]\n const keys = Object.keys(firstRow)\n \n // Try to find name/label field\n const nameField = keys.find(key => \n typeof firstRow[key] === 'string' ||\n key.toLowerCase().includes('name') ||\n key.toLowerCase().includes('label') ||\n key.toLowerCase().includes('category')\n ) || keys[0]\n\n // Find a numeric field for values\n const valueField = keys.find(key => \n typeof firstRow[key] === 'number' && key !== nameField\n ) || keys[1]\n\n if (!valueField) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">No numeric field found for radial bar chart values</div>\n </div>\n </div>\n )\n }\n\n // Transform data for radial bar chart\n radialData = data.map((item, index) => {\n let name = item[nameField]\n // Handle boolean values with better labels\n if (typeof name === 'boolean') {\n name = name ? 'Active' : 'Inactive'\n } else if (name === 'true' || name === 'false') {\n name = name === 'true' ? 'Active' : 'Inactive'\n } else {\n name = String(name)\n }\n return {\n name,\n value: typeof item[valueField] === 'string' \n ? parseFloat(item[valueField]) \n : (item[valueField] || 0),\n fill: (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) || CHART_COLORS[index % CHART_COLORS.length]\n }\n })\n }\n\n // Filter out zero/null values\n radialData = radialData.filter(item => item.value != null && item.value !== 0)\n \n if (radialData.length === 0) {\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No valid data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">No valid data points for radial bar chart after transformation</div>\n </div>\n </div>\n )\n }\n\n return (\n <ChartContainer height={height}>\n <RechartsRadialBarChart\n data={radialData}\n innerRadius=\"10%\"\n outerRadius=\"80%\"\n margin={{ top: 20, right: 30, bottom: 20, left: 30 }}\n accessibilityLayer={false}\n >\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={safeDisplayConfig.leftYAxisFormat\n ? (value: any, name: string) => [formatAxisValue(value, safeDisplayConfig.leftYAxisFormat), name]\n : undefined\n }\n />\n )}\n {safeDisplayConfig.showLegend && (\n <Legend \n wrapperStyle={{ fontSize: '12px', paddingTop: '10px' }}\n iconType=\"circle\"\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => setHoveredLegend(String(o.value || ''))}\n onMouseLeave={() => setHoveredLegend(null)}\n />\n )}\n <RadialBar\n dataKey=\"value\"\n cornerRadius={4}\n label={{\n position: 'insideStart',\n fill: '#fff',\n fontSize: 12,\n formatter: safeDisplayConfig.leftYAxisFormat\n ? (value: any) => formatAxisValue(value, safeDisplayConfig.leftYAxisFormat)\n : undefined\n }}\n >\n {radialData.map((entry, index) => (\n <Cell \n key={`cell-${index}`} \n fill={entry.fill}\n fillOpacity={hoveredLegend ? (hoveredLegend === entry.name ? 1 : 0.3) : 1}\n />\n ))}\n </RadialBar>\n </RechartsRadialBarChart>\n </ChartContainer>\n )\n } catch (error) {\n // 'RadialBarChart rendering error\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Radial Bar Chart Error</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : 'Unknown rendering error'}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">Check the data and configuration</div>\n </div>\n </div>\n )\n }\n})\n\nexport default RadialBarChart"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as e } from "./rolldown-runtime-lc2dmIiU.js";
|
|
2
|
-
import { n as t } from "./chart-activity-grid-
|
|
2
|
+
import { n as t } from "./chart-activity-grid-wR2Twpo7.js";
|
|
3
3
|
import n, { useEffect as r, useMemo as i, useRef as a, useState as o } from "react";
|
|
4
4
|
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
5
5
|
import { ResponsiveContainer as l, Sankey as u, Tooltip as d } from "recharts";
|
|
@@ -219,4 +219,4 @@ var x = n.memo(function({ data: e, height: n = "100%", colorPalette: f, displayC
|
|
|
219
219
|
//#endregion
|
|
220
220
|
export { f as n, p as r, m as t };
|
|
221
221
|
|
|
222
|
-
//# sourceMappingURL=chart-sankey-
|
|
222
|
+
//# sourceMappingURL=chart-sankey-8nRYnupt.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-sankey-FChb26UX.js","names":[],"sources":["../../../src/client/types/flow.ts","../../../src/client/components/charts/SankeyChart.tsx"],"sourcesContent":["/**\n * Flow Analysis Types\n *\n * Types for Flow analysis mode which visualizes user journeys using Sankey diagrams.\n * Flow analysis explores paths BEFORE and AFTER a defined starting step (bidirectional),\n * unlike funnel which only tracks linear forward progression.\n */\n\nimport type { Filter } from '../types'\nimport type { FunnelBindingKey } from './funnel'\n\n// ============================================================================\n// Starting Step Definition\n// ============================================================================\n\n/**\n * Starting step definition for flow analysis\n * The starting step is the anchor point from which we explore paths\n * in both directions (before and after)\n */\nexport interface FlowStartingStep {\n /** Display name for the starting step */\n name: string\n /** Filters that define which events qualify as the starting step */\n filters: Filter[]\n}\n\n// ============================================================================\n// Server Query Format\n// ============================================================================\n\n/**\n * Server flow query format\n * This is the shape sent to the server for execution\n * Wrapped in { flow: {...} } similar to funnel queries\n */\nexport interface ServerFlowQuery {\n flow: FlowQueryConfig\n}\n\n/**\n * Flow query configuration\n * Contains all parameters needed for server-side flow analysis\n */\nexport interface FlowQueryConfig {\n /**\n * Binding key that identifies individual entities (e.g., userId)\n * Can be a single string like 'Events.userId' or array for multi-cube\n */\n bindingKey: string | { cube: string; dimension: string }[]\n\n /**\n * Time dimension used for ordering events\n * Can be a single string like 'Events.timestamp' or array for multi-cube\n */\n timeDimension: string | { cube: string; dimension: string }[]\n\n /**\n * The starting step from which we explore paths\n * Defines the anchor point for bidirectional flow analysis\n */\n startingStep: {\n /** Display name for the starting step */\n name: string\n /** Filter(s) that identify events for this starting step */\n filter?: Filter | Filter[]\n }\n\n /** Number of steps to explore BEFORE the starting step (0-5) */\n stepsBefore: number\n\n /** Number of steps to explore AFTER the starting step (0-5) */\n stepsAfter: number\n\n /**\n * Event dimension that categorizes events (e.g., 'Events.eventType')\n * This dimension's values become the node labels in the Sankey diagram\n */\n eventDimension: string\n\n /**\n * Optional limit on the number of entities to process\n * Useful for performance on large datasets\n */\n entityLimit?: number\n\n /**\n * Output mode for flow data aggregation\n * - 'sankey': Aggregate by (layer, event_type) - standard flow visualization where paths can converge\n * - 'sunburst': Path-qualified nodes for hierarchical tree visualization where each path is unique\n * @default 'sankey'\n */\n outputMode?: 'sankey' | 'sunburst'\n\n /**\n * Join strategy for fetching steps\n * - 'auto' (default): use lateral when supported, otherwise window\n * - 'lateral': force lateral joins\n * - 'window': force window function strategy\n */\n joinStrategy: 'auto' | 'lateral' | 'window'\n}\n\n// ============================================================================\n// Sankey Result Types\n// ============================================================================\n\n/**\n * A node in the Sankey diagram\n * Represents an event type at a specific layer (distance from starting step)\n */\nexport interface SankeyNode {\n /**\n * Unique identifier for this node\n * Format: \"before_{depth}_{eventType}\" or \"after_{depth}_{eventType}\" or \"start_{eventType}\"\n * Examples: \"before_2_Signup\", \"start_Purchase\", \"after_1_Checkout\"\n */\n id: string\n\n /** Display name (typically the event type value) */\n name: string\n\n /**\n * Layer position in the Sankey diagram\n * Negative for steps before starting step, 0 for starting step, positive for after\n * Example: -2, -1, 0, 1, 2 for a flow with 2 steps before and 2 after\n */\n layer: number\n\n /** Total count of entities passing through this node */\n value?: number\n}\n\n/**\n * A link (edge) in the Sankey diagram\n * Represents a transition between two nodes\n */\nexport interface SankeyLink {\n /** Source node ID */\n source: string\n\n /** Target node ID */\n target: string\n\n /** Count of entities that follow this path */\n value: number\n}\n\n/**\n * Complete flow result row returned from server\n * Contains the full Sankey diagram data\n */\nexport interface FlowResultRow {\n nodes: SankeyNode[]\n links: SankeyLink[]\n}\n\n/**\n * Chart-ready flow data format\n * Same structure as FlowResultRow, used for chart components\n */\nexport interface FlowChartData {\n nodes: SankeyNode[]\n links: SankeyLink[]\n}\n\n// ============================================================================\n// Flow Slice State (for Zustand store)\n// ============================================================================\n\n/**\n * Flow mode state for the AnalysisBuilder store\n * Contains all UI state needed to configure a flow analysis\n */\nexport interface FlowSliceState {\n /** Selected cube for flow analysis (must be an eventStream cube) */\n flowCube: string | null\n\n /** Binding key that identifies entities (reuses funnel binding key type) */\n flowBindingKey: FunnelBindingKey | null\n\n /** Time dimension for ordering events */\n flowTimeDimension: string | null\n\n /** Starting step configuration */\n startingStep: FlowStartingStep\n\n /** Number of steps to explore before starting step (0-5, default 3) */\n stepsBefore: number\n\n /** Number of steps to explore after starting step (0-5, default 3) */\n stepsAfter: number\n\n /** Event dimension that categorizes events */\n eventDimension: string | null\n\n /** Join strategy for flow execution */\n joinStrategy: 'auto' | 'lateral' | 'window'\n}\n\n/**\n * Flow slice actions for the store\n */\nexport interface FlowSliceActions {\n setFlowCube: (cube: string | null) => void\n setFlowBindingKey: (key: FunnelBindingKey | null) => void\n setFlowTimeDimension: (dim: string | null) => void\n setEventDimension: (dim: string | null) => void\n setStartingStepName: (name: string) => void\n setStartingStepFilters: (filters: Filter[]) => void\n addStartingStepFilter: (filter: Filter) => void\n removeStartingStepFilter: (index: number) => void\n updateStartingStepFilter: (index: number, filter: Filter) => void\n setStepsBefore: (count: number) => void\n setStepsAfter: (count: number) => void\n setJoinStrategy: (strategy: 'auto' | 'lateral' | 'window') => void\n isFlowMode: () => boolean\n isFlowModeEnabled: () => boolean\n buildFlowQuery: () => ServerFlowQuery | null\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Type guard to check if data is Sankey/Flow chart data\n */\nexport function isSankeyData(data: unknown): data is FlowChartData {\n if (!data || typeof data !== 'object') return false\n const d = data as Record<string, unknown>\n return (\n Array.isArray(d.nodes) &&\n Array.isArray(d.links) &&\n (d.nodes.length === 0 ||\n (typeof d.nodes[0] === 'object' &&\n d.nodes[0] !== null &&\n 'id' in d.nodes[0]))\n )\n}\n\n/**\n * Type guard to detect server flow query format\n * Used to distinguish { flow: {...} } from CubeQuery, MultiQueryConfig, or ServerFunnelQuery\n */\nexport function isServerFlowQuery(obj: unknown): obj is ServerFlowQuery {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'flow' in obj &&\n typeof (obj as { flow: unknown }).flow === 'object'\n )\n}\n\n// ============================================================================\n// Default Values\n// ============================================================================\n\n/**\n * Default flow slice state for store initialization\n */\nexport const defaultFlowSliceState: FlowSliceState = {\n flowCube: null,\n flowBindingKey: null,\n flowTimeDimension: null,\n startingStep: {\n name: '',\n filters: [],\n },\n stepsBefore: 3,\n stepsAfter: 3,\n eventDimension: null,\n joinStrategy: 'auto',\n}\n\n/**\n * Minimum and maximum values for step depth\n */\nexport const FLOW_MIN_DEPTH = 0\nexport const FLOW_MAX_DEPTH = 5\n","/**\n * SankeyChart Component\n *\n * Visualizes flow data using Sankey diagrams showing paths between states.\n * Works with data from flow queries which provide nodes and links.\n *\n * The chart displays:\n * - Nodes representing events at different layers (before, start, after)\n * - Links showing the flow of entities between adjacent nodes\n * - Values indicating count of entities following each path\n */\n\nimport React, { useMemo, useRef, useState, useEffect } from 'react'\nimport { Sankey, Tooltip, ResponsiveContainer } from 'recharts'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport type { ChartProps } from '../../types'\nimport type { FlowChartData } from '../../types/flow'\nimport { isSankeyData } from '../../types/flow'\n\n/**\n * Color palette for Sankey nodes based on layer\n */\nconst SANKEY_COLORS = {\n before: '#F97316', // orange-500 - for steps before\n start: '#3B82F6', // blue-500 - for starting step\n after: '#10B981', // emerald-500 - for steps after\n}\n\n/**\n * Get color for a node based on its layer\n */\nfunction getNodeColor(layer: number, colorPalette?: string[]): string {\n if (colorPalette && colorPalette.length > 0) {\n // Use palette colors cycling through\n const absLayer = Math.abs(layer)\n return colorPalette[absLayer % colorPalette.length]\n }\n\n if (layer < 0) return SANKEY_COLORS.before\n if (layer === 0) return SANKEY_COLORS.start\n return SANKEY_COLORS.after\n}\n\n/**\n * Transform FlowChartData to Recharts Sankey format\n */\nfunction transformToRechartsFormat(\n data: FlowChartData,\n colorPalette?: string[]\n): {\n nodes: Array<{ name: string; fill: string; layer: number; value?: number }>\n links: Array<{ source: number; target: number; value: number }>\n} | null {\n if (!data.nodes || data.nodes.length === 0) return null\n\n // Create node index map for link resolution\n const nodeIndexMap = new Map<string, number>()\n data.nodes.forEach((node, index) => {\n nodeIndexMap.set(node.id, index)\n })\n\n // Transform nodes\n const nodes = data.nodes.map((node) => ({\n name: node.name,\n fill: getNodeColor(node.layer, colorPalette),\n layer: node.layer,\n value: node.value,\n }))\n\n // Transform links - resolve string IDs to numeric indices\n const links = data.links\n .map((link) => {\n const sourceIndex = nodeIndexMap.get(link.source)\n const targetIndex = nodeIndexMap.get(link.target)\n\n if (sourceIndex === undefined || targetIndex === undefined) {\n console.warn(`Sankey: Could not resolve link ${link.source} -> ${link.target}`)\n return null\n }\n\n return {\n source: sourceIndex,\n target: targetIndex,\n value: link.value,\n }\n })\n .filter((link): link is { source: number; target: number; value: number } => link !== null)\n\n return { nodes, links }\n}\n\n/**\n * Extract FlowChartData from various data formats\n */\nfunction extractFlowData(data: unknown[]): FlowChartData | null {\n if (!data || data.length === 0) return null\n\n // If first element is already FlowChartData (server returns as single-row array)\n if (isSankeyData(data[0])) {\n return data[0] as FlowChartData\n }\n\n // If data itself is FlowChartData shape\n if (isSankeyData(data)) {\n return data as unknown as FlowChartData\n }\n\n // Check if data is an array with nodes and links at top level\n const asAny = data as unknown as Record<string, unknown>\n if (asAny.nodes && asAny.links) {\n return asAny as unknown as FlowChartData\n }\n\n return null\n}\n\n/**\n * Custom node renderer for Sankey chart\n * Displays the node rectangle with label and value\n */\nfunction SankeyNode({\n x,\n y,\n width,\n height,\n payload,\n containerWidth,\n showLabels = true,\n}: {\n x: number\n y: number\n width: number\n height: number\n payload: { name: string; value?: number; fill: string }\n containerWidth: number\n showLabels?: boolean\n}) {\n // Determine if label should be on the left or right based on position\n const isRightSide = x > containerWidth / 2\n const labelX = isRightSide ? x - 6 : x + width + 6\n const textAnchor = isRightSide ? 'end' : 'start'\n\n return (\n <g>\n {/* Node rectangle */}\n <rect\n x={x}\n y={y}\n width={width}\n height={height}\n fill={payload.fill}\n rx={2}\n ry={2}\n />\n {/* Node label - only shown if showLabels is true */}\n {showLabels && (\n <text\n x={labelX}\n y={y + height / 2}\n textAnchor={textAnchor}\n dominantBaseline=\"middle\"\n className=\"dc:text-xs dc:fill-dc-text\"\n style={{ fontSize: 11 }}\n >\n {payload.name}\n {payload.value !== undefined && (\n <tspan className=\"dc:fill-dc-text-secondary\" dx={4}>\n ({payload.value.toLocaleString()})\n </tspan>\n )}\n </text>\n )}\n </g>\n )\n}\n\n/**\n * Custom tooltip for Sankey chart\n */\nfunction SankeyTooltip({ active, payload }: {\n active?: boolean\n payload?: Array<{ payload: { name: string; value: number; source?: { name: string }; target?: { name: string } } }>\n}) {\n if (!active || !payload || payload.length === 0) return null\n\n const data = payload[0].payload\n\n // Check if this is a link (has source/target) or a node\n if (data.source && data.target) {\n return (\n <div className=\"bg-dc-surface dc:border border-dc-border dc:rounded-md dc:px-3 dc:py-2 dc:shadow-lg dc:text-sm\">\n <div className=\"dc:font-medium text-dc-text\">\n {data.source.name} → {data.target.name}\n </div>\n <div className=\"text-dc-text-secondary dc:mt-1\">\n <span className=\"dc:font-medium\">{data.value.toLocaleString()}</span> entities\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"bg-dc-surface dc:border border-dc-border dc:rounded-md dc:px-3 dc:py-2 dc:shadow-lg dc:text-sm\">\n <div className=\"dc:font-medium text-dc-text\">{data.name}</div>\n {data.value !== undefined && (\n <div className=\"text-dc-text-secondary dc:mt-1\">\n <span className=\"dc:font-medium\">{data.value.toLocaleString()}</span> entities\n </div>\n )}\n </div>\n )\n}\n\n/**\n * SankeyChart Component\n *\n * Renders a Sankey diagram visualization from FlowChartData.\n * Shows flow paths with nodes at each layer and links between them.\n */\nconst SankeyChart = React.memo(function SankeyChart({\n data,\n height = '100%',\n colorPalette,\n displayConfig,\n}: ChartProps) {\n // Track container width for label positioning\n const containerRef = useRef<HTMLDivElement>(null)\n const [containerWidth, setContainerWidth] = useState(800)\n\n useEffect(() => {\n const updateWidth = () => {\n if (containerRef.current) {\n setContainerWidth(containerRef.current.offsetWidth)\n }\n }\n updateWidth()\n window.addEventListener('resize', updateWidth)\n return () => window.removeEventListener('resize', updateWidth)\n }, [])\n\n // Get display config options\n // Use custom properties from displayConfig if available, otherwise use defaults\n const displayConfigAny = displayConfig as Record<string, unknown> | undefined\n const linkOpacity = parseFloat(String(displayConfigAny?.linkOpacity || '0.5'))\n const nodeWidth = (displayConfigAny?.nodeWidth as number) ?? 10\n const nodePadding = (displayConfigAny?.nodePadding as number) ?? 50\n const showNodeLabels = displayConfigAny?.showNodeLabels !== false // Default true\n\n // Extract and transform data\n const flowData = useMemo(() => {\n const extracted = extractFlowData(data || [])\n if (!extracted) return null\n\n return transformToRechartsFormat(extracted, colorPalette?.colors || CHART_COLORS)\n }, [data, colorPalette])\n\n // Calculate summary stats\n const summaryStats = useMemo(() => {\n const extracted = extractFlowData(data || [])\n if (!extracted) return null\n\n const totalEntities = extracted.nodes\n .filter((n) => n.layer === 0)\n .reduce((sum, n) => sum + (n.value || 0), 0)\n\n const totalPaths = extracted.links.reduce((sum, l) => sum + l.value, 0)\n\n return {\n nodeCount: extracted.nodes.length,\n linkCount: extracted.links.length,\n totalEntities,\n totalPaths,\n }\n }, [data])\n\n // Handle no data\n if (!data || data.length === 0 || !flowData || flowData.nodes.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No flow data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n Configure a flow analysis with a starting step and event dimension\n </div>\n </div>\n </div>\n )\n }\n\n return (\n <div ref={containerRef} className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n {/* Sankey Diagram */}\n <div className=\"dc:flex-1 dc:min-h-0\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <Sankey\n data={flowData}\n nodeWidth={nodeWidth}\n nodePadding={nodePadding}\n margin={{ top: 20, right: 20, bottom: 20, left: 20 }}\n link={{\n stroke: 'var(--dc-border)',\n strokeOpacity: linkOpacity,\n }}\n node={(props: any) => (\n <SankeyNode {...props} containerWidth={containerWidth} showLabels={showNodeLabels} />\n )}\n >\n <Tooltip content={<SankeyTooltip />} />\n </Sankey>\n </ResponsiveContainer>\n </div>\n\n {/* Summary Footer */}\n {!(displayConfigAny?.hideSummaryFooter) && summaryStats && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{summaryStats.nodeCount}</span> events\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Paths:</span>{' '}\n <span className=\"dc:font-medium\">{summaryStats.linkCount}</span>\n </div>\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{summaryStats.totalEntities.toLocaleString()}</span> starting entities\n </div>\n </div>\n </div>\n )}\n </div>\n )\n})\n\nexport default SankeyChart\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chart-sankey-8nRYnupt.js","names":[],"sources":["../../../src/client/types/flow.ts","../../../src/client/components/charts/SankeyChart.tsx"],"sourcesContent":["/**\n * Flow Analysis Types\n *\n * Types for Flow analysis mode which visualizes user journeys using Sankey diagrams.\n * Flow analysis explores paths BEFORE and AFTER a defined starting step (bidirectional),\n * unlike funnel which only tracks linear forward progression.\n */\n\nimport type { Filter } from '../types'\nimport type { FunnelBindingKey } from './funnel'\n\n// ============================================================================\n// Starting Step Definition\n// ============================================================================\n\n/**\n * Starting step definition for flow analysis\n * The starting step is the anchor point from which we explore paths\n * in both directions (before and after)\n */\nexport interface FlowStartingStep {\n /** Display name for the starting step */\n name: string\n /** Filters that define which events qualify as the starting step */\n filters: Filter[]\n}\n\n// ============================================================================\n// Server Query Format\n// ============================================================================\n\n/**\n * Server flow query format\n * This is the shape sent to the server for execution\n * Wrapped in { flow: {...} } similar to funnel queries\n */\nexport interface ServerFlowQuery {\n flow: FlowQueryConfig\n}\n\n/**\n * Flow query configuration\n * Contains all parameters needed for server-side flow analysis\n */\nexport interface FlowQueryConfig {\n /**\n * Binding key that identifies individual entities (e.g., userId)\n * Can be a single string like 'Events.userId' or array for multi-cube\n */\n bindingKey: string | { cube: string; dimension: string }[]\n\n /**\n * Time dimension used for ordering events\n * Can be a single string like 'Events.timestamp' or array for multi-cube\n */\n timeDimension: string | { cube: string; dimension: string }[]\n\n /**\n * The starting step from which we explore paths\n * Defines the anchor point for bidirectional flow analysis\n */\n startingStep: {\n /** Display name for the starting step */\n name: string\n /** Filter(s) that identify events for this starting step */\n filter?: Filter | Filter[]\n }\n\n /** Number of steps to explore BEFORE the starting step (0-5) */\n stepsBefore: number\n\n /** Number of steps to explore AFTER the starting step (0-5) */\n stepsAfter: number\n\n /**\n * Event dimension that categorizes events (e.g., 'Events.eventType')\n * This dimension's values become the node labels in the Sankey diagram\n */\n eventDimension: string\n\n /**\n * Optional limit on the number of entities to process\n * Useful for performance on large datasets\n */\n entityLimit?: number\n\n /**\n * Output mode for flow data aggregation\n * - 'sankey': Aggregate by (layer, event_type) - standard flow visualization where paths can converge\n * - 'sunburst': Path-qualified nodes for hierarchical tree visualization where each path is unique\n * @default 'sankey'\n */\n outputMode?: 'sankey' | 'sunburst'\n\n /**\n * Join strategy for fetching steps\n * - 'auto' (default): use lateral when supported, otherwise window\n * - 'lateral': force lateral joins\n * - 'window': force window function strategy\n */\n joinStrategy: 'auto' | 'lateral' | 'window'\n}\n\n// ============================================================================\n// Sankey Result Types\n// ============================================================================\n\n/**\n * A node in the Sankey diagram\n * Represents an event type at a specific layer (distance from starting step)\n */\nexport interface SankeyNode {\n /**\n * Unique identifier for this node\n * Format: \"before_{depth}_{eventType}\" or \"after_{depth}_{eventType}\" or \"start_{eventType}\"\n * Examples: \"before_2_Signup\", \"start_Purchase\", \"after_1_Checkout\"\n */\n id: string\n\n /** Display name (typically the event type value) */\n name: string\n\n /**\n * Layer position in the Sankey diagram\n * Negative for steps before starting step, 0 for starting step, positive for after\n * Example: -2, -1, 0, 1, 2 for a flow with 2 steps before and 2 after\n */\n layer: number\n\n /** Total count of entities passing through this node */\n value?: number\n}\n\n/**\n * A link (edge) in the Sankey diagram\n * Represents a transition between two nodes\n */\nexport interface SankeyLink {\n /** Source node ID */\n source: string\n\n /** Target node ID */\n target: string\n\n /** Count of entities that follow this path */\n value: number\n}\n\n/**\n * Complete flow result row returned from server\n * Contains the full Sankey diagram data\n */\nexport interface FlowResultRow {\n nodes: SankeyNode[]\n links: SankeyLink[]\n}\n\n/**\n * Chart-ready flow data format\n * Same structure as FlowResultRow, used for chart components\n */\nexport interface FlowChartData {\n nodes: SankeyNode[]\n links: SankeyLink[]\n}\n\n// ============================================================================\n// Flow Slice State (for Zustand store)\n// ============================================================================\n\n/**\n * Flow mode state for the AnalysisBuilder store\n * Contains all UI state needed to configure a flow analysis\n */\nexport interface FlowSliceState {\n /** Selected cube for flow analysis (must be an eventStream cube) */\n flowCube: string | null\n\n /** Binding key that identifies entities (reuses funnel binding key type) */\n flowBindingKey: FunnelBindingKey | null\n\n /** Time dimension for ordering events */\n flowTimeDimension: string | null\n\n /** Starting step configuration */\n startingStep: FlowStartingStep\n\n /** Number of steps to explore before starting step (0-5, default 3) */\n stepsBefore: number\n\n /** Number of steps to explore after starting step (0-5, default 3) */\n stepsAfter: number\n\n /** Event dimension that categorizes events */\n eventDimension: string | null\n\n /** Join strategy for flow execution */\n joinStrategy: 'auto' | 'lateral' | 'window'\n}\n\n/**\n * Flow slice actions for the store\n */\nexport interface FlowSliceActions {\n setFlowCube: (cube: string | null) => void\n setFlowBindingKey: (key: FunnelBindingKey | null) => void\n setFlowTimeDimension: (dim: string | null) => void\n setEventDimension: (dim: string | null) => void\n setStartingStepName: (name: string) => void\n setStartingStepFilters: (filters: Filter[]) => void\n addStartingStepFilter: (filter: Filter) => void\n removeStartingStepFilter: (index: number) => void\n updateStartingStepFilter: (index: number, filter: Filter) => void\n setStepsBefore: (count: number) => void\n setStepsAfter: (count: number) => void\n setJoinStrategy: (strategy: 'auto' | 'lateral' | 'window') => void\n isFlowMode: () => boolean\n isFlowModeEnabled: () => boolean\n buildFlowQuery: () => ServerFlowQuery | null\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Type guard to check if data is Sankey/Flow chart data\n */\nexport function isSankeyData(data: unknown): data is FlowChartData {\n if (!data || typeof data !== 'object') return false\n const d = data as Record<string, unknown>\n return (\n Array.isArray(d.nodes) &&\n Array.isArray(d.links) &&\n (d.nodes.length === 0 ||\n (typeof d.nodes[0] === 'object' &&\n d.nodes[0] !== null &&\n 'id' in d.nodes[0]))\n )\n}\n\n/**\n * Type guard to detect server flow query format\n * Used to distinguish { flow: {...} } from CubeQuery, MultiQueryConfig, or ServerFunnelQuery\n */\nexport function isServerFlowQuery(obj: unknown): obj is ServerFlowQuery {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'flow' in obj &&\n typeof (obj as { flow: unknown }).flow === 'object'\n )\n}\n\n// ============================================================================\n// Default Values\n// ============================================================================\n\n/**\n * Default flow slice state for store initialization\n */\nexport const defaultFlowSliceState: FlowSliceState = {\n flowCube: null,\n flowBindingKey: null,\n flowTimeDimension: null,\n startingStep: {\n name: '',\n filters: [],\n },\n stepsBefore: 3,\n stepsAfter: 3,\n eventDimension: null,\n joinStrategy: 'auto',\n}\n\n/**\n * Minimum and maximum values for step depth\n */\nexport const FLOW_MIN_DEPTH = 0\nexport const FLOW_MAX_DEPTH = 5\n","/**\n * SankeyChart Component\n *\n * Visualizes flow data using Sankey diagrams showing paths between states.\n * Works with data from flow queries which provide nodes and links.\n *\n * The chart displays:\n * - Nodes representing events at different layers (before, start, after)\n * - Links showing the flow of entities between adjacent nodes\n * - Values indicating count of entities following each path\n */\n\nimport React, { useMemo, useRef, useState, useEffect } from 'react'\nimport { Sankey, Tooltip, ResponsiveContainer } from 'recharts'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport type { ChartProps } from '../../types'\nimport type { FlowChartData } from '../../types/flow'\nimport { isSankeyData } from '../../types/flow'\n\n/**\n * Color palette for Sankey nodes based on layer\n */\nconst SANKEY_COLORS = {\n before: '#F97316', // orange-500 - for steps before\n start: '#3B82F6', // blue-500 - for starting step\n after: '#10B981', // emerald-500 - for steps after\n}\n\n/**\n * Get color for a node based on its layer\n */\nfunction getNodeColor(layer: number, colorPalette?: string[]): string {\n if (colorPalette && colorPalette.length > 0) {\n // Use palette colors cycling through\n const absLayer = Math.abs(layer)\n return colorPalette[absLayer % colorPalette.length]\n }\n\n if (layer < 0) return SANKEY_COLORS.before\n if (layer === 0) return SANKEY_COLORS.start\n return SANKEY_COLORS.after\n}\n\n/**\n * Transform FlowChartData to Recharts Sankey format\n */\nfunction transformToRechartsFormat(\n data: FlowChartData,\n colorPalette?: string[]\n): {\n nodes: Array<{ name: string; fill: string; layer: number; value?: number }>\n links: Array<{ source: number; target: number; value: number }>\n} | null {\n if (!data.nodes || data.nodes.length === 0) return null\n\n // Create node index map for link resolution\n const nodeIndexMap = new Map<string, number>()\n data.nodes.forEach((node, index) => {\n nodeIndexMap.set(node.id, index)\n })\n\n // Transform nodes\n const nodes = data.nodes.map((node) => ({\n name: node.name,\n fill: getNodeColor(node.layer, colorPalette),\n layer: node.layer,\n value: node.value,\n }))\n\n // Transform links - resolve string IDs to numeric indices\n const links = data.links\n .map((link) => {\n const sourceIndex = nodeIndexMap.get(link.source)\n const targetIndex = nodeIndexMap.get(link.target)\n\n if (sourceIndex === undefined || targetIndex === undefined) {\n console.warn(`Sankey: Could not resolve link ${link.source} -> ${link.target}`)\n return null\n }\n\n return {\n source: sourceIndex,\n target: targetIndex,\n value: link.value,\n }\n })\n .filter((link): link is { source: number; target: number; value: number } => link !== null)\n\n return { nodes, links }\n}\n\n/**\n * Extract FlowChartData from various data formats\n */\nfunction extractFlowData(data: unknown[]): FlowChartData | null {\n if (!data || data.length === 0) return null\n\n // If first element is already FlowChartData (server returns as single-row array)\n if (isSankeyData(data[0])) {\n return data[0] as FlowChartData\n }\n\n // If data itself is FlowChartData shape\n if (isSankeyData(data)) {\n return data as unknown as FlowChartData\n }\n\n // Check if data is an array with nodes and links at top level\n const asAny = data as unknown as Record<string, unknown>\n if (asAny.nodes && asAny.links) {\n return asAny as unknown as FlowChartData\n }\n\n return null\n}\n\n/**\n * Custom node renderer for Sankey chart\n * Displays the node rectangle with label and value\n */\nfunction SankeyNode({\n x,\n y,\n width,\n height,\n payload,\n containerWidth,\n showLabels = true,\n}: {\n x: number\n y: number\n width: number\n height: number\n payload: { name: string; value?: number; fill: string }\n containerWidth: number\n showLabels?: boolean\n}) {\n // Determine if label should be on the left or right based on position\n const isRightSide = x > containerWidth / 2\n const labelX = isRightSide ? x - 6 : x + width + 6\n const textAnchor = isRightSide ? 'end' : 'start'\n\n return (\n <g>\n {/* Node rectangle */}\n <rect\n x={x}\n y={y}\n width={width}\n height={height}\n fill={payload.fill}\n rx={2}\n ry={2}\n />\n {/* Node label - only shown if showLabels is true */}\n {showLabels && (\n <text\n x={labelX}\n y={y + height / 2}\n textAnchor={textAnchor}\n dominantBaseline=\"middle\"\n className=\"dc:text-xs dc:fill-dc-text\"\n style={{ fontSize: 11 }}\n >\n {payload.name}\n {payload.value !== undefined && (\n <tspan className=\"dc:fill-dc-text-secondary\" dx={4}>\n ({payload.value.toLocaleString()})\n </tspan>\n )}\n </text>\n )}\n </g>\n )\n}\n\n/**\n * Custom tooltip for Sankey chart\n */\nfunction SankeyTooltip({ active, payload }: {\n active?: boolean\n payload?: Array<{ payload: { name: string; value: number; source?: { name: string }; target?: { name: string } } }>\n}) {\n if (!active || !payload || payload.length === 0) return null\n\n const data = payload[0].payload\n\n // Check if this is a link (has source/target) or a node\n if (data.source && data.target) {\n return (\n <div className=\"bg-dc-surface dc:border border-dc-border dc:rounded-md dc:px-3 dc:py-2 dc:shadow-lg dc:text-sm\">\n <div className=\"dc:font-medium text-dc-text\">\n {data.source.name} → {data.target.name}\n </div>\n <div className=\"text-dc-text-secondary dc:mt-1\">\n <span className=\"dc:font-medium\">{data.value.toLocaleString()}</span> entities\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"bg-dc-surface dc:border border-dc-border dc:rounded-md dc:px-3 dc:py-2 dc:shadow-lg dc:text-sm\">\n <div className=\"dc:font-medium text-dc-text\">{data.name}</div>\n {data.value !== undefined && (\n <div className=\"text-dc-text-secondary dc:mt-1\">\n <span className=\"dc:font-medium\">{data.value.toLocaleString()}</span> entities\n </div>\n )}\n </div>\n )\n}\n\n/**\n * SankeyChart Component\n *\n * Renders a Sankey diagram visualization from FlowChartData.\n * Shows flow paths with nodes at each layer and links between them.\n */\nconst SankeyChart = React.memo(function SankeyChart({\n data,\n height = '100%',\n colorPalette,\n displayConfig,\n}: ChartProps) {\n // Track container width for label positioning\n const containerRef = useRef<HTMLDivElement>(null)\n const [containerWidth, setContainerWidth] = useState(800)\n\n useEffect(() => {\n const updateWidth = () => {\n if (containerRef.current) {\n setContainerWidth(containerRef.current.offsetWidth)\n }\n }\n updateWidth()\n window.addEventListener('resize', updateWidth)\n return () => window.removeEventListener('resize', updateWidth)\n }, [])\n\n // Get display config options\n // Use custom properties from displayConfig if available, otherwise use defaults\n const displayConfigAny = displayConfig as Record<string, unknown> | undefined\n const linkOpacity = parseFloat(String(displayConfigAny?.linkOpacity || '0.5'))\n const nodeWidth = (displayConfigAny?.nodeWidth as number) ?? 10\n const nodePadding = (displayConfigAny?.nodePadding as number) ?? 50\n const showNodeLabels = displayConfigAny?.showNodeLabels !== false // Default true\n\n // Extract and transform data\n const flowData = useMemo(() => {\n const extracted = extractFlowData(data || [])\n if (!extracted) return null\n\n return transformToRechartsFormat(extracted, colorPalette?.colors || CHART_COLORS)\n }, [data, colorPalette])\n\n // Calculate summary stats\n const summaryStats = useMemo(() => {\n const extracted = extractFlowData(data || [])\n if (!extracted) return null\n\n const totalEntities = extracted.nodes\n .filter((n) => n.layer === 0)\n .reduce((sum, n) => sum + (n.value || 0), 0)\n\n const totalPaths = extracted.links.reduce((sum, l) => sum + l.value, 0)\n\n return {\n nodeCount: extracted.nodes.length,\n linkCount: extracted.links.length,\n totalEntities,\n totalPaths,\n }\n }, [data])\n\n // Handle no data\n if (!data || data.length === 0 || !flowData || flowData.nodes.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No flow data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n Configure a flow analysis with a starting step and event dimension\n </div>\n </div>\n </div>\n )\n }\n\n return (\n <div ref={containerRef} className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n {/* Sankey Diagram */}\n <div className=\"dc:flex-1 dc:min-h-0\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <Sankey\n data={flowData}\n nodeWidth={nodeWidth}\n nodePadding={nodePadding}\n margin={{ top: 20, right: 20, bottom: 20, left: 20 }}\n link={{\n stroke: 'var(--dc-border)',\n strokeOpacity: linkOpacity,\n }}\n node={(props: any) => (\n <SankeyNode {...props} containerWidth={containerWidth} showLabels={showNodeLabels} />\n )}\n >\n <Tooltip content={<SankeyTooltip />} />\n </Sankey>\n </ResponsiveContainer>\n </div>\n\n {/* Summary Footer */}\n {!(displayConfigAny?.hideSummaryFooter) && summaryStats && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{summaryStats.nodeCount}</span> events\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Paths:</span>{' '}\n <span className=\"dc:font-medium\">{summaryStats.linkCount}</span>\n </div>\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{summaryStats.totalEntities.toLocaleString()}</span> starting entities\n </div>\n </div>\n </div>\n )}\n </div>\n )\n})\n\nexport default SankeyChart\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as e } from "./rolldown-runtime-lc2dmIiU.js";
|
|
2
|
-
import { S as t, T as n, _ as r, i, n as a, w as o, x as s, y as c } from "./chart-activity-grid-
|
|
3
|
-
import { l } from "./chart-area-
|
|
2
|
+
import { S as t, T as n, _ as r, i, n as a, w as o, x as s, y as c } from "./chart-activity-grid-wR2Twpo7.js";
|
|
3
|
+
import { l } from "./chart-area-e9ysnatQ.js";
|
|
4
4
|
import u, { useState as d } from "react";
|
|
5
5
|
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
6
6
|
import { CartesianGrid as m, Legend as h, Scatter as g, ScatterChart as _, Tooltip as v, XAxis as y, YAxis as b } from "recharts";
|
|
@@ -252,4 +252,4 @@ var x = /* @__PURE__ */ e({ default: () => S }), S = u.memo(function({ data: e,
|
|
|
252
252
|
//#endregion
|
|
253
253
|
export { x as n, S as t };
|
|
254
254
|
|
|
255
|
-
//# sourceMappingURL=chart-scatter-
|
|
255
|
+
//# sourceMappingURL=chart-scatter-J2JNi88S.js.map
|