"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 { useCategories, useCreateCategory, useUpdateCategory } from "@/hooks/queries/useCategories"; import { useCheckCategoryDuplicate } from "@/hooks/queries/useValidation"; import { createCategorySchema, updateCategorySchema, type CreateCategoryData, type UpdateCategoryData, } from "@/lib/schemas/commonCode"; import type { CodeCategory } from "@/types/commonCode"; interface CodeCategoryFormModalProps { isOpen: boolean; onClose: () => void; editingCategoryCode?: string; } export function CodeCategoryFormModal({ isOpen, onClose, editingCategoryCode }: CodeCategoryFormModalProps) { const { data: categories = [] } = useCategories(); const createCategoryMutation = useCreateCategory(); const updateCategoryMutation = useUpdateCategory(); const isEditing = !!editingCategoryCode; const editingCategory = categories.find((c) => c.category_code === editingCategoryCode); // 검증 상태 관리 const [validationStates, setValidationStates] = useState({ categoryCode: { enabled: false, value: "" }, categoryName: { enabled: false, value: "" }, categoryNameEng: { enabled: false, value: "" }, description: { enabled: false, value: "" }, // 설명 필드 추가 }); // 중복 검사 훅들 const categoryCodeCheck = useCheckCategoryDuplicate( "categoryCode", validationStates.categoryCode.value, isEditing ? editingCategoryCode : undefined, validationStates.categoryCode.enabled, ); const categoryNameCheck = useCheckCategoryDuplicate( "categoryName", validationStates.categoryName.value, isEditing ? editingCategoryCode : undefined, validationStates.categoryName.enabled, ); const categoryNameEngCheck = useCheckCategoryDuplicate( "categoryNameEng", validationStates.categoryNameEng.value, isEditing ? editingCategoryCode : undefined, validationStates.categoryNameEng.enabled, ); // 중복 검사 결과 확인 (수정 시에는 카테고리 코드 검사 제외) const hasDuplicateErrors = (!isEditing && categoryCodeCheck.data?.isDuplicate && validationStates.categoryCode.enabled) || (categoryNameCheck.data?.isDuplicate && validationStates.categoryName.enabled) || (categoryNameEngCheck.data?.isDuplicate && validationStates.categoryNameEng.enabled); // 중복 검사 로딩 중인지 확인 (수정 시에는 카테고리 코드 검사 제외) const isDuplicateChecking = (!isEditing && categoryCodeCheck.isLoading) || categoryNameCheck.isLoading || categoryNameEngCheck.isLoading; // 필수 필드들이 모두 검증되었는지 확인 (생성 시에만 적용) const requiredFieldsValidated = isEditing || (validationStates.categoryCode.enabled && validationStates.categoryName.enabled && validationStates.categoryNameEng.enabled && validationStates.description.enabled); // 폼 스키마 선택 (생성/수정에 따라) const schema = isEditing ? updateCategorySchema : createCategorySchema; const form = useForm({ resolver: zodResolver(schema), mode: "onChange", // 실시간 검증 활성화 defaultValues: { categoryCode: "", categoryName: "", categoryNameEng: "", description: "", sortOrder: 1, ...(isEditing && { isActive: true }), }, }); // 편집 모드일 때 기존 데이터 로드 useEffect(() => { if (isOpen) { if (isEditing && editingCategory) { // 수정 모드: 기존 데이터 로드 form.reset({ categoryCode: editingCategory.category_code, // 카테고리 코드도 표시 categoryName: editingCategory.category_name, categoryNameEng: editingCategory.category_name_eng || "", description: editingCategory.description || "", sortOrder: editingCategory.sort_order, isActive: editingCategory.is_active, // 🔧 "Y"/"N" 문자열 그대로 사용 }); } else { // 새 카테고리 모드: 자동 순서 계산 const maxSortOrder = categories.length > 0 ? Math.max(...categories.map((c) => c.sort_order)) : 0; form.reset({ categoryCode: "", categoryName: "", categoryNameEng: "", description: "", sortOrder: maxSortOrder + 1, }); } } }, [isOpen, isEditing, editingCategory, categories, form]); const handleSubmit = form.handleSubmit(async (data) => { try { if (isEditing && editingCategoryCode) { // 수정 await updateCategoryMutation.mutateAsync({ categoryCode: editingCategoryCode, data: data as UpdateCategoryData, }); } else { // 생성 await createCategoryMutation.mutateAsync(data as CreateCategoryData); } onClose(); form.reset(); } catch (error) { console.error("카테고리 저장 실패:", error); } }); const isLoading = createCategoryMutation.isPending || updateCategoryMutation.isPending; return ( {isEditing ? "카테고리 수정" : "새 카테고리"}
{/* 카테고리 코드 */} {
{ const value = e.target.value.trim(); if (value) { setValidationStates((prev) => ({ ...prev, categoryCode: { enabled: true, value }, })); } }} /> {form.formState.errors.categoryCode && (

{form.formState.errors.categoryCode.message}

)} {!isEditing && !form.formState.errors.categoryCode && ( )}
} {/* 카테고리명 */}
{ const value = e.target.value.trim(); if (value) { setValidationStates((prev) => ({ ...prev, categoryName: { enabled: true, value }, })); } }} /> {form.formState.errors.categoryName && (

{form.formState.errors.categoryName.message}

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

{form.formState.errors.categoryNameEng.message}

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