"use client"; import React, { useState, useCallback, useEffect, useMemo } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Eye, EyeOff } from "lucide-react"; import { userAPI } from "@/lib/api/user"; // 알림 모달 컴포넌트 interface AlertModalProps { isOpen: boolean; onClose: () => void; title: string; message: string; type?: "success" | "error" | "info"; } function AlertModal({ isOpen, onClose, title, message, type = "info" }: AlertModalProps) { const getTypeColor = () => { switch (type) { case "success": return "text-green-600"; case "error": return "text-destructive"; default: return "text-primary"; } }; return ( {title}

{message}

); } interface UserFormModalProps { isOpen: boolean; onClose: () => void; onSuccess?: () => void; } interface CompanyOption { company_code: string; company_name: string; [key: string]: any; // 기타 필드들 } interface DepartmentOption { deptCode?: string; deptName?: string; parentDeptCode?: string; masterSabun?: string; masterUserId?: string; location?: string; locationName?: string; regdate?: string; dataType?: string; status?: string; salesYn?: string; companyName?: string; children?: DepartmentOption[]; // 기존 호환성을 위한 필드들 CODE?: string; NAME?: string; DEPT_CODE?: string; DEPT_NAME?: string; [key: string]: any; // 기타 필드들 } export function UserFormModal({ isOpen, onClose, onSuccess }: UserFormModalProps) { const [isLoading, setIsLoading] = useState(false); const [showPassword, setShowPassword] = useState(false); const [companies, setCompanies] = useState([]); const [departments, setDepartments] = useState([]); // 알림 모달 상태 const [alertModal, setAlertModal] = useState({ isOpen: false, title: "", message: "", type: "info" as "success" | "error" | "info", }); // 알림 모달 표시 함수 (useCallback 유지 - 다른 useCallback의 의존성으로 사용됨) const showAlert = useCallback((title: string, message: string, type: "success" | "error" | "info" = "info") => { setAlertModal({ isOpen: true, title, message, type, }); }, []); // 알림 모달 닫기 함수 (useCallback 제거 - 의존성이 없고 성능상 이점 없음) const closeAlert = () => { setAlertModal((prev) => ({ ...prev, isOpen: false })); }; const [formData, setFormData] = useState({ userId: "", userPassword: "", userName: "", email: "", tel: "", cellPhone: "", positionName: "", companyCode: "", deptCode: "", sabun: null, // 항상 null로 설정 }); // ID 중복체크 상태 관리 const [isUserIdChecked, setIsUserIdChecked] = useState(false); const [lastCheckedUserId, setLastCheckedUserId] = useState(""); const [duplicateCheckMessage, setDuplicateCheckMessage] = useState(""); const [duplicateCheckType, setDuplicateCheckType] = useState<"success" | "error" | "">(""); // 필수 필드 검증 (실시간) const isFormValid = useMemo(() => { const requiredFields = [ formData.userId.trim(), formData.userPassword.trim(), formData.userName.trim(), formData.companyCode, formData.deptCode, ]; // 모든 필수 필드가 입력되고 ID 중복체크가 완료되었는지 확인 const allFieldsFilled = requiredFields.every((field) => field); const duplicateCheckValid = isUserIdChecked && lastCheckedUserId === formData.userId; return allFieldsFilled && duplicateCheckValid; }, [formData, isUserIdChecked, lastCheckedUserId]); // 회사 목록 로드 const loadCompanies = useCallback(async () => { try { const companyList = await userAPI.getCompanyList(); setCompanies(companyList); } catch (error) { console.error("회사 목록 조회 오류:", error); showAlert("오류 발생", "회사 목록을 불러오는데 실패했습니다.", "error"); } }, [showAlert]); // 부서 목록 로드 const loadDepartments = useCallback( async (companyCode?: string) => { try { const departmentList = await userAPI.getDepartmentList(companyCode); setDepartments(departmentList); } catch (error) { console.error("부서 목록 조회 오류:", error); showAlert("오류 발생", "부서 목록을 불러오는데 실패했습니다.", "error"); } }, [showAlert], ); // 모달이 열릴 때 회사 목록 및 부서 목록 로드 useEffect(() => { if (isOpen) { loadCompanies(); loadDepartments(); // 전체 부서 목록 로드 } }, [isOpen, loadCompanies, loadDepartments]); // 회사 선택 시 부서 목록 업데이트 useEffect(() => { if (formData.companyCode) { loadDepartments(formData.companyCode); // 회사 변경 시 부서 선택 초기화 setFormData((prev) => ({ ...prev, deptCode: "" })); } }, [formData.companyCode, loadDepartments]); // 폼 데이터 변경 핸들러 (useCallback 제거 - 의존성이 없고 성능상 이점 없음) const handleInputChange = (field: string, value: string) => { // userId가 변경되면 중복체크 상태 초기화 if (field === "userId") { setIsUserIdChecked(false); setLastCheckedUserId(""); setDuplicateCheckMessage(""); setDuplicateCheckType(""); } setFormData((prev) => ({ ...prev, [field]: value, })); }; // 사용자 ID 중복 체크 const checkUserIdDuplicate = async () => { if (!formData.userId.trim()) { setDuplicateCheckMessage("사용자 ID를 입력해주세요."); setDuplicateCheckType("error"); return; } try { const response = await userAPI.checkDuplicateId(formData.userId); if (response.success && response.data) { // 백엔드 API 응답 구조: { isDuplicate: boolean, message: string } const isDuplicate = response.data.isDuplicate; const message = response.data.message; if (!isDuplicate) { // 중복되지 않음 (사용 가능) setIsUserIdChecked(true); setLastCheckedUserId(formData.userId); setDuplicateCheckMessage(message || "사용 가능한 사용자 ID입니다."); setDuplicateCheckType("success"); } else { // 중복됨 (사용 불가) setIsUserIdChecked(false); setLastCheckedUserId(""); setDuplicateCheckMessage(message || "이미 사용 중인 사용자 ID입니다."); setDuplicateCheckType("error"); } } } catch (error) { console.error("ID 중복 체크 오류:", error); setIsUserIdChecked(false); setLastCheckedUserId(""); setDuplicateCheckMessage("ID 중복 체크 중 오류가 발생했습니다."); setDuplicateCheckType("error"); } }; // 유효성 검사 const validateForm = useCallback(() => { if (!formData.userId.trim()) { showAlert("입력 오류", "사용자 ID를 입력해주세요.", "error"); return false; } // ID 중복체크 필수 검증 if (!isUserIdChecked || lastCheckedUserId !== formData.userId) { showAlert("중복체크 필요", "사용자 ID 중복체크를 먼저 진행해주세요.", "error"); return false; } if (!formData.userPassword.trim()) { showAlert("입력 오류", "비밀번호를 입력해주세요.", "error"); return false; } if (!formData.userName.trim()) { showAlert("입력 오류", "사용자명을 입력해주세요.", "error"); return false; } if (!formData.companyCode) { showAlert("입력 오류", "회사를 선택해주세요.", "error"); return false; } if (!formData.deptCode) { showAlert("입력 오류", "부서를 선택해주세요.", "error"); return false; } // 이메일 형식 검사 (입력된 경우만) if (formData.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { showAlert("입력 오류", "올바른 이메일 형식을 입력해주세요.", "error"); return false; } return true; }, [formData, isUserIdChecked, lastCheckedUserId, showAlert]); // 사용자 등록 const handleSubmit = useCallback(async () => { if (!validateForm()) { return; } setIsLoading(true); try { const userDataToSend = { userId: formData.userId, userPassword: formData.userPassword, userName: formData.userName, email: formData.email || null, tel: formData.tel || null, cellPhone: formData.cellPhone || null, positionName: formData.positionName || null, companyCode: formData.companyCode, deptCode: formData.deptCode || null, sabun: null, // 항상 null (테이블 1번 컬럼) status: "active", // 기본값 (테이블 18번 컬럼) }; const response = await userAPI.create(userDataToSend); if (response.success) { showAlert("등록 완료", "사용자가 성공적으로 등록되었습니다.", "success"); // 성공 시 모달을 바로 닫지 않고 사용자가 확인 후 닫도록 수정 setTimeout(() => { onClose(); onSuccess?.(); }, 1500); // 1.5초 후 자동으로 모달 닫기 } else { showAlert("등록 실패", response.message || "사용자 등록에 실패했습니다.", "error"); } } catch (error) { console.error("사용자 등록 오류:", error); showAlert("오류 발생", "사용자 등록 중 오류가 발생했습니다.", "error"); } finally { setIsLoading(false); } }, [formData, validateForm, onSuccess, onClose, showAlert]); // 모달 닫기 const handleClose = useCallback(() => { setFormData({ userId: "", userPassword: "", userName: "", email: "", tel: "", cellPhone: "", positionName: "", companyCode: "", deptCode: "", sabun: null, }); setShowPassword(false); // ID 중복체크 상태 초기화 setIsUserIdChecked(false); setLastCheckedUserId(""); setDuplicateCheckMessage(""); setDuplicateCheckType(""); onClose(); }, [onClose]); // Enter 키 처리 const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === "Enter" && !isLoading) { handleSubmit(); } }, [handleSubmit, isLoading], ); return ( <> 사용자 등록
{/* 기본 정보 */}
handleInputChange("userId", e.target.value)} onKeyDown={handleKeyDown} className="flex-1" />
{/* 중복확인 결과 메시지 */} {duplicateCheckMessage && (
{duplicateCheckMessage}
)}
handleInputChange("userName", e.target.value)} onKeyDown={handleKeyDown} />
{/* 비밀번호 */}
handleInputChange("userPassword", e.target.value)} onKeyDown={handleKeyDown} className="pr-10" />
{/* 회사 정보 */}
{/* 연락처 정보 */}
handleInputChange("email", e.target.value)} onKeyDown={handleKeyDown} />
handleInputChange("tel", e.target.value)} onKeyDown={handleKeyDown} />
{/* 추가 정보 */}
handleInputChange("cellPhone", e.target.value)} onKeyDown={handleKeyDown} />
handleInputChange("positionName", e.target.value)} onKeyDown={handleKeyDown} />
{/* 버튼 영역 */}
{/* 알림 모달 */} ); }