goldenChat base source add
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 로그인 모달 — 기본값 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;
|
||||
Reference in New Issue
Block a user