Documente les lots Q à Y, et corrige ce que la série a rendu faux

Nouvelle section « Lots Q à Y » dans CLAUDE.md : les décisions et les pièges,
pas la description du code. Elle dit aussi, en tête, ce qui n'est pas éprouvé —
aucun écran de la série n'a été vérifié en navigateur.

⚠️ Quatre passages étaient devenus FAUX, et un fichier qui fait autorité et qui
ment est pire qu'un fichier incomplet :

- le lot H actait « le retour passe par l'historique du navigateur, jamais par
  une destination calculée » et décrivait js/navigation.js, supprimé depuis. Le
  texte d'origine est conservé en citation — son motif reste vrai — sous le
  renversement qui explique pourquoi il n'a pas suffi, et pourquoi toute route
  nouvelle doit entrer dans la table de RemonteeRoutes ;
- « Cinq instantanés » : ils sont neuf, et une vue d'API sans instantané est un
  écran mort hors-ligne ;
- le modèle de données ignorait Livre.NombrePages, Livre.UrlNotice et l'entité
  RevueSouhaitee, dont Utilisateur est une frontière comme pour LivreSouhaite ;
- la fiche d'une série ne porte plus de barre d'actions flottante en
  consultation ; elle ne subsiste que sur l'écran d'ordre.

