mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-13 18:29:03 +02:00
Menu d'administration regroupé et version mobile du site
Le menu empilait douze liens sous un seul intitulé, avec des carrés, ronds et losanges qui ne portaient aucun sens. Sur téléphone, la barre latérale se dépliait en ligne et devenait un pavé de boutons inutilisable. Navigation - Six rubriques au lieu d'une pile : Pilotage, Vie du réseau, Rencontres, Communication, Configuration, Côté adhérent. Le quotidien est ainsi séparé de ce qu'on ne touche qu'occasionnellement. - Une icône par entrée (`BackendIcons.tsx`) à la place des formes génériques. - Le menu devient une table de données filtrée par capacité, au lieu de douze blocs JSX répétant les mêmes appels à `can()`. Mobile - Sous 1024px la barre latérale devient un tiroir : voile de fond, fermeture par Échap, par clic hors zone et à la navigation, défilement de la page bloqué, actions Voir le site / Déconnexion rapatriées dans le tiroir. - `min-height: 0` sur la zone de navigation : sans lui l'enfant flex refusait de rétrécir et la carte utilisateur recouvrait les derniers liens, interceptant les clics. - En-tête public repensé : logo toujours présent sur téléphone (seul retour à l'accueil), accès adhérent sur la même ligne, navigation répartie en dessous au lieu de se replier en escalier. - Liste des adhérents : le tableau à cinq colonnes devient une fiche par adhérent, sur deux lignes constantes, sans défilement horizontal. - Compteurs du tableau de bord en deux colonnes plutôt qu'empilés. Vérifié : aucun débordement horizontal sur les 23 pages en 320, 390 et 768 px ; tiroir et cibles tactiles testés au doigt sur toutes les interactions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
This commit is contained in:
6 files changed
+719
-256
No files matched your search
+188
-194
@@ -2,7 +2,8 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import type { ReactNode } from "react";
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { BackendIcon, type BackendIconName } from "@/components/BackendIcons";
|
||||
import { can, ROLE_LABELS } from "@/lib/rbac";
|
||||
import type { AppRole } from "@/types/next-auth";
|
||||
import { doSignOut } from "./actions";
|
||||
@@ -19,6 +20,7 @@ const TITLES: Record<string, [string, string]> = {
|
||||
"/backend/inscriptions": ["Inscriptions", "Gérer les participants aux rencontres"],
|
||||
"/backend/rencontres-passees": ["Rencontres passées", "Publier les archives et leurs photos"],
|
||||
"/backend/emails": ["Création d'e-mails", "Composer des messages aux couleurs de Plein R"],
|
||||
"/backend/reseaux": ["Réseaux sociaux", "Connecter les pages Facebook et LinkedIn"],
|
||||
"/backend/administrateurs": ["Administrateurs", "Gérer les accès à l'administration"],
|
||||
"/backend/categories": ["Catégories", "Gérer les métiers de l'annuaire"],
|
||||
"/backend/parametres": ["Paramètres du site", "Configurer l'association et les mentions légales"],
|
||||
@@ -26,6 +28,67 @@ const TITLES: Record<string, [string, string]> = {
|
||||
"/backend/changer-mot-de-passe": ["Mot de passe", "Sécurisez votre compte"],
|
||||
};
|
||||
|
||||
type Capability = Parameters<typeof can>[1];
|
||||
type BadgeKey = "inbox" | "promos";
|
||||
|
||||
type NavItem = {
|
||||
href: string;
|
||||
label: string;
|
||||
icon: BackendIconName;
|
||||
capability?: Capability;
|
||||
badge?: BadgeKey;
|
||||
/** Les fiches adhérent sont des sous-routes : l'entrée doit rester active. */
|
||||
prefix?: boolean;
|
||||
};
|
||||
|
||||
type NavSection = { label: string; items: NavItem[] };
|
||||
|
||||
/**
|
||||
* Menu groupé par usage plutôt qu'en une seule pile : on distingue le quotidien
|
||||
* (réseau, rencontres) de ce qu'on ne touche qu'occasionnellement (configuration).
|
||||
*/
|
||||
const SECTIONS: NavSection[] = [
|
||||
{
|
||||
label: "Pilotage",
|
||||
items: [{ href: "/backend", label: "Tableau de bord", icon: "dashboard", capability: "viewDashboard" }],
|
||||
},
|
||||
{
|
||||
label: "Vie du réseau",
|
||||
items: [
|
||||
{ href: "/backend/adherents", label: "Adhérents", icon: "members", capability: "manageMembers", prefix: true },
|
||||
{ href: "/backend/demandes", label: "Demandes & messages", icon: "inbox", capability: "manageMembers", badge: "inbox" },
|
||||
{ href: "/backend/promotions", label: "Promotions", icon: "promos", capability: "moderatePromos", badge: "promos" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Rencontres",
|
||||
items: [
|
||||
{ href: "/backend/rencontres", label: "Prochaines dates", icon: "meetings", capability: "manageMeetings" },
|
||||
{ href: "/backend/inscriptions", label: "Inscriptions", icon: "registrations", capability: "manageMeetings" },
|
||||
{ href: "/backend/rencontres-passees", label: "Archives & photos", icon: "gallery", capability: "manageMeetings" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Communication",
|
||||
items: [
|
||||
{ href: "/backend/emails", label: "E-mails", icon: "emails", capability: "manageEmails" },
|
||||
{ href: "/backend/reseaux", label: "Réseaux sociaux", icon: "social", capability: "manageSettings" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Configuration",
|
||||
items: [
|
||||
{ href: "/backend/categories", label: "Catégories", icon: "categories", capability: "manageCategories" },
|
||||
{ href: "/backend/parametres", label: "Paramètres du site", icon: "settings", capability: "manageSettings" },
|
||||
{ href: "/backend/administrateurs", label: "Administrateurs", icon: "admins", capability: "manageAdmins" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Côté adhérent",
|
||||
items: [{ href: "/backend/espace", label: "Mon espace", icon: "space" }],
|
||||
},
|
||||
];
|
||||
|
||||
function initialsOf(name: string) {
|
||||
return name
|
||||
.split(/\s+/)
|
||||
@@ -35,19 +98,21 @@ function initialsOf(name: string) {
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
function NavLink({
|
||||
href,
|
||||
active,
|
||||
children,
|
||||
}: {
|
||||
href: string;
|
||||
active: boolean;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
function Badge({ value }: { value: number }) {
|
||||
return (
|
||||
<Link href={href} className={`nav-btn${active ? " active" : ""}`}>
|
||||
{children}
|
||||
</Link>
|
||||
<span
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
background: "#E0A63C",
|
||||
color: "#33291D",
|
||||
fontSize: 11,
|
||||
fontWeight: 800,
|
||||
borderRadius: 999,
|
||||
padding: "1px 8px",
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,149 +128,116 @@ export function BackendShell({
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
const [title, sub] =
|
||||
TITLES[pathname] ??
|
||||
(pathname.startsWith("/backend/adherents")
|
||||
? TITLES["/backend/adherents"]
|
||||
: ["Administration", "Plein R"]);
|
||||
|
||||
const dot = (
|
||||
<span style={{ width: 18, height: 18, border: "2px solid currentColor", borderRadius: 5, display: "inline-block" }} />
|
||||
// Le tiroir se referme dès qu'on navigue.
|
||||
useEffect(() => {
|
||||
setMenuOpen(false);
|
||||
}, [pathname]);
|
||||
|
||||
// Pas de défilement de la page derrière le tiroir ouvert.
|
||||
useEffect(() => {
|
||||
if (!menuOpen) return;
|
||||
const previous = document.body.style.overflow;
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
document.body.style.overflow = previous;
|
||||
};
|
||||
}, [menuOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!menuOpen) return;
|
||||
const onKey = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") setMenuOpen(false);
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
}, [menuOpen]);
|
||||
|
||||
const badges: Record<BadgeKey, number> = { inbox: inboxCount, promos: pendingCount };
|
||||
|
||||
const sections = SECTIONS.map((section) => ({
|
||||
...section,
|
||||
items: section.items.filter((item) => !item.capability || can(user.role, item.capability)),
|
||||
})).filter((section) => section.items.length > 0);
|
||||
|
||||
const siteLink = (
|
||||
<Link href="/" className="backend-action">
|
||||
← Voir le site
|
||||
</Link>
|
||||
);
|
||||
const dotRound = (
|
||||
<span style={{ width: 18, height: 18, border: "2px solid currentColor", borderRadius: "50%", display: "inline-block" }} />
|
||||
);
|
||||
const dotDiamond = (
|
||||
<span style={{ width: 18, height: 18, border: "2px solid currentColor", borderRadius: 5, display: "inline-block", transform: "rotate(45deg)" }} />
|
||||
const signOut = (
|
||||
<form action={doSignOut}>
|
||||
<button type="submit" className="backend-action backend-action--strong">
|
||||
Déconnexion
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="backend" style={{ fontFamily: "'Public Sans',sans-serif", color: "#33291D" }}>
|
||||
{/* sidebar */}
|
||||
<aside className="sidebar">
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 11, padding: "6px 8px 22px" }}>
|
||||
{menuOpen && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Fermer le menu"
|
||||
className="sidebar-backdrop"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<aside className={`sidebar${menuOpen ? " is-open" : ""}`} id="backend-nav">
|
||||
<div className="sidebar-brand">
|
||||
<span style={{ display: "inline-flex", background: "#F6F2E8", borderRadius: 11, padding: 6 }}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/assets/logo.png" alt="Plein R" style={{ height: 34, width: "auto", display: "block" }} />
|
||||
</span>
|
||||
<div>
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<div className="font-display" style={{ fontWeight: 800, fontSize: 17, color: "#fff", lineHeight: 1 }}>
|
||||
Plein R
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: "#7f9bb4", marginTop: 3 }}>Administration</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="sidebar-close"
|
||||
aria-label="Fermer le menu"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
>
|
||||
<BackendIcon name="close" size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style={{ flex: "1 1 auto", display: "flex", flexDirection: "column", minWidth: 200 }}>
|
||||
{can(user.role, "viewDashboard") && (
|
||||
<>
|
||||
<div style={sectionLabel}>Pilotage</div>
|
||||
<NavLink href="/backend" active={pathname === "/backend"}>
|
||||
{dot}Tableau de bord
|
||||
</NavLink>
|
||||
{can(user.role, "manageMembers") && (
|
||||
<NavLink href="/backend/adherents" active={pathname.startsWith("/backend/adherents")}>
|
||||
{dotRound}Adhérents
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "manageMembers") && (
|
||||
<NavLink href="/backend/demandes" active={pathname === "/backend/demandes"}>
|
||||
{dot}Demandes & messages
|
||||
{inboxCount > 0 && (
|
||||
<span
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
background: "#E0A63C",
|
||||
color: "#33291D",
|
||||
fontSize: 11,
|
||||
fontWeight: 800,
|
||||
borderRadius: 999,
|
||||
padding: "1px 8px",
|
||||
}}
|
||||
>
|
||||
{inboxCount}
|
||||
</span>
|
||||
)}
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "moderatePromos") && (
|
||||
<NavLink href="/backend/promotions" active={pathname === "/backend/promotions"}>
|
||||
{dotDiamond}Promotions
|
||||
<span
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
background: "#E0A63C",
|
||||
color: "#33291D",
|
||||
fontSize: 11,
|
||||
fontWeight: 800,
|
||||
borderRadius: 999,
|
||||
padding: "1px 8px",
|
||||
}}
|
||||
<nav className="sidebar-nav">
|
||||
{sections.map((section) => (
|
||||
<div key={section.label}>
|
||||
<div className="sidebar-section">{section.label}</div>
|
||||
{section.items.map((item) => {
|
||||
const active = item.prefix ? pathname.startsWith(item.href) : pathname === item.href;
|
||||
const badge = item.badge ? badges[item.badge] : 0;
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={`nav-btn${active ? " active" : ""}`}
|
||||
aria-current={active ? "page" : undefined}
|
||||
>
|
||||
{pendingCount}
|
||||
</span>
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "manageMeetings") && (
|
||||
<NavLink href="/backend/rencontres" active={pathname === "/backend/rencontres"}>
|
||||
{dotRound}Rencontres
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "manageMeetings") && (
|
||||
<NavLink href="/backend/inscriptions" active={pathname === "/backend/inscriptions"}>
|
||||
{dot}Inscriptions
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "manageMeetings") && (
|
||||
<NavLink href="/backend/rencontres-passees" active={pathname === "/backend/rencontres-passees"}>
|
||||
{dotDiamond}Rencontres passées
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "manageEmails") && (
|
||||
<NavLink href="/backend/emails" active={pathname === "/backend/emails"}>
|
||||
{dot}E-mails
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "manageCategories") && (
|
||||
<NavLink href="/backend/categories" active={pathname === "/backend/categories"}>
|
||||
{dotDiamond}Catégories
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "manageSettings") && (
|
||||
<NavLink href="/backend/reseaux" active={pathname === "/backend/reseaux"}>
|
||||
{dot}Réseaux sociaux
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "manageSettings") && (
|
||||
<NavLink href="/backend/parametres" active={pathname === "/backend/parametres"}>
|
||||
{dot}Paramètres
|
||||
</NavLink>
|
||||
)}
|
||||
{can(user.role, "manageAdmins") && (
|
||||
<NavLink href="/backend/administrateurs" active={pathname === "/backend/administrateurs"}>
|
||||
{dot}Administrateurs
|
||||
</NavLink>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<BackendIcon name={item.icon} />
|
||||
{item.label}
|
||||
{item.badge && badge > 0 && <Badge value={badge} />}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div style={sectionLabel}>Côté adhérent</div>
|
||||
<NavLink href="/backend/espace" active={pathname === "/backend/espace"}>
|
||||
{dotRound}Mon espace
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: "auto",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 11,
|
||||
background: "rgba(255,255,255,0.06)",
|
||||
borderRadius: 12,
|
||||
padding: "11px 12px",
|
||||
}}
|
||||
>
|
||||
<div className="sidebar-user">
|
||||
<span
|
||||
className="font-display"
|
||||
style={{
|
||||
@@ -231,76 +263,38 @@ export function BackendShell({
|
||||
<div style={{ fontSize: 11, color: "#7f9bb4" }}>{ROLE_LABELS[user.role]}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sur téléphone, l'entête n'a pas la place : les actions vivent ici. */}
|
||||
<div className="sidebar-actions">
|
||||
{siteLink}
|
||||
{signOut}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* main */}
|
||||
<main style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column" }}>
|
||||
<header
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "18px 32px",
|
||||
background: "#F6F2E8",
|
||||
borderBottom: "1px solid #e2d6bd",
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
zIndex: 5,
|
||||
gap: 16,
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<h1 className="font-display" style={{ fontWeight: 800, fontSize: 22, margin: 0, color: "#26201a" }}>
|
||||
{title}
|
||||
</h1>
|
||||
<div style={{ fontSize: 13, color: "#9a8d72", marginTop: 2 }}>{sub}</div>
|
||||
<header className="backend-header">
|
||||
<button
|
||||
type="button"
|
||||
className="backend-burger"
|
||||
aria-label="Ouvrir le menu"
|
||||
aria-expanded={menuOpen}
|
||||
aria-controls="backend-nav"
|
||||
onClick={() => setMenuOpen(true)}
|
||||
>
|
||||
<BackendIcon name="menu" size={22} />
|
||||
</button>
|
||||
<div style={{ minWidth: 0, flex: 1 }}>
|
||||
<h1 className="font-display backend-header__title">{title}</h1>
|
||||
<div className="backend-header__sub">{sub}</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
||||
<Link
|
||||
href="/"
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
fontSize: 13.5,
|
||||
fontWeight: 600,
|
||||
color: "#6f6450",
|
||||
border: "1px solid #d8cdb4",
|
||||
padding: "9px 15px",
|
||||
borderRadius: 10,
|
||||
}}
|
||||
>
|
||||
← Voir le site
|
||||
</Link>
|
||||
<form action={doSignOut}>
|
||||
<button
|
||||
type="submit"
|
||||
style={{
|
||||
background: "#fff",
|
||||
border: "1px solid #d8cdb4",
|
||||
color: "#9a6638",
|
||||
fontSize: 13.5,
|
||||
fontWeight: 700,
|
||||
padding: "9px 15px",
|
||||
borderRadius: 10,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Déconnexion
|
||||
</button>
|
||||
</form>
|
||||
<div className="backend-header__actions">
|
||||
{siteLink}
|
||||
{signOut}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div style={{ padding: "28px 32px 44px", flex: 1 }}>{children}</div>
|
||||
<div className="backend-content">{children}</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const sectionLabel = {
|
||||
fontSize: 10.5,
|
||||
letterSpacing: "0.14em",
|
||||
textTransform: "uppercase" as const,
|
||||
color: "#5f7d97",
|
||||
fontWeight: 700,
|
||||
padding: "20px 10px 8px",
|
||||
};
|
||||
@@ -65,8 +65,6 @@ export default async function AdherentsPage({
|
||||
redirect("/backend/adherents");
|
||||
}
|
||||
|
||||
const COLS = "1.6fr 1fr 1.2fr 0.9fr 0.8fr";
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
|
||||
{missingAccounts > 0 && (
|
||||
@@ -89,8 +87,7 @@ export default async function AdherentsPage({
|
||||
|
||||
<AddMemberPanel categories={cats} />
|
||||
|
||||
<div className="table-scroll">
|
||||
<div style={{ background: "#fff", border: "1px solid #e6dcc6", borderRadius: 16, overflow: "hidden", minWidth: 560 }}>
|
||||
<div style={{ background: "#fff", border: "1px solid #e6dcc6", borderRadius: 16, overflow: "hidden" }}>
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "18px 22px", borderBottom: "1px solid #f0e8d6", gap: 12, flexWrap: "wrap" }}>
|
||||
<form style={{ display: "flex", alignItems: "center", gap: 10, background: "#faf7ef", border: "1px solid #e6dcc6", borderRadius: 10, padding: "8px 14px" }}>
|
||||
<span style={{ width: 9, height: 9, border: "2px solid #9a8d72", borderRadius: "50%" }} />
|
||||
@@ -104,9 +101,8 @@ export default async function AdherentsPage({
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="member-row member-head"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: COLS,
|
||||
padding: "13px 22px",
|
||||
background: "#faf7ef",
|
||||
borderBottom: "1px solid #f0e8d6",
|
||||
@@ -131,24 +127,28 @@ export default async function AdherentsPage({
|
||||
{rows.map((m) => {
|
||||
const active = m.status === "active";
|
||||
return (
|
||||
<div key={m.id} style={{ display: "grid", gridTemplateColumns: COLS, alignItems: "center", padding: "15px 22px", borderBottom: "1px solid #f4eede" }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
||||
<div key={m.id} className="member-row" style={{ padding: "15px 22px", borderBottom: "1px solid #f4eede" }}>
|
||||
<div className="member-row__identity">
|
||||
<span className="font-display" style={{ width: 38, height: 38, borderRadius: 10, background: "#f0e8d6", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 800, color: "#9a8d72", fontSize: 14, flexShrink: 0 }}>
|
||||
{initialsOf(m.name)}
|
||||
</span>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div style={{ fontSize: 14.5, fontWeight: 700, color: "#26201a" }}>{m.name}</div>
|
||||
<div style={{ fontSize: 12, color: "#a99c82" }}>{m.email}</div>
|
||||
<div className="member-row__mail" style={{ fontSize: 12, color: "#a99c82" }}>{m.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 13.5, color: "#6c6150" }}>{m.categoryLabel ?? "—"}</div>
|
||||
<div style={{ fontSize: 13.5, color: "#6c6150" }}>{m.city ?? "—"}</div>
|
||||
<div className="member-row__meta" style={{ fontSize: 13.5, color: "#6c6150" }}>
|
||||
{m.categoryLabel ?? "—"}
|
||||
</div>
|
||||
<div className="member-row__meta" style={{ fontSize: 13.5, color: "#6c6150" }}>
|
||||
{m.city ?? "—"}
|
||||
</div>
|
||||
<div>
|
||||
<span style={{ display: "inline-block", fontSize: 11.5, fontWeight: 700, padding: "4px 11px", borderRadius: 999, background: active ? "#e6f4ec" : "#fbeede", color: active ? "#1f8a5b" : "#9a6638" }}>
|
||||
{active ? "Actif" : "En attente"}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ textAlign: "right" }}>
|
||||
<div className="member-row__action">
|
||||
<Link href={`/backend/adherents/${m.id}`} style={{ textDecoration: "none", color: "#2C6FB3", fontWeight: 700, fontSize: 13 }}>
|
||||
Modifier
|
||||
</Link>
|
||||
@@ -157,7 +157,6 @@ export default async function AdherentsPage({
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -23,12 +23,12 @@ function StatCard({
|
||||
hintColor?: string;
|
||||
}) {
|
||||
return (
|
||||
<div style={{ background: "#fff", border: "1px solid #e6dcc6", borderRadius: 16, padding: 20 }}>
|
||||
<div style={{ fontSize: 13, color: "#9a8d72", marginBottom: 8 }}>{label}</div>
|
||||
<div className="font-display" style={{ fontWeight: 800, fontSize: 32, color: valueColor }}>
|
||||
<div className="stat-card" style={{ background: "#fff", border: "1px solid #e6dcc6", borderRadius: 16 }}>
|
||||
<div className="stat-card__label">{label}</div>
|
||||
<div className="font-display stat-card__value" style={{ color: valueColor }}>
|
||||
{value}
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: hintColor, marginTop: 6, fontWeight: 600 }}>{hint}</div>
|
||||
<div className="stat-card__hint" style={{ color: hintColor }}>{hint}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+362
-7
@@ -237,6 +237,87 @@ button {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
/* ---- En-tête public ---- */
|
||||
.site-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 18px clamp(16px, 5vw, 56px);
|
||||
}
|
||||
.site-header__brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-right: auto;
|
||||
}
|
||||
.site-header__brand.is-desktop-hidden {
|
||||
display: none;
|
||||
}
|
||||
.site-header__brand img {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: 46px;
|
||||
}
|
||||
.site-header__nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.site-header__link {
|
||||
color: var(--muted);
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.site-header__link.is-active {
|
||||
color: var(--ink-soft);
|
||||
font-weight: 600;
|
||||
}
|
||||
.site-header__cta {
|
||||
padding: 10px 18px;
|
||||
border-radius: 999px;
|
||||
background: var(--brown);
|
||||
color: #fff;
|
||||
font-size: 14.5px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
/* Ligne 1 : logo + accès adhérent. Ligne 2 : la navigation, répartie sur
|
||||
toute la largeur plutôt que repliée en escalier. */
|
||||
.site-header {
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 12px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
.site-header__brand,
|
||||
.site-header__brand.is-desktop-hidden {
|
||||
display: inline-flex;
|
||||
}
|
||||
.site-header__brand img {
|
||||
height: 38px;
|
||||
}
|
||||
.site-header__nav {
|
||||
order: 3;
|
||||
flex: 1 1 100%;
|
||||
margin-left: 0;
|
||||
gap: 6px;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid #e2d6bd;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.site-header__link {
|
||||
font-size: 14px;
|
||||
}
|
||||
.site-header__cta {
|
||||
padding: 9px 15px;
|
||||
font-size: 13.5px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Public footer & legal pages ---- */
|
||||
.footer-grid {
|
||||
display: grid;
|
||||
@@ -333,10 +414,15 @@ button {
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.grid-6,
|
||||
.grid-4,
|
||||
.grid-3 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
/* Les compteurs du tableau de bord restent sur deux colonnes : empilés, ils
|
||||
imposaient un écran entier de défilement pour quatre chiffres. */
|
||||
.grid-4 {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
.container {
|
||||
padding: 0 16px;
|
||||
}
|
||||
@@ -363,6 +449,98 @@ img {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* ---- Compteurs du tableau de bord ---- */
|
||||
.stat-card {
|
||||
padding: 20px;
|
||||
}
|
||||
.stat-card__label {
|
||||
margin-bottom: 8px;
|
||||
color: var(--muted-3);
|
||||
font-size: 13px;
|
||||
}
|
||||
.stat-card__value {
|
||||
font-size: 32px;
|
||||
font-weight: 800;
|
||||
}
|
||||
.stat-card__hint {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.stat-card {
|
||||
padding: 14px;
|
||||
}
|
||||
.stat-card__label {
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.stat-card__value {
|
||||
font-size: 26px;
|
||||
}
|
||||
.stat-card__hint {
|
||||
margin-top: 3px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Liste des adhérents : tableau sur grand écran, fiches sur téléphone ---- */
|
||||
.member-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.6fr 1fr 1.2fr 0.9fr 0.8fr;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.member-row__identity {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
.member-row__mail {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.member-row__action {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
/* Un tableau à 5 colonnes est illisible sur un téléphone : chaque adhérent
|
||||
devient une fiche qui se replie, sans défilement horizontal. */
|
||||
.member-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 9px 10px;
|
||||
}
|
||||
/* Doit rester après `.member-row` : même spécificité, c'est l'ordre qui tranche. */
|
||||
.member-row.member-head {
|
||||
display: none;
|
||||
}
|
||||
.member-row__identity {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
/* Base 0 : les métadonnées ne réclament pas leur largeur de contenu, donc la
|
||||
ligne ne se replie pas et c'est le texte qui se rogne. Chaque fiche tient
|
||||
ainsi sur deux lignes, toujours de la même façon. */
|
||||
.member-row__meta {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
font-size: 13px !important;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.member-row__action {
|
||||
flex: 0 0 auto;
|
||||
margin-left: auto;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Backend layout ---- */
|
||||
.backend {
|
||||
display: flex;
|
||||
@@ -381,6 +559,61 @@ img {
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
.sidebar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
padding: 6px 8px 18px;
|
||||
}
|
||||
.sidebar-close {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/* 40px : cible tactile confortable au pouce. */
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex-shrink: 0;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: #cfe0ee;
|
||||
cursor: pointer;
|
||||
}
|
||||
.sidebar-nav {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
/* Sans min-height:0 un enfant flex refuse de rétrécir : la zone débordait
|
||||
sous la carte utilisateur, qui interceptait les clics sur les derniers
|
||||
liens au lieu de laisser défiler le menu. */
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.sidebar-section {
|
||||
padding: 16px 10px 7px;
|
||||
color: #5f7d97;
|
||||
font-size: 10.5px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.sidebar-nav > div:first-child .sidebar-section {
|
||||
padding-top: 4px;
|
||||
}
|
||||
.sidebar-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
margin-top: 14px;
|
||||
padding: 11px 12px;
|
||||
border-radius: 12px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
/* Sur grand écran les actions vivent dans l'entête, pas dans la barre. */
|
||||
.sidebar-actions {
|
||||
display: none;
|
||||
gap: 9px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.nav-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -409,6 +642,75 @@ img {
|
||||
color: #fff;
|
||||
box-shadow: inset 3px 0 0 var(--gold);
|
||||
}
|
||||
.sidebar-backdrop {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 40;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: rgba(19, 50, 79, 0.5);
|
||||
}
|
||||
.backend-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 18px 32px;
|
||||
background: var(--cream);
|
||||
border-bottom: 1px solid #e2d6bd;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 5;
|
||||
}
|
||||
.backend-header__title {
|
||||
margin: 0;
|
||||
color: var(--ink-strong);
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
}
|
||||
.backend-header__sub {
|
||||
margin-top: 2px;
|
||||
color: var(--muted-3);
|
||||
font-size: 13px;
|
||||
}
|
||||
.backend-header__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.backend-burger {
|
||||
display: none;
|
||||
padding: 9px;
|
||||
border: 1px solid #d8cdb4;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
color: var(--ink-soft);
|
||||
cursor: pointer;
|
||||
}
|
||||
.backend-action {
|
||||
display: inline-block;
|
||||
border: 1px solid #d8cdb4;
|
||||
border-radius: 10px;
|
||||
padding: 9px 15px;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
font-family: inherit;
|
||||
font-size: 13.5px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
.backend-action--strong {
|
||||
background: #fff;
|
||||
color: var(--brown);
|
||||
font-weight: 700;
|
||||
}
|
||||
.backend-content {
|
||||
flex: 1;
|
||||
padding: 28px 32px 44px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
@@ -419,17 +721,70 @@ img {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
@media (max-width: 860px) {
|
||||
/* Sous 1024px la barre latérale devient un tiroir : l'empiler en ligne rendait
|
||||
la navigation illisible sur téléphone. */
|
||||
@media (max-width: 1024px) {
|
||||
.backend {
|
||||
flex-direction: column;
|
||||
}
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 50;
|
||||
width: min(304px, 86vw);
|
||||
height: 100dvh;
|
||||
padding: 18px 14px;
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.25s ease;
|
||||
box-shadow: 0 0 40px rgba(19, 50, 79, 0.35);
|
||||
}
|
||||
.sidebar.is-open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
.sidebar-close,
|
||||
.sidebar-actions {
|
||||
display: flex;
|
||||
}
|
||||
.sidebar-actions .backend-action {
|
||||
flex: 1;
|
||||
border-color: rgba(255, 255, 255, 0.22);
|
||||
color: #cfe0ee;
|
||||
text-align: center;
|
||||
}
|
||||
.sidebar-actions form {
|
||||
flex: 1;
|
||||
}
|
||||
.sidebar-actions .backend-action--strong {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: static;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
background: var(--gold);
|
||||
border-color: var(--gold);
|
||||
color: var(--ink);
|
||||
}
|
||||
.sidebar-backdrop {
|
||||
display: block;
|
||||
}
|
||||
.backend-burger {
|
||||
display: inline-flex;
|
||||
}
|
||||
.backend-header__actions {
|
||||
display: none;
|
||||
}
|
||||
.backend-header {
|
||||
padding: 13px 16px;
|
||||
gap: 12px;
|
||||
}
|
||||
.backend-header__title {
|
||||
font-size: 18px;
|
||||
}
|
||||
.backend-header__sub {
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.backend-content {
|
||||
padding: 18px 16px 40px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* Icônes de la navigation d'administration. Les anciens carrés / ronds /
|
||||
* losanges ne portaient aucun sens : une icône par rubrique rend le menu
|
||||
* lisible d'un coup d'œil.
|
||||
*/
|
||||
export type BackendIconName =
|
||||
| "dashboard"
|
||||
| "members"
|
||||
| "inbox"
|
||||
| "promos"
|
||||
| "meetings"
|
||||
| "registrations"
|
||||
| "gallery"
|
||||
| "emails"
|
||||
| "social"
|
||||
| "categories"
|
||||
| "settings"
|
||||
| "admins"
|
||||
| "space"
|
||||
| "menu"
|
||||
| "close";
|
||||
|
||||
const PATHS: Record<BackendIconName, ReactNode> = {
|
||||
dashboard: (
|
||||
<>
|
||||
<rect x="3" y="3" width="7.5" height="7.5" rx="2" />
|
||||
<rect x="13.5" y="3" width="7.5" height="7.5" rx="2" />
|
||||
<rect x="3" y="13.5" width="7.5" height="7.5" rx="2" />
|
||||
<rect x="13.5" y="13.5" width="7.5" height="7.5" rx="2" />
|
||||
</>
|
||||
),
|
||||
members: (
|
||||
<>
|
||||
<circle cx="9" cy="8" r="3.4" />
|
||||
<path d="M2.8 20c0-3.4 2.8-5.6 6.2-5.6s6.2 2.2 6.2 5.6" />
|
||||
<path d="M16.4 5.2a3.2 3.2 0 0 1 0 6M18 14.8c2 .7 3.2 2.4 3.2 5.2" />
|
||||
</>
|
||||
),
|
||||
inbox: (
|
||||
<>
|
||||
<rect x="2.5" y="4.5" width="19" height="15" rx="2.5" />
|
||||
<path d="m4 6.5 8 6 8-6" />
|
||||
</>
|
||||
),
|
||||
promos: (
|
||||
<>
|
||||
<path d="M11.6 2.8H20a1.2 1.2 0 0 1 1.2 1.2v8.4a1.6 1.6 0 0 1-.5 1.1l-7.4 7.4a1.6 1.6 0 0 1-2.3 0l-7-7a1.6 1.6 0 0 1 0-2.3l7.4-7.4a1.6 1.6 0 0 1 1.1-.5Z" />
|
||||
<circle cx="16.6" cy="7.4" r="1.4" />
|
||||
</>
|
||||
),
|
||||
meetings: (
|
||||
<>
|
||||
<rect x="3" y="5" width="18" height="16" rx="2.5" />
|
||||
<path d="M3 9.5h18M8 3v4M16 3v4" />
|
||||
</>
|
||||
),
|
||||
registrations: (
|
||||
<>
|
||||
<path d="M8 4.5H6.5A2.5 2.5 0 0 0 4 7v12a2.5 2.5 0 0 0 2.5 2.5h11A2.5 2.5 0 0 0 20 19V7a2.5 2.5 0 0 0-2.5-2.5H16" />
|
||||
<rect x="8" y="2.5" width="8" height="4" rx="1.4" />
|
||||
<path d="m8.6 12.4 1.7 1.7 3.4-3.4M8.6 17.2h6.8" />
|
||||
</>
|
||||
),
|
||||
gallery: (
|
||||
<>
|
||||
<rect x="3" y="4.5" width="18" height="15" rx="2.5" />
|
||||
<circle cx="8.6" cy="9.6" r="1.7" />
|
||||
<path d="m4 17 4.8-4.4a1.8 1.8 0 0 1 2.4 0L16 17M14 14.4l1.6-1.5a1.8 1.8 0 0 1 2.4 0L21 15.6" />
|
||||
</>
|
||||
),
|
||||
emails: (
|
||||
<>
|
||||
<path d="M20.5 3.5 10.8 13.2M20.5 3.5 14.3 21l-3.5-7.8L3 9.7l17.5-6.2Z" />
|
||||
</>
|
||||
),
|
||||
social: (
|
||||
<>
|
||||
<circle cx="18" cy="5.4" r="2.9" />
|
||||
<circle cx="6" cy="12" r="2.9" />
|
||||
<circle cx="18" cy="18.6" r="2.9" />
|
||||
<path d="m8.5 10.6 7-3.8M8.5 13.4l7 3.8" />
|
||||
</>
|
||||
),
|
||||
categories: (
|
||||
<>
|
||||
<rect x="3" y="3.5" width="7.5" height="7.5" rx="2" />
|
||||
<rect x="3" y="13" width="7.5" height="7.5" rx="2" />
|
||||
<rect x="13.5" y="13" width="7.5" height="7.5" rx="2" />
|
||||
<circle cx="17.2" cy="7.2" r="3.8" />
|
||||
</>
|
||||
),
|
||||
settings: (
|
||||
<>
|
||||
<path d="M4 7h10M18 7h2M4 17h2M10 17h10" />
|
||||
<circle cx="16" cy="7" r="2.4" />
|
||||
<circle cx="8" cy="17" r="2.4" />
|
||||
</>
|
||||
),
|
||||
admins: (
|
||||
<>
|
||||
<path d="M12 2.6 4.5 5.8V11c0 4.7 3.1 8.6 7.5 10.4 4.4-1.8 7.5-5.7 7.5-10.4V5.8L12 2.6Z" />
|
||||
<path d="m8.8 11.8 2.2 2.2 4.2-4.2" />
|
||||
</>
|
||||
),
|
||||
space: (
|
||||
<>
|
||||
<circle cx="12" cy="8.2" r="3.6" />
|
||||
<path d="M4.8 20.4c0-3.7 3.2-6 7.2-6s7.2 2.3 7.2 6" />
|
||||
</>
|
||||
),
|
||||
menu: <path d="M4 7h16M4 12h16M4 17h16" />,
|
||||
close: <path d="m6 6 12 12M18 6 6 18" />,
|
||||
};
|
||||
|
||||
export function BackendIcon({ name, size = 18 }: { name: BackendIconName; size?: number }) {
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
viewBox="0 0 24 24"
|
||||
width={size}
|
||||
height={size}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.7"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
style={{ display: "block", flexShrink: 0 }}
|
||||
>
|
||||
{PATHS[name]}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -9,56 +9,37 @@ const NAV = [
|
||||
|
||||
export function SiteHeader({ active, logo = false }: { active?: string; logo?: boolean }) {
|
||||
return (
|
||||
<header
|
||||
className="container"
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: logo ? "space-between" : "flex-end",
|
||||
padding: "18px clamp(16px, 5vw, 56px)",
|
||||
gap: 20,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
{logo && (
|
||||
<Link href="/" aria-label="Plein R — accueil" style={{ display: "inline-flex", alignItems: "center" }}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/assets/logo.png" alt="Plein R" style={{ height: 46, width: "auto", display: "block" }} />
|
||||
</Link>
|
||||
)}
|
||||
<nav style={{ display: "flex", alignItems: "center", gap: 30, flexWrap: "wrap" }}>
|
||||
<header className="container site-header">
|
||||
{/* Sur téléphone le logo est toujours visible : c'est le seul retour à
|
||||
l'accueil, la page d'accueil ne l'affichant qu'en grand dans le hero. */}
|
||||
<Link
|
||||
href="/"
|
||||
aria-label="Plein R — accueil"
|
||||
className={`site-header__brand${logo ? "" : " is-desktop-hidden"}`}
|
||||
>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src="/assets/logo.png" alt="Plein R" />
|
||||
</Link>
|
||||
|
||||
<nav className="site-header__nav">
|
||||
{NAV.map((n) => {
|
||||
const isActive = active === n.key;
|
||||
return (
|
||||
<Link
|
||||
key={n.key}
|
||||
href={n.href}
|
||||
style={{
|
||||
textDecoration: "none",
|
||||
color: isActive ? "#3c3322" : "#6f6450",
|
||||
fontWeight: isActive ? 600 : 500,
|
||||
fontSize: 15,
|
||||
}}
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
className={`site-header__link${isActive ? " is-active" : ""}`}
|
||||
>
|
||||
{n.label}
|
||||
</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>
|
||||
|
||||
<Link href="/backend" className="site-header__cta">
|
||||
Espace adhérent
|
||||
</Link>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user