Problem
PR #291 introduced friendly upstream error handling in the Research components, but the implementation was copy-pasted identically into 3 files:
web/src/components/research/ResearchDashboard.tsx
web/src/components/research/ResearchDiscoveryPage.tsx
web/src/components/research/ResearchPageNew.tsx
Duplicated code (3 copies each)
| Item |
Lines per copy |
Total duplicated |
type UpstreamErrorBody |
4 lines |
12 lines |
function toFriendlyErrorMessage() |
~25 lines |
~75 lines |
async function fetchJson<T>() |
~12 lines |
~36 lines |
Additionally, `e instanceof Error ? e.message : String(e)` appears 47 times across the entire frontend codebase, scattered across:
- ResearchDashboard (12x), ResearchPageNew (7x), ResearchDiscoveryPage (2x)
- Settings page (5x), WorkspacePanel (11x), RunbookPanel (4x)
- ScholarSubscriptionsPanel (4x), and others
Proposed fix
-
Create web/src/lib/fetch.ts — shared module:
export type UpstreamErrorBody = { detail?: string; error?: string }
export function toFriendlyErrorMessage(status: number, rawText: string): string | null { ... }
export async function fetchJson<T>(url: string, init?: RequestInit): Promise<T> { ... }
export function getErrorMessage(e: unknown): string {
return e instanceof Error ? e.message : String(e)
}
-
Update 3 Research components to import from shared lib, delete inline copies
-
Optionally replace scattered inline error extraction with getErrorMessage(e) across all components (low priority, incremental)
Context
web/src/lib/api.ts already has postJson<T>() but returns null on error (different contract)
web/src/lib/dashboard-api.ts has fetchJsonOrNull<T>() — yet another variant
- Long-term consider unifying all three fetch wrappers
Problem
PR #291 introduced friendly upstream error handling in the Research components, but the implementation was copy-pasted identically into 3 files:
web/src/components/research/ResearchDashboard.tsxweb/src/components/research/ResearchDiscoveryPage.tsxweb/src/components/research/ResearchPageNew.tsxDuplicated code (3 copies each)
type UpstreamErrorBodyfunction toFriendlyErrorMessage()async function fetchJson<T>()Additionally, `e instanceof Error ? e.message : String(e)` appears 47 times across the entire frontend codebase, scattered across:
Proposed fix
Create
web/src/lib/fetch.ts— shared module:Update 3 Research components to import from shared lib, delete inline copies
Optionally replace scattered inline error extraction with
getErrorMessage(e)across all components (low priority, incremental)Context
web/src/lib/api.tsalready haspostJson<T>()but returns null on error (different contract)web/src/lib/dashboard-api.tshasfetchJsonOrNull<T>()— yet another variant