Files
LoreMind/web/src/app/campaigns/arc/arc-edit/arc-edit.component.html
IETM_FIXE\ietm6 b2c3800bf8
All checks were successful
Build & Push Images / build (brain) (push) Successful in 1m38s
Build & Push Images / build (web) (push) Successful in 1m53s
Build & Push Images / build (core) (push) Successful in 3m40s
Build & Push Images / build-switcher (push) Successful in 16s
Co-MJ v1 : quêtes de première classe, guidage de préparation, assistant IA,
mode séance, battlemaps multiples et export Foundry ciblé

- Quêtes (Niveau 1) : entité Quest orthogonale à l'arbre, rattachée à un arc HUB
  ou libre (migrations V9-V12, V18, V20 ; réconciliation des jumeaux V10).
  Fusion quête/conteneur dans la sidebar, progression par partie (statuts
  disponible/en cours/terminée), et quêtes libres avec espace de scènes créé
  automatiquement (arc technique « Quêtes libres », V21 : levée de la
  contrainte arcs.type héritée du baseline).
- Guidage (Pilier B) : bilan de préparation 100 % dérivé (règles arc/chapitre/
  scène/quête), panneau « Prochaines étapes » avec boutons « Corriger », et
  pastilles détaillées (tooltip des manques) dans l'arbre.
- Assistant IA (Pilier A) : étoffage champ par champ (scène, chapitre, arc) et
  brouillons de scènes en propose→applique (brain : narrative-fields,
  scene-drafts).
- Horloges & menaces (V15-V17) : clocks à segments avec déclencheurs, fronts.
- Mode séance : préparation de séance (readiness + quêtes dispo), scène
  épinglée (V19), récap « précédemment » (brain : session-recap), onglet
  « Partie » du panneau de référence, graphe amélioré (pan/zoom, éditeur de
  liens).
- Perf : endpoint agrégé GET /api/campaigns/{id}/tree — la sidebar charge en
  1 requête au lieu de ~15.
- Battlemaps multiples par scène (variantes jour/nuit, étages…) : liste JSON
  étiquetée (V22, reprise automatique de la carte existante), rendu PDF avec
  légendes, export/import rétro-compatible.
- Export Foundry ciblé : modale de périmètre (cartes+ennemis / journaux /
  tables), bundle filtré côté serveur (zip allégé), module Foundry à jour
  (respect du périmètre + une Scene Foundry par variante de carte,
  rétro-compatible anciens bundles).
2026-07-03 15:29:23 +02:00

188 lines
7.1 KiB
HTML

