GooDrive/frontend/src/pages/LoginPage.jsx
Louis Mazin 71809b6a90 nickel
2026-10-15 13:56:12 +02:00

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>
)
}