mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-22 14:48:04 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86e7e28020 |
No files matched your search
@@ -1163,6 +1163,9 @@ export async function approveMembershipRequest(formData: FormData): Promise<Crea
|
||||
|
||||
const [req] = await db.select().from(membershipRequests).where(eq(membershipRequests.id, id));
|
||||
if (!req) throw new Error("Demande introuvable.");
|
||||
// Garde serveur : un double clic ou un onglet resté ouvert ne doit jamais
|
||||
// créer un second compte pour la même demande.
|
||||
if (req.status === "approved") throw new Error("Cette demande a déjà été approuvée : l'adhérent existe.");
|
||||
|
||||
const email = (req.email ?? "").trim().toLowerCase();
|
||||
if (!email) {
|
||||
@@ -1206,6 +1209,9 @@ export async function setRequestStatus(formData: FormData) {
|
||||
const id = Number(formData.get("id"));
|
||||
const status = String(formData.get("status") ?? "");
|
||||
if (!id || !["new", "approved", "rejected"].includes(status)) return;
|
||||
// Une demande approuvée a produit un adhérent : son statut ne se réécrit plus.
|
||||
const [current] = await db.select({ status: membershipRequests.status }).from(membershipRequests).where(eq(membershipRequests.id, id));
|
||||
if (!current || current.status === "approved") return;
|
||||
await db
|
||||
.update(membershipRequests)
|
||||
.set({ status: status as "new" | "approved" | "rejected" })
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { desc } from "drizzle-orm";
|
||||
import { desc, inArray } from "drizzle-orm";
|
||||
import { getSession } from "@/lib/session";
|
||||
import { db } from "@/db";
|
||||
import { contactMessages, membershipRequests } from "@/db/schema";
|
||||
import { contactMessages, members, membershipRequests } from "@/db/schema";
|
||||
import { can } from "@/lib/rbac";
|
||||
import { setContactStatus, setRequestStatus } from "../actions";
|
||||
import { ApproveRequestForm } from "./ApproveRequestForm";
|
||||
@@ -52,6 +53,21 @@ export default async function DemandesPage() {
|
||||
db.select().from(contactMessages).orderBy(desc(contactMessages.createdAt)),
|
||||
]);
|
||||
|
||||
// Demandes approuvées : on retrouve la fiche créée par son e-mail, pour
|
||||
// proposer un lien vers l'adhérent plutôt que des boutons déjà consommés.
|
||||
const approvedEmails = requests
|
||||
.filter((r) => r.status === "approved" && r.email)
|
||||
.map((r) => (r.email ?? "").trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
const memberByEmail = new Map<string, number>();
|
||||
if (approvedEmails.length > 0) {
|
||||
const rows = await db
|
||||
.select({ id: members.id, email: members.email })
|
||||
.from(members)
|
||||
.where(inArray(members.email, approvedEmails));
|
||||
for (const row of rows) memberByEmail.set(row.email.trim().toLowerCase(), row.id);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 26 }}>
|
||||
{/* ---- Demandes d'adhésion ---- */}
|
||||
@@ -72,13 +88,32 @@ export default async function DemandesPage() {
|
||||
</div>
|
||||
</div>
|
||||
<StatusPill map={REQ_STATUS} status={r.status} />
|
||||
<div style={{ display: "flex", gap: 8 }}>
|
||||
<ApproveRequestForm requestId={r.id} />
|
||||
<form action={setRequestStatus}>
|
||||
<input type="hidden" name="id" value={r.id} />
|
||||
<input type="hidden" name="status" value="rejected" />
|
||||
<ActionBtn color="#d8472b">Rejeter</ActionBtn>
|
||||
</form>
|
||||
{/* Les actions dépendent du statut : une demande approuvée a déjà
|
||||
produit son adhérent, on ne propose plus que d'ouvrir sa fiche. */}
|
||||
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
|
||||
{r.status === "approved" ? (
|
||||
(() => {
|
||||
const memberId = memberByEmail.get((r.email ?? "").trim().toLowerCase());
|
||||
return memberId ? (
|
||||
<Link href={`/backend/adherents/${memberId}`} style={{ color: "#2C6FB3", fontWeight: 700, fontSize: 12.5, textDecoration: "none", whiteSpace: "nowrap" }}>
|
||||
Ouvrir la fiche adhérent →
|
||||
</Link>
|
||||
) : (
|
||||
<span style={{ fontSize: 12.5, color: "#a99c82" }}>Adhérent créé</span>
|
||||
);
|
||||
})()
|
||||
) : (
|
||||
<>
|
||||
<ApproveRequestForm requestId={r.id} />
|
||||
{r.status === "new" && (
|
||||
<form action={setRequestStatus}>
|
||||
<input type="hidden" name="id" value={r.id} />
|
||||
<input type="hidden" name="status" value="rejected" />
|
||||
<ActionBtn color="#d8472b">Rejeter</ActionBtn>
|
||||
</form>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{r.message && (
|
||||
|
||||
Reference in new issue
Block a user