Files
Mathieu LimonierandClaude Opus 5 6a6d745af4 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>
2026-08-22 22:36:16 +02:00

105 lines
3.9 KiB
C#

namespace MaBibli.Shared.Dtos;
/// <summary>
/// Une série telle qu'exposée par l'API — trilogie, cycle, intégrale.
/// </summary>
/// <remarks>
/// <b>La liste est rendue à plat</b>, chaque série portant l'identifiant de sa parente, et c'est
/// le client qui rebâtit l'arbre. Deux raisons : un JSON récursif se cache mal dans un
/// instantané hors-ligne, et une seule lecture suffit alors à servir la liste <i>et</i> le
/// détail — donc un seul instantané à tenir à jour.
/// </remarks>
public record SerieDto
{
public required int Id { get; init; }
public required string Titre { get; init; }
/// <summary>La série qui contient celle-ci, si elle fait partie d'un cycle.</summary>
public int? SerieParenteId { get; init; }
/// <summary>Rang dans la série parente. Sans objet à la racine.</summary>
public int Position { get; init; }
/// <summary>Les tomes, dans l'ordre de <b>lecture</b>.</summary>
public IReadOnlyList<ElementSerieDto> Elements { get; init; } = [];
/// <summary>Qui a créé la série. Trace d'affichage : la série est commune au foyer.</summary>
public string? AjoutePar { get; init; }
/// <summary>Tomes possédés sur tomes recensés — « 4 sur 7 », le cœur de l'écran.</summary>
public int NombrePossedes => Elements.Count(e => e.Possede);
}
/// <summary>
/// Une place dans l'ordre de lecture, occupée ou non.
/// </summary>
/// <remarks>
/// ⚠️ Un élément sans <see cref="LivreId"/> n'est <b>pas</b> une anomalie : c'est un tome qu'on
/// ne possède pas, et c'est précisément ce que l'écran doit montrer.
/// </remarks>
public record ElementSerieDto
{
public required int Id { get; init; }
public int Position { get; init; }
/// <summary>Titre du tome : celui du livre quand il est rattaché, sinon celui qui a été saisi.</summary>
public required string Titre { get; init; }
public int? LivreId { get; init; }
public bool Possede => LivreId is not null;
/// <summary>Auteurs du livre rattaché, pour ne pas avoir à ouvrir sa fiche.</summary>
public string? Auteurs { get; init; }
/// <summary>À qui le tome est prêté, s'il est dehors. Commun au foyer, comme tout prêt.</summary>
public string? PreteA { get; init; }
}
/// <summary>Charge utile de création ou de renommage d'une série.</summary>
public record EnregistrementSerie
{
public string Titre { get; set; } = string.Empty;
/// <summary>Série parente, pour ranger une trilogie dans un cycle. <c>null</c> = à la racine.</summary>
public int? SerieParenteId { get; set; }
}
/// <summary>
/// Charge utile d'ajout d'un tome : soit un livre du catalogue, soit un simple titre.
/// </summary>
/// <remarks>
/// Les deux voies existent parce qu'une saga se recense d'un coup — souvent avant d'en posséder
/// la moitié — puis se remplit au fil des achats.
/// </remarks>
public record AjoutElementSerie
{
/// <summary>Livre du catalogue à placer ici. <c>null</c> = un tome qu'on ne possède pas.</summary>
public int? LivreId { get; set; }
/// <summary>Titre du tome. Obligatoire sans livre ; repris du livre sinon.</summary>
public string? Titre { get; set; }
}
/// <summary>Nouvel ordre de lecture : la liste <b>entière</b> des identifiants d'éléments.</summary>
/// <remarks>
/// Comme pour la liste d'envies, on prend la liste complète plutôt qu'un déplacement unitaire :
/// une seule opération à vérifier, et c'est ce dont a besoin le glisser-déposer comme les
/// flèches.
/// </remarks>
public record OrdreElementsSerie
{
public List<int> Ids { get; set; } = [];
}
/// <summary>Nouvel ordre des séries directement contenues dans un cycle.</summary>
public record OrdreSeries
{
public List<int> Ids { get; set; } = [];
}
/// <summary>Déplacement d'une série dans l'ordre de lecture de son cycle.</summary>
public readonly record struct DeplacementSerie(int SerieId, int ParenteId, int Delta);