mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-22 14:48:04 +02:00
« Application error: a server-side exception has occurred », digest 2117999835,
sur /backend/adherents : l'action levait `Error("Un compte existe déjà avec cet
e-mail.")`. Next masque le message d'une exception de server action en
production et remplace la page entière par son écran d'erreur — un e-mail déjà
pris, cas de saisie parfaitement ordinaire, mettait donc la page par terre.
Ces échecs attendus sont désormais **renvoyés** (`ActionError`) au lieu d'être
jetés, dans `addMember`, `inviteAdmin` et `approveMembershipRequest` :
- Le formulaire affiche la vraie raison, l'e-mail en cause inclus.
- `AddMemberPanel` réinjecte la saisie : React vide un formulaire non contrôlé
après chaque action, il fallait sinon tout retaper.
- `InviteAdminForm` avait un rattrapage, mais devait deviner le motif faute de
message ; il affiche maintenant celui de l'action. Son `catch` ne couvre plus
que l'imprévu.
- `ApproveRequestForm` n'en avait aucun et tombait de la même façon.
Le `throw` reste réservé aux violations d'accès.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
72 lines
2.8 KiB
TypeScript
72 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { OneTimeCredentials } from "@/components/OneTimeCredentials";
|
|
import { inviteAdmin, type IssuedCredentials } from "../actions";
|
|
|
|
export function InviteAdminForm() {
|
|
const [issued, setIssued] = useState<IssuedCredentials | null>(null);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [pending, setPending] = useState(false);
|
|
const router = useRouter();
|
|
|
|
return (
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
|
|
{issued && <OneTimeCredentials items={[issued]} title="Compte créé — identifiants à transmettre" />}
|
|
{error && (
|
|
<div role="alert" style={{ background: "#fdecea", border: "1px solid #f1c4bd", borderRadius: 10, padding: "10px 14px", fontSize: 13, color: "#a3372e" }}>
|
|
{error}
|
|
</div>
|
|
)}
|
|
<form
|
|
action={async (fd) => {
|
|
setPending(true);
|
|
setError(null);
|
|
try {
|
|
const result = await inviteAdmin(fd);
|
|
if (result && "error" in result) {
|
|
setError(result.error);
|
|
return;
|
|
}
|
|
setIssued(result ?? null);
|
|
router.refresh();
|
|
} catch {
|
|
// Filet pour l'imprévu : les échecs de saisie, eux, sont renvoyés
|
|
// par l'action et affichés tels quels ci-dessus.
|
|
setError("Impossible de créer ce compte. Réessayez dans un instant.");
|
|
} finally {
|
|
setPending(false);
|
|
}
|
|
}}
|
|
>
|
|
<label className="field-label">Nom complet</label>
|
|
<input name="name" required placeholder="ex : Julie Bernard" className="field" style={{ marginBottom: 14 }} />
|
|
|
|
<label className="field-label">E-mail</label>
|
|
<input name="email" type="email" required placeholder="prenom@plein-r.fr" className="field" style={{ marginBottom: 14 }} />
|
|
|
|
<label className="field-label">Rôle</label>
|
|
<select name="role" defaultValue="Administrateur" className="field" style={{ marginBottom: 18 }}>
|
|
<option>Administrateur</option>
|
|
<option>Modérateur</option>
|
|
<option>Éditeur</option>
|
|
</select>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={pending}
|
|
className="font-display"
|
|
style={{ width: "100%", border: "none", background: "#13324F", color: "#fff", fontWeight: 700, fontSize: 14.5, padding: 13, borderRadius: 11, cursor: "pointer" }}
|
|
>
|
|
{pending ? "Création…" : "Créer le compte"}
|
|
</button>
|
|
</form>
|
|
<p style={{ fontSize: 11.5, color: "#a99c82", margin: 0, lineHeight: 1.5 }}>
|
|
Un mot de passe temporaire est généré et affiché une seule fois ici. L'invité devra le
|
|
changer à sa première connexion.
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|