Some checks failed
Qualité & Sécurité / MegaLinter (PMD · Ruff · Bandit · gitleaks · hadolint) (push) Successful in 1m9s
Build & Push Images / build (brain) (push) Successful in 1m10s
Qualité & Sécurité / Trivy (CVE dépendances Maven / pip / npm) (push) Failing after 2m36s
Build & Push Images / build (web) (push) Successful in 1m48s
Build & Push Images / build-switcher (push) Successful in 20s
Build & Push Images / build (core) (push) Successful in 3m9s
Qualité & Sécurité / Web (ESLint · angular-eslint + sonarjs) (push) Successful in 38s
CI (Gitea Actions) : - Nouveau workflow quality.yml (séparé de ci.yml, non bloquant pour la release) : MegaLinter v9 (PMD, Ruff, Bandit, gitleaks, hadolint), ng lint (web) et Trivy (CVE des dépendances Maven/pip/npm), sur main + beta + PR - Trivy : préchargement du cache Maven avant le scan — sans ça Maven Central rate-limite l'IP du runner (429) en résolvant les BOMs du parent Spring Boot - upload-artifact v4 → v3 : l'API artifacts v4 n'est pas supportée par Gitea (corrige aussi l'upload du rapport Playwright de e2e.yml, cassé depuis toujours) - Ruleset PMD projet (java-pmd-ruleset.xml, auto-détecté par MegaLinter) orienté bugs réels, sans le style Lombok-hostile du défaut : 11 337 → 65 findings ; PMD en mode rapport (JAVA_PMD_DISABLE_ERRORS) le temps de purger ce backlog Web : - angular-eslint 21 + eslint-plugin-sonarjs : les règles Sonar vivent désormais dans ESLint (config web/eslint.config.js, réglages justifiés en commentaire) - tsconfig : skipLibCheck + types:[] + node_modules ré-exclu — répare les builds desktop Windows/Linux cassés par le conflit @types/eslint-scope vs ESLint 9 - Purge du backlog lint : 114 erreurs → 0 sur 66 fichiers (ngOnDestroy vides supprimés, any typés avec les vrais DTOs, outputs (close) → (closed), regex super-linéaires désamorcées, ternaires/fonctions imbriqués dépliés, intention documentée sur les catch/error volontairement vides) - Bug latent corrigé au passage : license.service.disconnect() émettait undefined au lieu de true en cas de succès (masqué par un double cast)
118 lines
4.5 KiB
HTML
118 lines
4.5 KiB
HTML
<div class="page">
|
|
|
|
<header class="page-header">
|
|
<h1>{{ 'pageCreate.title' | translate }}</h1>
|
|
<p class="subtitle">{{ 'pageCreate.subtitle' | translate }}</p>
|
|
</header>
|
|
|
|
<form [formGroup]="form" (ngSubmit)="submit()" class="page-form">
|
|
|
|
<!-- Titre -->
|
|
<div class="field">
|
|
<label for="page-title">{{ 'pageCreate.pageTitleLabel' | translate }}</label>
|
|
<input id="page-title" type="text" formControlName="title" [placeholder]="'pageCreate.pageTitlePlaceholder' | translate" />
|
|
</div>
|
|
|
|
<!-- Template -->
|
|
<div class="field">
|
|
<label>{{ 'pageCreate.templateLabel' | translate }}</label>
|
|
|
|
@if (templates.length) {
|
|
<div class="templates-grid">
|
|
@for (t of templates; track t) {
|
|
<button
|
|
type="button"
|
|
class="template-card"
|
|
[class.selected]="selectedTemplateId === t.id"
|
|
(click)="selectTemplate(t)">
|
|
<div class="template-card-head">
|
|
<lucide-icon [img]="FileText" [size]="16"></lucide-icon>
|
|
<span class="template-name">{{ t.name }}</span>
|
|
</div>
|
|
<p class="template-description">{{ t.description || '—' }}</p>
|
|
</button>
|
|
}
|
|
<!-- Carte "+" : sauvegarde le brouillon et part creer un nouveau template ;
|
|
template-create renverra ici via le mecanisme returnTo. -->
|
|
<a
|
|
class="template-card template-card-create"
|
|
[routerLink]="['/lore', loreId, 'templates', 'create']"
|
|
[queryParams]="{ returnTo: 'page-create' }"
|
|
(click)="saveDraft()"
|
|
[title]="'pageCreate.createTemplateTitle' | translate">
|
|
<div class="template-card-head">
|
|
<lucide-icon [img]="Plus" [size]="16"></lucide-icon>
|
|
<span class="template-name">{{ 'pageCreate.createTemplate' | translate }}</span>
|
|
</div>
|
|
<p class="template-description">
|
|
{{ 'pageCreate.createTemplateHint' | translate }}
|
|
</p>
|
|
</a>
|
|
</div>
|
|
} @else {
|
|
<p class="empty-hint">
|
|
{{ 'pageCreate.noTemplates' | translate }}
|
|
<a [routerLink]="['/lore', loreId, 'templates', 'create']" [queryParams]="{ returnTo: 'page-create' }" (click)="saveDraft()">{{ 'pageCreate.createTemplate' | translate }}</a> {{ 'pageCreate.firstSuffix' | translate }}
|
|
</p>
|
|
}
|
|
|
|
</div>
|
|
|
|
<!-- Dossier de destination -->
|
|
<div class="field">
|
|
<label for="page-node">{{ 'pageCreate.nodeLabel' | translate }}</label>
|
|
|
|
@if (nodes.length) {
|
|
<select id="page-node" formControlName="nodeId">
|
|
<option value="" disabled>{{ 'pageCreate.nodePlaceholder' | translate }}</option>
|
|
@for (node of nodes; track node) {
|
|
<option [value]="node.id">{{ node.name }}</option>
|
|
}
|
|
</select>
|
|
<p class="hint">{{ 'pageCreate.nodeHint' | translate }}</p>
|
|
} @else {
|
|
<p class="empty-hint">
|
|
{{ 'pageCreate.noNodes' | translate }}
|
|
<a [routerLink]="['/lore', loreId, 'nodes', 'create']" [queryParams]="{ returnTo: 'page-create' }" (click)="saveDraft()">{{ 'pageCreate.createNode' | translate }}</a> {{ 'pageCreate.firstSuffix' | translate }}
|
|
</p>
|
|
}
|
|
|
|
</div>
|
|
|
|
<!-- Aide contextuelle -->
|
|
<div class="info-box" [innerHTML]="'pageCreate.infoBox' | translate"></div>
|
|
|
|
<!-- Erreur wizard (parsing <values> ou échec HTTP) -->
|
|
@if (wizardError) {
|
|
<div class="wizard-error" role="alert">{{ wizardError }}</div>
|
|
}
|
|
|
|
<!-- Actions -->
|
|
<div class="actions-row">
|
|
<button type="button" class="btn-secondary" (click)="cancel()">{{ 'common.cancel' | translate }}</button>
|
|
<button type="button" class="btn-ai" (click)="openWizard()" [disabled]="!canSubmit"
|
|
[title]="'pageCreate.aiTitle' | translate">
|
|
<lucide-icon [img]="Sparkles" [size]="14"></lucide-icon>
|
|
{{ 'pageCreate.createWithAi' | translate }}
|
|
</button>
|
|
<button type="submit" class="btn-primary" [disabled]="!canSubmit">{{ 'pageCreate.createPage' | translate }}</button>
|
|
</div>
|
|
|
|
</form>
|
|
|
|
</div>
|
|
|
|
<!-- Drawer chat IA en mode wizard -->
|
|
<app-ai-chat-drawer
|
|
[loreId]="loreId"
|
|
[isOpen]="chatOpen"
|
|
[persistent]="false"
|
|
[welcomeMessage]="wizardWelcome"
|
|
[systemPromptAddon]="wizardSystemPrompt"
|
|
[quickSuggestions]="wizardSuggestions"
|
|
[primaryAction]="wizardPrimaryAction"
|
|
(closed)="closeWizard()"
|
|
(assistantReply)="onWizardReply($event)"
|
|
(primaryActionClick)="applyWizardAndCreate()">
|
|
</app-ai-chat-drawer>
|