<div class="edit-page">
<div class="page-header">
<div>
<h1>{{ arc?.name || ('arcEdit.fallbackTitle' | translate) }}</h1>
<p class="subtitle">{{ 'arcEdit.subtitle' | translate }}</p>
</div>
<div class="header-actions">
<button type="button" class="btn-ai"
(click)="toggleChat()"
[class.active]="chatOpen"
[title]="'arcEdit.aiAssistantTitle' | translate">
<lucide-icon [img]="Sparkles" [size]="14"></lucide-icon>
{{ 'arcEdit.aiAssistant' | translate }}
</button>
<button type="button" class="btn-secondary" (click)="cancel()">{{ 'common.cancel' | translate }}</button>
<button type="button" class="btn-danger" (click)="delete()">
<lucide-icon [img]="Trash2" [size]="14"></lucide-icon>
{{ 'common.delete' | translate }}
</button>
<button type="button" class="btn-primary" (click)="submit()" [disabled]="form.invalid">
{{ 'common.save' | translate }}
</button>
</div>
</div>
<form [formGroup]="form" (ngSubmit)="submit()" class="edit-form">
<!-- Tout est optionnel sauf le titre : identité visible, le reste replié. -->
<p class="optional-hint">{{ 'arcEdit.optionalHint' | translate }}</p>
<!-- Co-création (Pilier A) : le co-MJ propose, validation champ par champ → remplit le formulaire. -->
<app-entity-assist-panel
[entityType]="'arc'" [entityId]="arcId" [campaignId]="campaignId"
(applied)="onAssistApplied($event)"></app-entity-assist-panel>
<!-- Identité -->
<div class="field">
<label for="arc-edit-name">{{ 'arcEdit.nameLabel' | translate }}</label>
<input
id="arc-edit-name"
type="text"
formControlName="name"
[placeholder]="'arcEdit.namePlaceholder' | translate"
[class.invalid]="form.get('name')?.invalid && form.get('name')?.touched"
/>
</div>
<div class="field">
<label for="arc-edit-description">{{ 'arcEdit.synopsisLabel' | translate }}</label>
<textarea
id="arc-edit-description"
formControlName="description"
[placeholder]="'arcEdit.synopsisPlaceholder' | translate"
rows="5">
</textarea>
</div>
<div class="field">
<label>{{ 'arcEdit.structureLabel' | translate }}</label>
<div class="arc-type-choice">
<label class="arc-type-option" [class.selected]="form.get('type')?.value === 'LINEAR'">
<input type="radio" formControlName="type" value="LINEAR" />
<span class="arc-type-title">{{ 'arcEdit.linearTitle' | translate }}</span>
<span class="arc-type-desc">{{ 'arcEdit.linearDesc' | translate }}</span>
</label>
<label class="arc-type-option" [class.selected]="form.get('type')?.value === 'HUB'">
<input type="radio" formControlName="type" value="HUB" />
<span class="arc-type-title">{{ 'arcEdit.hubTitle' | translate }}</span>
<span class="arc-type-desc">{{ 'arcEdit.hubDesc' | translate }}</span>
</label>
</div>
</div>
<div class="field">
<label>{{ 'arcEdit.iconLabel' | translate }}</label>
<app-icon-picker [options]="campaignIconOptions" [(selected)]="selectedIcon"></app-icon-picker>
</div>
<!-- Illustrations (galerie editable, rendu editorial) -->
<app-expandable-section [title]="'arcEdit.illustrationsLabel' | translate" icon="🖼️" [filled]="illustrationsFilled">
<app-image-gallery
[imageIds]="illustrationImageIds"
[editable]="true"
[layout]="'EDITORIAL'"
(imageIdsChange)="illustrationImageIds = $event">
</app-image-gallery>
<small class="field-hint">{{ 'arcEdit.illustrationsHint' | translate }}</small>
</app-expandable-section>
<!-- Thèmes et enjeux -->
<app-expandable-section [title]="'arcEdit.themesStakesSectionTitle' | translate" icon="🎭" [filled]="themesStakesFilled">
<div class="field-row">
<div class="field">
<label for="arc-edit-themes">{{ 'arcEdit.themesLabel' | translate }}</label>
<textarea
id="arc-edit-themes"
formControlName="themes"
[placeholder]="'arcEdit.themesPlaceholder' | translate"
rows="4">
</textarea>
</div>
<div class="field">
<label for="arc-edit-stakes">{{ 'arcEdit.stakesLabel' | translate }}</label>
<textarea
id="arc-edit-stakes"
formControlName="stakes"
[placeholder]="'arcEdit.stakesPlaceholder' | translate"
rows="4">
</textarea>
</div>
</div>
</app-expandable-section>
<!-- Notes du MJ (privé) -->
<app-expandable-section [title]="'arcEdit.gmNotesSectionTitle' | translate" icon="🔒" variant="private" [filled]="gmNotesFilled">
<div class="field">
<label for="arc-edit-gm-notes">{{ 'arcEdit.gmNotesLabel' | translate }}</label>
<textarea
id="arc-edit-gm-notes"
formControlName="gmNotes"
[placeholder]="'arcEdit.gmNotesPlaceholder' | translate"
rows="5">
</textarea>
<small class="field-hint">{{ 'arcEdit.gmNotesHint' | translate }}</small>
</div>
</app-expandable-section>
<!-- Récompenses et dénouement -->
<app-expandable-section [title]="'arcEdit.rewardsResolutionSectionTitle' | translate" icon="🎁" [filled]="rewardsResolutionFilled">
<div class="field">
<label for="arc-edit-rewards">{{ 'arcEdit.rewardsLabel' | translate }}</label>
<textarea
id="arc-edit-rewards"
formControlName="rewards"
[placeholder]="'arcEdit.rewardsPlaceholder' | translate"
rows="4">
</textarea>
</div>
<div class="field">
<label for="arc-edit-resolution">{{ 'arcEdit.resolutionLabel' | translate }}</label>
<textarea
id="arc-edit-resolution"
formControlName="resolution"
[placeholder]="'arcEdit.resolutionPlaceholder' | translate"
rows="4">
</textarea>
</div>
</app-expandable-section>
<!-- ===== Pages Lore associées (phase B2 cross-context) ===== -->
@if (loreId) {
<app-expandable-section [title]="'arcEdit.relatedPagesLabel' | translate" icon="🔗" [filled]="loreFilled">
<app-lore-link-picker
[value]="relatedPageIds"
[availablePages]="availablePages"
[loreId]="loreId"
(valueChange)="relatedPageIds = $event">
</app-lore-link-picker>
<small class="field-hint">
{{ 'arcEdit.relatedPagesHint' | translate }}
</small>
</app-expandable-section>
}
@if (!loreId) {
<div class="field lore-hint">
<small class="field-hint">
{{ 'arcEdit.noLoreHint' | translate }}
</small>
</div>
}
</form>
</div>
<!-- Drawer chat IA (hors .edit-page pour couvrir le viewport à droite) -->
<app-ai-chat-drawer
[campaignId]="campaignId"
entityType="arc"
[entityId]="arcId"
[isOpen]="chatOpen"
[welcomeMessage]="'arcEdit.chatWelcome' | translate"
[quickSuggestions]="chatQuickSuggestions"
(close)="chatOpen = false">
</app-ai-chat-drawer>