- Catalogage rapide (douchette, code-barres)
- 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é.
This commit is contained in:
@@ -189,15 +189,53 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
@* La couverture d'un numéro ne vient d'aucune source — l'ISSN désigne la
|
||||
revue, pas la parution : sans URL collée, c'est le substitut à
|
||||
initiale qui s'affiche, et il n'est pas cliquable. *@
|
||||
@*
|
||||
La couverture d'un numéro ne vient d'AUCUNE source — l'ISSN désigne
|
||||
la revue, pas la parution : elle se colle à la main, ou il n'y en a
|
||||
pas. C'est donc ici que le clic pour la poser sert le plus.
|
||||
|
||||
⚠️ Même principe que la fiche livre : cliquer une couverture absente
|
||||
ou cassée ouvre son adresse, focalisée, et Entrée enregistre. Ce
|
||||
n'est pas « modifier le numéro » — c'est réparer ce qu'on a sous les
|
||||
yeux, et c'est pourquoi ce geste-là existe aussi en consultation,
|
||||
contrairement à « Modifier » et « Retirer ».
|
||||
*@
|
||||
<Couverture Url="@numero.CoverUrl" Titre="@($"N° {numero.Numero}")"
|
||||
Classe="couverture-petite" Agrandissable="true" />
|
||||
Classe="couverture-petite" Agrandissable="true"
|
||||
OnCorriger="() => OuvrirCorrectionCouverture(numero)" />
|
||||
|
||||
<div class="carte-corps">
|
||||
<p class="carte-titre">N° @numero.Numero</p>
|
||||
|
||||
@if (_correctionCouverture == numero.Id)
|
||||
{
|
||||
<div class="barre-recherche ligne-creation">
|
||||
<input class="champ-saisie" type="url" @ref="_champUrlCouverture"
|
||||
@bind="_urlCouverture" @bind:event="oninput"
|
||||
@onkeydown="@(e => SurToucheCouverture(e, numero))"
|
||||
placeholder="https://…"
|
||||
aria-label="@($"Adresse de la couverture du numéro {numero.Numero}")"
|
||||
autocomplete="off" />
|
||||
|
||||
<button type="button" class="bouton bouton-principal"
|
||||
disabled="@(_enCours || !Reseau.EnLigne)" title="@MotifBlocage"
|
||||
@onclick="() => EnregistrerCouvertureAsync(numero)">
|
||||
@(_enCours ? "Enregistrement…" : "Enregistrer")
|
||||
</button>
|
||||
|
||||
<button type="button" class="bouton bouton-discret"
|
||||
disabled="@_enCours"
|
||||
@onclick="() => _correctionCouverture = null">
|
||||
Annuler
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="message-discret">
|
||||
Collez l'adresse d'une image, puis Entrée. Vider le champ
|
||||
retire la couverture.
|
||||
</p>
|
||||
}
|
||||
|
||||
<p class="carte-details">
|
||||
@if (numero.DateParution is { } parution)
|
||||
{
|
||||
@@ -300,6 +338,15 @@ else
|
||||
/// <summary>Numéro dont le retrait attend confirmation — <c>null</c> = aucun.</summary>
|
||||
private int? _confirmeRetrait;
|
||||
|
||||
/// <summary>Numéro dont l'adresse de couverture est ouverte — <c>null</c> = aucun.</summary>
|
||||
private int? _correctionCouverture;
|
||||
|
||||
private string _urlCouverture = string.Empty;
|
||||
|
||||
private ElementReference _champUrlCouverture;
|
||||
|
||||
private bool _focusCouverture;
|
||||
|
||||
/// <summary>
|
||||
/// Écran d'édition de la revue : nom, ISSN, éditeur, suppression, et la seule place où un
|
||||
/// numéro se modifie ou se retire.
|
||||
@@ -440,6 +487,87 @@ else
|
||||
|
||||
private void FermerEdition() => _edition = null;
|
||||
|
||||
/// <summary>
|
||||
/// Ouvre l'adresse de la couverture d'un numéro, depuis l'image elle-même.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ⚠️ Le focus est demandé <b>une seule fois</b>, au prochain rendu : le reprendre à chaque
|
||||
/// rendu l'arracherait pendant qu'on colle ou qu'on tape.
|
||||
/// </remarks>
|
||||
private void OuvrirCorrectionCouverture(NumeroRevueDto numero)
|
||||
{
|
||||
_erreur = null;
|
||||
_confirmeRetrait = null;
|
||||
_correctionCouverture = numero.Id;
|
||||
_urlCouverture = numero.CoverUrl ?? string.Empty;
|
||||
_focusCouverture = true;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool premierRendu)
|
||||
{
|
||||
if (_focusCouverture)
|
||||
{
|
||||
_focusCouverture = false;
|
||||
await _champUrlCouverture.FocusAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SurToucheCouverture(KeyboardEventArgs e, NumeroRevueDto numero)
|
||||
{
|
||||
if (e.Key == "Enter")
|
||||
{
|
||||
await EnregistrerCouvertureAsync(numero);
|
||||
}
|
||||
else if (e.Key is "Escape" or "Esc")
|
||||
{
|
||||
_correctionCouverture = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// N'enregistre que l'adresse de la couverture, sans passer par l'écran d'édition.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ⚠️ <b>Le point d'entrée REMPLACE le numéro entier</b>, articles à la une compris : tout ce
|
||||
/// que porte la ligne doit être recopié dans la charge utile, faute de quoi poser une
|
||||
/// couverture effacerait la une. C'est la même précaution que dans <c>OuvrirEdition</c>, et
|
||||
/// c'est le genre d'oubli qu'aucun message d'erreur ne signalerait.
|
||||
/// </remarks>
|
||||
private async Task EnregistrerCouvertureAsync(NumeroRevueDto numero)
|
||||
{
|
||||
if (!Reseau.EnLigne || _enCours)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var url = _urlCouverture.Trim();
|
||||
|
||||
_enCours = true;
|
||||
_erreur = null;
|
||||
|
||||
var resultat = await Api.ModifierNumeroAsync(
|
||||
numero.Id,
|
||||
new AjoutNumeroRevue
|
||||
{
|
||||
Numero = numero.Numero,
|
||||
DateParution = numero.DateParution,
|
||||
Note = numero.Note,
|
||||
CoverUrl = string.IsNullOrEmpty(url) ? null : url,
|
||||
Articles = [.. numero.Articles],
|
||||
});
|
||||
|
||||
_enCours = false;
|
||||
|
||||
if (!resultat.EstOk)
|
||||
{
|
||||
_erreur = resultat.Erreur;
|
||||
return;
|
||||
}
|
||||
|
||||
Appliquer(resultat.Valeur!);
|
||||
_correctionCouverture = null;
|
||||
}
|
||||
|
||||
private async Task EnregistrerNumeroAsync()
|
||||
{
|
||||
if (_edition is not { } numeroId
|
||||
@@ -490,6 +618,12 @@ else
|
||||
_edition = null;
|
||||
}
|
||||
|
||||
// Le numéro retiré portait peut-être le champ d'adresse ouvert : il n'a plus de cible.
|
||||
if (_correctionCouverture == numero.Id)
|
||||
{
|
||||
_correctionCouverture = null;
|
||||
}
|
||||
|
||||
if (_erreur is null)
|
||||
{
|
||||
await ChargerAsync();
|
||||
|
||||
Reference in New Issue
Block a user