Commit Graph
38 Commits
Author SHA1 Message Date
mathieuandClaude Opus 5 498579f27b Dis à l'application quelle version elle est, et sous quelle licence
Page « À propos » (/a-propos) : manuel annoncé à venir, contact, site de
l'auteur, version publiée avec sa date de build, et licence AGPL v3 avec le
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 donc pas un ornement.

L'application ne connaissait pas sa version : elle vivait dans le manifeste du
paquet et dans les tags git, jamais dans le binaire. `build/publier-release.sh`
pose désormais `-p:Version` et `-p:MaBibliDateBuild` ; l'API rend les deux par
`GET /api/version`.

⚠️ L'horodatage 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 pose par
défaut : 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.

Autres décisions :

- l'entrée du menu est DÉTACHÉE des six destinations, par un filet au-dessus
  sur téléphone et à 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 — une règle scopée n'atteint pas ce que rend un NavLink ;
- 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 ;
- la version est un sixième instantané hors-ligne : on la lit 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.

Le point de rupture de 40 rem reste identique dans les deux feuilles, et
/a-propos est inscrite dans la table de remontée des routes.

Vérifié en exécution, l'API lancée : sans injection
`{"numero":null,"publiee":false}`, avec `-p:Version=0.4.1
-p:MaBibliDateBuild=…` `{"numero":"0.4.1","publiee":true}`. Le rendu des écrans
n'a PAS été vérifié en navigateur. 618 tests au vert (605 avant).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:50:34 +02:00
mathieuandClaude Opus 5 0976da90be Sors du chemin de la consultation d'une revue ce qui la modifie
La fiche d'une revue portait tout à la fois : ses numéros, le formulaire
d'ajout, les champs de la revue et sa suppression. Elle prend la forme
déjà tenue par la fiche d'un livre et par celle d'une série — une route
pour regarder, une route pour changer.

« Ajouter un numéro » et « Modifier la revue » remontent à droite du
titre, dans le bloc d'en-tête partagé avec la fiche d'une série : les
deux actions sont visibles sans descendre au bas d'une collection de
trente numéros.

⚠️ Modifier et retirer un numéro n'existent plus QUE dans
/revues/{id}/edition. En consultation, ces deux 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.

⚠️ Ajouter un numéro, lui, 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, comme noter un prêt depuis la fiche d'un 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.

Un retrait de numéro se confirme désormais, comme la suppression d'un
livre ou d'une revue : il ne se défait pas, et la liste s'égrène sous le
pouce.

⚠️ 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.

/revues/{id}/edition entre dans la table de RemonteeRoutes : elle
retombait jusqu'ici sur le repli de branche, donc sur la liste des
revues au lieu de la fiche qu'on venait de quitter.

Deux tests de service viennent avec, sur ce dont l'écran dépend sans le
recalculer : la revue rendue par la modification d'un numéro revient
déjà rangée (corriger une parution déplace le numéro, les numéros sans
date fermant toujours la liste), et un retrait ne touche qu'au numéro
visé. 605 tests au vert.

Pas de vérification en navigateur : le rendu repose sur la relecture.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:07:51 +02:00
mathieuandClaude Opus 5 94f0ef15c0 Compte les pages d'un livre, et fais des envies une liste à deux moitiés
Lot U — le nombre de pages
`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 pour
`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 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, et les notices
déjà enregistrées sous Fixtures/ le montrent : « 1 vol. (113 p.) : ill., couv.
ill. en coul. ; 18 cm », « 503 p. : couv. ill. ; 17 cm ». Les règles 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) »,
qui est 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.

Lot X — éditer une envie, et souhaiter une revue
`PUT /api/souhaits/{id}` recalcule la clé d'œuvre et l'auteur normalisé : sans
ce recalcul, 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, œuvre, auteur), 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. Le rang n'est pas touché — l'ordre a son propre point d'entrée.

⚠️ 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), donc un
remplacement inconditionnel l'aurait effacée à la première faute de frappe
corrigée.

`RevueSouhaitee` est une table sœur, et non des colonnes de plus sur
`LivreSouhaite` : un numéro n'a pas d'auteur et se distingue par son numéro,
deux choses que la clé d'unicité des envies de livres 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. L'ISSN est canonisé avec son tiret,
seul code du projet rangé ainsi.

Le coût de la table sœur est payé partout où il devait l'être : affichage,
`.txt`, `.csv` et instantané hors-ligne `souhaits-revues`. ⚠️ Les revues forment
une SECTION à part plutôt que des lignes entrelacées : chaque table numérote son
rang indépendamment, et mélanger deux suites sans rapport produirait un ordre que
personne n'a choisi. Le `.txt`, groupé par auteur, ne pouvait de toute façon pas
les accueillir — elles n'en ont pas, et « Auteur non précisé » désigne des livres
dont l'auteur est inconnu. Le CSV gagne une colonne « Type » : sans elle, un tri
par titre rendrait revues et livres indiscernables, et la colonne des codes
mêlerait ISBN et ISSN en silence.

`ServiceRenormalisation` connaît la nouvelle table, avec la règle de collision
déjà en place. ⚠️ L'ISSN y est canonisé à part : `Renormaliser` n'applique rien
quand la clé ne bouge pas, un ISSN mal formé sur une ligne au titre inchangé y
échapperait.

`RevueSouhaitee` ne porte PAS de `CoverUrl` : rien à ajouter au garde de
`GET /api/couvertures`.

