"use client"; import React, { useState, useEffect, useMemo } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Separator } from "@/components/ui/separator"; import { Badge } from "@/components/ui/badge"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Plus, Trash2, GripVertical, ChevronUp, ChevronDown, Settings, Check, ChevronsUpDown, Filter, Table as TableIcon, Search } from "lucide-react"; import { cn } from "@/lib/utils"; // 타입 import import { FormSectionConfig, TableSectionConfig, TableColumnConfig, TablePreFilter, TableModalFilter, TableCalculationRule, LookupOption, ExternalTableLookup, TABLE_COLUMN_TYPE_OPTIONS, FILTER_OPERATOR_OPTIONS, MODAL_FILTER_TYPE_OPTIONS, LOOKUP_TYPE_OPTIONS, LOOKUP_CONDITION_SOURCE_OPTIONS, } from "../types"; import { defaultTableSectionConfig, defaultTableColumnConfig, defaultPreFilterConfig, defaultModalFilterConfig, defaultCalculationRuleConfig, generateTableColumnId, generateFilterId, } from "../config"; // 도움말 텍스트 컴포넌트 const HelpText = ({ children }: { children: React.ReactNode }) => (

{children}

); // 컬럼 설정 아이템 컴포넌트 interface ColumnSettingItemProps { col: TableColumnConfig; index: number; totalCount: number; saveTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string }[]; displayColumns: string[]; // 검색 설정에서 선택한 표시 컬럼 목록 sourceTableColumns: { column_name: string; data_type: string; is_nullable: string; comment?: string }[]; // 소스 테이블 컬럼 sourceTableName: string; // 소스 테이블명 tables: { table_name: string; comment?: string }[]; // 전체 테이블 목록 tableColumns: Record; // 테이블별 컬럼 sections: { id: string; title: string }[]; // 섹션 목록 formFields: { columnName: string; label: string; sectionId?: string }[]; // 폼 필드 목록 tableConfig: TableSectionConfig; // 현재 행 필드 목록 표시용 onLoadTableColumns: (tableName: string) => void; onUpdate: (updates: Partial) => void; onMoveUp: () => void; onMoveDown: () => void; onRemove: () => void; } function ColumnSettingItem({ col, index, totalCount, saveTableColumns, displayColumns, sourceTableColumns, sourceTableName, tables, tableColumns, sections, formFields, tableConfig, onLoadTableColumns, onUpdate, onMoveUp, onMoveDown, onRemove, }: ColumnSettingItemProps) { const [fieldSearchOpen, setFieldSearchOpen] = useState(false); const [sourceFieldSearchOpen, setSourceFieldSearchOpen] = useState(false); const [lookupTableOpenMap, setLookupTableOpenMap] = useState>({}); // 조회 옵션 추가 const addLookupOption = () => { const newOption: LookupOption = { id: `lookup_${Date.now()}`, label: `조회 옵션 ${(col.lookup?.options || []).length + 1}`, type: "sameTable", tableName: sourceTableName, valueColumn: "", conditions: [], isDefault: (col.lookup?.options || []).length === 0, }; onUpdate({ lookup: { enabled: true, options: [...(col.lookup?.options || []), newOption], }, }); }; // 조회 옵션 삭제 const removeLookupOption = (optIndex: number) => { const newOptions = (col.lookup?.options || []).filter((_, i) => i !== optIndex); if (newOptions.length > 0 && !newOptions.some((opt) => opt.isDefault)) { newOptions[0].isDefault = true; } onUpdate({ lookup: { enabled: col.lookup?.enabled ?? false, options: newOptions, }, }); }; // 조회 옵션 업데이트 const updateLookupOption = (optIndex: number, updates: Partial) => { onUpdate({ lookup: { enabled: col.lookup?.enabled ?? false, options: (col.lookup?.options || []).map((opt, i) => i === optIndex ? { ...opt, ...updates } : opt ), }, }); }; // 조회 조건 추가 const addLookupCondition = (optIndex: number) => { const option = col.lookup?.options?.[optIndex]; if (!option) return; const newCondition: LookupCondition = { sourceType: "currentRow", sourceField: "", targetColumn: "", }; updateLookupOption(optIndex, { conditions: [...(option.conditions || []), newCondition], }); }; // 조회 조건 삭제 const removeLookupCondition = (optIndex: number, condIndex: number) => { const option = col.lookup?.options?.[optIndex]; if (!option) return; updateLookupOption(optIndex, { conditions: option.conditions.filter((_, i) => i !== condIndex), }); }; // 조회 조건 업데이트 const updateLookupCondition = (optIndex: number, condIndex: number, updates: Partial) => { const option = col.lookup?.options?.[optIndex]; if (!option) return; updateLookupOption(optIndex, { conditions: option.conditions.map((c, i) => i === condIndex ? { ...c, ...updates } : c ), }); }; return (
{col.label || col.field || `컬럼 ${index + 1}`} {TABLE_COLUMN_TYPE_OPTIONS.find((t) => t.value === col.type)?.label || col.type} {col.calculated && 계산}
{/* 필드명 - Combobox (저장할 컬럼) */}
필드를 찾을 수 없습니다. {saveTableColumns.map((column) => ( { onUpdate({ field: column.column_name, // 라벨이 비어있으면 comment로 자동 설정 ...((!col.label || col.label.startsWith("컬럼 ")) && column.comment ? { label: column.comment } : {}) }); setFieldSearchOpen(false); }} className="text-xs" >
{column.column_name} {column.comment || column.data_type}
))}
{/* 소스 필드 - Combobox (검색 모달에서 가져올 컬럼) */}
소스 필드를 찾을 수 없습니다. {/* 필드명과 동일 옵션 */} { onUpdate({ sourceField: undefined }); setSourceFieldSearchOpen(false); }} className="text-xs" > (필드명과 동일) {/* 표시 컬럼 목록 */} {displayColumns.map((colName) => { const colInfo = sourceTableColumns.find((c) => c.column_name === colName); return ( { onUpdate({ sourceField: colName }); setSourceFieldSearchOpen(false); }} className="text-xs" >
{colName} {colInfo?.comment && ( {colInfo.comment} )}
); })}
{/* 라벨 */}
onUpdate({ label: e.target.value })} placeholder="표시 라벨" className="h-8 text-xs mt-1" />
{/* 타입 */}
{/* 너비 */}
onUpdate({ width: e.target.value })} placeholder="150px" className="h-8 text-xs mt-1" />
{/* 옵션 스위치 */}
{/* 날짜 타입일 때만 일괄 적용 옵션 표시 */} {col.type === "date" && ( )}
{/* 조회 설정 (조회 ON일 때만 표시) */} {col.lookup?.enabled && (
{(col.lookup?.options || []).length === 0 ? (

"옵션 추가" 버튼을 클릭하여 조회 방식을 추가하세요.

) : (
{(col.lookup?.options || []).map((option, optIndex) => (
{/* 옵션 헤더 */}
{option.label || `옵션 ${optIndex + 1}`} {option.isDefault && ( 기본 )}
{/* 기본 설정 - 첫 번째 줄: 옵션명, 표시 라벨 */}
updateLookupOption(optIndex, { label: e.target.value })} placeholder="예: 기준단가" className="h-7 text-xs mt-0.5" />
updateLookupOption(optIndex, { displayLabel: e.target.value })} placeholder={`예: 단가 (${option.label || "옵션명"})`} className="h-7 text-xs mt-0.5" />

비워두면 옵션명만 표시

{/* 기본 설정 - 두 번째 줄: 조회 유형, 테이블, 가져올 컬럼 */}
{option.type === "sameTable" ? ( ) : ( setLookupTableOpenMap((prev) => ({ ...prev, [option.id]: open }))} > 없음 {tables.map((table) => ( { updateLookupOption(optIndex, { tableName: table.table_name }); onLoadTableColumns(table.table_name); setLookupTableOpenMap((prev) => ({ ...prev, [option.id]: false })); }} className="text-xs" > {table.table_name} ))} )}
{/* 기본 옵션 & 조회 조건 */}
{/* 조회 조건 목록 */} {(option.conditions || []).length > 0 && (
{option.conditions.map((cond, condIndex) => (
{/* 기본 조건 행 */}
{/* 다른 섹션 선택 시 - 섹션 드롭다운 */} {cond.sourceType === "sectionField" && ( )} {/* 현재 행 / 소스 테이블 / 다른 섹션 - 필드 선택 */} {cond.sourceType !== "externalTable" && (
{cond.sourceField && (

{cond.sourceType === "currentRow" ? `rowData.${cond.sourceField}` : cond.sourceType === "sourceTable" ? `${sourceTableName}.${cond.sourceField}` : `formData.${cond.sourceField}` }

)}
)} {/* 현재 행 / 소스 테이블 / 다른 섹션일 때 = 기호와 조회 컬럼 */} {cond.sourceType !== "externalTable" && ( <> =
{cond.targetColumn && option.tableName && (

{option.tableName}.{cond.targetColumn}

)}
)}
{/* 외부 테이블 조회 설정 */} {cond.sourceType === "externalTable" && cond.externalLookup && (

외부 테이블에서 조건 값 조회

{/* 1행: 조회 테이블 선택 */}

조회 테이블

setLookupTableOpenMap(prev => ({ ...prev, [`ext_${optIndex}_${condIndex}`]: open }))} > 테이블을 찾을 수 없습니다. {tables.map((table) => ( { onLoadTableColumns(table.table_name); updateLookupCondition(optIndex, condIndex, { externalLookup: { ...cond.externalLookup!, tableName: table.table_name, matchColumn: "", resultColumn: "" } }); setLookupTableOpenMap(prev => ({ ...prev, [`ext_${optIndex}_${condIndex}`]: false })); }} className="text-xs" > {table.table_name} ))}

찾을 컬럼

가져올 컬럼

{/* 2행: 비교 값 출처 */}

비교 값 출처 (찾을 때 사용할 값)

{cond.externalLookup.matchSourceType === "sectionField" && ( )}
{/* 3행: 최종 조회 컬럼 */}
조회된 값 (비교할 컬럼) =
{/* 설명 텍스트 */} {cond.externalLookup.tableName && cond.externalLookup.matchColumn && cond.externalLookup.resultColumn && cond.targetColumn && (

{cond.externalLookup.tableName}에서 {cond.externalLookup.matchColumn} = 입력값(비교 값 출처)인 행의{" "} {cond.externalLookup.resultColumn} 값을 가져와 {option.tableName}.{cond.targetColumn}와 비교

)}
)} {/* 값 변환 설정 (다른 섹션일 때만 표시) */} {cond.sourceType === "sectionField" && (
{cond.transform?.enabled && (
없음 {tables.map((table) => ( { updateLookupCondition(optIndex, condIndex, { transform: { ...cond.transform!, tableName: table.table_name, matchColumn: "", resultColumn: "" } }); onLoadTableColumns(table.table_name); }} className="text-xs" > {table.table_name} ))}
{cond.transform.tableName && cond.transform.matchColumn && cond.transform.resultColumn && (

{cond.transform.tableName}에서 {cond.transform.matchColumn} = 입력값 인 행의 {cond.transform.resultColumn} 값으로 변환

)}
)}
)}
))}
)} {/* 조회 유형 설명 */}

