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)
137 lines
4.5 KiB
TypeScript
137 lines
4.5 KiB
TypeScript
import { Component, EventEmitter, OnInit, Output, DestroyRef } from '@angular/core';
|
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
|
|
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { LucideAngularModule, BookCopy, X, Plus, Check } from 'lucide-angular';
|
|
import { TranslatePipe } from '@ngx-translate/core';
|
|
import { LoreService } from '../../../services/lore.service';
|
|
import { Lore } from '../../../services/lore.model';
|
|
import { GameSystemService } from '../../../services/game-system.service';
|
|
import { GameSystem } from '../../../services/game-system.model';
|
|
|
|
/**
|
|
* Payload émis vers le parent à la création d'une campagne.
|
|
* `loreId` et `gameSystemId` sont optionnels (null = non associé).
|
|
*/
|
|
export interface CampaignCreatePayload {
|
|
name: string;
|
|
description: string;
|
|
playerCount: number;
|
|
loreId: string | null;
|
|
gameSystemId: string | null;
|
|
}
|
|
|
|
@Component({
|
|
selector: 'app-campaign-create',
|
|
imports: [ReactiveFormsModule, FormsModule, LucideAngularModule, TranslatePipe],
|
|
templateUrl: './campaign-create.component.html',
|
|
styleUrls: ['./campaign-create.component.scss']
|
|
})
|
|
export class CampaignCreateComponent implements OnInit {
|
|
@Output() closed = new EventEmitter<void>();
|
|
@Output() created = new EventEmitter<CampaignCreatePayload>();
|
|
|
|
readonly BookCopy = BookCopy;
|
|
readonly X = X;
|
|
readonly Plus = Plus;
|
|
readonly Check = Check;
|
|
|
|
/** Valeur sentinelle de l'option "Creer un systeme" dans le <select>. */
|
|
readonly CREATE_GAMESYSTEM_SENTINEL = '__create__';
|
|
|
|
form: FormGroup;
|
|
/** Lores disponibles pour association. Chargés à l'ouverture de la modal. */
|
|
availableLores: Lore[] = [];
|
|
/** GameSystems disponibles pour association. */
|
|
availableGameSystems: GameSystem[] = [];
|
|
|
|
/** Mode creation inline d'un GameSystem depuis le dropdown. */
|
|
creatingGameSystem = false;
|
|
newGameSystemName = '';
|
|
creatingGameSystemInFlight = false;
|
|
|
|
constructor(
|
|
private fb: FormBuilder,
|
|
private loreService: LoreService,
|
|
private gameSystemService: GameSystemService,
|
|
private destroyRef: DestroyRef
|
|
) {
|
|
this.form = this.fb.group({
|
|
name: ['', Validators.required],
|
|
description: [''],
|
|
playerCount: [4, [Validators.required, Validators.min(1)]],
|
|
loreId: [''],
|
|
gameSystemId: ['']
|
|
});
|
|
}
|
|
|
|
ngOnInit(): void {
|
|
this.loreService.getAllLores().subscribe({
|
|
next: (lores) => this.availableLores = lores,
|
|
error: () => this.availableLores = []
|
|
});
|
|
this.gameSystemService.getAll().subscribe({
|
|
next: (gs) => this.availableGameSystems = gs,
|
|
error: () => this.availableGameSystems = []
|
|
});
|
|
|
|
// Detecte la selection de l'option sentinelle "Creer un systeme" et bascule
|
|
// en mode creation inline. On reinitialise immediatement le control a ''
|
|
// pour que la sentinelle ne reste pas en valeur reelle du form.
|
|
this.form.get('gameSystemId')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
|
if (value === this.CREATE_GAMESYSTEM_SENTINEL) {
|
|
this.form.get('gameSystemId')?.setValue('', { emitEvent: false });
|
|
this.startCreateGameSystem();
|
|
}
|
|
});
|
|
}
|
|
|
|
startCreateGameSystem(): void {
|
|
this.creatingGameSystem = true;
|
|
this.newGameSystemName = '';
|
|
}
|
|
|
|
cancelCreateGameSystem(): void {
|
|
this.creatingGameSystem = false;
|
|
this.newGameSystemName = '';
|
|
}
|
|
|
|
submitCreateGameSystem(): void {
|
|
const name = this.newGameSystemName.trim();
|
|
if (!name || this.creatingGameSystemInFlight) return;
|
|
this.creatingGameSystemInFlight = true;
|
|
this.gameSystemService.create({ name, isPublic: false }).subscribe({
|
|
next: (created) => {
|
|
this.creatingGameSystemInFlight = false;
|
|
this.availableGameSystems = [...this.availableGameSystems, created];
|
|
if (created.id) {
|
|
this.form.get('gameSystemId')?.setValue(created.id);
|
|
}
|
|
this.creatingGameSystem = false;
|
|
this.newGameSystemName = '';
|
|
},
|
|
error: () => {
|
|
this.creatingGameSystemInFlight = false;
|
|
console.error('Erreur lors de la creation du systeme de jeu');
|
|
}
|
|
});
|
|
}
|
|
|
|
submit(): void {
|
|
if (this.form.invalid) return;
|
|
const raw = this.form.value;
|
|
this.created.emit({
|
|
name: raw.name,
|
|
description: raw.description,
|
|
playerCount: raw.playerCount,
|
|
loreId: raw.loreId ? raw.loreId : null,
|
|
gameSystemId: raw.gameSystemId ? raw.gameSystemId : null
|
|
});
|
|
}
|
|
|
|
onCancel(): void {
|
|
this.closed.emit();
|
|
}
|
|
}
|