diff --git a/frontend/components/admin/dashboard/CanvasElement.tsx b/frontend/components/admin/dashboard/CanvasElement.tsx index 599718b9..90eec9ca 100644 --- a/frontend/components/admin/dashboard/CanvasElement.tsx +++ b/frontend/components/admin/dashboard/CanvasElement.tsx @@ -903,11 +903,6 @@ export function CanvasElement({
- ) : element.type === "widget" && element.subtype === "list-v2" ? ( - // 리스트 위젯 (다중 데이터 소스) - 승격 완료 -
- -
) : element.type === "widget" && element.subtype === "custom-metric-v2" ? ( // 통계 카드 위젯 (다중 데이터 소스) - 승격 완료
@@ -1014,8 +1009,8 @@ export function CanvasElement({ }} />
- ) : element.type === "widget" && element.subtype === "list" ? ( - // 리스트 위젯 렌더링 (구버전) + ) : element.type === "widget" && (element.subtype === "list" || element.subtype === "list-v2") ? ( + // 리스트 위젯 렌더링 (v1 & v2)
diff --git a/frontend/components/admin/dashboard/DashboardDesigner.tsx b/frontend/components/admin/dashboard/DashboardDesigner.tsx index cdd080b6..3df5abdd 100644 --- a/frontend/components/admin/dashboard/DashboardDesigner.tsx +++ b/frontend/components/admin/dashboard/DashboardDesigner.tsx @@ -270,7 +270,14 @@ export default function DashboardDesigner({ dashboardId: initialDashboardId }: D // 요소 업데이트 const updateElement = useCallback((id: string, updates: Partial) => { - setElements((prev) => prev.map((el) => (el.id === id ? { ...el, ...updates } : el))); + setElements((prev) => + prev.map((el) => { + if (el.id === id) { + return { ...el, ...updates }; + } + return el; + }), + ); }, []); // 요소 삭제 @@ -359,14 +366,17 @@ export default function DashboardDesigner({ dashboardId: initialDashboardId }: D (updatedElement: DashboardElement) => { // 현재 요소의 최신 상태를 가져와서 position과 size는 유지 const currentElement = elements.find((el) => el.id === updatedElement.id); + if (currentElement) { - // position과 size는 현재 상태 유지, 나머지만 업데이트 + // id, position, size 제거 후 나머지만 업데이트 + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, position, size, ...updates } = updatedElement; const finalElement = { - ...updatedElement, - position: currentElement.position, - size: currentElement.size, + ...currentElement, + ...updates, }; - updateElement(finalElement.id, finalElement); + + updateElement(id, updates); // 사이드바도 최신 상태로 업데이트 setSidebarElement(finalElement); } diff --git a/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx b/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx index f4104e73..4f2b14a5 100644 --- a/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx +++ b/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx @@ -270,14 +270,28 @@ export function WidgetConfigSidebar({ element, isOpen, onClose, onApply }: Widge const handleApply = useCallback(() => { if (!element) return; + // 다중 데이터 소스를 사용하는 위젯 체크 + const isMultiDataSourceWidget = + element.subtype === "map-summary-v2" || + element.subtype === "chart" || + element.subtype === "list-v2" || + element.subtype === "custom-metric-v2" || + element.subtype === "risk-alert-v2"; + const updatedElement: DashboardElement = { ...element, customTitle: customTitle.trim() || undefined, showHeader, - // 데이터 소스가 필요한 위젯만 dataSource 포함 + // 데이터 소스 처리 ...(needsDataSource(element.subtype) ? { dataSource, + // 다중 데이터 소스 위젯은 dataSources도 포함 + ...(isMultiDataSourceWidget + ? { + dataSources: element.dataSources || [], + } + : {}), } : {}), // 리스트 위젯 설정 @@ -291,7 +305,16 @@ export function WidgetConfigSidebar({ element, isOpen, onClose, onApply }: Widge element.subtype === "chart" || ["bar", "horizontal-bar", "pie", "line", "area", "stacked-bar", "donut", "combo"].includes(element.subtype) ? { - chartConfig, + // 다중 데이터 소스 위젯은 chartConfig에 dataSources 포함 + chartConfig: isMultiDataSourceWidget + ? { ...chartConfig, dataSources: element.dataSources || [] } + : chartConfig, + // 프론트엔드 호환성을 위해 dataSources도 element에 직접 포함 + ...(isMultiDataSourceWidget + ? { + dataSources: element.dataSources || [], + } + : {}), } : {}), // 커스텀 메트릭 설정 diff --git a/frontend/components/admin/dashboard/widgets/ListWidget.tsx b/frontend/components/admin/dashboard/widgets/ListWidget.tsx index 54237d26..8193aea4 100644 --- a/frontend/components/admin/dashboard/widgets/ListWidget.tsx +++ b/frontend/components/admin/dashboard/widgets/ListWidget.tsx @@ -39,7 +39,9 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) { // 데이터 로드 useEffect(() => { const loadData = async () => { - if (!element.dataSource || (!element.dataSource.query && !element.dataSource.endpoint)) return; + if (!element.dataSource || (!element.dataSource.query && !element.dataSource.endpoint)) { + return; + } setIsLoading(true); setError(null); @@ -168,8 +170,8 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) { return (
-
-
데이터 로딩 중...
+
+
데이터 로딩 중...
); @@ -181,8 +183,8 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
⚠️
-
오류 발생
-
{error}
+
오류 발생
+
{error}
); @@ -194,8 +196,8 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
📋
-
리스트를 설정하세요
-
⚙️ 버튼을 클릭하여 데이터 소스와 컬럼을 설정해주세요
+
리스트를 설정하세요
+
⚙️ 버튼을 클릭하여 데이터 소스와 컬럼을 설정해주세요
); @@ -222,7 +224,7 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) {
{/* 제목 - 항상 표시 */}
-

{element.customTitle || element.title}

+

{element.customTitle || element.title}

{/* 테이블 뷰 */} @@ -251,7 +253,7 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) { col.visible).length} - className="text-center text-muted-foreground" + className="text-muted-foreground text-center" > 데이터가 없습니다 @@ -281,7 +283,7 @@ export function ListWidget({ element, onConfigUpdate }: ListWidgetProps) { {config.viewMode === "card" && (
{paginatedRows.length === 0 ? ( -
데이터가 없습니다
+
데이터가 없습니다
) : (
col.visible) .map((col) => (
-
{col.label || col.name}
+
{col.label || col.name}
{String(row[col.dataKey || col.field] ?? "")}