template-monorepo/frontend/src/pages/user/LoginPage.tsx

76 lines
2.3 KiB
TypeScript
Raw Normal View History

2026-05-26 09:32:32 +00:00
import { useState, type FormEvent } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import * as authApi from '../../api/auth';
import { ApiError } from '../../api/http';
import { DEFAULT_TENANT } from '../../config';
import { useAuth } from '../../context/AuthContext';
import * as permApi from '../../api/permission';
export function LoginPage() {
const navigate = useNavigate();
const { syncSession, refreshRoles } = useAuth();
const [tenant, setTenant] = useState(
() => localStorage.getItem('tenant_slug') ?? DEFAULT_TENANT,
);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const submit = async (e: FormEvent) => {
e.preventDefault();
setError('');
setLoading(true);
try {
localStorage.setItem('tenant_slug', tenant);
await authApi.login(tenant, email, password);
syncSession();
await refreshRoles();
const me = await permApi.getMyPermissions();
const admin = permApi.isAdminRole(me.roles ?? []);
navigate(admin ? '/admin' : '/app');
} catch (err) {
setError(err instanceof ApiError ? err.message : '登入失敗');
} finally {
setLoading(false);
}
};
return (
<div className="auth-card">
<h1></h1>
<form onSubmit={submit} className="form">
<label>
<input value={tenant} onChange={(e) => setTenant(e.target.value)} />
</label>
<label>
Email
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</label>
<label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</label>
{error && <p className="form-error">{error}</p>}
<button type="submit" className="btn-primary" disabled={loading}>
{loading ? '登入中…' : '登入'}
</button>
</form>
<p className="auth-footer">
<Link to="/register"></Link>
</p>
</div>
);
}