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>
This commit is contained in:
@@ -67,7 +67,9 @@ else if (_livres is not null)
|
||||
@foreach (var livre in _livres)
|
||||
{
|
||||
<li class="carte-livre" @key="livre.Id">
|
||||
<Couverture Url="@livre.CoverUrl" Titre="@livre.Titre" Classe="couverture-petite" />
|
||||
@* La vignette s'agrandit au clic ; le titre, lui, ouvre la fiche. *@
|
||||
<Couverture Url="@livre.CoverUrl" Titre="@livre.Titre"
|
||||
Classe="couverture-petite" Agrandissable="true" />
|
||||
|
||||
<div class="carte-corps">
|
||||
<a class="carte-titre" href="@($"livres/{livre.Id}")">@livre.Titre</a>
|
||||
@@ -83,11 +85,26 @@ else if (_livres is not null)
|
||||
</p>
|
||||
}
|
||||
|
||||
@* Une seule ligne d'étiquettes, en lecture seule (IDEES.md) : la liste se
|
||||
parcourt, elle ne se manipule pas. Tout se modifie depuis la fiche. *@
|
||||
<p class="carte-details">
|
||||
<span class="etiquette">@Libelles.Format(livre.Format)</span>
|
||||
@* Un livre est physique par défaut : ne l'afficher que pour les ebooks,
|
||||
sinon l'étiquette est sur toutes les lignes et n'apprend rien. *@
|
||||
@if (livre.Format == Format.Numerique)
|
||||
{
|
||||
<span class="etiquette">@Libelles.Format(livre.Format)</span>
|
||||
}
|
||||
|
||||
@* Un livre dehors l'est pour tout le foyer : cette étiquette est la même
|
||||
pour tout le monde, contrairement au statut de lecture juste dessous. *@
|
||||
@* Personnel : ce que montre cette pastille est MA lecture, pas celle du foyer.
|
||||
Pas de pastille = non commencé, l'absence de ligne en base. *@
|
||||
@if (livre.Statut is { } statut)
|
||||
{
|
||||
<span class="etiquette etiquette-statut @Libelles.ClasseStatut(statut)">
|
||||
@Libelles.Statut(statut)
|
||||
</span>
|
||||
}
|
||||
|
||||
@* Un livre dehors l'est pour tout le foyer, contrairement au statut. *@
|
||||
@if (livre.PreteA is { } emprunteur)
|
||||
{
|
||||
<span class="etiquette etiquette-prete">Prêté à @emprunteur</span>
|
||||
@@ -98,12 +115,6 @@ else if (_livres is not null)
|
||||
<span>@livre.Editeur</span>
|
||||
}
|
||||
</p>
|
||||
|
||||
@* Changement de statut : action la plus fréquente, donc directement dans la liste. *@
|
||||
<ChoixStatut Valeur="livre.Statut"
|
||||
ValeurChanged="s => ChangerStatut(livre, s)"
|
||||
Desactive="_statutEnCours == livre.Id"
|
||||
Compact="true" />
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
@@ -130,7 +141,6 @@ else if (_livres is not null)
|
||||
private Statut? _statut;
|
||||
private bool _chargement;
|
||||
private string? _erreur;
|
||||
private int? _statutEnCours;
|
||||
|
||||
// Une frappe au clavier annule la recherche précédente encore en vol.
|
||||
private CancellationTokenSource _cts = new();
|
||||
@@ -207,35 +217,6 @@ else if (_livres is not null)
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ChangerStatut(LivreDto livre, Statut? statut)
|
||||
{
|
||||
if (livre.Statut == statut)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_statutEnCours = livre.Id;
|
||||
var resultat = await Api.ChangerStatutAsync(livre.Id, statut);
|
||||
_statutEnCours = null;
|
||||
|
||||
if (!resultat.EstOk)
|
||||
{
|
||||
_erreur = resultat.Erreur;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_statut is not null)
|
||||
{
|
||||
// Un filtre par statut est actif : le livre vient peut-être de sortir de la sélection,
|
||||
// il faut donc recharger plutôt que corriger la liste en place.
|
||||
await ChargerAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remplacement local : évite de recharger toute la liste pour un seul champ.
|
||||
_livres = _livres!.Select(l => l.Id == livre.Id ? resultat.Valeur! : l).ToList();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cts.Cancel();
|
||||
|
||||
Reference in New Issue
Block a user