mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-22 06:38:05 +02:00
L'association pouvait publier des promotions et des rencontres, mais rien à destination de ses seuls adhérents connectés. Un troisième onglet, placé en tête de l'espace, porte désormais ce que publie le bureau : annonces, convocations, rappels. Côté rédaction, `/backend/informations` (capacité `manageInformations`, administrateur et modérateur) : titre, texte balisé avec barre de mise en forme et aperçu, image de couverture, brouillon puis publication, épinglage — une seule information en tête à la fois, appliqué dans l'action plutôt que par un index conditionnel. Côté lecture, `information_reads` porte une ligne par lecture plutôt qu'une date « vu jusqu'ici » sur l'utilisateur : c'est ce qui permet à la fois la pastille « Nouveau » par information et le « lue par 12 / 40 » du back-office. Le marquage passe par une action appelée après affichage : la pastille de l'onglet et celle de la barre latérale sont calculées par deux composants serveur distincts, dont l'ordre de rendu n'est pas garanti, et se seraient contredites sur la même page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014QsLjRAnuLwivqxCbM4WeP
71 lines
2.8 KiB
TypeScript
71 lines
2.8 KiB
TypeScript
import Link from "next/link";
|
|
|
|
function initialsOf(name: string) {
|
|
return name.split(/\s+/).map((w) => w[0]).join("").slice(0, 2).toUpperCase();
|
|
}
|
|
|
|
const TABS = [
|
|
{ key: "informations", href: "/backend/espace/informations", label: "Informations" },
|
|
{ key: "profil", href: "/backend/espace", label: "Mon profil" },
|
|
{ key: "promotions", href: "/backend/espace/promotions", label: "Mes promotions" },
|
|
] as const;
|
|
|
|
export type EspaceTab = (typeof TABS)[number]["key"];
|
|
|
|
/**
|
|
* Bandeau de l'espace adhérent et ses onglets. Trois pages distinctes pour que
|
|
* les annonces de l'association, la fiche publique et la vie des promotions ne
|
|
* se mélangent pas dans une seule longue page.
|
|
*
|
|
* Les compteurs sont passés par chaque page : sans cela ils disparaîtraient
|
|
* en changeant d'onglet.
|
|
*/
|
|
export function EspaceHeader({
|
|
memberName,
|
|
subtitle,
|
|
active,
|
|
publicPath,
|
|
promoBadge,
|
|
infoBadge,
|
|
}: {
|
|
memberName: string;
|
|
subtitle: string;
|
|
active: EspaceTab;
|
|
publicPath?: string | null;
|
|
/** Nombre de promotions en ligne, affiché sur l'onglet. */
|
|
promoBadge?: number;
|
|
/** Nombre d'informations non lues. */
|
|
infoBadge?: number;
|
|
}) {
|
|
return (
|
|
<div style={{ marginBottom: 22 }}>
|
|
<div style={{ background: "#13324F", borderRadius: "16px 16px 0 0", padding: "20px 24px", display: "flex", alignItems: "center", gap: 15, flexWrap: "wrap" }}>
|
|
<span className="font-display" style={{ width: 46, height: 46, borderRadius: 12, background: "#E0A63C", color: "#33291D", display: "flex", alignItems: "center", justifyContent: "center", fontWeight: 800, fontSize: 16, flexShrink: 0 }}>
|
|
{initialsOf(memberName)}
|
|
</span>
|
|
<div style={{ flex: 1, minWidth: 200 }}>
|
|
<div className="font-display" style={{ fontWeight: 700, fontSize: 18, color: "#fff" }}>{memberName}</div>
|
|
<div style={{ fontSize: 13, color: "#9bb6cd" }}>{subtitle}</div>
|
|
</div>
|
|
{publicPath && (
|
|
<Link href={publicPath} target="_blank" style={{ color: "#fff", border: "1px solid rgba(255,255,255,0.35)", borderRadius: 10, padding: "8px 14px", fontSize: 13, fontWeight: 700, textDecoration: "none", whiteSpace: "nowrap" }}>
|
|
Voir ma fiche publique ↗
|
|
</Link>
|
|
)}
|
|
</div>
|
|
<nav className="espace-tabs" aria-label="Sections de l'espace adhérent">
|
|
{TABS.map((tab) => {
|
|
const isActive = tab.key === active;
|
|
const badge = tab.key === "promotions" ? promoBadge : tab.key === "informations" ? infoBadge : 0;
|
|
return (
|
|
<Link key={tab.key} href={tab.href} className={`espace-tab${isActive ? " active" : ""}`} aria-current={isActive ? "page" : undefined}>
|
|
{tab.label}
|
|
{badge ? <span className="espace-tab__badge">{badge}</span> : null}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
</div>
|
|
);
|
|
}
|