From 388d39a3796e422fa6d5b8baca2a43ca7d61cbaa Mon Sep 17 00:00:00 2001 From: tristan Date: Tue, 16 Jun 2026 18:02:16 +0200 Subject: [PATCH] =?UTF-8?q?refactor(transport)=20:=20onglet=20Qualimat=20e?= =?UTF-8?q?n=20MalioDataTable=20pagin=C3=A9,=20recherche=20branch=C3=A9e?= =?UTF-8?q?=20sur=20le=20nom=20(ERP-166)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/i18n/locales/fr.json | 1 - .../__tests__/useQualimatSearch.test.ts | 85 +++++----- .../composables/useQualimatSearch.ts | 60 ++----- .../modules/transport/pages/carriers/new.vue | 148 ++++++++++-------- 4 files changed, 144 insertions(+), 150 deletions(-) diff --git a/frontend/i18n/locales/fr.json b/frontend/i18n/locales/fr.json index 524e414..ff0c395 100644 --- a/frontend/i18n/locales/fr.json +++ b/frontend/i18n/locales/fr.json @@ -557,7 +557,6 @@ "liotPlatesHint": "Séparées par « ; »" }, "qualimat": { - "search": "Rechercher un transporteur QUALIMAT", "empty": "Aucun transporteur QUALIMAT trouvé.", "continue": "Continuer", "columns": { diff --git a/frontend/modules/transport/composables/__tests__/useQualimatSearch.test.ts b/frontend/modules/transport/composables/__tests__/useQualimatSearch.test.ts index 339f8d3..eb38d1a 100644 --- a/frontend/modules/transport/composables/__tests__/useQualimatSearch.test.ts +++ b/frontend/modules/transport/composables/__tests__/useQualimatSearch.test.ts @@ -1,55 +1,62 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { useQualimatSearch, type QualimatCarrierRow } from '../useQualimatSearch' + +const mockApiGet = vi.hoisted(() => vi.fn()) +vi.stubGlobal('useApi', () => ({ get: mockApiGet })) /** * Tests de la saisie assistée QUALIMAT (M4 Transport, ERP-166 — RG-4.01). * - * `useQualimatSearch` interroge `GET /api/qualimat_carriers?search=`. On vérifie le - * CONTRAT (pas le timing du debounce, couvert par `debounce.test.ts`) via `fetchNow` : - * - ressource ciblée + paramètre `search` (trimé) + header `Accept: application/ld+json` ; - * - consommation de l'enveloppe Hydra (`member`) ; - * - échec réseau → résultats vidés, pas de throw (recherche non bloquante). + * `useQualimatSearch` est une fine enveloppe de `usePaginatedList` + * sur `/qualimat_carriers`. La pagination générique est couverte par + * `usePaginatedList.test.ts` ; on vérifie ici le CONTRAT propre à la recherche : + * - ressource ciblée `/qualimat_carriers` + enveloppe Hydra + `Accept: application/ld+json` ; + * - le filtre `search` (branché sur le nom du transporteur) est transmis et + * retombe en page 1. */ - -const mockGet = vi.hoisted(() => vi.fn()) -vi.stubGlobal('useApi', () => ({ - get: mockGet, - post: vi.fn(), - put: vi.fn(), - patch: vi.fn(), - delete: vi.fn(), -})) - -const { useQualimatSearch } = await import('../useQualimatSearch') - describe('useQualimatSearch', () => { beforeEach(() => { - mockGet.mockReset() + mockApiGet.mockReset() }) - it('fetchNow cible /qualimat_carriers (search trimé, ld+json) et consomme member', async () => { - mockGet.mockResolvedValueOnce({ - member: [{ '@id': '/api/qualimat_carriers/1', id: '1', name: 'ACME', validityDate: '2027-01-01' }], - }) - const q = useQualimatSearch() + const PAGE: QualimatCarrierRow[] = [ + { + '@id': '/api/qualimat_carriers/1', + id: '1', + name: 'TRANSPORTS ACME', + siret: '12345678900012', + address: '1 rue du Port', + postalCode: '86000', + city: 'Poitiers', + validityDate: '2027-01-15', + status: 'VALIDE', + }, + ] - await q.fetchNow(' acme ') + it('cible /qualimat_carriers, consomme l\'enveloppe Hydra et envoie l\'Accept ld+json', async () => { + mockApiGet.mockResolvedValueOnce({ member: PAGE, totalItems: 1 }) + const repo = useQualimatSearch() - expect(mockGet).toHaveBeenCalledWith( - '/qualimat_carriers', - { search: 'acme' }, - { headers: { Accept: 'application/ld+json' }, toast: false }, - ) - expect(q.results.value).toHaveLength(1) - expect(q.results.value[0]?.name).toBe('ACME') - expect(q.loading.value).toBe(false) + await repo.fetch() + + const [url, query, opts] = mockApiGet.mock.calls[0] + expect(url).toBe('/qualimat_carriers') + expect(query).toMatchObject({ page: 1, itemsPerPage: 10 }) + expect(opts).toMatchObject({ toast: false, headers: { Accept: 'application/ld+json' } }) + expect(repo.items.value).toEqual(PAGE) + expect(repo.totalItems.value).toBe(1) }) - it('échec réseau : résultats vidés, pas de throw', async () => { - mockGet.mockRejectedValueOnce(new Error('network')) - const q = useQualimatSearch() + it('transmet le filtre `search` (nom du transporteur) et retombe en page 1', async () => { + mockApiGet.mockResolvedValueOnce({ member: PAGE, totalItems: 1 }) + const repo = useQualimatSearch() + await repo.fetch() - await expect(q.fetchNow('x')).resolves.toBeUndefined() - expect(q.results.value).toEqual([]) - expect(q.loading.value).toBe(false) + mockApiGet.mockResolvedValueOnce({ member: PAGE, totalItems: 1 }) + await repo.setFilters({ search: 'acme' }) + + expect(repo.currentPage.value).toBe(1) + const query = mockApiGet.mock.calls.at(-1)?.[1] as Record + expect(query.search).toBe('acme') }) }) diff --git a/frontend/modules/transport/composables/useQualimatSearch.ts b/frontend/modules/transport/composables/useQualimatSearch.ts index b14ad34..4bcfb32 100644 --- a/frontend/modules/transport/composables/useQualimatSearch.ts +++ b/frontend/modules/transport/composables/useQualimatSearch.ts @@ -1,6 +1,4 @@ -import { ref } from 'vue' -import { debounce } from '~/shared/utils/debounce' -import type { HydraCollection } from '~/shared/utils/api' +import { usePaginatedList } from '~/shared/composables/usePaginatedList' /** * Ligne du référentiel QUALIMAT renvoyée par la saisie assistée (groupe @@ -20,57 +18,23 @@ export interface QualimatCarrierRow { status: string | null } -/** Délai de debounce de la recherche (ms) — une requête après la dernière frappe. */ -const SEARCH_DEBOUNCE_MS = 300 +/** Filtre de la recherche QUALIMAT (branché sur le nom du transporteur). */ +export interface QualimatSearchFilters { + search?: string +} /** * Saisie assistée QUALIMAT (M4 Transport, ERP-166 — RG-4.01 / spec-back § 4.7). * * `GET /api/qualimat_carriers?search=` : référentiel en LECTURE SEULE, lignes - * actives uniquement (filtré côté serveur), recherche fuzzy nom + siret. Alimente - * le tableau de sélection de l'onglet Qualimat ; la ligne choisie est copiée dans - * le formulaire principal (cf. `useCarrierForm.applyQualimatSelection`). + * actives uniquement (filtré côté serveur), recherche fuzzy nom + siret. Simple + * enveloppe de `usePaginatedList` (règle frontend : toute GetCollection passe par + * ce composable — pagination Hydra, état 100 % local) consommée par le + * `MalioDataTable` de l'onglet Qualimat. Le filtre `search` est piloté par le nom + * saisi dans le formulaire principal (pas de champ de recherche dédié). * - * Volontairement PAR INSTANCE (état local à l'écran d'ajout). `search()` est - * debouncé (anti-spam réseau) ; `fetchNow()` expose l'appel immédiat (montage / - * tests). + * Volontairement PAR INSTANCE (état local à l'écran d'ajout). */ export function useQualimatSearch() { - const api = useApi() - - const results = ref([]) - const loading = ref(false) - - /** Lance immédiatement la recherche (sans debounce). */ - async function fetchNow(term: string): Promise { - loading.value = true - try { - const data = await api.get>( - '/qualimat_carriers', - { search: term.trim() }, - { headers: { Accept: 'application/ld+json' }, toast: false }, - ) - results.value = data.member ?? [] - } - catch { - // Échec réseau / 403 : on vide les résultats, pas de toast (la recherche - // assistée est non bloquante — l'utilisateur peut saisir manuellement). - results.value = [] - } - finally { - loading.value = false - } - } - - // Recherche debouncée branchée sur le champ de recherche de l'onglet Qualimat. - const search = debounce((term: string) => { - void fetchNow(term) - }, SEARCH_DEBOUNCE_MS) - - return { - results, - loading, - search, - fetchNow, - } + return usePaginatedList({ url: '/qualimat_carriers' }) } diff --git a/frontend/modules/transport/pages/carriers/new.vue b/frontend/modules/transport/pages/carriers/new.vue index 7b8f40f..4733037 100644 --- a/frontend/modules/transport/pages/carriers/new.vue +++ b/frontend/modules/transport/pages/carriers/new.vue @@ -131,57 +131,42 @@ assistee (table de selection) ; Adresses / Contacts / Prix arrivent aux tickets suivants (placeholders « A venir »). --> - +