karbe/src/app/inscription/_components/SignupForm.tsx
Claude Integration 59786e5365
All checks were successful
CI / test (pull_request) Successful in 2m33s
feat(rental): Sprint C — espace prestataire (signup+dashboard+items+calendrier+résa)
2026-06-02 08:01:42 +00:00

220 lines
8.2 KiB
TypeScript

"use client";
import { useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import { signIn } from "next-auth/react";
type Props = { next: string };
export function SignupForm({ next }: Props) {
const router = useRouter();
const [pending, startTransition] = useTransition();
const [error, setError] = useState<string | null>(null);
const [role, setRole] = useState<"TOURIST" | "OWNER" | "RENTAL_PROVIDER">("TOURIST");
const [providerName, setProviderName] = useState("");
const [providerRivers, setProviderRivers] = useState("");
function onSubmit(formData: FormData) {
setError(null);
const email = (formData.get("email") as string | null)?.trim() ?? "";
const password = (formData.get("password") as string | null) ?? "";
const firstName = (formData.get("firstName") as string | null)?.trim() ?? "";
const lastName = (formData.get("lastName") as string | null)?.trim() ?? "";
const phone = (formData.get("phone") as string | null)?.trim() ?? "";
if (password.length < 8) {
setError("Le mot de passe doit faire au moins 8 caractères.");
return;
}
if (role === "RENTAL_PROVIDER" && providerName.trim().length < 2) {
setError("Le nom de votre activité de loueur est requis.");
return;
}
startTransition(async () => {
const body: Record<string, unknown> = {
email,
password,
firstName,
lastName,
phone: phone || null,
role,
};
if (role === "RENTAL_PROVIDER") {
body.providerName = providerName.trim();
body.providerRivers = providerRivers
.split(/[,;\n]/)
.map((s) => s.trim())
.filter((s) => s.length > 0);
}
const res = await fetch("/api/signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const json = await res.json().catch(() => ({}));
if (!res.ok) {
setError(json?.error || `Erreur ${res.status}`);
return;
}
const result = await signIn("credentials", {
email,
password,
redirect: false,
});
if (result?.error) {
setError("Compte créé mais connexion impossible. Essayez la page de connexion.");
return;
}
router.push(next);
router.refresh();
});
}
const inputCls =
"w-full rounded-md border border-zinc-300 px-3 py-2 text-sm focus:border-zinc-900 focus:outline-none";
return (
<form action={onSubmit} className="space-y-3">
<fieldset disabled={pending} className="space-y-3">
<div className="grid grid-cols-2 gap-2">
<label className="block">
<span className="text-xs text-zinc-600">Prénom</span>
<input name="firstName" type="text" required maxLength={100} className={inputCls + " mt-0.5"} />
</label>
<label className="block">
<span className="text-xs text-zinc-600">Nom</span>
<input name="lastName" type="text" required maxLength={100} className={inputCls + " mt-0.5"} />
</label>
</div>
<label className="block">
<span className="text-xs text-zinc-600">Email</span>
<input name="email" type="email" required maxLength={200} className={inputCls + " mt-0.5"} />
</label>
<label className="block">
<span className="text-xs text-zinc-600">Mot de passe (8 caractères min.)</span>
<input
name="password"
type="password"
required
minLength={8}
maxLength={200}
className={inputCls + " mt-0.5"}
/>
</label>
<label className="block">
<span className="text-xs text-zinc-600">Téléphone (optionnel)</span>
<input name="phone" type="tel" maxLength={40} className={inputCls + " mt-0.5"} />
</label>
<fieldset className="space-y-1">
<legend className="text-xs text-zinc-600">Type de compte</legend>
<div className="grid grid-cols-1 gap-2 pt-1 sm:grid-cols-3">
<label
className={
"flex cursor-pointer flex-col items-start rounded-md border px-3 py-2 text-sm " +
(role === "TOURIST"
? "border-zinc-900 bg-zinc-50 ring-1 ring-zinc-900"
: "border-zinc-300 hover:bg-zinc-50")
}
>
<input
type="radio"
name="role"
value="TOURIST"
checked={role === "TOURIST"}
onChange={() => setRole("TOURIST")}
className="sr-only"
/>
<span className="font-semibold text-zinc-900">Voyageur</span>
<span className="text-[11px] text-zinc-500">Réserver un séjour.</span>
</label>
<label
className={
"flex cursor-pointer flex-col items-start rounded-md border px-3 py-2 text-sm " +
(role === "OWNER"
? "border-zinc-900 bg-zinc-50 ring-1 ring-zinc-900"
: "border-zinc-300 hover:bg-zinc-50")
}
>
<input
type="radio"
name="role"
value="OWNER"
checked={role === "OWNER"}
onChange={() => setRole("OWNER")}
className="sr-only"
/>
<span className="font-semibold text-zinc-900">Hôte</span>
<span className="text-[11px] text-zinc-500">Publier un carbet.</span>
</label>
<label
className={
"flex cursor-pointer flex-col items-start rounded-md border px-3 py-2 text-sm " +
(role === "RENTAL_PROVIDER"
? "border-zinc-900 bg-zinc-50 ring-1 ring-zinc-900"
: "border-zinc-300 hover:bg-zinc-50")
}
>
<input
type="radio"
name="role"
value="RENTAL_PROVIDER"
checked={role === "RENTAL_PROVIDER"}
onChange={() => setRole("RENTAL_PROVIDER")}
className="sr-only"
/>
<span className="font-semibold text-zinc-900">Loueur matériel</span>
<span className="text-[11px] text-zinc-500">Hamac, pirogue, kayak</span>
</label>
</div>
</fieldset>
{role === "RENTAL_PROVIDER" ? (
<div className="space-y-2 rounded-md border border-emerald-200 bg-emerald-50/30 p-3">
<p className="text-[11px] text-emerald-900">
Votre compte sera créé en <strong>attente de validation</strong>. Un admin Karbé
vous contactera pour confirmer votre activité avant publication de vos items.
</p>
<label className="block">
<span className="text-xs text-zinc-600">Nom de votre activité</span>
<input
type="text"
value={providerName}
onChange={(e) => setProviderName(e.target.value)}
placeholder="ex. Pirogues du Bas-Oyapock"
maxLength={200}
className={inputCls + " mt-0.5"}
/>
</label>
<label className="block">
<span className="text-xs text-zinc-600">Fleuves desservis (séparés par virgule)</span>
<input
type="text"
value={providerRivers}
onChange={(e) => setProviderRivers(e.target.value)}
placeholder="Maroni, Oyapock"
maxLength={300}
className={inputCls + " mt-0.5"}
/>
</label>
</div>
) : null}
{error ? (
<div className="rounded border border-rose-200 bg-rose-50 px-3 py-2 text-sm text-rose-700">{error}</div>
) : null}
<button
type="submit"
className="w-full rounded-md bg-zinc-900 px-3 py-2 text-sm font-semibold text-white hover:bg-zinc-800 disabled:opacity-50"
>
{pending ? "Création…" : "Créer mon compte"}
</button>
</fieldset>
</form>
);
}