"use client"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { LoadingSpinner } from "@/components/common/LoadingSpinner"; import { ValidationMessage } from "@/components/common/ValidationMessage"; import { useCreateCode, useUpdateCode } from "@/hooks/queries/useCodes"; import { useCheckCodeDuplicate } from "@/hooks/queries/useValidation"; import { createCodeSchema, updateCodeSchema, type CreateCodeData, type UpdateCodeData } from "@/lib/schemas/commonCode"; import type { CodeInfo } from "@/types/commonCode"; import type { FieldError } from "react-hook-form"; interface CodeFormModalProps { isOpen: boolean; onClose: () => void; categoryCode: string; editingCode?: CodeInfo | null; codes: CodeInfo[]; } // 에러 메시지를 안전하게 문자열로 변환하는 헬퍼 함수 const getErrorMessage = (error: FieldError | undefined): string => { if (!error) return ""; if (typeof error === "string") return error; return error.message || ""; }; export function CodeFormModal({ isOpen, onClose, categoryCode, editingCode, codes }: CodeFormModalProps) { const createCodeMutation = useCreateCode(); const updateCodeMutation = useUpdateCode(); const isEditing = !!editingCode; // 검증 상태 관리 const [validationStates, setValidationStates] = useState({ codeValue: { enabled: false, value: "" }, codeName: { enabled: false, value: "" }, codeNameEng: { enabled: false, value: "" }, }); // 중복 검사 훅들 const codeValueCheck = useCheckCodeDuplicate( categoryCode, "codeValue", validationStates.codeValue.value, isEditing ? editingCode?.codeValue || editingCode?.code_value : undefined, validationStates.codeValue.enabled, ); const codeNameCheck = useCheckCodeDuplicate( categoryCode, "codeName", validationStates.codeName.value, isEditing ? editingCode?.codeValue || editingCode?.code_value : undefined, validationStates.codeName.enabled, ); const codeNameEngCheck = useCheckCodeDuplicate( categoryCode, "codeNameEng", validationStates.codeNameEng.value, isEditing ? editingCode?.codeValue || editingCode?.code_value : undefined, validationStates.codeNameEng.enabled, ); // 중복 검사 결과 확인 const hasDuplicateErrors = (codeValueCheck.data?.isDuplicate && validationStates.codeValue.enabled) || (codeNameCheck.data?.isDuplicate && validationStates.codeName.enabled) || (codeNameEngCheck.data?.isDuplicate && validationStates.codeNameEng.enabled); // 중복 검사 로딩 중인지 확인 const isDuplicateChecking = codeValueCheck.isLoading || codeNameCheck.isLoading || codeNameEngCheck.isLoading; // 폼 스키마 선택 (생성/수정에 따라) const schema = isEditing ? updateCodeSchema : createCodeSchema; const form = useForm({ resolver: zodResolver(schema), mode: "onChange", // 실시간 검증 활성화 defaultValues: { codeValue: "", codeName: "", codeNameEng: "", description: "", sortOrder: 1, ...(isEditing && { isActive: "Y" as const }), }, }); // 편집 모드일 때 기존 데이터 로드 useEffect(() => { if (isOpen) { if (isEditing && editingCode) { // 수정 모드: 기존 데이터 로드 (codeValue는 표시용으로만 설정) form.reset({ codeName: editingCode.codeName || editingCode.code_name, codeNameEng: editingCode.codeNameEng || editingCode.code_name_eng || "", description: editingCode.description || "", sortOrder: editingCode.sortOrder || editingCode.sort_order, isActive: (editingCode.isActive || editingCode.is_active) as "Y" | "N", // 타입 캐스팅 }); // codeValue는 별도로 설정 (표시용) form.setValue("codeValue" as any, editingCode.codeValue || editingCode.code_value); } else { // 새 코드 모드: 자동 순서 계산 const maxSortOrder = codes.length > 0 ? Math.max(...codes.map((c) => c.sortOrder || c.sort_order)) : 0; form.reset({ codeValue: "", codeName: "", codeNameEng: "", description: "", sortOrder: maxSortOrder + 1, }); } } }, [isOpen, isEditing, editingCode, codes]); const handleSubmit = form.handleSubmit(async (data) => { try { if (isEditing && editingCode) { // 수정 await updateCodeMutation.mutateAsync({ categoryCode, codeValue: editingCode.codeValue || editingCode.code_value, data: data as UpdateCodeData, }); } else { // 생성 await createCodeMutation.mutateAsync({ categoryCode, data: data as CreateCodeData, }); } onClose(); form.reset(); } catch (error) { console.error("코드 저장 실패:", error); } }); const isLoading = createCodeMutation.isPending || updateCodeMutation.isPending; return ( {isEditing ? "코드 수정" : "새 코드"}
{/* 코드값 */}
{ const value = e.target.value.trim(); if (value && !isEditing) { setValidationStates((prev) => ({ ...prev, codeValue: { enabled: true, value }, })); } }} /> {(form.formState.errors as any)?.codeValue && (

{getErrorMessage((form.formState.errors as any)?.codeValue)}

)} {!isEditing && !(form.formState.errors as any)?.codeValue && ( )}
{/* 코드명 */}
{ const value = e.target.value.trim(); if (value) { setValidationStates((prev) => ({ ...prev, codeName: { enabled: true, value }, })); } }} /> {form.formState.errors.codeName && (

{getErrorMessage(form.formState.errors.codeName)}

)} {!form.formState.errors.codeName && ( )}
{/* 영문명 */}
{ const value = e.target.value.trim(); if (value) { setValidationStates((prev) => ({ ...prev, codeNameEng: { enabled: true, value }, })); } }} /> {form.formState.errors.codeNameEng && (

{getErrorMessage(form.formState.errors.codeNameEng)}

)} {!form.formState.errors.codeNameEng && ( )}
{/* 설명 */}