MaBibli 1.0.0
Gestion de bibliothèque personnelle auto-hébergée : catalogue, prêts, scan de code-barres, consultation hors-ligne. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
@page "/series"
|
||||
@inject ServiceLivresApi Api
|
||||
@inject EtatReseau Reseau
|
||||
@implements IDisposable
|
||||
|
||||
<PageTitle>MaBibli — séries</PageTitle>
|
||||
|
||||
<h1 class="titre-page">Séries</h1>
|
||||
|
||||
<p class="message-discret">
|
||||
Sagas, cycles et trilogies, dans leur ordre de <strong>lecture</strong> — qui n'est pas
|
||||
toujours celui de publication. Les séries sont communes au foyer, comme le catalogue.
|
||||
</p>
|
||||
|
||||
@if (_erreur is not null)
|
||||
{
|
||||
<p class="message-erreur" role="alert">@_erreur</p>
|
||||
}
|
||||
|
||||
@* Créer une série depuis la liste : c'est le seul endroit d'où l'on part, et un écran de plus
|
||||
pour un unique champ ne se justifierait pas. *@
|
||||
@* M1 : le champ et son bouton sur UNE ligne. Le champ prend la place restante, le bouton
|
||||
garde la sienne et son libellé ne se tronque pas — vérifié jusqu'à 320 px. *@
|
||||
<div class="ligne-creation">
|
||||
<input class="champ-saisie" type="text" placeholder="Nom d'une nouvelle série…"
|
||||
@bind="_nouveau" @bind:event="oninput" @onkeydown="SurTouche"
|
||||
aria-label="Nom de la nouvelle série" autocomplete="off" />
|
||||
|
||||
<button type="button" class="bouton bouton-principal"
|
||||
disabled="@(_enCours || !Reseau.EnLigne || string.IsNullOrWhiteSpace(_nouveau))"
|
||||
title="@MotifBlocage"
|
||||
@onclick="CreerAsync">
|
||||
Créer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (_series is null)
|
||||
{
|
||||
<p class="message-discret" role="status">Chargement…</p>
|
||||
}
|
||||
else if (_series.Count == 0)
|
||||
{
|
||||
<p class="message-discret">
|
||||
Aucune série pour l'instant. Créez-en une, puis ajoutez-y vos livres et les tomes
|
||||
qui vous manquent : c'est ce qui permet de voir les trous.
|
||||
</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="liste-series arbre">
|
||||
@foreach (var serie in Racines)
|
||||
{
|
||||
<ArbreSerie Serie="serie" Toutes="_series" Totaux="_totaux" Replies="_replies" />
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
@code {
|
||||
private IReadOnlyList<SerieDto>? _series;
|
||||
private IReadOnlyDictionary<int, (int Possedes, int Total)> _totaux =
|
||||
new Dictionary<int, (int, int)>();
|
||||
|
||||
/// <summary>Nœuds repliés à la main. Tout est déplié par défaut — voir ArbreSerie.</summary>
|
||||
private readonly HashSet<int> _replies = [];
|
||||
private string _nouveau = string.Empty;
|
||||
private bool _enCours;
|
||||
private string? _erreur;
|
||||
|
||||
private string? MotifBlocage => Reseau.EnLigne ? null : EtatReseau.MotifHorsLigne;
|
||||
|
||||
/// <summary>Séries de premier niveau. Celles rangées dans un cycle s'affichent sous lui.</summary>
|
||||
private IEnumerable<SerieDto> Racines =>
|
||||
_series?.Where(s => s.SerieParenteId is null) ?? [];
|
||||
|
||||
protected override void OnInitialized() => Reseau.Change += SurChangementReseau;
|
||||
|
||||
private void SurChangementReseau() => _ = InvokeAsync(async () =>
|
||||
{
|
||||
await ChargerAsync();
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
public void Dispose() => Reseau.Change -= SurChangementReseau;
|
||||
|
||||
protected override Task OnInitializedAsync() => ChargerAsync();
|
||||
|
||||
private async Task ChargerAsync()
|
||||
{
|
||||
// ⚠️ Le catalogue n'est plus chargé ici : l'arbre ne montre que la descendance des
|
||||
// séries, plus leurs tomes. C'était une lecture complète du fonds pour rien.
|
||||
_series = await Api.ListerSeriesAsync();
|
||||
_totaux = TotauxSeries.Calculer(_series);
|
||||
}
|
||||
|
||||
private async Task SurTouche(KeyboardEventArgs e)
|
||||
{
|
||||
if (e.Key == "Enter" && !string.IsNullOrWhiteSpace(_nouveau) && Reseau.EnLigne)
|
||||
{
|
||||
await CreerAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreerAsync()
|
||||
{
|
||||
_enCours = true;
|
||||
_erreur = null;
|
||||
|
||||
var resultat = await Api.CreerSerieAsync(new EnregistrementSerie { Titre = _nouveau });
|
||||
|
||||
_enCours = false;
|
||||
|
||||
if (!resultat.EstOk)
|
||||
{
|
||||
_erreur = resultat.Erreur;
|
||||
return;
|
||||
}
|
||||
|
||||
_nouveau = string.Empty;
|
||||
await ChargerAsync();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user