"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 { 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 { Plus, Trash2, Settings as SettingsIcon } from "lucide-react"; import { cn } from "@/lib/utils"; import { FormFieldConfig, LinkedFieldMapping, FIELD_TYPE_OPTIONS, SELECT_OPTION_TYPE_OPTIONS, LINKED_FIELD_DISPLAY_FORMAT_OPTIONS, } from "../types"; import { apiClient } from "@/lib/api/client"; // 카테고리 컬럼 타입 (table_column_category_values 용) interface CategoryColumnOption { tableName: string; columnName: string; columnLabel: string; valueCount: number; // 조합키: tableName.columnName key: string; } // 도움말 텍스트 컴포넌트 const HelpText = ({ children }: { children: React.ReactNode }) => (

{children}

); interface FieldDetailSettingsModalProps { open: boolean; onOpenChange: (open: boolean) => void; field: FormFieldConfig; onSave: (updates: Partial) => void; tables: { name: string; label: string }[]; tableColumns: { [tableName: string]: { name: string; type: string; label: string }[] }; numberingRules: { id: string; name: string }[]; onLoadTableColumns: (tableName: string) => void; } export function FieldDetailSettingsModal({ open, onOpenChange, field, onSave, tables, tableColumns, numberingRules, onLoadTableColumns, }: FieldDetailSettingsModalProps) { // 로컬 상태로 필드 설정 관리 const [localField, setLocalField] = useState(field); // 전체 카테고리 컬럼 목록 상태 const [categoryColumns, setCategoryColumns] = useState([]); const [loadingCategoryColumns, setLoadingCategoryColumns] = useState(false); // open이 변경될 때마다 필드 데이터 동기화 useEffect(() => { if (open) { setLocalField(field); } }, [open, field]); // 모든 카테고리 컬럼 목록 로드 (모달 열릴 때) useEffect(() => { const loadAllCategoryColumns = async () => { if (!open) return; setLoadingCategoryColumns(true); try { // /api/table-categories/all-columns API 호출 const response = await apiClient.get("/table-categories/all-columns"); if (response.data?.success && response.data?.data) { // 중복 제거를 위해 Map 사용 const uniqueMap = new Map(); response.data.data.forEach((col: any) => { const tableName = col.tableName || col.table_name; const columnName = col.columnName || col.column_name; const key = `${tableName}.${columnName}`; // 이미 존재하는 경우 valueCount가 더 큰 것을 유지 if (!uniqueMap.has(key)) { uniqueMap.set(key, { tableName, columnName, columnLabel: col.columnLabel || col.column_label || columnName, valueCount: parseInt(col.valueCount || col.value_count || "0"), key, }); } }); setCategoryColumns(Array.from(uniqueMap.values())); } else { setCategoryColumns([]); } } catch (error) { setCategoryColumns([]); } finally { setLoadingCategoryColumns(false); } }; loadAllCategoryColumns(); }, [open]); // 필드 업데이트 함수 const updateField = (updates: Partial) => { setLocalField((prev) => ({ ...prev, ...updates })); }; // 저장 함수 const handleSave = () => { onSave(localField); onOpenChange(false); }; // 연결 필드 매핑 추가 const addLinkedFieldMapping = () => { const newMapping: LinkedFieldMapping = { sourceColumn: "", targetColumn: "", }; const mappings = [...(localField.linkedFieldGroup?.mappings || []), newMapping]; updateField({ linkedFieldGroup: { ...localField.linkedFieldGroup, enabled: true, mappings, }, }); }; // 연결 필드 매핑 삭제 const removeLinkedFieldMapping = (index: number) => { const mappings = [...(localField.linkedFieldGroup?.mappings || [])]; mappings.splice(index, 1); updateField({ linkedFieldGroup: { ...localField.linkedFieldGroup, mappings, }, }); }; // 연결 필드 매핑 업데이트 const updateLinkedFieldMapping = (index: number, updates: Partial) => { const mappings = [...(localField.linkedFieldGroup?.mappings || [])]; mappings[index] = { ...mappings[index], ...updates }; updateField({ linkedFieldGroup: { ...localField.linkedFieldGroup, mappings, }, }); }; // 소스 테이블 컬럼 목록 (연결 필드용) const sourceTableColumns = localField.linkedFieldGroup?.sourceTable ? tableColumns[localField.linkedFieldGroup.sourceTable] || [] : []; // Select 옵션의 참조 테이블 컬럼 목록 const selectTableColumns = localField.selectOptions?.tableName ? tableColumns[localField.selectOptions.tableName] || [] : []; return ( 필드 상세 설정: {localField.label} 필드의 타입, 동작 방식, 고급 옵션을 설정합니다.
{/* 기본 정보 섹션 */}

기본 정보

입력 필드의 유형을 선택하세요 (텍스트, 숫자, 날짜 등)
폼에서 차지할 너비를 설정합니다 (12칸 그리드 기준)
updateField({ placeholder: e.target.value })} placeholder="입력 힌트" className="h-7 text-xs mt-1" /> 입력 필드에 표시될 힌트 텍스트입니다
{/* 옵션 토글 */}

