@* Formulaire d'une fiche livre, partagé par l'ajout (ISBN ou manuel) et l'édition. Le pré-remplissage par ISBN est une AIDE, pas une contrainte : tous les champs restent modifiables, et une fiche incomplète peut toujours être complétée à la main (CLAUDE.md). *@
@if (!string.IsNullOrEmpty(Saisie.CoverUrl) || !string.IsNullOrWhiteSpace(Saisie.Titre)) {
} @* ⚠️ L'ORDRE DES CHAMPS EST UNE DÉCISION (lot Q, 2026-08-21), pas une mise en page. Type de document, puis thèmes, puis auteurs (chacun avec son rôle) : c'est l'ordre dans lequel on regarde un livre qu'on tient en main, et surtout le type COMMANDE LA SUITE de la saisie — les rôles ne se posent que là. Relégué en bas, il obligeait à descendre tout le formulaire pour dire « c'est une BD », c'est-à-dire après avoir saisi ce qui en dépend. ⚠️ Remonter le champ le rend visible ; le PRÉREMPLIR écrirait quelque chose de faux. Le défaut reste « non précisé », qui ne prétend rien (décision actée) — c'est aussi ce qui rend l'étiquette honnête dans les listes : elle ne s'affiche que si un type a été choisi. *@
Type (facultatif)
@foreach (var type in Types) { }
@* ⚠️ Thèmes et auteurs se saisissent UN PAR UN, et non plus sur une ligne à séparateurs. Le champ unique se reliait à chaque frappe sur une propriété qui découpait puis recomposait la liste : le point-virgule tapé y était élagué (entrée vide) et l'espace qui le suit rogné — le séparateur était le seul caractère qu'on ne pouvait pas taper. Voir CLAUDE.md. *@ @* Un auteur par ligne, et son RÔLE avec lui. ⚠️ Le rôle vit sur le LIEN, pas sur la personne : le même auteur scénarise un album et dessine le suivant. C'est aussi pourquoi renommer une ligne conserve son rôle (NomsAuteurs) — on corrige un nom, on ne désigne pas quelqu'un d'autre. ⚠️ Les rôles n'apparaissent qu'à partir de DEUX auteurs, décision inchangée : un roman n'a qu'un signataire dont le rôle ne se pose pas. Le défaut reste « non précisé ». *@ @if (Saisie.Auteurs.Count > 1) {
@foreach (var role in Roles) { }
}
@* Le format n'est plus affiché dans les listes que pour les ebooks : un livre y est donc physique par défaut. Cette valeur DOIT rester présélectionnée ici, sinon on créerait des fiches sans format en croyant que « rien d'affiché = physique ». C'est le cas sans code particulier : Format.Physique vaut 0, valeur par défaut de l'énumération, et le segment correspondant est donc actif dès l'ouverture. *@
Format (physique par défaut)
@foreach (var format in Formats) { }
@* ⚠️ Le champ reste VIDE quand on ne sait pas : « 0 » se lirait comme une donnée. C'est aussi pourquoi la valeur proposée par la BnF arrive ici et non en base directement — une pagination lue dans une phrase (« 1 vol. (349 p.) ») se corrige d'un geste. *@
Statut de lecture (le vôtre)
@if (!string.IsNullOrEmpty(Erreur)) { } @* Un enregistrement impossible se dit AVANT le clic : un bouton qui échoue en silence est pire que pas de bouton du tout (CLAUDE.md, stratégie hors-ligne). *@ @if (!string.IsNullOrEmpty(MessageBlocage)) {

@MessageBlocage

}
@if (OnAnnuler.HasDelegate) { }
@code { private static readonly Format[] Formats = Enum.GetValues(); private static readonly TypeDocument[] Types = Enum.GetValues(); private static readonly RoleAuteur[] Roles = Enum.GetValues(); /// /// Pose le rôle d'un auteur. /// /// /// est un record : on remplace l'entrée plutôt que de la muter, /// et la liste reste celle que la propriété Auteur saura relire pour préserver les /// rôles à la frappe suivante. /// private void PoserRole(int index, RoleAuteur role) => Saisie.Auteurs[index] = Saisie.Auteurs[index] with { Role = role }; /// /// Les noms des auteurs, vus comme une liste de chaînes par SaisieListe. /// /// /// ⚠️ Sa référence doit rester stable tant que la saisie ne change pas : le composant /// se remet à zéro quand la liste qu'on lui prête change d'identité, et il perdrait donc à /// chaque rendu ce qui est en cours de frappe. /// private NomsAuteurs _noms = default!; private EnregistrementLivre? _saisiePrecedente; protected override void OnParametersSet() { if (!ReferenceEquals(_saisiePrecedente, Saisie)) { _saisiePrecedente = Saisie; _noms = new NomsAuteurs(Saisie.Auteurs); } } [Parameter, EditorRequired] public EnregistrementLivre Saisie { get; set; } = new(); [Parameter] public string LibelleValidation { get; set; } = "Enregistrer"; [Parameter] public string? Erreur { get; set; } [Parameter] public bool EnCours { get; set; } /// /// Raison pour laquelle l'enregistrement est impossible (typiquement l'absence de réseau). /// Renseignée, elle désactive la validation et s'affiche : la saisie reste lisible, mais /// l'utilisateur sait pourquoi il ne peut pas valider. /// [Parameter] public string? MessageBlocage { get; set; } private bool Bloque => !string.IsNullOrEmpty(MessageBlocage); [Parameter] public EventCallback OnValider { get; set; } [Parameter] public EventCallback OnAnnuler { get; set; } private Task Valider() => OnValider.InvokeAsync(); }