Files
diabetix/src/app/auth/login/LoginForm.tsx
jeremy bayse e7f151d14e feat: Initial Diabetix application commit
- Add authentication with NextAuth v5 (credentials + email verification)
- Implement dashboard with glycemia tracking and AI analysis
- Add PDF report generation for Premium users
- Implement Stripe integration for Premium subscriptions
- Add responsive UI with Tailwind CSS and shadcn components
- Database schema with Prisma ORM and PostgreSQL support
- Real-time glycemia visualization with Recharts
- Mobile-optimized entry form
- User profile management with medical information
- Subscription lifecycle management (create, cancel, webhook)
- Email notifications with Resend
- Feature gates for Free vs Premium plans

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 23:06:29 +02:00

118 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import { signIn } from "next-auth/react";
import { useRouter, useSearchParams } from "next/navigation";
import Link from "next/link";
import { Loader2, Eye, EyeOff } from "lucide-react";
export function LoginForm() {
const router = useRouter();
const params = useSearchParams();
const verified = params.get("verified") === "1";
const errorParam = params.get("error");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [showPwd, setShowPwd] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError(null);
setLoading(true);
const result = await signIn("credentials", {
email: email.trim().toLowerCase(),
password,
redirect: false,
});
if (result?.error) {
setError("Email ou mot de passe incorrect.");
setLoading(false);
return;
}
router.push("/dashboard");
router.refresh();
}
return (
<div className="rounded-2xl border border-slate-200 bg-white p-8 shadow-sm">
{verified && (
<div className="mb-4 rounded-lg border border-emerald-300 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">
Email confirmé ! Vous pouvez vous connecter.
</div>
)}
{errorParam === "token_expired" && (
<div className="mb-4 rounded-lg border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-800">
Lien expiré. Inscrivez-vous à nouveau.
</div>
)}
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="mb-1.5 block text-sm font-medium text-slate-700">Email</label>
<input
type="email"
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="w-full rounded-lg border border-slate-300 px-3 py-2.5 outline-none focus:border-teal-500 focus:ring-2 focus:ring-teal-200"
placeholder="vous@exemple.fr"
/>
</div>
<div>
<label className="mb-1.5 block text-sm font-medium text-slate-700">
Mot de passe
</label>
<div className="relative">
<input
type={showPwd ? "text" : "password"}
autoComplete="current-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="w-full rounded-lg border border-slate-300 px-3 py-2.5 pr-10 outline-none focus:border-teal-500 focus:ring-2 focus:ring-teal-200"
placeholder="••••••••"
/>
<button
type="button"
onClick={() => setShowPwd((v) => !v)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600"
>
{showPwd ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
</div>
{error && (
<div className="rounded-lg border border-rose-300 bg-rose-50 px-3 py-2 text-sm text-rose-800">
{error}
</div>
)}
<button
type="submit"
disabled={loading}
className="flex w-full items-center justify-center gap-2 rounded-lg bg-teal-600 py-2.5 text-sm font-semibold text-white hover:bg-teal-700 disabled:opacity-60"
>
{loading && <Loader2 className="h-4 w-4 animate-spin" />}
Se connecter
</button>
</form>
<p className="mt-6 text-center text-sm text-slate-500">
Pas encore de compte ?{" "}
<Link href="/auth/register" className="font-medium text-teal-700 hover:underline">
S'inscrire gratuitement
</Link>
</p>
</div>
);
}