L'assistant publiait directement dans le wiki global. La politique de contenu
ecarte la faute franche — conseil FCL, cas client, hors perimetre — mais une
heuristique ne juge pas la justesse : une page fausse mais bien ecrite la
franchissait, et se retrouvait citee comme documentation Xpeditis aupres de
tous les clients.
Domaine
- `WikiContributionStatus` : pending / published / rejected. `create()` ne
prend pas le statut en parametre — rien ne nait publie.
- `publish(reviewerId, edits?)` valide, en acceptant une correction du
relecteur, qui repasse la meme politique de contenu.
- `reject(reviewerId, note?)` ecarte sans supprimer : la liste des refus montre
ou l'assistant se trompe.
- `revise()` remet une page validee en attente : sans cela, la validation
porterait sur un texte que l'assistant a remplace depuis.
Ce qui sort du depot
- `findPublished` pour la recherche et la page publique, `findForReview` pour
l'administration. Le statut est porte par la requete, pas filtre en memoire :
une page en attente ne peut pas sortir par le chemin des clients.
- `revision()` ne compte que le publie, donc l'index vectoriel ne se reconstruit
que sur une decision.
Relecture
- `GET /admin/wiki-contributions`, `POST :id/publish`, `POST :id/reject`, sous
JwtAuthGuard + RolesGuard et @Roles('admin').
- Chaque decision est journalisee (`WIKI_CONTRIBUTION_REVIEWED`) : une page
publiee engage la marque, on doit pouvoir dire qui l'a laissee passer.
- Ecran `/admin/wiki` : file par statut, corps complet affiche, correction et
motif de refus facultatifs.
L'assistant
- La capacite rend `pending_review` et un message d'attente, plus d'URL : le
modele annonce une proposition, jamais une publication. La consigne le lui
dit explicitement.
SQL de la migration verifie sur la base locale : l'upsert sur index
d'expression met bien a jour a la casse pres, la contrainte de statut refuse
une valeur inconnue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
L'assistant pouvait conclure « prenez plutot un conteneur complet » avec
l'autorite de la marque, et repondre sur du transport interieur. Il ne pouvait
pas non plus combler un trou de documentation qu'il venait de rencontrer.
Perimetre (openai-trade.adapter)
- SCOPE_RULES : LCL uniquement. Le FCL reste explicable — c'est du vocabulaire
metier — mais jamais recommande, chiffre, ni presente comme la meilleure
option. Un cas hors LCL part vers support@xpeditis.com.
- SCOPE_RULES : transport international uniquement. Le transport interieur, le
demenagement et le transport de personnes sont refuses, pas traites « un peu ».
- La page wiki « LCL vs FCL » ne conseille plus le FCL : elle le decrit, et
liste les cas hors perimetre a signaler au support. Corpus reconstruit.
- L'amorce « LCL ou FCL ? » devient une question sur le calcul du fret LCL.
Documentation interne d'abord (KNOWLEDGE_RULES)
- Les extraits du wiki sont dits source de reference, avant les connaissances
generales du modele.
Entretien du wiki global
- Nouvelle capacite `contribute_wiki_page` (scope write) : quand le wiki ne
couvre pas un sujet d'information generale sur le transport international,
l'assistant ecrit la page. Un titre deja pris est mis a jour, pas duplique.
- `wiki-contribution-policy` (domaine) decide ce qui entre : refus du contenu
qui conseille le FCL, du cas client (dossier, tarif, coordonnees) et du hors
perimetre. Le prompt oriente, cette fonction empeche.
- Un sujet deja couvert par le wiki publie (score >= 0,62) est refuse.
- `WikiRetriever` indexe les contributions a cote du corpus fige, avec un index
en memoire invalide par la revision du jeu ; une panne de ce cote ne coute pas
la reponse.
- Page `/dashboard/wiki/complements`, etiquetee comme ecrite par l'assistant,
et carte sur l'index du wiki.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le statut PENDING_PAYMENT ne designe plus un paiement en attente mais un devis :
une reservation construite dont les frais de booking ne sont pas encore regles.
L'enum backend devient QUOTE, converti en place par migration.
Les deux onglets se partagent desormais la meme liste :
- « Reservations » n'affiche que PENDING, ACCEPTED et REJECTED ;
- « Historique des devis » regroupe QUOTE, PENDING_BANK_TRANSFER et CANCELLED,
avec les actions propres a un devis (modifier, payer, supprimer).
La table, l'export et les filtres sont extraits dans BookingListView pour eviter
de dupliquer la page entre les deux vues.
Tableau de bord :
- l'alerte « Paiement en attente » disparait des points a traiter, les devis non
regles ne bloquant aucun envoi en cours ;
- « Sans reponse du transporteur » devient « En attente du transporteur ».
Migration verifiee en transaction annulee sur la base de dev : les lignes
PENDING_PAYMENT deviennent QUOTE, l'enum et le defaut de colonne suivent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NZJ5aowZJVAcyPZw5TiNEm
- Nouvel endpoint GET /csv-bookings/reservation-quota: limite = vrai plan de
l'org (pas l'override ADMIN PLATINIUM), used = bookings PAYES de l'annee
(PENDING_BANK_TRANSFER/PENDING/ACCEPTED), limitReached.
- ShipmentCounter: countPaidShipmentsForOrganizationInYear.
- create-check compte desormais les payes (au lieu de tous).
- Frontend useReservationQuota consomme l'endpoint (fiable meme pour un admin
dont l'overview renvoie PLATINIUM).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Les options du formulaire de recherche etaient collectees mais jamais stockees.
Ajout d'une colonne jsonb options sur csv_bookings (+ migration), stockee a la
creation et a l'edition (editFromRate), renvoyee dans la reponse, et re-pre-
remplie a la reprise (search-advanced <- URL <- booking.options).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
toOrmUpdate ne mappait que status/notes/commission, donc les modifications de
volume/poids/palettes/prix/transporteur/route via editDetails/editFromRate
n'etaient jamais persistees (repository.update). Ajout de tous les champs
editables au mapping de mise a jour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Telechargement docs transporteur: proxy de streaming API (fin des URLs
presignees MinIO injoignables depuis l'exterieur)
- Documents: ajout de l'action "Supprimer" (endpoint DELETE deja existant)
- Blog: suppression reelle de l'image de couverture (null explicite bout en bout)
- Blog: le bouton Retour navigateur ferme l'editeur au lieu de quitter vers Logs
- Paiement: bouton Retour revient au contexte precedent (router.back)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Backend: add forgot-password and reset-password endpoints with token-based
flow (1h expiry, secure random token, email via existing EmailPort)
- Backend: add PasswordResetTokenOrmEntity + migration
- Backend: add siret field to RegisterOrganizationDto and pass it to org creation
- Backend: remove MinLength(12) from LoginDto.password (wrong for login use case)
- Backend: add rememberMe to LoginDto (optional, informational)
- Frontend: register page rewritten as 2-step flow (account info → org info)
with SIRET field, Suspense wrapper, "Centre d'aide" link removed
- Frontend: forgot-password page rewritten in French, matching login style
- Frontend: reset-password page rewritten in French, matching login style, with Suspense
- Frontend: remember me now works — localStorage when checked, sessionStorage otherwise
- Frontend: login page removes "Centre d'aide" link, connects rememberMe to login
- Frontend: auth-context and api/auth updated to pass rememberMe through full chain
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>