Correction bug suppression complète coté lore (et suppression dans tout ce qui est campagne de la partie lore liée).
Améliorations ux : - Bandeau en haut qui reste accessible lors de la création d'un élément (chapitre, page, scène etc...) - Mise en place d'un surlignage pour voir su quel élément on est positionné
This commit is contained in:
85
web/src/app/lore/folder-view/folder-view.component.html
Normal file
85
web/src/app/lore/folder-view/folder-view.component.html
Normal file
@@ -0,0 +1,85 @@
|
||||
<div class="folder-view" *ngIf="node">
|
||||
|
||||
<!-- Fil d'Ariane : Lore → ancêtres → dossier courant -->
|
||||
<nav class="breadcrumb" aria-label="Fil d'Ariane">
|
||||
<button type="button" class="crumb" (click)="navigateToLoreRoot()" *ngIf="lore">
|
||||
{{ lore.name }}
|
||||
</button>
|
||||
<ng-container *ngFor="let ancestor of ancestors">
|
||||
<lucide-icon [img]="ChevronRight" [size]="12" class="crumb-sep"></lucide-icon>
|
||||
<button type="button" class="crumb" (click)="navigateToSubfolder(ancestor.id!)">
|
||||
{{ ancestor.name }}
|
||||
</button>
|
||||
</ng-container>
|
||||
<lucide-icon [img]="ChevronRight" [size]="12" class="crumb-sep"></lucide-icon>
|
||||
<span class="crumb current">{{ node.name }}</span>
|
||||
</nav>
|
||||
|
||||
<!-- Header : icône + nom + actions -->
|
||||
<div class="detail-header">
|
||||
<div class="header-texts">
|
||||
<h1>
|
||||
<lucide-icon [img]="folderIcon" [size]="24" class="title-icon"></lucide-icon>
|
||||
{{ node.name }}
|
||||
</h1>
|
||||
<p class="description">
|
||||
{{ subfolders.length }} sous-dossier(s) · {{ pages.length }} page(s)
|
||||
</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button type="button" class="btn-secondary" (click)="navigateToEdit()" title="Modifier le dossier">
|
||||
<lucide-icon [img]="Pencil" [size]="14"></lucide-icon>
|
||||
Modifier
|
||||
</button>
|
||||
<button type="button" class="btn-danger" (click)="delete()" title="Supprimer le dossier et tout son contenu">
|
||||
<lucide-icon [img]="Trash2" [size]="14"></lucide-icon>
|
||||
Supprimer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sous-dossiers -->
|
||||
<section class="detail-section">
|
||||
<div class="section-header">
|
||||
<h2>Sous-dossiers</h2>
|
||||
<button class="btn-add" (click)="navigateToCreateSubfolder()">
|
||||
<lucide-icon [img]="Plus" [size]="14"></lucide-icon>
|
||||
Nouveau sous-dossier
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="items-grid" *ngIf="subfolders.length > 0">
|
||||
<div class="node-card" *ngFor="let sub of subfolders" (click)="navigateToSubfolder(sub.id!)">
|
||||
<lucide-icon [img]="Folder" [size]="24" class="node-icon"></lucide-icon>
|
||||
<span class="node-name">{{ sub.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" *ngIf="subfolders.length === 0">
|
||||
<p>Aucun sous-dossier.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Pages -->
|
||||
<section class="detail-section">
|
||||
<div class="section-header">
|
||||
<h2>Pages</h2>
|
||||
<button class="btn-add" (click)="navigateToCreatePage()">
|
||||
<lucide-icon [img]="Plus" [size]="14"></lucide-icon>
|
||||
Nouvelle page
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="items-grid" *ngIf="pages.length > 0">
|
||||
<div class="node-card" *ngFor="let page of pages" (click)="navigateToPage(page.id!)">
|
||||
<lucide-icon [img]="FileText" [size]="24" class="node-icon"></lucide-icon>
|
||||
<span class="node-name">{{ page.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" *ngIf="pages.length === 0">
|
||||
<p>Aucune page dans ce dossier.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
154
web/src/app/lore/folder-view/folder-view.component.scss
Normal file
154
web/src/app/lore/folder-view/folder-view.component.scss
Normal file
@@ -0,0 +1,154 @@
|
||||
.folder-view {
|
||||
padding: 2.5rem 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
font-size: 0.85rem;
|
||||
color: #6b7280;
|
||||
|
||||
.crumb {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #9ca3af;
|
||||
padding: 0.15rem 0.35rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
transition: color 0.15s, background 0.15s;
|
||||
|
||||
&:hover { color: #c7d2fe; background: #1f2937; }
|
||||
|
||||
&.current {
|
||||
color: #e5e7eb;
|
||||
font-weight: 500;
|
||||
cursor: default;
|
||||
}
|
||||
&.current:hover { background: transparent; }
|
||||
}
|
||||
|
||||
.crumb-sep { color: #4b5563; flex-shrink: 0; }
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
// Sticky pour que Modifier/Supprimer restent accessibles même en scrollant
|
||||
// une longue liste de sous-dossiers/pages.
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background: #0a0a14;
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid #1f2937;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 1.5rem;
|
||||
|
||||
.header-texts { flex: 1; min-width: 0; }
|
||||
|
||||
h1 {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.title-icon { color: #6c63ff; }
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #6b7280;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary, .btn-danger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.btn-secondary { background: #1f2937; color: #d1d5db; &:hover { background: #374151; } }
|
||||
.btn-danger { background: #3a1e1e; color: #f87171; &:hover { background: #5a2e2e; } }
|
||||
|
||||
.detail-section {
|
||||
background: #0d1117;
|
||||
border: 1px solid #1f2937;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem 1.75rem;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1.25rem;
|
||||
|
||||
h2 { color: #d1d5db; font-size: 1rem; font-weight: 600; }
|
||||
}
|
||||
|
||||
.btn-add {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: #6c63ff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover { background: #5b52e0; }
|
||||
}
|
||||
|
||||
.items-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.node-card {
|
||||
background: #111827;
|
||||
border: 1px solid #1f2937;
|
||||
border-radius: 10px;
|
||||
padding: 1.25rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, transform 0.2s;
|
||||
|
||||
&:hover { border-color: #6c63ff; transform: translateY(-2px); }
|
||||
|
||||
.node-icon { color: #6c63ff; }
|
||||
.node-name { color: white; font-size: 0.9rem; font-weight: 600; }
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
color: #6b7280;
|
||||
font-size: 0.9rem;
|
||||
padding: 1rem 0.5rem;
|
||||
}
|
||||
179
web/src/app/lore/folder-view/folder-view.component.ts
Normal file
179
web/src/app/lore/folder-view/folder-view.component.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { forkJoin } from 'rxjs';
|
||||
import { LucideAngularModule, LucideIconData, Folder, FileText, Pencil, Trash2, Plus, ChevronRight } from 'lucide-angular';
|
||||
import { LoreService } from '../../services/lore.service';
|
||||
import { TemplateService } from '../../services/template.service';
|
||||
import { PageService } from '../../services/page.service';
|
||||
import { LayoutService } from '../../services/layout.service';
|
||||
import { PageTitleService } from '../../services/page-title.service';
|
||||
import { Lore, LoreNode } from '../../services/lore.model';
|
||||
import { Page } from '../../services/page.model';
|
||||
import { loadLoreSidebarData, buildLoreSidebarConfig } from '../lore-sidebar.helper';
|
||||
import { resolveIcon } from '../lore-icons';
|
||||
|
||||
/**
|
||||
* Vue "détail" d'un dossier : affiche son contenu (sous-dossiers + pages) et
|
||||
* expose les actions Modifier / Supprimer dans le header.
|
||||
*
|
||||
* L'édition du nom/icône/parent se fait dans l'écran séparé folder-edit
|
||||
* (/folders/:folderId/edit). La suppression avec cascade déclenche le même
|
||||
* dialogue d'impact que les autres écrans.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-folder-view',
|
||||
standalone: true,
|
||||
imports: [CommonModule, LucideAngularModule],
|
||||
templateUrl: './folder-view.component.html',
|
||||
styleUrls: ['./folder-view.component.scss']
|
||||
})
|
||||
export class FolderViewComponent implements OnInit, OnDestroy {
|
||||
readonly Folder = Folder;
|
||||
readonly FileText = FileText;
|
||||
readonly Pencil = Pencil;
|
||||
readonly Trash2 = Trash2;
|
||||
readonly Plus = Plus;
|
||||
readonly ChevronRight = ChevronRight;
|
||||
|
||||
loreId = '';
|
||||
folderId = '';
|
||||
lore: Lore | null = null;
|
||||
node: LoreNode | null = null;
|
||||
subfolders: LoreNode[] = [];
|
||||
pages: Page[] = [];
|
||||
/** Chaîne des dossiers ancêtres (du plus proche du racine vers le parent direct). */
|
||||
ancestors: LoreNode[] = [];
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private loreService: LoreService,
|
||||
private templateService: TemplateService,
|
||||
private pageService: PageService,
|
||||
private layoutService: LayoutService,
|
||||
private pageTitleService: PageTitleService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loreId = this.route.snapshot.paramMap.get('loreId')!;
|
||||
// Réagit aux changements de :folderId pour que la navigation d'un dossier
|
||||
// à un autre via la sidebar ne démonte/remonte pas le composant à blanc.
|
||||
this.route.paramMap.subscribe(pm => {
|
||||
const next = pm.get('folderId')!;
|
||||
if (next !== this.folderId) {
|
||||
this.folderId = next;
|
||||
this.load();
|
||||
}
|
||||
});
|
||||
this.folderId = this.route.snapshot.paramMap.get('folderId')!;
|
||||
this.load();
|
||||
}
|
||||
|
||||
private load(): void {
|
||||
forkJoin({
|
||||
sidebar: loadLoreSidebarData(this.loreId, this.loreService, this.templateService, this.pageService),
|
||||
node: this.loreService.getLoreNodeById(this.folderId)
|
||||
}).subscribe(({ sidebar, node }) => {
|
||||
this.layoutService.show(buildLoreSidebarConfig(sidebar));
|
||||
this.lore = sidebar.lore;
|
||||
this.node = node;
|
||||
this.pageTitleService.set(node.name);
|
||||
this.subfolders = sidebar.nodes.filter(n => n.parentId === this.folderId);
|
||||
this.pages = sidebar.pages.filter(p => p.nodeId === this.folderId);
|
||||
this.ancestors = this.buildAncestors(node, sidebar.nodes);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remonte la chaîne parentId → parent en partant du dossier courant,
|
||||
* sans s'inclure soi-même. Ordre : racine → parent direct.
|
||||
* Garde-fou sur la longueur au cas où une boucle existerait en BDD
|
||||
* (ne devrait pas, mais ceinture+bretelles).
|
||||
*/
|
||||
private buildAncestors(current: LoreNode, allNodes: LoreNode[]): LoreNode[] {
|
||||
const byId = new Map(allNodes.map(n => [n.id!, n]));
|
||||
const chain: LoreNode[] = [];
|
||||
const seen = new Set<string>();
|
||||
let parentId = current.parentId ?? null;
|
||||
while (parentId && !seen.has(parentId) && chain.length < 32) {
|
||||
const parent = byId.get(parentId);
|
||||
if (!parent) break;
|
||||
chain.push(parent);
|
||||
seen.add(parent.id!);
|
||||
parentId = parent.parentId ?? null;
|
||||
}
|
||||
return chain.reverse();
|
||||
}
|
||||
|
||||
/** Icône du dossier courant, résolue depuis la clé lucide stockée sur le node. */
|
||||
get folderIcon(): LucideIconData {
|
||||
return resolveIcon(this.node?.icon ?? null);
|
||||
}
|
||||
|
||||
navigateToSubfolder(id: string): void {
|
||||
this.router.navigate(['/lore', this.loreId, 'folders', id]);
|
||||
}
|
||||
|
||||
navigateToLoreRoot(): void {
|
||||
this.router.navigate(['/lore', this.loreId]);
|
||||
}
|
||||
|
||||
navigateToPage(id: string): void {
|
||||
this.router.navigate(['/lore', this.loreId, 'pages', id]);
|
||||
}
|
||||
|
||||
navigateToCreateSubfolder(): void {
|
||||
this.router.navigate(['/lore', this.loreId, 'folders', this.folderId, 'create']);
|
||||
}
|
||||
|
||||
navigateToCreatePage(): void {
|
||||
this.router.navigate(['/lore', this.loreId, 'nodes', this.folderId, 'pages', 'create']);
|
||||
}
|
||||
|
||||
navigateToEdit(): void {
|
||||
this.router.navigate(['/lore', this.loreId, 'folders', this.folderId, 'edit']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppression en cascade avec dialogue d'impact. On délègue au backend (transaction
|
||||
* atomique), et au retour on remonte soit au dossier parent soit au Lore racine.
|
||||
*/
|
||||
delete(): void {
|
||||
if (!this.node) return;
|
||||
const node = this.node;
|
||||
this.loreService.getLoreNodeDeletionImpact(this.folderId).subscribe({
|
||||
next: impact => {
|
||||
const parts: string[] = [];
|
||||
if (impact.folders > 0) parts.push(`${impact.folders} sous-dossier${impact.folders > 1 ? 's' : ''}`);
|
||||
if (impact.pages > 0) parts.push(`${impact.pages} page${impact.pages > 1 ? 's' : ''}`);
|
||||
|
||||
const lines = [`Supprimer le dossier "${node.name}" ?`];
|
||||
if (parts.length) {
|
||||
lines.push('');
|
||||
lines.push(`Cette action supprimera aussi : ${parts.join(', ')}.`);
|
||||
}
|
||||
lines.push('');
|
||||
lines.push('Cette action est irréversible.');
|
||||
|
||||
if (!confirm(lines.join('\n'))) return;
|
||||
this.loreService.deleteLoreNode(this.folderId).subscribe({
|
||||
next: () => {
|
||||
// Remonte au dossier parent si présent, sinon au Lore.
|
||||
if (node.parentId) {
|
||||
this.router.navigate(['/lore', this.loreId, 'folders', node.parentId]);
|
||||
} else {
|
||||
this.router.navigate(['/lore', this.loreId]);
|
||||
}
|
||||
},
|
||||
error: () => console.error('Erreur lors de la suppression du dossier')
|
||||
});
|
||||
},
|
||||
error: () => console.error('Impossible de récupérer les dépendances du dossier')
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.layoutService.hide();
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,19 @@
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
// Sticky : les actions Modifier/Supprimer du Lore restent accessibles
|
||||
// quand on scrolle la grille de dossiers.
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background: #0a0a14;
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid #1f2937;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
|
||||
.header-texts { flex: 1; min-width: 0; }
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ export class LoreDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
navigateToFolder(nodeId: string): void {
|
||||
this.router.navigate(['/lore', this.lore!.id, 'folders', nodeId, 'edit']);
|
||||
this.router.navigate(['/lore', this.lore!.id, 'folders', nodeId]);
|
||||
}
|
||||
|
||||
// ─────────────── Édition / suppression du Lore ───────────────
|
||||
|
||||
@@ -9,13 +9,6 @@
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button type="button" class="btn-secondary" (click)="cancel()">Annuler</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-danger"
|
||||
title="Supprimer le dossier et tout son contenu"
|
||||
(click)="delete()">
|
||||
Supprimer
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn-primary"
|
||||
|
||||
@@ -129,45 +129,15 @@ export class LoreNodeEditComponent implements OnInit, OnDestroy {
|
||||
parentId: raw.parentId && raw.parentId !== '' ? raw.parentId : null
|
||||
};
|
||||
this.loreService.updateLoreNode(this.folderId, updated).subscribe({
|
||||
next: () => this.router.navigate(['/lore', this.loreId]),
|
||||
next: () => this.router.navigate(['/lore', this.loreId, 'folders', this.folderId]),
|
||||
error: () => console.error('Erreur lors de la sauvegarde du dossier')
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppression en cascade : on va chercher le compte exact de sous-dossiers et
|
||||
* de pages (qui tombent avec le dossier), on l'annonce dans la confirmation,
|
||||
* puis on délègue au backend — l'atomicité est garantie côté transaction.
|
||||
*/
|
||||
delete(): void {
|
||||
if (!this.node) return;
|
||||
const node = this.node;
|
||||
this.loreService.getLoreNodeDeletionImpact(this.folderId).subscribe({
|
||||
next: impact => {
|
||||
const parts: string[] = [];
|
||||
if (impact.folders > 0) parts.push(`${impact.folders} sous-dossier${impact.folders > 1 ? 's' : ''}`);
|
||||
if (impact.pages > 0) parts.push(`${impact.pages} page${impact.pages > 1 ? 's' : ''}`);
|
||||
|
||||
const lines = [`Supprimer le dossier "${node.name}" ?`];
|
||||
if (parts.length) {
|
||||
lines.push('');
|
||||
lines.push(`Cette action supprimera aussi : ${parts.join(', ')}.`);
|
||||
}
|
||||
lines.push('');
|
||||
lines.push('Cette action est irréversible.');
|
||||
|
||||
if (!confirm(lines.join('\n'))) return;
|
||||
this.loreService.deleteLoreNode(this.folderId).subscribe({
|
||||
next: () => this.router.navigate(['/lore', this.loreId]),
|
||||
error: () => console.error('Erreur lors de la suppression du dossier')
|
||||
});
|
||||
},
|
||||
error: () => console.error('Impossible de récupérer les dépendances du dossier')
|
||||
});
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.router.navigate(['/lore', this.loreId]);
|
||||
// Retour vers la vue détail du dossier plutôt que la racine du Lore :
|
||||
// l'édition est un sous-écran du détail.
|
||||
this.router.navigate(['/lore', this.loreId, 'folders', this.folderId]);
|
||||
}
|
||||
|
||||
/** Retourne l'icône lucide à afficher dans l'aperçu du bouton "Sauvegarder". */
|
||||
|
||||
@@ -85,7 +85,7 @@ export function buildLoreSidebarConfig(data: LoreSidebarData): SecondarySidebarC
|
||||
id: `folder-${node.id}`,
|
||||
label: node.name,
|
||||
iconKey: node.icon ?? undefined,
|
||||
route: `/lore/${lore.id}/folders/${node.id}/edit`,
|
||||
route: `/lore/${lore.id}/folders/${node.id}`,
|
||||
meta: nodePages.length > 0 ? String(nodePages.length) : undefined,
|
||||
children,
|
||||
createActions: [
|
||||
|
||||
@@ -147,7 +147,7 @@ export class PageEditComponent implements OnInit, OnDestroy {
|
||||
for (const node of folderChain) {
|
||||
items.push({
|
||||
label: node.name,
|
||||
route: ['/lore', this.loreId, 'folders', node.id, 'edit']
|
||||
route: ['/lore', this.loreId, 'folders', node.id]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
<lucide-icon [img]="Pencil" [size]="14"></lucide-icon>
|
||||
Modifier
|
||||
</button>
|
||||
<button type="button" class="btn-danger" (click)="deletePage()" title="Supprimer la page">
|
||||
<lucide-icon [img]="Trash2" [size]="14"></lucide-icon>
|
||||
Supprimer
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { forkJoin } from 'rxjs';
|
||||
import { LucideAngularModule, Pencil } from 'lucide-angular';
|
||||
import { LucideAngularModule, Pencil, Trash2 } from 'lucide-angular';
|
||||
import { LoreService } from '../../services/lore.service';
|
||||
import { TemplateService } from '../../services/template.service';
|
||||
import { PageService } from '../../services/page.service';
|
||||
@@ -34,6 +34,7 @@ import { ImageGalleryComponent } from '../../shared/image-gallery/image-gallery.
|
||||
})
|
||||
export class PageViewComponent implements OnInit, OnDestroy {
|
||||
readonly Pencil = Pencil;
|
||||
readonly Trash2 = Trash2;
|
||||
|
||||
loreId = '';
|
||||
pageId = '';
|
||||
@@ -96,7 +97,7 @@ export class PageViewComponent implements OnInit, OnDestroy {
|
||||
: undefined;
|
||||
}
|
||||
for (const node of folderChain) {
|
||||
items.push({ label: node.name, route: ['/lore', this.loreId, 'folders', node.id, 'edit'] });
|
||||
items.push({ label: node.name, route: ['/lore', this.loreId, 'folders', node.id] });
|
||||
}
|
||||
items.push({ label: this.page.title });
|
||||
return items;
|
||||
@@ -121,6 +122,26 @@ export class PageViewComponent implements OnInit, OnDestroy {
|
||||
this.router.navigate(['/lore', this.loreId, 'pages', this.pageId, 'edit']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppression simple : pas d'enfants. On remonte au dossier parent
|
||||
* si on peut, sinon à la racine du Lore.
|
||||
*/
|
||||
deletePage(): void {
|
||||
if (!this.page) return;
|
||||
const page = this.page;
|
||||
if (!confirm(`Supprimer la page "${page.title}" ?\n\nCette action est irréversible.`)) return;
|
||||
this.pageService.delete(page.id!).subscribe({
|
||||
next: () => {
|
||||
if (page.nodeId) {
|
||||
this.router.navigate(['/lore', this.loreId, 'folders', page.nodeId]);
|
||||
} else {
|
||||
this.router.navigate(['/lore', this.loreId]);
|
||||
}
|
||||
},
|
||||
error: () => console.error('Erreur lors de la suppression de la page')
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.layoutService.hide();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user