mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-16 03:36:32 +02:00
Logo annuaire + modals devenir membre & contact
- Logo en haut a gauche de la page Annuaire (prop logo sur SiteHeader) - Modal "Devenir adherent" sur les CTA accueil + annuaire : cree une demande d'adhesion (membership_requests) et alerte les admins via le journal d'activite (compteur tableau de bord deja cable) - Formulaire de contact (modal depuis le pied de page) : nouvelle table contact_messages + alerte admin - Composant Modal partage, server actions publiques (sans auth) - Migration drizzle 0002 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
e2cf03c9f3
commit
141acffc1c
12 files changed
+1057
-17
No files matched your search
@@ -0,0 +1,73 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { db } from "@/db";
|
||||
import { activityLog, contactMessages, membershipRequests } from "@/db/schema";
|
||||
|
||||
export type SubmitResult = { ok: boolean; error?: string };
|
||||
|
||||
const isEmail = (v: string) => /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v);
|
||||
|
||||
async function logActivity(message: string, dot: string) {
|
||||
await db.insert(activityLog).values({ message, dot });
|
||||
}
|
||||
|
||||
// ---- Demande pour devenir membre (public, sans authentification) ----
|
||||
export async function submitMembershipRequest(formData: FormData): Promise<SubmitResult> {
|
||||
const name = String(formData.get("name") ?? "").trim();
|
||||
const email = String(formData.get("email") ?? "").trim();
|
||||
const phone = String(formData.get("phone") ?? "").trim();
|
||||
const note = String(formData.get("message") ?? "").trim();
|
||||
|
||||
if (!name) return { ok: false, error: "Le nom est requis." };
|
||||
if (email && !isEmail(email)) return { ok: false, error: "Adresse e-mail invalide." };
|
||||
|
||||
// Pas de colonne téléphone dédiée : on l'intègre au message.
|
||||
const message = [phone ? `Téléphone : ${phone}` : null, note || null]
|
||||
.filter(Boolean)
|
||||
.join("\n")
|
||||
.slice(0, 2000);
|
||||
|
||||
await db.insert(membershipRequests).values({
|
||||
name,
|
||||
email: email || null,
|
||||
message: message || null,
|
||||
status: "new",
|
||||
});
|
||||
|
||||
await logActivity(
|
||||
`<strong>${name}</strong> souhaite devenir adhérent`,
|
||||
"#9a6638",
|
||||
);
|
||||
|
||||
revalidatePath("/backend");
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
// ---- Message via le formulaire de contact (public) ----
|
||||
export async function submitContactMessage(formData: FormData): Promise<SubmitResult> {
|
||||
const name = String(formData.get("name") ?? "").trim();
|
||||
const email = String(formData.get("email") ?? "").trim();
|
||||
const subject = String(formData.get("subject") ?? "").trim();
|
||||
const message = String(formData.get("message") ?? "").trim();
|
||||
|
||||
if (!name) return { ok: false, error: "Le nom est requis." };
|
||||
if (!isEmail(email)) return { ok: false, error: "Adresse e-mail invalide." };
|
||||
if (!message) return { ok: false, error: "Le message est requis." };
|
||||
|
||||
await db.insert(contactMessages).values({
|
||||
name,
|
||||
email,
|
||||
subject: subject ? subject.slice(0, 200) : null,
|
||||
message: message.slice(0, 4000),
|
||||
status: "new",
|
||||
});
|
||||
|
||||
await logActivity(
|
||||
`Nouveau message de contact de <strong>${name}</strong>`,
|
||||
"#2C6FB3",
|
||||
);
|
||||
|
||||
revalidatePath("/backend");
|
||||
return { ok: true };
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
import { Sparkle } from "@/components/Sparkle";
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
import { MembershipModalButton } from "@/components/MembershipModalButton";
|
||||
import {
|
||||
getActiveMembersWithCategory,
|
||||
getAllCategories,
|
||||
@@ -45,7 +45,7 @@ export default async function AnnuairePage({
|
||||
|
||||
return (
|
||||
<div style={{ background: "#F6F2E8", minHeight: "100vh", fontFamily: "'Public Sans',sans-serif", color: "#33291D" }}>
|
||||
<SiteHeader active="annuaire" />
|
||||
<SiteHeader active="annuaire" logo />
|
||||
|
||||
<div className="container" style={{ paddingBottom: 56 }}>
|
||||
{/* title block */}
|
||||
@@ -107,13 +107,11 @@ export default async function AnnuairePage({
|
||||
Rejoignez l'annuaire Plein R
|
||||
</h2>
|
||||
</div>
|
||||
<Link
|
||||
href="/backend"
|
||||
<MembershipModalButton
|
||||
label="Adhérer à l'association"
|
||||
className="font-display"
|
||||
style={{ textDecoration: "none", background: "#E0A63C", color: "#33291D", fontWeight: 700, fontSize: 15.5, padding: "14px 26px", borderRadius: 12, whiteSpace: "nowrap" }}
|
||||
>
|
||||
Adhérer à l'association
|
||||
</Link>
|
||||
style={{ border: "none", background: "#E0A63C", color: "#33291D", fontWeight: 700, fontSize: 15.5, padding: "14px 26px", borderRadius: 12, whiteSpace: "nowrap" }}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
+5
-6
@@ -3,6 +3,7 @@ import { Sparkle } from "@/components/Sparkle";
|
||||
import { Reveal } from "@/components/Reveal";
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
import { MembershipModalButton } from "@/components/MembershipModalButton";
|
||||
import { getHighlightedMembers, getLivePromotions } from "@/lib/queries";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -456,11 +457,11 @@ export default async function AccueilPage() {
|
||||
Rejoignez plus de 120 commerçants et entreprises qui font réseau, se rencontrent et réussissent
|
||||
ensemble.
|
||||
</p>
|
||||
<a
|
||||
href="#"
|
||||
<MembershipModalButton
|
||||
label="Devenir adhérent"
|
||||
className="font-display lift-cta"
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
border: "none",
|
||||
background: "#fff",
|
||||
color: "#9a6638",
|
||||
fontWeight: 700,
|
||||
@@ -469,9 +470,7 @@ export default async function AccueilPage() {
|
||||
borderRadius: 12,
|
||||
display: "inline-block",
|
||||
}}
|
||||
>
|
||||
Devenir adhérent
|
||||
</a>
|
||||
/>
|
||||
</Reveal>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user