Quêtes libres : fin des conteneurs fantômes (cascade, impact annoncé, export filtré, migration V24)
Some checks failed
Qualité & Sécurité / Web (ESLint · angular-eslint + sonarjs) (push) Successful in 52s
Qualité & Sécurité / Trivy (CVE dépendances Maven / pip / npm) (push) Successful in 2m36s
Qualité & Sécurité / MegaLinter (PMD · Ruff · Bandit · gitleaks · hadolint) (push) Successful in 1m11s
Build & Push Images / build (web) (push) Failing after 35s
Build & Push Images / build (brain) (push) Successful in 1m18s
Build & Push Images / build-switcher (push) Successful in 42s
Build & Push Images / build (core) (push) Successful in 3m8s

Sidebar Lore : le panneau Templates ne peut plus écraser l'arbre

Plafonné à 45 % de la sidebar avec ascenseur interne sur la liste —
l'en-tête, le repli et le bouton + restent toujours visibles, l'arbre
garde son propre scroll (remonté par un utilisateur avec ~30 templates)

Passage v1.0.3
This commit is contained in:
2026-07-10 09:19:26 +02:00
parent 1c8b24c5dd
commit 0c44e42a22
17 changed files with 488 additions and 20 deletions

4
web/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "loremind-web",
"version": "1.0.2-beta",
"version": "1.0.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "loremind-web",
"version": "1.0.2-beta",
"version": "1.0.3",
"dependencies": {
"@angular/animations": "^21.2.17",
"@angular/cdk": "^21.2.14",

View File

@@ -1,6 +1,6 @@
{
"name": "loremind-web",
"version": "1.0.2-beta",
"version": "1.0.3",
"description": "LoreMind Frontend - Angular",
"scripts": {
"ng": "ng",

View File

@@ -181,14 +181,32 @@ export class QuestViewComponent implements OnInit {
this.router.navigate(['/campaigns', this.campaignId, 'quests', this.questId, 'edit']);
}
/** Suppression simple : une quête n'a pas d'enfants (les nœuds sont des références faibles). */
/**
* Suppression avec annonce de l'impact réel : le conteneur de scènes d'une quête
* LIBRE (arc SYSTEM, invisible dans l'arbre) part en cascade avec ses scènes —
* le nombre est affiché dans la confirmation. Les nœuds simplement liés et les
* jumeaux de hub (qui restent visibles dans leur arc) ne sont pas concernés.
*/
deleteQuest(): void {
if (!this.quest) return;
const quest = this.quest;
if (!quest?.id) return;
this.questService.deletionImpact(this.campaignId, quest.id).subscribe({
next: impact => this.confirmDeleteQuest(quest, impact.scenes),
error: () => this.confirmDeleteQuest(quest, 0) // impact indisponible : confirmation simple
});
}
private confirmDeleteQuest(quest: Quest, scenes: number): void {
const details = [
...(scenes > 0
? [this.translate.instant('questView.deleteImpactScenes', { count: scenes })]
: []),
this.translate.instant('questView.irreversible')
];
this.confirmDialog.confirm({
title: this.translate.instant('questView.deleteTitle'),
message: this.translate.instant('questView.deleteMessage', { name: quest.name }),
details: [this.translate.instant('questView.irreversible')],
details,
confirmLabel: this.translate.instant('common.delete'),
variant: 'danger'
}).then(ok => {

View File

@@ -3,6 +3,11 @@ import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Quest, QuestCreate } from './campaign.model';
/** Impact d'une suppression : scènes du conteneur (quête libre) qui partiront avec. */
export interface QuestDeletionImpact {
scenes: number;
}
/**
* Service HTTP pour les Quêtes (Niveau 1). API nestée sous la campagne :
* /api/campaigns/{campaignId}/quests. Si {@code playthroughId} est fourni, les
@@ -39,6 +44,10 @@ export class QuestService {
return this.http.delete<void>(`${this.base(campaignId)}/${questId}`);
}
deletionImpact(campaignId: string, questId: string): Observable<QuestDeletionImpact> {
return this.http.get<QuestDeletionImpact>(`${this.base(campaignId)}/${questId}/deletion-impact`);
}
reorder(campaignId: string, orderedIds: string[]): Observable<void> {
return this.http.put<void>(`${this.base(campaignId)}/reorder`, { orderedIds });
}

View File

@@ -148,6 +148,11 @@
display: flex;
flex-direction: column;
gap: 0.25rem;
// PLAFONNÉ : avec beaucoup de templates, c'est la liste du panneau qui défile
// (cf. .panel-list) — sans plafond, le panneau grandissait jusqu'à écraser
// complètement l'arbre au-dessus (sidebar en overflow: hidden).
flex-shrink: 0;
max-height: 45%;
}
.panel-header-row {
@@ -202,6 +207,10 @@
display: flex;
flex-direction: column;
gap: 0.1rem;
// Ascenseur interne, symétrique de celui de l'arbre (.tree) : l'en-tête du
// panneau et son bouton « + » restent toujours visibles.
overflow-y: auto;
min-height: 0;
}
.panel-item {

View File

@@ -1707,6 +1707,7 @@
"notProvided": "Not provided",
"deleteTitle": "Delete quest",
"deleteMessage": "Delete the quest \"{{name}}\"?",
"deleteImpactScenes": "{{count}} scene(s) of this quest will be deleted with it.",
"irreversible": "This action is irreversible.",
"deletedPage": "(deleted page)",
"deletedQuest": "(deleted quest)",

View File

@@ -1707,6 +1707,7 @@
"notProvided": "Non renseigné",
"deleteTitle": "Supprimer la quête",
"deleteMessage": "Supprimer la quête « {{name}} » ?",
"deleteImpactScenes": "{{count}} scène(s) de cette quête seront supprimées avec elle.",
"irreversible": "Cette action est irréversible.",
"deletedPage": "(page supprimée)",
"deletedQuest": "(quête supprimée)",