95 lines
4.7 KiB
JavaScript
95 lines
4.7 KiB
JavaScript
import { useState } from 'react'
|
|
import { api, message, storeTokens } from '@/api/client'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Input } from '@/components/ui/input'
|
|
import { ArrowUpRight, LoaderCircle, ShieldCheck } from 'lucide-react'
|
|
|
|
export default function LoginPage({ onSigned }) {
|
|
const [username, setUsername] = useState('')
|
|
const [password, setPassword] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState('')
|
|
|
|
const submit = async event => {
|
|
event.preventDefault()
|
|
setLoading(true)
|
|
setError('')
|
|
let signedIn = false
|
|
try {
|
|
const tokens = (await api.post('/auth/login/', { username, password })).data
|
|
storeTokens(tokens)
|
|
signedIn = true
|
|
const user = (await api.get('/auth/me/')).data
|
|
onSigned(user)
|
|
} catch (exception) {
|
|
// Never forget a successful login because loading /auth/me/ failed.
|
|
// Failed credentials never created a session, so only show the error.
|
|
setError(signedIn ? 'Connexion enregistrée, mais le chargement du compte a échoué. Actualise la page pour réessayer.' : exception.response?.status === 401 ? 'Identifiants incorrects.' : message(exception))
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-dvh bg-background p-4 flex items-center justify-center">
|
|
<div className="grid w-full max-w-4xl overflow-hidden rounded-3xl border bg-card shadow-2xl md:grid-cols-2">
|
|
<div className="drive-brand relative hidden min-h-[480px] flex-col justify-between overflow-hidden p-10 text-white md:flex">
|
|
<div className="absolute -right-24 -top-24 size-80 rounded-full border border-white/20" />
|
|
<div className="absolute -left-28 bottom-16 size-72 rounded-full bg-white/5" />
|
|
<div className="relative flex items-center gap-3">
|
|
<div className="flex size-11 items-center justify-center rounded-2xl bg-white/15">
|
|
<img
|
|
src="/icon.png"
|
|
alt="Logo de GooDrive"
|
|
className="block size-full object-contain"
|
|
/>
|
|
</div>
|
|
<span className="text-xl font-semibold tracking-tight">GoodDrive</span>
|
|
</div>
|
|
<div className="relative space-y-4">
|
|
<span className="rounded-full border border-white/20 px-3 py-1 text-xs font-medium">Votre espace privé</span>
|
|
<h2 className="text-4xl font-semibold leading-tight tracking-tight">Votre espace.<br />Vos fichiers.<br />Pour vous.</h2>
|
|
<p className="max-w-xs leading-relaxed text-blue-100">
|
|
Un espace pour ceux qui en ont besoin. Des dossiers privés, à partager en confiance avec les personnes invitées.
|
|
</p>
|
|
</div>
|
|
<p className="relative flex items-center gap-2 text-xs text-white/70">
|
|
<ShieldCheck className="size-4" /> Hébergé sur votre infrastructure
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex min-h-[480px] flex-col justify-center p-7 sm:p-12">
|
|
<div className="mb-8 flex items-center gap-3 md:hidden">
|
|
<span className="drive-brand flex size-10 items-center justify-center rounded-xl text-white">
|
|
<img
|
|
src="/icon.png"
|
|
alt="Logo de GooDrive"
|
|
className="block size-full object-contain"
|
|
/>
|
|
</span>
|
|
<b className="text-xl">GoodDrive</b>
|
|
</div>
|
|
<h1 className="text-2xl font-semibold tracking-tight">Bon retour 👋</h1>
|
|
<p className="mt-2 text-sm text-muted-foreground">Connectez-vous à votre espace de stockage.</p>
|
|
|
|
<form onSubmit={submit} className="mt-8 space-y-5">
|
|
<label className="block space-y-2">
|
|
<span className="text-sm font-medium">Identifiant</span>
|
|
<Input autoComplete="username" value={username} onChange={event => setUsername(event.target.value)} required placeholder="Votre identifiant" className="h-11" />
|
|
</label>
|
|
<label className="block space-y-2">
|
|
<span className="text-sm font-medium">Mot de passe</span>
|
|
<Input autoComplete="current-password" type="password" value={password} onChange={event => setPassword(event.target.value)} required placeholder="••••••••" className="h-11" />
|
|
</label>
|
|
{error && <p role="alert" className="text-sm text-destructive">{error}</p>}
|
|
<Button type="submit" disabled={loading} className="h-11 w-full">
|
|
{loading ? <LoaderCircle className="animate-spin" /> : 'Se connecter'}
|
|
{!loading && <ArrowUpRight />}
|
|
</Button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|