'use client';
import React from 'react';
import { DashboardElement, QueryResult } from '../types';
import { BarChartComponent } from './BarChartComponent';
import { PieChartComponent } from './PieChartComponent';
import { LineChartComponent } from './LineChartComponent';
interface ChartRendererProps {
element: DashboardElement;
data?: QueryResult;
width?: number;
height?: number;
}
/**
* 차트 렌더러 컴포넌트
* - 차트 타입에 따라 적절한 차트 컴포넌트를 렌더링
* - 데이터 변환 및 에러 처리
*/
export function ChartRenderer({ element, data, width = 250, height = 200 }: ChartRendererProps) {
// 데이터가 없거나 설정이 불완전한 경우
if (!data || !element.chartConfig?.xAxis || !element.chartConfig?.yAxis) {
return (
📊
데이터를 설정해주세요
⚙️ 버튼을 클릭하여 설정
);
}
// 데이터 변환
const chartData = transformData(data, element.chartConfig);
// 에러가 있는 경우
if (chartData.length === 0) {
return (
);
}
// 차트 공통 props
const chartProps = {
data: chartData,
config: element.chartConfig,
width: width - 20, // 패딩 고려
height: height - 60, // 헤더 높이 고려
};
// 차트 타입에 따른 렌더링
switch (element.subtype) {
case 'bar':
return ;
case 'pie':
return ;
case 'line':
return ;
default:
return (
);
}
}
/**
* 쿼리 결과를 차트 데이터로 변환
*/
function transformData(queryResult: QueryResult, config: any) {
try {
const { xAxis, yAxis, groupBy, aggregation = 'sum' } = config;
if (!queryResult.rows || queryResult.rows.length === 0) {
return [];
}
// 그룹핑이 있는 경우
if (groupBy && groupBy !== xAxis) {
const grouped = queryResult.rows.reduce((acc, row) => {
const xValue = String(row[xAxis] || '');
const groupValue = String(row[groupBy] || '');
const yValue = Number(row[yAxis]) || 0;
if (!acc[xValue]) {
acc[xValue] = { [xAxis]: xValue };
}
if (!acc[xValue][groupValue]) {
acc[xValue][groupValue] = 0;
}
// 집계 함수 적용
switch (aggregation) {
case 'sum':
acc[xValue][groupValue] += yValue;
break;
case 'avg':
// 평균 계산을 위해 임시로 배열 저장
if (!acc[xValue][`${groupValue}_values`]) {
acc[xValue][`${groupValue}_values`] = [];
}
acc[xValue][`${groupValue}_values`].push(yValue);
break;
case 'count':
acc[xValue][groupValue] += 1;
break;
case 'max':
acc[xValue][groupValue] = Math.max(acc[xValue][groupValue], yValue);
break;
case 'min':
acc[xValue][groupValue] = Math.min(acc[xValue][groupValue], yValue);
break;
}
return acc;
}, {} as any);
// 평균 계산 후처리
if (aggregation === 'avg') {
Object.keys(grouped).forEach(xValue => {
Object.keys(grouped[xValue]).forEach(key => {
if (key.endsWith('_values')) {
const baseKey = key.replace('_values', '');
const values = grouped[xValue][key];
grouped[xValue][baseKey] = values.reduce((sum: number, val: number) => sum + val, 0) / values.length;
delete grouped[xValue][key];
}
});
});
}
return Object.values(grouped);
}
// 단순 변환 (그룹핑 없음)
const dataMap = new Map();
queryResult.rows.forEach(row => {
const xValue = String(row[xAxis] || '');
const yValue = Number(row[yAxis]) || 0;
if (!dataMap.has(xValue)) {
dataMap.set(xValue, { [xAxis]: xValue, [yAxis]: 0, count: 0 });
}
const existing = dataMap.get(xValue);
switch (aggregation) {
case 'sum':
existing[yAxis] += yValue;
break;
case 'avg':
existing[yAxis] += yValue;
existing.count += 1;
break;
case 'count':
existing[yAxis] += 1;
break;
case 'max':
existing[yAxis] = Math.max(existing[yAxis], yValue);
break;
case 'min':
existing[yAxis] = existing[yAxis] === 0 ? yValue : Math.min(existing[yAxis], yValue);
break;
}
});
// 평균 계산 후처리
if (aggregation === 'avg') {
dataMap.forEach(item => {
if (item.count > 0) {
item[yAxis] = item[yAxis] / item.count;
}
delete item.count;
});
}
return Array.from(dataMap.values()).slice(0, 50); // 최대 50개 데이터포인트
} catch (error) {
console.error('데이터 변환 오류:', error);
return [];
}
}