Skip to content

feat(admin): tableau de bord, lieux, parcours et import CSV - #398

Open
Nascimento95 wants to merge 25 commits into
issue/53-moderation-queuefrom
issue/55-catalog-dashboard
Open

Nascimento95 wants to merge 25 commits into
issue/53-moderation-queuefrom
issue/55-catalog-dashboard

Conversation

@Nascimento95

@Nascimento95 Nascimento95 commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Résultat et problème

L'équipe n'avait ni tableau de bord ni moyen de gérer le catalogue. Cette PR ajoute le tableau de bord (A-05) et la section « Lieux et parcours » (A-04) : parcours, lieux et import CSV, côté API et côté admin.

Ce qui est fait :

  • Tableau de bord : GET /admin/counters et GET /admin/stats ; tuiles « à traiter » selon le rôle, chacune ouvre sa liste filtrée ; chiffres du catalogue et des utilisateurs ; liens vers PostHog et RevenueCat.
  • Parcours : GET /admin/routes (recherche, filtres, tri, pages) ; fiche en lecture ; Dépublier et Republier avec motif, Réassigner à l'équipe (admin), chaque action journalisée.
  • Lieux : GET/POST /admin/places, GET/PATCH /admin/places/:id ; liste, fiche dans le panneau de détail, création et édition dans un formulaire (éditeurs et admins ; un modérateur lit). La ville est déduite de la position.
  • Import CSV (admin) : POST /admin/places/import, avec aperçu (dryRun) puis création ; modèle à télécharger ; chaque ligne affichée avec son résultat (prêt, créé, invalide avec les champs à corriger, doublon, hors zone) ; doublon = même nom à moins de 50 m, dans le catalogue ou plus haut dans le fichier ; une ligne au journal par lieu créé.
  • Carte de position (mapbox-gl) : dans le formulaire d'un lieu, une épingle à déplacer ou un clic sur la carte renseigne la latitude et la longitude, et la saisie des champs déplace l'épingle. Sans jeton NEXT_PUBLIC_MAPBOX_TOKEN, le formulaire garde ses deux champs seuls.
  • Ouvrir dans l'app : l'écran du parcours de l'app est encore provisoire (Construire la fiche parcours (E-05) #37) ; le lien n'ouvre pas encore la fiche. Sur ordinateur il ne fait rien.
  • Accès : une page dont le rôle n'ouvre pas la section, le formulaire de lieu pour un modérateur et l'import pour un non-admin affichent un refus, même avec l'adresse tapée à la main ; l'API refuse de son côté (403).

Critères d'acceptation :

  • Import de 50 lieux avec 2 lignes invalides : 48 créés, 2 erreurs explicites (test d'API, et testé à l'écran).
  • Chaque tuile « à traiter » ouvre la liste filtrée. Les tuiles dont la liste n'existe pas encore (signalements, avis, support) affichent 0 et mènent à une page introuvable jusqu'à Admin : signalements de lieux et de parcours, avis (A-03) #54.

Limites, et le ticket qui les lève :

Questions pour Ilan :

  1. Quand un membre de l'équipe n'a pas de prénom, l'historique d'un parcours affiche le début de son e-mail et la pastille du compte son e-mail entier : quel affichage veut-on ?
  2. Le partage des rôles sur le catalogue (éditeur : écrit les lieux ; admin : import, Republier, Réassigner ; modérateur : lit les lieux, Dépublie) suit ma lecture du wiki : à confirmer.
  3. Jeton Mapbox web de Widoo (voir Limites) : qui le crée et où il est stocké.

Fixes #55
Écrans : A-04, A-05
Dépendance : empilée sur #365 (modération, #53), elle-même sur #363.

Comment vérifier

Montage local de #363, base locale, comptes admin, editor et moderator.

Testé par Marco les 2026-10-12, 2026-10-13 et 2026-10-14, en local (base locale, Firebase de staging pour la connexion), face aux planches A-05 et aux modèles de Ecrans pour A-04 :

  • le tableau de bord, ses tuiles et leurs liens ;
  • la liste et la fiche des parcours, Dépublier, Republier, Réassigner ;
  • la liste des lieux, la fiche, la création et l'édition ; le refus du formulaire pour un modérateur ;
  • la carte de position, avec un jeton Mapbox public de test sur le compte de Marco : clic sur la carte (épingle posée, latitude et longitude remplies), saisie des coordonnées (épingle déplacée), création d'un lieu avec sa position ; le déplacement de l'épingle à la souris et la réouverture d'un lieu enregistré n'ont pas été confirmés ;
  • « Ouvrir dans l'app » : bouton affiché dans la carte « Actions » d'un parcours ; l'ouverture sur un téléphone n'a pas été testée ;
  • l'import : fichier de 50 lignes dont 2 invalides (aperçu « 48 lieux prêts, 2 lignes écartées », puis 48 créés) ; fichier avec doublons du catalogue, doublon dans le fichier et lieu hors zone ; fichier sans les colonnes obligatoires.

