@page "/revues/{Id:int}" @inject ServiceLivresApi Api @inject NavigationManager Navigation @inject EtatReseau Reseau @implements IDisposable MaBibli — @(_revue?.Titre ?? "revue") @if (_revue is null) {

@(_chargement ? "Chargement…" : "Cette revue n'est pas dans les données enregistrées sur cet appareil.")

} else {

@_revue.Titre

@if (!string.IsNullOrWhiteSpace(_revue.Issn)) { ISSN @_revue.Issn } @if (!string.IsNullOrWhiteSpace(_revue.Editeur)) { @_revue.Editeur }

@if (_erreur is not null) { }

Numéros possédés

@if (_revue.Numeros.Count == 0) {

Aucun numéro recensé pour l'instant.

} else { @* Du plus récent au plus ancien : devant un kiosque, la question est « ai-je le dernier ? », pas « par quoi ai-je commencé ». C'est l'inverse d'une série, qui suit son ordre de lecture. *@ }

Ajouter un numéro

@* ⚠️ Le numéro se saisit à la main, et ce n'est pas un oubli : les deux chiffres de parution du code-barres ne sont PAS un numéro fiable (CLAUDE.md), et l'add-on EAN-2 qui le porterait vraiment n'a pas pu être vérifié sur un magazine réel. *@ @if (_numeroPropose && !string.IsNullOrWhiteSpace(NumeroScanne)) {

Le code-barres portait « @NumeroScanne » à côté du code principal, ce qui est normalement le numéro de parution. Vérifiez-le sur la couverture avant d'enregistrer.

}

La revue

@if (_confirmeSuppression) {

Les @_revue.Numeros.Count numéro(s) recensés disparaîtront avec elle : ils n'existent que par leur revue.

} } @code { [Parameter] public int Id { get; set; } /// /// Numéro lu sur l'add-on EAN-2 du code-barres, transmis par l'écran de scan. /// /// /// ⚠️ Proposé, pas acquis. L'EAN-2 est normativement le numéro de parution, mais /// rien ne l'a confirmé sur des magazines réels : il atterrit donc dans le champ, où /// l'utilisateur le compare à la couverture et le corrige. C'est aussi pourquoi l'écran le /// dit explicitement au lieu de le glisser en douce. /// [SupplyParameterFromQuery(Name = "numero")] public string? NumeroScanne { get; set; } /// Le numéro scanné n'est proposé qu'une fois : ensuite, le champ appartient à l'utilisateur. private bool _numeroPropose; private RevueDto? _revue; private bool _chargement = true; private bool _enCours; private string? _erreur; private bool _confirmeSuppression; private string _titre = string.Empty; private string? _issn; private string? _editeur; private string _numero = string.Empty; private DateTime? _parution; private string? _note; private string? MotifBlocage => Reseau.EnLigne ? null : EtatReseau.MotifHorsLigne; protected override void OnInitialized() => Reseau.Change += SurChangementReseau; private void SurChangementReseau() => _ = InvokeAsync(async () => { await ChargerAsync(); StateHasChanged(); }); public void Dispose() => Reseau.Change -= SurChangementReseau; protected override Task OnParametersSetAsync() => ChargerAsync(); private async Task ChargerAsync() { _chargement = true; _revue = (await Api.ListerRevuesAsync()).FirstOrDefault(r => r.Id == Id); _chargement = false; if (_revue is not null) { _titre = _revue.Titre; _issn = _revue.Issn; _editeur = _revue.Editeur; } if (!_numeroPropose && !string.IsNullOrWhiteSpace(NumeroScanne)) { _numeroPropose = true; _numero = NumeroScanne.Trim(); } } private void Appliquer(RevueDto revue) { _revue = revue; _titre = revue.Titre; _issn = revue.Issn; _editeur = revue.Editeur; } private async Task SurTouche(KeyboardEventArgs e) { if (e.Key == "Enter" && !string.IsNullOrWhiteSpace(_numero) && Reseau.EnLigne) { await AjouterAsync(); } } private async Task AjouterAsync() { _enCours = true; _erreur = null; // Comme pour les prêts : le produit une date SANS fuseau. Elle est // déclarée locale avant conversion, sans quoi la parution se décalerait d'un jour pour // la moitié du globe. var parution = _parution is { } date ? DateTime.SpecifyKind(date, DateTimeKind.Local).ToUniversalTime() : (DateTime?)null; var resultat = await Api.AjouterNumeroAsync(Id, new AjoutNumeroRevue { Numero = _numero, DateParution = parution, Note = _note, }); _enCours = false; if (!resultat.EstOk) { _erreur = resultat.Erreur; return; } Appliquer(resultat.Valeur!); _numero = string.Empty; _parution = null; _note = null; } private async Task RetirerAsync(NumeroRevueDto numero) { _enCours = true; _erreur = await Api.RetirerNumeroAsync(numero.Id); _enCours = false; if (_erreur is null) { await ChargerAsync(); } } private async Task EnregistrerAsync() { _enCours = true; _erreur = null; var resultat = await Api.ModifierRevueAsync( Id, new EnregistrementRevue { Titre = _titre, Issn = _issn, Editeur = _editeur }); _enCours = false; if (!resultat.EstOk) { _erreur = resultat.Erreur; return; } Appliquer(resultat.Valeur!); } private async Task SupprimerAsync() { if (!_confirmeSuppression) { _confirmeSuppression = true; return; } _enCours = true; _erreur = await Api.SupprimerRevueAsync(Id); _enCours = false; if (_erreur is null) { Navigation.NavigateTo("revues"); } } }