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>
This commit is contained in:
co-authored by
Copilot
parent
5d45eb492e
commit
3d5e73838e
@@ -1766,6 +1766,26 @@ affichée, convertir en WebP). IDEES.md posait la question à mesurer avant de s
|
||||
règle que pour l'AOT WASM — non fait tant que la taille du cache image n'a pas montré de
|
||||
problème réel en usage.
|
||||
|
||||
## Lot B — consulter d'abord, modifier ensuite (2026-08-20)
|
||||
|
||||
`CarteLivre.razor` est le composant partagé des cartes du catalogue. Il reçoit un `LivreDto`,
|
||||
porte la couverture, les auteurs et les étiquettes de format, type, statut, prêt et éditeur,
|
||||
et le catalogue l'utilise lui-même. Une œuvre bibliographique possédée réutilise cette carte
|
||||
quand son livre est disponible.
|
||||
|
||||
Pour une série, `/series/{id}` est la consultation : parenté, tomes, carte du livre rattaché,
|
||||
réordonnancement, rattachement, ajout et retrait restent des gestes courants. Seuls le titre,
|
||||
la parenté dans un cycle et la suppression sont dans `/series/{id}/edition`. Comme pour la
|
||||
fiche livre, le composant s'abonne à `LocationChanged` car les routes partagent le même paramètre.
|
||||
|
||||
Pour les envies, `/souhaits` consulte et conserve l'export et la suppression courante.
|
||||
`/souhaits/ordre` porte exclusivement le réordonnancement (flèches et glisser-déposer), afin
|
||||
que ces contrôles ne puissent pas être déclenchés en faisant défiler la liste.
|
||||
|
||||
Une œuvre bibliographique non possédée n'a pas de fiche locale. Son titre est donc un bouton
|
||||
qui déplie inline son ISBN et son nombre d'éditions, tandis que les œuvres possédées ouvrent
|
||||
leur fiche via la carte partagée.
|
||||
|
||||
## Installation YunoHost — éprouvée en production le 2026-08-18
|
||||
|
||||
Le paquet vit dans le dépôt `mabibli_ynh` (voir « Deux dépôts distincts »). Installé,
|
||||
|
||||
@@ -130,7 +130,7 @@ mesure montre un gain réel. Pas encore mesuré à l'échelle d'un vrai fonds.
|
||||
|
||||
---
|
||||
|
||||
## Lot B — Consulter d'abord, modifier ensuite
|
||||
## Lot B — Consulter d'abord, modifier ensuite — traité le 2026-08-20
|
||||
|
||||
Un seul motif, déjà acté et éprouvé pour la fiche livre (`/livres/{id}` consulte,
|
||||
`/livres/{id}/edition` modifie, le mode est **dans l'URL**), à étendre aux écrans qui présentent
|
||||
@@ -145,6 +145,8 @@ encore un formulaire vivant sous le pouce.
|
||||
- **B3. Bibliographie d'un auteur** : même carte que le catalogue, et un **clic ouvre le détail**
|
||||
de l'œuvre au lieu de s'arrêter à une ligne.
|
||||
|
||||
✅ Traité : voir la décision et les limites retenues dans `CLAUDE.md`.
|
||||
|
||||
⚠️ Le point commun des trois n'est pas cosmétique : c'est **une seule carte de livre** réutilisée
|
||||
partout. Si les trois écrans réimplémentent la carte chacun de leur côté, les pastilles
|
||||
divergeront comme le filtre a divergé (voir A2).
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<div class="carte-livre">
|
||||
<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>
|
||||
|
||||
@if (Livre.Auteurs.Count > 0)
|
||||
{
|
||||
<p class="carte-auteur">
|
||||
@foreach (var (auteur, index) in Livre.Auteurs.Select((a, i) => (a, i)))
|
||||
{
|
||||
@(index > 0 ? ", " : "")
|
||||
<a class="lien-auteur" href="@($"?auteur={auteur.Id}")">@auteur.Nom</a>
|
||||
@if (auteur.Role != RoleAuteur.NonPrecise)
|
||||
{
|
||||
<span class="role-auteur"> (@Libelles.RoleAuteur(auteur.Role).ToLowerInvariant())</span>
|
||||
}
|
||||
}
|
||||
</p>
|
||||
}
|
||||
|
||||
<p class="carte-details">
|
||||
@if (Livre.Format == Format.Numerique)
|
||||
{
|
||||
<span class="etiquette">@Libelles.Format(Livre.Format)</span>
|
||||
}
|
||||
@if (Livre.TypeDocument != TypeDocument.NonPrecise)
|
||||
{
|
||||
<span class="etiquette etiquette-type @Libelles.ClasseTypeDocument(Livre.TypeDocument)">
|
||||
@Libelles.TypeDocument(Livre.TypeDocument)
|
||||
</span>
|
||||
}
|
||||
@if (Livre.Statut is { } statut)
|
||||
{
|
||||
<span class="etiquette etiquette-statut @Libelles.ClasseStatut(statut)">
|
||||
@Libelles.Statut(statut)
|
||||
</span>
|
||||
}
|
||||
@if (Livre.PreteA is { } emprunteur)
|
||||
{
|
||||
<span class="etiquette etiquette-prete">Prêté à @emprunteur</span>
|
||||
}
|
||||
@if (!string.IsNullOrWhiteSpace(Livre.Editeur))
|
||||
{
|
||||
<span>@Livre.Editeur</span>
|
||||
}
|
||||
</p>
|
||||
|
||||
@Actions
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter, EditorRequired] public LivreDto Livre { get; set; } = default!;
|
||||
[Parameter] public RenderFragment? Actions { get; set; }
|
||||
}
|
||||
@@ -140,15 +140,26 @@ else if (_bibliographie is { } biblio)
|
||||
{
|
||||
@* La classe « oeuvre-possedee » grise la ligne : le livre est déjà sur l'étagère. *@
|
||||
<li class="carte-oeuvre @(oeuvre.Possede ? "oeuvre-possedee" : null)" @key="oeuvre.Titre">
|
||||
@if (oeuvre is { Possede: true, LivreId: { } livreId }
|
||||
&& _livresCatalogue.FirstOrDefault(l => l.Id == livreId) is { } livre)
|
||||
{
|
||||
<CarteLivre Livre="livre" />
|
||||
}
|
||||
@if (!oeuvre.Possede || !_livresCatalogue.Any(l => l.Id == oeuvre.LivreId))
|
||||
{
|
||||
<div class="carte-corps">
|
||||
<p class="carte-titre">
|
||||
@if (oeuvre is { Possede: true, LivreId: { } livreId })
|
||||
@if (oeuvre is { Possede: true, LivreId: { } livreIdPossede })
|
||||
{
|
||||
<a class="lien-auteur" href="@($"livres/{livreId}")">@oeuvre.Titre</a>
|
||||
<a class="lien-auteur" href="@($"livres/{livreIdPossede}")">@oeuvre.Titre</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="carte-titre oeuvre-titre-bouton"
|
||||
@onclick="() => BasculerOeuvre(oeuvre.Titre)"
|
||||
aria-expanded="@_oeuvresOuvertes.Contains(oeuvre.Titre)">
|
||||
@oeuvre.Titre
|
||||
</button>
|
||||
}
|
||||
</p>
|
||||
|
||||
@@ -176,6 +187,20 @@ else if (_bibliographie is { } biblio)
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
@if (_oeuvresOuvertes.Contains(oeuvre.Titre))
|
||||
{
|
||||
<p class="message-discret detail-oeuvre">
|
||||
@if (!string.IsNullOrWhiteSpace(oeuvre.Isbn))
|
||||
{
|
||||
<span>ISBN : @FormatageIsbn.Afficher(oeuvre.Isbn)</span>
|
||||
}
|
||||
@if (oeuvre.NombreEditions > 1)
|
||||
{
|
||||
<span>@oeuvre.NombreEditions éditions recensées</span>
|
||||
}
|
||||
</p>
|
||||
}
|
||||
}
|
||||
|
||||
@if (oeuvre.ADecouvrir)
|
||||
{
|
||||
@@ -225,6 +250,8 @@ else if (_bibliographie is { } biblio)
|
||||
private string? _message;
|
||||
|
||||
private int _possedees;
|
||||
private IReadOnlyList<LivreDto> _livresCatalogue = [];
|
||||
private readonly HashSet<string> _oeuvresOuvertes = [];
|
||||
|
||||
protected override async Task OnParametersSetAsync() => await ChargerAsync();
|
||||
|
||||
@@ -240,6 +267,7 @@ else if (_bibliographie is { } biblio)
|
||||
try
|
||||
{
|
||||
_bibliographie = await Api.ObtenirBibliographieAsync(AuteurId);
|
||||
_livresCatalogue = await Api.ListerAsync(new CritereLivres());
|
||||
|
||||
if (_bibliographie is null)
|
||||
{
|
||||
@@ -272,6 +300,14 @@ else if (_bibliographie is { } biblio)
|
||||
}
|
||||
}
|
||||
|
||||
private void BasculerOeuvre(string titre)
|
||||
{
|
||||
if (!_oeuvresOuvertes.Add(titre))
|
||||
{
|
||||
_oeuvresOuvertes.Remove(titre);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ajoute une œuvre à la liste d'envies, en emportant ce que la BnF en sait.
|
||||
/// </summary>
|
||||
|
||||
@@ -115,69 +115,8 @@ else if (_livres is not null)
|
||||
<ul class="liste-livres">
|
||||
@foreach (var livre in _livres)
|
||||
{
|
||||
<li class="carte-livre" @key="livre.Id">
|
||||
@* 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>
|
||||
|
||||
@if (livre.Auteurs.Count > 0)
|
||||
{
|
||||
<p class="carte-auteur">
|
||||
@foreach (var (auteur, index) in livre.Auteurs.Select((a, i) => (a, i)))
|
||||
{
|
||||
@(index > 0 ? ", " : "")
|
||||
<a class="lien-auteur" href="@($"?auteur={auteur.Id}")">@auteur.Nom</a>@if (auteur.Role != RoleAuteur.NonPrecise)
|
||||
{
|
||||
@* Comme le format et le type : seul ce qui a été choisi s'affiche. *@
|
||||
<span class="role-auteur"> (@Libelles.RoleAuteur(auteur.Role).ToLowerInvariant())</span>
|
||||
}
|
||||
}
|
||||
</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">
|
||||
@* 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>
|
||||
}
|
||||
|
||||
@* Même règle que le format : on n'affiche que ce qui a été CHOISI.
|
||||
« Non précisé » n'apprendrait rien et serait sur presque chaque ligne. *@
|
||||
@if (livre.TypeDocument != TypeDocument.NonPrecise)
|
||||
{
|
||||
<span class="etiquette etiquette-type @Libelles.ClasseTypeDocument(livre.TypeDocument)">
|
||||
@Libelles.TypeDocument(livre.TypeDocument)
|
||||
</span>
|
||||
}
|
||||
|
||||
@* 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>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(livre.Editeur))
|
||||
{
|
||||
<span>@livre.Editeur</span>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
<li @key="livre.Id">
|
||||
<CarteLivre Livre="livre" />
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@page "/series/{Id:int}"
|
||||
@page "/series/{Id:int}/edition"
|
||||
@inject ServiceLivresApi Api
|
||||
@inject NavigationManager Navigation
|
||||
@inject EtatReseau Reseau
|
||||
@@ -14,12 +15,15 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (_parente is { } parente)
|
||||
@if (!ModeEdition && _parente is { } parente)
|
||||
{
|
||||
@if (!ModeEdition)
|
||||
{
|
||||
<p class="message-discret">
|
||||
Fait partie de <a href="@($"series/{parente.Id}")">@parente.Titre</a>.
|
||||
</p>
|
||||
}
|
||||
}
|
||||
|
||||
<h1 class="titre-page">@_serie.Titre</h1>
|
||||
|
||||
@@ -47,7 +51,7 @@ else
|
||||
}
|
||||
|
||||
@* Les sous-séries d'un cycle : on les liste, on ne les déplie pas — chacune a son écran. *@
|
||||
@if (_filles.Count > 0)
|
||||
@if (!ModeEdition && _filles.Count > 0)
|
||||
{
|
||||
<h2 class="titre-section">Séries de ce cycle</h2>
|
||||
<ul class="liste-sous-series">
|
||||
@@ -63,7 +67,7 @@ else
|
||||
</ul>
|
||||
}
|
||||
|
||||
@if (_serie.Elements.Count > 0)
|
||||
@if (!ModeEdition && _serie.Elements.Count > 0)
|
||||
{
|
||||
<h2 class="titre-section">Ordre de lecture</h2>
|
||||
|
||||
@@ -94,20 +98,27 @@ else
|
||||
</div>
|
||||
|
||||
<div class="carte-corps">
|
||||
@if (element.LivreId is { } livreId)
|
||||
@if (element.LivreId is { } livreId && _livresCatalogue.FirstOrDefault(l => l.Id == livreId) is { } livre)
|
||||
{
|
||||
<a class="carte-titre" href="@($"livres/{livreId}")">@element.Titre</a>
|
||||
<CarteLivre Livre="livre" />
|
||||
}
|
||||
else if (element.LivreId is { } idRattache)
|
||||
{
|
||||
<a class="carte-titre" href="@($"livres/{idRattache}")">@element.Titre</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="carte-titre">@element.Titre</p>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(element.Auteurs))
|
||||
@if ((element.LivreId is null || !_livresCatalogue.Any(l => l.Id == element.LivreId))
|
||||
&& !string.IsNullOrWhiteSpace(element.Auteurs))
|
||||
{
|
||||
<p class="carte-auteur">@element.Auteurs</p>
|
||||
}
|
||||
|
||||
@if (element.LivreId is null || !_livresCatalogue.Any(l => l.Id == element.LivreId))
|
||||
{
|
||||
<p class="carte-details">
|
||||
@if (!element.Possede)
|
||||
{
|
||||
@@ -120,6 +131,7 @@ else
|
||||
<span class="etiquette etiquette-prete">Prêté à @emprunteur</span>
|
||||
}
|
||||
</p>
|
||||
}
|
||||
|
||||
<div class="actions-tome">
|
||||
@if (!element.Possede)
|
||||
@@ -161,12 +173,12 @@ else
|
||||
value="@_recherche" @oninput="ChercherAsync"
|
||||
aria-label="Chercher un livre à rattacher" />
|
||||
|
||||
@foreach (var livre in _resultats)
|
||||
@foreach (var resultat in _resultats)
|
||||
{
|
||||
<button type="button" class="bouton bouton-discret"
|
||||
@key="livre.Id"
|
||||
@onclick="() => RattacherAsync(element.Id, livre.Id)">
|
||||
@livre.Titre@(livre.AuteursAffiches is { } a ? $" — {a}" : "")
|
||||
@key="resultat.Id"
|
||||
@onclick="() => RattacherAsync(element.Id, resultat.Id)">
|
||||
@resultat.Titre@(resultat.AuteursAffiches is { } a ? $" — {a}" : "")
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
@@ -177,6 +189,8 @@ else
|
||||
</ul>
|
||||
}
|
||||
|
||||
@if (!ModeEdition)
|
||||
{
|
||||
<h2 class="titre-section">Ajouter un tome</h2>
|
||||
|
||||
@*
|
||||
@@ -221,7 +235,10 @@ else
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@if (ModeEdition)
|
||||
{
|
||||
<h2 class="titre-section">La série</h2>
|
||||
|
||||
<label class="champ">
|
||||
@@ -268,6 +285,21 @@ else
|
||||
</p>
|
||||
}
|
||||
}
|
||||
@if (!ModeEdition)
|
||||
{
|
||||
<nav class="actions-flottantes">
|
||||
@if (Reseau.EnLigne)
|
||||
{
|
||||
<a class="bouton bouton-principal" href="@($"series/{Id}/edition")">Modifier</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="bouton bouton-principal" disabled
|
||||
title="@EtatReseau.MotifHorsLigne">Modifier</button>
|
||||
}
|
||||
</nav>
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
/// <summary>Marqueur de la recherche « ajouter depuis le catalogue », qui ne vise aucun élément.</summary>
|
||||
@@ -276,6 +308,7 @@ else
|
||||
[Parameter] public int Id { get; set; }
|
||||
|
||||
private IReadOnlyList<SerieDto> _toutes = [];
|
||||
private IReadOnlyList<LivreDto> _livresCatalogue = [];
|
||||
private SerieDto? _serie;
|
||||
private SerieDto? _parente;
|
||||
private List<SerieDto> _filles = [];
|
||||
@@ -296,6 +329,10 @@ else
|
||||
|
||||
private string? MotifBlocage => Reseau.EnLigne ? null : EtatReseau.MotifHorsLigne;
|
||||
|
||||
private bool ModeEdition =>
|
||||
Navigation.ToAbsoluteUri(Navigation.Uri).AbsolutePath.TrimEnd('/')
|
||||
.EndsWith("/edition", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Séries dans lesquelles celle-ci peut être rangée.
|
||||
/// </summary>
|
||||
@@ -326,7 +363,13 @@ else
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnInitialized() => Reseau.Change += SurChangementReseau;
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Reseau.Change += SurChangementReseau;
|
||||
Navigation.LocationChanged += SurNavigation;
|
||||
}
|
||||
|
||||
private void SurNavigation(object? _, LocationChangedEventArgs __) => InvokeAsync(StateHasChanged);
|
||||
|
||||
private void SurChangementReseau() => _ = InvokeAsync(async () =>
|
||||
{
|
||||
@@ -334,7 +377,11 @@ else
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
public void Dispose() => Reseau.Change -= SurChangementReseau;
|
||||
public void Dispose()
|
||||
{
|
||||
Reseau.Change -= SurChangementReseau;
|
||||
Navigation.LocationChanged -= SurNavigation;
|
||||
}
|
||||
|
||||
protected override Task OnParametersSetAsync() => ChargerAsync();
|
||||
|
||||
@@ -342,6 +389,7 @@ else
|
||||
{
|
||||
_chargement = true;
|
||||
_toutes = await Api.ListerSeriesAsync();
|
||||
_livresCatalogue = await Api.ListerAsync(new CritereLivres());
|
||||
_chargement = false;
|
||||
|
||||
Rattacher();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@page "/souhaits"
|
||||
@page "/souhaits/ordre"
|
||||
@inject ServiceLivresApi Api
|
||||
@inject EtatReseau Reseau
|
||||
@inject NavigationManager Navigation
|
||||
@implements IDisposable
|
||||
|
||||
<PageTitle>MaBibli — liste d'envies</PageTitle>
|
||||
@@ -92,7 +94,7 @@ else
|
||||
</p>
|
||||
}
|
||||
|
||||
@if (_souhaits.Count > 1)
|
||||
@if (ModeOrdre && _souhaits.Count > 1)
|
||||
{
|
||||
<p class="champ-aide">
|
||||
Du plus désiré au moins désiré. Utilisez les flèches pour déplacer une envie —
|
||||
@@ -110,13 +112,13 @@ else
|
||||
{
|
||||
<li class="carte-souhait @(_glisse == souhait.Id ? "carte-glissee" : null)"
|
||||
@key="souhait.Id"
|
||||
draggable="@((Reseau.EnLigne && _souhaits.Count > 1).ToString().ToLowerInvariant())"
|
||||
@ondragstart="() => _glisse = souhait.Id"
|
||||
@ondragend="() => _glisse = null"
|
||||
@ondragover:preventDefault="true"
|
||||
draggable="@((ModeOrdre && Reseau.EnLigne && _souhaits.Count > 1).ToString().ToLowerInvariant())"
|
||||
@ondragstart="() => DebuterGlisse(souhait.Id)"
|
||||
@ondragend="TerminerGlisse"
|
||||
@ondragover:preventDefault="ModeOrdre"
|
||||
@ondrop="() => DeposerAsync(index)">
|
||||
|
||||
@if (_souhaits.Count > 1)
|
||||
@if (ModeOrdre && _souhaits.Count > 1)
|
||||
{
|
||||
<div class="rang-actions">
|
||||
<button type="button" class="bouton bouton-compact"
|
||||
@@ -182,12 +184,15 @@ else
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (!ModeOrdre)
|
||||
{
|
||||
<button type="button" class="bouton bouton-compact bouton-danger"
|
||||
disabled="@(_enCours || !Reseau.EnLigne)" title="@MotifBlocage"
|
||||
@onclick="() => SupprimerAsync(souhait)"
|
||||
aria-label="@($"Retirer « {souhait.Titre} » de la liste d'envies")">
|
||||
Retirer
|
||||
</button>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
@@ -197,6 +202,14 @@ else
|
||||
@* L'ajout est passé dans son propre écran : la page ne montre plus que la liste, comme la
|
||||
fiche livre ne montre plus son formulaire. *@
|
||||
<nav class="actions-flottantes">
|
||||
@if (ModeOrdre)
|
||||
{
|
||||
<a class="bouton bouton-discret" href="souhaits">Terminer</a>
|
||||
}
|
||||
else if (Reseau.EnLigne)
|
||||
{
|
||||
<a class="bouton bouton-discret" href="souhaits/ordre">Modifier l'ordre</a>
|
||||
}
|
||||
@if (Reseau.EnLigne)
|
||||
{
|
||||
<a class="bouton bouton-principal" href="souhaits/ajout">Ajouter une envie</a>
|
||||
@@ -213,13 +226,19 @@ else
|
||||
private bool _enCours;
|
||||
private int? _glisse;
|
||||
private string? _erreur;
|
||||
private bool ModeOrdre =>
|
||||
Navigation.ToAbsoluteUri(Navigation.Uri).AbsolutePath.TrimEnd('/')
|
||||
.EndsWith("/souhaits/ordre", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Reseau.Change += SurChangementReseau;
|
||||
Navigation.LocationChanged += SurNavigation;
|
||||
await ChargerAsync();
|
||||
}
|
||||
|
||||
private void SurNavigation(object? _, LocationChangedEventArgs __) => InvokeAsync(StateHasChanged);
|
||||
|
||||
/// <summary>
|
||||
/// Recharge la liste au retour du réseau, et redessine pour rendre les actions à l'écran.
|
||||
/// </summary>
|
||||
@@ -233,7 +252,11 @@ else
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
public void Dispose() => Reseau.Change -= SurChangementReseau;
|
||||
public void Dispose()
|
||||
{
|
||||
Reseau.Change -= SurChangementReseau;
|
||||
Navigation.LocationChanged -= SurNavigation;
|
||||
}
|
||||
|
||||
/// <summary>Raison du blocage des écritures, ou <c>null</c> quand tout est possible.</summary>
|
||||
private string? MotifBlocage => Reseau.EnLigne ? null : EtatReseau.MotifHorsLigne;
|
||||
@@ -307,7 +330,7 @@ else
|
||||
/// <summary>Fin d'un glisser-déposer : la carte saisie prend la place de celle survolée.</summary>
|
||||
private async Task DeposerAsync(int cible)
|
||||
{
|
||||
if (_souhaits is null || _glisse is not { } id)
|
||||
if (!ModeOrdre || _souhaits is null || _glisse is not { } id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -321,6 +344,16 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
private void DebuterGlisse(int id)
|
||||
{
|
||||
if (ModeOrdre && Reseau.EnLigne)
|
||||
{
|
||||
_glisse = id;
|
||||
}
|
||||
}
|
||||
|
||||
private void TerminerGlisse() => _glisse = null;
|
||||
|
||||
private async Task SupprimerAsync(SouhaitDto souhait)
|
||||
{
|
||||
_enCours = true;
|
||||
|
||||
@@ -850,6 +850,22 @@ body {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.oeuvre-titre-bouton {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.detail-oeuvre {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin: 0.25rem 0 0;
|
||||
}
|
||||
|
||||
.etiquette-possede {
|
||||
background: #e3ece3;
|
||||
color: #2f5d34;
|
||||
|
||||
Reference in New Issue
Block a user