Repository navigation
feat(admin): partenaires B2B, leurs parcours et leurs QR codes - #551
Merged
Merged
Conversation
Nascimento95
requested review from
IlanAMG and
PaulThiberville
as code owners
October 20, 2026 06:55
1 task
Nascimento95
force-pushed
the
issue/528-recent-second-factor
branch
from
October 20, 2026 07:19
f4bb245 to
256a22d
Compare
Nascimento95
force-pushed
the
issue/346-b2b-partners
branch
from
October 20, 2026 07:19
46f0ce0 to
a8418f9
Compare
Collaborator
Author
|
Branche remise sur la base de la PR #398 corrigée (test instable de |
This was referenced Oct 20, 2026
Closed
This was referenced Oct 20, 2026
Open
Collaborator
|
📋 MANAGER, 2026-10-21 — relecture de la pile admin pour Paul (tête Verdict : B, fusionnable, avec une question à Ilan. Niveau 2, cohérent. Vérifié :
Points :
|
PaulThiberville
force-pushed
the
issue/528-recent-second-factor
branch
from
October 21, 2026 16:17
256a22d to
aa4e44a
Compare
…d their public page (A-10) Types of the partners, their statuses, the 3 to 10 routes of a page, the card, the body of each write and the public page the site reads. The slug never changes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
b2b_clients and b2b_client_routes, with a unique slug that is checked in the database, a price bounded in cents and at most 10 routes in order. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
GET /admin/b2b-clients and /:id, and POST: the active partners first, the cities of their routes, a source of measure b2b-<slug>, a journal line on each write. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
PATCH /admin/b2b-clients/:id changes the fields given and journals the before and after. The slug is refused: the QR codes printed with it would stop measuring. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
PUT /admin/b2b-clients/:id/routes replaces the routes of a partner, 3 to 10, in the order of its page. Only published routes can be chosen. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ontact or its price (A-10) GET /public/partners/:slug serves the name and the published routes of an active or prospect partner as cards, cached a minute. A suspended or ended partner is a 404, and no contact, price or note ever leaves. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
getB2bClients, getB2bClient, createB2bClient, updateB2bClient and setB2bClientRoutes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… a panel The card that stays on screen while the next one loads moves to a hook shared by the users and the partners. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A-10 opens on the list of the partners and the card of the one the address names. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…A-10) The price is typed in euros and sent in cents without a float, the slug follows the name until it is typed, and only the fields that changed are sent. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The window that registers a partner or changes one, with its toasts. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A window with a search among the published routes, an order set by arrows, and a Save button that waits for 3 to 10 routes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…y its statistics are not wired (A-10) The card gives the link of the page and the link of the stores with the source of measure, each as a QR code to download, from NEXT_PUBLIC_SITE_URL. The statistics say they are not wired yet. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
journalOf had no order by, so the lines written by one partner could come back in any order and fail the test at random, as the one of the users did. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
PaulThiberville
force-pushed
the
issue/346-b2b-partners
branch
from
October 21, 2026 16:28
a8418f9 to
b8ddffc
Compare
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Oct 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Résultat et problème
Les hôtels, conciergeries et offices de tourisme qui paient pour des parcours à leur nom n'existaient nulle part : ni table, ni écran, ni page. A-10 permet à l'équipe de les enregistrer, de choisir de 3 à 10 parcours dans l'ordre de leur page, et d'obtenir le lien de cette page et ses QR codes ; l'API publique donne au site de quoi la construire.
b2b_clients(nom, identifiant, type, contact, adresse, statut, prix mensuel HT en centimes, début, notes) etb2b_client_routes(parcours et place, 10 au plus), migration additive0021_add_b2b_clients. L'identifiant (slug) est vérifié par la base.GET /admin/b2b-clients(les actifs d'abord, avec les villes de leurs parcours),GET /:id,POST,PATCH /:id(jamais le slug) etPUT /:id/routes(3 à 10 parcours publiés, dans l'ordre ; 409routes_unavailableavec les parcours refusés).GET /v1/public/partners/:slug: le nom,isActiveet les parcours publiés dans l'ordre, sous forme de cartes pour un visiteur anonyme ; cache public d'une minute. Un partenaire suspendu, résilié ou inconnu répond 404. Ni le contact, ni le prix, ni les notes n'en sortent (testé)./partenaires) : la liste, la fiche (contact, contrat, notes, parcours choisis, source de mesureb2b-<slug>), le formulaire d'ajout et de modification (prix saisi en euros, envoyé en centimes sans nombre à virgule ; l'identifiant suit le nom tant qu'on ne l'a pas tapé), le choix des parcours (recherche parmi les parcours publiés, ordre par flèches, bouton grisé hors de 3 à 10), le lien de la page/p/<slug>et deux QR codes téléchargeables (la page, et/app?src=b2b-<slug>pour la mesure).useCardOnScreen, utilisé aussi par les partenaires.Ce que cette PR ne tient pas (décision de l'opérateur, Marco, 2026-10-20) :
b2b-<slug>, le wiki ne dit pas quels événements comptent comme un clic ou une installation, et la lecture demande une clé PostHog de lecture côté API, à créer par Ilan ou Paul. La fiche dit « pas encore branché ». Questions déposées sur l'Inbox.Choix à relire
isActive: false) pour que le site garde sa page hors des moteurs de recherche ; suspendu et résilié répondent 404 (ticket du site).GET /admin/routes) ; un admin voit tous les parcours publiés.NEXT_PUBLIC_SITE_URL(origine du site, sans chemin) est à régler au déploiement ; sans elle, la fiche le dit et ne donne aucun lien. Les QR codes sont tracés dans le navigateur avec la bibliothèqueqrcode, déjà présente.packages/ui: une hauteur et une largeurqr(96 px) au preset, « à reporter danstokens.json» ;ConfirmDialoggagneisConfirmDisabled.Fixes #346
Écrans : A-10
Dépendance : PR empilée sur #550 (ticket #528), elle-même sur #501, #500, #499, #479, #398, #365 et #363 (base
issue/528-recent-second-factor).Comment vérifier
Montage local de la pile, base locale migrée (
pnpm --filter @widoo/api db:migrate), compteadminconnecté, et dansapps/admin/.env.local:NEXT_PUBLIC_SITE_URL=http://localhost:3000.hotel-du-nord), prospect, avec un contact et un prix « 49,50 » : la fiche montre « 49,50 € HT » et la sourceb2b-hotel-du-nord.http://localhost:3000/p/hotel-du-nordet…/app?src=b2b-hotel-du-nord, et « Télécharger le QR code » qui donne un PNG.curl localhost:8080/v1/public/partners/hotel-du-nord: actif, le JSON avec"isActive":trueet les 3 parcours dans l'ordre, sans e-mail, téléphone ni prix ; suspendu,not_found.moderator: pas de section Partenaires B2B, 403 de l'API.Tests : 12 tests d'API (
admin-b2b.test.ts: accès par rôle, création et journal, slug pris ou invalide, modification et journal avant et après, slug refusé, choix des routes et leur ordre, remplacement, refus sous 3, doublon, parcours non publiés ;public-partners.test.ts: l'ordre, un parcours dépublié, prospect, suspendu, résilié, inconnu, aucun champ privé) ; schémas, formulaire, prix, choix des parcours, liens et variable d'environnement côtésharedet admin.Testé par Marco le 2026-10-20, en local, sur le commit
467fd5a(base locale, Firebase de staging) : la liste et la fiche ; l'ajout et les refus ; le choix des 3 parcours dans l'ordre ; les statuts prospect, actif et suspendu ; la réponse de l'API publique pour un partenaire actif (nom, actif, 3 parcours) et sa réponse « introuvable » pour un suspendu. Rien n'a été testé sur un environnement déployé, ni avec un compte éditeur.pnpm lint && pnpm typecheck && pnpm testverts en local (chaque commit passe lint et types)Formatage mécanique : non
Risque
Niveau : 2 — migration additive (deux tables), nouveaux endpoints authentifiés qui écrivent, un endpoint public en lecture.
Taille : tous les commits sous 400 lignes utiles (le plus gros : 367). Fichiers générés à part : migration
0021_add_b2b_clients.sqlet son instantanémeta/0021_snapshot.json, générés par drizzle-kit.Revue OWASP (Top 10, édition en cours)
requireAdminSection('b2bClients')(éditeur et admin ; un modérateur reçoit 403, une requête sans jeton 401, testé). L'endpoint public ne lit que ce qui est destiné à la page : nom, drapeau actif et cartes de parcours publiés ; aucun champ de contact, de prix ni de notes (testé sur le corps de la réponse). Un partenaire suspendu ou résilié répond 404.[a-z0-9-]par le schéma et par une contrainte de la base ; l'identifiant est encodé dans les liens et les QR codes.for update).admin_actionsdans la transaction de l'écriture.users).Nouvelle dépendance : aucune (
qrcodeest déjà dans l'admin).Retour arrière
Le revert de la PR suffit pour le code. La migration
0021est additive : après un revert, les tablesb2b_clientsetb2b_client_routesrestent en base sans effet ; les supprimer demande une migration à part et perd les partenaires enregistrés, leurs parcours choisis et leurs lignes de journal ne sont pas touchées. La route publique répond alors 404.🤖 Generated with Claude Code