Files
goldenChart/frontend/src/components/LoginModal.tsx
T
2026-05-23 15:11:48 +09:00

104 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 로그인 모달 — 기본값 admin / admin
*/
import React, { useEffect, useState } from 'react';
import { loginUser, type LoginResponse } from '../utils/backendApi';
const DEFAULT_USERNAME = 'admin';
const DEFAULT_PASSWORD = 'admin';
interface Props {
open: boolean;
onClose: () => void;
onSuccess: (session: LoginResponse) => void;
}
const LoginModal: React.FC<Props> = ({ open, onClose, onSuccess }) => {
const [username, setUsername] = useState(DEFAULT_USERNAME);
const [password, setPassword] = useState(DEFAULT_PASSWORD);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!open) return;
setUsername(DEFAULT_USERNAME);
setPassword(DEFAULT_PASSWORD);
setError(null);
setLoading(false);
}, [open]);
if (!open) return null;
const submit = async (e: React.FormEvent) => {
e.preventDefault();
setError(null);
setLoading(true);
try {
const res = await loginUser(username.trim(), password);
if (!res) {
setError('로그인에 실패했습니다. 아이디와 비밀번호를 확인하세요.');
return;
}
onSuccess(res);
onClose();
} catch (err) {
setError(err instanceof Error ? err.message : '로그인에 실패했습니다.');
} finally {
setLoading(false);
}
};
return (
<div className="login-modal-backdrop" onClick={onClose}>
<div
className="login-modal"
role="dialog"
aria-labelledby="login-modal-title"
onClick={e => e.stopPropagation()}
>
<div className="login-modal-header">
<h2 id="login-modal-title">로그인</h2>
<button type="button" className="login-modal-close" onClick={onClose} aria-label="닫기">×</button>
</div>
<p className="login-modal-hint">
로그인 동일 계정의 설정이 모든 기기에서 공유됩니다. 로그인하지 않으면 브라우저(기기)별로 설정이 저장됩니다.
</p>
<form className="login-modal-form" onSubmit={submit}>
<label className="login-modal-field">
<span>아이디</span>
<input
type="text"
autoComplete="username"
value={username}
onChange={e => setUsername(e.target.value)}
disabled={loading}
/>
</label>
<label className="login-modal-field">
<span>비밀번호</span>
<input
type="text"
autoComplete="off"
className="login-modal-field--visible"
value={password}
onChange={e => setPassword(e.target.value)}
disabled={loading}
/>
</label>
{error && <p className="login-modal-error">{error}</p>}
<div className="login-modal-actions">
<button type="button" className="login-modal-btn login-modal-btn--ghost" onClick={onClose} disabled={loading}>
취소
</button>
<button type="submit" className="login-modal-btn login-modal-btn--primary" disabled={loading}>
{loading ? '로그인 중…' : '로그인'}
</button>
</div>
</form>
</div>
</div>
);
};
export default LoginModal;