Bloc « Informations pratiques » (fiche adhérent) - Suppression des dégradés hors palette (radial doré de la section, dégradés crème/bleu des deux sous-cartes) au profit du vocabulaire visuel du site : carte blanche bordée, sous-cartes plates sur le fond de champ, variables CSS. - La pastille « Préparer votre visite » devient une puce arrondie identique aux badges du reste du site ; le chip d'ouverture reprend les teintes de statut. - Les glyphes texte ◷ et ↗ sont remplacés par des icônes SVG cohérentes. Suspension des promotions - Nouveau statut `suspended` + colonnes `suspended_by` / `suspended_by_id` / `suspended_at`. - L'adhérent suspend et réactive ses propres promotions depuis Mon espace ; l'association suspend et réactive n'importe laquelle depuis le backoffice. - Une suspension décidée par l'association ne peut pas être levée par l'adhérent : le motif est affiché dans son espace. - Les lectures publiques filtrant déjà sur `live`, une promo suspendue quitte immédiatement l'accueil, l'annuaire et la fiche. Publication Facebook / LinkedIn - `src/lib/social.ts` : publication d'une promo en ligne sur la page Facebook (Graph API, upload multipart car les images sont en data-URI) et sur la page LinkedIn (Posts API + Images API en trois étapes). - Jetons d'accès exclusivement en variables d'environnement ; un réseau non configuré masque son bouton. - Table `social_posts` : chaque tentative est tracée, un échec est rattrapé et affiché sur la carte de la promo sans interrompre le backoffice. - Nouvelle capacité RBAC `publishSocial` (admin + modérateur). Liens sociaux publics - Réglages `association_facebook` / `association_linkedin` éditables dans Paramètres, préremplis avec les pages de l'association. - Section « Plein R sur les réseaux » sur l'accueil et icônes dans le pied de page, masquées si le réglage est vide. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
3.3 KiB
CLAUDE.md
Guidance for working in this repository.
What this is
Plein R — website for an association of merchants/businesses in the Bassin de Pompey. A single Next.js application serving a public Accueil page and an authenticated, role-based Backend (admin + member space). Postgres is an external container.
Stack
- Next.js 15 (App Router, TypeScript,
output: "standalone") - PostgreSQL 16 + Drizzle ORM (
src/db/schema.ts) withdrizzle-kitmigrations - Auth.js v5 (credentials provider, JWT sessions) — RBAC in
src/lib/rbac.ts - One app container + one Postgres container (
docker-compose.yml)
Commands
npm run dev # dev server
npm run build # production build
npm run db:generate # regenerate SQL after editing src/db/schema.ts
npm run db:migrate # apply migrations
npm run db:seed # seed demo data
docker compose up --build # full stack
Conventions
- Styling: faithful port of the original design as inline styles + a small
design-system in
src/app/globals.css(palette as CSS vars, fonts, twinkle/float animations, hover lifts, responsive grid helpers). No Tailwind. - Mutations: server actions in
src/app/backend/actions.ts. Each action re-checks auth + capability viaauth()andcan()before writing, thenrevalidatePath(). - Access control:
src/middleware.tsgates/backend/*; each page further guards by role (isStaff,can) and redirects. - Data reads for the public site live in
src/lib/queries.ts. - After changing
src/db/schema.ts, runnpm run db:generateand commit the new file underdrizzle/.
Promotions
Statuts : pending → live → suspended ⇄ live (+ rejected / expired).
promotions.suspended_by retient qui a suspendu (member ou staff) : une
suspension par le staff ne peut être levée que par le staff. Les lectures
publiques filtrent sur status = 'live', donc une promo suspendue disparaît du
site sans traitement supplémentaire.
Réseaux sociaux
src/lib/social.tspublie une promo sur la page Facebook (Graph API) ou LinkedIn (Posts API). Jetons uniquement en variables d'environnement, jamais en base ni dans le backoffice. Réseau non configuré = bouton masqué.- Les images de promo sont stockées en data-URI : l'upload se fait donc en binaire (multipart pour Facebook, Images API en 3 étapes pour LinkedIn), pas par URL.
- Chaque tentative est journalisée dans
social_posts(succès ou échec) ; un échec n'interrompt pas l'action, il s'affiche sur la carte de la promo. - Les URLs publiques des pages FB/LinkedIn sont des
site_settings(association_facebook,association_linkedin), éditables dans Paramètres.
Roles
admin > moderator > editor are staff; member is an adhérent linked to a
members row via users.memberId. Capability matrix is in src/lib/rbac.ts.
Docker notes
- Migrations + optional seed run on container start via
docker-entrypoint.sh. npm run build:scriptsbundlesmigrate/seedintodist/*.cjsso the runtime image needs no dev dependencies.- The standalone server binds
HOSTNAME=0.0.0.0,PORT=3000.
Logo
public/assets/logo.svg is a brand-colour recreation; swap in the official asset
when available (referenced as /assets/logo.svg).