Le bouton libre « Publier sur X » permettait de diffuser n'importe quelle promo en ligne, à tout moment. Le choix appartient désormais à l'adhérent, la publication part à la validation, et le choix est figé ensuite. - Colonnes `promotions.share_facebook` / `share_linkedin` : réseaux demandés à la soumission, modifiables tant que la promo est `pending`. - L'adhérent coche les réseaux dans le formulaire de soumission et peut encore les changer depuis Mes promotions tant qu'elle est en attente ; une fois validée, il ne voit plus qu'un état en lecture seule. - Le modérateur retrouve ces cases pré-cochées dans le formulaire « Valider » et peut les ajuster : c'est le dernier moment où la diffusion est modifiable. Valider met en ligne puis publie, dans cet ordre, afin qu'un réseau en panne ne bloque pas la mise en ligne. - `publishPromoShares` devient le seul point de publication. Elle ne lève jamais et ignore tout réseau ayant déjà une publication réussie : une promo n'est jamais postée deux fois, y compris après un cycle suspension → remise en ligne. - `sharePromoToSocial` est remplacée par `retryPromoShare`, limitée au rattrapage d'un échec sur un réseau déjà choisi, sur une promo en ligne uniquement. - Un réseau non configuré sur le serveur enregistre un échec explicite plutôt que d'échouer en silence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
3.8 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é = case masquée.- 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.
- La diffusion est déclenchée par la validation, pas par un bouton :
promotions.share_facebook/share_linkedinsont choisis par l'adhérent, ajustables par le modérateur dans le formulaire « Valider », puis figés (status !== 'pending'). publishPromoShares()dansbackend/actions.tsest le seul point de publication. Elle ne lève jamais et ignore tout réseau ayant déjà une lignesocial_postsenposted: c'est la garde anti-republication, qui couvre aussi le cycle suspension → remise en ligne.retryPromoSharene sert qu'au rattrapage d'un échec sur un réseau déjà choisi ; il ne peut pas élargir la diffusion.- 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).