From 2e9ce8e1da79b85de5b0973dc8953d45cec684b0 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 8 May 2026 10:55:04 +0200 Subject: [PATCH] fix(documents): surface timeline density fetch failures via console.warn (#385) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously a 5xx, network blip, or JSON parse error all collapsed into the same silent "no buckets" rendering. The widget still degrades gracefully — failure should not block the document list — but operators and Sentry now see the failure in browser devtools instead of having to reverse-engineer a missing chart. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/lib/document/timeline.spec.ts | 23 ++++++++++++++++++++++ frontend/src/lib/document/timeline.ts | 8 ++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/document/timeline.spec.ts b/frontend/src/lib/document/timeline.spec.ts index 212437a5..9deca472 100644 --- a/frontend/src/lib/document/timeline.spec.ts +++ b/frontend/src/lib/document/timeline.spec.ts @@ -285,6 +285,29 @@ describe('fetchDensity', () => { expect(result).toEqual({ density: [], minDate: null, maxDate: null }); }); + + it('emits console.warn with the status when the response is non-ok', async () => { + const fetch = vi.fn().mockResolvedValue({ ok: false, status: 503 }); + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); + + await fetchDensity(fetch, null, true); + + expect(warn).toHaveBeenCalledTimes(1); + expect(warn.mock.calls[0][0]).toContain('503'); + warn.mockRestore(); + }); + + it('emits console.warn with the caught error when fetch rejects', async () => { + const error = new TypeError('Network down'); + const fetch = vi.fn().mockRejectedValue(error); + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); + + await fetchDensity(fetch, null, true); + + expect(warn).toHaveBeenCalledTimes(1); + expect(warn.mock.calls[0]).toContain(error); + warn.mockRestore(); + }); }); describe('tickIndicesFor', () => { diff --git a/frontend/src/lib/document/timeline.ts b/frontend/src/lib/document/timeline.ts index 5919faf7..81d5c90e 100644 --- a/frontend/src/lib/document/timeline.ts +++ b/frontend/src/lib/document/timeline.ts @@ -208,14 +208,18 @@ export async function fetchDensity( try { const response = await fetch(buildDensityUrl(filters)); - if (!response.ok) return EMPTY; + if (!response.ok) { + console.warn(`[timeline] density fetch responded with ${response.status}`); + return EMPTY; + } const body = (await response.json()) as DocumentDensityResult; return { density: body.buckets, minDate: body.minDate ?? null, maxDate: body.maxDate ?? null }; - } catch { + } catch (error) { + console.warn('[timeline] density fetch failed', error); return EMPTY; } }