{option.type === "sameTable" && "동일 테이블: 검색 모달에서 선택한 행의 다른 컬럼 값"} {option.type === "relatedTable" && "연관 테이블: 현재 행 데이터로 다른 테이블 조회"} {option.type === "combinedLookup" && "복합 조건: 다른 섹션 필드 + 현재 행 조합 조회"}

))}
)}
)}
); } interface TableSectionSettingsModalProps { open: boolean; onOpenChange: (open: boolean) => void; section: FormSectionConfig; onSave: (updates: Partial) => void; tables: { table_name: string; comment?: string }[]; tableColumns: Record; onLoadTableColumns: (tableName: string) => void; // 카테고리 목록 (table_column_category_values에서 가져옴) categoryList?: { tableName: string; columnName: string; displayName?: string }[]; onLoadCategoryList?: () => void; // 전체 섹션 목록 (다른 섹션 필드 참조용) allSections?: FormSectionConfig[]; } export function TableSectionSettingsModal({ open, onOpenChange, section, onSave, tables, tableColumns, onLoadTableColumns, categoryList = [], onLoadCategoryList, allSections = [], }: TableSectionSettingsModalProps) { // 로컬 상태 const [title, setTitle] = useState(section.title); const [description, setDescription] = useState(section.description || ""); const [tableConfig, setTableConfig] = useState( section.tableConfig || { ...defaultTableSectionConfig } ); // 테이블 검색 Combobox 상태 const [tableSearchOpen, setTableSearchOpen] = useState(false); const [saveTableSearchOpen, setSaveTableSearchOpen] = useState(false); // 활성 탭 const [activeTab, setActiveTab] = useState("source"); // open이 변경될 때마다 데이터 동기화 useEffect(() => { if (open) { setTitle(section.title); setDescription(section.description || ""); setTableConfig(section.tableConfig || { ...defaultTableSectionConfig }); } }, [open, section]); // 소스 테이블 변경 시 컬럼 로드 useEffect(() => { if (tableConfig.source.tableName) { onLoadTableColumns(tableConfig.source.tableName); } }, [tableConfig.source.tableName, onLoadTableColumns]); // 저장 테이블 변경 시 컬럼 로드 useEffect(() => { if (tableConfig.saveConfig?.targetTable) { onLoadTableColumns(tableConfig.saveConfig.targetTable); } }, [tableConfig.saveConfig?.targetTable, onLoadTableColumns]); // 조회 설정에 있는 테이블들의 컬럼 로드 (모달 열릴 때) useEffect(() => { if (open && tableConfig.columns) { const tablesToLoad = new Set(); // 각 컬럼의 lookup 설정에서 테이블 수집 tableConfig.columns.forEach((col) => { if (col.lookup?.enabled && col.lookup.options) { col.lookup.options.forEach((option) => { // 조회 테이블 if (option.tableName) { tablesToLoad.add(option.tableName); } // 변환 테이블 option.conditions?.forEach((cond) => { if (cond.transform?.enabled && cond.transform.tableName) { tablesToLoad.add(cond.transform.tableName); } }); }); } }); // 수집된 테이블들의 컬럼 로드 tablesToLoad.forEach((tableName) => { if (!tableColumns[tableName]) { onLoadTableColumns(tableName); } }); } }, [open, tableConfig.columns, tableColumns, onLoadTableColumns]); // 소스 테이블의 컬럼 목록 const sourceTableColumns = useMemo(() => { return tableColumns[tableConfig.source.tableName] || []; }, [tableColumns, tableConfig.source.tableName]); // 저장 테이블의 컬럼 목록 const saveTableColumns = useMemo(() => { // 저장 테이블이 지정되어 있으면 해당 테이블의 컬럼, 아니면 소스 테이블의 컬럼 사용 const targetTable = tableConfig.saveConfig?.targetTable; if (targetTable) { return tableColumns[targetTable] || []; } return sourceTableColumns; }, [tableColumns, tableConfig.saveConfig?.targetTable, sourceTableColumns]); // 다른 섹션 목록 (현재 섹션 제외, 테이블 타입이 아닌 섹션만) const otherSections = useMemo(() => { return allSections .filter((s) => s.id !== section.id && s.type !== "table") .map((s) => ({ id: s.id, title: s.title })); }, [allSections, section.id]); // 다른 섹션의 필드 목록 const otherSectionFields = useMemo(() => { const fields: { columnName: string; label: string; sectionId: string }[] = []; allSections .filter((s) => s.id !== section.id && s.type !== "table") .forEach((s) => { (s.fields || []).forEach((f) => { fields.push({ columnName: f.columnName, label: f.label, sectionId: s.id, }); }); }); return fields; }, [allSections, section.id]); // 설정 업데이트 함수 const updateTableConfig = (updates: Partial) => { setTableConfig((prev) => ({ ...prev, ...updates })); }; const updateSource = (updates: Partial) => { updateTableConfig({ source: { ...tableConfig.source, ...updates }, }); }; const updateFilters = (updates: Partial) => { updateTableConfig({ filters: { ...tableConfig.filters, ...updates }, }); }; const updateUiConfig = (updates: Partial>) => { updateTableConfig({ uiConfig: { ...tableConfig.uiConfig, ...updates }, }); }; const updateSaveConfig = (updates: Partial>) => { updateTableConfig({ saveConfig: { ...tableConfig.saveConfig, ...updates }, }); }; // 저장 함수 const handleSave = () => { onSave({ title, description, tableConfig, }); onOpenChange(false); }; // 컬럼 추가 const addColumn = () => { const newColumn: TableColumnConfig = { ...defaultTableColumnConfig, field: `column_${(tableConfig.columns || []).length + 1}`, label: `컬럼 ${(tableConfig.columns || []).length + 1}`, }; updateTableConfig({ columns: [...(tableConfig.columns || []), newColumn], }); }; // 컬럼 삭제 const removeColumn = (index: number) => { updateTableConfig({ columns: (tableConfig.columns || []).filter((_, i) => i !== index), }); }; // 컬럼 업데이트 const updateColumn = (index: number, updates: Partial) => { updateTableConfig({ columns: (tableConfig.columns || []).map((col, i) => i === index ? { ...col, ...updates } : col ), }); }; // 컬럼 이동 const moveColumn = (index: number, direction: "up" | "down") => { const columns = [...(tableConfig.columns || [])]; if (direction === "up" && index > 0) { [columns[index - 1], columns[index]] = [columns[index], columns[index - 1]]; } else if (direction === "down" && index < columns.length - 1) { [columns[index], columns[index + 1]] = [columns[index + 1], columns[index]]; } updateTableConfig({ columns }); }; // 사전 필터 추가 const addPreFilter = () => { const newFilter: TablePreFilter = { ...defaultPreFilterConfig }; updateFilters({ preFilters: [...(tableConfig.filters?.preFilters || []), newFilter], }); }; // 사전 필터 삭제 const removePreFilter = (index: number) => { updateFilters({ preFilters: (tableConfig.filters?.preFilters || []).filter((_, i) => i !== index), }); }; // 사전 필터 업데이트 const updatePreFilter = (index: number, updates: Partial) => { updateFilters({ preFilters: (tableConfig.filters?.preFilters || []).map((f, i) => i === index ? { ...f, ...updates } : f ), }); }; // 모달 필터 추가 const addModalFilter = () => { const newFilter: TableModalFilter = { ...defaultModalFilterConfig }; updateFilters({ modalFilters: [...(tableConfig.filters?.modalFilters || []), newFilter], }); }; // 모달 필터 삭제 const removeModalFilter = (index: number) => { updateFilters({ modalFilters: (tableConfig.filters?.modalFilters || []).filter((_, i) => i !== index), }); }; // 모달 필터 업데이트 const updateModalFilter = (index: number, updates: Partial) => { updateFilters({ modalFilters: (tableConfig.filters?.modalFilters || []).map((f, i) => i === index ? { ...f, ...updates } : f ), }); }; // 계산 규칙 추가 const addCalculation = () => { const newCalc: TableCalculationRule = { ...defaultCalculationRuleConfig }; updateTableConfig({ calculations: [...(tableConfig.calculations || []), newCalc], }); }; // 계산 규칙 삭제 const removeCalculation = (index: number) => { updateTableConfig({ calculations: (tableConfig.calculations || []).filter((_, i) => i !== index), }); }; // 계산 규칙 업데이트 const updateCalculation = (index: number, updates: Partial) => { updateTableConfig({ calculations: (tableConfig.calculations || []).map((c, i) => i === index ? { ...c, ...updates } : c ), }); }; // 표시 컬럼 토글 const toggleDisplayColumn = (columnName: string) => { const current = tableConfig.source.displayColumns || []; if (current.includes(columnName)) { updateSource({ displayColumns: current.filter((c) => c !== columnName), }); } else { updateSource({ displayColumns: [...current, columnName], }); } }; // 검색 컬럼 토글 const toggleSearchColumn = (columnName: string) => { const current = tableConfig.source.searchColumns || []; if (current.includes(columnName)) { updateSource({ searchColumns: current.filter((c) => c !== columnName), }); } else { updateSource({ searchColumns: [...current, columnName], }); } }; // 표시 컬럼 순서 변경 const moveDisplayColumn = (index: number, direction: "up" | "down") => { const columns = [...(tableConfig.source.displayColumns || [])]; if (direction === "up" && index > 0) { [columns[index - 1], columns[index]] = [columns[index], columns[index - 1]]; } else if (direction === "down" && index < columns.length - 1) { [columns[index], columns[index + 1]] = [columns[index + 1], columns[index]]; } updateSource({ displayColumns: columns }); }; // 표시 컬럼 삭제 (순서 편집 영역에서) const removeDisplayColumn = (columnName: string) => { updateSource({ displayColumns: (tableConfig.source.displayColumns || []).filter((c) => c !== columnName), }); }; return ( 테이블 섹션 설정 테이블 형식의 데이터를 표시하고 편집하는 섹션을 설정합니다.
{/* 기본 정보 */}

기본 정보

setTitle(e.target.value)} placeholder="예: 품목 목록" className="h-9 text-sm" />
setDescription(e.target.value)} placeholder="섹션에 대한 설명" className="h-9 text-sm" />
{/* 탭 구성 */} 테이블 설정 컬럼 설정 검색 설정 고급 설정 {/* 테이블 설정 탭 */} {/* 소스 테이블 설정 */}

