"use client"; /** * UnifiedList 설정 패널 * 통합 목록 컴포넌트의 세부 설정을 관리합니다. * - 현재 화면의 테이블 데이터를 사용 * - 테이블 컬럼 + 엔티티 조인 컬럼 선택 지원 */ import React, { useState, useEffect, useMemo } from "react"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Separator } from "@/components/ui/separator"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Database, Link2, GripVertical, ChevronDown, ChevronRight } from "lucide-react"; import { tableTypeApi } from "@/lib/api/screen"; import { cn } from "@/lib/utils"; interface UnifiedListConfigPanelProps { config: Record; onChange: (config: Record) => void; /** 현재 화면의 테이블명 */ currentTableName?: string; } interface ColumnOption { columnName: string; displayName: string; isJoinColumn?: boolean; sourceTable?: string; inputType?: string; } export const UnifiedListConfigPanel: React.FC = ({ config, onChange, currentTableName, }) => { // 컬럼 목록 (테이블 컬럼 + 엔티티 조인 컬럼) const [columns, setColumns] = useState([]); const [loadingColumns, setLoadingColumns] = useState(false); const [expandedJoinSections, setExpandedJoinSections] = useState>(new Set()); // 설정 업데이트 핸들러 const updateConfig = (field: string, value: any) => { const newConfig = { ...config, [field]: value }; console.log("⚙️ UnifiedListConfigPanel updateConfig:", { field, value, newConfig }); onChange(newConfig); }; // 테이블명 (현재 화면의 테이블 사용) const tableName = currentTableName || config.tableName; // 화면의 테이블명을 config에 자동 저장 useEffect(() => { if (currentTableName && config.tableName !== currentTableName) { onChange({ ...config, tableName: currentTableName }); } }, [currentTableName]); // 테이블 컬럼 및 엔티티 조인 컬럼 로드 useEffect(() => { const loadColumns = async () => { if (!tableName) { setColumns([]); return; } setLoadingColumns(true); try { // 1. 테이블 컬럼 로드 const columnData = await tableTypeApi.getColumns(tableName); const baseColumns: ColumnOption[] = columnData.map((c: any) => ({ columnName: c.columnName || c.column_name, displayName: c.displayName || c.columnLabel || c.columnName || c.column_name, isJoinColumn: false, inputType: c.inputType || c.input_type || c.webType || c.web_type, })); // 2. 엔티티 타입 컬럼 찾기 및 조인 컬럼 정보 로드 const entityColumns = columnData.filter((c: any) => (c.inputType || c.input_type) === "entity"); const joinColumnOptions: ColumnOption[] = []; for (const entityCol of entityColumns) { const colName = entityCol.columnName || entityCol.column_name; // referenceTable 우선순위: // 1. 컬럼의 reference_table 필드 // 2. detailSettings.referenceTable let referenceTable = entityCol.referenceTable || entityCol.reference_table; if (!referenceTable) { let detailSettings = entityCol.detailSettings || entityCol.detail_settings; if (typeof detailSettings === "string") { try { detailSettings = JSON.parse(detailSettings); } catch { continue; } } referenceTable = detailSettings?.referenceTable; } if (referenceTable) { try { const refColumnData = await tableTypeApi.getColumns(referenceTable); refColumnData.forEach((refCol: any) => { const refColName = refCol.columnName || refCol.column_name; const refDisplayName = refCol.displayName || refCol.columnLabel || refColName; joinColumnOptions.push({ columnName: `${colName}.${refColName}`, displayName: refDisplayName, isJoinColumn: true, sourceTable: referenceTable, }); }); } catch (error) { console.error(`참조 테이블 ${referenceTable} 컬럼 로드 실패:`, error); } } } setColumns([...baseColumns, ...joinColumnOptions]); } catch (error) { console.error("컬럼 목록 로드 실패:", error); setColumns([]); } finally { setLoadingColumns(false); } }; loadColumns(); }, [tableName]); // 컬럼 설정 const configColumns: Array<{ key: string; title: string; width?: string; isJoinColumn?: boolean; inputType?: string; thousandSeparator?: boolean; }> = config.columns || []; // 컬럼이 추가되었는지 확인 const isColumnAdded = (columnName: string) => { return configColumns.some((col) => col.key === columnName); }; // 컬럼 토글 (추가/제거) const toggleColumn = (column: ColumnOption) => { if (isColumnAdded(column.columnName)) { // 제거 const newColumns = configColumns.filter((col) => col.key !== column.columnName); updateConfig("columns", newColumns); } else { // 추가 const isNumberType = ["number", "decimal", "integer", "float", "double", "numeric", "currency"].includes( column.inputType || "", ); const newColumn = { key: column.columnName, title: column.displayName, width: "", isJoinColumn: column.isJoinColumn || false, inputType: column.inputType, thousandSeparator: isNumberType ? true : undefined, // 숫자 타입은 기본적으로 천단위 구분자 사용 }; updateConfig("columns", [...configColumns, newColumn]); } }; // 컬럼 천단위 구분자 토글 const toggleThousandSeparator = (columnKey: string, checked: boolean) => { const newColumns = configColumns.map((col) => (col.key === columnKey ? { ...col, thousandSeparator: checked } : col)); updateConfig("columns", newColumns); }; // 숫자 타입 컬럼인지 확인 const isNumberColumn = (columnKey: string) => { const colInfo = columns.find((c) => c.columnName === columnKey); const configCol = configColumns.find((c) => c.key === columnKey); const inputType = configCol?.inputType || colInfo?.inputType || ""; return ["number", "decimal", "integer", "float", "double", "numeric", "currency"].includes(inputType); }; // 컬럼 제목 수정 const updateColumnTitle = (columnKey: string, title: string) => { const newColumns = configColumns.map((col) => (col.key === columnKey ? { ...col, title } : col)); updateConfig("columns", newColumns); }; // 그룹별 컬럼 분리 const baseColumns = useMemo(() => columns.filter((col) => !col.isJoinColumn), [columns]); // 조인 컬럼을 소스 테이블별로 그룹화 const joinColumnsByTable = useMemo(() => { const grouped: Record = {}; columns .filter((col) => col.isJoinColumn) .forEach((col) => { const table = col.sourceTable || "unknown"; if (!grouped[table]) { grouped[table] = []; } grouped[table].push(col); }); return grouped; }, [columns]); // 조인 섹션 토글 const toggleJoinSection = (tableName: string) => { setExpandedJoinSections((prev) => { const newSet = new Set(prev); if (newSet.has(tableName)) { newSet.delete(tableName); } else { newSet.add(tableName); } return newSet; }); }; return (
{/* 뷰 모드 */}
{/* 카드 모드 설정 */} {config.viewMode === "card" && ( <>
{/* 제목 컬럼 */}
{/* 부제목 컬럼 */}
{/* 행당 카드 수 */}
)} {/* 컬럼 선택 */}
{loadingColumns ? (

컬럼 로딩 중...

) : !tableName ? (

테이블을 선택해주세요

) : (
{/* 테이블 컬럼 */}
{baseColumns.map((column) => (
toggleColumn(column)} > toggleColumn(column)} className="pointer-events-none h-3.5 w-3.5 flex-shrink-0" /> {column.displayName}
))}
{/* 조인 컬럼 (테이블별 그룹) */} {Object.keys(joinColumnsByTable).length > 0 && (
엔티티 조인 컬럼
{Object.entries(joinColumnsByTable).map(([refTable, refColumns]) => (
toggleJoinSection(refTable)} > {expandedJoinSections.has(refTable) ? ( ) : ( )} {refTable} ({refColumns.length})
{expandedJoinSections.has(refTable) && (
{refColumns.map((column) => (
toggleColumn(column)} > toggleColumn(column)} className="pointer-events-none h-3.5 w-3.5 flex-shrink-0" /> {column.displayName}
))}
)}
))}
)}
)}
{/* 선택된 컬럼 상세 설정 */} {configColumns.length > 0 && ( <>
{configColumns.map((column, index) => { const colInfo = columns.find((c) => c.columnName === column.key); const showThousandSeparator = isNumberColumn(column.key); return (
{column.isJoinColumn ? ( ) : ( )} updateColumnTitle(column.key, e.target.value)} placeholder="제목" className="h-6 flex-1 text-xs" />
{/* 숫자 컬럼인 경우 천단위 구분자 옵션 표시 */} {showThousandSeparator && (
toggleThousandSeparator(column.key, !!checked)} className="h-3 w-3" />
)}
); })}
)} {/* 페이지네이션 설정 */}
updateConfig("pagination", checked)} />
{config.pagination !== false && (
)}
); }; UnifiedListConfigPanel.displayName = "UnifiedListConfigPanel"; export default UnifiedListConfigPanel;