mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-24 07:38:04 +02:00
Ajoute la page Annuaire publique (/annuaire) reliée aux fiches adhérents
Liste des adhérents actifs avec recherche (nom/métier), filtre par commune, filtres catégories (chips + compteurs), tri (A–Z / avec promotions), état vide et CTA d'adhésion — fidèle à la maquette. Chaque carte ouvre la fiche adhérent (/adherents/[id]). La recherche de l'accueil et la nav pointent vers /annuaire (la barre de recherche pré-remplit via ?q=). Nouvelles requêtes annuaire. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XwfhYHzC9hQWPDnQ2p53GL
This commit is contained in:
5 files changed
+455
-6
No files matched your search
@@ -0,0 +1,166 @@
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
import { Sparkle } from "@/components/Sparkle";
|
||||
import {
|
||||
getActiveMembersWithCategory,
|
||||
getAllCategories,
|
||||
getLiveBadgesByMember,
|
||||
} from "@/lib/queries";
|
||||
import { AnnuaireClient, type DirectoryMember } from "./AnnuaireClient";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Annuaire — Plein R",
|
||||
description:
|
||||
"L'annuaire des commerçants et entreprises du Bassin de Pompey. Trouvez un professionnel et ses bons plans.",
|
||||
};
|
||||
|
||||
export default async function AnnuairePage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ q?: string }>;
|
||||
}) {
|
||||
const { q } = await searchParams;
|
||||
const [rawMembers, categories, badges] = await Promise.all([
|
||||
getActiveMembersWithCategory(),
|
||||
getAllCategories(),
|
||||
getLiveBadgesByMember(),
|
||||
]);
|
||||
|
||||
const badgeByMember = new Map<number, string | null>();
|
||||
for (const b of badges) {
|
||||
if (b.memberId != null && !badgeByMember.has(b.memberId)) {
|
||||
badgeByMember.set(b.memberId, b.badge);
|
||||
}
|
||||
}
|
||||
|
||||
const members: DirectoryMember[] = rawMembers.map((m) => ({
|
||||
...m,
|
||||
hasPromo: badgeByMember.has(m.id),
|
||||
promoBadge: badgeByMember.get(m.id) ?? null,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div style={{ background: "#F6F2E8", minHeight: "100vh", fontFamily: "'Public Sans',sans-serif", color: "#33291D" }}>
|
||||
{/* header */}
|
||||
<header
|
||||
className="container"
|
||||
style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "18px 56px", gap: 20 }}
|
||||
>
|
||||
<Link href="/" style={{ display: "flex", alignItems: "center", gap: 10, textDecoration: "none" }}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/assets/logo.png" alt="Plein R" style={{ height: 44, width: "auto" }} />
|
||||
<span className="font-display" style={{ fontWeight: 800, fontSize: 20, color: "#13324F" }}>
|
||||
Plein R
|
||||
</span>
|
||||
</Link>
|
||||
<nav style={{ display: "flex", alignItems: "center", gap: 30, flexWrap: "wrap" }}>
|
||||
<Link href="/" style={{ textDecoration: "none", color: "#6f6450", fontWeight: 500, fontSize: 15 }}>
|
||||
Accueil
|
||||
</Link>
|
||||
<Link href="/annuaire" style={{ textDecoration: "none", color: "#3c3322", fontWeight: 600, fontSize: 15 }}>
|
||||
Annuaire
|
||||
</Link>
|
||||
<Link href="/#promotions" style={{ textDecoration: "none", color: "#6f6450", fontWeight: 500, fontSize: 15 }}>
|
||||
Promotions
|
||||
</Link>
|
||||
<Link
|
||||
href="/backend"
|
||||
style={{ textDecoration: "none", color: "#fff", background: "#9a6638", fontWeight: 600, fontSize: 14.5, padding: "10px 18px", borderRadius: 999 }}
|
||||
>
|
||||
Espace adhérent
|
||||
</Link>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div className="container" style={{ paddingBottom: 56 }}>
|
||||
{/* title block */}
|
||||
<section style={{ position: "relative", padding: "14px 0 22px", overflow: "hidden" }}>
|
||||
<Sparkle color="#E0A63C" size={18} style={{ top: 22, right: 30 }} duration={3.2} />
|
||||
<Sparkle color="#6FB0C6" size={12} style={{ top: 60, right: 90 }} duration={2.7} delay={0.5} />
|
||||
<div
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 9,
|
||||
background: "#fff",
|
||||
border: "1px solid #e6dcc6",
|
||||
color: "#9a6638",
|
||||
fontSize: 12,
|
||||
letterSpacing: "0.12em",
|
||||
textTransform: "uppercase",
|
||||
fontWeight: 800,
|
||||
padding: "6px 13px",
|
||||
borderRadius: 999,
|
||||
marginBottom: 14,
|
||||
}}
|
||||
>
|
||||
<span style={{ width: 7, height: 7, borderRadius: "50%", background: "#E0A63C" }} />
|
||||
{members.length} adhérents
|
||||
</div>
|
||||
<h1 className="font-display" style={{ fontWeight: 800, fontSize: 40, letterSpacing: "-0.02em", margin: "0 0 8px", color: "#26201a" }}>
|
||||
L'annuaire des commerçants & entreprises
|
||||
</h1>
|
||||
<p style={{ margin: 0, fontSize: 16.5, color: "#6c6150", maxWidth: 560 }}>
|
||||
Trouvez un professionnel du Bassin de Pompey, découvrez sa fiche et ses bons plans.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<AnnuaireClient members={members} categories={categories} initialQuery={q ?? ""} />
|
||||
|
||||
{/* join CTA */}
|
||||
<section
|
||||
style={{
|
||||
marginTop: 34,
|
||||
position: "relative",
|
||||
background: "linear-gradient(120deg,#13324F,#1d4a72)",
|
||||
borderRadius: 20,
|
||||
padding: "34px 40px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
gap: 28,
|
||||
overflow: "hidden",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Sparkle color="#E0A63C" size={18} style={{ top: 20, right: 40 }} duration={3.2} />
|
||||
<div>
|
||||
<div style={{ fontSize: 12.5, letterSpacing: "0.12em", textTransform: "uppercase", color: "#9fc6e6", fontWeight: 700, marginBottom: 8 }}>
|
||||
Vous n'êtes pas encore référencé ?
|
||||
</div>
|
||||
<h2 className="font-display" style={{ fontWeight: 800, fontSize: 26, margin: 0, color: "#fff" }}>
|
||||
Rejoignez l'annuaire Plein R
|
||||
</h2>
|
||||
</div>
|
||||
<Link
|
||||
href="/backend"
|
||||
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>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{/* footer */}
|
||||
<footer style={{ background: "#EFE9DA", borderTop: "1px solid #e2d6bd", padding: "28px 56px" }}>
|
||||
<div className="container" style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 16, flexWrap: "wrap" }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/assets/logo.png" alt="Plein R" style={{ height: 36, width: "auto" }} />
|
||||
<span className="font-display" style={{ fontWeight: 800, fontSize: 16, color: "#13324F" }}>
|
||||
Plein R
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: "#8c8068", textAlign: "right", lineHeight: 1.7 }}>
|
||||
contact@plein-r.fr · Bassin de Pompey
|
||||
<br />
|
||||
Réseau · Rencontre · Réussite
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user