검색용 소스 테이블

검색 모달에서 데이터를 가져올 테이블입니다.

테이블을 찾을 수 없습니다. {tables.map((table) => ( { updateSource({ tableName: table.table_name }); setTableSearchOpen(false); }} className="text-sm" >
{table.table_name} {table.comment && ( {table.comment} )}
))}
{/* 저장 테이블 설정 */}

저장용 테이블

테이블 섹션 데이터를 저장할 테이블입니다. 미설정 시 메인 테이블에 저장됩니다.

테이블을 찾을 수 없습니다. { updateSaveConfig({ targetTable: undefined }); setSaveTableSearchOpen(false); }} className="text-sm" > (메인 테이블과 동일) {tables.map((table) => ( { updateSaveConfig({ targetTable: table.table_name }); // 선택 즉시 컬럼 로드 요청 onLoadTableColumns(table.table_name); setSaveTableSearchOpen(false); }} className="text-sm" >
{table.table_name} {table.comment && ( {table.comment} )}
))}
updateSaveConfig({ uniqueField: e.target.value || undefined })} placeholder="예: item_id" className="h-9 text-sm mt-1" /> 동일 값이 있으면 추가하지 않습니다.
{/* 컬럼 설정 탭 */} {/* 안내 메시지 */} {saveTableColumns.length === 0 && !tableConfig.saveConfig?.targetTable && !tableConfig.source.tableName && (

"테이블 설정" 탭에서 저장 테이블을 먼저 선택해주세요. 선택한 테이블의 컬럼을 여기서 설정할 수 있습니다.

)} {/* 테이블은 선택했지만 컬럼이 아직 로드되지 않은 경우 */} {saveTableColumns.length === 0 && (tableConfig.saveConfig?.targetTable || tableConfig.source.tableName) && (

테이블 "{tableConfig.saveConfig?.targetTable || tableConfig.source.tableName}" 의 컬럼을 불러오는 중입니다...

)}
{saveTableColumns.length > 0 && (

사용 가능한 컬럼: {saveTableColumns.length}개 ({tableConfig.saveConfig?.targetTable || tableConfig.source.tableName || "테이블 미선택"})

)}
{(tableConfig.columns || []).length === 0 ? (

컬럼이 없습니다

"컬럼 추가" 버튼으로 추가하세요

) : (
{(tableConfig.columns || []).map((col, index) => ( updateColumn(index, updates)} onMoveUp={() => moveColumn(index, "up")} onMoveDown={() => moveColumn(index, "down")} onRemove={() => removeColumn(index)} /> ))}
)}
{/* 검색 설정 탭 */} {/* 표시 컬럼 / 검색 컬럼 설정 */}

