diff --git a/CLAUDE.md b/CLAUDE.md index 237f82d..3298d48 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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/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 -**entier**, sans filtre), `auteurs`, `prets-en-cours`, `utilisateur`, et `souhaits` depuis le -2026-08-18 (voir « La liste d'envies est le cinquième instantané »). C'est exactement ce qui +**Un instantané par vue de l'API**, jamais un par requête. Ils étaient cinq le 2026-08-18 ; +ils sont **neuf** depuis le 2026-08-21 : `catalogue` (le catalogue **entier**, sans filtre), +`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 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 @@ -717,7 +721,9 @@ Livre ├── Editeur ├── Format : Physique | Numerique ├── TypeDocument : NonPrecise | Roman | BandeDessinee (défaut = NonPrecise, PAS Roman) +├── NombrePages : int? (⚠ NULL = « on ne sait pas » ; 0 est REFUSÉ, pas stocké) ├── CoverUrl +├── UrlNotice (notice BnF ou OpenLibrary ayant prérempli la fiche) ├── DateAjout └── AjoutePar (YNH_USER — traçabilité, PAS un cloisonnement) ⚠ 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) └── UNIQUE (Utilisateur, TitreNormalise, AuteurNormalise) 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. @@ -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 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 : -un même écran est atteignable par plusieurs chemins — une fiche livre s'ouvre depuis le -catalogue, une bibliographie, une série ou une recherche. +⚠️ **Ce qui suit a été RENVERSÉ le 2026-08-21** — voir « S — le retour est une remontée +hiérarchique ». Conservé parce que le motif d'origine reste vrai, et qu'il faut savoir +pourquoi il n'a pas suffi. -⚠️ `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. +> Le retour passe par l'**historique du navigateur**, jamais par une destination calculée : +> un même écran est atteignable par plusieurs chemins — une fiche livre s'ouvre depuis le +> catalogue, une bibliographie, une série ou une recherche. +> +> ⚠️ `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 @@ -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 : 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) 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 repose sur la compilation et la relecture, pas sur une exécution. À regarder au prochain 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 `` + 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. diff --git a/IDEES.md b/IDEES.md index 3193966..3dd5a83 100644 --- a/IDEES.md +++ b/IDEES.md @@ -466,6 +466,11 @@ Deux points à trancher avant de coder : # 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 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. @@ -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 +✅ **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 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 +✅ **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 : | 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 +✅ **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. - **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 +✅ **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, 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 +✅ **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 : - **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 +✅ **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 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 +✅ **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 : 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 +✅ **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 livre souhaité. @@ -642,6 +673,10 @@ casser ; le second rouvre la question d'une table de plus. ## 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** `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 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.