필드 옵션

필수 입력 updateField({ required: checked })} />
이 필드를 필수 입력으로 만듭니다
비활성화 (읽기전용) updateField({ disabled: checked })} />
필드를 비활성화하여 수정할 수 없게 만듭니다
숨김 (자동 저장만) updateField({ hidden: checked })} />
화면에 표시하지 않지만 값은 저장됩니다
부모에서 값 받기 updateField({ receiveFromParent: checked })} />
부모 화면에서 전달받은 값으로 자동 채워집니다
{/* Accordion으로 고급 설정 */} {/* Select 옵션 설정 */} {localField.fieldType === "select" && (
Select 옵션 설정 {localField.selectOptions?.type && ( ({localField.selectOptions.type === "code" ? "공통코드" : "직접 입력"}) )}
드롭다운에 표시될 옵션 목록을 어디서 가져올지 설정합니다.
{localField.selectOptions?.type === "table" && (
테이블 참조: DB 테이블에서 옵션 목록을 가져옵니다.
드롭다운 목록을 가져올 테이블을 선택하세요
{selectTableColumns.length > 0 ? ( ) : ( updateField({ selectOptions: { ...localField.selectOptions, valueColumn: e.target.value, }, }) } placeholder="customer_code" className="h-7 text-xs mt-1" /> )} 참조 테이블에서 조인할 컬럼 (기본키)
예: customer_code, customer_id
{selectTableColumns.length > 0 ? ( ) : ( updateField({ selectOptions: { ...localField.selectOptions, labelColumn: e.target.value, }, }) } placeholder="customer_name" className="h-7 text-xs mt-1" /> )} 드롭다운에 표시할 컬럼 (이름)
예: customer_name, dept_name
{selectTableColumns.length > 0 ? ( ) : ( updateField({ selectOptions: { ...localField.selectOptions, saveColumn: e.target.value, }, }) } placeholder="비워두면 조인 컬럼 저장" className="h-7 text-xs mt-1" /> )} 실제로 DB에 저장할 컬럼을 선택하세요
예: customer_name 저장 (비워두면 customer_code 저장)
)} {localField.selectOptions?.type === "code" && (
공통코드: 코드설정에서 등록한 카테고리 값을 가져옵니다.
코드설정에서 등록한 카테고리를 선택하세요
)}
)} {/* 연결 필드 설정 */}
연결 필드 설정 (다중 컬럼 저장) {localField.linkedFieldGroup?.enabled && ( ({(localField.linkedFieldGroup?.mappings || []).length}개) )}
연결 필드 사용 updateField({ linkedFieldGroup: { ...localField.linkedFieldGroup, enabled: checked, }, }) } />
드롭다운 선택 시 다른 테이블의 값도 함께 저장합니다.
예: 고객 선택 → 고객코드, 고객명, 연락처를 각각 저장
{localField.linkedFieldGroup?.enabled && (
값을 가져올 소스 테이블 (예: customer_mng)
{sourceTableColumns.length > 0 ? ( ) : ( updateField({ linkedFieldGroup: { ...localField.linkedFieldGroup, displayColumn: e.target.value, }, }) } placeholder="customer_name" className="h-7 text-xs mt-1" /> )} 드롭다운에 표시할 컬럼 (예: customer_name)
드롭다운에 표시될 형식을 선택하세요
소스 테이블의 컬럼을 현재 폼의 어느 컬럼에 저장할지 매핑합니다.
예: customer_code → partner_id, customer_name → partner_name
{(localField.linkedFieldGroup?.mappings || []).length === 0 ? (

매핑이 없습니다

위의 "매핑 추가" 버튼을 클릭하세요

) : (
{(localField.linkedFieldGroup?.mappings || []).map((mapping, index) => (
매핑 {index + 1}
{sourceTableColumns.length > 0 ? ( ) : ( updateLinkedFieldMapping(index, { sourceColumn: e.target.value }) } placeholder="customer_code" className="h-6 text-[9px] mt-0.5" /> )}
updateLinkedFieldMapping(index, { targetColumn: e.target.value }) } placeholder="partner_id" className="h-6 text-[9px] mt-0.5" />
))}
)}
)}
{/* 채번규칙 설정 */}
채번규칙 설정 {localField.numberingRule?.enabled && ( (활성화됨) )}
채번규칙 사용 updateField({ numberingRule: { ...localField.numberingRule, enabled: checked, }, }) } />
자동으로 코드/번호를 생성합니다.
예: EMP-001, ORD-20240101-001
{localField.numberingRule?.enabled && (
사용할 채번규칙을 선택하세요
사용자 수정 가능 updateField({ numberingRule: { ...localField.numberingRule, editable: checked, }, }) } />
생성된 번호를 사용자가 수정할 수 있게 합니다
저장 시점에 생성 updateField({ numberingRule: { ...localField.numberingRule, generateOnSave: checked, generateOnOpen: !checked, }, }) } />
OFF: 모달 열릴 때 생성 / ON: 저장 버튼 클릭 시 생성
)}
); }