Programme and Lot Editing

Summary

Admins have a full back-office view of programmes and their lots, with partial-update endpoints (PATCH) for editorial corrections. Only fields present in explicit server-side whitelists are applied — non-whitelisted fields are silently ignored. Rich-text fields go through script-tag sanitization. The editorial JSON object (pinned content, highlight flags, etc.) has its own dedicated PATCH route backed by Zod validation. Document categories in documentsVente can be patched individually by array index, and a bulk reclassification endpoint re-runs the AI classifier on all programmes.

State Diagram

stateDiagram-v2
    state "Liste des programmes" as Browsing
    state "Détail programme" as DetailView
    state "Liste des lots" as LotsView
    state "Modification programme" as Editing
    state "Modification lot" as LotEditing
    state "Modification éditoriale" as EditorialEditing
    state "Modification document" as DocumentEditing
    [*] --> Browsing: admin opens programmes list
    Browsing --> DetailView: admin opens programme detail
    DetailView --> LotsView: admin views lots
    DetailView --> Editing: admin patches programme
    LotsView --> LotEditing: admin patches lot
    DetailView --> EditorialEditing: admin patches editorial
    DetailView --> DocumentEditing: admin patches document category
    Editing --> DetailView: updated programme returned
    LotEditing --> LotsView: updated lot returned
    EditorialEditing --> DetailView: updated programme returned
    DocumentEditing --> DetailView: updated documentsVente returned

Steps

1. List Programmes (Actor: admin)

GET /admin/programmes with optional query parameters:

  • q: text search on ville and name (case-insensitive)
  • statuses: comma-separated status filter (en_cours, livre, a_venir, retire)
  • types: comma-separated lot type filter (filters programmes that have lots of those types)
  • price_min, price_max: range on prixMin/prixMax
  • surface_min, surface_max: range on surfaceMin/surfaceMax
  • promoteur: partial match on promoteur name
  • partnerId: exact partner UUID filter
  • cursor, limit: cursor pagination (createdAt + id compound)

Returns lightweight summary fields including _count.lots and partner name.

2. Programme Detail (Actor: admin)

GET /admin/programmes/:id — full programme record including partner name. Does not include lots (separate endpoint to avoid payload bloat).

3. Programme Lots (Actor: admin)

GET /admin/programmes/:id/lots — offset pagination (page, limit). Returns { data: lots, pagination: { page, totalPages, total } }.

4. Patch Programme (Actor: admin)

PATCH /admin/programmes/:id — partial update using the PROGRAMME_PATCH_WHITELIST set (covers name, promoteur, description, location fields, all dispositifs fiscaux, display flags, rich text fields, and more). Non-whitelisted keys are silently dropped. Rich-text fields (description, texteAccroche, aProximite, prestations, presentation, immeuble, environnement) go through stripScriptTags(). Returns 400 NO_VALID_FIELDS if the body contains no whitelisted fields.

5. Patch Lot (Actor: admin)

PATCH /admin/programmes/:id/lots/:lotId — partial update using LOT_PATCH_WHITELIST (covers numeroLot, type, surface, status, all price columns, dispositif-specific prices, performance metrics). Ownership verified: lot must belong to the programme. Returns 400 NO_VALID_FIELDS if no valid fields.

6. Patch Editorial (Actor: admin)

PATCH /admin/programmes/:id/editorial — body passed to setEditorial(id, body) from lib/editorial.ts. Validated by Zod inside setEditorial(). Zod errors surface as 400 VALIDATION_ERROR.

7. Patch Document Category (Actor: admin)

PATCH /admin/programmes/:id/documents/:index with { category: string | null }. The index is 0-based position in documentsVente JSON array. category: null resets the category to “Non catégorisé”. Returns 400 INVALID_INDEX if out of range.

8. Bulk Reclassify Documents (Actor: admin)

POST /admin/programmes/reclassify-documents — iterates all programmes, runs classifyDocument(doc.nom) from lib/document-classifier.ts on each document in documentsVente. Only writes back rows where at least one category changed. Returns { programmesUpdated, programmesTotal }.

Error States

  • Programme not found → 404 NOT_FOUND
  • Lot not found or not belonging to programme → 404 NOT_FOUND
  • No whitelisted fields in PATCH body → 400 NO_VALID_FIELDS
  • Invalid enum value in PATCH → 400 INVALID_VALUE
  • Zod validation error in editorial PATCH → 400 VALIDATION_ERROR
  • Document index out of range → 400 INVALID_INDEX