@(_chargement ? "Chargement…" : "Cette revue n'est pas dans les données enregistrées sur cet appareil.")
}
else
{
@_revue.Titre
@if (!string.IsNullOrWhiteSpace(_revue.Issn))
{
@* K2 : un ISSN se coupe TOUJOURS au même endroit, contrairement aux tranches d'un
ISBN. Aucune table à embarquer, et aucune coupure ne peut être fausse. *@
ISSN @FormatageIssn.Afficher(_revue.Issn)
}
@if (!string.IsNullOrWhiteSpace(_revue.Editeur))
{
@_revue.Editeur
}
@if (_erreur is not null)
{
@_erreur
}
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. *@
@foreach (var numero in _revue.Numeros)
{
@if (_edition == numero.Id)
{
}
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. *@
N° @numero.Numero
@if (numero.DateParution is { } parution)
{
@parution.ToLocalTime().ToString("MMMM yyyy")
}
else
{
Date de parution inconnue
}
@* Les articles à la une décrivent CE numéro, pas la revue : ils
changent à chaque parution. D'où une étiquette par titre, sur la
fiche du numéro et nulle part ailleurs. *@
@if (numero.Articles.Count > 0)
{
@foreach (var article in numero.Articles)
{
@article
}
}
@if (!string.IsNullOrWhiteSpace(numero.Note))
{
@numero.Note
}
}
}
}
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;
/// Saisie du numéro qu'on ajoute.
private AjoutNumeroRevue _saisie = new();
/// Numéro en cours de modification, et sa saisie — null = aucun.
private int? _edition;
private AjoutNumeroRevue _saisieEdition = new();
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;
_saisie.Numero = NumeroScanne.Trim();
}
}
private void Appliquer(RevueDto revue)
{
_revue = revue;
_titre = revue.Titre;
_issn = revue.Issn;
_editeur = revue.Editeur;
}
private async Task AjouterAsync()
{
if (string.IsNullOrWhiteSpace(_saisie.Numero) || !Reseau.EnLigne || _enCours)
{
return;
}
_enCours = true;
_erreur = null;
// La conversion de la date en UTC vit dans ChampsNumeroRevue : les deux formulaires qui
// s'en servent ne peuvent donc pas l'oublier chacun de leur côté.
var resultat = await Api.AjouterNumeroAsync(Id, _saisie);
_enCours = false;
if (!resultat.EstOk)
{
_erreur = resultat.Erreur;
return;
}
Appliquer(resultat.Valeur!);
_saisie = new AjoutNumeroRevue();
}
///
/// Ouvre la modification d'un numéro, en recopiant ce qu'il porte déjà.
///
///
/// La couverture et les articles à la une ne peuvent pas être connus à l'achat : on
/// note un numéro quand on le range, et on en recopie la une plus tard. Sans cet écran, ces
/// deux champs n'auraient existé qu'à la création, c'est-à-dire jamais.
///
private void OuvrirEdition(NumeroRevueDto numero)
{
_erreur = null;
_edition = numero.Id;
_saisieEdition = new AjoutNumeroRevue
{
Numero = numero.Numero,
DateParution = numero.DateParution,
Note = numero.Note,
CoverUrl = numero.CoverUrl,
Articles = [.. numero.Articles],
};
}
private async Task EnregistrerNumeroAsync()
{
if (_edition is not { } numeroId
|| string.IsNullOrWhiteSpace(_saisieEdition.Numero)
|| !Reseau.EnLigne
|| _enCours)
{
return;
}
_enCours = true;
_erreur = null;
var resultat = await Api.ModifierNumeroAsync(numeroId, _saisieEdition);
_enCours = false;
if (!resultat.EstOk)
{
_erreur = resultat.Erreur;
return;
}
Appliquer(resultat.Valeur!);
_edition = null;
}
private async Task RetirerAsync(NumeroRevueDto numero)
{
_enCours = true;
_erreur = await Api.RetirerNumeroAsync(numero.Id);
_enCours = false;
// Le numéro retiré était peut-être celui qu'on modifiait : un formulaire ouvert sur une
// ligne disparue n'aurait plus rien à enregistrer.
if (_edition == numero.Id)
{
_edition = null;
}
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");
}
}
}