IDEES.md marque les lots Q à Y traités, en gardant le texte qui dit le besoin,
et ouvre trois points que la série laisse : la vérification en navigateur de
tous les écrans touchés, le réordonnancement des envies de revues, et un
éventuel signalement « déjà possédée » pour elles — plus douteux qu'il n'y
paraît, une envie portant souvent la revue entière. Ce qui était déjà ouvert
(A5, K1, N2, homonymes, OpenLibrary, EAN-2) reste intact.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mathieu
2026-08-21 14:56:23 +02:00
co-authored by Claude Opus 5
parent 498579f27b
commit d7ea12ddbf
2 changed files with 332 additions and 10 deletions
+275 -10
View File
@@ -348,9 +348,13 @@ l'utilisateur, et la douchette tape des chiffres.
| `Services/FiltreLivresLocal.cs` | Recherche, filtres et tri hors-ligne sur l'instantané | | `Services/FiltreLivresLocal.cs` | Recherche, filtres et tri hors-ligne sur l'instantané |
| `Services/ServiceLivresApi.cs` | Lectures avec repli sur le cache, écritures refusées | | `Services/ServiceLivresApi.cs` | Lectures avec repli sur le cache, écritures refusées |
**Cinq instantanés, un par vue de l'API**, jamais un par requête : `catalogue` (le catalogue **Un instantané par vue de l'API**, jamais un par requête. Ils étaient cinq le 2026-08-18 ;
**entier**, sans filtre), `auteurs`, `prets-en-cours`, `utilisateur`, et `souhaits` depuis le ils sont **neuf** depuis le 2026-08-21 : `catalogue` (le catalogue **entier**, sans filtre),
2026-08-18 (voir « La liste d'envies est le cinquième instantané »). C'est exactement ce qui `auteurs`, `prets-en-cours`, `utilisateur`, `souhaits` (voir « La liste d'envies est le cinquième
instantané »), `series`, `revues`, `souhaits-revues` et `version`. ⚠️ **Toute nouvelle vue de
l'API doit en recevoir un**, faute de quoi l'écran correspondant est mort hors-ligne — c'est
exactement ce qui était arrivé à la liste d'envies, et le symptôme (un `404` en plein écran) ne
désignait pas la cause. C'est exactement ce qui
justifie IndexedDB plutôt que le cache du service worker : un cache de réponses HTTP ne justifie IndexedDB plutôt que le cache du service worker : un cache de réponses HTTP ne
restituerait que les URL déjà visitées, donc **une recherche jamais tapée en ligne ne rendrait restituerait que les URL déjà visitées, donc **une recherche jamais tapée en ligne ne rendrait
rien**. Vérifié en exécution — hors-ligne, chercher un auteur jamais affiché auparavant remonte rien**. Vérifié en exécution — hors-ligne, chercher un auteur jamais affiché auparavant remonte
@@ -717,7 +721,9 @@ Livre
├── Editeur ├── Editeur
├── Format : Physique | Numerique ├── Format : Physique | Numerique
├── TypeDocument : NonPrecise | Roman | BandeDessinee (défaut = NonPrecise, PAS Roman) ├── TypeDocument : NonPrecise | Roman | BandeDessinee (défaut = NonPrecise, PAS Roman)
├── NombrePages : int? (⚠ NULL = « on ne sait pas » ; 0 est REFUSÉ, pas stocké)
├── CoverUrl ├── CoverUrl
├── UrlNotice (notice BnF ou OpenLibrary ayant prérempli la fiche)
├── DateAjout ├── DateAjout
└── AjoutePar (YNH_USER — traçabilité, PAS un cloisonnement) └── AjoutePar (YNH_USER — traçabilité, PAS un cloisonnement)
⚠ pas de colonne Statut, pas de colonne Auteur ⚠ pas de colonne Statut, pas de colonne Auteur
@@ -806,6 +812,20 @@ LivreSouhaite (la liste d'envies est PERSONNELLE — voir sa sec
├── Rang (ordre CHOISI, 0 = le plus désiré — personnel comme le reste) ├── Rang (ordre CHOISI, 0 = le plus désiré — personnel comme le reste)
└── UNIQUE (Utilisateur, TitreNormalise, AuteurNormalise) └── UNIQUE (Utilisateur, TitreNormalise, AuteurNormalise)
une œuvre par personne ; deux personnes peuvent souhaiter le même livre une œuvre par personne ; deux personnes peuvent souhaiter le même livre
RevueSouhaitee (table SŒUR de LivreSouhaite — PERSONNELLE elle aussi)
├── Id
├── Utilisateur (YNH_USER — une FRONTIÈRE, comme pour LivreSouhaite)
├── Titre (« Médor », sans son numéro)
├── TitreNormalise (NormalisationTexte, PAS CleOeuvre : une revue n'a pas de sous-titre)
├── Numero : string? (NULL = la revue entière, et non un numéro précis)
├── NumeroNormalise (jamais NULL — chaîne vide, sinon l'unicité ne tient pas)
├── Issn (facultatif, CANONISÉ AVEC SON TIRET)
├── Note
├── DateAjout
├── Rang (numéroté indépendamment de LivreSouhaite.Rang)
└── UNIQUE (Utilisateur, TitreNormalise, NumeroNormalise)
⚠ pas de CoverUrl, pas d'auteur, aucune FK vers Revue ni NumeroRevue
``` ```
Garder `Pret` comme table séparée (pas un champ sur `Livre`) pour conserver l'historique complet des prêts passés, pas juste l'état actuel. Garder `Pret` comme table séparée (pas un champ sur `Livre`) pour conserver l'historique complet des prêts passés, pas juste l'état actuel.
@@ -2353,14 +2373,21 @@ Vérifié en exécution : `crossOrigin === "use-credentials"` sur le lien, manif
« Le déplacement est foireux » : d'un écran profond (bibliographie, fiche de tome, ajout « Le déplacement est foireux » : d'un écran profond (bibliographie, fiche de tome, ajout
d'envie), il fallait deviner quelle destination du menu ramenait en arrière. d'envie), il fallait deviner quelle destination du menu ramenait en arrière.
Le retour passe par l'**historique du navigateur**, jamais par une destination calculée : ⚠️ **Ce qui suit a été RENVERSÉ le 2026-08-21** — voir « S — le retour est une remontée
un même écran est atteignable par plusieurs chemins — une fiche livre s'ouvre depuis le hiérarchique ». Conservé parce que le motif d'origine reste vrai, et qu'il faut savoir
catalogue, une bibliographie, une série ou une recherche. pourquoi il n'a pas suffi.
⚠️ `history.back()` **seul ne suffit pas** : ouverte depuis l'écran d'accueil du téléphone, > Le retour passe par l'**historique du navigateur**, jamais par une destination calculée :
la PWA démarre sur une pile d'un seul cran et « retour » **sortirait de l'application**. > un même écran est atteignable par plusieurs chemins — une fiche livre s'ouvre depuis le
D'où `js/navigation.js`, qui teste `history.length` — sans équivalent côté C# — et retombe > catalogue, une bibliographie, une série ou une recherche.
sur le catalogue. >
> ⚠️ `history.back()` **seul ne suffit pas** : ouverte depuis l'écran d'accueil du téléphone,
> la PWA démarre sur une pile d'un seul cran et « retour » **sortirait de l'application**.
> D'où `js/navigation.js`, qui teste `history.length` — sans équivalent côté C# — et retombe
> sur le catalogue.
⚠️ `wwwroot/js/navigation.js` **n'existe plus** : le retour est calculé en C# par
`Services/RemonteeRoutes.cs`. Ne pas le recréer.
### L'icône cassée du bandeau ### L'icône cassée du bandeau
@@ -2516,6 +2543,10 @@ numéro de rang, lui, reste visible en consultation — c'est une information, p
Le lien « Changer l'ordre » n'apparaît qu'à partir de deux tomes ou deux sous-séries : Le lien « Changer l'ordre » n'apparaît qu'à partir de deux tomes ou deux sous-séries :
réordonner un élément unique n'a pas de sens. réordonner un élément unique n'a pas de sens.
⚠️ Depuis le 2026-08-21, « Modifier » et « Changer l'ordre » ne sont plus au **bas** de la
fiche : ils sont groupés à droite de la ligne du titre, et `.actions-flottantes` ne subsiste
sur cet écran que dans le mode ordre (le bouton « Terminé »). Voir « T et W ».
### Une seule entrée d'ajout : « Ajouter un ouvrage » (décidé avec l'utilisateur) ### Une seule entrée d'ajout : « Ajouter un ouvrage » (décidé avec l'utilisateur)
Question posée telle quelle par l'utilisateur : refaire une page « ajouter une revue » Question posée telle quelle par l'utilisateur : refaire une page « ajouter une revue »
@@ -2793,3 +2824,237 @@ API lancée sur une base neuve, migration `ImagesEtUnesDesNumeros` appliquée au
scan depuis une série ou depuis les envies, du « Tout cocher » et de l'indicateur d'attente scan depuis une série ou depuis les envies, du « Tout cocher » et de l'indicateur d'attente
repose sur la compilation et la relecture, pas sur une exécution. À regarder au prochain repose sur la compilation et la relecture, pas sur une exécution. À regarder au prochain
passage sur un vrai appareil. passage sur un vrai appareil.
## Lots Q à Y — retours d'usage du 2026-08-21, traités le 2026-08-21
Neuf lots, quatre commits. Ce qui suit ne redit pas ce que le code montre : seulement les
décisions, les invariants qu'elles ont mis à l'épreuve, et ce qui a failli être fait de travers.
⚠️ **Aucun écran n'a été vérifié en navigateur sur toute la série.** Le rendu — formulaire
réordonné, menu plein écran, actions en tête de fiche, page « À propos », deux sections d'envies
— repose sur la compilation et la relecture, pas sur une exécution. Ce qui **est** vérifié : les
services et l'API (618 tests au vert, 506 au début de la série) et les points d'entrée éprouvés à
la main, listés en fin de section. À regarder au prochain passage sur un vrai appareil.
### S — le retour est une remontée hiérarchique, et non plus l'historique
⚠️ **Ceci renverse la décision du 2026-08-20** (« le retour passe par l'historique du navigateur,
jamais par une destination calculée »). Le motif d'alors reste vrai — un même écran s'atteint par
plusieurs chemins — **et il ne suffisait pas** : l'historique remonte aussi les **allers-retours**
(ouvrir un filtre, changer un ordre, entrer puis sortir d'une édition). On cliquait cinq fois sans
quitter le même écran. La question posée par la flèche est « où suis-je ? », pas « qu'ai-je fait
juste avant ? ».
`MaBibli.Client/Services/RemonteeRoutes.cs` porte une **table de parenté explicite**, et une
fonction **pure** donc testable sans navigateur.
⚠️ **Pas un découpage naïf de l'URL** : toutes les routes n'ont pas la forme d'une arborescence.
`/souhaits/ordre` remonte bien à `/souhaits`, mais `/auteurs/{id}/bibliographie` remonte à
`/auteurs`**la fiche d'un auteur n'existe pas**. Un `..` d'URL aurait mené sur une page
inexistante.
⚠️ **Toute route nouvelle doit être inscrite dans la table.** Le repli — la destination de menu de
la branche — existe pour qu'une route oubliée reste utilisable, **pas** pour dispenser de la
ligne : sans elle, `/revues/{id}/edition` retombait sur la *liste* des revues au lieu de la fiche
qu'on venait de quitter (constaté, et corrigé au lot W). L'ordre de lecture de la table compte
aussi : le premier modèle qui correspond gagne, donc `/revues/ajout` est écrit **avant**
`/revues/{id}`.
Le garde-fou « ne jamais sortir de l'application » vit désormais là aussi : la fonction rend
**toujours** un chemin interne. C'est ce qui remplace le test de `history.length`, qui ne disait
pas ce qu'on croyait dans une PWA `standalone` — la pile d'une session y contient aussi ce qui
précède l'application. `wwwroot/js/navigation.js` est **supprimé**.
### R — le menu colle en haut, et occupe l'écran entier sur téléphone
**Un seul conteneur collant** portant bandeau *et* menu, et non deux éléments collants
superposés : le second aurait exigé d'écrire en dur la hauteur d'un bandeau qui **varie** avec la
pastille hors-ligne et le nom d'utilisateur. C'est la même famille de piège que
`--mb-onglets-hauteur`, disparue avec la barre du bas.
Sur téléphone, le menu déployé prend **tout l'écran** : sous le bandeau, il partageait la place
avec la liste qu'on venait de quitter, et l'on choisissait sa destination au milieu d'autre chose.
⚠️ **Un calque plein écran sans porte de sortie est un piège.** Trois s'ajoutent à la fermeture
déjà en place sur `LocationChanged` : la croix, **Échap** (le calque prend le focus à l'ouverture
— sans focus, aucun `keydown` ne lui parviendrait, exactement comme le calque d'agrandissement des
couvertures) et le clic hors des liens.
⚠️ Le plein écran est **explicitement annulé au-delà de 40 rem** : un menu ouvert au doigt puis
une fenêtre agrandie laisserait sinon un calque sans bascule pour le refermer.
⚠️ **Les règles du menu restent en feuille GLOBALE** (`wwwroot/css/app.css`) : une règle scopée
n'atteint pas ce que rend un `NavLink` (défaut du lot H, à ne pas refaire), et la navigation est
ce qui doit le moins pouvoir tomber. Le point de rupture de **40 rem** reste écrit aux **deux**
endroits (`app.css` et `MainLayout.razor.css`) et les deux doivent rester identiques.
### Q — l'ordre des champs du formulaire livre est une décision
Titre, **type de document**, thèmes, auteurs, rôles, puis le reste. Le type **commande la suite de
la saisie** — les rôles ne se posent que là — et il fallait descendre tout le formulaire pour dire
« c'est une BD », c'est-à-dire après avoir saisi ce qui en dépend.
Deux invariants **maintenus**, et il fallait résister à les entamer :
- **rien n'est présélectionné** : `NonPrecise` ne prétend toujours rien. Remonter le champ le rend
visible, ce qui est le but ; le préremplir écrirait quelque chose de faux ;
- **les rôles n'apparaissent qu'à partir de deux auteurs**, BD ou non. IDEES.md proposait « BD
**et** deux auteurs » : ç'aurait été un revirement, un album signé d'une seule personne n'ayant
pas plus de rôle à distinguer qu'un roman.
⚠️ Le champ des noms se relie **à chaque frappe** et reconstruit la liste entière : des tests
verrouillent la préservation des rôles, que ce réordonnancement ne doit pas entamer.
### T et W — les actions remontent à droite du titre, la fiche revue prend deux modes
Sur `/series/{id}` comme sur `/revues/{id}`, les actions sont **groupées à droite de la ligne du
titre**, dans le même bloc d'en-tête — disposition déjà tenue par la bibliographie et la liste des
auteurs. Reléguées en bas, les actions d'une saga de vingt tomes ou d'une collection de trente
numéros ne se découvraient qu'après avoir tout déroulé. La barre `.actions-flottantes` disparaît
donc de la **consultation** d'une série ; elle ne subsiste que sur l'écran d'ordre.
`/revues/{id}` consulte, `/revues/{id}/edition` modifie — troisième écran à prendre cette forme
après la fiche livre et la fiche série.
⚠️ **Modifier et retirer un numéro n'existent QUE dans l'édition** : en consultation, ces boutons
se déclenchaient sous le pouce en faisant défiler la liste. Même raison que les flèches d'ordre
d'une série, reléguées sur leur propre écran. Un retrait de numéro se **confirme** désormais, comme
la suppression d'un livre ou d'une revue.
⚠️ **« Ajouter un numéro » RESTE en consultation, et ce n'est pas une entorse** : le geste ne
touche pas à la fiche de la revue, il range un objet de plus — même exception que « Prêter » et le
statut de lecture sur une fiche livre. Le formulaire se replie derrière son bouton et s'ouvre de
lui-même quand on arrive du scanner avec un numéro lu sur l'add-on EAN-2 : demander un clic de plus
pour saisir ce qu'on tient en main serait un détour.
⚠️ Les deux routes partagent le paramètre `{id}` : le routeur ne redessine rien en passant de
l'une à l'autre, d'où l'abonnement à `LocationChanged` — piège déjà rencontré sur la fiche livre.
Quitter l'édition referme au passage ce qui n'a de sens que là : formulaire de numéro ouvert,
retrait ou suppression en attente de confirmation.
### V — toute la ligne d'une revue ouvre sa fiche
Comme la carte entière au catalogue. ⚠️ **Le titre reste un vrai lien** — adresse, clavier,
clic-milieu — et son clic **ne remonte pas** jusqu'à la ligne, qui naviguerait une seconde fois.
Toute action posée un jour sur cette ligne devra faire de même, sinon la ligne avalera le clic du
bouton.
### U — le nombre de pages, et pourquoi `0` est refusé
`Livre.NombrePages` est **nullable, sans valeur par défaut** : « 0 page » se lirait comme une
donnée là où l'on veut dire « on ne sait pas ». Même raison que `TypeDocument.NonPrecise` — un
défaut qui ne prétend rien n'a rien à reprendre, d'où une migration réduite à un `AddColumn`. Le
service **refuse** un zéro avec un message plutôt que de l'écrire ; **effacer le champ** reste la
façon de revenir à « inconnu ».
Le préremplissage vient de `dc:format`, que rien ne lisait jusqu'ici. ⚠️ **Ce champ n'est pas un
nombre mais une phrase décrivant le support** : « 1 vol. (113 p.) : ill., couv. ill. en coul. ;
18 cm ». Les règles de `NettoyageIsbd.NombrePages` sont donc **étroites** — un nombre suivi de
`p.` ou de `page(s)`, rien d'autre — et tout le reste rend `null`.
⚠️ **L'erreur n'est pas symétrique** : un champ vide se remplit à la main en trois secondes, un
chiffre faux s'enregistre sans que personne ne le voie. « 1 vol. » ne vaut pas 1, « 30 cm » ne vaut
pas 30, et « (p. 45-90) » — une pagination de contribution — ne vaut rien. La valeur reste
**proposée** dans un champ modifiable, et rien n'est déduit pour un ebook (le nombre de pages y
varie avec la police : s'il est saisi, c'est une indication).
### X — éditer une envie, et souhaiter une revue
#### `PUT /api/souhaits/{id}`
- **Les formes normalisées se recalculent.** Sans cela, le rapprochement « déjà au catalogue »
continuerait de se faire sur l'ancienne forme, et **le signalement mentirait sans le dire**.
- **Le filtre sur l'appelant fait partie de la clé de recherche**, pas d'une vérification
ultérieure : l'envie d'un autre est **introuvable** (404), jamais refusée (403).
- ⚠️ **Une édition peut heurter l'unicité `(Utilisateur, TitreNormalise, AuteurNormalise)`**, ce
qu'un ajout ne peut pas. Renommer une envie en une autre déjà présente répond par un message
lisible, jamais par « UNIQUE constraint failed ». **400 et non 409**, contrairement au doublon du
catalogue : là-bas posséder deux exemplaires est légitime et l'appel se reconfirme, ici l'index
l'interdit et il n'y a **rien à confirmer**.
- ⚠️ **La couverture n'est écrite que si la charge utile en porte une.** Aucun écran n'offre de
champ « URL de couverture » pour une envie (décision actée) : un remplacement inconditionnel
l'aurait effacée à la première faute de frappe corrigée.
- Le **rang n'est pas touché** — l'ordre a son propre point d'entrée.
#### `RevueSouhaitee` : une table sœur, pour la quatrième fois
Même argument que `Revue` face à `Livre`, et que `LivreSouhaite` face au catalogue. Une envie de
livre est identifiée par un couple **(œuvre, auteur)** : c'est ce que porte son index unique et ce
qui permet de la rapprocher du catalogue. Un numéro de revue **n'a pas d'auteur** et se distingue
par son **numéro** — deux notions que la clé de `LivreSouhaite` ne sait pas exprimer sans devenir
fausse pour tout le monde.
⚠️ `NumeroNormalise` est **NOT NULL avec un défaut vide** : SQLite tient deux `NULL` pour
distincts, et « Médor, sans numéro » s'ajouterait autant de fois qu'on cliquerait. Piège déjà
rencontré sur `LivreSouhaite.AuteurNormalise` — il se repose à chaque table personnelle.
⚠️ **Le coût d'une table sœur se paie partout où les deux listes se rejoignent** : affichage,
export `.txt`, export `.csv`, instantané hors-ligne `souhaits-revues`, et
`ServiceRenormalisation`. C'est là que le sujet se rate s'il est bâclé — une envie de revue
invisible en librairie ne sert à rien.
Décisions prises en chemin :
| Point | Décision | Pourquoi |
|---|---|---|
| Affichage | **deux sections**, jamais un classement entrelacé | chaque table numérote son rang **indépendamment** ; mélanger deux suites sans rapport produirait un ordre que personne n'a choisi |
| `.txt` | livres groupés par auteur, puis les revues en **section à part, à la fin** | une revue n'a pas d'auteur : la ranger sous « Auteur non précisé » la mêlerait à des **livres** dont l'auteur est simplement inconnu. Au kiosque comme en librairie, ce ne sont pas les mêmes rayons |
| `.csv` | colonne **« Type »**, en-tête **« ISBN / ISSN »** | sans elles, un tri par titre rendrait revues et livres indiscernables, et la colonne des codes mêlerait deux natures de code en silence |
| Réordonnancement des envies de revues | **non fait** | la section est courte ; à rouvrir si l'usage le demande (noté dans IDEES.md) |
| Signalement « déjà possédé » | **non fait** | demanderait de rapprocher un numéro d'un `NumeroRevue`, et l'envie porte souvent la revue entière (noté dans IDEES.md) |
| `CoverUrl` | **absent** | aucune source ne donne l'image d'un numéro souhaité — et donc **rien à ajouter au garde de `GET /api/couvertures`** |
⚠️ **L'ISSN y est canonisé à part dans `ServiceRenormalisation`** : `Renormaliser` n'applique rien
quand la clé ne bouge pas, donc un ISSN mal formé sur une ligne au titre inchangé lui échapperait.
C'est la même précaution que `CanoniserLesIssn` sur `Revue` (lot K2).
### Y — l'application dit sa version, et sous quelle licence
`/a-propos` : manuel annoncé à venir, `mailto:info@limonier.be`, site de l'auteur, version publiée
avec sa date de build, licence **AGPL v3** et lien vers le dépôt. ⚠️ **L'AGPL attend que les
utilisateurs d'un service en réseau puissent en obtenir la source** : ce lien n'est pas un
ornement, c'est ce que la licence demande.
L'application ne connaissait pas sa version : elle vivait dans le `manifest.toml` du dépôt
`mabibli_ynh` et dans les tags git, **jamais dans le binaire**. `build/publier-release.sh` pose
désormais `-p:Version` et `-p:MaBibliDateBuild` (en respectant `SOURCE_DATE_EPOCH`), et l'API rend
les deux par `GET /api/version`.
⚠️ **L'horodatage de build est le TÉMOIN de l'injection, et rien ne le calcule côté MSBuild.**
Sans lui, la version lue serait le `1.0.0` que le SDK .NET pose par défaut dans tout assembly : il
se lirait comme une vraie version alors qu'il ne désigne rien — et c'est **exactement** la valeur
qu'on ira chercher pour diagnostiquer un appareil au cache dépareillé. Mieux vaut ne rien annoncer :
un binaire compilé à la main se déclare « version de développement », ce qui est vrai.
`VersionApplication.Depuis` est une fonction **pure**, éprouvable sans fabriquer d'assembly, et
elle coupe le `+empreinte` que le SDK ajoute parfois à `AssemblyInformationalVersion`.
Trois décisions d'écran :
- **L'entrée du menu est DÉTACHÉE des six destinations** — filet au-dessus sur téléphone, à gauche
en rangée sur PC : « À propos » est une **annexe**, pas une septième destination. Ses règles
vivent en feuille **globale**, comme tout le menu.
- **Le `mailto:` reste un lien même hors-ligne** : il ne charge aucune page et passe la main au
client de messagerie, qui sait mettre un message en attente. Le site et le dépôt, eux, basculent
en **boutons désactivés portant leur motif**, comme les liens d'export des envies — un `<a>`
laissé actif quitterait l'application pour une page d'erreur du navigateur.
- **La version est mise en instantané hors-ligne** : on lit ce numéro justement quand quelque chose
ne va pas, et un appareil qu'on soupçonne est souvent celui qui n'a plus de réseau. L'écran dit
alors que c'est la **dernière version vue du serveur**, et non la version courante.
### ⚠️ `dotnet ef` et les migrations de la série
Deux migrations, toutes deux sans reprise de données : `NombreDePages` (un `AddColumn`, le défaut
`NULL` ne prétendant rien) et `EnviesDeRevues` (une table neuve). C'est ce que permet un défaut qui
n'affirme rien — comparer avec `RangDesEnvies`, qui avait dû reconduire un ordre en SQL.
### Vérifié en exécution le 2026-08-21 (API, pas navigateur)
| Cas | Résultat |
|---|---|
| ISSN d'envie de revue saisi « 24666718 » | rangé **`2466-6718`** |
| `PUT /api/souhaits/{id}` sur l'envie d'un autre | **404** |
| Exports `.txt` et `.csv` | portent bien **les deux moitiés** de la liste |
| `GET /api/version` sans injection | `{"numero":null,"publiee":false}` |
| `GET /api/version` avec `-p:Version=0.4.1 -p:MaBibliDateBuild=…` | `{"numero":"0.4.1","publiee":true}` |
**618 tests au vert** (506 au début de la série), aucun avertissement de compilation.
+57
View File
@@ -466,6 +466,11 @@ Deux points à trancher avant de coder :
# Retours d'usage du 2026-08-21 (7ᵉ série) # Retours d'usage du 2026-08-21 (7ᵉ série)
**Les lots Q à Y ont tous été traités le 2026-08-21** — voir `CLAUDE.md`, section « Lots Q à Y
— retours d'usage du 2026-08-21 ». Le texte d'origine de chaque lot est conservé ci-dessous : il
dit le **besoin**, que la décision ne remplace pas. Ce que la série laisse ouvert est listé en fin
de fichier.
Dix demandes remontées en usage. **Rien n'est acté** : ce qui suit est la matière brute, classée Dix demandes remontées en usage. **Rien n'est acté** : ce qui suit est la matière brute, classée
en lots, avec ce que la relecture du code a déjà établi. Deux items portent sur le même sujet en lots, avec ce que la relecture du code a déjà établi. Deux items portent sur le même sujet
(la flèche de retour) et sont réunis dans le lot S. (la flèche de retour) et sont réunis dans le lot S.
@@ -474,6 +479,10 @@ en lots, avec ce que la relecture du code a déjà établi. Deux items portent s
## Lot Q — L'ordre des champs du formulaire d'ajout ## Lot Q — L'ordre des champs du formulaire d'ajout
**Traité le 2026-08-21.** Ordre retenu : titre, type, thèmes, auteurs, rôles, reste. ⚠️ La
règle des rôles est **maintenue telle quelle** — deux auteurs au moins, BD ou non : la lier au
type aurait été un revirement.
Demande : dans « Ajouter un ouvrage », placer le **type de document juste après le titre**, puis Demande : dans « Ajouter un ouvrage », placer le **type de document juste après le titre**, puis
les **thèmes**, puis les **auteurs**, puis — **si c'est une BD** — les rôles, le reste ensuite. les **thèmes**, puis les **auteurs**, puis — **si c'est une BD** — les rôles, le reste ensuite.
@@ -495,6 +504,9 @@ est exactement le but ; le préremplir écrirait quelque chose de faux.
## Lot R — Le menu ne doit pas fuir au défilement ## Lot R — Le menu ne doit pas fuir au défilement
**Traité le 2026-08-21.** Un seul conteneur collant (bandeau + menu), plein écran sous 40 rem,
trois portes de sortie. Les règles restent en feuille **globale**.
Deux comportements demandés, un par famille d'appareil : Deux comportements demandés, un par famille d'appareil :
| Appareil | Aujourd'hui | Demandé | | Appareil | Aujourd'hui | Demandé |
@@ -523,6 +535,10 @@ mais il faudra aussi une fermeture explicite (croix, Échap, clic hors du menu)
## Lot S — La flèche de retour ## Lot S — La flèche de retour
**Traité le 2026-08-21**, et la question S1 est tranchée : le retour ne passe plus par
l'historique du tout. `RemonteeRoutes` calcule la parenté et rend **toujours** un chemin interne ;
`js/navigation.js` est supprimé.
Deux items remontés ensemble, qui ne demandent pas la même chose. Deux items remontés ensemble, qui ne demandent pas la même chose.
- **S1. Elle ne doit pas faire sortir de l'application.** ⚠️ **C'est censé être déjà tenu** : - **S1. Elle ne doit pas faire sortir de l'application.** ⚠️ **C'est censé être déjà tenu** :
@@ -549,6 +565,8 @@ Deux items remontés ensemble, qui ne demandent pas la même chose.
## Lot T — La fiche d'une série : les actions à droite du titre ## Lot T — La fiche d'une série : les actions à droite du titre
**Traité le 2026-08-21.** La barre flottante disparaît de la consultation d'une série.
Demande : sur `/series/{id}`, grouper **« Modifier »** et **« Changer l'ordre »** tout à droite, Demande : sur `/series/{id}`, grouper **« Modifier »** et **« Changer l'ordre »** tout à droite,
sur la ligne du titre et du compteur « x sur n tomes ». sur la ligne du titre et du compteur « x sur n tomes ».
@@ -564,6 +582,9 @@ sous-séries. Un bouton toujours présent mais inerte serait un recul.
## Lot U — Le nombre de pages sur la fiche d'un livre ## Lot U — Le nombre de pages sur la fiche d'un livre
**Traité le 2026-08-21.** Colonne nullable, `0` refusé, préremplissage depuis `dc:format` avec
des règles étroites — tout ce qui n'est pas « n p. » rend `null`.
Nouvelle colonne sur `Livre`, donc **migration**. Deux points à trancher avant de coder : Nouvelle colonne sur `Livre`, donc **migration**. Deux points à trancher avant de coder :
- **Nullable, obligatoirement.** Aucun livre déjà catalogué n'a cette information, et `0` page se - **Nullable, obligatoirement.** Aucun livre déjà catalogué n'a cette information, et `0` page se
@@ -581,6 +602,9 @@ S'il est saisi, il l'est comme une indication, pas comme une propriété du fich
## Lot V — Dans la liste des revues, toute la ligne s'ouvre ## Lot V — Dans la liste des revues, toute la ligne s'ouvre
**Traité le 2026-08-21**, en même temps que le lot W : le clic du titre ne remonte pas à la
ligne.
Aujourd'hui seul le **titre** est cliquable. C'est le motif inverse de ce que le projet tient Aujourd'hui seul le **titre** est cliquable. C'est le motif inverse de ce que le projet tient
ailleurs : sur une carte du catalogue, c'est la carte entière qui mène à la fiche. ailleurs : sur une carte du catalogue, c'est la carte entière qui mène à la fiche.
@@ -592,6 +616,9 @@ que le lot W, qui ajoute précisément des boutons sur ces écrans.
## Lot W — La fiche d'une revue : mêmes actions à droite, et l'édition d'un numéro ## Lot W — La fiche d'une revue : mêmes actions à droite, et l'édition d'un numéro
**Traité le 2026-08-21.** Tranché : édition et retrait d'un numéro **dans `/revues/{id}/edition`
seulement** ; « Ajouter un numéro » **reste en consultation**, le geste ne modifiant pas la fiche.
Symétrique du lot T, en trois demandes : Symétrique du lot T, en trois demandes :
1. **« Ajouter un numéro » et « Modifier la revue » groupés à droite** du titre. 1. **« Ajouter un numéro » et « Modifier la revue » groupés à droite** du titre.
@@ -609,6 +636,10 @@ accordée au statut de lecture et au prêt. Ne pas trancher au clavier : c'est u
## Lot X — Éditer une envie, et savoir ce qu'on peut souhaiter ## Lot X — Éditer une envie, et savoir ce qu'on peut souhaiter
**Traité le 2026-08-21.** `PUT /api/souhaits/{id}` existe, et le second volet est tranché : une
**table sœur `RevueSouhaitee`**, pas un titre libre. Ce qui reste ouvert est listé en fin de
fichier.
Demande : dans `/souhaits`, un bouton **« Éditer »** au-dessus de « Retirer », pour corriger le Demande : dans `/souhaits`, un bouton **« Éditer »** au-dessus de « Retirer », pour corriger le
livre souhaité. livre souhaité.
@@ -642,6 +673,10 @@ casser ; le second rouvre la question d'une table de plus.
## Lot Y — Une page « À propos » ## Lot Y — Une page « À propos »
**Traité le 2026-08-21.** `/a-propos`, entrée de menu **détachée** des six destinations,
`GET /api/version` — et l'horodatage de build comme **témoin** de l'injection, pour ne jamais
afficher le `1.0.0` du SDK.
Contenu demandé : **manuel** (à venir), **e-mail** `mailto:info@limonier.be`, **site** Contenu demandé : **manuel** (à venir), **e-mail** `mailto:info@limonier.be`, **site**
`www.limonier.be` dans une autre fenêtre, **version publiée**, et la **licence du dépôt**. `www.limonier.be` dans une autre fenêtre, **version publiée**, et la **licence du dépôt**.
@@ -663,3 +698,25 @@ désactivés faute de pouvoir aboutir.
Où la ranger : une septième destination dans le menu déséquilibrerait une navigation qu'on vient Où la ranger : une septième destination dans le menu déséquilibrerait une navigation qu'on vient
justement de reprendre (lot R). À peser — pied de page, ou entrée à part visuellement. justement de reprendre (lot R). À peser — pied de page, ou entrée à part visuellement.
---
# Ce que la 7ᵉ série laisse ouvert
Aux items déjà ouverts plus haut — **A5** (alléger les couvertures), **K1** (photographier une
couverture), **N2** (stocker l'URL finale après redirections), les **homonymes**, **OpenLibrary
en second rideau** et l'**EAN-2 à confirmer en kiosque** — la série du 2026-08-21 en ajoute trois.
- **Vérifier en navigateur tous les écrans touchés.** ⚠️ C'est le plus important : **aucun** n'a
été regardé s'afficher. Formulaire livre réordonné, menu collant et plein écran, flèche de
retour sur chaque branche, actions en tête des fiches série et revue, édition d'un numéro,
deux sections de la liste d'envies, page « À propos ». Les services et l'API sont couverts par
618 tests ; le rendu ne l'est par rien.
- **Réordonner les envies de revues.** Elles ont un `Rang` et une section à elles, mais aucun
écran pour les déplacer — les livres, eux, ont `/souhaits/ordre`. Le besoin n'est pas constaté :
la section est courte par nature. À faire le jour où elle ne l'est plus.
- **Signaler une envie de revue « déjà possédée ».** Les envies de livres le sont depuis le
2026-08-19. ⚠️ Le rapprochement est plus douteux ici : une envie porte souvent la **revue
entière** (`Numero` à `NULL`), et posséder un numéro ne comble pas ce souhait-là. Un signalement
faux sur une liste de cadeaux est pire que pas de signalement — même raisonnement que le grisage
faillible de la bibliographie.