사용자 계정 관리 API 연동 및 기능 개선
- 사용자 비밀번호 변경 API 추가 - 메뉴 권한 관리 API 추가 (조회/저장) - 인증 방법 수정 API 추가 - 사용자 권한 업데이트 API 추가 - 계정 관리 UI 컴포넌트 개선 - Docker 및 Makefile 설정 업데이트 🤖 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,12 @@ import { useState, useEffect, useRef } from 'react';
|
||||
import { VerificationItem } from '@/entities/account/model/types';
|
||||
import { useUserCreateMutation } from '@/entities/user/api/use-user-create-mutation';
|
||||
import { useUserExistsUseridQuery } from '@/entities/user/api/use-user-exists-userid-query';
|
||||
import { useLocation } from 'react-router';
|
||||
|
||||
export const UserAddAccountPage = () => {
|
||||
const { navigate } = useNavigate();
|
||||
const location = useLocation();
|
||||
const { mid } = location.state || {};
|
||||
const { mutateAsync: userCreate, isPending } = useUserCreateMutation();
|
||||
|
||||
// 폼 상태 관리
|
||||
@@ -143,6 +147,16 @@ export const UserAddAccountPage = () => {
|
||||
setNewPhones(updated);
|
||||
};
|
||||
|
||||
// 비밀번호 검증 함수
|
||||
const validatePassword = (password: string) => {
|
||||
if (!password.trim()) {
|
||||
return '비밀번호를 입력해 주세요';
|
||||
} else if (password.length < 8) {
|
||||
return '8자리 이상 입력해 주세요';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
// 폼 입력 핸들러
|
||||
const handleInputChange = (field: string, value: string) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
@@ -174,6 +188,14 @@ export const UserAddAccountPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 비밀번호 blur 핸들러
|
||||
const handlePasswordBlur = () => {
|
||||
const passwordError = validatePassword(formData.password);
|
||||
if (passwordError) {
|
||||
setErrors(prev => ({ ...prev, password: passwordError }));
|
||||
}
|
||||
};
|
||||
|
||||
// 컴포넌트 언마운트 시 타이머 정리
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
@@ -241,6 +263,44 @@ export const UserAddAccountPage = () => {
|
||||
return totalCount > 1;
|
||||
};
|
||||
|
||||
// 저장 버튼 활성화 조건 체크
|
||||
const isSaveButtonEnabled = () => {
|
||||
// 1. 사용자 ID가 입력되고 중복되지 않아야 함
|
||||
if (!formData.usrid.trim()) return false;
|
||||
if (errors.usrid) return false;
|
||||
if (isCheckingUsrid || isUserExistsLoading) return false;
|
||||
|
||||
// 2. 비밀번호가 8자리 이상
|
||||
if (!formData.password.trim() || formData.password.length < 8) return false;
|
||||
|
||||
// 3. 입력된 모든 이메일과 휴대폰 번호가 유효한 형식이어야 함
|
||||
const nonEmptyEmails = newEmails.filter(email => email.trim());
|
||||
const nonEmptyPhones = newPhones.filter(phone => phone.trim());
|
||||
|
||||
// 입력된 이메일이 있으면 모두 유효한 형식이어야 함
|
||||
if (nonEmptyEmails.length > 0) {
|
||||
const invalidEmails = nonEmptyEmails.filter(email => !isValidEmail(email));
|
||||
if (invalidEmails.length > 0) return false;
|
||||
}
|
||||
|
||||
// 입력된 휴대폰 번호가 있으면 모두 유효한 형식이어야 함
|
||||
if (nonEmptyPhones.length > 0) {
|
||||
const invalidPhones = nonEmptyPhones.filter(phone => !isValidPhone(phone));
|
||||
if (invalidPhones.length > 0) return false;
|
||||
}
|
||||
|
||||
// 4. 유효한 이메일 또는 휴대폰 번호가 최소 1개 이상
|
||||
const validEmails = nonEmptyEmails.filter(email => isValidEmail(email));
|
||||
const validPhones = nonEmptyPhones.filter(phone => isValidPhone(phone));
|
||||
|
||||
if (validEmails.length === 0 && validPhones.length === 0) return false;
|
||||
|
||||
// 5. 중복이 없어야 함
|
||||
if (hasDuplicateEmail() || hasDuplicatePhone()) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// 폼 검증
|
||||
const validateForm = () => {
|
||||
const newErrors = { usrid: '', password: '' };
|
||||
@@ -302,10 +362,11 @@ export const UserAddAccountPage = () => {
|
||||
});
|
||||
|
||||
const request = {
|
||||
userId: formData.usrid,
|
||||
mid: mid,
|
||||
usrid: formData.usrid,
|
||||
password: formData.password,
|
||||
loginRange: formData.loginRange,
|
||||
verification: verifications
|
||||
verifications: verifications
|
||||
};
|
||||
|
||||
const response = await userCreate(request);
|
||||
@@ -365,7 +426,7 @@ export const UserAddAccountPage = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{errors.usrid && <div className="ua-help error">{errors.usrid}</div>}
|
||||
{errors.usrid && <div className="ua-help error pt-10">{errors.usrid}</div>}
|
||||
{!errors.usrid && formData.usrid && userExistsData && !userExistsData.exists && (
|
||||
<div className="ua-help" style={{ color: '#78D197' }}>사용 가능한 ID입니다.</div>
|
||||
)}
|
||||
@@ -378,9 +439,10 @@ export const UserAddAccountPage = () => {
|
||||
placeholder="8자리 이상 입력해 주세요"
|
||||
value={formData.password}
|
||||
onChange={(e) => handleInputChange('password', e.target.value)}
|
||||
onBlur={handlePasswordBlur}
|
||||
/>
|
||||
</div>
|
||||
{errors.password && <div className="ua-help error">{errors.password}</div>}
|
||||
{errors.password && <div className="ua-help error pt-10">{errors.password}</div>}
|
||||
|
||||
<div className="ua-row">
|
||||
<div className="ua-label">로그인 범위</div>
|
||||
@@ -390,7 +452,7 @@ export const UserAddAccountPage = () => {
|
||||
onChange={(e) => handleInputChange('loginRange', e.target.value)}
|
||||
>
|
||||
<option value="MID">MID</option>
|
||||
<option value="MID + GID">MID + GID</option>
|
||||
<option value="GID">MID + GID</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -471,7 +533,7 @@ export const UserAddAccountPage = () => {
|
||||
className="btn-50 btn-blue flex-1"
|
||||
type="button"
|
||||
onClick={handleSave}
|
||||
disabled={isPending}
|
||||
disabled={!isSaveButtonEnabled() || isPending}
|
||||
>
|
||||
{isPending ? '저장 중...' : '저장'}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user