139 lines
3.3 KiB
TypeScript
139 lines
3.3 KiB
TypeScript
import * as React from 'react';
|
||
import { Box, TextField, Button, Typography, IconButton, CircularProgress, Link } from '@mui/material';
|
||
import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';
|
||
|
||
export type AuthMode = "login" | "register";
|
||
|
||
export interface AuthPageProps {
|
||
mode: AuthMode;
|
||
onBack(): void;
|
||
onSwitchMode(): void;
|
||
login(username: string, password: string): Promise<void>;
|
||
register(username: string, password: string): Promise<void>;
|
||
loading: boolean;
|
||
error: string | null;
|
||
currentUser: any;
|
||
}
|
||
|
||
export function AuthPage({
|
||
mode,
|
||
onBack,
|
||
onSwitchMode,
|
||
login,
|
||
register,
|
||
loading,
|
||
error,
|
||
currentUser,
|
||
}: AuthPageProps) {
|
||
|
||
const [username, setUsername] = React.useState('');
|
||
const [password, setPassword] = React.useState('');
|
||
|
||
const isLogin = mode === "login";
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (isLogin) {
|
||
await login(username, password);
|
||
} else {
|
||
await register(username, password);
|
||
}
|
||
};
|
||
|
||
// ✅ Auto-return if already logged in
|
||
React.useEffect(() => {
|
||
if (currentUser) onBack();
|
||
}, [currentUser, onBack]);
|
||
|
||
return (
|
||
<Box
|
||
sx={{
|
||
maxWidth: 400,
|
||
mx: 'auto',
|
||
mt: 8,
|
||
p: 4,
|
||
borderRadius: 3,
|
||
boxShadow: 3,
|
||
bgcolor: 'background.paper',
|
||
}}
|
||
>
|
||
<IconButton onClick={onBack} sx={{ mb: 2 }}>
|
||
<ArrowBackRoundedIcon />
|
||
</IconButton>
|
||
|
||
<Typography variant="h4" fontWeight="bold" gutterBottom>
|
||
{isLogin ? "Sign In" : "Create Account"}
|
||
</Typography>
|
||
|
||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||
{isLogin
|
||
? "Please log in to continue"
|
||
: "Create an account to get started"}
|
||
</Typography>
|
||
|
||
<form onSubmit={handleSubmit}>
|
||
<TextField
|
||
fullWidth
|
||
label="Username"
|
||
type="username"
|
||
margin="normal"
|
||
value={username}
|
||
onChange={(e) => setUsername(e.target.value)}
|
||
required
|
||
autoFocus
|
||
/>
|
||
<TextField
|
||
fullWidth
|
||
label="Password"
|
||
type="password"
|
||
margin="normal"
|
||
value={password}
|
||
onChange={(e) => setPassword(e.target.value)}
|
||
required
|
||
/>
|
||
|
||
{error && (
|
||
<Typography color="error" variant="body2" sx={{ mt: 1 }}>
|
||
{error}
|
||
</Typography>
|
||
)}
|
||
|
||
<Button
|
||
fullWidth
|
||
type="submit"
|
||
variant="contained"
|
||
color="primary"
|
||
sx={{ mt: 3 }}
|
||
disabled={loading}
|
||
>
|
||
{loading ? (
|
||
<CircularProgress size={24} color="inherit" />
|
||
) : isLogin ? (
|
||
"Login"
|
||
) : (
|
||
"Register"
|
||
)}
|
||
</Button>
|
||
</form>
|
||
|
||
<Typography
|
||
variant="body2"
|
||
color="text.secondary"
|
||
align="center"
|
||
sx={{ mt: 3 }}
|
||
>
|
||
{isLogin ? "Don’t have an account?" : "Already have an account?"}{' '}
|
||
<Link
|
||
component="button"
|
||
underline="hover"
|
||
color="primary"
|
||
onClick={onSwitchMode}
|
||
sx={{ fontWeight: 500 }}
|
||
>
|
||
{isLogin ? "Register" : "Login"}
|
||
</Link>
|
||
</Typography>
|
||
</Box>
|
||
);
|
||
}
|