ERP-node/frontend/components/admin/dashboard/charts/ChartRenderer.tsx

155 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useEffect, useState } from "react";
import { DashboardElement, QueryResult, ChartData } from "../types";
import { Chart } from "./Chart";
import { transformQueryResultToChartData } from "../utils/chartDataTransform";
import { ExternalDbConnectionAPI } from "@/lib/api/externalDbConnection";
import { dashboardApi } from "@/lib/api/dashboard";
interface ChartRendererProps {
element: DashboardElement;
data?: QueryResult;
width?: number;
height?: number;
}
/**
* 차트 렌더러 컴포넌트 (D3 기반)
* - 데이터 소스에서 데이터 페칭
* - QueryResult를 ChartData로 변환
* - D3 Chart 컴포넌트에 전달
*/
export function ChartRenderer({ element, data, width = 250, height = 200 }: ChartRendererProps) {
const [chartData, setChartData] = useState<ChartData | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 데이터 페칭
useEffect(() => {
const fetchData = async () => {
// 이미 data가 전달된 경우 사용
if (data) {
const transformed = transformQueryResultToChartData(data, element.chartConfig || {});
setChartData(transformed);
return;
}
// 데이터 소스가 설정되어 있으면 페칭
if (element.dataSource && element.dataSource.query && element.chartConfig) {
setIsLoading(true);
setError(null);
try {
let queryResult: QueryResult;
// 현재 DB vs 외부 DB 분기
if (element.dataSource.connectionType === "external" && element.dataSource.externalConnectionId) {
// 외부 DB
const result = await ExternalDbConnectionAPI.executeQuery(
parseInt(element.dataSource.externalConnectionId),
element.dataSource.query,
);
if (!result.success) {
throw new Error(result.message || "외부 DB 쿼리 실행 실패");
}
queryResult = {
columns: result.data?.[0] ? Object.keys(result.data[0]) : [],
rows: result.data || [],
totalRows: result.data?.length || 0,
executionTime: 0,
};
} else {
// 현재 DB
const result = await dashboardApi.executeQuery(element.dataSource.query);
queryResult = {
columns: result.columns,
rows: result.rows,
totalRows: result.rowCount,
executionTime: 0,
};
}
// ChartData로 변환
const transformed = transformQueryResultToChartData(queryResult, element.chartConfig);
setChartData(transformed);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "데이터 로딩 실패";
setError(errorMessage);
} finally {
setIsLoading(false);
}
}
};
fetchData();
// 자동 새로고침 설정 (0이면 수동이므로 interval 설정 안 함)
const refreshInterval = element.dataSource?.refreshInterval;
if (refreshInterval && refreshInterval > 0) {
const interval = setInterval(fetchData, refreshInterval);
return () => clearInterval(interval);
}
}, [
element.dataSource?.query,
element.dataSource?.connectionType,
element.dataSource?.externalConnectionId,
element.dataSource?.refreshInterval,
element.chartConfig,
data,
]);
// 로딩 중
if (isLoading) {
return (
<div className="flex h-full w-full items-center justify-center text-gray-500">
<div className="text-center">
<div className="mx-auto mb-2 h-6 w-6 animate-spin rounded-full border-2 border-blue-600 border-t-transparent" />
<div className="text-sm"> ...</div>
</div>
</div>
);
}
// 에러
if (error) {
return (
<div className="flex h-full w-full items-center justify-center text-red-500">
<div className="text-center">
<div className="mb-2 text-2xl"></div>
<div className="text-sm font-medium"> </div>
<div className="mt-1 text-xs">{error}</div>
</div>
</div>
);
}
// 데이터나 설정이 없으면
if (!chartData || !element.chartConfig?.xAxis || !element.chartConfig?.yAxis) {
return (
<div className="flex h-full w-full items-center justify-center text-gray-500">
<div className="text-center">
<div className="mb-2 text-2xl">📊</div>
<div className="text-sm"> </div>
<div className="mt-1 text-xs"> </div>
</div>
</div>
);
}
// D3 차트 렌더링
return (
<div className="flex h-full w-full items-center justify-center bg-white p-2">
<Chart
chartType={element.subtype}
data={chartData}
config={element.chartConfig}
width={width - 20}
height={height - 20}
/>
</div>
);
}