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,157 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using MaBibli.Shared.Entites;
|
||||
using MaBibli.Shared.Textes;
|
||||
|
||||
namespace MaBibli.Shared.Dtos;
|
||||
|
||||
/// <summary>
|
||||
/// Charge utile de création et de modification d'un livre.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="Livre.AjoutePar"/> n'y figure pas volontairement : il est déterminé par le serveur
|
||||
/// à partir de l'utilisateur authentifié, jamais par le client.
|
||||
/// </remarks>
|
||||
public record EnregistrementLivre
|
||||
{
|
||||
/// <summary>Séparateur des auteurs dans la forme texte d'un champ de saisie.</summary>
|
||||
/// <remarks>
|
||||
/// Le point-virgule, et non la virgule : celle-ci est déjà prise par la forme
|
||||
/// bibliographique « Zola, Émile », qu'on doit pouvoir taper telle quelle.
|
||||
/// </remarks>
|
||||
public const char SeparateurAuteurs = ';';
|
||||
|
||||
/// <summary>Facultatif : certains livres n'ont pas d'ISBN. Normalisé par le serveur.</summary>
|
||||
public string? Isbn { get; set; }
|
||||
|
||||
public string Titre { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Auteurs, dans l'ordre de la couverture, chacun avec son rôle sur <b>ce</b> livre. Le
|
||||
/// serveur les rattache aux fiches existantes quand c'est sûr, et en crée sinon.
|
||||
/// </summary>
|
||||
public List<AuteurSaisi> Auteurs { get; set; } = [];
|
||||
|
||||
/// <summary>Thèmes saisis manuellement, séparés par des points-virgules dans l'interface.</summary>
|
||||
public List<string> Themes { get; set; } = [];
|
||||
|
||||
public string? Editeur { get; set; }
|
||||
|
||||
public Format Format { get; set; }
|
||||
|
||||
/// <summary>Roman, bande dessinée… Facultatif : « non précisé » est une réponse valable.</summary>
|
||||
public TypeDocument TypeDocument { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Statut de lecture <b>de l'appelant</b>. <c>null</c> = non commencé, et aucune ligne n'est
|
||||
/// alors écrite en base.
|
||||
/// </summary>
|
||||
public Statut? Statut { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nombre de pages. <c>null</c> = non renseigné, et surtout pas <c>0</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Peut arriver prérempli depuis <c>dc:format</c> de la BnF ; c'est alors une
|
||||
/// <b>proposition</b>, que la saisie corrige ou efface.
|
||||
/// </remarks>
|
||||
public int? NombrePages { get; set; }
|
||||
|
||||
public string? CoverUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Notice ayant servi au préremplissage, ou <c>null</c> pour une saisie manuelle.
|
||||
/// </summary>
|
||||
public string? UrlNotice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vue « une seule ligne » de <see cref="Auteurs"/>, pour les champs de saisie simples.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ⚠️ <b>Ce n'est plus un champ de saisie</b> : le formulaire remplit la liste un auteur à
|
||||
/// la fois (voir <c>SaisieListe</c>), un champ relié à chaque frappe sur cette propriété
|
||||
/// ayant rendu son propre séparateur intapable. Elle ne sert plus qu'au <b>préremplissage</b>
|
||||
/// depuis une notice, qui donne les auteurs sur une ligne — une seule affectation, jamais
|
||||
/// une par caractère.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public string? Auteur
|
||||
{
|
||||
get => Auteurs.Count == 0 ? null : string.Join($"{SeparateurAuteurs} ", Auteurs.Select(a => a.Nom));
|
||||
set => Auteurs = Decouper(value, Auteurs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Découpe une saisie texte en auteurs, en écartant les entrées vides.
|
||||
/// </summary>
|
||||
/// <param name="connus">
|
||||
/// Auteurs déjà saisis, dont les <b>rôles</b> sont repris quand le nom réapparaît.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// ⚠️ La reprise des rôles a été écrite pour un champ qui se reconstruisait à chaque frappe.
|
||||
/// Ce champ n'existe plus — le formulaire corrige un nom sans toucher à son rôle
|
||||
/// (<c>NomsAuteurs</c>) — mais elle reste juste, et gratuite, pour un préremplissage qui
|
||||
/// écraserait sinon des rôles déjà posés.
|
||||
/// </remarks>
|
||||
public static List<AuteurSaisi> Decouper(string? saisie, IEnumerable<AuteurSaisi>? connus = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(saisie))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var roles = new Dictionary<string, RoleAuteur>(StringComparer.Ordinal);
|
||||
|
||||
foreach (var connu in connus ?? [])
|
||||
{
|
||||
roles.TryAdd(NormalisationTexte.Normaliser(connu.Nom), connu.Role);
|
||||
}
|
||||
|
||||
return saisie
|
||||
.Split(SeparateurAuteurs, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.Select(nom => new AuteurSaisi(
|
||||
nom, roles.GetValueOrDefault(NormalisationTexte.Normaliser(nom), RoleAuteur.NonPrecise)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Découpe une ligne de thèmes. Séparateur : le <b>point-virgule</b>, la virgule restant
|
||||
/// acceptée — un thème n'en contient jamais.
|
||||
/// <para>
|
||||
/// ⚠️ Plus aucun champ ne s'y lie : les thèmes se saisissent un par un. Cela reste la façon
|
||||
/// de relire une ligne venue d'ailleurs.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static List<string> DecouperThemes(string? saisie) =>
|
||||
ListeSaisie.Decouper(saisie);
|
||||
}
|
||||
|
||||
/// <summary>Un auteur tel qu'il est saisi : son nom, et son rôle sur ce livre.</summary>
|
||||
/// <remarks>
|
||||
/// Le rôle n'appartient pas à la personne mais au <b>lien</b> : le même auteur scénarise un
|
||||
/// album et dessine le suivant. Voir <see cref="RoleAuteur"/>.
|
||||
/// </remarks>
|
||||
public record AuteurSaisi(string Nom, RoleAuteur Role = RoleAuteur.NonPrecise);
|
||||
|
||||
/// <summary>Demande de renommage d'une fiche auteur.</summary>
|
||||
public record RenommerAuteur(string Nom);
|
||||
|
||||
/// <summary>Résultat d'un renommage, avec collision éventuelle à confirmer.</summary>
|
||||
public record ResultatRenommageAuteur
|
||||
{
|
||||
public AuteurDto? Auteur { get; init; }
|
||||
|
||||
public AuteurDto? Collision { get; init; }
|
||||
|
||||
public bool EstCollision => Collision is not null;
|
||||
}
|
||||
|
||||
/// <summary>Charge utile du changement de statut de lecture — l'action la plus fréquente.</summary>
|
||||
/// <remarks>
|
||||
/// Le statut posé est toujours celui de l'appelant : il n'y a pas de champ « utilisateur »,
|
||||
/// et il ne doit jamais y en avoir. Personne ne coche « lu » à la place d'un autre.
|
||||
/// </remarks>
|
||||
public record ChangementStatut
|
||||
{
|
||||
/// <summary><c>null</c> retire le statut : le livre redevient « non commencé » pour l'appelant.</summary>
|
||||
public Statut? Statut { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user