import { Component, signal, inject, OnInit, OnDestroy, ChangeDetectionStrategy, } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { ApiService } from '../../core/api.service'; import { CategoryService } from '../../core/category.service'; import { ApiEntry, Category, ApiConvention } from '@datacat/shared'; @Component({ selector: 'app-api-detail', standalone: true, imports: [CommonModule, RouterLink], changeDetection: ChangeDetectionStrategy.OnPush, template: `
@if (loading()) {

Chargement...

} @if (error()) {

Erreur

{{ error() }}

} @if (api(); as entry) {

{{ entry.title }}

{{ entry.type }} v{{ entry.version }} {{ entry.status }}
@if (entry.status === 'PENDING') {

Génération en cours

La documentation est en cours de génération. Cette page se rafraîchit automatiquement.

} @if (entry.status === 'ERROR') {

Erreur de génération

{{ entry.errorMessage }}

}

Informations

Version
{{ entry.version }}
Type
{{ entry.type }}
Fournisseur
{{ entry.provider }}
@if (entry.description) {
Description
{{ entry.description }}
}
Créé le
{{ entry.createdAt | date:'dd/MM/yyyy HH:mm' }}
Mis à jour le
{{ entry.updatedAt | date:'dd/MM/yyyy HH:mm' }}

Contacts

Contact métier

{{ entry.contactFunctionalName }}

{{ entry.contactFunctionalEntity }}

{{ entry.contactFunctionalEmail }}

Contact technique

{{ entry.contactTechnicalName }}

{{ entry.contactTechnicalEntity }}

{{ entry.contactTechnicalEmail }}

Voir la documentation Télécharger YAML
}
@if (showEditModal()) {

Modifier l'API

@if (editError()) {

{{ editError() }}

}

Titre *

@if (editNameError()) {

Le nom est obligatoire.

}

Titre : {{ editPreviewTitle() }}

@if (editProviderError()) {

Le fournisseur est obligatoire.

}

Version *

Contact métier *

Contact technique *

} `, }) export class ApiDetailComponent implements OnInit, OnDestroy { private route = inject(ActivatedRoute); private router = inject(Router); private apiService = inject(ApiService); private categoryService = inject(CategoryService); api = signal(null); categoryBreadcrumb = signal([]); loading = signal(false); error = signal(null); regenerating = signal(false); deleting = signal(false); /* Modale édition */ showEditModal = signal(false); editLoading = signal(false); editError = signal(null); editNameError = signal(false); editProviderError = signal(false); categories = signal([]); editConvention = signal('CONSULTER'); editName = signal(''); editProvider = signal(''); editVersionMajor = signal(0); editVersionMinor = signal(0); editVersionPatch = signal(0); editCategoryId = signal(null); editDescription = signal(''); editContactFunctionalName = signal(''); editContactFunctionalEntity = signal(''); editContactFunctionalEmail = signal(''); editContactTechnicalName = signal(''); editContactTechnicalEntity = signal(''); editContactTechnicalEmail = signal(''); editPreviewTitle() { const labels: Record = { CONSULTER: 'Consulter', ENREGISTRER: 'Enregistrer', ETRE_NOTIFIE: 'Être notifié' }; return `${labels[this.editConvention()]} ${this.editName()}`.trim(); } private pollInterval: ReturnType | null = null; private id = ''; ngOnInit() { this.id = this.route.snapshot.paramMap.get('id') ?? ''; this.loadApi(); } ngOnDestroy() { this.clearPoll(); } onRegenerate() { this.regenerating.set(true); this.apiService.regenerate(this.id).subscribe({ next: (entry) => { this.api.set(entry); this.regenerating.set(false); this.startPollIfPending(); }, error: () => this.regenerating.set(false), }); } openEdit() { const entry = this.api(); if (!entry) return; this.editConvention.set(entry.convention); this.editName.set(entry.name); this.editProvider.set(entry.provider); this.editVersionMajor.set(entry.versionMajor); this.editVersionMinor.set(entry.versionMinor); this.editVersionPatch.set(entry.versionPatch); this.editCategoryId.set(entry.categoryId ?? null); this.editDescription.set(entry.description ?? ''); this.editContactFunctionalName.set(entry.contactFunctionalName); this.editContactFunctionalEntity.set(entry.contactFunctionalEntity); this.editContactFunctionalEmail.set(entry.contactFunctionalEmail); this.editContactTechnicalName.set(entry.contactTechnicalName); this.editContactTechnicalEntity.set(entry.contactTechnicalEntity); this.editContactTechnicalEmail.set(entry.contactTechnicalEmail); this.editError.set(null); this.editNameError.set(false); this.editProviderError.set(false); this.categoryService.list().subscribe((res) => this.categories.set(res.items)); this.showEditModal.set(true); } closeEdit() { this.showEditModal.set(false); } submitEdit() { this.editNameError.set(!this.editName().trim()); this.editProviderError.set(!this.editProvider().trim()); if (!this.editName().trim() || !this.editProvider().trim()) return; this.editLoading.set(true); this.apiService.update(this.id, { convention: this.editConvention(), name: this.editName().trim(), provider: this.editProvider().trim(), versionMajor: this.editVersionMajor(), versionMinor: this.editVersionMinor(), versionPatch: this.editVersionPatch(), categoryId: this.editCategoryId() || null, description: this.editDescription().trim() || null, contactFunctionalName: this.editContactFunctionalName().trim(), contactFunctionalEntity: this.editContactFunctionalEntity().trim(), contactFunctionalEmail: this.editContactFunctionalEmail().trim(), contactTechnicalName: this.editContactTechnicalName().trim(), contactTechnicalEntity: this.editContactTechnicalEntity().trim(), contactTechnicalEmail: this.editContactTechnicalEmail().trim(), }).subscribe({ next: (entry) => { this.api.set(entry); this.editLoading.set(false); this.showEditModal.set(false); }, error: (err: { error?: { message?: string } }) => { this.editLoading.set(false); this.editError.set(err?.error?.message ?? 'Une erreur est survenue.'); }, }); } onDelete() { if (!confirm('Supprimer cette API ?')) return; this.deleting.set(true); this.apiService.delete(this.id).subscribe({ next: () => this.router.navigate(['/catalog']), error: () => this.deleting.set(false), }); } badgeClass(status: string): string { if (status === 'GENERATED') return 'fr-badge fr-badge--success'; if (status === 'ERROR') return 'fr-badge fr-badge--error'; return 'fr-badge fr-badge--info'; } private loadApi() { this.loading.set(true); this.apiService.get(this.id).subscribe({ next: (entry) => { this.api.set(entry); this.loading.set(false); this.startPollIfPending(); if (entry.categoryId) { this.categoryService.browse(entry.categoryId).subscribe({ next: (data) => this.categoryBreadcrumb.set(data.breadcrumb), error: () => {}, }); } }, error: (err) => { this.error.set(err.message ?? 'Erreur lors du chargement'); this.loading.set(false); }, }); } private startPollIfPending() { this.clearPoll(); if (this.api()?.status === 'PENDING') { this.pollInterval = setInterval(() => this.pollStatus(), 3000); } } private pollStatus() { this.apiService.get(this.id).subscribe({ next: (entry) => { this.api.set(entry); if (entry.status !== 'PENDING') this.clearPoll(); }, }); } private clearPoll() { if (this.pollInterval) { clearInterval(this.pollInterval); this.pollInterval = null; } } }