220 lines
8.2 KiB
TypeScript
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>
|
|
);
|
|
}
|