mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-22 22:58: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));
|
const [req] = await db.select().from(membershipRequests).where(eq(membershipRequests.id, id));
|
||||||
if (!req) throw new Error("Demande introuvable.");
|
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();
|
const email = (req.email ?? "").trim().toLowerCase();
|
||||||
if (!email) {
|
if (!email) {
|
||||||
@@ -1206,6 +1209,9 @@ export async function setRequestStatus(formData: FormData) {
|
|||||||
const id = Number(formData.get("id"));
|
const id = Number(formData.get("id"));
|
||||||
const status = String(formData.get("status") ?? "");
|
const status = String(formData.get("status") ?? "");
|
||||||
if (!id || !["new", "approved", "rejected"].includes(status)) return;
|
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
|
await db
|
||||||
.update(membershipRequests)
|
.update(membershipRequests)
|
||||||
.set({ status: status as "new" | "approved" | "rejected" })
|
.set({ status: status as "new" | "approved" | "rejected" })
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { desc } from "drizzle-orm";
|
import { desc, inArray } from "drizzle-orm";
|
||||||
import { getSession } from "@/lib/session";
|
import { getSession } from "@/lib/session";
|
||||||
import { db } from "@/db";
|
import { db } from "@/db";
|
||||||
import { contactMessages, membershipRequests } from "@/db/schema";
|
import { contactMessages, members, membershipRequests } from "@/db/schema";
|
||||||
import { can } from "@/lib/rbac";
|
import { can } from "@/lib/rbac";
|
||||||
import { setContactStatus, setRequestStatus } from "../actions";
|
import { setContactStatus, setRequestStatus } from "../actions";
|
||||||
import { ApproveRequestForm } from "./ApproveRequestForm";
|
import { ApproveRequestForm } from "./ApproveRequestForm";
|
||||||
@@ -52,6 +53,21 @@ export default async function DemandesPage() {
|
|||||||
db.select().from(contactMessages).orderBy(desc(contactMessages.createdAt)),
|
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 (
|
return (
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 26 }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: 26 }}>
|
||||||
{/* ---- Demandes d'adhésion ---- */}
|
{/* ---- Demandes d'adhésion ---- */}
|
||||||
@@ -72,13 +88,32 @@ export default async function DemandesPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<StatusPill map={REQ_STATUS} status={r.status} />
|
<StatusPill map={REQ_STATUS} status={r.status} />
|
||||||
<div style={{ display: "flex", gap: 8 }}>
|
{/* 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} />
|
<ApproveRequestForm requestId={r.id} />
|
||||||
|
{r.status === "new" && (
|
||||||
<form action={setRequestStatus}>
|
<form action={setRequestStatus}>
|
||||||
<input type="hidden" name="id" value={r.id} />
|
<input type="hidden" name="id" value={r.id} />
|
||||||
<input type="hidden" name="status" value="rejected" />
|
<input type="hidden" name="status" value="rejected" />
|
||||||
<ActionBtn color="#d8472b">Rejeter</ActionBtn>
|
<ActionBtn color="#d8472b">Rejeter</ActionBtn>
|
||||||
</form>
|
</form>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{r.message && (
|
{r.message && (
|
||||||
|
|||||||
Reference in new issue
Block a user