"use client"; import React, { useState, useCallback, useEffect, useMemo } from "react"; import { ResizableDialog, ResizableDialogContent, ResizableDialogHeader, ResizableDialogTitle } from "@/components/ui/resizable-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"; import { useAuth } from "@/hooks/useAuth"; // 알림 모달 컴포넌트 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; editingUser?: any | null; } 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, editingUser }: UserFormModalProps) { // 현재 로그인한 사용자 정보 const { user: currentUser } = useAuth(); // 최고 관리자 여부 (company_code === '*' && userType === 'SUPER_ADMIN') const isSuperAdmin = currentUser?.companyCode === "*" && currentUser?.userType === "SUPER_ADMIN"; // 수정 모드 여부 const isEditMode = !!editingUser; 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: "", userType: "USER", // 기본값: 일반 사용자 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 = isEditMode ? [formData.userId.trim(), formData.userName.trim(), formData.companyCode, formData.deptCode] : [ formData.userId.trim(), formData.userPassword.trim(), formData.userName.trim(), formData.companyCode, formData.deptCode, ]; // 모든 필수 필드가 입력되었는지 확인 const allFieldsFilled = requiredFields.every((field) => field); // 수정 모드: ID 중복체크 불필요 (이미 존재하는 사용자) // 등록 모드: ID 중복체크 필수 const duplicateCheckValid = isEditMode || (isUserIdChecked && lastCheckedUserId === formData.userId); return allFieldsFilled && duplicateCheckValid; }, [formData, isUserIdChecked, lastCheckedUserId, isEditMode]); // 회사 목록 로드 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(); // 전체 부서 목록 로드 // 수정 모드: 기존 사용자 정보 로드 if (isEditMode && editingUser) { setFormData({ userId: editingUser.userId || "", userPassword: "", // 수정 시 비밀번호는 비워둠 (변경 원할 경우만 입력) userName: editingUser.userName || "", email: editingUser.email || "", tel: editingUser.tel || "", cellPhone: editingUser.cellPhone || "", positionName: editingUser.positionName || "", companyCode: editingUser.companyCode || "", deptCode: editingUser.deptCode || "", userType: editingUser.userType || "USER", sabun: editingUser.sabun || null, }); // 수정 모드에서는 ID 중복체크 불필요 setIsUserIdChecked(true); setLastCheckedUserId(editingUser.userId); } else { // 등록 모드: 폼 초기화 setFormData({ userId: "", userPassword: "", userName: "", email: "", tel: "", cellPhone: "", positionName: "", companyCode: "", deptCode: "", userType: "USER", sabun: null, }); setIsUserIdChecked(false); setLastCheckedUserId(""); setDuplicateCheckMessage(""); setDuplicateCheckType(""); } } }, [isOpen, isEditMode, editingUser, 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, userType: formData.userType, // 권한 타입 추가 sabun: null, // 항상 null (테이블 1번 컬럼) status: "active", // 기본값 (테이블 18번 컬럼) }; let response; if (isEditMode) { // 수정 모드: 비밀번호 필드 제외 (비밀번호 초기화 기능 별도 제공) const updateData = { ...userDataToSend }; delete updateData.userPassword; response = await userAPI.update(updateData); } else { // 등록 모드 response = await userAPI.create(userDataToSend); } if (response.success) { showAlert( isEditMode ? "수정 완료" : "등록 완료", isEditMode ? "사용자 정보가 성공적으로 수정되었습니다." : "사용자가 성공적으로 등록되었습니다.", "success", ); // 성공 시 모달을 바로 닫지 않고 사용자가 확인 후 닫도록 수정 setTimeout(() => { onClose(); onSuccess?.(); }, 1500); // 1.5초 후 자동으로 모달 닫기 } else { showAlert( isEditMode ? "수정 실패" : "등록 실패", response.message || (isEditMode ? "사용자 정보 수정에 실패했습니다." : "사용자 등록에 실패했습니다."), "error", ); } } catch (error) { console.error(isEditMode ? "사용자 수정 오류:" : "사용자 등록 오류:", error); showAlert( "오류 발생", isEditMode ? "사용자 정보 수정 중 오류가 발생했습니다." : "사용자 등록 중 오류가 발생했습니다.", "error", ); } finally { setIsLoading(false); } }, [formData, validateForm, onSuccess, onClose, showAlert, isEditMode]); // 모달 닫기 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 ( <> {isEditMode ? "사용자 정보 수정" : "사용자 등록"}
{/* 기본 정보 */}
{isEditMode ? ( ) : ( <>
handleInputChange("userId", e.target.value)} onKeyDown={handleKeyDown} className="flex-1" />
{/* 중복확인 결과 메시지 */} {duplicateCheckMessage && (
{duplicateCheckMessage}
)} )}
handleInputChange("userName", e.target.value)} onKeyDown={handleKeyDown} />
{/* 비밀번호 - 등록 모드에만 표시 */} {!isEditMode && (
handleInputChange("userPassword", e.target.value)} onKeyDown={handleKeyDown} className="pr-10" />

비밀번호 변경은 별도의 비밀번호 초기화 기능을 이용하세요.

)} {/* 회사 선택 */}
{isSuperAdmin ? ( <>

권한 관리는 별도의 권한 관리 페이지에서 설정할 수 있습니다.

) : ( <> c.company_code === formData.companyCode)?.company_name || formData.companyCode } disabled className="bg-muted cursor-not-allowed" />

회사는 최고 관리자만 변경할 수 있습니다.

)}
{/* 부서 정보 */}
{/* 연락처 정보 */}
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} />
{/* 버튼 영역 */}
{/* 알림 모달 */} ); }