Promotions : page publique dédiée, l'accueil garde les six dernières

`/promotions` liste toutes les offres en cours ; l'accueil en montre six et
renvoie vers elle. Les deux listes partagent `PromoCard`, extrait de la page
d'accueil : un seul rendu de carte à maintenir.

- `getLivePromotions(limit)` accepte `null` pour « toutes les offres », la
  limite restant appliquée en SQL et non après coup.
- Page complète : `<h1>` unique, `<main>`, métadonnées et canonique via
  `pageMetadata`, JSON-LD `BreadcrumbList` + `ItemList`, entrée au sitemap,
  état vide qui renvoie vers l'annuaire.
- Les liens « Promotions » de l'en-tête, du pied de page et de l'espace
  adhérent visent désormais cette page. Idem pour le lien d'une publication
  réseau sans adhérent rattaché, qui pointait sur l'ancre `/#promotions`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
This commit is contained in:
Claude committed 2026-09-06 14:34:27 +00:00
1 parent cf8cf5c8e2
commit a52e13e623
10 files changed
+294 -116

No files matched your search

+12 -4
View File
@@ -72,8 +72,14 @@ export async function getCategoriesWithCounts(limit = 6) {
return rows;
}
export async function getLivePromotions(limit = 6) {
const rows = await db
/**
* Promotions visibles, de la plus récente à la plus ancienne.
*
* `limit` à `null` : toutes les offres en cours (page `/promotions`) ; sinon
* les N dernières (accueil).
*/
export async function getLivePromotions(limit: number | null = 6) {
const query = db
.select({
id: promotions.id,
title: promotions.title,
@@ -93,8 +99,10 @@ export async function getLivePromotions(limit = 6) {
.leftJoin(members, eq(promotions.memberId, members.id))
.where(VISIBLE_PROMO)
.orderBy(desc(promotions.createdAt))
.limit(limit);
return rows;
.$dynamic();
// La limite reste dans le SQL : la page « toutes les promos » est la seule à
// tout demander, l'accueil ne rapatrie que ses six cartes.
return limit === null ? query : query.limit(limit);
}
export async function getPublicMember(id: number) {
+1
View File
@@ -71,6 +71,7 @@ export const PRIVATE_PATHS = ["/backend", "/api", "/login", "/inscription"];
export const STATIC_ROUTES: { path: string; priority: number; changeFrequency: "daily" | "weekly" | "monthly" | "yearly" }[] = [
{ path: "/", priority: 1, changeFrequency: "daily" },
{ path: "/annuaire", priority: 0.9, changeFrequency: "daily" },
{ path: "/promotions", priority: 0.9, changeFrequency: "daily" },
{ path: "/association", priority: 0.8, changeFrequency: "weekly" },
{ path: "/rencontres-passees", priority: 0.6, changeFrequency: "monthly" },
{ path: "/mentions-legales", priority: 0.2, changeFrequency: "yearly" },
+1 -1
View File
@@ -49,7 +49,7 @@ export async function promoLink(promo: PromoForSharing): Promise<string | null>
if (!base) return null;
return promo.memberId
? `${base}${memberPath({ id: promo.memberId, name: promo.memberName ?? "", city: promo.memberCity })}`
: `${base}/#promotions`;
: `${base}/promotions`;
}
/** Texte du post, commun aux deux réseaux. */