diff --git a/back/src/modules/apis/apis.service.ts b/back/src/modules/apis/apis.service.ts index 7cfb4d1..c6d1a13 100644 --- a/back/src/modules/apis/apis.service.ts +++ b/back/src/modules/apis/apis.service.ts @@ -41,7 +41,7 @@ export class ApisService { description: dto.description ?? null, type: dto.type, yamlContent: dto.yamlContent, - status: 'PENDING', + status: dto.yamlContent ? 'PENDING' : 'NO_YAML', categoryId: dto.categoryId ?? null, isCurrent, contactFunctionalName: dto.contactFunctionalName, diff --git a/back/src/modules/uploads/uploads.controller.ts b/back/src/modules/uploads/uploads.controller.ts index 1d80489..fdbf5b0 100644 --- a/back/src/modules/uploads/uploads.controller.ts +++ b/back/src/modules/uploads/uploads.controller.ts @@ -205,7 +205,6 @@ export class UploadsController { contactTechnicalEmail: string; }, ) { - if (!files || files.length === 0) throw new BadRequestException('YAML file is required'); if (!body.name) throw new BadRequestException('name is required'); if (!body.convention) throw new BadRequestException('convention is required'); @@ -218,7 +217,10 @@ export class UploadsController { let yamlContent: string; let detectedType: ApiType | null = null; - if (files.length === 1) { + if (!files || files.length === 0) { + /* Pas de fichier : entrée créée sans YAML (YAML pourra être ajouté plus tard) */ + yamlContent = ''; + } else if (files.length === 1) { /* Cas simple : un seul fichier */ yamlContent = fs.readFileSync(files[0].path, 'utf-8'); @@ -260,8 +262,10 @@ export class UploadsController { contactTechnicalEmail: body.contactTechnicalEmail, }); - /* Fire-and-forget : génération asynchrone */ - this.docsService.generate(entry.id).catch(console.error); + /* Fire-and-forget : génération asynchrone (seulement si YAML fourni) */ + if (yamlContent) { + this.docsService.generate(entry.id).catch(console.error); + } return entry; } diff --git a/front-public/src/app/pages/api-detail/api-detail.component.ts b/front-public/src/app/pages/api-detail/api-detail.component.ts index d2a18a2..563d3e6 100644 --- a/front-public/src/app/pages/api-detail/api-detail.component.ts +++ b/front-public/src/app/pages/api-detail/api-detail.component.ts @@ -596,8 +596,17 @@ export class ApiDetailComponent implements OnInit, OnDestroy { onDelete() { if (!confirm('Supprimer cette API ?')) return; this.deleting.set(true); + /* Calculer la cible de redirection avant la suppression */ + const otherVersions = this.versions().filter((v) => v.id !== this.id); + const redirectTarget = otherVersions.find((v) => v.isCurrent) ?? otherVersions[0]; this.apiService.delete(this.id).subscribe({ - next: () => this.router.navigate(['/catalog']), + next: () => { + if (redirectTarget) { + this.router.navigate(['/catalog', redirectTarget.id]); + } else { + this.router.navigate(this.backUrl()); + } + }, error: () => this.deleting.set(false), }); } @@ -605,12 +614,14 @@ export class ApiDetailComponent implements OnInit, OnDestroy { badgeClass(status: string): string { if (status === 'GENERATED') return 'fr-badge fr-badge--success'; if (status === 'ERROR') return 'fr-badge fr-badge--error'; + if (status === 'NO_YAML') return 'fr-badge fr-badge--warning'; return 'fr-badge fr-badge--info'; } statusLabel(status: string): string { if (status === 'GENERATED') return 'Générée'; if (status === 'ERROR') return 'Erreur'; + if (status === 'NO_YAML') return 'Sans fichier'; return 'En attente'; } diff --git a/front-public/src/app/pages/catalog/catalog.component.ts b/front-public/src/app/pages/catalog/catalog.component.ts index 10d1792..5d64934 100644 --- a/front-public/src/app/pages/catalog/catalog.component.ts +++ b/front-public/src/app/pages/catalog/catalog.component.ts @@ -208,6 +208,7 @@ export class CatalogComponent implements OnInit { const base = 'fr-badge'; if (status === 'GENERATED') return `${base} fr-badge--success`; if (status === 'ERROR') return `${base} fr-badge--error`; + if (status === 'NO_YAML') return `${base} fr-badge--warning`; return `${base} fr-badge--info`; } diff --git a/front-public/src/app/pages/upload/upload.component.ts b/front-public/src/app/pages/upload/upload.component.ts index 9359e8e..8bf8eda 100644 --- a/front-public/src/app/pages/upload/upload.component.ts +++ b/front-public/src/app/pages/upload/upload.component.ts @@ -393,7 +393,9 @@ import { ApiEntry, Category, API_CONVENTION_LABELS, ApiConvention } from '@datac
Fichier{{ selectedFiles().length > 1 ? 's' : '' }}
- @if (selectedFiles().length === 1) { + @if (selectedFiles().length === 0) { + Aucun fichier + } @else if (selectedFiles().length === 1) { {{ selectedFiles()[0].name }} } @else { @for (f of selectedFiles(); track f.name) { @@ -568,7 +570,7 @@ export class UploadComponent implements OnInit { return !!(this.metaForm.get('versionPatch')?.invalid && this.metaForm.get('versionPatch')?.touched); } canProceedFromStep1 = computed(() => { - if (this.selectedFiles().length === 0) return false; + if (this.selectedFiles().length === 0) return true; // fichier optionnel if (this.validating()) return false; return this.validationDone() && this.validationErrors().length === 0; }); @@ -627,6 +629,7 @@ export class UploadComponent implements OnInit { onFileChange(event: Event) { this.fileError.set(null); + this.uploadError.set(null); this.detectedType.set(null); this.detectedMainFilename.set(null); this.validationErrors.set([]); @@ -725,7 +728,12 @@ export class UploadComponent implements OnInit { onSubmit() { const files = this.selectedFiles(); this.metaForm.markAllAsTouched(); - if (files.length === 0 || this.metaForm.invalid) return; + if (this.metaForm.invalid) { + /* Naviguer vers l'étape avec les erreurs pour les rendre visibles */ + const infoInvalid = this.INFO_FIELDS.some((f) => this.metaForm.get(f)?.invalid); + this.step.set(infoInvalid ? 2 : 3); + return; + } this.uploading.set(true); this.uploadError.set(null); diff --git a/shared/src/types/api-entry.types.ts b/shared/src/types/api-entry.types.ts index a9792f1..27ee89c 100644 --- a/shared/src/types/api-entry.types.ts +++ b/shared/src/types/api-entry.types.ts @@ -1,6 +1,6 @@ export type ApiType = 'ASYNCAPI' | 'OPENAPI'; -export type ApiStatus = 'PENDING' | 'GENERATED' | 'ERROR'; +export type ApiStatus = 'PENDING' | 'GENERATED' | 'ERROR' | 'NO_YAML'; export type ApiConvention = 'CONSULTER' | 'ENREGISTRER' | 'ETRE_NOTIFIE';