검색 모달 컬럼 설정

검색 모달에서 보여줄 컬럼과 검색 대상 컬럼을 설정합니다.

{/* 소스 테이블 미선택 시 안내 */} {!tableConfig.source.tableName && (

"테이블 설정" 탭에서 검색용 소스 테이블을 먼저 선택해주세요.

)} {/* 표시 컬럼 선택 */} {sourceTableColumns.length > 0 && (
{sourceTableColumns.map((col) => ( ))}
선택된 컬럼: {(tableConfig.source.displayColumns || []).length}개 {/* 선택된 컬럼 순서 편집 */} {(tableConfig.source.displayColumns || []).length > 0 && (
{(tableConfig.source.displayColumns || []).map((colName, index) => { const colInfo = sourceTableColumns.find((c) => c.column_name === colName); return (
{colName} {colInfo?.comment && ( {colInfo.comment} )}
); })}
)}
)} {/* 검색 컬럼 선택 */} {sourceTableColumns.length > 0 && (
{sourceTableColumns.map((col) => ( ))}
검색 컬럼: {(tableConfig.source.searchColumns || []).length}개
)}
{/* 사전 필터 */}

항상 적용되는 필터 조건입니다.

{(tableConfig.filters?.preFilters || []).map((filter, index) => (
updatePreFilter(index, { value: e.target.value })} placeholder="값" className="h-8 text-xs flex-1" />
))}
{/* 모달 필터 */}

