164 lines
4.2 KiB
TypeScript
164 lines
4.2 KiB
TypeScript
import type { FormEvent, ReactNode } from "react";
|
|
import {
|
|
Alert,
|
|
AlertDescription,
|
|
AlertIcon,
|
|
Button,
|
|
FormControl,
|
|
FormLabel,
|
|
Input,
|
|
Spinner,
|
|
Stack,
|
|
Text
|
|
} from "./chakra-compat";
|
|
import type { PasswordResetMode, PasswordResetTokenState } from "./types";
|
|
|
|
type PasswordResetRequestTexts = {
|
|
emailLabel: string;
|
|
submitLabel: string;
|
|
requestSentMessage: string;
|
|
};
|
|
|
|
type PasswordResetRequestFormProps = {
|
|
texts: PasswordResetRequestTexts;
|
|
helperText: ReactNode;
|
|
loading?: boolean;
|
|
requestSent?: boolean;
|
|
onSubmit: (values: { email: string }) => void | Promise<void>;
|
|
emailPlaceholder?: string;
|
|
};
|
|
|
|
type PasswordResetConfirmTexts = {
|
|
loadingLabel: string;
|
|
passwordLabel: string;
|
|
passwordConfirmLabel: string;
|
|
invalidLinkLabel: string;
|
|
resetSubmitLabel: string;
|
|
createSubmitLabel: string;
|
|
resetSuccessLabel: string;
|
|
createSuccessLabel: string;
|
|
};
|
|
|
|
type PasswordResetConfirmFormProps = {
|
|
texts: PasswordResetConfirmTexts;
|
|
tokenState: PasswordResetTokenState;
|
|
loading?: boolean;
|
|
completedMode?: PasswordResetMode | null;
|
|
onSubmit: (values: { password: string; passwordConfirm: string }) => void | Promise<void>;
|
|
};
|
|
|
|
export function PasswordResetRequestForm({
|
|
texts,
|
|
helperText,
|
|
loading = false,
|
|
requestSent = false,
|
|
onSubmit,
|
|
emailPlaceholder = "you@example.com"
|
|
}: PasswordResetRequestFormProps) {
|
|
function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
const form = new FormData(event.currentTarget);
|
|
void onSubmit({
|
|
email: String(form.get("email") ?? "")
|
|
});
|
|
}
|
|
|
|
return (
|
|
<Stack spacing={5}>
|
|
{requestSent ? (
|
|
<Alert status="success" borderRadius="md">
|
|
<AlertIcon />
|
|
<AlertDescription>{texts.requestSentMessage}</AlertDescription>
|
|
</Alert>
|
|
) : null}
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
<Stack spacing={4}>
|
|
<FormControl isRequired>
|
|
<FormLabel>{texts.emailLabel}</FormLabel>
|
|
<Input name="email" type="email" placeholder={emailPlaceholder} />
|
|
</FormControl>
|
|
|
|
<Text fontSize="sm" color="gray.600">
|
|
{helperText}
|
|
</Text>
|
|
|
|
<Button type="submit" isLoading={loading}>
|
|
{texts.submitLabel}
|
|
</Button>
|
|
</Stack>
|
|
</form>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
export function PasswordResetConfirmForm({
|
|
texts,
|
|
tokenState,
|
|
loading = false,
|
|
completedMode = null,
|
|
onSubmit
|
|
}: PasswordResetConfirmFormProps) {
|
|
function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
const form = new FormData(event.currentTarget);
|
|
void onSubmit({
|
|
password: String(form.get("password") ?? ""),
|
|
passwordConfirm: String(form.get("passwordConfirm") ?? "")
|
|
});
|
|
}
|
|
|
|
if (tokenState.status === "loading") {
|
|
return (
|
|
<Stack align="center" py={6} spacing={3}>
|
|
<Spinner />
|
|
<Text color="gray.600">{texts.loadingLabel}</Text>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
if (tokenState.status === "invalid") {
|
|
return (
|
|
<Alert status="error" borderRadius="md">
|
|
<AlertIcon />
|
|
<AlertDescription>{tokenState.error || texts.invalidLinkLabel}</AlertDescription>
|
|
</Alert>
|
|
);
|
|
}
|
|
|
|
if (completedMode !== null) {
|
|
return (
|
|
<Alert status="success" borderRadius="md">
|
|
<AlertIcon />
|
|
<AlertDescription>{completedMode === "create" ? texts.createSuccessLabel : texts.resetSuccessLabel}</AlertDescription>
|
|
</Alert>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Stack spacing={4}>
|
|
<Text fontSize="sm" color="gray.600">
|
|
{tokenState.email}
|
|
</Text>
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
<Stack spacing={4}>
|
|
<FormControl isRequired>
|
|
<FormLabel>{texts.passwordLabel}</FormLabel>
|
|
<Input name="password" type="password" minLength={8} />
|
|
</FormControl>
|
|
|
|
<FormControl isRequired>
|
|
<FormLabel>{texts.passwordConfirmLabel}</FormLabel>
|
|
<Input name="passwordConfirm" type="password" minLength={8} />
|
|
</FormControl>
|
|
|
|
<Button type="submit" isLoading={loading}>
|
|
{tokenState.mode === "create" ? texts.createSubmitLabel : texts.resetSubmitLabel}
|
|
</Button>
|
|
</Stack>
|
|
</form>
|
|
</Stack>
|
|
);
|
|
}
|