"use client";
import React, { useState, useEffect } 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 { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { Plus, Trash2, Database, Layers, Info, Check, ChevronsUpDown } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { cn } from "@/lib/utils";
import { SaveConfig, SubTableSaveConfig, SubTableFieldMapping, FormSectionConfig, FormFieldConfig, SectionSaveMode } from "../types";
// 도움말 텍스트 컴포넌트
const HelpText = ({ children }: { children: React.ReactNode }) => (
{children}
);
interface SaveSettingsModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
saveConfig: SaveConfig;
sections: FormSectionConfig[];
onSave: (updates: SaveConfig) => void;
tables: { name: string; label: string }[];
tableColumns: { [tableName: string]: { name: string; type: string; label: string }[] };
onLoadTableColumns: (tableName: string) => void;
}
export function SaveSettingsModal({
open,
onOpenChange,
saveConfig,
sections,
onSave,
tables,
tableColumns,
onLoadTableColumns,
}: SaveSettingsModalProps) {
// 로컬 상태로 저장 설정 관리
const [localSaveConfig, setLocalSaveConfig] = useState(saveConfig);
// 저장 모드 (단일 테이블 vs 다중 테이블)
const [saveMode, setSaveMode] = useState<"single" | "multi">(
saveConfig.customApiSave?.enabled && saveConfig.customApiSave?.multiTable?.enabled ? "multi" : "single"
);
// 테이블 검색 Popover 상태
const [singleTableSearchOpen, setSingleTableSearchOpen] = useState(false);
const [mainTableSearchOpen, setMainTableSearchOpen] = useState(false);
const [subTableSearchOpen, setSubTableSearchOpen] = useState>({});
// 컬럼 검색 Popover 상태
const [mainKeyColumnSearchOpen, setMainKeyColumnSearchOpen] = useState(false);
const [mainFieldSearchOpen, setMainFieldSearchOpen] = useState>({});
const [subColumnSearchOpen, setSubColumnSearchOpen] = useState>({});
const [subTableColumnSearchOpen, setSubTableColumnSearchOpen] = useState>({});
const [markerColumnSearchOpen, setMarkerColumnSearchOpen] = useState>({});
// open이 변경될 때마다 데이터 동기화
useEffect(() => {
if (open) {
setLocalSaveConfig(saveConfig);
setSaveMode(saveConfig.customApiSave?.enabled && saveConfig.customApiSave?.multiTable?.enabled ? "multi" : "single");
// 모달이 열릴 때 기존에 설정된 테이블들의 컬럼 정보 로드
const mainTableName = saveConfig.customApiSave?.multiTable?.mainTable?.tableName;
if (mainTableName && !tableColumns[mainTableName]) {
onLoadTableColumns(mainTableName);
}
// 서브 테이블들의 컬럼 정보도 로드
const subTables = saveConfig.customApiSave?.multiTable?.subTables || [];
subTables.forEach((subTable) => {
if (subTable.tableName && !tableColumns[subTable.tableName]) {
onLoadTableColumns(subTable.tableName);
}
});
}
}, [open, saveConfig, tableColumns, onLoadTableColumns]);
// 저장 설정 업데이트 함수
const updateSaveConfig = (updates: Partial) => {
setLocalSaveConfig((prev) => ({ ...prev, ...updates }));
};
// 저장 함수
const handleSave = () => {
// 저장 모드에 따라 설정 조정
let finalConfig = { ...localSaveConfig };
if (saveMode === "single") {
// 단일 테이블 모드: customApiSave 비활성화
finalConfig = {
...finalConfig,
customApiSave: {
enabled: false,
apiType: "custom",
},
};
} else {
// 다중 테이블 모드: customApiSave 활성화
finalConfig = {
...finalConfig,
customApiSave: {
...finalConfig.customApiSave,
enabled: true,
apiType: "multi-table",
multiTable: {
...finalConfig.customApiSave?.multiTable,
enabled: true,
},
},
};
}
onSave(finalConfig);
onOpenChange(false);
};
// 서브 테이블 추가
const addSubTable = () => {
const newSubTable: SubTableSaveConfig = {
enabled: true,
tableName: "",
repeatSectionId: "",
linkColumn: {
mainField: "",
subColumn: "",
},
fieldMappings: [],
};
const subTables = [...(localSaveConfig.customApiSave?.multiTable?.subTables || []), newSubTable];
updateSaveConfig({
customApiSave: {
...localSaveConfig.customApiSave,
apiType: "multi-table",
multiTable: {
...localSaveConfig.customApiSave?.multiTable,
enabled: true,
subTables,
},
},
});
};
// 서브 테이블 삭제
const removeSubTable = (index: number) => {
const subTables = [...(localSaveConfig.customApiSave?.multiTable?.subTables || [])];
subTables.splice(index, 1);
updateSaveConfig({
customApiSave: {
...localSaveConfig.customApiSave,
multiTable: {
...localSaveConfig.customApiSave?.multiTable,
subTables,
},
},
});
};
// 서브 테이블 업데이트
const updateSubTable = (index: number, updates: Partial) => {
const subTables = [...(localSaveConfig.customApiSave?.multiTable?.subTables || [])];
subTables[index] = { ...subTables[index], ...updates };
updateSaveConfig({
customApiSave: {
...localSaveConfig.customApiSave,
multiTable: {
...localSaveConfig.customApiSave?.multiTable,
subTables,
},
},
});
};
// 필드 매핑 추가
const addFieldMapping = (subTableIndex: number) => {
const newMapping: SubTableFieldMapping = {
formField: "",
targetColumn: "",
};
const subTables = [...(localSaveConfig.customApiSave?.multiTable?.subTables || [])];
const fieldMappings = [...(subTables[subTableIndex].fieldMappings || []), newMapping];
subTables[subTableIndex] = { ...subTables[subTableIndex], fieldMappings };
updateSaveConfig({
customApiSave: {
...localSaveConfig.customApiSave,
multiTable: {
...localSaveConfig.customApiSave?.multiTable,
subTables,
},
},
});
};
// 필드 매핑 삭제
const removeFieldMapping = (subTableIndex: number, mappingIndex: number) => {
const subTables = [...(localSaveConfig.customApiSave?.multiTable?.subTables || [])];
const fieldMappings = [...(subTables[subTableIndex].fieldMappings || [])];
fieldMappings.splice(mappingIndex, 1);
subTables[subTableIndex] = { ...subTables[subTableIndex], fieldMappings };
updateSaveConfig({
customApiSave: {
...localSaveConfig.customApiSave,
multiTable: {
...localSaveConfig.customApiSave?.multiTable,
subTables,
},
},
});
};
// 필드 매핑 업데이트
const updateFieldMapping = (subTableIndex: number, mappingIndex: number, updates: Partial) => {
const subTables = [...(localSaveConfig.customApiSave?.multiTable?.subTables || [])];
const fieldMappings = [...(subTables[subTableIndex].fieldMappings || [])];
fieldMappings[mappingIndex] = { ...fieldMappings[mappingIndex], ...updates };
subTables[subTableIndex] = { ...subTables[subTableIndex], fieldMappings };
updateSaveConfig({
customApiSave: {
...localSaveConfig.customApiSave,
multiTable: {
...localSaveConfig.customApiSave?.multiTable,
subTables,
},
},
});
};
// 메인 테이블 컬럼 목록
const mainTableColumns = localSaveConfig.customApiSave?.multiTable?.mainTable?.tableName
? tableColumns[localSaveConfig.customApiSave.multiTable.mainTable.tableName] || []
: [];
// 반복 섹션 목록
const repeatSections = sections.filter((s) => s.repeatable);
// 모든 필드 목록 (반복 섹션 포함)
const getAllFields = (): { columnName: string; label: string; sectionTitle: string; sectionId: string }[] => {
const fields: { columnName: string; label: string; sectionTitle: string; sectionId: string }[] = [];
sections.forEach((section) => {
// 필드 타입 섹션만 처리 (테이블 타입은 fields가 undefined)
if (section.fields && Array.isArray(section.fields)) {
section.fields.forEach((field) => {
fields.push({
columnName: field.columnName,
label: field.label,
sectionTitle: section.title,
sectionId: section.id,
});
});
}
});
return fields;
};
const allFields = getAllFields();
// 섹션별 저장 방식 조회 (없으면 기본값 반환)
const getSectionSaveMode = (sectionId: string, sectionType: "fields" | "table"): "common" | "individual" => {
const sectionMode = localSaveConfig.sectionSaveModes?.find((s) => s.sectionId === sectionId);
if (sectionMode) {
return sectionMode.saveMode;
}
// 기본값: fields 타입은 공통 저장, table 타입은 개별 저장
return sectionType === "fields" ? "common" : "individual";
};
// 필드별 저장 방식 조회 (오버라이드 확인)
const getFieldSaveMode = (sectionId: string, fieldName: string, sectionType: "fields" | "table"): "common" | "individual" => {
const sectionMode = localSaveConfig.sectionSaveModes?.find((s) => s.sectionId === sectionId);
if (sectionMode) {
// 필드별 오버라이드 확인
const fieldOverride = sectionMode.fieldOverrides?.find((f) => f.fieldName === fieldName);
if (fieldOverride) {
return fieldOverride.saveMode;
}
return sectionMode.saveMode;
}
// 기본값
return sectionType === "fields" ? "common" : "individual";
};
// 섹션별 저장 방식 업데이트
const updateSectionSaveMode = (sectionId: string, mode: "common" | "individual") => {
const currentModes = localSaveConfig.sectionSaveModes || [];
const existingIndex = currentModes.findIndex((s) => s.sectionId === sectionId);
let newModes: SectionSaveMode[];
if (existingIndex >= 0) {
newModes = [...currentModes];
newModes[existingIndex] = { ...newModes[existingIndex], saveMode: mode };
} else {
newModes = [...currentModes, { sectionId, saveMode: mode }];
}
updateSaveConfig({ sectionSaveModes: newModes });
};
// 필드별 오버라이드 토글
const toggleFieldOverride = (sectionId: string, fieldName: string, sectionType: "fields" | "table") => {
const currentModes = localSaveConfig.sectionSaveModes || [];
const sectionIndex = currentModes.findIndex((s) => s.sectionId === sectionId);
// 섹션 설정이 없으면 먼저 생성
let newModes = [...currentModes];
if (sectionIndex < 0) {
const defaultMode = sectionType === "fields" ? "common" : "individual";
newModes.push({ sectionId, saveMode: defaultMode, fieldOverrides: [] });
}
const targetIndex = newModes.findIndex((s) => s.sectionId === sectionId);
const sectionMode = newModes[targetIndex];
const currentFieldOverrides = sectionMode.fieldOverrides || [];
const fieldOverrideIndex = currentFieldOverrides.findIndex((f) => f.fieldName === fieldName);
let newFieldOverrides;
if (fieldOverrideIndex >= 0) {
// 이미 오버라이드가 있으면 제거 (섹션 기본값으로 돌아감)
newFieldOverrides = currentFieldOverrides.filter((f) => f.fieldName !== fieldName);
} else {
// 오버라이드 추가 (섹션 기본값의 반대)
const oppositeMode = sectionMode.saveMode === "common" ? "individual" : "common";
newFieldOverrides = [...currentFieldOverrides, { fieldName, saveMode: oppositeMode }];
}
newModes[targetIndex] = { ...sectionMode, fieldOverrides: newFieldOverrides };
updateSaveConfig({ sectionSaveModes: newModes });
};
// 섹션의 필드 목록 가져오기
const getSectionFields = (section: FormSectionConfig): { fieldName: string; label: string }[] => {
if (section.type === "table" && section.tableConfig) {
// 테이블 타입: tableConfig.columns에서 필드 목록 가져오기
return (section.tableConfig.columns || []).map((col) => ({
fieldName: col.field,
label: col.label,
}));
} else if (section.fields) {
// 필드 타입: fields에서 목록 가져오기
return section.fields.map((field) => ({
fieldName: field.columnName,
label: field.label,
}));
}
return [];
};
return (
);
}