La carte n'a pas de planche : elle suit la phrase du wiki (« épingle déplaçable »). Rien n'a été testé contre un environnement déployé. Pas de test à 150 % de texte ni au lecteur d'écran sur ces écrans.

  • pnpm lint && pnpm typecheck && pnpm test verts en local
  • Testé sur iOS / Android (préciser) — sans objet

Formatage mécanique : non

Risque

Niveau : 2 — nouveaux endpoints authentifiés qui écrivent dans le catalogue (lieux, statut des parcours, import en lot) ; aucune migration.

Taille : deux commits au-dessus de 400 lignes. 56b6289 (451) : la liste des parcours, ses filtres, son tableau, sa configuration et ses textes. 4c58c56 (427) : les composants du tableau de bord, son conteneur et ses textes.

Revue OWASP (Top 10) :

  • Contrôle d'accès : lecture par requireAdminSection('catalog') ; écriture d'un lieu par requireRole('editor') ; import, Republier et Réassigner par requireRole('admin'). Refus testés par rôle (403, rien d'écrit). Les gardes de l'admin ne sont qu'un confort : l'API décide.
  • Entrées non fiables : chaque ligne importée passe par le même schéma strict que la création à la main (texte brut borné, site en https, téléphone, coordonnées) ; le CSV est lu dans le navigateur, l'API ne reçoit que du JSON ; 500 lignes au plus par appel. Les textes s'affichent sans HTML.
  • Injection : requêtes paramétrées ; recherche échappée ; filtres et tri validés par schéma strict.
  • Intégrité : un lieu et ses horaires s'écrivent avec leur ligne de journal dans la même transaction ; les actions sur un parcours verrouillent sa ligne.
  • Journalisation : place.create, place.import, place.update et les actions de parcours gardent l'auteur et l'état.
  • Limites : un import fait au plus 500 créations ; pas de quota par admin.

Nouvelle dépendance : mapbox-gl 3.32, chargée avec le formulaire de lieu seulement ; choisie parce que le wiki fixe Mapbox pour les cartes de Widoo (Architecture-Technique). Écartée : maplibre-gl, qui aurait demandé un autre fournisseur de tuiles. CSP de l'admin : api.mapbox.com, events.mapbox.com et un worker blob: ne sont autorisés que lorsqu'un jeton est configuré ; un jeton secret (sk.) est refusé au démarrage.

Retour arrière

Le revert de la PR suffit pour le code, sans migration à annuler. Les lieux créés ou importés restent en base ; un import fait par erreur se retrouve par le journal (admin_actions, action place.import).

🤖 Generated with Claude Code

@Nascimento95

Copy link
Copy Markdown
Collaborator Author

Branche remise à jour sur la modération (#365), elle-même sur le socle rebasé sur main. Aucune migration dans ce ticket.

Adaptations au rebase : le client API reprend celui de main avec les méthodes du catalogue ; les tests des corps invalides attendent 422, la convention de main (D-071), sauf la position hors de toute ville, qui reste un 400 de la route ; le nouveau statut de vérification unverified (« Pas encore vérifié », D-049) a sa pastille et son filtre dans la liste des lieux. Lint, types et tests verts en local ; quelques fichiers de l'API (éditeur, programmations, signalements) dépassent le délai de 5 s quand toute la suite tourne et passent en séquence.

Nascimento95 and others added 14 commits October 16, 2026 10:34
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…users (A-05)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…first name

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to the team

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eason dialog

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sort (A-04)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (A-04)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…urnal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rnal

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nascimento95 and others added 11 commits October 16, 2026 10:34
…es address

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s (A-04)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… API

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-04)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s (A-04)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Nascimento95
Nascimento95 force-pushed the issue/55-catalog-dashboard branch from f2ccb57 to 52d1d10 Compare October 16, 2026 03:35
@Nascimento95

Copy link
Copy Markdown
Collaborator Author

Branche remise à jour sur la modération (#365), elle-même sur le socle rebasé sur main. Aucune migration dans ce ticket, aucun changement de comportement. Lint, types et tests verts en local.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:admin apps/admin level:2 Risque niveau 2 (SECURITY.md) type:feature Nouvelle fonctionnalité

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant