TypeScript 98.3%
CSS 0.9%
Shell 0.7%
1"use client";2import { useRouter, useSearchParams } from "next/navigation";3import { Suspense, useState } from "react";4import { ImmbotLogo, UqoLogo } from "@/components/logo";5import { Button, Card, Input, Label, Spinner } from "@/components/ui";67function ChangePasswordForm() {8 const router = useRouter();9 const forced = useSearchParams().get("force") === "1";10 const [current, setCurrent] = useState("");11 const [next, setNext] = useState("");12 const [confirm, setConfirm] = useState("");13 const [error, setError] = useState<string | null>(null);14 const [loading, setLoading] = useState(false);1516 async function submit(e: React.FormEvent) {17 e.preventDefault();18 setError(null);19 if (next !== confirm) return setError("La confirmation ne correspond pas.");20 setLoading(true);21 try {22 const res = await fetch("/api/auth/change-password", {23 method: "POST",24 headers: { "Content-Type": "application/json" },25 body: JSON.stringify({ currentPassword: current, newPassword: next }),26 });27 const data = await res.json();28 if (!res.ok) return setError(data.error ?? "Erreur.");29 router.push("/chat");30 router.refresh();31 } catch {32 setError("Impossible de joindre le serveur.");33 } finally {34 setLoading(false);35 }36 }3738 return (39 <Card className="w-full max-w-sm p-7 animate-fade-up">40 <h1 className="text-lg font-bold text-fg">Changer le mot de passe</h1>41 {forced ? (42 <p className="text-sm mt-2 text-amber-700 dark:text-amber-400 bg-amber-500/10 rounded-lg px-3 py-2.5">43 <strong>Sécurité :</strong> le mot de passe initial doit être remplacé avant d'utiliser la44 plateforme. Choisissez un mot de passe fort et unique.45 </p>46 ) : (47 <p className="text-sm text-muted mt-1">Choisissez un nouveau mot de passe (10 caractères minimum).</p>48 )}49 <form onSubmit={submit} className="mt-6 space-y-4">50 <div>51 <Label htmlFor="current">Mot de passe actuel</Label>52 <Input id="current" type="password" value={current} onChange={(e) => setCurrent(e.target.value)} autoComplete="current-password" required autoFocus />53 </div>54 <div>55 <Label htmlFor="next">Nouveau mot de passe</Label>56 <Input id="next" type="password" value={next} onChange={(e) => setNext(e.target.value)} autoComplete="new-password" required minLength={10} />57 </div>58 <div>59 <Label htmlFor="confirm">Confirmer le nouveau mot de passe</Label>60 <Input id="confirm" type="password" value={confirm} onChange={(e) => setConfirm(e.target.value)} autoComplete="new-password" required minLength={10} />61 </div>62 {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>}63 <Button type="submit" className="w-full justify-center" disabled={loading}>64 {loading ? <Spinner /> : "Enregistrer et continuer"}65 </Button>66 </form>67 </Card>68 );69}7071export default function ChangePasswordPage() {72 return (73 <div className="min-h-dvh bg-app flex flex-col items-center justify-center px-4">74 <div className="mb-8"><ImmbotLogo size={34} /></div>75 <Suspense>76 <ChangePasswordForm />77 </Suspense>78 <div className="mt-8"><UqoLogo height={28} className="opacity-80 dark:invert" /></div>79 </div>80 );81}82