비밀번호 변경

This commit is contained in:
현성필
2025-01-31 16:33:40 +09:00
parent c2b99b31dc
commit c36a446f7c
5 changed files with 215 additions and 2 deletions
+5
View File
@@ -32,6 +32,7 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
showTCPServerEdit: false,
showQueueEdit: false,
showUserEdit: false,
showPasswordChange: false,
selectedItem: null,
});
@@ -77,6 +78,9 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
case 'variables-show':
setDialogState(prev => ({ ...prev, showVariableModal: true }));
break;
case 'change-password':
setDialogState(prev => ({ ...prev, showPasswordChange: true }));
break;
case 'logout':
const success = await handleLogout();
if (success) {
@@ -139,6 +143,7 @@ const AppContent = ({ options, showLogin, setShowLogin }) => {
showQueueEdit={dialogState.showQueueEdit}
showUserEdit={dialogState.showUserEdit}
selectedItem={dialogState.selectedItem}
showPasswordChange={dialogState.showPasswordChange}
onDialogChange={handleDialogChange}
/>
<div className="flex-1 mt-14">
@@ -0,0 +1,155 @@
import React, { useState } from 'react';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Label } from '@/components/ui/label';
import { getCSRFToken } from '@/services/token.js';
const PasswordChangeDialog = ({ open, onOpenChange }) => {
const [formData, setFormData] = useState({
oldPassword: '',
newPassword: '',
newPassword2: ''
});
const [message, setMessage] = useState({ type: '', content: '' });
const [isLoading, setIsLoading] = useState(false);
const resetForm = () => {
setFormData({
oldPassword: '',
newPassword: '',
newPassword2: ''
});
setMessage({ type: '', content: '' });
};
const handleClose = () => {
resetForm();
onOpenChange(false);
};
const validateForm = () => {
if (!formData.oldPassword) {
setMessage({ type: 'error', content: '현재 비밀번호를 입력해주세요.' });
return false;
}
if (!formData.newPassword) {
setMessage({ type: 'error', content: '새 비밀번호를 입력해주세요.' });
return false;
}
if (formData.newPassword !== formData.newPassword2) {
setMessage({ type: 'error', content: '새 비밀번호가 일치하지 않습니다.' });
return false;
}
return true;
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateForm()) return;
setIsLoading(true);
setMessage({ type: '', content: '' });
try {
const response = await fetch('/api/account/password', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-XSRF-TOKEN': getCSRFToken()
},
body: JSON.stringify(formData)
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || '비밀번호 변경에 실패했습니다.');
}
setMessage({ type: 'success', content: '비밀번호가 성공적으로 변경되었습니다.' });
setTimeout(() => {
handleClose();
}, 2000);
} catch (error) {
setMessage({ type: 'error', content: error.message });
} finally {
setIsLoading(false);
}
};
return (
<Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>비밀번호 변경</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit}>
<div className="grid gap-4 py-4">
{message.content && (
<Alert variant={message.type === 'error' ? 'destructive' : 'default'}>
<AlertDescription>{message.content}</AlertDescription>
</Alert>
)}
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="oldPassword" className="text-right">
현재 비밀번호
</Label>
<Input
id="oldPassword"
type="password"
value={formData.oldPassword}
onChange={(e) => setFormData(prev => ({ ...prev, oldPassword: e.target.value }))}
className="col-span-3"
required
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="newPassword" className="text-right">
비밀번호
</Label>
<Input
id="newPassword"
type="password"
value={formData.newPassword}
onChange={(e) => setFormData(prev => ({ ...prev, newPassword: e.target.value }))}
className="col-span-3"
required
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="newPassword2" className="text-right">
비밀번호 확인
</Label>
<Input
id="newPassword2"
type="password"
value={formData.newPassword2}
onChange={(e) => setFormData(prev => ({ ...prev, newPassword2: e.target.value }))}
className="col-span-3"
required
/>
</div>
</div>
<DialogFooter>
<Button type="button" variant="secondary" onClick={handleClose}>
취소
</Button>
<Button type="submit" disabled={isLoading}>
{isLoading ? '변경 중...' : '변경'}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
};
export default PasswordChangeDialog;
@@ -7,6 +7,7 @@ import MockRouteEditDialog from '@/components/mock-route/MockRouteEditDialog.jsx
import TCPServerEditDialog from '@/components/tcp-servers/TCPServerEditDialog.jsx';
import QueueEditDialog from '@/components/queues/QueueEditDialog.jsx';
import UserEditDialog from '@/components/users/UserEditDialog.jsx';
import PasswordChangeDialog from '@/components/account/PasswordChangeDialog.jsx';
const DialogManager = ({
showAddApi,
@@ -18,6 +19,7 @@ const DialogManager = ({
showQueueEdit,
showUserEdit,
selectedItem,
showPasswordChange,
onDialogChange
}) => {
@@ -58,6 +60,10 @@ const DialogManager = ({
onOpenChange={(isOpen) => onDialogChange('showUserEdit', isOpen)}
item={selectedItem}
/>
<PasswordChangeDialog
open={showPasswordChange}
onOpenChange={(isOpen) => onDialogChange('showPasswordChange', isOpen)}
/>
</>;
}