사용자가 선택할 수 있는 필터입니다.

{(tableConfig.filters?.modalFilters || []).map((filter, index) => (
{/* 컬럼 선택 */} {/* 라벨 */} updateModalFilter(index, { label: e.target.value })} placeholder="라벨" className="h-8 text-xs w-[100px]" /> {/* 타입 */} {/* 카테고리 선택 (타입이 category일 때만 표시) */} {filter.type === "category" && ( )} {/* 삭제 버튼 */}
))}
{/* 고급 설정 탭 */} {/* UI 설정 */}

UI 설정

updateUiConfig({ addButtonText: e.target.value })} placeholder="항목 검색" className="h-8 text-xs mt-1" />
updateUiConfig({ modalTitle: e.target.value })} placeholder="항목 검색 및 선택" className="h-8 text-xs mt-1" />
updateUiConfig({ maxHeight: e.target.value })} placeholder="400px" className="h-8 text-xs mt-1" />
{/* 계산 규칙 */}

계산 규칙

다른 컬럼 값을 기반으로 자동 계산합니다.

{(tableConfig.calculations || []).map((calc, index) => (
= updateCalculation(index, { formula: e.target.value })} placeholder="수식 (예: quantity * unit_price)" className="h-8 text-xs flex-1" />
))}
); }