Mesuré au navigateur avant/après (scrollWidth vs clientWidth, 320/375/414/768/
900/1024/1280px) : la page débordait partout, de 17px en mode carte et de 32px
au-dessus de 1024. Deux causes, aucune liée aux points de rupture.
1. L'application tourne en content-box. src/styles/global.css porte bien un
`* { box-sizing: border-box }` mais n'est importé nulle part — main.tsx ne
charge que index.css, qui pose border-box sur le seul #root. Tout width:100%
assorti d'un padding débordait donc de son conteneur. Réinitialisation
cantonnée à .page : global.css est un vestige d'une ancienne maquette (son
propre .sidebar, .layout, un autre fond de body), l'importer déplacerait tout
le site.
2. En mode carte, .table td était en display:flex : le texte de la cellule
devenait un élément flex anonyme, avec min-width:auto, et un titre de
document long refusait de se réduire — il sortait de la carte et entraînait
la page entière, onglets compris. Passé en bloc, intitulé au-dessus de la
valeur, avec coupure des mots.
Aussi : min-width:0 sur .tabs, sans quoi son overflow-x:auto ne s'enclenchait
jamais ; et rétablissement de la rangée d'actions, dont le display:flex était
écrasé en spécificité par `.table td { display: block }`.
Après correction : aucun débordement sur les quatre onglets, de 320 à 1280px.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cause réelle : .main est un élément flex sans min-width:0. Sa valeur par défaut
min-width:auto l'autorise à dépasser la largeur de son conteneur dès qu'il
contient un bloc large — ici le tableau des documents, dont les titres sont
longs. La page entière partait donc plus large que l'écran, entraînant l'entête
et les onglets avec elle. Aucun point de rupture ne pouvait rattraper ça, le
problème étant en amont du responsive.
- .main : min-width:0 (corrige aussi les autres pages à tableau).
- .page : width:100% + min-width:0, pour ne jamais dépasser la place offerte.
- Onglets : bande défilante à toutes les largeurs, plus seulement sous le
breakpoint — avec la sidebar de 16rem, quatre onglets débordent bien avant.
- Cellules : coupure des mots à toutes les largeurs, et borne de largeur sur la
colonne titre, qu'un seul libellé long suffisait à étirer.
- Bascule en cartes remontée de 768 à 900px : la sidebar retire 16rem à la
largeur utile, les tableaux étaient à l'étroit bien avant le seuil.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
nginx ne posait aucun Cache-Control sur index.html. Le navigateur appliquait
son cache heuristique et gardait l'ancien index.html, qui référence les anciens
assets fingerprintés — eux marqués immutable un an. Conséquence : un
déploiement pouvait rester invisible pendant une durée indéterminée, sans
Ctrl+Shift+R. no-cache ne désactive pas le cache, il impose une revalidation :
304 tant que rien ne change, nouveau build dès qu'il change.
Côté page admin, un 401 (jeton absent ou expiré au bout de 30 min) produisait
« Impossible de charger les utilisateurs » — un message qui accuse le serveur
alors que la session est simplement morte, l'interface continuant de croire
l'utilisateur connecté. Les 401 et 403 sont désormais distingués et nommés.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Statistiques : le type Stats attendait cir/sccs/users alors que /admin/stats
renvoie cir_documents/sccs_documents/total_users. Trois cartes sur quatre
affichaient « - » et les deux barres restaient à zéro. Contrat aligné sur l'API.
Style : la feuille n'utilisait aucune couleur du site — palette bleu ardoise,
unités en px, cartes sans bordure. Reprise de la DA des autres pages : accent
violet #7c3aed (celui de la Sidebar), échelle de gris et rayons 0.5/0.75rem de
DocumentsPage, cartes blanches bordées, unités rem.
Responsive :
- onglets en bande défilante au lieu d'un retour à la ligne qui cassait l'entête ;
- les tableaux deviennent des cartes sous 768px, chaque cellule portant son
intitulé de colonne via data-label + ::before — l'ancien display:block empilait
les valeurs sans dire à quoi elles correspondaient ;
- cartes de stats en grille auto-fit, plus de paliers codés en dur ;
- panneau scrapers en deux colonnes, empilé sous 1024px, boutons pleine largeur
sous 480px ;
- modale contrainte à la largeur de l'écran, focus visible au clavier.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Admin.tsx gardait le TODO d'origine :
const token = '' // token should be passed / read from auth in real app
Tous les appels authentifiés partaient donc en `?token=` et retombaient en
401 : « Impossible de charger les utilisateurs / les statistiques / les logs
de scrapers ». Seul l'onglet documents fonctionnait, parce qu'il tape sur
/documents qui est public.
Le jeton est lu dans localStorage, là où App.tsx le pose au login. Corrige du
même coup les actions de la page (suppression d'utilisateur, changement de
rôle, suppression de document) qui utilisent la même constante.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Admin.tsx faisait ses 8 appels en relatif (`/admin/users?token=…`), donc vers
le nginx du front et non vers l'API. Le fallback SPA répondait index.html en
HTTP 200 : le res.json() partait en erreur de parsing — « The string did not
match the expected pattern » sous Safari. Les appels passent désormais par
API_URL, comme le reste du front.
App.tsx ne lisait le user que depuis localStorage, instantané pris au login :
une promotion en admin n'y apparaissait jamais et AdminRoute éjectait le compte
de /admin jusqu'à reconnexion. Le profil est resynchronisé depuis /me au
montage.
Restent 404 côté API : /admin/scrapers/logs et /admin/scrapers/run, que la page
appelle mais que le backend n'expose pas (les scrapers tournent via le cron
hôte). Le panneau affichera une erreur franche au lieu d'une erreur de parsing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
theapi.ts pointait sur http://127.0.0.1:8000 et AuthPage.tsx sur
http://localhost:8000. Depuis un navigateur sur https://regwatch.nfteam.ovh,
ces appels visent la machine du visiteur : « Failed to fetch » au login et
plus aucun document affiché (doublé d'un blocage mixed-content https -> http).
Le Dockerfile bake déjà ARG VITE_API_URL=https://regwatch-api.nfteam.ovh ;
le code ne le lisait simplement pas. Le repli garde l'adresse locale, donc
`pnpm dev` sans .env continue de fonctionner — c'est ce qui manquait au
commit « Fix : URL API production », codé en dur puis reverté pour cette raison.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>