Résolution d'un bug de switch entre les PNJ lorsqu'on essai de passer directement de l'un à l'autre

Ajout de dossiers pour la partie PNJ pour qu'on puisse les regrouper par cité par exemple et que ce soit plus lisible
This commit is contained in:
2026-06-08 11:05:07 +02:00
parent edc4434298
commit ed22d9f29c
14 changed files with 145 additions and 32 deletions

View File

@@ -36,6 +36,21 @@
/>
</div>
<div class="field">
<label for="npc-folder">Dossier</label>
<input
id="npc-folder"
type="text"
[(ngModel)]="folder"
name="folder"
list="npc-folders"
placeholder="Ex: Bard's Gate, Faction des Pipers… (laisser vide = non classé)"
/>
<datalist id="npc-folders">
<option *ngFor="let f of existingFolders" [value]="f"></option>
</datalist>
</div>
<div class="field-row image-row">
<div class="field portrait-field">
<label>Portrait</label>

View File

@@ -46,6 +46,9 @@ export class NpcEditComponent implements OnInit {
npcId: string | null = null;
name = '';
folder = '';
/** Dossiers déjà utilisés dans la campagne (datalist d'auto-complétion). */
existingFolders: string[] = [];
portraitImageId: string | null = null;
headerImageId: string | null = null;
values: Record<string, string> = {};
@@ -72,12 +75,14 @@ export class NpcEditComponent implements OnInit {
if (this.campaignId) {
this.loadTemplateForCampaign(this.campaignId);
this.campaignSidebar.show(this.campaignId);
this.loadExistingFolders(this.campaignId);
}
if (this.npcId) {
this.service.getById(this.npcId).subscribe({
next: (n) => {
this.name = n.name;
this.folder = n.folder ?? '';
this.portraitImageId = n.portraitImageId ?? null;
this.headerImageId = n.headerImageId ?? null;
this.values = n.values ?? {};
@@ -90,6 +95,17 @@ export class NpcEditComponent implements OnInit {
}
}
private loadExistingFolders(campaignId: string): void {
this.service.getByCampaign(campaignId).subscribe({
next: (list) => {
this.existingFolders = [...new Set(
list.map(n => (n.folder ?? '').trim()).filter(f => f.length > 0)
)].sort((a, b) => a.localeCompare(b, 'fr'));
},
error: () => { this.existingFolders = []; }
});
}
private loadTemplateForCampaign(campaignId: string): void {
this.campaignService.getCampaignById(campaignId).subscribe({
next: (campaign) => {
@@ -111,6 +127,7 @@ export class NpcEditComponent implements OnInit {
if (!this.name.trim() || !this.campaignId) return;
const payload = {
name: this.name.trim(),
folder: this.folder.trim() || null,
portraitImageId: this.portraitImageId,
headerImageId: this.headerImageId,
values: this.values,

View File

@@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { LucideAngularModule, ArrowLeft, Edit3, Sparkles } from 'lucide-angular';
import { NpcService } from '../../../services/npc.service';
import { CampaignService } from '../../../services/campaign.service';
@@ -22,7 +23,7 @@ import { AiChatDrawerComponent } from '../../../shared/ai-chat-drawer/ai-chat-dr
templateUrl: './npc-view.component.html',
styleUrls: ['./npc-view.component.scss']
})
export class NpcViewComponent implements OnInit {
export class NpcViewComponent implements OnInit, OnDestroy {
readonly ArrowLeft = ArrowLeft;
readonly Edit3 = Edit3;
readonly Sparkles = Sparkles;
@@ -36,6 +37,8 @@ export class NpcViewComponent implements OnInit {
chatOpen = false;
toggleChat(): void { this.chatOpen = !this.chatOpen; }
private paramsSub?: Subscription;
constructor(
private route: ActivatedRoute,
private router: Router,
@@ -46,25 +49,42 @@ export class NpcViewComponent implements OnInit {
) {}
ngOnInit(): void {
const params = this.route.snapshot.paramMap;
this.campaignId = params.get('campaignId');
this.npcId = params.get('npcId');
if (this.npcId) {
this.service.getById(this.npcId).subscribe({
next: n => { this.npc = n; },
error: () => this.back()
});
}
if (this.campaignId) {
this.campaignSidebar.show(this.campaignId);
this.campaignService.getCampaignById(this.campaignId).subscribe(camp => {
if (camp.gameSystemId) {
this.gameSystemService.getById(camp.gameSystemId).subscribe(gs => {
this.templateFields = gs.npcTemplate ?? [];
});
}
});
}
// S'abonner aux paramMap (pas le snapshot) : quand on passe d'un PNJ à un autre,
// Angular RÉUTILISE le composant (même route) → ngOnInit ne re-tourne pas. Sans ce
// subscribe, la fiche du centre resterait figée sur l'ancien PNJ.
this.paramsSub = this.route.paramMap.subscribe(params => {
const newCampaignId = params.get('campaignId');
this.npcId = params.get('npcId');
// Recharge la fiche à CHAQUE changement de PNJ.
this.chatOpen = false;
if (this.npcId) {
this.service.getById(this.npcId).subscribe({
next: n => { this.npc = n; },
error: () => this.back()
});
}
// Sidebar + template du système : seulement quand la campagne change (inutile
// de les recharger à chaque switch de PNJ d'une même campagne).
if (newCampaignId && newCampaignId !== this.campaignId) {
this.campaignId = newCampaignId;
this.campaignSidebar.show(this.campaignId);
this.campaignService.getCampaignById(this.campaignId).subscribe(camp => {
if (camp.gameSystemId) {
this.gameSystemService.getById(camp.gameSystemId).subscribe(gs => {
this.templateFields = gs.npcTemplate ?? [];
});
}
});
} else if (newCampaignId) {
this.campaignId = newCampaignId;
}
});
}
ngOnDestroy(): void {
this.paramsSub?.unsubscribe();
}
edit(): void {