import { UserFindAuthMethodParams, UserAuthMethodData, AuthMethodModifyItem } from '@/entities/user/model/types'; import { useUserFindAuthMethodMutation } from '@/entities/user/api/use-user-find-authmethod-mutation'; import { DEFAULT_PAGE_PARAM } from '@/entities/common/model/constant'; import { useEffect, useState } from 'react'; import { useUserModifyAuthMethodMutation } from '@/entities/user/api/use-user-modify-authmethod-mutation'; export const UserLoginAuthInfoWrap = ({ mid, usrid, idCl, status, }: UserFindAuthMethodParams) => { const { mutateAsync: userFindAuthMethod } = useUserFindAuthMethodMutation(); const { mutateAsync: userModifyAuthMethod } = useUserModifyAuthMethodMutation(); const [pageParam] = useState(DEFAULT_PAGE_PARAM); const [authMethodData, setAuthMethodData] = useState(); const [initialData, setInitialData] = useState(); const [newEmails, setNewEmails] = useState([]); const [newPhones, setNewPhones] = useState([]); const [editableEmailIndex, setEditableEmailIndex] = useState(-1); const [editablePhoneIndex, setEditablePhoneIndex] = useState(-1); const [readOnlyEmails, setReadOnlyEmails] = useState>(new Set()); const [readOnlyPhones, setReadOnlyPhones] = useState>(new Set()); console.log("UserLoginAuthInfoWrap", mid, usrid, idCl, status); const handleRemoveExistingEmail = (index: number) => { if (authMethodData?.emails) { const updatedEmails = authMethodData.emails.filter((_, i) => i !== index); setAuthMethodData({ ...authMethodData, emails: updatedEmails }); } }; const handleRemoveExistingPhone = (index: number) => { if (authMethodData?.phones) { const updatedPhones = authMethodData.phones.filter((_, i) => i !== index); setAuthMethodData({ ...authMethodData, phones: updatedPhones }); } }; const callUserFindAuthMethod = (mid: string, usrid: string) => { let params: UserFindAuthMethodParams = { mid: mid, usrid: usrid, idCl: idCl, status: status, page: pageParam }; userFindAuthMethod(params).then((rs: any) => { console.log("User Find Auth Method: ", rs); // API 응답이 직접 데이터를 반환하는 경우 if (rs.emails || rs.phones) { console.log("Setting authMethodData directly from response: ", rs); setAuthMethodData(rs); setInitialData(rs); } // API 응답이 data 프로퍼티 안에 있는 경우 else if (rs.data) { console.log("Setting authMethodData from rs.data: ", rs.data); setAuthMethodData(rs.data); setInitialData(rs.data); } }); }; useEffect(() => { if (mid && usrid) { callUserFindAuthMethod(mid, usrid); } }, [mid, usrid]); const handleAddEmail = () => { // 현재 편집 중인 항목을 읽기전용으로 고정 if (editableEmailIndex >= 0) { setReadOnlyEmails(prev => new Set([...prev, editableEmailIndex])); } // 새로운 편집 가능한 항목 추가 setEditableEmailIndex(newEmails.length); setNewEmails([...newEmails, '']); }; const handleAddPhone = () => { // 현재 편집 중인 항목을 읽기전용으로 고정 if (editablePhoneIndex >= 0) { setReadOnlyPhones(prev => new Set([...prev, editablePhoneIndex])); } // 새로운 편집 가능한 항목 추가 setEditablePhoneIndex(newPhones.length); setNewPhones([...newPhones, '']); }; const handleRemoveNewEmail = (index: number) => { const updatedEmails = newEmails.filter((_, i) => i !== index); setNewEmails(updatedEmails); // 읽기전용 인덱스들을 업데이트 (삭제된 인덱스보다 큰 인덱스들은 1씩 감소) const updatedReadOnlyEmails = new Set(); readOnlyEmails.forEach(readOnlyIndex => { if (readOnlyIndex < index) { updatedReadOnlyEmails.add(readOnlyIndex); } else if (readOnlyIndex > index) { updatedReadOnlyEmails.add(readOnlyIndex - 1); } // readOnlyIndex === index인 경우 제거됨 (Set에 추가하지 않음) }); setReadOnlyEmails(updatedReadOnlyEmails); // 삭제 후 편집 가능한 인덱스 조정 if (index === editableEmailIndex) { // 현재 편집 중인 항목을 삭제한 경우 - 편집 가능한 항목이 없음 setEditableEmailIndex(-1); } else if (index < editableEmailIndex) { // 편집 가능한 항목보다 앞의 항목을 삭제한 경우 setEditableEmailIndex(editableEmailIndex - 1); } }; const handleRemoveNewPhone = (index: number) => { const updatedPhones = newPhones.filter((_, i) => i !== index); setNewPhones(updatedPhones); // 읽기전용 인덱스들을 업데이트 (삭제된 인덱스보다 큰 인덱스들은 1씩 감소) const updatedReadOnlyPhones = new Set(); readOnlyPhones.forEach(readOnlyIndex => { if (readOnlyIndex < index) { updatedReadOnlyPhones.add(readOnlyIndex); } else if (readOnlyIndex > index) { updatedReadOnlyPhones.add(readOnlyIndex - 1); } // readOnlyIndex === index인 경우 제거됨 (Set에 추가하지 않음) }); setReadOnlyPhones(updatedReadOnlyPhones); // 삭제 후 편집 가능한 인덱스 조정 if (index === editablePhoneIndex) { // 현재 편집 중인 항목을 삭제한 경우 - 편집 가능한 항목이 없음 setEditablePhoneIndex(-1); } else if (index < editablePhoneIndex) { // 편집 가능한 항목보다 앞의 항목을 삭제한 경우 setEditablePhoneIndex(editablePhoneIndex - 1); } }; const handleNewEmailChange = (index: number, value: string) => { const updated = [...newEmails]; updated[index] = value; setNewEmails(updated); }; const handleNewPhoneChange = (index: number, value: string) => { const updated = [...newPhones]; updated[index] = value; setNewPhones(updated); }; // 이메일 형식 검증 const isValidEmail = (email: string) => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; // 전화번호 형식 검증 (010으로 시작하고 총 11자리 숫자) const isValidPhone = (phone: string) => { const phoneRegex = /^010\d{8}$/; return phoneRegex.test(phone); }; // 이메일 추가 버튼 활성화 조건 const isEmailAddButtonEnabled = () => { if (newEmails.length === 0) return true; // 처음은 활성화 const lastEmailIndex = newEmails.length - 1; const lastEmail = newEmails[lastEmailIndex]; // 마지막 항목이 편집 가능하고, 유효한 형식이며, 중복이 없으면 활성화 return lastEmailIndex >= editableEmailIndex && lastEmail && lastEmail.trim() && isValidEmail(lastEmail) && !hasDuplicateEmail(); }; // 전화번호 추가 버튼 활성화 조건 const isPhoneAddButtonEnabled = () => { if (newPhones.length === 0) return true; // 처음은 활성화 const lastPhoneIndex = newPhones.length - 1; const lastPhone = newPhones[lastPhoneIndex]; // 마지막 항목이 편집 가능하고, 유효한 형식이며, 중복이 없으면 활성화 return lastPhoneIndex >= editablePhoneIndex && lastPhone && lastPhone.trim() && isValidPhone(lastPhone) && !hasDuplicatePhone(); }; // 삭제 버튼 활성화 조건 (전체 항목이 1개만 남으면 비활성화) const isDeleteButtonEnabled = () => { const totalEmailCount = (authMethodData?.emails?.length || 0) + newEmails.length; const totalPhoneCount = (authMethodData?.phones?.length || 0) + newPhones.length; const totalCount = totalEmailCount + totalPhoneCount; return totalCount > 1; }; // 중복 이메일 검증 const hasDuplicateEmail = () => { const allEmails = [ ...(authMethodData?.emails?.map(e => e.content) || []), ...newEmails.filter(e => e.trim()) ]; const uniqueEmails = new Set(allEmails); return allEmails.length !== uniqueEmails.size; }; // 중복 전화번호 검증 const hasDuplicatePhone = () => { const allPhones = [ ...(authMethodData?.phones?.map(p => p.content) || []), ...newPhones.filter(p => p.trim()) ]; const uniquePhones = new Set(allPhones); return allPhones.length !== uniquePhones.size; }; const isSaveButtonEnabled = () => { // 새로 추가된 이메일 중 값이 있는 것들의 형식 검증 const validNewEmails = newEmails.filter(e => e.trim()); const hasInvalidEmail = validNewEmails.some(email => !isValidEmail(email)); // 새로 추가된 전화번호 중 값이 있는 것들의 형식 검증 const validNewPhones = newPhones.filter(p => p.trim()); const hasInvalidPhone = validNewPhones.some(phone => !isValidPhone(phone)); // 형식이 맞지 않는 항목이 있거나 중복이 있으면 비활성화 if (hasInvalidEmail || hasInvalidPhone || hasDuplicateEmail() || hasDuplicatePhone()) { return false; } // 현재 이메일과 전화번호가 모두 없으면 비활성화 const currentEmailCount = (authMethodData?.emails?.length || 0) + validNewEmails.length; const currentPhoneCount = (authMethodData?.phones?.length || 0) + validNewPhones.length; if (currentEmailCount === 0 && currentPhoneCount === 0) { return false; } // 초기 데이터와 비교하여 변경사항이 있는지 확인 const initialEmailCount = initialData?.emails?.length || 0; const initialPhoneCount = initialData?.phones?.length || 0; const currentApiEmailCount = authMethodData?.emails?.length || 0; const currentApiPhoneCount = authMethodData?.phones?.length || 0; // 삭제가 발생했거나 새로운 항목이 추가된 경우 활성화 const hasChanges = ( currentApiEmailCount < initialEmailCount || currentApiPhoneCount < initialPhoneCount || validNewEmails.length > 0 || validNewPhones.length > 0 ); return hasChanges; }; const handleSave = async () => { try { const addMethods: AuthMethodModifyItem[] = []; const removeMethods: AuthMethodModifyItem[] = []; // 삭제된 이메일 항목 수집 if (initialData?.emails) { initialData.emails.forEach((email) => { const stillExists = authMethodData?.emails?.some( e => e.content === email.content && e.sequence === email.sequence ); if (!stillExists) { removeMethods.push({ usrid: usrid, systemAdminClassId: mid, idCl: "MID", authMethodType: "EMAIL", sequence: email.sequence, content: email.content }); } }); } // 삭제된 전화번호 항목 수집 if (initialData?.phones) { initialData.phones.forEach((phone) => { const stillExists = authMethodData?.phones?.some( p => p.content === phone.content && p.sequence === phone.sequence ); if (!stillExists) { removeMethods.push({ usrid: usrid, systemAdminClassId: mid, idCl: "MID", authMethodType: "PHONE", sequence: phone.sequence, content: phone.content }); } }); } // 새로 추가된 이메일 항목 수집 const validNewEmails = newEmails.filter(e => e.trim()); validNewEmails.forEach((email, index) => { const existingEmailCount = authMethodData?.emails?.length || 0; addMethods.push({ usrid: usrid, systemAdminClassId: mid, idCl: "MID", authMethodType: "EMAIL", sequence: existingEmailCount + index + 1, content: email }); }); // 새로 추가된 전화번호 항목 수집 const validNewPhones = newPhones.filter(p => p.trim()); validNewPhones.forEach((phone, index) => { const existingPhoneCount = authMethodData?.phones?.length || 0; addMethods.push({ usrid: usrid, systemAdminClassId: mid, idCl: "MID", authMethodType: "PHONE", sequence: existingPhoneCount + index + 1, content: phone }); }); const requestBody = { addMethods: addMethods.length > 0 ? addMethods : undefined, removeMethods: removeMethods.length > 0 ? removeMethods : undefined }; // 빈 배열 제거 const finalRequestBody = Object.fromEntries( Object.entries(requestBody).filter(([_, value]) => value !== undefined) ); console.log("Save request body:", finalRequestBody); // API 호출 await userModifyAuthMethod(finalRequestBody); // 성공 후 데이터 새로고침 callUserFindAuthMethod(mid, usrid); // 새로 추가한 항목들 초기화 setNewEmails([]); setNewPhones([]); setEditableEmailIndex(-1); setEditablePhoneIndex(-1); setReadOnlyEmails(new Set()); setReadOnlyPhones(new Set()); } catch (error) { console.error("Failed to save auth methods:", error); } }; console.log("Rendering with authMethodData: ", authMethodData); console.log("Emails: ", authMethodData?.emails); console.log("Phones: ", authMethodData?.phones); return ( <>
이메일 주소
{authMethodData?.emails && authMethodData.emails.length > 0 && authMethodData.emails.map((email, index) => (
))} {newEmails.map((email, index) => (
handleNewEmailChange(index, e.target.value)} readOnly={readOnlyEmails.has(index) || index !== editableEmailIndex} />
))}
휴대폰 번호
{authMethodData?.phones && authMethodData.phones.length > 0 && authMethodData.phones.map((phone, index) => (
))} {newPhones.map((phone, index) => (
handleNewPhoneChange(index, e.target.value)} readOnly={readOnlyPhones.has(index) || index !== editablePhoneIndex} />
))}
※ 탭을 변경하면 미저장 내용은 초기화됩니다.
); };