Migration Angular 20 -> 21 : fin de la migration securite (0 vulnerabilite npm)

- ng update @angular/core@21 @angular/cli@21 (corrige GHSA-jrmj-c5cx-3cw6, XSS SVG)
- Migration automatique des templates vers le control flow natif (@if/@for, 138 fichiers)
- Correction des 5 `track` invalides generes par la migration (trackBy a 1 argument
  -> track $index) + suppression des fonctions trackBy mortes
- TypeScript 5.9, zone.js 0.15 ; npm audit : 0 vulnerabilite

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 14:55:36 +02:00
parent 6acad41672
commit 23878f1c63
142 changed files with 7055 additions and 6292 deletions

View File

@@ -1,70 +1,78 @@
<div class="lore-detail" *ngIf="lore">
<!-- ============ Header : mode lecture ============ -->
<div class="detail-header" *ngIf="!editing">
<div class="header-texts">
<h1>{{ lore.name }}</h1>
<p class="description">{{ lore.description }}</p>
</div>
<div class="header-actions">
<button type="button" class="btn-secondary" (click)="startEdit()" title="Modifier le Lore">
<lucide-icon [img]="Pencil" [size]="14"></lucide-icon>
Modifier
</button>
<button type="button" class="btn-danger" (click)="deleteLore()" title="Supprimer le Lore">
<lucide-icon [img]="Trash2" [size]="14"></lucide-icon>
Supprimer
</button>
</div>
</div>
<!-- ============ Header : mode édition inline ============ -->
<div class="detail-header edit-mode" *ngIf="editing">
<div class="field">
<label for="lore-detail-edit-name">Nom</label>
<input id="lore-detail-edit-name" type="text" [(ngModel)]="editName" name="editName" required />
</div>
<div class="field">
<label for="lore-detail-edit-description">Description</label>
<textarea id="lore-detail-edit-description" [(ngModel)]="editDescription" name="editDescription" rows="3"></textarea>
</div>
<div class="header-actions">
<button type="button" class="btn-primary" (click)="saveEdit()" [disabled]="!editName.trim()">
Sauvegarder
</button>
<button type="button" class="btn-secondary" (click)="cancelEdit()">
Annuler
</button>
</div>
</div>
<!-- ============ Grille des dossiers racine ============ -->
<section class="detail-section nodes-section" *ngIf="!editing">
<div class="section-header">
<h2>Dossiers</h2>
<button class="btn-add" (click)="navigateToCreateNode()">
<lucide-icon [img]="Plus" [size]="14"></lucide-icon>
Nouveau dossier
</button>
</div>
<!-- rootNodes : uniquement les dossiers racine (pas les sous-dossiers,
qui sont visibles dans l'arbre de la sidebar). -->
<div class="nodes-grid" *ngIf="rootNodes.length > 0">
<div class="node-card" *ngFor="let node of rootNodes" (click)="navigateToFolder(node.id!)">
<lucide-icon [img]="Folder" [size]="24" class="node-icon"></lucide-icon>
<span class="node-name">{{ node.name }}</span>
@if (lore) {
<div class="lore-detail">
<!-- ============ Header : mode lecture ============ -->
@if (!editing) {
<div class="detail-header">
<div class="header-texts">
<h1>{{ lore.name }}</h1>
<p class="description">{{ lore.description }}</p>
</div>
<div class="header-actions">
<button type="button" class="btn-secondary" (click)="startEdit()" title="Modifier le Lore">
<lucide-icon [img]="Pencil" [size]="14"></lucide-icon>
Modifier
</button>
<button type="button" class="btn-danger" (click)="deleteLore()" title="Supprimer le Lore">
<lucide-icon [img]="Trash2" [size]="14"></lucide-icon>
Supprimer
</button>
</div>
</div>
</div>
<div class="empty-state" *ngIf="rootNodes.length === 0">
<lucide-icon [img]="Folder" [size]="40" class="empty-icon"></lucide-icon>
<p>Aucun dossier pour le moment.</p>
<button class="btn-add-first" (click)="navigateToCreateNode()">
<lucide-icon [img]="Plus" [size]="14"></lucide-icon>
Créer votre premier dossier
</button>
</div>
</section>
</div>
}
<!-- ============ Header : mode édition inline ============ -->
@if (editing) {
<div class="detail-header edit-mode">
<div class="field">
<label for="lore-detail-edit-name">Nom</label>
<input id="lore-detail-edit-name" type="text" [(ngModel)]="editName" name="editName" required />
</div>
<div class="field">
<label for="lore-detail-edit-description">Description</label>
<textarea id="lore-detail-edit-description" [(ngModel)]="editDescription" name="editDescription" rows="3"></textarea>
</div>
<div class="header-actions">
<button type="button" class="btn-primary" (click)="saveEdit()" [disabled]="!editName.trim()">
Sauvegarder
</button>
<button type="button" class="btn-secondary" (click)="cancelEdit()">
Annuler
</button>
</div>
</div>
}
<!-- ============ Grille des dossiers racine ============ -->
@if (!editing) {
<section class="detail-section nodes-section">
<div class="section-header">
<h2>Dossiers</h2>
<button class="btn-add" (click)="navigateToCreateNode()">
<lucide-icon [img]="Plus" [size]="14"></lucide-icon>
Nouveau dossier
</button>
</div>
<!-- rootNodes : uniquement les dossiers racine (pas les sous-dossiers,
qui sont visibles dans l'arbre de la sidebar). -->
@if (rootNodes.length > 0) {
<div class="nodes-grid">
@for (node of rootNodes; track node) {
<div class="node-card" (click)="navigateToFolder(node.id!)">
<lucide-icon [img]="Folder" [size]="24" class="node-icon"></lucide-icon>
<span class="node-name">{{ node.name }}</span>
</div>
}
</div>
}
@if (rootNodes.length === 0) {
<div class="empty-state">
<lucide-icon [img]="Folder" [size]="40" class="empty-icon"></lucide-icon>
<p>Aucun dossier pour le moment.</p>
<button class="btn-add-first" (click)="navigateToCreateNode()">
<lucide-icon [img]="Plus" [size]="14"></lucide-icon>
Créer votre premier dossier
</button>
</div>
}
</section>
}
</div>
}

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { LucideAngularModule, Folder, Plus, Pencil, Trash2 } from 'lucide-angular';
@@ -14,7 +14,7 @@ import { ConfirmDialogService } from '../../shared/confirm-dialog/confirm-dialog
@Component({
selector: 'app-lore-detail',
imports: [CommonModule, FormsModule, LucideAngularModule],
imports: [FormsModule, LucideAngularModule],
templateUrl: './lore-detail.component.html',
styleUrls: ['./lore-detail.component.scss']
})