사용자 계정 관리 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:
Jay Sheen
2025-09-26 14:22:37 +09:00
parent 43e7eefefa
commit dd2fa9d6f3
25 changed files with 999 additions and 261 deletions

View File

@@ -1,15 +1,44 @@
import { useState } from 'react';
import { PATHS } from '@/shared/constants/paths';
import { useNavigate } from '@/shared/lib/hooks/use-navigate';
import { HeaderType } from '@/entities/common/model/types';
import {
import {
useSetHeaderTitle,
useSetHeaderType,
useSetFooterMode,
useSetOnBack
} from '@/widgets/sub-layout/use-sub-layout';
import { useUserChangePasswordMutation } from '@/entities/user/api/use-user-change-password-mutation';
import { snackBar } from '@/shared/lib/toast';
export const PasswordModifyLoginPasswordPage = () => {
const { navigate } = useNavigate();
const [mid, setMid] = useState<string>('nictest00m');
const [currentPassword, setCurrentPassword] = useState<string>('');
const [newPassword, setNewPassword] = useState<string>('');
const [confirmPassword, setConfirmPassword] = useState<string>('');
const [usrid] = useState<string>(''); // TODO: Get actual user ID from context/session
const changePasswordMutation = useUserChangePasswordMutation({
onSuccess: () => {
snackBar('비밀번호가 성공적으로 변경되었습니다.');
// Clear form
setCurrentPassword('');
setNewPassword('');
setConfirmPassword('');
// Navigate back
navigate(PATHS.account.password.manage);
},
onError: (error) => {
snackBar(error?.response?.data?.message || '비밀번호 변경에 실패했습니다.');
}
});
const midList = [
{ value: 'nictest00m', label: 'nictest00m' },
{ value: 'nictest01m', label: 'nictest01m' },
{ value: 'nictest02m', label: 'nictest02m' },
];
useSetHeaderTitle('로그인 비밀번호 변경');
useSetHeaderType(HeaderType.LeftArrow);
@@ -17,6 +46,29 @@ export const PasswordModifyLoginPasswordPage = () => {
useSetOnBack(() => {
navigate(PATHS.account.password.manage);
});
// 저장 버튼 활성화 조건 체크
const isFormValid = () => {
return (
currentPassword.length >= 8 &&
newPassword.length >= 8 &&
confirmPassword.length >= 8 &&
newPassword === confirmPassword
);
};
// 저장 버튼 클릭 핸들러
const handleSave = () => {
if (!isFormValid()) return;
// TODO: Validate current password before submitting
changePasswordMutation.mutate({
mid,
usrid: usrid || 'test', // TODO: Get actual user ID
password: newPassword
});
};
return (
<>
<main>
@@ -26,42 +78,54 @@ export const PasswordModifyLoginPasswordPage = () => {
<div className="user-add">
<div className="ua-row">
<div className="ua-label"> <span className="red">*</span></div>
<select className="wid-100">
<option>nictest01g</option>
<select className="wid-100" value={mid} onChange={(e) => setMid(e.target.value)}>
{midList.map((item) => (
<option key={item.value} value={item.value}>{item.label}</option>
))}
</select>
</div>
<div className="ua-row">
<div className="ua-label"> <span className="red">*</span></div>
<input
<input
className="wid-100"
type="password"
placeholder=""
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
/>
</div>
<div className="ua-row">
<div className="ua-label"> <span className="red">*</span></div>
<input
className="wid-100 error"
<input
className={`wid-100 ${confirmPassword && newPassword !== confirmPassword ? 'error' : ''}`}
type="password"
placeholder=""
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</div>
<div className="ua-help error"> </div>
<div className="ua-row">
<div className="ua-label"> <span className="red">*</span></div>
<input
className="wid-100 error"
<input
className={`wid-100 ${confirmPassword && newPassword !== confirmPassword ? 'error' : ''}`}
type="password"
placeholder=""
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
{confirmPassword && newPassword !== confirmPassword && (
<div className="ua-help error"> </div>
)}
</div>
<div className="apply-row">
<button
<button
className="btn-50 btn-blue flex-1"
type="button"
></button>
disabled={!isFormValid() || changePasswordMutation.isPending}
onClick={handleSave}
>{changePasswordMutation.isPending ? '처리중...' : '저장'}</button>
</div>
</div>
</div>