- Cataloguer en rafale — nouvel écran /ajout/rafale : on scanne une pile de livres à la suite dans une zone de texte, chaque code est traité (BnF puis OpenLibrary), les doublons connus sont passés automatiquement. La collecte marche même hors-ligne. Le compte rendu liste maintenant les livres créés, en lien vers leur fiche, et reste consultable en revenant sur l'écran même après une rafale entièrement réussie. Le catalogue reconnaît un ISBN dans sa barre de recherche (13 ou 10 chiffres, avec ou sans tirets) : scanner un livre en main ouvre directement sa fiche s'il n'y en a qu'un. Un bouton « Scanner » l'alimente, actif hors-ligne. - Ajouter un tome à une série accepte aussi un ISBN dans le champ manuel : le catalogue est cherché d'abord (rattachement direct si un seul exemplaire), sinon la BnF prend le relais. Séries et sagas - Numéro de tome distinct de la position de lecture : on peut indiquer « c'est le tome 7 » même si on ne possède pas les six premiers ; l'ordre de lecture reste un réglage séparé (utile pour les préquelles). - Tri par numéro en plus du tri par ordre de lecture quand des tomes en portent un. - Panneau « Ajouter » regroupé et repliable sur la fiche d'une série (manuellement / en rafale / depuis le catalogue / depuis les envies), au lieu de quatre formulaires ouverts en permanence. - Filtre catalogue « sans couverture » pour repérer les livres à illustrer. Le catalogue groupe les tomes d'une même série sous un bloc repliable, avec un décompte plus clair (affichés / possédés / total). - Corrections directes sur la fiche - Effacer un prêt saisi par erreur (bouton ✕ sur chaque ligne, avec confirmation), sans passer par « rendre ». - Corriger une couverture manquante ou cassée en cliquant dessus : le champ d'adresse s'ouvre focalisé, Entrée enregistre. Étendu aux numéros de revue dans la dernière modification. - Les thèmes déjà utilisés dans la bibliothèque sont proposés à la frappe. - La recherche d'un livre à rattacher montre des suggestions dès le focus, sans attendre de taper. - Visuel : un rendu manquant après une écriture asynchrone dans le formulaire de livre, une bascule de rôle cassée, des débordements à 320 px, et le style d'un bouton-lien qui restait souligné.
108 lines
4.3 KiB
Plaintext
108 lines
4.3 KiB
Plaintext
@using MaBibli.Client.Services
|
|
|
|
@*
|
|
Un bloc de série au catalogue, replié ou déplié — et récursif, parce qu'un cycle est une
|
|
série de séries : chaque niveau porte sa propre bascule.
|
|
|
|
⚠️ Tout est DÉPLIÉ par défaut, et l'on replie ce dont on ne veut pas. C'est la règle déjà
|
|
tenue par l'arbre des séries : l'inverse obligerait à ouvrir chaque bloc pour retrouver un
|
|
livre qu'on voyait très bien avant.
|
|
|
|
⚠️ Replié, le bloc annonce toujours ce qu'il contient : sans ce compte, le compteur du haut
|
|
du catalogue paraîtrait mentir.
|
|
|
|
⚠️ Et ce compte distingue trois choses (2026-09-08) : ce qui est AFFICHÉ (filtre compris), ce
|
|
qu'on POSSÈDE de cette série, et ce qu'elle compte de tomes. Un seul nombre suivait le
|
|
filtre — « 3 tomes » devenait « 1 tome » sous une recherche, et se lisait « cette série n'a
|
|
qu'un tome ». Les égalités se replient : sans filtre et série complète, on relit « 3 tomes ».
|
|
*@
|
|
<li class="groupe-serie" @key="Entree.Serie!.Id">
|
|
<div class="groupe-serie-entete">
|
|
@* ⚠️ Aria.Etat, et non le booléen nu : voir la mise en garde qu'il porte. *@
|
|
<button type="button" class="groupe-serie-plier"
|
|
aria-expanded="@Aria.Etat(Deplie)"
|
|
aria-label="@($"{(Deplie ? "Replier" : "Déplier")} « {Entree.Serie!.Titre} »")"
|
|
@onclick="Basculer">
|
|
<span aria-hidden="true">@(Deplie ? "▾" : "▸")</span>
|
|
</button>
|
|
|
|
@* Le nom mène à la SÉRIE : c'est le seul écran qui montre aussi les tomes manquants,
|
|
ce que le catalogue ne peut pas faire. *@
|
|
<a class="groupe-serie-titre" href="@($"series/{Entree.Serie!.Id}")">
|
|
<span class="groupe-serie-nom">@Entree.Serie!.Titre</span>
|
|
<span class="groupe-serie-compte">
|
|
@Libelles.CompteBloc(Entree.NombreLivres, Possedes, Total)
|
|
</span>
|
|
</a>
|
|
</div>
|
|
|
|
@if (Deplie)
|
|
{
|
|
@if (Entree.SousEntrees.Count > 0)
|
|
{
|
|
<ul class="liste-livres">
|
|
@foreach (var sous in Entree.SousEntrees)
|
|
{
|
|
<GroupeCatalogue Entree="sous" Replies="Replies" Totaux="Totaux" />
|
|
}
|
|
</ul>
|
|
}
|
|
|
|
@if (Entree.Livres.Count > 0)
|
|
{
|
|
<ul class="liste-livres">
|
|
@foreach (var livre in Entree.Livres)
|
|
{
|
|
<li @key="livre.Id">
|
|
<CarteLivre Livre="livre" />
|
|
</li>
|
|
}
|
|
</ul>
|
|
}
|
|
}
|
|
</li>
|
|
|
|
@code {
|
|
[Parameter, EditorRequired] public EntreeCatalogue Entree { get; set; } = default!;
|
|
|
|
/// <summary>
|
|
/// Blocs REPLIÉS, par identifiant de série — et non dépliés : le défaut est de tout montrer.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// État local de l'écran, volontairement non persistant, comme celui de l'arbre des séries :
|
|
/// c'est un confort de lecture, pas une propriété de la série. Il survit en revanche aux
|
|
/// rechargements du catalogue (frappe dans la recherche, filtre), le jeu appartenant à
|
|
/// l'écran et non au bloc.
|
|
/// </remarks>
|
|
[Parameter, EditorRequired] public ISet<int> Replies { get; set; } = new HashSet<int>();
|
|
|
|
/// <summary>
|
|
/// Compteurs par série, descendance comprise : ce qu'on possède, et ce que la série recense.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// ⚠️ Calculés une fois par l'écran (<c>TotauxSeries</c>), et non ici : le catalogue se relit
|
|
/// à chaque frappe dans la recherche, et chaque bloc les recalculerait pour tout l'arbre.
|
|
/// C'est aussi ce qui garantit le <b>même</b> compte que la fiche de la série.
|
|
/// </remarks>
|
|
[Parameter] public IReadOnlyDictionary<int, (int Possedes, int Total)>? Totaux { get; set; }
|
|
|
|
private (int Possedes, int Total) Compte =>
|
|
Totaux is not null && Totaux.TryGetValue(Entree.Serie!.Id, out var connu)
|
|
? connu
|
|
: (Entree.NombreLivres, Entree.NombreLivres);
|
|
|
|
private int Possedes => Compte.Possedes;
|
|
|
|
private int Total => Compte.Total;
|
|
|
|
private bool Deplie => !Replies.Contains(Entree.Serie!.Id);
|
|
|
|
private void Basculer()
|
|
{
|
|
if (!Replies.Add(Entree.Serie!.Id))
|
|
{
|
|
Replies.Remove(Entree.Serie!.Id);
|
|
}
|
|
}
|
|
}
|