fix(pop-dashboard): 차트 X/Y축 자동 적용 및 데이터 처리 안정화
설정 패널 간소화: - 차트 X축/Y축 수동 입력 필드 제거 (자동 적용 안내 문구로 대체) - groupBy 선택 시 X축 자동, 집계 결과를 Y축(value)으로 자동 매핑 차트 렌더링 개선 (ChartItem): - PieChart에 카테고리명+값+비율 라벨 표시 - Legend 컴포넌트 추가 (containerWidth 300px 이상 시) - Tooltip formatter로 이름/값 쌍 표시 데이터 fetcher 안정화 (dataFetcher): - apiClient(axios) 우선 호출, dashboardApi(fetch) 폴백 패턴 적용 - PostgreSQL bigint/numeric 문자열 -> 숫자 자동 변환 처리 - Recharts가 숫자 타입을 요구하는 문제 해결 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
* - fetch 직접 사용 금지: 반드시 dashboardApi/dataApi 사용
|
||||
*/
|
||||
|
||||
import { apiClient } from "@/lib/api/client";
|
||||
import { dashboardApi } from "@/lib/api/dashboard";
|
||||
import { dataApi } from "@/lib/api/data";
|
||||
import { tableManagementApi } from "@/lib/api/tableManagement";
|
||||
@@ -238,19 +239,46 @@ export async function fetchAggregatedData(
|
||||
// 집계 또는 조인이 있으면 SQL 직접 실행
|
||||
if (config.aggregation || (config.joins && config.joins.length > 0)) {
|
||||
const sql = buildAggregationSQL(config);
|
||||
const result = await dashboardApi.executeQuery(sql);
|
||||
|
||||
if (result.rows.length === 0) {
|
||||
// API 호출: apiClient(axios) 우선, dashboardApi(fetch) 폴백
|
||||
let queryResult: { columns: string[]; rows: any[] };
|
||||
try {
|
||||
// 1차: apiClient (axios 기반, 인증/세션 안정적)
|
||||
const response = await apiClient.post("/dashboards/execute-query", { query: sql });
|
||||
if (response.data?.success && response.data?.data) {
|
||||
queryResult = response.data.data;
|
||||
} else {
|
||||
throw new Error(response.data?.message || "쿼리 실행 실패");
|
||||
}
|
||||
} catch {
|
||||
// 2차: dashboardApi (fetch 기반, 폴백)
|
||||
queryResult = await dashboardApi.executeQuery(sql);
|
||||
}
|
||||
|
||||
if (queryResult.rows.length === 0) {
|
||||
return { value: 0, rows: [] };
|
||||
}
|
||||
|
||||
// PostgreSQL bigint/numeric는 JS에서 문자열로 반환됨
|
||||
// Recharts PieChart 등은 숫자 타입이 필수이므로 변환 처리
|
||||
const processedRows = queryResult.rows.map((row: Record<string, unknown>) => {
|
||||
const converted: Record<string, unknown> = { ...row };
|
||||
for (const key of Object.keys(converted)) {
|
||||
const val = converted[key];
|
||||
if (typeof val === "string" && val !== "" && !isNaN(Number(val))) {
|
||||
converted[key] = Number(val);
|
||||
}
|
||||
}
|
||||
return converted;
|
||||
});
|
||||
|
||||
// 첫 번째 행의 value 컬럼 추출
|
||||
const firstRow = result.rows[0];
|
||||
const numericValue = parseFloat(firstRow.value ?? firstRow[result.columns[0]] ?? 0);
|
||||
const firstRow = processedRows[0];
|
||||
const numericValue = parseFloat(String(firstRow.value ?? firstRow[queryResult.columns[0]] ?? 0));
|
||||
|
||||
return {
|
||||
value: Number.isFinite(numericValue) ? numericValue : 0,
|
||||
rows: result.rows,
|
||||
rows: processedRows,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user