Files
ahriman/frontend/src/components/dialogs/LoginDialog.tsx
2026-03-01 15:08:29 +02:00

99 lines
3.8 KiB
TypeScript

import React, { useCallback, useState } from "react";
import {
Dialog, DialogContent, DialogActions, Button, TextField,
InputAdornment, IconButton,
} from "@mui/material";
import DialogHeader from "components/common/DialogHeader";
import VisibilityIcon from "@mui/icons-material/Visibility";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
import PersonIcon from "@mui/icons-material/Person";
import { useAuth } from "hooks/useAuth";
import { useNotification } from "hooks/useNotification";
import { useDialogClose } from "hooks/useDialogClose";
import { ApiError } from "api/client/ApiError";
interface LoginDialogProps {
open: boolean;
onClose: () => void;
}
export default function LoginDialog({ open, onClose }: LoginDialogProps): React.JSX.Element {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);
const { login } = useAuth();
const { showSuccess, showError } = useNotification();
const onOpen = useCallback(() => {
setUsername("");
setPassword("");
setShowPassword(false);
}, []);
const { isOpen, requestClose, transitionProps } = useDialogClose(open, onClose, onOpen);
const handleSubmit = async (): Promise<void> => {
if (!username || !password) {
return;
}
try {
await login(username, password);
requestClose();
showSuccess("Logged in", `Successfully logged in as ${username}`);
window.location.reload();
} catch (e) {
const detail = ApiError.errorDetail(e);
if (username === "admin" && password === "admin") {
showError("Login error", "You've entered a password for user \"root\", did you make a typo in username?");
} else {
showError("Login error", `Could not login as ${username}: ${detail}`);
}
}
};
return (
<Dialog open={isOpen} onClose={requestClose} maxWidth="xs" fullWidth slotProps={{ transition: transitionProps }}>
<DialogHeader onClose={requestClose}>
Login
</DialogHeader>
<DialogContent>
<TextField
label="username"
fullWidth
margin="normal"
value={username}
onChange={(e) => setUsername(e.target.value)}
autoFocus
/>
<TextField
label="password"
fullWidth
margin="normal"
type={showPassword ? "text" : "password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
void handleSubmit();
}
}}
slotProps={{
input: {
endAdornment: (
<InputAdornment position="end">
<IconButton aria-label={showPassword ? "Hide password" : "Show password"} onClick={() => setShowPassword(!showPassword)} edge="end" size="small">
{showPassword ? <VisibilityOffIcon /> : <VisibilityIcon />}
</IconButton>
</InputAdornment>
),
},
}}
/>
</DialogContent>
<DialogActions>
<Button onClick={() => void handleSubmit()} variant="contained" startIcon={<PersonIcon />}>login</Button>
</DialogActions>
</Dialog>
);
}