Livret PDF v2, graphe des liens côté campagne, et refactor de l'import

Livret PDF (export campagne) :
- refonte lisibilité façon livre de JdR : sommaire paginé, libellés de champs
  en tête de ligne, encadrés « À lire aux joueurs » / secrets MJ / combat,
  ligne de contexte des scènes, en-tête courant, tables zébrées
- reflète la fusion quête/conteneur : arc SYSTEM masqué, quêtes de hub
  fusionnées dans leur chapitre, partie « Quêtes » sans doublon
- contenu complété : sorties de scènes, ennemis liés au bestiaire, pièces
  explorables, partie « Tables aléatoires »
- polices DejaVu embarquées (couverture Unicode, licence incluse) et harness
  visuel PdfExportPreviewTest (Mockito pur, tourne sans Postgres)

Graphe des liens :
- déplacé du Lore vers la campagne (les PNJ sont des entités de campagne) ;
  endpoint /api/npcs/lore/{id} supprimé
- layout retravaillé (anti-chevauchement des libellés, éventail des feuilles
  autour des hubs), scènes et quêtes ajoutées, libellés sur 2 lignes,
  filtres de légende mémorisés, focus au survol
- positions drag & drop persistées sur la campagne (migration V23,
  PUT /api/campaigns/{id}/graph-positions, bouton « Disposition auto »)

Divers :
- ImportService découpé en classes dédiées (parser d'archive, remapper d'ids,
  inserters campagne/lore/état de jeu, conversion legacy des quêtes)
- fuites d'abonnements corrigées (takeUntilDestroyed sur paramMap) sur ~15 écrans
- garde-fou i18n fragments/consolidés branché sur npm run build (check-i18n.mjs),
  budget SCSS anyComponentStyle relevé à 12 ko
This commit is contained in:
2026-07-04 11:15:16 +02:00
parent b2c3800bf8
commit beafbc2fa9
69 changed files with 3720 additions and 1509 deletions

View File

@@ -10,6 +10,8 @@ export interface Campaign {
loreId?: string | null;
/** ID du GameSystem associé (weak reference cross-context). `null` = campagne générique. */
gameSystemId?: string | null;
/** Positions des nœuds du graphe de campagne (JSON `"<kind>:<id>" -> {x,y}`). Null = auto. */
graphPositions?: string | null;
}
// Interface pour la création de Campaign (sans id)

View File

@@ -73,6 +73,11 @@ export class CampaignService {
return this.http.get<Campaign>(`${this.apiUrl}/${id}`);
}
/** Sauvegarde la disposition du graphe de campagne (JSON opaque ; '' = disposition auto). */
updateGraphPositions(id: string, positionsJson: string): Observable<void> {
return this.http.put<void>(`${this.apiUrl}/${id}/graph-positions`, { positions: positionsJson });
}
/** Bilan de préparation (Pilier B) — alimente les pastilles de l'arbre de la sidebar. */
getReadiness(id: string): Observable<CampaignReadinessAssessment> {
return this.http.get<CampaignReadinessAssessment>(`${this.apiUrl}/${id}/readiness`);

View File

@@ -16,11 +16,6 @@ export class NpcService {
return this.http.get<Npc[]>(`${this.apiUrl}/campaign/${campaignId}`);
}
/** PNJ de toutes les campagnes liées à un Lore — alimente le graphe du Lore. */
getByLore(loreId: string): Observable<Npc[]> {
return this.http.get<Npc[]>(`${this.apiUrl}/lore/${loreId}`);
}
getById(id: string): Observable<Npc> {
return this.http.get<Npc>(`${this.apiUrl}/${id}`);
}