diff --git a/frontend/components/admin/dashboard/CanvasElement.tsx b/frontend/components/admin/dashboard/CanvasElement.tsx index 530e510e..33b1d801 100644 --- a/frontend/components/admin/dashboard/CanvasElement.tsx +++ b/frontend/components/admin/dashboard/CanvasElement.tsx @@ -2,9 +2,26 @@ import React, { useState, useCallback, useRef, useEffect } from "react"; import dynamic from "next/dynamic"; -import { DashboardElement, QueryResult, Position } from "./types"; +import { + DashboardElement, + QueryResult, + Position, + ElementSubtype, + AXIS_BASED_CHARTS, + CIRCULAR_CHARTS, + getChartCategory, +} from "./types"; import { ChartRenderer } from "./charts/ChartRenderer"; import { GRID_CONFIG, magneticSnap, snapSizeToGrid } from "./gridUtils"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; // 위젯 동적 임포트 const WeatherWidget = dynamic(() => import("@/components/dashboard/widgets/WeatherWidget"), { @@ -710,14 +727,54 @@ export function CanvasElement({ top: displayPosition.y, width: displaySize.width, height: displaySize.height, - padding: `${GRID_CONFIG.ELEMENT_PADDING}px`, boxSizing: "border-box", }} onMouseDown={handleMouseDown} > {/* 헤더 */} -
- {element.customTitle || element.title} +
+
+ {/* 차트 타입 전환 드롭다운 (차트일 경우만) */} + {element.type === "chart" && ( + + )} + {/* 제목 */} + {!element.type || element.type !== "chart" ? ( + {element.customTitle || element.title} + ) : null} +
{/* 삭제 버튼 */}
{/* 내용 */} -
+
{element.type === "chart" ? ( // 차트 렌더링
diff --git a/frontend/components/admin/dashboard/ChartConfigPanel.tsx b/frontend/components/admin/dashboard/ChartConfigPanel.tsx index 04dd2d0e..703530c2 100644 --- a/frontend/components/admin/dashboard/ChartConfigPanel.tsx +++ b/frontend/components/admin/dashboard/ChartConfigPanel.tsx @@ -1,7 +1,7 @@ "use client"; import React, { useState, useCallback, useEffect } from "react"; -import { ChartConfig, QueryResult } from "./types"; +import { ChartConfig, QueryResult, isCircularChart, isAxisBasedChart } from "./types"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Label } from "@/components/ui/label"; @@ -40,8 +40,8 @@ export function ChartConfigPanel({ const [currentConfig, setCurrentConfig] = useState(config || {}); const [dateColumns, setDateColumns] = useState([]); - // 원형/도넛 차트 또는 REST API는 Y축이 필수가 아님 - const isPieChart = chartType === "pie" || chartType === "donut"; + // 원형 차트 또는 REST API는 Y축이 필수가 아님 + const isPieChart = chartType ? isCircularChart(chartType as any) : false; const isApiSource = dataSourceType === "api"; // 설정 업데이트 diff --git a/frontend/components/admin/dashboard/DashboardTopMenu.tsx b/frontend/components/admin/dashboard/DashboardTopMenu.tsx index 6392b355..b9e5976d 100644 --- a/frontend/components/admin/dashboard/DashboardTopMenu.tsx +++ b/frontend/components/admin/dashboard/DashboardTopMenu.tsx @@ -159,15 +159,18 @@ export function DashboardTopMenu({ - 차트 + 축 기반 차트 바 차트 수평 바 차트 누적 바 차트 꺾은선 차트 영역 차트 + 콤보 차트 + + + 원형 차트 원형 차트 도넛 차트 - 콤보 차트 diff --git a/frontend/components/admin/dashboard/types.ts b/frontend/components/admin/dashboard/types.ts index a07b5247..71a86a13 100644 --- a/frontend/components/admin/dashboard/types.ts +++ b/frontend/components/admin/dashboard/types.ts @@ -42,6 +42,40 @@ export type ElementSubtype = | "transport-stats" // 커스텀 통계 카드 위젯 | "custom-metric"; // 사용자 커스텀 카드 위젯 +// 차트 분류 +export type ChartCategory = "axis-based" | "circular"; + +// 축 기반 차트 (X/Y축 사용) +export const AXIS_BASED_CHARTS = ["bar", "horizontal-bar", "line", "area", "stacked-bar", "combo"] as const; + +// 원형 차트 (그룹핑 사용) +export const CIRCULAR_CHARTS = ["pie", "donut"] as const; + +// 차트인지 확인 +export const isChartType = (subtype: ElementSubtype): boolean => { + return ( + (AXIS_BASED_CHARTS as readonly string[]).includes(subtype) || + (CIRCULAR_CHARTS as readonly string[]).includes(subtype) + ); +}; + +// 축 기반 차트인지 확인 +export const isAxisBasedChart = (subtype: ElementSubtype): boolean => { + return (AXIS_BASED_CHARTS as readonly string[]).includes(subtype); +}; + +// 원형 차트인지 확인 +export const isCircularChart = (subtype: ElementSubtype): boolean => { + return (CIRCULAR_CHARTS as readonly string[]).includes(subtype); +}; + +// 차트 카테고리 가져오기 +export const getChartCategory = (subtype: ElementSubtype): ChartCategory | null => { + if (isAxisBasedChart(subtype)) return "axis-based"; + if (isCircularChart(subtype)) return "circular"; + return null; +}; + export interface Position { x: number; y: number;