"use client"; import React, { useState, useEffect } from "react"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Checkbox } from "@/components/ui/checkbox"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { ScrollArea } from "@/components/ui/scroll-area"; import { TableListConfig, ColumnConfig } from "./types"; import { entityJoinApi } from "@/lib/api/entityJoin"; import { tableTypeApi } from "@/lib/api/screen"; import { Plus, Trash2, ArrowUp, ArrowDown, Settings, Columns, Filter, Palette, MousePointer } from "lucide-react"; export interface TableListConfigPanelProps { config: TableListConfig; onChange: (config: Partial) => void; screenTableName?: string; // 화면에 연결된 테이블명 tableColumns?: any[]; // 테이블 컬럼 정보 } /** * TableList 설정 패널 * 컴포넌트의 설정값들을 편집할 수 있는 UI 제공 */ export const TableListConfigPanel: React.FC = ({ config, onChange, screenTableName, tableColumns, }) => { console.log("🔍 TableListConfigPanel props:", { config, configType: typeof config, configSelectedTable: config?.selectedTable, configPagination: config?.pagination, paginationEnabled: config?.pagination?.enabled, paginationPageSize: config?.pagination?.pageSize, configKeys: typeof config === 'object' ? Object.keys(config || {}) : 'not object', screenTableName, tableColumns: tableColumns?.length, tableColumnsSample: tableColumns?.[0], }); const [availableTables, setAvailableTables] = useState>([]); const [loadingTables, setLoadingTables] = useState(false); const [availableColumns, setAvailableColumns] = useState< Array<{ columnName: string; dataType: string; label?: string }> >([]); const [entityJoinColumns, setEntityJoinColumns] = useState<{ availableColumns: Array<{ tableName: string; columnName: string; columnLabel: string; dataType: string; joinAlias: string; suggestedLabel: string; }>; joinTables: Array<{ tableName: string; currentDisplayColumn: string; availableColumns: Array<{ columnName: string; columnLabel: string; dataType: string; description?: string; }>; }>; }>({ availableColumns: [], joinTables: [] }); const [loadingEntityJoins, setLoadingEntityJoins] = useState(false); // 🎯 엔티티 컬럼 표시 설정을 위한 상태 const [entityDisplayConfigs, setEntityDisplayConfigs] = useState< Record< string, { sourceColumns: Array<{ columnName: string; displayName: string; dataType: string }>; joinColumns: Array<{ columnName: string; displayName: string; dataType: string }>; selectedColumns: string[]; separator: string; } > >({}); // 화면 테이블명이 있으면 자동으로 설정 useEffect(() => { if (screenTableName && (!config.selectedTable || config.selectedTable !== screenTableName)) { console.log("🔄 화면 테이블명 자동 설정:", screenTableName); onChange({ selectedTable: screenTableName }); } }, [screenTableName, config.selectedTable, onChange]); // 테이블 목록 가져오기 useEffect(() => { const fetchTables = async () => { setLoadingTables(true); try { // API 클라이언트를 사용하여 올바른 포트로 호출 const response = await tableTypeApi.getTables(); setAvailableTables( response.map((table: any) => ({ tableName: table.tableName, displayName: table.displayName || table.tableName, })), ); } catch (error) { console.error("테이블 목록 가져오기 실패:", error); } finally { setLoadingTables(false); } }; fetchTables(); }, []); // 선택된 테이블의 컬럼 목록 설정 useEffect(() => { console.log( "🔍 useEffect 실행됨 - config.selectedTable:", config.selectedTable, "screenTableName:", screenTableName, ); // 컴포넌트에 명시적으로 테이블이 선택되었거나, 화면에 연결된 테이블이 있는 경우에만 컬럼 목록 표시 const shouldShowColumns = config.selectedTable || (screenTableName && config.columns && config.columns.length > 0); if (!shouldShowColumns) { console.log("🔧 컬럼 목록 숨김 - 명시적 테이블 선택 또는 설정된 컬럼이 없음"); setAvailableColumns([]); return; } // tableColumns prop을 우선 사용하되, 컴포넌트가 명시적으로 설정되었을 때만 if (tableColumns && tableColumns.length > 0 && (config.selectedTable || config.columns?.length > 0)) { console.log("🔧 tableColumns prop 사용:", tableColumns); const mappedColumns = tableColumns.map((column: any) => ({ columnName: column.columnName || column.name, dataType: column.dataType || column.type || "text", label: column.label || column.displayName || column.columnLabel || column.columnName || column.name, })); console.log("🏷️ availableColumns 설정됨:", mappedColumns); setAvailableColumns(mappedColumns); } else if (config.selectedTable || screenTableName) { // API에서 컬럼 정보 가져오기 const fetchColumns = async () => { const tableName = config.selectedTable || screenTableName; if (!tableName) { setAvailableColumns([]); return; } console.log("🔧 API에서 컬럼 정보 가져오기:", tableName); try { const response = await fetch(`/api/tables/${tableName}/columns`); if (response.ok) { const result = await response.json(); if (result.success && result.data) { console.log("🔧 API 응답 컬럼 데이터:", result.data); setAvailableColumns( result.data.map((col: any) => ({ columnName: col.columnName, dataType: col.dataType, label: col.displayName || col.columnName, })), ); } } } catch (error) { console.error("컬럼 목록 가져오기 실패:", error); } }; fetchColumns(); } else { setAvailableColumns([]); } }, [config.selectedTable, screenTableName, tableColumns, config.columns]); // Entity 조인 컬럼 정보 가져오기 useEffect(() => { const fetchEntityJoinColumns = async () => { const tableName = config.selectedTable || screenTableName; if (!tableName) { setEntityJoinColumns({ availableColumns: [], joinTables: [] }); return; } setLoadingEntityJoins(true); try { console.log("🔗 Entity 조인 컬럼 정보 가져오기:", tableName); const result = await entityJoinApi.getEntityJoinColumns(tableName); console.log("✅ Entity 조인 컬럼 응답:", result); setEntityJoinColumns({ availableColumns: result.availableColumns || [], joinTables: result.joinTables || [], }); } catch (error) { console.error("❌ Entity 조인 컬럼 조회 오류:", error); setEntityJoinColumns({ availableColumns: [], joinTables: [] }); } finally { setLoadingEntityJoins(false); } }; fetchEntityJoinColumns(); }, [config.selectedTable, screenTableName]); const handleChange = (key: keyof TableListConfig, value: any) => { onChange({ [key]: value }); }; const handleNestedChange = (parentKey: keyof TableListConfig, childKey: string, value: any) => { console.log("🔧 TableListConfigPanel handleNestedChange:", { parentKey, childKey, value, parentValue: config[parentKey], hasOnChange: !!onChange, onChangeType: typeof onChange, }); const parentValue = config[parentKey] as any; const newConfig = { [parentKey]: { ...parentValue, [childKey]: value, }, }; console.log("📤 TableListConfigPanel onChange 호출:", newConfig); onChange(newConfig); }; // 컬럼 추가 const addColumn = (columnName: string) => { const existingColumn = config.columns?.find((col) => col.columnName === columnName); if (existingColumn) return; // tableColumns에서 해당 컬럼의 라벨 정보 찾기 const columnInfo = tableColumns?.find((col: any) => (col.columnName || col.name) === columnName); // 라벨명 우선 사용, 없으면 컬럼명 사용 const displayName = columnInfo?.label || columnInfo?.displayName || columnName; const newColumn: ColumnConfig = { columnName, displayName, visible: true, sortable: true, searchable: true, align: "left", format: "text", order: config.columns?.length || 0, }; handleChange("columns", [...(config.columns || []), newColumn]); }; // 🎯 조인 컬럼 추가 (조인 탭에서 추가하는 컬럼들은 일반 컬럼으로 처리) const addEntityColumn = (joinColumn: (typeof entityJoinColumns.availableColumns)[0]) => { const existingColumn = config.columns?.find((col) => col.columnName === joinColumn.joinAlias); if (existingColumn) return; // 조인 탭에서 추가하는 컬럼들은 일반 컬럼으로 처리 (isEntityJoin: false) const newColumn: ColumnConfig = { columnName: joinColumn.joinAlias, displayName: joinColumn.columnLabel, visible: true, sortable: true, searchable: true, align: "left", format: "text", order: config.columns?.length || 0, isEntityJoin: false, // 조인 탭에서 추가하는 컬럼은 엔티티 타입이 아님 }; handleChange("columns", [...(config.columns || []), newColumn]); console.log("🔗 조인 컬럼 추가됨 (일반 컬럼으로 처리):", newColumn); }; // 컬럼 제거 const removeColumn = (columnName: string) => { const updatedColumns = config.columns?.filter((col) => col.columnName !== columnName) || []; handleChange("columns", updatedColumns); }; // 컬럼 업데이트 const updateColumn = (columnName: string, updates: Partial) => { const updatedColumns = config.columns?.map((col) => (col.columnName === columnName ? { ...col, ...updates } : col)) || []; handleChange("columns", updatedColumns); }; // 🎯 기존 컬럼들을 체크하여 엔티티 타입인 경우 isEntityJoin 플래그 설정 useEffect(() => { console.log("🔍 엔티티 컬럼 감지 useEffect 실행:", { hasColumns: !!config.columns, columnsCount: config.columns?.length || 0, hasTableColumns: !!tableColumns, tableColumnsCount: tableColumns?.length || 0, selectedTable: config.selectedTable, }); if (!config.columns || !tableColumns) { console.log("⚠️ 컬럼 또는 테이블 컬럼 정보가 없어서 엔티티 감지 스킵"); return; } const updatedColumns = config.columns.map((column) => { // 이미 isEntityJoin이 설정된 경우 스킵 if (column.isEntityJoin) { console.log("✅ 이미 엔티티 플래그 설정됨:", column.columnName); return column; } // 테이블 컬럼 정보에서 해당 컬럼 찾기 const tableColumn = tableColumns.find((tc) => tc.columnName === column.columnName); console.log("🔍 컬럼 검색:", { columnName: column.columnName, found: !!tableColumn, inputType: tableColumn?.input_type, webType: tableColumn?.web_type, }); // 엔티티 타입인 경우 isEntityJoin 플래그 설정 (input_type 또는 web_type 확인) if (tableColumn && (tableColumn.input_type === "entity" || tableColumn.web_type === "entity")) { console.log("🎯 엔티티 컬럼 감지 및 플래그 설정:", { columnName: column.columnName, referenceTable: tableColumn.reference_table, referenceTableAlt: tableColumn.referenceTable, allTableColumnKeys: Object.keys(tableColumn), }); return { ...column, isEntityJoin: true, entityJoinInfo: { sourceTable: config.selectedTable || "", sourceColumn: column.columnName, joinAlias: column.columnName, }, entityDisplayConfig: { displayColumns: [], // 빈 배열로 초기화 separator: " - ", sourceTable: config.selectedTable || "", joinTable: tableColumn.reference_table || tableColumn.referenceTable || "", }, }; } return column; }); // 변경사항이 있는 경우에만 업데이트 const hasChanges = updatedColumns.some((col, index) => col.isEntityJoin !== config.columns![index].isEntityJoin); if (hasChanges) { console.log("🎯 엔티티 컬럼 플래그 업데이트:", updatedColumns); handleChange("columns", updatedColumns); } else { console.log("ℹ️ 엔티티 컬럼 변경사항 없음"); } }, [config.columns, tableColumns, config.selectedTable]); // 🎯 엔티티 컬럼의 표시 컬럼 정보 로드 const loadEntityDisplayConfig = async (column: ColumnConfig) => { console.log("🔍 loadEntityDisplayConfig 시작:", { columnName: column.columnName, isEntityJoin: column.isEntityJoin, entityJoinInfo: column.entityJoinInfo, entityDisplayConfig: column.entityDisplayConfig, configSelectedTable: config.selectedTable, }); if (!column.isEntityJoin || !column.entityJoinInfo) { console.log("⚠️ 엔티티 컬럼 조건 불만족:", { isEntityJoin: column.isEntityJoin, entityJoinInfo: column.entityJoinInfo, }); return; } // entityDisplayConfig가 없으면 초기화 if (!column.entityDisplayConfig) { console.log("🔧 entityDisplayConfig 초기화:", column.columnName); const updatedColumns = config.columns?.map((col) => { if (col.columnName === column.columnName) { return { ...col, entityDisplayConfig: { displayColumns: [], separator: " - ", sourceTable: config.selectedTable || "", joinTable: "", }, }; } return col; }); if (updatedColumns) { handleChange("columns", updatedColumns); // 업데이트된 컬럼으로 다시 시도 const updatedColumn = updatedColumns.find((col) => col.columnName === column.columnName); if (updatedColumn) { console.log("🔄 업데이트된 컬럼으로 재시도:", updatedColumn.entityDisplayConfig); return loadEntityDisplayConfig(updatedColumn); } } return; } console.log("🔍 entityDisplayConfig 전체 구조:", column.entityDisplayConfig); console.log("🔍 entityDisplayConfig 키들:", Object.keys(column.entityDisplayConfig)); // sourceTable과 joinTable이 없으면 entityJoinInfo에서 가져오기 let sourceTable = column.entityDisplayConfig.sourceTable; let joinTable = column.entityDisplayConfig.joinTable; if (!sourceTable && column.entityJoinInfo) { sourceTable = column.entityJoinInfo.sourceTable; } if (!joinTable) { // joinTable이 없으면 tableTypeApi로 조회해서 설정 try { console.log("🔍 joinTable이 없어서 tableTypeApi로 조회:", sourceTable); const columnList = await tableTypeApi.getColumns(sourceTable); const columnInfo = columnList.find((col: any) => (col.column_name || col.columnName) === column.columnName); if (columnInfo?.reference_table || columnInfo?.referenceTable) { joinTable = columnInfo.reference_table || columnInfo.referenceTable; console.log("✅ tableTypeApi에서 조인 테이블 정보 찾음:", joinTable); // entityDisplayConfig 업데이트 const updatedConfig = { ...column.entityDisplayConfig, sourceTable: sourceTable, joinTable: joinTable, }; // 컬럼 설정 업데이트 const updatedColumns = config.columns?.map((col) => col.columnName === column.columnName ? { ...col, entityDisplayConfig: updatedConfig } : col, ); if (updatedColumns) { handleChange("columns", updatedColumns); } } } catch (error) { console.error("tableTypeApi 컬럼 정보 조회 실패:", error); } } console.log("🔍 최종 추출한 값:", { sourceTable, joinTable }); const configKey = `${column.columnName}`; // 이미 로드된 경우 스킵 if (entityDisplayConfigs[configKey]) return; // joinTable이 비어있으면 tableTypeApi로 컬럼 정보를 다시 가져와서 referenceTable 정보를 찾기 let actualJoinTable = joinTable; if (!actualJoinTable && sourceTable) { try { console.log("🔍 tableTypeApi로 컬럼 정보 다시 조회:", { tableName: sourceTable, columnName: column.columnName, }); const columnList = await tableTypeApi.getColumns(sourceTable); const columnInfo = columnList.find((col: any) => (col.column_name || col.columnName) === column.columnName); console.log("🔍 컬럼 정보 조회 결과:", { columnInfo: columnInfo, referenceTable: columnInfo?.reference_table || columnInfo?.referenceTable, referenceColumn: columnInfo?.reference_column || columnInfo?.referenceColumn, }); if (columnInfo?.reference_table || columnInfo?.referenceTable) { actualJoinTable = columnInfo.reference_table || columnInfo.referenceTable; console.log("✅ tableTypeApi에서 조인 테이블 정보 찾음:", actualJoinTable); // entityDisplayConfig 업데이트 const updatedConfig = { ...column.entityDisplayConfig, joinTable: actualJoinTable, }; // 컬럼 설정 업데이트 const updatedColumns = config.columns?.map((col) => col.columnName === column.columnName ? { ...col, entityDisplayConfig: updatedConfig } : col, ); if (updatedColumns) { handleChange("columns", updatedColumns); } } else { console.log("⚠️ tableTypeApi에서도 referenceTable을 찾을 수 없음:", { columnName: column.columnName, columnInfo: columnInfo, }); } } catch (error) { console.error("tableTypeApi 컬럼 정보 조회 실패:", error); } } // sourceTable과 joinTable이 모두 있어야 로드 if (!sourceTable || !actualJoinTable) { console.log("⚠️ sourceTable 또는 joinTable이 비어있어서 로드 스킵:", { sourceTable, joinTable: actualJoinTable }); return; } try { // 기본 테이블과 조인 테이블의 컬럼 정보를 병렬로 로드 const [sourceResult, joinResult] = await Promise.all([ entityJoinApi.getReferenceTableColumns(sourceTable), entityJoinApi.getReferenceTableColumns(actualJoinTable), ]); const sourceColumns = sourceResult.columns || []; const joinColumns = joinResult.columns || []; setEntityDisplayConfigs((prev) => ({ ...prev, [configKey]: { sourceColumns, joinColumns, selectedColumns: column.entityDisplayConfig?.displayColumns || [], separator: column.entityDisplayConfig?.separator || " - ", }, })); } catch (error) { console.error("엔티티 표시 컬럼 정보 로드 실패:", error); } }; // 🎯 엔티티 표시 컬럼 선택 토글 const toggleEntityDisplayColumn = (columnName: string, selectedColumn: string) => { const configKey = `${columnName}`; const localConfig = entityDisplayConfigs[configKey]; if (!localConfig) return; const newSelectedColumns = localConfig.selectedColumns.includes(selectedColumn) ? localConfig.selectedColumns.filter((col) => col !== selectedColumn) : [...localConfig.selectedColumns, selectedColumn]; // 로컬 상태 업데이트 setEntityDisplayConfigs((prev) => ({ ...prev, [configKey]: { ...prev[configKey], selectedColumns: newSelectedColumns, }, })); // 실제 컬럼 설정도 업데이트 const updatedColumns = config.columns?.map((col) => { if (col.columnName === columnName && col.entityDisplayConfig) { return { ...col, entityDisplayConfig: { ...col.entityDisplayConfig, displayColumns: newSelectedColumns, }, }; } return col; }); if (updatedColumns) { handleChange("columns", updatedColumns); console.log("🎯 엔티티 표시 컬럼 설정 업데이트:", { columnName, selectedColumns: newSelectedColumns, updatedColumn: updatedColumns.find((col) => col.columnName === columnName), }); } }; // 🎯 엔티티 표시 구분자 업데이트 const updateEntityDisplaySeparator = (columnName: string, separator: string) => { const configKey = `${columnName}`; const localConfig = entityDisplayConfigs[configKey]; if (!localConfig) return; // 로컬 상태 업데이트 setEntityDisplayConfigs((prev) => ({ ...prev, [configKey]: { ...prev[configKey], separator, }, })); // 실제 컬럼 설정도 업데이트 const updatedColumns = config.columns?.map((col) => { if (col.columnName === columnName && col.entityDisplayConfig) { return { ...col, entityDisplayConfig: { ...col.entityDisplayConfig, separator, }, }; } return col; }); if (updatedColumns) { handleChange("columns", updatedColumns); console.log("🎯 엔티티 표시 구분자 설정 업데이트:", { columnName, separator, updatedColumn: updatedColumns.find((col) => col.columnName === columnName), }); } }; // 컬럼 순서 변경 const moveColumn = (columnName: string, direction: "up" | "down") => { const columns = [...(config.columns || [])]; const index = columns.findIndex((col) => col.columnName === columnName); if (index === -1) return; const targetIndex = direction === "up" ? index - 1 : index + 1; if (targetIndex < 0 || targetIndex >= columns.length) return; [columns[index], columns[targetIndex]] = [columns[targetIndex], columns[index]]; // order 값 재정렬 columns.forEach((col, idx) => { col.order = idx; }); handleChange("columns", columns); }; // 필터 추가 const addFilter = (columnName: string) => { const existingFilter = config.filter?.filters?.find((f) => f.columnName === columnName); if (existingFilter) return; const column = availableColumns.find((col) => col.columnName === columnName); if (!column) return; // tableColumns에서 해당 컬럼의 메타정보 찾기 const tableColumn = tableColumns?.find((tc) => tc.columnName === columnName); // 컬럼의 데이터 타입과 웹타입에 따라 위젯 타입 결정 const inferWidgetType = (dataType: string, webType?: string): string => { // 웹타입이 있으면 우선 사용 if (webType) { return webType; } // 데이터 타입으로 추론 const type = dataType.toLowerCase(); if (type.includes("int") || type.includes("numeric") || type.includes("decimal")) return "number"; if (type.includes("date") || type.includes("timestamp")) return "date"; if (type.includes("bool")) return "boolean"; return "text"; }; const widgetType = inferWidgetType(column.dataType, tableColumn?.webType || tableColumn?.web_type); const newFilter = { columnName, widgetType, label: column.label || column.columnName, gridColumns: 3, numberFilterMode: "range" as const, // 코드 타입인 경우 코드 카테고리 추가 ...(widgetType === "code" && { codeCategory: tableColumn?.codeCategory || tableColumn?.code_category, }), // 엔티티 타입인 경우 참조 정보 추가 ...(widgetType === "entity" && { referenceTable: tableColumn?.referenceTable || tableColumn?.reference_table, referenceColumn: tableColumn?.referenceColumn || tableColumn?.reference_column, displayColumn: tableColumn?.displayColumn || tableColumn?.display_column, }), }; console.log("🔍 필터 추가:", newFilter); const currentFilters = config.filter?.filters || []; handleNestedChange("filter", "filters", [...currentFilters, newFilter]); }; // 필터 제거 const removeFilter = (index: number) => { const currentFilters = config.filter?.filters || []; const updatedFilters = currentFilters.filter((_, i) => i !== index); handleNestedChange("filter", "filters", updatedFilters); }; // 필터 업데이트 const updateFilter = (index: number, key: string, value: any) => { const currentFilters = config.filter?.filters || []; const updatedFilters = currentFilters.map((filter, i) => (i === index ? { ...filter, [key]: value } : filter)); handleNestedChange("filter", "filters", updatedFilters); }; return (
테이블 리스트 설정
기본 컬럼 조인 필터 액션 스타일 {/* 기본 설정 탭 */} 연결된 테이블 화면에 연결된 테이블 정보가 자동으로 매핑됩니다
{screenTableName ? ( {screenTableName} ) : ( 테이블이 연결되지 않았습니다 )}
{screenTableName && (
화면 설정에서 자동으로 연결된 테이블입니다
)}
handleChange("title", e.target.value)} placeholder="테이블 제목 (선택사항)" />
표시 설정
handleChange("showHeader", checked)} />
handleChange("showFooter", checked)} />
handleChange("autoLoad", checked)} />
높이 설정
{config.height === "fixed" && (
handleChange("fixedHeight", parseInt(e.target.value) || 400)} min={200} max={1000} />
)}
페이지네이션
handleNestedChange("pagination", "enabled", checked)} />
{config.pagination?.enabled && ( <>
handleNestedChange("pagination", "showSizeSelector", checked)} />
handleNestedChange("pagination", "showPageInfo", checked)} />
)}
가로 스크롤 및 컬럼 고정 컬럼이 많을 때 가로 스크롤과 컬럼 고정 기능을 설정하세요
handleNestedChange("horizontalScroll", "enabled", checked)} />
{config.horizontalScroll?.enabled && (
handleNestedChange("horizontalScroll", "maxVisibleColumns", parseInt(e.target.value) || 8) } min={3} max={20} placeholder="8" className="h-8" />
이 수를 넘는 컬럼이 있으면 가로 스크롤이 생성됩니다
handleNestedChange("horizontalScroll", "minColumnWidth", parseInt(e.target.value) || 100) } min={50} max={500} placeholder="100" className="h-8" />
handleNestedChange("horizontalScroll", "maxColumnWidth", parseInt(e.target.value) || 300) } min={100} max={800} placeholder="300" className="h-8" />
)}
체크박스 설정 행 선택을 위한 체크박스 기능을 설정하세요
handleNestedChange("checkbox", "enabled", checked)} />
{config.checkbox?.enabled && (
handleNestedChange("checkbox", "multiple", checked)} />
handleNestedChange("checkbox", "selectAll", checked)} />
)}
{/* 컬럼 설정 탭 */} {/* 🎯 엔티티 컬럼 표시 설정 섹션 - 컬럼 설정 패널 바깥으로 분리 */} {config.columns?.some((col) => col.isEntityJoin) && ( 🎯 엔티티 컬럼 표시 설정 엔티티 타입 컬럼의 표시할 컬럼들을 조합하여 설정하세요 {config.columns ?.filter((col) => col.isEntityJoin && col.entityDisplayConfig) .map((column) => (
{column.columnName} {column.displayName}
{entityDisplayConfigs[column.columnName] && (
{/* 구분자 설정 */}
updateEntityDisplaySeparator(column.columnName, e.target.value)} className="h-7 text-xs" placeholder=" - " />
{/* 기본 테이블 컬럼 */}
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
toggleEntityDisplayColumn(column.columnName, col.columnName) } className="h-3 w-3" />
))}
{/* 조인 테이블 컬럼 */}
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
toggleEntityDisplayColumn(column.columnName, col.columnName) } className="h-3 w-3" />
))}
{/* 선택된 컬럼 미리보기 */} {entityDisplayConfigs[column.columnName].selectedColumns.length > 0 && (
{entityDisplayConfigs[column.columnName].selectedColumns.map((colName, idx) => ( {colName} {idx < entityDisplayConfigs[column.columnName].selectedColumns.length - 1 && ( {entityDisplayConfigs[column.columnName].separator} )} ))}
)}
)}
))}
)} {!screenTableName ? (

테이블이 연결되지 않았습니다.

화면에 테이블을 연결한 후 컬럼을 설정할 수 있습니다.

) : availableColumns.length === 0 ? (

컬럼을 추가하려면 먼저 컴포넌트에 테이블을 명시적으로 선택하거나

기본 설정 탭에서 테이블을 설정해주세요.

현재 화면 테이블: {screenTableName}

) : ( <> 컬럼 추가 - {screenTableName} {availableColumns.length > 0 ? `${availableColumns.length}개의 사용 가능한 컬럼에서 선택하세요` : "컬럼 정보를 불러오는 중..."} {availableColumns.length > 0 ? (
{availableColumns .filter((col) => !config.columns?.find((c) => c.columnName === col.columnName)) .map((column) => ( ))}
) : (

컬럼 정보를 불러오는 중입니다...

)}
)} {screenTableName && ( 컬럼 설정 선택된 컬럼들의 표시 옵션을 설정하세요
{config.columns?.map((column, index) => (
updateColumn(column.columnName, { visible: checked as boolean }) } /> {availableColumns.find((col) => col.columnName === column.columnName)?.label || column.displayName || column.columnName}
{column.visible && (
col.columnName === column.columnName)?.label || column.displayName || column.columnName } onChange={(e) => updateColumn(column.columnName, { displayName: e.target.value })} className="h-8" />
{/* 엔티티 타입 컬럼 표시 */} {column.isEntityJoin && (
엔티티 타입 표시 컬럼 설정은 상단의 "🎯 엔티티 컬럼 표시 설정" 섹션에서 하세요
)}
updateColumn(column.columnName, { width: e.target.value ? parseInt(e.target.value) : undefined, }) } placeholder="자동" className="h-8" />
{(column.fixed === "left" || column.fixed === "right") && (
updateColumn(column.columnName, { fixedOrder: parseInt(e.target.value) || 0, }) } placeholder="0" className="h-8" min="0" />
)}
updateColumn(column.columnName, { sortable: checked as boolean }) } />
updateColumn(column.columnName, { searchable: checked as boolean }) } />
)}
))}
)}
{/* Entity 조인 컬럼 추가 탭 */} Entity 조인 컬럼 추가 Entity 조인된 테이블의 다른 컬럼들을 추가로 표시할 수 있습니다. {loadingEntityJoins ? (
조인 정보를 가져오는 중...
) : entityJoinColumns.joinTables.length === 0 ? (
Entity 조인이 설정된 컬럼이 없습니다.
먼저 컬럼의 웹타입을 'entity'로 설정하고 참조 테이블을 지정해주세요.
) : (
{/* 조인 테이블별 그룹 */} {entityJoinColumns.joinTables.map((joinTable, tableIndex) => ( 📊 {joinTable.tableName} 현재: {joinTable.currentDisplayColumn} {joinTable.availableColumns.length === 0 ? (
추가할 수 있는 컬럼이 없습니다.
) : (
{joinTable.availableColumns.map((column, colIndex) => { const matchingJoinColumn = entityJoinColumns.availableColumns.find( (jc) => jc.tableName === joinTable.tableName && jc.columnName === column.columnName, ); const isAlreadyAdded = config.columns?.some( (col) => col.columnName === matchingJoinColumn?.joinAlias, ); return (
{column.columnLabel}
{column.columnName} ({column.dataType})
{column.description && (
{column.description}
)}
{isAlreadyAdded ? ( 추가됨 ) : ( matchingJoinColumn && ( ) )}
); })}
)}
))} {/* 전체 사용 가능한 컬럼 요약 */} {entityJoinColumns.availableColumns.length > 0 && ( 📋 추가 가능한 컬럼 요약
총 {entityJoinColumns.availableColumns.length}개의 컬럼을 추가할 수 있습니다.
{entityJoinColumns.availableColumns.map((column, index) => { const isAlreadyAdded = config.columns?.some( (col) => col.columnName === column.joinAlias, ); return ( !isAlreadyAdded && addEntityColumn(column)} > {column.columnLabel} {!isAlreadyAdded && } ); })}
)}
)}
{/* 필터 설정 탭 */} {/* 필터 기능 활성화 */} 필터 설정 테이블에서 사용할 검색 필터를 설정하세요
handleNestedChange("filter", "enabled", checked)} />
{/* 필터 목록 */} {config.filter?.enabled && ( 사용할 필터 검색에 사용할 컬럼 필터를 추가하고 설정하세요 {/* 필터 추가 버튼 */} {availableColumns.length > 0 && (
{availableColumns .filter((col) => !config.filter?.filters?.find((f) => f.columnName === col.columnName)) .map((column) => ( ))}
)} {/* 설정된 필터 목록 */} {config.filter?.filters && config.filter.filters.length > 0 && (

설정된 필터

{config.filter.filters.map((filter, index) => (
{filter.widgetType} {filter.label}
updateFilter(index, "label", e.target.value)} placeholder="필터 라벨" />
{/* 숫자 타입인 경우 검색 모드 선택 */} {(filter.widgetType === "number" || filter.widgetType === "decimal") && (
)} {/* 코드 타입인 경우 코드 카테고리 */} {filter.widgetType === "code" && (
updateFilter(index, "codeCategory", e.target.value)} placeholder="코드 카테고리" />
)}
))}
)}
)}
{/* 액션 설정 탭 */} 행 액션
handleNestedChange("actions", "showActions", checked)} />
handleNestedChange("actions", "bulkActions", checked)} />
{/* 스타일 설정 탭 */} 테이블 스타일
handleNestedChange("tableStyle", "alternateRows", checked)} />
handleNestedChange("tableStyle", "hoverEffect", checked)} />
handleChange("stickyHeader", checked)} />
); };