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;