@for (col of field.labels; track $index) {
diff --git a/web/src/app/lore/page-edit/page-edit.component.ts b/web/src/app/lore/page-edit/page-edit.component.ts
index 371f631..415db58 100644
--- a/web/src/app/lore/page-edit/page-edit.component.ts
+++ b/web/src/app/lore/page-edit/page-edit.component.ts
@@ -240,6 +240,11 @@ export class PageEditComponent implements OnInit, OnDestroy {
this.tableValues[fieldName]?.splice(rowIndex, 1);
}
+ /** Lignes du tableau d'un champ — toujours un tableau (jamais undefined) pour le `@for`. */
+ tableRows(fieldName: string): Array> {
+ return this.tableValues[fieldName] ?? [];
+ }
+
// --- Chat IA conversationnel (Phase b5) --------------------------------
toggleChat(): void {
diff --git a/web/src/app/services/quest-progression.service.ts b/web/src/app/services/quest-progression.service.ts
deleted file mode 100644
index 35c2cb4..0000000
--- a/web/src/app/services/quest-progression.service.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
-import { Observable } from 'rxjs';
-import { ProgressionStatus } from './campaign.model';
-
-/**
- * Endpoints de progression des quêtes pour un Playthrough.
- * Modèle "absence = NOT_STARTED" — envoyer NOT_STARTED supprime la ligne côté backend.
- */
-@Injectable({ providedIn: 'root' })
-export class QuestProgressionService {
-
- constructor(private http: HttpClient) {}
-
- /** Map chapterId -> ProgressionStatus pour le Playthrough donné. */
- list(playthroughId: string): Observable> {
- return this.http.get>(
- `/api/playthroughs/${playthroughId}/quest-progressions`
- );
- }
-
- setStatus(playthroughId: string, chapterId: string, status: ProgressionStatus): Observable {
- return this.http.put(
- `/api/playthroughs/${playthroughId}/quest-progressions/${chapterId}`,
- { status }
- );
- }
-}
diff --git a/web/src/app/shared/dynamic-fields-form/dynamic-fields-form.component.html b/web/src/app/shared/dynamic-fields-form/dynamic-fields-form.component.html
index 47209d7..9632d37 100644
--- a/web/src/app/shared/dynamic-fields-form/dynamic-fields-form.component.html
+++ b/web/src/app/shared/dynamic-fields-form/dynamic-fields-form.component.html
@@ -1,4 +1,4 @@
-@if (fields?.length) {
+@if (fields.length) {
@for (f of fields; track trackByName($index, f)) {
diff --git a/web/src/app/shared/quest-status-badge/quest-status-badge.component.html b/web/src/app/shared/quest-status-badge/quest-status-badge.component.html
deleted file mode 100644
index 302d972..0000000
--- a/web/src/app/shared/quest-status-badge/quest-status-badge.component.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- @if (!compact) {
- {{ label }}
- }
-
diff --git a/web/src/app/shared/quest-status-badge/quest-status-badge.component.scss b/web/src/app/shared/quest-status-badge/quest-status-badge.component.scss
deleted file mode 100644
index b0fb777..0000000
--- a/web/src/app/shared/quest-status-badge/quest-status-badge.component.scss
+++ /dev/null
@@ -1,43 +0,0 @@
-.status-badge {
- display: inline-flex;
- align-items: center;
- gap: 0.35rem;
- padding: 0.2rem 0.55rem;
- border-radius: 999px;
- font-size: 0.78rem;
- font-weight: 500;
- line-height: 1;
- border: 1px solid transparent;
-}
-
-.status-label {
- white-space: nowrap;
-}
-
-// Verrouillée — gris discret
-.status-locked {
- background: rgba(128, 128, 128, 0.12);
- color: #6b6b6b;
- border-color: rgba(128, 128, 128, 0.25);
-}
-
-// Disponible — vert calme (prête à être lancée)
-.status-available {
- background: rgba(52, 168, 83, 0.12);
- color: #2f7a47;
- border-color: rgba(52, 168, 83, 0.3);
-}
-
-// En cours — bleu actif
-.status-in_progress {
- background: rgba(66, 133, 244, 0.14);
- color: #2c6cd6;
- border-color: rgba(66, 133, 244, 0.35);
-}
-
-// Terminée — violet doux (clos, pas neutre)
-.status-completed {
- background: rgba(120, 80, 200, 0.12);
- color: #6d4fb5;
- border-color: rgba(120, 80, 200, 0.3);
-}
diff --git a/web/src/app/shared/quest-status-badge/quest-status-badge.component.ts b/web/src/app/shared/quest-status-badge/quest-status-badge.component.ts
deleted file mode 100644
index 90ca34a..0000000
--- a/web/src/app/shared/quest-status-badge/quest-status-badge.component.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Component, Input } from '@angular/core';
-
-import { LucideAngularModule, Lock, Circle, Play, CheckCircle2, LucideIconData } from 'lucide-angular';
-import { TranslateService } from '@ngx-translate/core';
-import { QuestStatus } from '../../services/campaign.model';
-
-/**
- * Badge visuel pour un QuestStatus (vue Hub).
- * Composant standalone, sans dépendance métier.
- */
-@Component({
- selector: 'app-quest-status-badge',
- imports: [LucideAngularModule],
- templateUrl: './quest-status-badge.component.html',
- styleUrls: ['./quest-status-badge.component.scss']
-})
-export class QuestStatusBadgeComponent {
- @Input() status: QuestStatus | undefined | null = 'AVAILABLE';
-
- /** Variante visuelle compacte (sans label) — utile pour les listes denses. */
- @Input() compact = false;
-
- constructor(private translate: TranslateService) {}
-
- get icon(): LucideIconData {
- switch (this.status) {
- case 'LOCKED': return Lock;
- case 'IN_PROGRESS': return Play;
- case 'COMPLETED': return CheckCircle2;
- case 'AVAILABLE':
- default: return Circle;
- }
- }
-
- get label(): string {
- switch (this.status) {
- case 'LOCKED': return this.translate.instant('questStatusBadge.locked');
- case 'IN_PROGRESS': return this.translate.instant('questStatusBadge.inProgress');
- case 'COMPLETED': return this.translate.instant('questStatusBadge.completed');
- case 'AVAILABLE':
- default: return this.translate.instant('questStatusBadge.available');
- }
- }
-
- get cssClass(): string {
- return `status-badge status-${(this.status ?? 'AVAILABLE').toLowerCase()}`;
- }
-}
|