97 lines
2.8 KiB
TypeScript
97 lines
2.8 KiB
TypeScript
import { createFileRoute, useNavigate } from '@tanstack/react-router'
|
|
import { useState } from 'react'
|
|
import { useQueryClient } from '@tanstack/react-query'
|
|
import {
|
|
Box,
|
|
Card,
|
|
TextInput,
|
|
PasswordInput,
|
|
Button,
|
|
Text,
|
|
Stack,
|
|
Alert,
|
|
Group,
|
|
} from '@pikku/mantine/core'
|
|
import { m } from '@/i18n/messages'
|
|
import { useLocale } from '@/i18n/config'
|
|
import type { I18nString } from '@pikku/react'
|
|
import { signIn, INVALID_CREDENTIALS } from '../lib/auth'
|
|
|
|
function LoginPage() {
|
|
useLocale()
|
|
const navigate = useNavigate()
|
|
const qc = useQueryClient()
|
|
const [email, setEmail] = useState(import.meta.env.DEV ? 'admin@perauset.org' : '')
|
|
const [password, setPassword] = useState(import.meta.env.DEV ? 'test' : '')
|
|
const [isPending, setIsPending] = useState(false)
|
|
const [error, setError] = useState<I18nString | null>(null)
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
setIsPending(true)
|
|
setError(null)
|
|
try {
|
|
await signIn(email, password)
|
|
await qc.invalidateQueries({ queryKey: ['me'] })
|
|
navigate({ to: '/' })
|
|
} catch (err: any) {
|
|
setError(err.message === INVALID_CREDENTIALS ? m.login_invalid_credentials() : m.login_error())
|
|
} finally {
|
|
setIsPending(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Box
|
|
style={{
|
|
minHeight: '100vh',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
backgroundColor: '#F5F0E8',
|
|
padding: 16,
|
|
}}
|
|
>
|
|
<Card shadow="lg" padding="xl" radius="lg" w={400} maw="100%" style={{ backgroundColor: '#ffffff' }}>
|
|
<form onSubmit={handleSubmit}>
|
|
<Stack gap="md">
|
|
<Group justify="center">
|
|
<Text size="xl" fw={700} style={{ color: '#2A7B88', fontSize: 28 }}>
|
|
{m.brand()}
|
|
</Text>
|
|
</Group>
|
|
<Text ta="center" size="sm" c="dimmed">
|
|
{m.login_subtitle()}
|
|
</Text>
|
|
{error && (
|
|
<Alert color="red" variant="light">
|
|
{error}
|
|
</Alert>
|
|
)}
|
|
<TextInput
|
|
label={m.common_email()}
|
|
type="email"
|
|
required
|
|
value={email}
|
|
onChange={(e) => setEmail(e.currentTarget.value)}
|
|
/>
|
|
<PasswordInput
|
|
label={m.common_password()}
|
|
required
|
|
value={password}
|
|
onChange={(e) => setPassword(e.currentTarget.value)}
|
|
/>
|
|
<Button type="submit" fullWidth loading={isPending} color="teal" size="md" radius="md" mt="sm">
|
|
{m.login_cta()}
|
|
</Button>
|
|
</Stack>
|
|
</form>
|
|
</Card>
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
export const Route = createFileRoute('/login')({
|
|
component: LoginPage,
|
|
})
|