TypeScript 98.3%
CSS 0.9%
Shell 0.7%
1"use client";2import Link from "next/link";3import { useRouter } from "next/navigation";4import { useState } from "react";5import { ImmbotLogo, UqoLogo } from "@/components/logo";6import { Button, Card, Input, Label, Spinner } from "@/components/ui";78export default function LoginPage() {9 const router = useRouter();10 const [username, setUsername] = useState("");11 const [password, setPassword] = useState("");12 const [error, setError] = useState<string | null>(null);13 const [loading, setLoading] = useState(false);1415 async function submit(e: React.FormEvent) {16 e.preventDefault();17 setError(null);18 setLoading(true);19 try {20 const res = await fetch("/api/auth/login", {21 method: "POST",22 headers: { "Content-Type": "application/json" },23 body: JSON.stringify({ username, password }),24 });25 const data = await res.json();26 if (!res.ok) {27 setError(data.error ?? "Erreur de connexion.");28 return;29 }30 router.push(data.mustChangePassword ? "/changer-mot-de-passe?force=1" : "/chat");31 router.refresh();32 } catch {33 setError("Impossible de joindre le serveur.");34 } finally {35 setLoading(false);36 }37 }3839 return (40 <div className="min-h-dvh bg-app flex flex-col items-center justify-center px-4">41 <Link href="/" className="mb-8"><ImmbotLogo size={34} /></Link>42 <Card className="w-full max-w-sm p-7 animate-fade-up">43 <h1 className="text-lg font-bold text-fg">Connexion</h1>44 <p className="text-sm text-muted mt-1">Accédez à vos cours d'évaluation immobilière.</p>45 <form onSubmit={submit} className="mt-6 space-y-4">46 <div>47 <Label htmlFor="username">Identifiant</Label>48 <Input id="username" value={username} onChange={(e) => setUsername(e.target.value)} autoComplete="username" autoFocus required />49 </div>50 <div>51 <Label htmlFor="password">Mot de passe</Label>52 <Input id="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} autoComplete="current-password" required />53 </div>54 {error && <p className="text-sm text-red-600 dark:text-red-400 bg-red-500/10 rounded-lg px-3 py-2" role="alert">{error}</p>}55 <Button type="submit" className="w-full justify-center" disabled={loading}>56 {loading ? <Spinner /> : "Se connecter"}57 </Button>58 </form>59 <p className="text-[13px] text-muted mt-5 text-center">60 Pas de compte ? <Link href="/inscription" className="text-brand-500 font-medium hover:underline">S'inscrire</Link>61 </p>62 </Card>63 <div className="mt-8"><UqoLogo height={28} className="opacity-80 dark:invert" /></div>64 </div>65 );66}67