feat: refonte métadonnées API + UX dossiers et import
- Nouveau schéma api_entries : convention+name, version X.X.X, provider, contacts splitées (name/entity/email), suppression des anciens champs - Formulaire upload étape 2 : convention+nom, version 3 champs, fournisseur, contacts métier et technique obligatoires ; type détecté auto → select désactivé - Validation YAML : redocly.yaml (extends: spec), support noms avec espaces, extraction métadonnées (name, version, description) depuis info YAML - Dossiers : modale création avec champ Emplacement pré-initialisé depuis le contexte de navigation, options └ avec indentation par profondeur - Import depuis un dossier : pré-sélection de la catégorie via queryParam - Recherche étendue : name, provider, description, contacts - Résolution @datacat/shared via workspace:* + compilation CJS avant NestJS Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
@@ -6,12 +6,13 @@ import {
|
||||
OnInit,
|
||||
ChangeDetectionStrategy,
|
||||
} from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { Router, RouterLink, ActivatedRoute } from '@angular/router';
|
||||
import { ApiService } from '../../core/api.service';
|
||||
import { CategoryService } from '../../core/category.service';
|
||||
import { Category } from '@datacat/shared';
|
||||
import { Category, API_CONVENTION_LABELS, ApiConvention } from '@datacat/shared';
|
||||
|
||||
@Component({
|
||||
selector: 'app-upload',
|
||||
@@ -122,58 +123,52 @@ import { Category } from '@datacat/shared';
|
||||
<!-- Étape 2 : Métadonnées -->
|
||||
@if (step() === 2) {
|
||||
<form [formGroup]="metaForm" (ngSubmit)="goToStep3()" novalidate>
|
||||
<div class="fr-input-group fr-mb-3w" [class.fr-input-group--error]="titleInvalid()">
|
||||
<label class="fr-label" for="title-input">
|
||||
Titre <span style="color:var(--text-default-error)">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="title-input"
|
||||
class="fr-input"
|
||||
[class.fr-input--error]="titleInvalid()"
|
||||
formControlName="title"
|
||||
type="text"
|
||||
placeholder="Nom de l'API"
|
||||
/>
|
||||
@if (titleInvalid()) {
|
||||
<p class="fr-error-text">Le titre est obligatoire.</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="fr-input-group fr-mb-3w" [class.fr-input-group--error]="versionInvalid()">
|
||||
<label class="fr-label" for="version-input">
|
||||
Version <span style="color:var(--text-default-error)">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="version-input"
|
||||
class="fr-input"
|
||||
[class.fr-input--error]="versionInvalid()"
|
||||
formControlName="version"
|
||||
type="text"
|
||||
placeholder="1.0.0"
|
||||
/>
|
||||
@if (versionInvalid()) {
|
||||
<p class="fr-error-text">La version est obligatoire.</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="fr-mb-3w">
|
||||
<p class="fr-label fr-mb-1w">Type <span style="color:var(--text-default-error)">*</span></p>
|
||||
@if (detectedType()) {
|
||||
<span class="fr-tag">{{ detectedType() }}</span>
|
||||
<span class="fr-hint-text fr-mt-1w">Détecté automatiquement depuis le fichier YAML</span>
|
||||
} @else {
|
||||
<div class="fr-select-group">
|
||||
<select id="type-input" class="fr-select" formControlName="type">
|
||||
<option value="ASYNCAPI">AsyncAPI</option>
|
||||
<option value="OPENAPI">OpenAPI</option>
|
||||
<!-- Titre : convention + nom -->
|
||||
<p class="fr-label fr-mb-1w">Titre <span style="color:var(--text-default-error)">*</span></p>
|
||||
<div class="fr-grid-row fr-grid-row--gutters fr-mb-1w">
|
||||
<div class="fr-col-12 fr-col-md-4">
|
||||
<div class="fr-select-group" [class.fr-select-group--error]="conventionInvalid()">
|
||||
<label class="fr-label" for="convention-input">Convention</label>
|
||||
<select
|
||||
id="convention-input"
|
||||
class="fr-select"
|
||||
[class.fr-select--error]="conventionInvalid()"
|
||||
formControlName="convention"
|
||||
>
|
||||
<option value="CONSULTER">Consulter</option>
|
||||
<option value="ENREGISTRER">Enregistrer</option>
|
||||
<option value="ETRE_NOTIFIE">Être notifié</option>
|
||||
</select>
|
||||
@if (conventionInvalid()) {
|
||||
<p class="fr-error-text">La convention est obligatoire.</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="fr-col-12 fr-col-md-8">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="nameInvalid()">
|
||||
<label class="fr-label" for="name-input">Nom de l'API</label>
|
||||
<input
|
||||
id="name-input"
|
||||
class="fr-input"
|
||||
[class.fr-input--error]="nameInvalid()"
|
||||
formControlName="name"
|
||||
type="text"
|
||||
placeholder="Référentiel Adresses"
|
||||
/>
|
||||
@if (nameInvalid()) {
|
||||
<p class="fr-error-text">Le nom est obligatoire.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="fr-hint-text fr-mb-3w">Titre complet : <strong>{{ previewTitle() }}</strong></p>
|
||||
|
||||
<!-- Catégorie -->
|
||||
<div class="fr-select-group fr-mb-3w">
|
||||
<label class="fr-label" for="category-input">
|
||||
Catégorie </label>
|
||||
Catégorie
|
||||
</label>
|
||||
<select id="category-input" class="fr-select" formControlName="categoryId">
|
||||
<option value="">— Sans catégorie —</option>
|
||||
@for (cat of categories(); track cat.id) {
|
||||
@@ -182,9 +177,43 @@ import { Category } from '@datacat/shared';
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Fournisseur -->
|
||||
<div class="fr-input-group fr-mb-3w" [class.fr-input-group--error]="providerInvalid()">
|
||||
<label class="fr-label" for="provider-input">
|
||||
Fournisseur <span style="color:var(--text-default-error)">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="provider-input"
|
||||
class="fr-input"
|
||||
[class.fr-input--error]="providerInvalid()"
|
||||
formControlName="provider"
|
||||
type="text"
|
||||
placeholder="DINUM, Direction XYZ..."
|
||||
/>
|
||||
@if (providerInvalid()) {
|
||||
<p class="fr-error-text">Le fournisseur est obligatoire.</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Type -->
|
||||
<div class="fr-select-group fr-mb-3w">
|
||||
<label class="fr-label" for="type-input">
|
||||
Type <span style="color:var(--text-default-error)">*</span>
|
||||
</label>
|
||||
<select id="type-input" class="fr-select" formControlName="type">
|
||||
<option value="ASYNCAPI">AsyncAPI</option>
|
||||
<option value="OPENAPI">OpenAPI</option>
|
||||
</select>
|
||||
@if (detectedType()) {
|
||||
<p class="fr-hint-text">Détecté automatiquement depuis le fichier YAML</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="fr-input-group fr-mb-3w">
|
||||
<label class="fr-label" for="description-input">
|
||||
Description </label>
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
id="description-input"
|
||||
class="fr-input"
|
||||
@@ -194,76 +223,127 @@ import { Category } from '@datacat/shared';
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="fr-input-group fr-mb-3w">
|
||||
<label class="fr-label" for="functional-doc-input">
|
||||
Documentation fonctionnelle </label>
|
||||
<textarea
|
||||
id="functional-doc-input"
|
||||
class="fr-input"
|
||||
formControlName="functionalDoc"
|
||||
rows="4"
|
||||
placeholder="Cas d'usage, contexte métier..."
|
||||
></textarea>
|
||||
<!-- Version X.Y.Z -->
|
||||
<p class="fr-label fr-mb-1w">Version <span style="color:var(--text-default-error)">*</span></p>
|
||||
<div class="fr-grid-row fr-grid-row--gutters fr-mb-3w">
|
||||
<div class="fr-col-4">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="versionMajorInvalid()">
|
||||
<label class="fr-label" for="version-major-input">Majeure (X)</label>
|
||||
<input
|
||||
id="version-major-input"
|
||||
class="fr-input"
|
||||
[class.fr-input--error]="versionMajorInvalid()"
|
||||
formControlName="versionMajor"
|
||||
type="number"
|
||||
min="0"
|
||||
placeholder="1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fr-col-4">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="versionMinorInvalid()">
|
||||
<label class="fr-label" for="version-minor-input">Mineure (Y)</label>
|
||||
<input
|
||||
id="version-minor-input"
|
||||
class="fr-input"
|
||||
[class.fr-input--error]="versionMinorInvalid()"
|
||||
formControlName="versionMinor"
|
||||
type="number"
|
||||
min="0"
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fr-col-4">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="versionPatchInvalid()">
|
||||
<label class="fr-label" for="version-patch-input">Correctif (Z)</label>
|
||||
<input
|
||||
id="version-patch-input"
|
||||
class="fr-input"
|
||||
[class.fr-input--error]="versionPatchInvalid()"
|
||||
formControlName="versionPatch"
|
||||
type="number"
|
||||
min="0"
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fr-input-group fr-mb-3w">
|
||||
<label class="fr-label" for="technical-doc-input">
|
||||
Documentation technique </label>
|
||||
<textarea
|
||||
id="technical-doc-input"
|
||||
class="fr-input"
|
||||
formControlName="technicalDoc"
|
||||
rows="4"
|
||||
placeholder="Guide d'intégration technique..."
|
||||
></textarea>
|
||||
<!-- Contact métier -->
|
||||
<p class="fr-label fr-mb-1w">Contact métier <span style="color:var(--text-default-error)">*</span></p>
|
||||
<div class="fr-grid-row fr-grid-row--gutters fr-mb-3w">
|
||||
<div class="fr-col-12 fr-col-md-4">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="fieldInvalid('contactFunctionalName')">
|
||||
<label class="fr-label" for="cf-name-input">Nom et prénom</label>
|
||||
<input id="cf-name-input" class="fr-input"
|
||||
[class.fr-input--error]="fieldInvalid('contactFunctionalName')"
|
||||
formControlName="contactFunctionalName" type="text" placeholder="Prénom Nom" />
|
||||
@if (fieldInvalid('contactFunctionalName')) {
|
||||
<p class="fr-error-text">Obligatoire.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="fr-col-12 fr-col-md-4">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="fieldInvalid('contactFunctionalEntity')">
|
||||
<label class="fr-label" for="cf-entity-input">Entité</label>
|
||||
<input id="cf-entity-input" class="fr-input"
|
||||
[class.fr-input--error]="fieldInvalid('contactFunctionalEntity')"
|
||||
formControlName="contactFunctionalEntity" type="text" placeholder="Direction XYZ" />
|
||||
@if (fieldInvalid('contactFunctionalEntity')) {
|
||||
<p class="fr-error-text">Obligatoire.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="fr-col-12 fr-col-md-4">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="fieldInvalid('contactFunctionalEmail')">
|
||||
<label class="fr-label" for="cf-email-input">Email</label>
|
||||
<input id="cf-email-input" class="fr-input"
|
||||
[class.fr-input--error]="fieldInvalid('contactFunctionalEmail')"
|
||||
formControlName="contactFunctionalEmail" type="email" placeholder="prenom.nom@example.fr" />
|
||||
@if (fieldInvalid('contactFunctionalEmail')) {
|
||||
<p class="fr-error-text">Email valide requis.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fr-input-group fr-mb-3w">
|
||||
<label class="fr-label" for="external-doc-url-input">
|
||||
URL documentation externe </label>
|
||||
<input
|
||||
id="external-doc-url-input"
|
||||
class="fr-input"
|
||||
formControlName="externalDocUrl"
|
||||
type="text"
|
||||
placeholder="https://..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="fr-input-group fr-mb-3w">
|
||||
<label class="fr-label" for="contact-functional-input">
|
||||
Contact fonctionnel </label>
|
||||
<input
|
||||
id="contact-functional-input"
|
||||
class="fr-input"
|
||||
formControlName="contactFunctional"
|
||||
type="text"
|
||||
placeholder="Nom, email, téléphone..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="fr-input-group fr-mb-3w">
|
||||
<label class="fr-label" for="contact-technical-input">
|
||||
Contact technique </label>
|
||||
<input
|
||||
id="contact-technical-input"
|
||||
class="fr-input"
|
||||
formControlName="contactTechnical"
|
||||
type="text"
|
||||
placeholder="Nom, email, téléphone..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="fr-input-group fr-mb-4w">
|
||||
<label class="fr-label" for="access-rights-input">
|
||||
Droits d'accès </label>
|
||||
<textarea
|
||||
id="access-rights-input"
|
||||
class="fr-input"
|
||||
formControlName="accessRights"
|
||||
rows="3"
|
||||
placeholder="Processus d'habilitation, conditions d'accès..."
|
||||
></textarea>
|
||||
<!-- Contact technique -->
|
||||
<p class="fr-label fr-mb-1w">Contact technique <span style="color:var(--text-default-error)">*</span></p>
|
||||
<div class="fr-grid-row fr-grid-row--gutters fr-mb-4w">
|
||||
<div class="fr-col-12 fr-col-md-4">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="fieldInvalid('contactTechnicalName')">
|
||||
<label class="fr-label" for="ct-name-input">Nom et prénom</label>
|
||||
<input id="ct-name-input" class="fr-input"
|
||||
[class.fr-input--error]="fieldInvalid('contactTechnicalName')"
|
||||
formControlName="contactTechnicalName" type="text" placeholder="Prénom Nom" />
|
||||
@if (fieldInvalid('contactTechnicalName')) {
|
||||
<p class="fr-error-text">Obligatoire.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="fr-col-12 fr-col-md-4">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="fieldInvalid('contactTechnicalEntity')">
|
||||
<label class="fr-label" for="ct-entity-input">Entité</label>
|
||||
<input id="ct-entity-input" class="fr-input"
|
||||
[class.fr-input--error]="fieldInvalid('contactTechnicalEntity')"
|
||||
formControlName="contactTechnicalEntity" type="text" placeholder="Équipe Technique" />
|
||||
@if (fieldInvalid('contactTechnicalEntity')) {
|
||||
<p class="fr-error-text">Obligatoire.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="fr-col-12 fr-col-md-4">
|
||||
<div class="fr-input-group" [class.fr-input-group--error]="fieldInvalid('contactTechnicalEmail')">
|
||||
<label class="fr-label" for="ct-email-input">Email</label>
|
||||
<input id="ct-email-input" class="fr-input"
|
||||
[class.fr-input--error]="fieldInvalid('contactTechnicalEmail')"
|
||||
formControlName="contactTechnicalEmail" type="email" placeholder="tech@example.fr" />
|
||||
@if (fieldInvalid('contactTechnicalEmail')) {
|
||||
<p class="fr-error-text">Email valide requis.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fr-btns-group fr-btns-group--inline-sm">
|
||||
@@ -303,13 +383,17 @@ import { Category } from '@datacat/shared';
|
||||
<dt class="fr-text--bold">Type</dt>
|
||||
<dd>{{ metaForm.value.type }}</dd>
|
||||
</div>
|
||||
<div class="fr-col-12 fr-col-md-6">
|
||||
<div class="fr-col-12">
|
||||
<dt class="fr-text--bold">Titre</dt>
|
||||
<dd>{{ metaForm.value.title }}</dd>
|
||||
<dd>{{ previewTitle() }}</dd>
|
||||
</div>
|
||||
<div class="fr-col-12 fr-col-md-6">
|
||||
<dt class="fr-text--bold">Version</dt>
|
||||
<dd>{{ metaForm.value.version }}</dd>
|
||||
<dd>{{ metaForm.value.versionMajor }}.{{ metaForm.value.versionMinor }}.{{ metaForm.value.versionPatch }}</dd>
|
||||
</div>
|
||||
<div class="fr-col-12 fr-col-md-6">
|
||||
<dt class="fr-text--bold">Fournisseur</dt>
|
||||
<dd>{{ metaForm.value.provider }}</dd>
|
||||
</div>
|
||||
@if (metaForm.value.categoryId) {
|
||||
<div class="fr-col-12 fr-col-md-6">
|
||||
@@ -356,6 +440,7 @@ export class UploadComponent implements OnInit {
|
||||
private apiService = inject(ApiService);
|
||||
private categoryService = inject(CategoryService);
|
||||
private router = inject(Router);
|
||||
private route = inject(ActivatedRoute);
|
||||
|
||||
step = signal(1);
|
||||
selectedFiles = signal<File[]>([]);
|
||||
@@ -376,30 +461,57 @@ export class UploadComponent implements OnInit {
|
||||
];
|
||||
|
||||
metaForm = this.fb.group({
|
||||
title: ['', Validators.required],
|
||||
version: ['', Validators.required],
|
||||
convention: ['CONSULTER', Validators.required],
|
||||
name: ['', Validators.required],
|
||||
provider: ['', Validators.required],
|
||||
versionMajor: [0, [Validators.required, Validators.min(0)]],
|
||||
versionMinor: [0, [Validators.required, Validators.min(0)]],
|
||||
versionPatch: [0, [Validators.required, Validators.min(0)]],
|
||||
type: ['ASYNCAPI', Validators.required],
|
||||
categoryId: [''],
|
||||
description: [''],
|
||||
functionalDoc: [''],
|
||||
technicalDoc: [''],
|
||||
externalDocUrl: [''],
|
||||
contactFunctional: [''],
|
||||
contactTechnical: [''],
|
||||
accessRights: [''],
|
||||
contactFunctionalName: ['', Validators.required],
|
||||
contactFunctionalEntity: ['', Validators.required],
|
||||
contactFunctionalEmail: ['', [Validators.required, Validators.email]],
|
||||
contactTechnicalName: ['', Validators.required],
|
||||
contactTechnicalEntity: ['', Validators.required],
|
||||
contactTechnicalEmail: ['', [Validators.required, Validators.email]],
|
||||
});
|
||||
|
||||
titleInvalid = computed(
|
||||
() => !!(this.metaForm.get('title')?.invalid && this.metaForm.get('title')?.touched),
|
||||
);
|
||||
versionInvalid = computed(
|
||||
() => !!(this.metaForm.get('version')?.invalid && this.metaForm.get('version')?.touched),
|
||||
);
|
||||
/* Convertit valueChanges en signal pour que les computed() trackent les changements du formulaire */
|
||||
private readonly formValues = toSignal(this.metaForm.valueChanges, { initialValue: this.metaForm.value });
|
||||
|
||||
previewTitle = computed(() => {
|
||||
const values = this.formValues();
|
||||
const convention = (values.convention ?? 'CONSULTER') as ApiConvention;
|
||||
const name = values.name ?? '';
|
||||
return `${API_CONVENTION_LABELS[convention]} ${name}`.trim();
|
||||
});
|
||||
selectedCategoryName = computed(() => {
|
||||
const id = this.metaForm.value.categoryId;
|
||||
const id = this.formValues().categoryId;
|
||||
if (!id) return '';
|
||||
return this.categories().find((c) => c.id === id)?.name ?? '';
|
||||
});
|
||||
|
||||
/* Méthodes de validation (pas de computed — la re-exécution est déclenchée par les événements DOM blur/input) */
|
||||
conventionInvalid(): boolean {
|
||||
return !!(this.metaForm.get('convention')?.invalid && this.metaForm.get('convention')?.touched);
|
||||
}
|
||||
nameInvalid(): boolean {
|
||||
return !!(this.metaForm.get('name')?.invalid && this.metaForm.get('name')?.touched);
|
||||
}
|
||||
providerInvalid(): boolean {
|
||||
return !!(this.metaForm.get('provider')?.invalid && this.metaForm.get('provider')?.touched);
|
||||
}
|
||||
versionMajorInvalid(): boolean {
|
||||
return !!(this.metaForm.get('versionMajor')?.invalid && this.metaForm.get('versionMajor')?.touched);
|
||||
}
|
||||
versionMinorInvalid(): boolean {
|
||||
return !!(this.metaForm.get('versionMinor')?.invalid && this.metaForm.get('versionMinor')?.touched);
|
||||
}
|
||||
versionPatchInvalid(): boolean {
|
||||
return !!(this.metaForm.get('versionPatch')?.invalid && this.metaForm.get('versionPatch')?.touched);
|
||||
}
|
||||
canProceedFromStep1 = computed(() => {
|
||||
/* Sans fichier, on peut toujours passer à l'étape 2 */
|
||||
if (this.selectedFiles().length === 0) return true;
|
||||
@@ -408,9 +520,20 @@ export class UploadComponent implements OnInit {
|
||||
return this.validationDone() && this.validationErrors().length === 0;
|
||||
});
|
||||
|
||||
fieldInvalid(name: string): boolean {
|
||||
const ctrl = this.metaForm.get(name);
|
||||
return !!(ctrl?.invalid && ctrl?.touched);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const preselectedCategoryId = this.route.snapshot.queryParamMap.get('categoryId');
|
||||
this.categoryService.list().subscribe({
|
||||
next: (res) => this.categories.set(res.items),
|
||||
next: (res) => {
|
||||
this.categories.set(res.items);
|
||||
if (preselectedCategoryId) {
|
||||
this.metaForm.patchValue({ categoryId: preselectedCategoryId });
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -426,6 +549,7 @@ export class UploadComponent implements OnInit {
|
||||
this.detectedMainFilename.set(null);
|
||||
this.validationErrors.set([]);
|
||||
this.validationDone.set(false);
|
||||
this.metaForm.get('type')?.enable();
|
||||
|
||||
const input = event.target as HTMLInputElement;
|
||||
const fileList = input.files;
|
||||
@@ -471,18 +595,22 @@ export class UploadComponent implements OnInit {
|
||||
this.validationDone.set(true);
|
||||
if (!result.valid) {
|
||||
this.validationErrors.set(result.errors);
|
||||
this.metaForm.get('type')?.enable();
|
||||
} else {
|
||||
this.validationErrors.set([]);
|
||||
if (result.type) {
|
||||
this.detectedType.set(result.type);
|
||||
this.metaForm.patchValue({ type: result.type });
|
||||
this.metaForm.get('type')?.disable();
|
||||
}
|
||||
if (result.mainFile) {
|
||||
this.detectedMainFilename.set(result.mainFile);
|
||||
}
|
||||
/* Pré-remplissage du formulaire depuis les métadonnées du fichier */
|
||||
if (result.title) this.metaForm.patchValue({ title: result.title });
|
||||
if (result.version) this.metaForm.patchValue({ version: String(result.version) });
|
||||
if (result.name) this.metaForm.patchValue({ name: result.name });
|
||||
if (result.versionMajor !== null) this.metaForm.patchValue({ versionMajor: result.versionMajor });
|
||||
if (result.versionMinor !== null) this.metaForm.patchValue({ versionMinor: result.versionMinor });
|
||||
if (result.versionPatch !== null) this.metaForm.patchValue({ versionPatch: result.versionPatch });
|
||||
if (result.description) this.metaForm.patchValue({ description: result.description });
|
||||
}
|
||||
},
|
||||
@@ -509,37 +637,30 @@ export class UploadComponent implements OnInit {
|
||||
this.uploading.set(true);
|
||||
this.uploadError.set(null);
|
||||
|
||||
const raw = this.metaForm.getRawValue();
|
||||
const formData = new FormData();
|
||||
for (const f of files) {
|
||||
formData.append('files', f);
|
||||
}
|
||||
formData.append('title', this.metaForm.value.title ?? '');
|
||||
formData.append('version', this.metaForm.value.version ?? '');
|
||||
formData.append('type', this.metaForm.value.type ?? 'ASYNCAPI');
|
||||
if (this.metaForm.value.categoryId) {
|
||||
formData.append('categoryId', this.metaForm.value.categoryId);
|
||||
formData.append('convention', raw.convention ?? 'CONSULTER');
|
||||
formData.append('name', raw.name ?? '');
|
||||
formData.append('provider', raw.provider ?? '');
|
||||
formData.append('versionMajor', String(raw.versionMajor ?? 0));
|
||||
formData.append('versionMinor', String(raw.versionMinor ?? 0));
|
||||
formData.append('versionPatch', String(raw.versionPatch ?? 0));
|
||||
formData.append('type', raw.type ?? 'ASYNCAPI');
|
||||
if (raw.categoryId) {
|
||||
formData.append('categoryId', raw.categoryId);
|
||||
}
|
||||
if (this.metaForm.value.description) {
|
||||
formData.append('description', this.metaForm.value.description);
|
||||
}
|
||||
if (this.metaForm.value.functionalDoc) {
|
||||
formData.append('functionalDoc', this.metaForm.value.functionalDoc);
|
||||
}
|
||||
if (this.metaForm.value.technicalDoc) {
|
||||
formData.append('technicalDoc', this.metaForm.value.technicalDoc);
|
||||
}
|
||||
if (this.metaForm.value.externalDocUrl) {
|
||||
formData.append('externalDocUrl', this.metaForm.value.externalDocUrl);
|
||||
}
|
||||
if (this.metaForm.value.contactFunctional) {
|
||||
formData.append('contactFunctional', this.metaForm.value.contactFunctional);
|
||||
}
|
||||
if (this.metaForm.value.contactTechnical) {
|
||||
formData.append('contactTechnical', this.metaForm.value.contactTechnical);
|
||||
}
|
||||
if (this.metaForm.value.accessRights) {
|
||||
formData.append('accessRights', this.metaForm.value.accessRights);
|
||||
if (raw.description) {
|
||||
formData.append('description', raw.description);
|
||||
}
|
||||
formData.append('contactFunctionalName', raw.contactFunctionalName ?? '');
|
||||
formData.append('contactFunctionalEntity', raw.contactFunctionalEntity ?? '');
|
||||
formData.append('contactFunctionalEmail', raw.contactFunctionalEmail ?? '');
|
||||
formData.append('contactTechnicalName', raw.contactTechnicalName ?? '');
|
||||
formData.append('contactTechnicalEntity', raw.contactTechnicalEntity ?? '');
|
||||
formData.append('contactTechnicalEmail', raw.contactTechnicalEmail ?? '');
|
||||
|
||||
this.apiService.upload(formData).subscribe({
|
||||
next: (entry) => {
|
||||
|
||||
Reference in New Issue
Block a user