"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, Check, ChevronsUpDown, ArrowRight } from "lucide-react";
import { cn } from "@/lib/utils";
// 타입 import
import {
TableColumnConfig,
ValueMappingConfig,
ColumnModeConfig,
TableJoinCondition,
LookupConfig,
LookupOption,
LookupCondition,
VALUE_MAPPING_TYPE_OPTIONS,
JOIN_SOURCE_TYPE_OPTIONS,
TABLE_COLUMN_TYPE_OPTIONS,
LOOKUP_TYPE_OPTIONS,
LOOKUP_CONDITION_SOURCE_OPTIONS,
} from "../types";
import {
defaultValueMappingConfig,
defaultColumnModeConfig,
generateColumnModeId,
} from "../config";
// 도움말 텍스트 컴포넌트
const HelpText = ({ children }: { children: React.ReactNode }) => (
{children}
);
interface TableColumnSettingsModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
column: TableColumnConfig;
sourceTableName: string; // 소스 테이블명
sourceTableColumns: { column_name: string; data_type: string; comment?: string; input_type?: string }[];
formFields: { columnName: string; label: string; sectionId?: string; sectionTitle?: string }[]; // formData 필드 목록 (섹션 정보 포함)
sections: { id: string; title: string }[]; // 섹션 목록
onSave: (updatedColumn: TableColumnConfig) => void;
tables: { table_name: string; comment?: string }[];
tableColumns: Record;
onLoadTableColumns: (tableName: string) => void;
}
export function TableColumnSettingsModal({
open,
onOpenChange,
column,
sourceTableName,
sourceTableColumns,
formFields,
sections,
onSave,
tables,
tableColumns,
onLoadTableColumns,
}: TableColumnSettingsModalProps) {
// 로컬 상태
const [localColumn, setLocalColumn] = useState({ ...column });
// 외부 테이블 검색 상태
const [externalTableOpen, setExternalTableOpen] = useState(false);
// 조회 테이블 검색 상태 (옵션별)
const [lookupTableOpenMap, setLookupTableOpenMap] = useState>({});
// 활성 탭
const [activeTab, setActiveTab] = useState("basic");
// open이 변경될 때마다 데이터 동기화
useEffect(() => {
if (open) {
setLocalColumn({ ...column });
}
}, [open, column]);
// 외부 테이블 컬럼 로드
const externalTableName = localColumn.valueMapping?.externalRef?.tableName;
useEffect(() => {
if (externalTableName) {
onLoadTableColumns(externalTableName);
}
}, [externalTableName, onLoadTableColumns]);
// 외부 테이블의 컬럼 목록
const externalTableColumns = useMemo(() => {
if (!externalTableName) return [];
return tableColumns[externalTableName] || [];
}, [tableColumns, externalTableName]);
// 소스 필드 기준으로 카테고리 타입인지 확인
const actualSourceField = localColumn.sourceField || localColumn.field;
const sourceColumnInfo = sourceTableColumns.find((c) => c.column_name === actualSourceField);
const isCategoryColumn = sourceColumnInfo?.input_type === "category";
// 카테고리 컬럼인 경우 타입을 자동으로 category로 설정
useEffect(() => {
if (isCategoryColumn && localColumn.type !== "category") {
updateColumn({ type: "category" });
}
}, [isCategoryColumn, localColumn.type]);
// 컬럼 업데이트 함수
const updateColumn = (updates: Partial) => {
setLocalColumn((prev) => ({ ...prev, ...updates }));
};
// 값 매핑 업데이트
const updateValueMapping = (updates: Partial) => {
const current = localColumn.valueMapping || { ...defaultValueMappingConfig };
updateColumn({
valueMapping: { ...current, ...updates },
});
};
// 외부 참조 업데이트
const updateExternalRef = (updates: Partial>) => {
const current = localColumn.valueMapping?.externalRef || {
tableName: "",
valueColumn: "",
joinConditions: [],
};
updateValueMapping({
externalRef: { ...current, ...updates },
});
};
// 조인 조건 추가
const addJoinCondition = () => {
const current = localColumn.valueMapping?.externalRef?.joinConditions || [];
const newCondition: TableJoinCondition = {
sourceType: "row",
sourceField: "",
targetColumn: "",
operator: "=",
};
updateExternalRef({
joinConditions: [...current, newCondition],
});
};
// 조인 조건 삭제
const removeJoinCondition = (index: number) => {
const current = localColumn.valueMapping?.externalRef?.joinConditions || [];
updateExternalRef({
joinConditions: current.filter((_, i) => i !== index),
});
};
// 조인 조건 업데이트
const updateJoinCondition = (index: number, updates: Partial) => {
const current = localColumn.valueMapping?.externalRef?.joinConditions || [];
updateExternalRef({
joinConditions: current.map((c, i) => (i === index ? { ...c, ...updates } : c)),
});
};
// 컬럼 모드 추가
const addColumnMode = () => {
const newMode: ColumnModeConfig = {
...defaultColumnModeConfig,
id: generateColumnModeId(),
label: `모드 ${(localColumn.columnModes || []).length + 1}`,
};
updateColumn({
columnModes: [...(localColumn.columnModes || []), newMode],
});
};
// 컬럼 모드 삭제
const removeColumnMode = (index: number) => {
updateColumn({
columnModes: (localColumn.columnModes || []).filter((_, i) => i !== index),
});
};
// 컬럼 모드 업데이트
const updateColumnMode = (index: number, updates: Partial) => {
updateColumn({
columnModes: (localColumn.columnModes || []).map((m, i) =>
i === index ? { ...m, ...updates } : m
),
});
};
// ============================================
// 조회(Lookup) 관련 함수들
// ============================================
// 조회 설정 업데이트
const updateLookup = (updates: Partial) => {
const current = localColumn.lookup || { enabled: false, options: [] };
updateColumn({
lookup: { ...current, ...updates },
});
};
// 조회 옵션 추가
const addLookupOption = () => {
const newOption: LookupOption = {
id: `lookup_${Date.now()}`,
label: `조회 옵션 ${(localColumn.lookup?.options || []).length + 1}`,
type: "sameTable",
tableName: sourceTableName, // 기본값: 소스 테이블
valueColumn: "",
conditions: [],
isDefault: (localColumn.lookup?.options || []).length === 0, // 첫 번째 옵션은 기본값
};
updateLookup({
options: [...(localColumn.lookup?.options || []), newOption],
});
};
// 조회 옵션 삭제
const removeLookupOption = (index: number) => {
const newOptions = (localColumn.lookup?.options || []).filter((_, i) => i !== index);
// 삭제 후 기본 옵션이 없으면 첫 번째를 기본으로
if (newOptions.length > 0 && !newOptions.some(opt => opt.isDefault)) {
newOptions[0].isDefault = true;
}
updateLookup({ options: newOptions });
};
// 조회 옵션 업데이트
const updateLookupOption = (index: number, updates: Partial) => {
updateLookup({
options: (localColumn.lookup?.options || []).map((opt, i) =>
i === index ? { ...opt, ...updates } : opt
),
});
};
// 조회 조건 추가
const addLookupCondition = (optionIndex: number) => {
const option = localColumn.lookup?.options?.[optionIndex];
if (!option) return;
const newCondition: LookupCondition = {
sourceType: "currentRow",
sourceField: "",
targetColumn: "",
};
updateLookupOption(optionIndex, {
conditions: [...(option.conditions || []), newCondition],
});
};
// 조회 조건 삭제
const removeLookupCondition = (optionIndex: number, conditionIndex: number) => {
const option = localColumn.lookup?.options?.[optionIndex];
if (!option) return;
updateLookupOption(optionIndex, {
conditions: option.conditions.filter((_, i) => i !== conditionIndex),
});
};
// 조회 조건 업데이트
const updateLookupCondition = (optionIndex: number, conditionIndex: number, updates: Partial) => {
const option = localColumn.lookup?.options?.[optionIndex];
if (!option) return;
updateLookupOption(optionIndex, {
conditions: option.conditions.map((c, i) =>
i === conditionIndex ? { ...c, ...updates } : c
),
});
};
// 조회 옵션의 테이블 컬럼 로드
useEffect(() => {
if (localColumn.lookup?.enabled) {
localColumn.lookup.options?.forEach(option => {
if (option.tableName) {
onLoadTableColumns(option.tableName);
}
});
}
}, [localColumn.lookup?.enabled, localColumn.lookup?.options, onLoadTableColumns]);
// 저장 함수
const handleSave = () => {
onSave(localColumn);
onOpenChange(false);
};
// 값 매핑 타입에 따른 설정 UI 렌더링
const renderValueMappingConfig = () => {
const mappingType = localColumn.valueMapping?.type || "source";
switch (mappingType) {
case "source":
return (
소스 테이블에서 복사할 컬럼을 선택하세요.
);
case "manual":
return (
사용자가 직접 입력하는 필드입니다.
기본값을 설정하려면 "기본 설정" 탭에서 설정하세요.
);
case "internal":
return (
같은 모달의 다른 필드 값을 참조합니다.
);
case "external":
return (
{/* 외부 테이블 선택 */}
테이블을 찾을 수 없습니다.
{tables.map((table) => (
{
updateExternalRef({ tableName: table.table_name });
setExternalTableOpen(false);
}}
className="text-xs"
>
{table.table_name}
))}
{/* 가져올 컬럼 선택 */}
{externalTableName && (
)}
{/* 조인 조건 */}
{externalTableName && (
{(localColumn.valueMapping?.externalRef?.joinConditions || []).map((condition, index) => (
{/* 소스 타입 */}
{/* 소스 필드 */}
{/* 타겟 컬럼 */}
))}
{(localColumn.valueMapping?.externalRef?.joinConditions || []).length === 0 && (
조인 조건을 추가하세요.
)}
)}
);
default:
return null;
}
};
return (
);
}