106 lines
3.1 KiB
TypeScript
106 lines
3.1 KiB
TypeScript
import type { FormEvent } from 'react'
|
|
import type { I18nNode, I18nString } from '@pikku/react'
|
|
import {
|
|
Box,
|
|
Button,
|
|
Card,
|
|
Container,
|
|
PasswordInput,
|
|
Stack,
|
|
Text,
|
|
TextInput,
|
|
Title,
|
|
} from '@pikku/mantine/core'
|
|
import { m } from '@/i18n/messages'
|
|
import { useLocale } from '@/i18n/config'
|
|
|
|
type AuthCardProps = {
|
|
title: I18nString
|
|
description: I18nString
|
|
cta: I18nString
|
|
email: string
|
|
password: string
|
|
// 'current-password' on login (let the browser fill a saved password),
|
|
// 'new-password' on signup (let it suggest a strong one).
|
|
passwordAutoComplete: 'current-password' | 'new-password'
|
|
busy: boolean
|
|
message: I18nString | null
|
|
error: I18nString | null
|
|
footer: I18nNode
|
|
onEmailChange: (value: string) => void
|
|
onPasswordChange: (value: string) => void
|
|
onSubmit: (event: FormEvent<HTMLFormElement>) => void
|
|
}
|
|
|
|
export function AuthCard(props: AuthCardProps) {
|
|
useLocale()
|
|
|
|
return (
|
|
<Box
|
|
mih="100vh"
|
|
style={{
|
|
background:
|
|
'radial-gradient(circle at top left, rgba(96, 165, 250, 0.16), transparent 30%), linear-gradient(180deg, #10203a 0%, #07111e 56%, #040913 100%)',
|
|
}}
|
|
py="xl"
|
|
>
|
|
<Container size={560}>
|
|
<Card radius="xl" padding="xl" withBorder shadow="xl" bg="rgba(10, 20, 36, 0.86)">
|
|
<Stack gap="lg">
|
|
<div>
|
|
<Text
|
|
tt="uppercase"
|
|
fw={700}
|
|
fz="xs"
|
|
c="blue.3"
|
|
mb="sm"
|
|
style={{ letterSpacing: '0.18em' }}
|
|
>
|
|
{m.auth__eyebrow()}
|
|
</Text>
|
|
<Title order={1}>{props.title}</Title>
|
|
<Text c="dimmed" mt="sm" maw={440}>
|
|
{props.description}
|
|
</Text>
|
|
</div>
|
|
|
|
<form onSubmit={props.onSubmit}>
|
|
<Stack gap="md">
|
|
<TextInput
|
|
label={m.common__email()}
|
|
placeholder={m.common__email_placeholder()}
|
|
value={props.email}
|
|
onChange={(event) => props.onEmailChange(event.currentTarget.value)}
|
|
required
|
|
radius="md"
|
|
type="email"
|
|
autoComplete="email"
|
|
/>
|
|
<PasswordInput
|
|
label={m.common__password()}
|
|
placeholder={m.common__password_placeholder()}
|
|
value={props.password}
|
|
onChange={(event) => props.onPasswordChange(event.currentTarget.value)}
|
|
required
|
|
radius="md"
|
|
autoComplete={props.passwordAutoComplete}
|
|
/>
|
|
<Button type="submit" loading={props.busy} radius="md" size="md">
|
|
{props.cta}
|
|
</Button>
|
|
</Stack>
|
|
</form>
|
|
|
|
{props.message ? <Text c="teal.3">{props.message}</Text> : null}
|
|
{props.error ? <Text c="red.3">{props.error}</Text> : null}
|
|
|
|
<Text c="dimmed" size="sm">
|
|
{props.footer}
|
|
</Text>
|
|
</Stack>
|
|
</Card>
|
|
</Container>
|
|
</Box>
|
|
)
|
|
}
|