"use client"; import React, { useState, useCallback, useEffect } from "react"; import { DashboardElement, ChartDataSource, QueryResult, ListWidgetConfig, ListColumn } from "../types"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { ChevronLeft, ChevronRight, Save, X } from "lucide-react"; import { DataSourceSelector } from "../data-sources/DataSourceSelector"; import { DatabaseConfig } from "../data-sources/DatabaseConfig"; import { ApiConfig } from "../data-sources/ApiConfig"; import { QueryEditor } from "../QueryEditor"; import { ColumnSelector } from "./list-widget/ColumnSelector"; import { ManualColumnEditor } from "./list-widget/ManualColumnEditor"; import { ListTableOptions } from "./list-widget/ListTableOptions"; interface ListWidgetConfigModalProps { isOpen: boolean; element: DashboardElement; onClose: () => void; onSave: (updates: Partial) => void; } /** * 리스트 위젯 설정 모달 * - 3단계 설정: 데이터 소스 → 데이터 가져오기 → 컬럼 설정 */ export function ListWidgetConfigModal({ isOpen, element, onClose, onSave }: ListWidgetConfigModalProps) { const [currentStep, setCurrentStep] = useState<1 | 2 | 3>(1); const [title, setTitle] = useState(element.title || "📋 리스트"); const [dataSource, setDataSource] = useState( element.dataSource || { type: "database", connectionType: "current", refreshInterval: 0 }, ); const [queryResult, setQueryResult] = useState(null); const [listConfig, setListConfig] = useState( element.listConfig || { columnMode: "auto", viewMode: "table", columns: [], pageSize: 10, enablePagination: true, showHeader: true, stripedRows: true, compactMode: false, cardColumns: 3, }, ); // 모달 열릴 때 element에서 설정 로드 (한 번만) useEffect(() => { if (isOpen) { // element가 변경되었을 때만 설정을 다시 로드 setTitle(element.title || "📋 리스트"); // 기존 dataSource가 있으면 그대로 사용, 없으면 기본값 if (element.dataSource) { setDataSource(element.dataSource); } // 기존 listConfig가 있으면 그대로 사용, 없으면 기본값 if (element.listConfig) { setListConfig(element.listConfig); } // 현재 스텝은 1로 초기화 setCurrentStep(1); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isOpen, element.id]); // element.id가 변경될 때만 재실행 // 데이터 소스 타입 변경 const handleDataSourceTypeChange = useCallback((type: "database" | "api") => { if (type === "database") { setDataSource((prev) => ({ ...prev, type: "database", connectionType: "current", })); } else { setDataSource((prev) => ({ ...prev, type: "api", method: "GET", })); } // 데이터 소스 타입 변경 시에는 쿼리 결과만 초기화 (컬럼 설정은 유지) setQueryResult(null); }, []); // 데이터 소스 업데이트 const handleDataSourceUpdate = useCallback((updates: Partial) => { setDataSource((prev) => ({ ...prev, ...updates })); }, []); // 쿼리 실행 결과 처리 const handleQueryTest = useCallback( (result: QueryResult) => { setQueryResult(result); // 자동 모드이고 기존 컬럼이 없을 때만 자동 생성 if (listConfig.columnMode === "auto" && result.columns.length > 0 && listConfig.columns.length === 0) { const autoColumns: ListColumn[] = result.columns.map((col, idx) => ({ id: `col_${idx}`, label: col, field: col, align: "left", visible: true, })); setListConfig((prev) => ({ ...prev, columns: autoColumns })); } }, [listConfig.columnMode, listConfig.columns.length], ); // 다음 단계 const handleNext = () => { if (currentStep < 3) { setCurrentStep((prev) => (prev + 1) as 1 | 2 | 3); } }; // 이전 단계 const handlePrev = () => { if (currentStep > 1) { setCurrentStep((prev) => (prev - 1) as 1 | 2 | 3); } }; // 저장 const handleSave = () => { onSave({ customTitle: title, dataSource, listConfig, }); onClose(); }; // 저장 가능 여부 const canSave = queryResult && queryResult.rows.length > 0 && listConfig.columns.length > 0; if (!isOpen) return null; return (
{/* 헤더 */}

📋 리스트 위젯 설정

데이터 소스와 컬럼을 설정하세요

{/* 제목 입력 */}
setTitle(e.target.value)} onKeyDown={(e) => { // 모든 키보드 이벤트를 input 필드 내부에서만 처리 e.stopPropagation(); }} placeholder="예: 사용자 목록" className="mt-1" />
{/* 참고: 리스트 위젯은 제목이 항상 표시됩니다 */}
💡 리스트 위젯은 제목이 항상 표시됩니다
{/* 진행 상태 표시 */}
= 1 ? "text-blue-600" : "text-gray-400"}`}>
= 1 ? "bg-blue-600 text-white" : "bg-gray-300"}`} > 1
데이터 소스
= 2 ? "text-blue-600" : "text-gray-400"}`}>
= 2 ? "bg-blue-600 text-white" : "bg-gray-300"}`} > 2
데이터 가져오기
= 3 ? "text-blue-600" : "text-gray-400"}`}>
= 3 ? "bg-blue-600 text-white" : "bg-gray-300"}`} > 3
컬럼 설정
{/* 컨텐츠 */}
{currentStep === 1 && ( )} {currentStep === 2 && (
{/* 왼쪽: 데이터 소스 설정 */}
{dataSource.type === "database" ? ( ) : ( )} {dataSource.type === "database" && (
)}
{/* 오른쪽: 데이터 미리보기 */}
{queryResult && queryResult.rows.length > 0 ? (

📋 데이터 미리보기

{queryResult.totalRows}개 데이터
                        {JSON.stringify(queryResult.rows.slice(0, 3), null, 2)}
                      
) : (
데이터를 가져온 후 미리보기가 표시됩니다
)}
)} {currentStep === 3 && queryResult && (
{listConfig.columnMode === "auto" ? ( setListConfig((prev) => ({ ...prev, columns }))} /> ) : ( setListConfig((prev) => ({ ...prev, columns }))} /> )} setListConfig((prev) => ({ ...prev, ...updates }))} />
)}
{/* 푸터 */}
{queryResult && ( 📊 {queryResult.rows.length}개 데이터 로드됨 )}
{currentStep > 1 && ( )} {currentStep < 3 ? ( ) : ( )}
); }