Vérifié en exécution : ISSN « 24666718 » rangé « 2466-6718 », édition de l'envie
d'un autre en 404, et les deux exports portant bien les deux moitiés.
602 tests au vert (552 au départ), aucun avertissement de compilation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 14:02:09 +02:00
mathieuandClaude Opus 5 1b47828c11 Remonte le type de document, colle le menu, et fais du retour une remontée
Cinq retours d'usage du 2026-08-21 qui ne touchent que l'interface (lots Q, R,
S, T et V d'IDEES.md). Aucune migration, aucune entité modifiée.

Q — L'ordre des champs du formulaire livre est une décision, pas une mise en
page : 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. Rien n'est présélectionné : « non précisé » ne
prétend toujours rien, et les rôles gardent leur règle actée (deux auteurs au
moins, BD ou non). Des tests verrouillent la préservation des rôles à chaque
frappe, que ce réordonnancement ne doit pas entamer.

R — Bandeau et menu passent dans un même conteneur collant : sur PC la rangée
des six destinations défilait avec la page et devenait inatteignable au bas
d'une longue liste. Un seul conteneur, et non deux éléments collants
superposés, qui auraient obligé à écrire en dur la hauteur d'un bandeau qui
varie avec la pastille hors-ligne et le nom d'utilisateur. Sur téléphone le
menu déployé occupe l'écran entier : sous le bandeau, il partageait l'écran
avec la liste qu'on quittait. Trois portes de sortie s'ajoutent à la fermeture
déjà en place sur LocationChanged — croix, Échap (le calque prend le focus à
l'ouverture, comme celui d'agrandissement des couvertures) et clic hors des
liens. Le plein écran est explicitement annulé au-delà de 40 rem, sans quoi un
menu ouvert au doigt puis une fenêtre agrandie laisseraient un calque sans
bascule pour le refermer. Toutes les règles du menu restent en feuille globale.

S — ⚠️ Le retour du bandeau devient une remontée hiérarchique d'un cran de
route, ce qui RENVERSE la décision actée le 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 — mais
l'historique remonte aussi les allers-retours (filtre, ordre, édition) et l'on
cliquait cinq fois sans quitter le même écran. La parenté est une table
explicite et testable, pas un découpage naïf d'URL : toutes les routes n'ont
pas la forme d'une arborescence, /auteurs/{id}/bibliographie remontant à la
liste des auteurs, dont il n'existe pas de fiche. Le garde-fou « ne jamais
sortir de l'application » y vit désormais aussi : la fonction rend toujours un
chemin interne, là où history.length ne disait pas ce qu'on croyait dans une
PWA standalone. js/navigation.js n'a plus d'utilisateur et disparaît.

T — Sur la fiche d'une série, « Modifier » et « Changer l'ordre » rejoignent la
ligne du titre et de l'avancement, groupés à droite comme la bibliographie et
la liste des auteurs le font déjà. Reléguées au bas de l'écran, les actions
d'une saga de vingt tomes ne se découvraient qu'après avoir déroulé la liste.
« Changer l'ordre » n'apparaît toujours qu'à partir de deux tomes ou deux
sous-séries.

V — Dans la liste des revues, toute la ligne ouvre la fiche, comme la carte
entière le fait 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.

⚠️ Aucune vérification en navigateur : le rendu de ces écrans repose sur la
compilation et la relecture. 552 tests au vert (506 avant ce lot).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 13:38:46 +02:00
mathieuandClaude Opus 5 7c050cb410 Coche toute la bibliographie visible, et montre ce qu'on ne possède pas
« Tout cocher » vit à côté du filtre et non dans la barre de sélection : celle-ci
n'apparaît qu'une fois une case cochée, c'est-à-dire trop tard pour rendre
service. « Tout » signifie ce qui est actuellement visible — après filtre, et
selon que les masquées sont affichées. Cocher les deux cents notices remontées
alors que l'écran n'en montre que trente serait précisément ce que les compteurs
par bouton cherchent à éviter.

La couverture d'une œuvre non possédée n'est rendue qu'au dépliage : une
bibliographie compte des dizaines de lignes, et les charger d'avance ferait payer
des images que personne ne regarde. Sans ISBN, pas de couverture et on n'en
invente pas : la vignette retombe sur son substitut à initiale.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 11:25:48 +02:00
mathieuandClaude Opus 5 7e47a5d6c0 Scanne depuis une série et depuis les envies, et y range un souhait
Le scanner s'ouvre dans la place visée d'une série, sous son champ ISBN. C'est ce
qui répond à « le retour doit ramener sur la série ET sur la place » : il n'y a
pas de retour, on n'a jamais quitté la place. Passer par l'écran d'ajout
obligeait à revenir rattacher à la main, donc à risquer le mauvais tome.

Même composant dans la voie « par ISBN » des envies, sans création de livre au
bout. L'add-on EAN-2 y est ignoré, et ce n'est pas un oubli : il ne concerne que
les revues, qui ne se souhaitent pas.

Ranger une envie dans une série se fait depuis la série — c'est là qu'on voit
l'ordre de lecture, donc là qu'on sait quelle position donner. Le geste crée une
place ordinaire, LivreId à NULL, titre repris de l'envie : rien ne relie les deux
en base, et c'est tout le sujet. Les séries sont communes au foyer, la liste
d'envies est personnelle, et son sens même est de préparer un cadeau sans que
l'autre le voie venir. Une clé étrangère afficherait « tome 3 souhaité par
untel » à tout le monde ; ce qui doit rester invisible est le lien, pas
l'existence du tome, qu'on aurait saisie à la main de toute façon.

L'envie survit au rattachement, comme une envie déjà au catalogue est signalée et
jamais supprimée — à plus forte raison ici, puisque rien n'a été acheté. Les
envies dont le titre est déjà un tome de la série ne sont pas proposées : la
place n'étant identifiée que par son titre, les offrir mènerait au doublon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 11:25:48 +02:00
mathieuandClaude Opus 5 1d35dc06f4 Donne aux numéros de revue leur couverture et leur une, et un tiret à l'ISSN
L'ISSN est désormais rangé sous sa forme à tiret, et pas seulement affiché ainsi.
C'est le seul endroit où le projet s'écarte de « la valeur stockée reste nue »,
et il y a une raison : le code-barres produit déjà un ISSN à tiret, et la BnF
interroge `bib.issn` avec le tiret. Un ISSN tapé « 24666718 » ne se rapprochait
donc de rien, et le scan suivant créait une seconde fiche à côté. La canonisation
a lieu avant la recherche, pas seulement à l'écriture. Les champs de saisie, eux,
gardent la valeur tapée : découper à la frappe se battrait avec le curseur.

`ServiceRenormalisation` rattrape les ISSN existants — par un passage dédié, le
mécanisme générique confondant tous les NULL en une seule clé, alors que
l'unicité est ici un index partiel. Une fiche dont la forme canonique est déjà
prise garde la sienne : le serveur doit démarrer.

La couverture d'un numéro est une URL collée, jamais une photo : aucune source ne
peut la fournir, l'ISSN désignant la revue et non la parution, et le projet ne
stocke aucun fichier. Le garde du relais de couvertures a donc été étendu aux
numéros — l'oublier n'aurait produit aucune erreur visible, seulement une image
présente en ligne et absente hors-ligne.

Les articles à la une vivent dans une table à part et non dans `Theme` : un thème
est un vocabulaire qu'on réutilise, un titre d'article est unique à sa parution.
Rien ne se partage, donc pas de n-n — une simple clé étrangère vers le numéro.
Ils se saisissent au point-virgule seul : « Ukraine, deux ans après » serait
coupé en deux par la virgule.

Un PUT sur le numéro était indispensable : on note un numéro le jour où on le
range et on en recopie le sommaire plus tard. Sans lui, couverture et une
n'auraient existé qu'à la création, c'est-à-dire jamais.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 11:25:29 +02:00
mathieuandClaude Opus 5 b8a5122b6f Rend l'attente visible, et met le champ de création sur une ligne
Une recherche BnF coûte environ une seconde par page, et jusqu'à dix pages pour
les nouveautés : sans rien à l'écran, l'application se lit comme plantée. Le lot
H l'avait montré — un écran qui ne dit rien pousse à recliquer, donc à relancer
l'appel.

Un indicateur ne remplace aucun message d'échec : une source muette garde ses
`EtatSourceBibliographie` et ses motifs. Ici on dit « ça travaille », jamais
« ça a marché ». L'animation se neutralise sous `prefers-reduced-motion`, la
rondelle restant visible : c'est elle qui porte l'information, pas sa rotation.

Le champ « nom d'une nouvelle série » et son bouton tiennent maintenant sur une
ligne. `min-width: 0` est ce qui la fait tenir à 320 px : sans lui, un élément de
formulaire refuse de rétrécir sous sa largeur intrinsèque et pousse le bouton
hors de l'écran.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 11:25:09 +02:00
mathieuandClaude Opus 5 0c4babd7b9 Une seule entrée d'ajout : « Ajouter un ouvrage »
Le geste réel est « j'ai un truc avec un code-barres », pas « je vais cataloguer un
livre » : obliger à savoir d'avance ce qu'on tient était un détour, d'autant que le
décodage distingue déjà un 978/979 d'un 977. Le même écran est atteint depuis le
catalogue et depuis les revues.

⚠️ Le MODÈLE ne bouge pas : une revue reste une Revue, jamais un Livre. Fondre les deux
tables aurait obligé à répéter « et qui n'est pas une revue » à chaque lecture du
catalogue, des compteurs, des doublons, des séries et des bibliographies.

- /ajout (alias /ajout/isbn) : « Ajouter un ouvrage », deux saisies manuelles distinctes
  puisque, sans code à lire, il faut bien trancher soi-même.
- Nouvelle page /revues/ajout : titre, ISSN et éditeur, puis la fiche de la revue. Elle
  remplace le champ posé au milieu de la liste des revues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:13:01 +02:00
mathieuandClaude Opus 5 bd4fea2638 Arbre des séries dépouillé, et ordre de lecture derrière son propre écran
- L'arbre ne montre plus que la descendance : titre, avancement, lien. Il dépliait
  auparavant la liste complète des tomes de chaque nœud, cartes de livres comprises —
  un cycle occupait plusieurs écrans et l'on ne voyait plus qui contient quoi. Corollaire :
  tout est déplié par défaut, et l'on replie ce dont on ne veut pas.
- /series ne charge plus le catalogue entier : l'arbre n'en a plus l'usage.
- ⚠️ Nouvelle route /series/{id}/ordre, sur le modèle de /souhaits/ordre. Les flèches
  n'existent plus qu'ici : en consultation, elles se déclenchaient au défilement du pouce
  et l'ordre de lecture d'une saga changeait sans qu'on l'ait voulu.
- Le mode ordre ne fait QUE réordonner : les actions par tome et l'ajout de tome n'y
  figurent pas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 20:45:14 +02:00
mathieuandClaude Opus 5 4fd67e4e3b Auteurs cherchables et triables, filtre de prêt, rang dans les exports, couvertures d'envies
Auteurs
- Recherche et tri (par nom, ou du plus présent au moins présent), en mémoire : la liste
  est déjà chargée en entier — c'est elle qui sert l'instantané hors-ligne — et la filtrer
  côté serveur aurait rendu la page inutilisable sans réseau.
- Trois colonnes dont une seule est élastique : compteur à gauche sur une largeur figée,
  nom au milieu, actions à droite. Les compteurs et les boutons s'alignent alors d'une
  ligne à l'autre au lieu de suivre la longueur du nom.

Catalogue
- Filtre « À la maison / Prêtés », posé dans les deux implémentations (serveur et
  hors-ligne) et dans le test qui les confronte, avec un cas de prêt CLOS — c'est
  exactement là que les deux auraient pu diverger en silence.
- La ligne disparaît tant que rien n'est prêté, comme celles des formats et des types.

Envies
- ⚠️ Les couvertures manquaient parce que seul l'écran « ajouter une envie » remplissait
  CoverUrl : la bibliographie et les tomes manquants transmettent un ISBN mais aucune
  image. Le repli est posé à la LECTURE, donc il vaut aussi pour les envies déjà
  enregistrées, sans migration.
- Les exports .txt et .csv portent le rang d'envie. Le .txt en a le plus besoin : son
  groupement par auteur détruit précisément cet ordre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 20:42:13 +02:00
mathieuandClaude Opus 5 83089d7479 Menu en haut, retour explicite, et bibliographie qui ne rappelle plus la BnF
Navigation
- La barre d'onglets du bas devient un menu en haut, sur le repli du gabarit Blazor :
  bascule sur téléphone, rangée sur PC. Cela renverse la décision du 2026-08-18 (le
  pouce atteint le bas de l'écran) : le raisonnement reste juste, mais il ne vaut rien
  face à une barre qui s'affiche mal. Le point tenu est qu'il existe désormais un état
  de repli — sans feuille de style, il reste une liste de liens lisible.
- Bouton « retour » dans le bandeau, sur tous les écrans, appuyé sur l'historique du
  navigateur, avec repli sur le catalogue quand la pile est vide (PWA ouverte depuis
  l'écran d'accueil).
- Le pictogramme de la marque passe en SVG en ligne : le fichier logo-bandeau.svg
  pouvait manquer sur un appareil dont le cache précédait son ajout, d'où l'icône cassée.

Bibliographie
- Case à cocher à gauche du titre, ligne centrée verticalement, boutons groupés à droite.
- Barre de sélection collante EN HAUT, avec masquage groupé et « tout décocher ».
- « Nouveautés » et « Ses livres chez vous » remontent à côté du filtre.
- ⚠️ « Je le veux » et « Masquer » ne rechargent plus rien : ils basculent un drapeau
  personnel que le client connaît déjà. Auparavant chaque clic vidait le cache des
  bibliographies et relançait la BnF (une seconde par page, jusqu'à dix pages pour les
  nouveautés, qui ne sont pas mises en cache) puis relisait tout le catalogue.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 20:35:39 +02:00
LIMONIER MathieuandCopilot a0cb727ff0 Filtre les prêts en cours par nom d'emprunteur (lot G)
Ajoute un champ de recherche insensible aux accents sur l'écran des
prêts en cours, visible dès qu'il y a plus d'un prêt. Filtrage en
mémoire, sans colonne normalisée : la liste est courte par
construction, une forme dédiée en base serait disproportionnée.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 14:42:23 +02:00
LIMONIER MathieuandCopilot c2d408afc6 Ajoute les thèmes communs sur les fiches de livres
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 14:38:19 +02:00
LIMONIER MathieuandCopilot e28d5bce32 Ajoute les notices sources et le renommage fusionné des auteurs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 14:27:42 +02:00
LIMONIER MathieuandCopilot 6b8f08ef26 Ajoute ISBN, tris et nouveautés d'auteur
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 13:49:08 +02:00
LIMONIER MathieuandCopilot 359f4f59c6 Transforme la bibliographie en écran de travail D1 à D5
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 13:42:46 +02:00
LIMONIER MathieuandCopilot ddc60898da Implémente C1 à C5 pour les séries et cycles
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 13:27:49 +02:00
LIMONIER MathieuandCopilot 3d5e73838e Sépare consultation et édition pour les lots B1 B2 B3
Réutilise la carte livre du catalogue, sépare les modes des séries et des envies, et déplie les œuvres bibliographiques non possédées.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 13:11:43 +02:00
LIMONIER MathieuandCopilot 5d45eb492e Traiter le lot A d'IDEES.md : CSS onglets, pastilles, identité visuelle, couverture cassée, cache couvertures
- A1 : forcer la revalidation HTTP (Cache-Control: no-cache) sur les fichiers statiques,
  pour empêcher un navigateur de garder indéfiniment un CSS/JS périmé quand le service
  worker ne s'active pas.
- A2 : le filtre du catalogue (statut, type de document) reprend les mêmes pastilles
  colorées que les cartes (Libelles.ClasseStatut / nouvelle Libelles.ClasseTypeDocument).
- A3 : remplacement du logo par défaut de Blazor par un pictogramme de livre ouvert
  (favicon, icônes PWA, logo du bandeau, logo mabibli_ynh).
- A4 : une couverture cassée désactive aussi son bouton d'agrandissement.
- A5 : cache IndexedDB des couvertures pour la consultation hors-ligne, sans ralentir
  l'affichage en ligne (mise en cache en tâche de fond). L'allègement (redimensionnement,
  WebP) reste hors scope, à mesurer avant de s'y engager.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 13:03:14 +02:00
mathieuandClaude Opus 5 07cb48b2be Distinguer scénario, dessin et couleurs sur chaque livre
Le rôle vit sur le LIEN livre-auteur, jamais sur l'auteur : le même auteur
scénarise un album et dessine le suivant, et il ne doit pas exister en deux
fiches pour autant — ce que l'index unique sur la clé de regroupement
interdit de toute façon.

Les rôles se retrouvent par clé d'auteur résolue et non par position :
ResoudreAsync écarte les noms vides et fusionne les variantes, donc sa
liste peut être plus courte que la saisie.

Piège traité : le champ des noms reconstruit la liste à chaque frappe.
Decouper reprend les rôles connus en rapprochant sur la forme normalisée,
sans quoi corriger une faute de frappe les effacerait tous.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:37:20 +02:00
mathieuandClaude Opus 5 9ab6f23b8c Lire l'add-on EAN-2 et proposer le numéro de la revue scannée
Découverte à la mesure : l'EAN-2 est DÉSACTIVÉ par défaut dans zbar. Il ne
suffisait donc pas de cesser de le filtrer — il n'était jamais produit.
Vérifié sur un EAN-13 + EAN-2 rendus en canvas : un symbole par défaut,
deux après setConfig(ZBAR_EAN2, ZBAR_CFG_ENABLE, 1).

L'EAN-5 reste désactivé : il porte un prix, pas un numéro.

Ce qui reste non vérifié — que les magazines réels y mettent bien leur
numéro de parution — est dit à l'utilisateur : le numéro est proposé dans
un champ modifiable, sous une invitation à le comparer à la couverture.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:30:21 +02:00
mathieuandClaude Opus 5 ca0a9dc18c Cataloguer les revues : une fiche, ses numéros à l'intérieur
Clôt la décision du premier lot, qui écartait les périodiques faute de
modèle. Douze numéros d'un magazine ne font plus douze fiches identiques.

Table à part, pour la troisième fois et pour le même motif que la liste
d'envies et les séries : logée dans Livres, une revue entrerait dans le
catalogue, ses compteurs, les doublons et les bibliographies, et il
faudrait écrire « et qui n'est pas une revue » à chaque lecture.

Un numéro est recensé, rien de plus — ni prêt ni statut, tous deux liés à
Livre par clé étrangère. Réversible.

« Créer ou retrouver » et non « créer » : scanner le numéro suivant du même
magazine retombe forcément sur la même revue. L'unicité de l'ISSN est un
index partiel, sans quoi les revues sans ISSN se bloqueraient entre elles.

Le flux 977 mène désormais à la fiche de la revue, et ne pré-remplit plus
le formulaire d'un livre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:19:08 +02:00
mathieuandClaude Opus 5 4e372ab694 Ajouter les sagas et cycles, avec leurs tomes manquants
Première relation entre livres du projet. Une série contient des séries
(un cycle EST une série de séries) et des PLACES dans l'ordre de lecture —
pas des livres : une place sans livre est le trou qu'on vient voir.

L'ordre est stocké, jamais déduit d'une année : une préquelle se lit avant
le livre paru dix ans plus tôt. Les tomes non possédés se saisissent à la
main, aucune source ne donnant l'ordre de lecture d'une saga.

Le titre de la place survit à la suppression du livre (SetNull, pas de
cascade), sans quoi perdre un exemplaire effacerait le tome 3 de la série.

Les séries sont communes au foyer. Seule exception : la mise en envies d'un
tome manquant, qui écrit dans une liste personnelle — c'est le seul point
où les deux portées se rencontrent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:03:08 +02:00
mathieuandClaude Opus 5 49eb8e1c98 Distinguer romans et BD par un type de document
Format disait physique ou numérique, jamais roman ou BD. Trois valeurs
seulement : une valeur s'ajoute sans migration, la renommer une fois posée
sur des centaines de fiches, non.

Le défaut est « non précisé », pas « roman » : les fiches existantes n'ont
pas été saisies avec cette question en tête, et un AddColumn suffit
justement parce que la valeur par défaut ne prétend rien. L'interface
n'affiche donc que ce qui a été choisi, comme pour le format.

Filtre ajouté aux DEUX implémentations, serveur et hors-ligne, avec quatre
jeux de critères de plus dans le test qui les confronte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:45:37 +02:00
mathieuandClaude Opus 5 a49125ddc3 Signaler les envies déjà entrées au catalogue, sans les supprimer
La demande initiale était de retirer l'envie ; c'est refusé. Le catalogue
est commun et la liste d'envies personnelle : supprimer modifierait la
liste d'un autre en silence, avec sa note. Et le rapprochement par clé
d'œuvre est faillible, alors qu'une suppression ne se rattrape pas.

L'étiquette « Déjà au catalogue » mène à la fiche, pour vérifier avant de
retirer. Le critère est celui des doublons, la convention d'auteur commun
étant désormais partagée par les deux (RapprochementAuteurs).

Au passage : accolade manquante sur .etiquette-souhaite, qui avalait la
règle suivante — le bandeau de mise à jour perdait son style.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:30:55 +02:00
mathieuandClaude Opus 5 383ed43227 Signaler les doublons à l'ajout d'un livre, sans jamais les refuser
Rien n'empêchait de rescanner un livre déjà catalogué. Plutôt qu'un index
unique — qui interdirait le second exemplaire, cas parfaitement légitime —
la création répond 409 avec les fiches semblables, et « confirmerDoublon »
enregistre la même saisie.

Deux critères, dont aucun n'est une clé : ISBN identique, ou clé d'œuvre
et auteur communs. La clé d'œuvre étant un préfixe de TitreNormalise, SQL
dégrossit sur la colonne indexée et l'égalité exacte se vérifie ensuite en
mémoire.

Vérifié en exécution : avertissement, retour au formulaire intact, et ajout
confirmé créant bien un second exemplaire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:11:39 +02:00
mathieuandClaude Opus 5 5cf566bc33 Traiter les retours d'usage du 2026-08-18 (2ᵉ série)
Six lots, issus d'IDEES.md. Les décisions actées sont reportées dans
CLAUDE.md, et les entrées traitées retirées d'IDEES.md.

1. Douchette USB, ISSN et enchaînement du scan
   - Champ ISBN focalisé à l'ouverture : c'est tout ce qu'exige une
     douchette USB, qui se présente au système comme un clavier. Remède
     au scan caméra, qui rate sur la webcam d'un PC (optique, pas
     décodage).
   - Un EAN en 977 est un périodique : il porte un ISSN, donc un titre de
     revue. Intercepté AVANT la validation ISBN (c'est un EAN-13
     parfaitement valide), l'ISSN est déduit du code puis nommé via
     bib.issn. Auparavant la cascade s'exécutait en entier pour finir sur
     « aucun résultat ».
   - L'enchaînement après scan existait déjà mais était invisible : une
     étape « Recherche » affiche désormais le code interrogé.

2. ISBN affichés avec des tirets
   Tables extraites du RangeMessage.xml officiel — plusieurs tranches ne
   sont pas celles qu'on suppose. Le francophone est découpé en entier,
   ailleurs on s'arrête au groupe et à la clé : aucune coupure fausse.
   Corrige au passage l'export CSV, où un ISBN nu était lu comme un
   nombre par Excel.

3. Hors-ligne : la liste d'envies
   ListerSouhaitsAsync était le seul point de lecture hors du dispositif
   hors-ligne, d'où le « 404 Not Found » brut à l'écran. Cinquième
   instantané, écritures refusées, plus aucun message HTTP. Même défaut
   corrigé sur la bibliographie.

4. Navigation par onglets
   Catalogue / Auteurs / Prêts / Envies dans MainLayout ; les barres
   d'actions ne portent plus que des actions. Filtres repliés derrière un
   bouton compteur, ligne « format » masquée quand le fonds n'a qu'un
   format. Une seule entrée d'ajout, désactivée hors-ligne — pas masquée.

5. Liste d'envies : ordre, recherche, couvertures
   Migration RangDesEnvies. Le remplissage reconduit l'ordre affiché
   jusqu'ici : sans lui, les listes existantes se seraient réordonnées
   toutes seules. Réordonnancement par flèches et glisser-déposer (le
   drag & drop HTML5 ne marche pas au doigt). Ajout dans son propre
   écran, avec recherche par titre (bib.title) et couvertures enfin
   alimentées.

6. Bibliographie : une source muette n'est pas une liste vide
   L'écran affichait « BnF injoignable » PUIS « la BnF ne connaît aucun
   livre de cet auteur » — la seconde phrase étant fausse. Les deux cas
   s'excluent désormais, et un bouton Réessayer est offert.

   Vérifié en exécution : le diagnostic d'IDEES.md était faux sur un
   point. Robert A. Harper a bien 7 œuvres à la BnF (85 notices
   annoncées) ; c'était le même délai dépassé observé deux fois, pris
   pour deux causes distinctes.

380 tests, dont un qui applique réellement la migration (EnsureCreated
n'en joue aucune) et un qui verrouille les messages atteignant
l'utilisateur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 00:14:01 +02:00
mathieu 580d8469ac Fusionner le cache hors-ligne IndexedDB
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

# Conflicts:
#	MaBibli.Client/wwwroot/css/app.css
2026-08-18 14:06:07 +02:00
mathieuandClaude Opus 5 ebc5f95d49 Consulter la bibliotheque hors-ligne depuis un cache IndexedDB
Le piege que CLAUDE.md signale : en Blazor WebAssembly le code tourne dans le
navigateur alors que SQLite vit sur le serveur, et le service worker ne met en
cache que les assets. Sans travail explicite, l'application demarre hors-ligne
et affiche une bibliotheque vide.

- js/cache-hors-ligne.js : instantanes JSON dans IndexedDB, et surveillance des
  bascules online/offline. Aucune logique metier.
- CacheHorsLigne / EtatReseau : lecture-ecriture des instantanes, et etat reseau
  combinant navigator.onLine (fiable seulement par sa negation) avec le sort
  reel des appels HTTP.
- ServiceLivresApi : les lectures retombent sur le cache, les ecritures sont
  refusees. Le catalogue entier est memorise, pas les reponses filtrees : c'est
  ce qui rend la recherche hors-ligne possible sur tout le fonds.
- FiltreLivresLocal : pendant navigateur de FiltreLivres, avec un test qui
  confronte les deux implementations sur les memes donnees.
- Interface : pastille et bandeau d'etat avec la date de synchronisation, et
  actions d'ecriture desactivees avec leur raison plutot que boutons morts.
- js/mise-a-jour.js : les empreintes WASM etant desactivees, le service worker
  est le seul cache-busting du projet. L'enregistrement journalise desormais ses
  echecs, et un bandeau propose la nouvelle version sans attendre la fermeture
  de tous les onglets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 13:59:19 +02:00
mathieuandClaude Opus 5 8b8fe3be6e Ajouter la liste d'envies personnelle, son export et la bibliographie par auteur
La liste d'envies vit dans une table séparée (LivreSouhaite) plutôt que dans un
statut de plus sur Livre : un livre souhaité n'est pas possédé, et le loger dans
Livres l'aurait fait entrer dans le catalogue, les compteurs et les prêts, au
prix d'un « et qui n'est pas souhaité » à répéter dans chaque lecture. La portée
est personnelle, comme le statut de lecture — mais ici Utilisateur est une vraie
frontière : toute lecture filtre dessus.

Bibliographie : SRU BnF interrogé par bib.author, vérifié le 2026-08-18. Deux
filtres mesurés sur des réponses réelles sont indispensables — le type de
document (l'index mêle livres audio, jeux et spectacles) et surtout l'auteur
réel de la notice, « all » rapprochant les mots sur l'ensemble des auteurs :
« Émile Zola » remonte sinon toute l'œuvre de sa fille Denise Le Blond-Zola.
Les rééditions sont regroupées par clé d'œuvre (183 notices Werber -> 51
œuvres). Le rapprochement avec l'étagère se fait par titre, pas par ISBN, qui
désigne une édition et non une œuvre ; ses limites sont dites à l'écran.

Export en deux formats, tous deux du texte sans dépendance : .txt groupé par
auteur pour la librairie, .csv à séparateur point-virgule et BOM pour le
tableur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 13:59:06 +02:00
mathieuandClaude Opus 5 6b783bafa0 Appliquer les retours d'interface : fiche en lecture seule, liste allegee
Cinq retours d'IDEES.md, tous pensés pour le mobile d'abord.

Fiche du livre en deux modes, portés par deux routes : « /livres/{id} »
consulte, « /livres/{id}/edition » modifie. Ouvrir une fiche depuis le
catalogue ne place plus un formulaire vivant sous le pouce. Mettre le mode
dans l'adresse fait du retour navigateur une sortie d'édition naturelle.

Piège rencontré : les deux routes visent le même composant avec le même Id,
donc aucun paramètre ne change et le routeur ne redessine pas — l'URL passait
à « /edition » sans que l'écran bascule, alors qu'un chargement direct de
cette URL marchait, ce qui masque le défaut. Le composant s'abonne donc à
LocationChanged.

Restent hors « Éditer », parce que ce ne sont pas des modifications de la
fiche : le statut de lecture (action la plus fréquente, et personnelle) et
les prêts (gestes du quotidien, donnée du foyer). La suppression, elle, est
réservée au mode édition.

Dans la liste, le statut devient une pastille en lecture seule sur la ligne
des étiquettes ; les trois boutons actionnables par carte partaient trop
facilement en faisant défiler. Le format ne s'affiche plus que pour les
ebooks, le physique étant le cas par défaut — le formulaire le
présélectionne déjà, l'énumération valant 0.

Couverture agrandissable au clic, fermeture au clic extérieur ou par Échap
(le calque est focalisé à l'ouverture, sans quoi Échap resterait sans effet).
Le substitut à initiale n'est jamais cliquable : le déclencheur n'existe que
s'il y a une image.

Le regroupement par auteur existait déjà (table Auteur, page /auteurs, filtre
« ?auteur= ») ; y manquaient les liens depuis la fiche et les styles de la
page auteurs, jamais écrits — la liste sortait en puces brutes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 07:13:37 +02:00
mathieuandClaude Opus 5 2a3dfb88de Gerer les prets : API, ecrans et contrainte en base
L'entite Pret existait depuis le squelette sans jamais etre exploitee. Cette
phase la met en service de bout en bout.

API
- GET  /api/prets/en-cours       ce qui n'est pas a la maison, du plus ancien
                                 au plus recent : on cherche le livre oublie,
                                 pas celui prete hier
- POST /api/livres/{id}/prets    preter
- GET  /api/livres/{id}/prets    historique complet, du plus recent au plus
                                 ancien
- POST /api/prets/{id}/retour    clore le pret sans le supprimer

Un livre deja sorti ne peut pas etre prete une seconde fois. Le service le
verifie et nomme celui qui l'a deja, mais entre sa verification et l'insertion
il reste une fenetre : un index unique PARTIEL (LivreId WHERE DateRetour IS
NULL) la ferme, tout en laissant l'historique accumuler autant de prets clos
que necessaire sur le meme livre.

Les ebooks sont refuses cote API, pas seulement grises dans l'interface : une
fiche n'a pas d'exemplaire a confier.

Les prets sont COMMUNS au foyer, symetrique inverse du statut de lecture. Le
service ne recoit meme pas d'identite, pour qu'on ne puisse pas s'en servir
par inadvertance.

Interface, pensee mobile d'abord
- ecran « Prets en cours » avec bouton « Rendu » a meme la liste
- bloc pret sur la fiche d'un livre : etat courant, action, puis historique
- etiquette « Prete a X » dans le catalogue
- la barre d'actions passe sur deux lignes plutot que de comprimer ses
  libelles maintenant qu'elle compte quatre entrees

Toutes les dates sont en UTC ; le client convertit la date locale du
<input type="date"> avant l'envoi, faute de quoi le pret se decalerait d'un
jour pour la moitie du globe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:45:01 +02:00
mathieuandClaude Opus 5 d9e6cf425d Fusionner la phase base de donnees (statut par utilisateur, table Auteur)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:24:09 +02:00
mathieuandClaude Opus 5 b594a4b2c1 Sortir le statut de lecture et l'auteur de la table Livre
Deux changements de modele en une seule migration, plus la recherche qui
en depend.

Le statut de lecture devient personnel. Il etait une colonne de Livre,
donc partage par tout le foyer, alors que deux membres lisent le meme
exemplaire a des rythmes differents. Il vit desormais dans une table
(LivreId, Utilisateur, Statut) avec unicite sur le couple. L'absence de
ligne vaut « non commence » : sur une bibliotheque de foyer la plupart
des couples n'ont aucun statut, et les materialiser tous multiplierait
les lignes par le nombre de comptes pour n'exprimer qu'un vide. Rien
n'est donc ecrit a la creation d'un livre.

Piege rencontre : un Dictionary<int, Statut> renvoyait la valeur 0 de
l'enumeration — « À lire » — pour un livre sans ligne, rendant « non
commence » indiscernable d'un choix explicite. Le dictionnaire est
desormais typé Statut?.

L'auteur devient une table. Deux formes normalisees y cohabitent, et ce
n'est pas une redondance : NomNormalise garde l'ordre de saisie pour la
recherche en sous-chaine, CleRegroupement trie les mots et porte l'index
unique, donc l'invariant « un auteur, une fiche ». Les initiales
echappent a la cle et sont traitees en memoire, sur une table qui compte
au plus quelques centaines de lignes.

Un livre peut avoir plusieurs auteurs — le lookup ISBN en renvoie quatre
pour Introduction to Algorithms — d'ou la table de liaison, avec une
position qui conserve l'ordre de la couverture.

Les rapprochements ambigus ne sont jamais appliques seuls : l'API les
liste, l'utilisateur accepte ou refuse, et les refus sont memorises pour
que la suggestion ne revienne pas. Le couple refuse est range par
identifiant croissant, donc un refus vaut dans les deux sens.

Reprise des donnees existantes. L'ancien statut, commun, est rattache a
AjoutePar — seule personne que la base associe au livre. Les statuts des
livres sans AjoutePar sont perdus : les attribuer serait une invention.
Les trois valeurs sont reprises telles quelles, « À lire » compris, parce
que c'est ce que l'ancienne interface affichait. L'ancien champ auteur
devient une fiche par valeur distincte.

La migration ne peut pas tout faire : lower() de SQLite ne retire pas les
accents, donc « Émile Zola » et « emile zola » y restent deux fiches.
ServiceRenormalisation finit le travail en C# au demarrage, reunit ces
variantes, applique aussi la regle des initiales — sans quoi une base
heritee resterait eclatee la ou une saisie neuve aurait ete reunie
d'emblee — et garde le nom d'affichage le plus presentable. Il est
idempotent, et sert de filet si les regles de normalisation changent.

L'ordre de la migration compte : les colonnes condamnees sont recopiees
dans une table de transit avant d'etre supprimees, parce que supprimer
une colonne sous SQLite reconstruit la table.

Verifie sur une base a l'ancien schema contenant 9 livres, 2 prets et
trois variantes de Zola : prets intacts, statuts rattaches, les trois
Zola reunis sous « Émile Zola », « P.F. Hamilton » absorbe par
« Peter F. Hamilton », « Hamilton » seul laisse en suggestion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 02:22:46 +02:00
mathieuandClaude Opus 5 f7ad9582f6 Ajouter le scan camera du code-barres ISBN
Le decodage EAN-13/EAN-8 est fait en C# par ZXing.Net (IsbnScanner), conformement
a la decision actee dans CLAUDE.md : le JavaScript ne fournit que les pixels de la
camera, aucune logique de decodage ne lui est confiee.

Le scan se greffe en amont du flux d'ajout par ISBN existant : des qu'un code est
lu et reconnu comme ISBN-13 valide, la recherche de notices est declenchee sans
que l'utilisateur ait a retaper quoi que ce soit. La saisie manuelle reste
accessible en permanence, y compris quand la camera echoue.

Points traites explicitement :
- camera arriere par defaut (facingMode "environment", en "ideal" pour ne pas
  echouer sur un PC sans camera arriere) ;
- liberation de la camera a l'annulation, au succes et au demontage du composant ;
- cadence limitee a ~12 images/s et decodage restreint a la bande centrale
  reduite a 640 px de large : inutile de decoder chaque frame en pleine taille ;
- messages distincts pour permission refusee, absence de camera, camera occupee
  et contexte non securise (getUserMedia exige HTTPS ou localhost : une IP de
  reseau local en http ne marchera jamais, c'est une source de confusion garantie).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 23:32:05 +02:00
mathieuandClaude Opus 5 fd6438b912 Phase 3 : CRUD des livres, statuts de lecture et identite SSOwat
API
- CRUD complet sur /api/livres : liste filtrable (format, statut) et
  cherchable (titre, auteur), consultation, creation, modification,
  changement de statut isole, suppression.
- Aucune lecture ne filtre sur AjoutePar : la bibliotheque est commune au
  foyer. Le critere n'existe meme pas dans CritereLivres, pour qu'on ne
  puisse pas s'en servir par inadvertance. Deux tests verrouillent
  l'invariant, dont un par reflexion sur le type des criteres.
- AjoutePar est renseigne par le serveur a la creation, jamais par le
  client, et n'est pas reecrit a l'edition (pas plus que DateAjout).
- Identite lue dans les en-tetes SSOwat (YNH_USER, YNH_USER_EMAIL,
  YNH_USER_FULLNAME), avec repli sur un utilisateur simule configure en
  developpement uniquement. Sans en-tete ni simulation, l'identite reste
  inconnue plutot qu'inventee. Expose par GET /api/moi.
- Migration appliquee au demarrage : pas d'etape manuelle a l'installation.

Interface Blazor, pensee mobile d'abord
- Catalogue unique (physiques et numeriques melanges, distingues par le
  format) avec recherche, filtres et changement de statut directement dans
  la liste.
- Ajout par ISBN : quand la cascade rend plusieurs notices, un ecran de
  choix les presente en mettant en avant editeur et annee, seuls elements
  qui les departagent. Le formulaire reste ensuite entierement modifiable.
- Ajout manuel, edition, suppression avec confirmation explicite.
- Couvertures : l'URL OpenLibrary n'est jamais validee par un appel
  prealable (502 intermittents mesures en phase 2) ; un attribut onerror
  masque l'image cassee et laisse apparaitre un substitut.

Decisions prises la ou CLAUDE.md etait muet
- Tri alphabetique par titre.
- Un ISBN saisi doit etre valide, mais reste facultatif.
- Ecran de choix affiche seulement a partir de deux candidats.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 22:55:07 +02:00
mathieuandClaude Opus 5 4990e41f3e Phase 1 : squelette de la solution (3 projets .NET 10)
Mise en place de la structure de base, sans aucune fonctionnalité métier :

- MaBibli.sln à la racine, avec trois projets ciblant net10.0
- MaBibli.Client : Blazor WebAssembly PWA (dotnet new blazorwasm --pwa),
  pages d'exemple du modèle (Counter, Weather) retirées
- MaBibli.Api : ASP.NET Core, sert aussi les fichiers statiques du client
  (UseBlazorFrameworkFiles + MapFallbackToFile), via le package
  Microsoft.AspNetCore.Components.WebAssembly.Server
- MaBibli.Shared : entités et enums partagés (Livre, Pret, Format, Statut),
  conformes au modèle de données de CLAUDE.md — AjoutePar est une simple
  traçabilité, Pret est une table séparée pour garder l'historique complet
- EF Core + SQLite dans l'API : MaBibliDbContext, chaîne de connexion,
  et migration initiale InitialCreate vérifiée par un database update
- dotnet-tools.json : dotnet-ef épinglé en outil local

Vérifié : dotnet build sans avertissement, et
dotnet publish MaBibli.Api -c Release -r linux-x64 --self-contained
produit un dossier unique dont le wwwroot contient bien _framework/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:52:37 +02:00