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 28d5bfd..d0bd0c0 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 @@ -14,34 +14,17 @@ import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { ApiService } from '../../core/api.service'; import { CategoryService } from '../../core/category.service'; +import { BreadcrumbComponent, BreadcrumbItem } from '../../shared/breadcrumb/breadcrumb.component'; import { ApiEntry, ApiEntryListItem, Category } from '@datacat/shared'; @Component({ selector: 'app-api-detail', standalone: true, - imports: [CommonModule, RouterLink, ReactiveFormsModule], + imports: [CommonModule, RouterLink, ReactiveFormsModule, BreadcrumbComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
- - + @if (loading()) {
@@ -436,6 +419,15 @@ export class ApiDetailComponent implements OnInit, OnDestroy { return ['/browse']; }); + breadcrumbItems = computed(() => { + const crumbs = this.categoryBreadcrumb(); + const items: BreadcrumbItem[] = [{ label: 'Accueil', link: '/' }]; + if (crumbs.length === 0) items.push({ label: 'Parcourir', link: '/browse' }); + for (const crumb of crumbs) items.push({ label: crumb.name, link: ['/browse', crumb.id] }); + items.push({ label: this.api()?.title ?? 'Chargement...' }); + return items; + }); + /* Onglets */ activeTab = signal<'info' | 'contacts'>('info'); editingInfo = signal(false); diff --git a/front-public/src/app/pages/browse/browse.component.ts b/front-public/src/app/pages/browse/browse.component.ts index 8434a1b..6cd967d 100644 --- a/front-public/src/app/pages/browse/browse.component.ts +++ b/front-public/src/app/pages/browse/browse.component.ts @@ -17,32 +17,16 @@ import { CategoryBrowseResponse, Category, CategoryWithCounts, ApiEntryListItem import { forkJoin } from 'rxjs'; import { CategoryFormModalComponent } from '../../shared/category-form-modal/category-form-modal.component'; import { CategoryDeleteModalComponent } from '../../shared/category-delete-modal/category-delete-modal.component'; +import { BreadcrumbComponent, BreadcrumbItem } from '../../shared/breadcrumb/breadcrumb.component'; @Component({ selector: 'app-browse', standalone: true, - imports: [CommonModule, RouterLink, CategoryFormModalComponent, CategoryDeleteModalComponent], + imports: [CommonModule, RouterLink, CategoryFormModalComponent, CategoryDeleteModalComponent, BreadcrumbComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
- - + @if (categoryId()) { @@ -309,6 +293,16 @@ export class BrowseComponent implements OnInit { return d.breadcrumb.slice(0, -1); }); + breadcrumbItems = computed(() => { + const items: BreadcrumbItem[] = [{ label: 'Accueil', link: '/' }]; + for (const crumb of this.ancestors()) { + items.push({ label: crumb.name, link: ['/browse', crumb.id] }); + } + const current = this.browseData()?.category; + if (current) items.push({ label: current.name }); + return items; + }); + backUrl = computed(() => { const anc = this.ancestors(); if (anc.length > 0) return ['/browse', anc[anc.length - 1].id]; diff --git a/front-public/src/app/pages/upload/upload.component.ts b/front-public/src/app/pages/upload/upload.component.ts index 97cb840..e1b7eb1 100644 --- a/front-public/src/app/pages/upload/upload.component.ts +++ b/front-public/src/app/pages/upload/upload.component.ts @@ -13,23 +13,17 @@ import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms'; import { Router, RouterLink, ActivatedRoute } from '@angular/router'; import { ApiService } from '../../core/api.service'; import { CategoryService } from '../../core/category.service'; +import { BreadcrumbComponent, BreadcrumbItem } from '../../shared/breadcrumb/breadcrumb.component'; import { ApiEntry, ApiEntryListItem, Category, API_CONVENTION_LABELS, ApiConvention } from '@datacat/shared'; @Component({ selector: 'app-upload', standalone: true, - imports: [CommonModule, ReactiveFormsModule, RouterLink], + imports: [CommonModule, ReactiveFormsModule, RouterLink, BreadcrumbComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
- - +

Importer une API

@@ -511,6 +505,12 @@ export class UploadComponent implements OnInit { private destroyRef = inject(DestroyRef); step = signal(1); + readonly breadcrumbItems: BreadcrumbItem[] = [ + { label: 'Parcourir', link: '/browse' }, + { label: 'Catalogue', link: '/catalog' }, + { label: 'Importer une API' }, + ]; + private readonly maxFileSize = 5 * 1024 * 1024; // 5 Mo selectedFiles = signal([]); detectedType = signal<'ASYNCAPI' | 'OPENAPI' | null>(null); diff --git a/front-public/src/app/shared/breadcrumb/breadcrumb.component.ts b/front-public/src/app/shared/breadcrumb/breadcrumb.component.ts new file mode 100644 index 0000000..17a854d --- /dev/null +++ b/front-public/src/app/shared/breadcrumb/breadcrumb.component.ts @@ -0,0 +1,37 @@ +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; +import { RouterLink } from '@angular/router'; + +/** Un maillon du fil d'Ariane. `link` absent => élément courant (aria-current). */ +export interface BreadcrumbItem { + label: string; + link?: string | unknown[]; +} + +/** + * Fil d'Ariane DSFR générique (présentationnel). Chaque page construit sa liste + * d'items et la passe ; le dernier item sans `link` est marqué comme courant. + */ +@Component({ + selector: 'app-breadcrumb', + standalone: true, + imports: [RouterLink], + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + `, +}) +export class BreadcrumbComponent { + items = input.required(); +}