mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-22 06:38:05 +02:00
- Fiches adhérents : URL canonique /adherents/12-au-bon-pain-frouard (memberPath). L'identifiant en tête suffit ; /adherents/12 et tout slug périmé sont redirigés de façon permanente vers la forme canonique, donc les anciens liens et les partages sociaux restent valides. Tous les liens (accueil, carrousel, annuaire, JSON-LD, sitemap, publications Facebook / LinkedIn) passent par memberPath. - Pages métier /annuaire/[categorie] : une page indexable par catégorie avec titre, introduction (effectif, communes), grille d'adhérents, fil d'Ariane et ItemList. Sans adhérent : noindex et hors sitemap. Section « Parcourir par métier » sur l'annuaire et « Autres métiers » sur chaque page pour le maillage interne. - Composants partagés MemberCard et CategoryLinks ; slugify déplacé dans src/lib/slug.ts. - Backend : conseils de rédaction sous les champs description et tags des formulaires adhérent (espace adhérent et fiche staff). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDbKbJrPWweXXYSxA8MXW7
54 lines
1.9 KiB
TypeScript
54 lines
1.9 KiB
TypeScript
import type { MetadataRoute } from "next";
|
|
import { asc, eq } from "drizzle-orm";
|
|
import { db } from "@/db";
|
|
import { members } from "@/db/schema";
|
|
import { getCategoriesInUse } from "@/lib/queries";
|
|
import { STATIC_ROUTES, absoluteUrl, categoryPath, memberPath } from "@/lib/seo";
|
|
import { publicBaseUrl } from "@/lib/seo-server";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
/** /sitemap.xml : pages publiques + une entrée par adhérent actif. */
|
|
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
const base = await publicBaseUrl();
|
|
|
|
// Pas de `lastModified` : sans date de mise à jour fiable en base, mieux vaut
|
|
// l'omettre qu'annoncer une valeur que les moteurs finiraient par ignorer.
|
|
const statics: MetadataRoute.Sitemap = STATIC_ROUTES.map((route) => ({
|
|
url: absoluteUrl(base, route.path),
|
|
changeFrequency: route.changeFrequency,
|
|
priority: route.priority,
|
|
}));
|
|
|
|
let rows: { id: number; name: string; city: string | null }[] = [];
|
|
let categoriesInUse: { slug: string }[] = [];
|
|
try {
|
|
[rows, categoriesInUse] = await Promise.all([
|
|
db
|
|
.select({ id: members.id, name: members.name, city: members.city })
|
|
.from(members)
|
|
.where(eq(members.status, "active"))
|
|
.orderBy(asc(members.id)),
|
|
getCategoriesInUse(),
|
|
]);
|
|
} catch {
|
|
// Base indisponible : on livre au moins les pages statiques.
|
|
}
|
|
|
|
// Pages métier : seulement celles qui ont au moins un adhérent (les autres
|
|
// sont en noindex, une page vide n'apporte rien aux moteurs).
|
|
const metiers: MetadataRoute.Sitemap = categoriesInUse.map((c) => ({
|
|
url: absoluteUrl(base, categoryPath(c.slug)),
|
|
changeFrequency: "weekly",
|
|
priority: 0.8,
|
|
}));
|
|
|
|
const fiches: MetadataRoute.Sitemap = rows.map((m) => ({
|
|
url: absoluteUrl(base, memberPath(m)),
|
|
changeFrequency: "weekly",
|
|
priority: 0.7,
|
|
}));
|
|
|
|
return [...statics, ...metiers, ...fiches];
|
|
}
|