Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5b4e117ba | ||
|
|
d76fb10ee0 |
@@ -214,13 +214,17 @@ public sealed class ServiceSouhaits(MaBibliDbContext db) : IServiceSouhaits
|
|||||||
souhait.Isbn = isbn;
|
souhait.Isbn = isbn;
|
||||||
souhait.Note = Vide(saisie.Note);
|
souhait.Note = Vide(saisie.Note);
|
||||||
|
|
||||||
// ⚠️ La couverture n'est écrite QUE si la charge utile en porte une, contrairement aux
|
// ⚠️ Écrite SEULEMENT quand le geste de correction a été utilisé (CoverUrlModifiee) :
|
||||||
// autres champs. Raison : aucun écran n'offre de champ « URL de couverture » pour une
|
// « Modifier une envie » enregistre aussi le titre, l'ISBN, etc. sans qu'on ait touché à
|
||||||
// envie — décision actée, un tel champ inviterait à coller des liens morts. Un
|
// la couverture, et cette valeur affichée peut être une DÉDUCTION à la lecture (formule
|
||||||
// remplacement inconditionnel effacerait donc, à chaque correction de titre, l'image
|
// OpenLibrary à ISBN connu, voir Projeter) plutôt que ce qu'il y a réellement en base.
|
||||||
// que la bibliographie avait fournie. Il n'y a rien à « vider » puisqu'il n'y a rien à
|
// La matérialiser à chaque enregistrement figerait cette déduction, et une correction
|
||||||
// saisir, et la lecture retombe de toute façon sur la formule OpenLibrary à ISBN connu.
|
// d'ISBN qui suit ne la referait plus jouer. Le drapeau distingue « je n'y ai pas
|
||||||
souhait.CoverUrl = Vide(saisie.CoverUrl) ?? souhait.CoverUrl;
|
// touché » de « je viens de vider le champ », ce qu'un simple null ne pourrait pas dire.
|
||||||
|
if (saisie.CoverUrlModifiee)
|
||||||
|
{
|
||||||
|
souhait.CoverUrl = Vide(saisie.CoverUrl);
|
||||||
|
}
|
||||||
|
|
||||||
// ⚠️ Indispensable : TitreNormalise est la CLÉ D'ŒUVRE, celle qui sert au rapprochement
|
// ⚠️ Indispensable : TitreNormalise est la CLÉ D'ŒUVRE, celle qui sert au rapprochement
|
||||||
// « déjà au catalogue ». Sans ce recalcul, corriger un titre laisserait le rapprochement
|
// « déjà au catalogue ». Sans ce recalcul, corriger un titre laisserait le rapprochement
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@inject ServiceLivresApi Api
|
@inject ServiceLivresApi Api
|
||||||
@inject EtatReseau Reseau
|
@inject EtatReseau Reseau
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
|
@inject CacheHorsLigne Cache
|
||||||
|
|
||||||
@*
|
@*
|
||||||
Mise en page pensée mobile d'abord : un bandeau, une colonne, rien à gauche.
|
Mise en page pensée mobile d'abord : un bandeau, une colonne, rien à gauche.
|
||||||
@@ -93,6 +94,16 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@*
|
||||||
|
Une rafale non vidée reste consultable ailleurs sur le site — sans quoi y revenir
|
||||||
|
depuis un autre écran demande de refaire tout le chemin d'« Ajouter ». L'entrée
|
||||||
|
disparaît d'elle-même une fois la file effacée (bouton « Nouvelle rafale »).
|
||||||
|
*@
|
||||||
|
@if (_rafale is { Total: > 0 })
|
||||||
|
{
|
||||||
|
<NavLink class="menu-lien menu-lien-rafale" href="ajout/rafale">@LibelleRafale</NavLink>
|
||||||
|
}
|
||||||
|
|
||||||
<NavLink class="menu-lien" href="" Match="NavLinkMatch.All">Catalogue</NavLink>
|
<NavLink class="menu-lien" href="" Match="NavLinkMatch.All">Catalogue</NavLink>
|
||||||
<NavLink class="menu-lien" href="auteurs">Auteurs</NavLink>
|
<NavLink class="menu-lien" href="auteurs">Auteurs</NavLink>
|
||||||
<NavLink class="menu-lien" href="series">Séries</NavLink>
|
<NavLink class="menu-lien" href="series">Séries</NavLink>
|
||||||
@@ -148,6 +159,12 @@
|
|||||||
|
|
||||||
private ElementReference _menu;
|
private ElementReference _menu;
|
||||||
|
|
||||||
|
/// <summary>Rafale non vidée, s'il y en a une. Relue à chaque navigation (lot AB).</summary>
|
||||||
|
private Rafale? _rafale;
|
||||||
|
|
||||||
|
private string LibelleRafale =>
|
||||||
|
_rafale is { Crees: > 0 } r ? $"Compte rendu ({r.Crees})" : "Rafale à reprendre";
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
Reseau.Change += SurChangementReseau;
|
Reseau.Change += SurChangementReseau;
|
||||||
@@ -158,6 +175,8 @@
|
|||||||
// entrées empilées au-dessus de la liste des auteurs.
|
// entrées empilées au-dessus de la liste des auteurs.
|
||||||
Navigation.LocationChanged += SurNavigation;
|
Navigation.LocationChanged += SurNavigation;
|
||||||
|
|
||||||
|
_rafale = await Cache.LireRafaleAsync();
|
||||||
|
|
||||||
// Écoute des bascules online/offline avant tout appel : un démarrage hors-ligne doit
|
// Écoute des bascules online/offline avant tout appel : un démarrage hors-ligne doit
|
||||||
// aller directement au cache, sans attendre l'échec d'une requête.
|
// aller directement au cache, sans attendre l'échec d'une requête.
|
||||||
await Reseau.DemarrerAsync();
|
await Reseau.DemarrerAsync();
|
||||||
@@ -226,13 +245,19 @@
|
|||||||
|
|
||||||
private void SurNavigation(object? _, LocationChangedEventArgs __)
|
private void SurNavigation(object? _, LocationChangedEventArgs __)
|
||||||
{
|
{
|
||||||
if (!_menuOuvert)
|
if (_menuOuvert)
|
||||||
{
|
{
|
||||||
return;
|
_menuOuvert = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_menuOuvert = false;
|
// ⚠️ Relu à CHAQUE navigation, pas seulement au quitter de l'écran de rafale : c'est ce
|
||||||
_ = InvokeAsync(StateHasChanged);
|
// qui fait apparaître l'entrée du menu dès le premier code scanné, et disparaître dès
|
||||||
|
// la file effacée, où qu'on soit rendu entre-temps.
|
||||||
|
_ = InvokeAsync(async () =>
|
||||||
|
{
|
||||||
|
_rafale = await Cache.LireRafaleAsync();
|
||||||
|
StateHasChanged();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -41,6 +41,16 @@ else if (_saisie is null)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="formulaire">
|
<div class="formulaire">
|
||||||
|
@* Même ligne que dans le formulaire d'un ouvrage (FormulaireLivre) : un aperçu quand il y
|
||||||
|
a quelque chose à montrer, puis un champ d'adresse ordinaire, au même titre que les
|
||||||
|
autres. *@
|
||||||
|
@if (!string.IsNullOrEmpty(_saisie.CoverUrl) || !string.IsNullOrWhiteSpace(_saisie.Titre))
|
||||||
|
{
|
||||||
|
<div class="formulaire-apercu">
|
||||||
|
<Couverture Url="@_saisie.CoverUrl" Titre="@_saisie.Titre" Classe="couverture-moyenne" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<label class="champ">
|
<label class="champ">
|
||||||
<span class="champ-libelle">Titre <span class="obligatoire" aria-hidden="true">*</span></span>
|
<span class="champ-libelle">Titre <span class="obligatoire" aria-hidden="true">*</span></span>
|
||||||
<input class="champ-saisie" type="text" @bind="_saisie.Titre" @bind:event="oninput"
|
<input class="champ-saisie" type="text" @bind="_saisie.Titre" @bind:event="oninput"
|
||||||
@@ -73,6 +83,12 @@ else
|
|||||||
@bind="_saisie.Isbn" @bind:event="oninput" placeholder="978…" autocomplete="off" />
|
@bind="_saisie.Isbn" @bind:event="oninput" placeholder="978…" autocomplete="off" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label class="champ">
|
||||||
|
<span class="champ-libelle">URL de couverture</span>
|
||||||
|
<input class="champ-saisie" type="url" @bind="_saisie.CoverUrl" @bind:event="oninput"
|
||||||
|
placeholder="https://…" autocomplete="off" />
|
||||||
|
</label>
|
||||||
|
|
||||||
<label class="champ">
|
<label class="champ">
|
||||||
<span class="champ-libelle">Note</span>
|
<span class="champ-libelle">Note</span>
|
||||||
<input class="champ-saisie" type="text" @bind="_saisie.Note" @bind:event="oninput"
|
<input class="champ-saisie" type="text" @bind="_saisie.Note" @bind:event="oninput"
|
||||||
@@ -104,6 +120,13 @@ else
|
|||||||
private bool _enCours;
|
private bool _enCours;
|
||||||
private string? _erreur;
|
private string? _erreur;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// La couverture telle que chargée — éventuellement déduite de l'ISBN (voir <c>Projeter</c>
|
||||||
|
/// côté serveur), pas forcément ce qu'il y a réellement en base. Sert uniquement à détecter,
|
||||||
|
/// à l'enregistrement, si l'utilisateur a touché au champ.
|
||||||
|
/// </summary>
|
||||||
|
private string? _couvertureChargee;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
Reseau.Change += SurChangementReseau;
|
Reseau.Change += SurChangementReseau;
|
||||||
@@ -135,9 +158,8 @@ else
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⚠️ La couverture n'est PAS reprise ici : elle n'a pas de champ de saisie, et le
|
_couvertureChargee = souhait.CoverUrl;
|
||||||
// service la conserve donc telle qu'elle est en base. La valeur portée par le DTO peut
|
|
||||||
// en outre être déduite de l'ISBN — la figer serait écrire en base ce qui se calcule.
|
|
||||||
_saisie = new EnregistrementSouhait
|
_saisie = new EnregistrementSouhait
|
||||||
{
|
{
|
||||||
Titre = souhait.Titre,
|
Titre = souhait.Titre,
|
||||||
@@ -145,6 +167,7 @@ else
|
|||||||
Editeur = souhait.Editeur,
|
Editeur = souhait.Editeur,
|
||||||
Annee = souhait.Annee,
|
Annee = souhait.Annee,
|
||||||
Isbn = souhait.Isbn,
|
Isbn = souhait.Isbn,
|
||||||
|
CoverUrl = souhait.CoverUrl,
|
||||||
Note = souhait.Note,
|
Note = souhait.Note,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -156,6 +179,16 @@ else
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ⚠️ Envoyée seulement si elle a changé depuis le chargement, et non à chaque
|
||||||
|
// enregistrement : la valeur affichée peut être une DÉDUCTION à la lecture (formule
|
||||||
|
// OpenLibrary à ISBN connu, voir `Projeter`) plutôt que ce qu'il y a réellement en base.
|
||||||
|
// La renvoyer telle quelle à chaque correction de titre la figerait en base, et une
|
||||||
|
// correction d'ISBN qui suit ne la referait plus jouer.
|
||||||
|
_saisie.CoverUrlModifiee = !string.Equals(
|
||||||
|
_saisie.CoverUrl?.Trim() ?? string.Empty,
|
||||||
|
_couvertureChargee?.Trim() ?? string.Empty,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
|
||||||
_enCours = true;
|
_enCours = true;
|
||||||
_erreur = null;
|
_erreur = null;
|
||||||
|
|
||||||
|
|||||||
@@ -1664,6 +1664,20 @@ body {
|
|||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Le compte rendu de la dernière rafale (lot AB) : une entrée transitoire, présente
|
||||||
|
seulement tant qu'une file non vidée existe. Le même jaune que la pastille hors-ligne
|
||||||
|
la distingue des six destinations permanentes — c'est une chose à finir, pas un lieu.
|
||||||
|
*/
|
||||||
|
.menu-lien-rafale {
|
||||||
|
border-left-color: #f5d76e;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-lien-rafale.active {
|
||||||
|
border-left-color: #f5d76e;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
À partir de 40rem la rangée tient sans se comprimer, et la bascule n'a plus
|
À partir de 40rem la rangée tient sans se comprimer, et la bascule n'a plus
|
||||||
de raison d'être. Le point de rupture est répété dans `MainLayout.razor.css`
|
de raison d'être. Le point de rupture est répété dans `MainLayout.razor.css`
|
||||||
@@ -1705,6 +1719,10 @@ body {
|
|||||||
border-bottom-color: #f5d76e;
|
border-bottom-color: #f5d76e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-lien-rafale {
|
||||||
|
border-bottom-color: #f5d76e;
|
||||||
|
}
|
||||||
|
|
||||||
/* En rangée, un filet au-dessus ne séparerait rien : le trait passe à GAUCHE, et la marge
|
/* En rangée, un filet au-dessus ne séparerait rien : le trait passe à GAUCHE, et la marge
|
||||||
supérieure disparaît sous peine de désaligner l'entrée du reste de la rangée. */
|
supérieure disparaît sous peine de désaligner l'entrée du reste de la rangée. */
|
||||||
.menu-lien-annexe {
|
.menu-lien-annexe {
|
||||||
|
|||||||
@@ -65,6 +65,18 @@ public record EnregistrementSouhait
|
|||||||
|
|
||||||
public string? CoverUrl { get; set; }
|
public string? CoverUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Vrai seulement si l'utilisateur vient d'utiliser le geste de correction de la couverture.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// ⚠️ Sans ce signal, <see cref="CoverUrl"/> à <c>null</c> serait ambigu entre « je n'y ai
|
||||||
|
/// pas touché » (garder ce qu'il y a en base, y compris rien du tout, et laisser jouer la
|
||||||
|
/// formule OpenLibrary à ISBN connu) et « je viens de vider le champ » (retirer la
|
||||||
|
/// couverture pour de bon). Modifier le titre ou l'ISBN d'une envie ne doit jamais
|
||||||
|
/// matérialiser en base la couverture déduite qu'elle ne fait qu'afficher à la lecture.
|
||||||
|
/// </remarks>
|
||||||
|
public bool CoverUrlModifiee { get; set; }
|
||||||
|
|
||||||
public string? Note { get; set; }
|
public string? Note { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user