espace-paie-odentas/components/staff/InviteForm.tsx
odentas e9cb6e7e0e feat: Système unifié d'invitation avec emails d'activation
- Créé sendInvitationWithActivationEmail() pour unifier les invitations
- Modifié /api/staff/users/invite pour utiliser generateLink + email
- Modifié /api/access/nouveau pour envoyer email d'activation
- Modifié /api/access POST pour remplacer pending_invites par système direct
- Template account-activation mis à jour :
  * Titre 'Activez votre compte'
  * Encart avec infos : invitant (statut), organisation, niveau d'accès
  * Message de contact formaté comme autres emails
  * Renommage 'Odentas Paie' → 'Espace Paie Odentas'
- Fix page /activate : délai 100ms pour hash fragment + redirection 1s
- Liens d'activation forcés vers paie.odentas.fr (tests depuis localhost)
- Messages UI cohérents : 'Invitation envoyée' au lieu de 'Compte créé'
2025-11-14 17:41:46 +01:00

119 lines
No EOL
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// components/staff/InviteForm.tsx
"use client";
import { useState } from "react";
type Org = { id: string; name: string };
export default function InviteForm({ orgs }: { orgs: Org[] }) {
const [firstName, setFirstName] = useState("");
const [email, setEmail] = useState("");
const [orgId, setOrgId] = useState(orgs[0]?.id || "");
const [role, setRole] = useState("ADMIN");
const [submitting, setSubmitting] = useState(false);
const [done, setDone] = useState<null | { email: string; orgName: string; role: string }>(null);
const [error, setError] = useState<string | null>(null);
async function onSubmit(e: React.FormEvent) {
e.preventDefault();
setError(null);
setDone(null);
setSubmitting(true);
try {
const res = await fetch("/api/staff/users/invite", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ firstName, email, orgId, role }),
});
const json = await res.json().catch(() => ({}));
if (!res.ok) {
setError(json?.error || "Erreur lors de la création de lutilisateur");
} else {
const orgName = orgs.find((o) => o.id === orgId)?.name || "";
setDone({ email, orgName, role });
setFirstName("");
setEmail("");
}
} catch (err: any) {
setError(err?.message || "Erreur réseau");
} finally {
setSubmitting(false);
}
}
return (
<form onSubmit={onSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium">Prénom</label>
<input
className="mt-1 w-full rounded-lg border px-3 py-2"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
placeholder="Prénom"
/>
</div>
<div>
<label className="block text-sm font-medium">Adresse e-mail</label>
<input
type="email"
className="mt-1 w-full rounded-lg border px-3 py-2"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="ex: nom@domaine.fr"
required
/>
</div>
<div>
<label className="block text-sm font-medium">Client (organisation)</label>
<select
className="mt-1 w-full rounded-lg border px-3 py-2"
value={orgId}
onChange={(e) => setOrgId(e.target.value)}
required
>
{orgs.map((o) => (
<option key={o.id} value={o.id}>{o.name}</option>
))}
</select>
</div>
<div>
<label className="block text-sm font-medium">Niveau daccès</label>
<select
className="mt-1 w-full rounded-lg border px-3 py-2"
value={role}
onChange={(e) => setRole(e.target.value)}
required
>
<option value="SUPER_ADMIN">Super Admin</option>
<option value="ADMIN">Admin</option>
<option value="AGENT">Agent</option>
<option value="COMPTA">Compta</option>
</select>
<p className="mt-1 text-xs text-slate-500">
Un email d'activation sera automatiquement envoyé à l'utilisateur avec un lien sécurisé.
</p>
</div>
</div>
{error && <p className="text-sm text-red-600">{error}</p>}
{done && (
<div className="rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-sm text-emerald-800">
Invitation envoyée à <strong>{done.email}</strong> pour <strong>{done.orgName}</strong> ({done.role})
</div>
)}
<div className="flex gap-3">
<button
type="submit"
disabled={submitting}
className="px-4 py-2 rounded-lg bg-emerald-600 text-white hover:bg-emerald-700 disabled:opacity-60"
>
{submitting ? "Envoi en cours…" : "Inviter l'utilisateur"}
</button>
<a href="/staff/utilisateurs" className="px-4 py-2 rounded-lg border">Annuler</a>
</div>
</form>
);
}