From a6488d160fafb91de168b946c688ce64b6e95895 Mon Sep 17 00:00:00 2001 From: Git Agent Date: Sun, 23 Aug 2026 16:24:02 +0200 Subject: [PATCH] =?UTF-8?q?fix(web):=20admin=20=E2=80=94=20scan=20initial?= =?UTF-8?q?=20apr=C3=A8s=20cr=C3=A9ation,=20retrait=20des=20fallbacks=20mo?= =?UTF-8?q?ck?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La création d'une bibliothèque déclenche un scan initial avec état de réessai en cas d'échec; suppression des fallbacks mock sur create/scan pour ne masquer aucune erreur réelle. Co-Authored-By: Claude Opus 4.8 --- apps/web/src/api/client.test.ts | 20 ++++++++++++++++ apps/web/src/api/client.ts | 5 ++-- apps/web/src/pages/AdminPage.tsx | 40 ++++++++++++++++++++++++-------- 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/apps/web/src/api/client.test.ts b/apps/web/src/api/client.test.ts index aab4b8c..80ddfdc 100644 --- a/apps/web/src/api/client.test.ts +++ b/apps/web/src/api/client.test.ts @@ -31,6 +31,26 @@ describe("api fallback helpers", () => { expect(headers.has("Content-Type")).toBe(false); }); + it("surfaces create library API errors without fallback", async () => { + const fetchMock = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ message: "Library path does not exist" }), { + status: 400, + statusText: "Bad Request", + headers: { "Content-Type": "application/json" } + }) + ); + vi.stubGlobal("fetch", fetchMock); + + await expect(api.createLibrary({ name: "Books", path: "/missing", enabled: true })).rejects.toThrow("Library path does not exist"); + }); + + it("does not fallback when scan enqueue fails", async () => { + const fetchMock = vi.fn().mockRejectedValue(new Error("offline")); + vi.stubGlobal("fetch", fetchMock); + + await expect(api.scanLibrary(42)).rejects.toThrow("offline"); + }); + it("sends metadata source updates to the admin endpoint", async () => { const fetchMock = vi.fn().mockResolvedValue( new Response(JSON.stringify({ isbnPriorityEnabled: false, sources: [] }), { diff --git a/apps/web/src/api/client.ts b/apps/web/src/api/client.ts index 489f910..baeb744 100644 --- a/apps/web/src/api/client.ts +++ b/apps/web/src/api/client.ts @@ -185,15 +185,14 @@ export const api = { async createLibrary(input: CreateLibraryDto): Promise { return request("/admin/libraries", { method: "POST", - body: JSON.stringify(input), - fallback: { id: Date.now(), createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), ...input } + body: JSON.stringify(input) }); }, async deleteLibrary(id: number): Promise { await request<{ ok: true }>(`/admin/libraries/${id}`, { method: "DELETE" }); }, async scanLibrary(id: number): Promise { - return request(`/admin/libraries/${id}/scan`, { method: "POST", fallback: mockJobs[0] }); + return request(`/admin/libraries/${id}/scan`, { method: "POST" }); }, async jobs(): Promise { return request("/admin/jobs", { fallback: mockJobs }); diff --git a/apps/web/src/pages/AdminPage.tsx b/apps/web/src/pages/AdminPage.tsx index 2e42aef..d3573dc 100644 --- a/apps/web/src/pages/AdminPage.tsx +++ b/apps/web/src/pages/AdminPage.tsx @@ -13,6 +13,7 @@ export function AdminPage() { const [path, setPath] = useState("/library"); const [error, setError] = useState(); const [success, setSuccess] = useState(); + const [scanRetryLibrary, setScanRetryLibrary] = useState(); async function refresh() { setLoading(true); @@ -53,28 +54,38 @@ export function AdminPage() { event.preventDefault(); setError(undefined); setSuccess(undefined); + setScanRetryLibrary(undefined); try { - await api.createLibrary({ name, path, enabled: true }); + const created = await api.createLibrary({ name, path, enabled: true }); await refresh(); - setSuccess(`Bibliothèque "${name}" ajoutée.`); + setName("Bibliotheque locale"); + setPath("/library"); + try { + await api.scanLibrary(created.id); + await refresh(); + setSuccess(`Bibliothèque "${created.name}" ajoutée. Scan initial demandé.`); + } catch (scanError) { + setScanRetryLibrary(created); + setSuccess( + `Bibliothèque "${created.name}" ajoutée, mais le scan initial n'a pas pu être demandé. Tu peux réessayer le scan.` + ); + setError(scanError instanceof Error ? `Scan initial impossible : ${scanError.message}` : "Scan initial impossible."); + } } catch (createError) { - const fallback = getApiFallback(createError); - if (fallback) setLibraries((current) => [fallback, ...current]); - setError(fallback ? "Creation en mode secours, synchronisation a retenter." : "Creation impossible"); + setError(createError instanceof Error ? `Création impossible : ${createError.message}` : "Création impossible."); } } async function scan(id: number) { setError(undefined); setSuccess(undefined); + setScanRetryLibrary(undefined); try { await api.scanLibrary(id); await refresh(); setSuccess("Scan demandé."); } catch (scanError) { - const fallback = getApiFallback(scanError); - if (fallback) setJobs((current) => [fallback, ...current]); - setError(fallback ? "Scan place en file de secours, statut a verifier." : "Scan impossible"); + setError(scanError instanceof Error ? `Scan impossible : ${scanError.message}` : "Scan impossible."); } } @@ -86,6 +97,7 @@ export function AdminPage() { setError(undefined); setSuccess(undefined); + setScanRetryLibrary(undefined); try { await api.deleteLibrary(library.id); setLibraries((current) => current.filter((item) => item.id !== library.id)); @@ -104,14 +116,22 @@ export function AdminPage() { {success &&
{success}
} - {error && ( + {scanRetryLibrary ? ( +
+ La bibliothèque est conservée dans la liste. + +
+ ) : error ? (
Les formulaires restent disponibles.
- )} + ) : null}