From 50bfa3deb419cd4793461802dbd31316df1ddca1 Mon Sep 17 00:00:00 2001 From: m Date: Wed, 25 Mar 2026 18:34:11 +0100 Subject: [PATCH] fix: add array guards to all frontend components consuming API responses Prevents "M.forEach is not a function" crashes when API returns error objects or unexpected shapes instead of arrays. Guards all useQuery consumers with Array.isArray checks and safe defaults for object props. Files fixed: DeadlineList, AppointmentList, TenantSwitcher, DeadlineTrafficLights, UpcomingTimeline, CaseOverviewGrid, AISummaryCard, TeamSettings, and all page-level components (dashboard, cases, fristen, termine, ai/extract). --- frontend/src/app/(app)/ai/extract/page.tsx | 2 +- frontend/src/app/(app)/cases/[id]/page.tsx | 10 +++++----- frontend/src/app/(app)/cases/page.tsx | 2 +- frontend/src/app/(app)/dashboard/page.tsx | 8 ++++---- frontend/src/app/(app)/fristen/page.tsx | 2 +- frontend/src/app/(app)/termine/page.tsx | 2 +- .../src/components/appointments/AppointmentList.tsx | 9 +++++---- frontend/src/components/dashboard/AISummaryCard.tsx | 4 +++- .../src/components/dashboard/CaseOverviewGrid.tsx | 7 ++++--- .../components/dashboard/DeadlineTrafficLights.tsx | 11 ++++++----- .../src/components/dashboard/UpcomingTimeline.tsx | 7 +++++-- frontend/src/components/deadlines/DeadlineList.tsx | 8 ++++---- frontend/src/components/layout/TenantSwitcher.tsx | 1 + frontend/src/components/settings/TeamSettings.tsx | 2 +- 14 files changed, 42 insertions(+), 33 deletions(-) diff --git a/frontend/src/app/(app)/ai/extract/page.tsx b/frontend/src/app/(app)/ai/extract/page.tsx index c9eb691..ff1fc08 100644 --- a/frontend/src/app/(app)/ai/extract/page.tsx +++ b/frontend/src/app/(app)/ai/extract/page.tsx @@ -27,7 +27,7 @@ export default function AIExtractPage() { queryFn: () => api.get>("/cases"), }); - const cases = casesData?.data ?? []; + const cases = Array.isArray(casesData?.data) ? casesData.data : []; async function handleExtract(file: File | null, text: string) { setIsExtracting(true); diff --git a/frontend/src/app/(app)/cases/[id]/page.tsx b/frontend/src/app/(app)/cases/[id]/page.tsx index d5e6b2e..0aac465 100644 --- a/frontend/src/app/(app)/cases/[id]/page.tsx +++ b/frontend/src/app/(app)/cases/[id]/page.tsx @@ -132,8 +132,8 @@ export default function CaseDetailPage() { ); } - const deadlines = deadlinesData?.deadlines ?? []; - const documents = documentsData ?? []; + const deadlines = Array.isArray(deadlinesData?.deadlines) ? deadlinesData.deadlines : []; + const documents = Array.isArray(documentsData) ? documentsData : []; return (
@@ -205,7 +205,7 @@ export default function CaseDetailPage() { {caseDetail.deadlines_count} )} - {tab.key === "parties" && caseDetail.parties.length > 0 && ( + {tab.key === "parties" && Array.isArray(caseDetail.parties) && caseDetail.parties.length > 0 && ( {caseDetail.parties.length} @@ -217,7 +217,7 @@ export default function CaseDetailPage() {
{activeTab === "timeline" && ( - + )} {activeTab === "deadlines" && ( @@ -229,7 +229,7 @@ export default function CaseDetailPage() { )} {activeTab === "parties" && ( - + )}
diff --git a/frontend/src/app/(app)/cases/page.tsx b/frontend/src/app/(app)/cases/page.tsx index b16d707..5a125c8 100644 --- a/frontend/src/app/(app)/cases/page.tsx +++ b/frontend/src/app/(app)/cases/page.tsx @@ -68,7 +68,7 @@ export default function CasesPage() { }, }); - const cases = data?.cases ?? []; + const cases = Array.isArray(data?.cases) ? data.cases : []; return (
diff --git a/frontend/src/app/(app)/dashboard/page.tsx b/frontend/src/app/(app)/dashboard/page.tsx index 8a4fc03..7ae7549 100644 --- a/frontend/src/app/(app)/dashboard/page.tsx +++ b/frontend/src/app/(app)/dashboard/page.tsx @@ -80,17 +80,17 @@ export default function DashboardPage() {

- +
- +
diff --git a/frontend/src/app/(app)/fristen/page.tsx b/frontend/src/app/(app)/fristen/page.tsx index 213d52d..6a620f0 100644 --- a/frontend/src/app/(app)/fristen/page.tsx +++ b/frontend/src/app/(app)/fristen/page.tsx @@ -66,7 +66,7 @@ export default function FristenPage() { {view === "list" ? ( ) : ( - + )}
); diff --git a/frontend/src/app/(app)/termine/page.tsx b/frontend/src/app/(app)/termine/page.tsx index b080919..2c6f4d4 100644 --- a/frontend/src/app/(app)/termine/page.tsx +++ b/frontend/src/app/(app)/termine/page.tsx @@ -84,7 +84,7 @@ export default function TerminePage() { ) : ( )} diff --git a/frontend/src/components/appointments/AppointmentList.tsx b/frontend/src/components/appointments/AppointmentList.tsx index c1d21ae..78fb0b0 100644 --- a/frontend/src/components/appointments/AppointmentList.tsx +++ b/frontend/src/components/appointments/AppointmentList.tsx @@ -73,12 +73,13 @@ export function AppointmentList({ onEdit }: AppointmentListProps) { const caseMap = useMemo(() => { const map = new Map(); - cases?.cases?.forEach((c) => map.set(c.id, c)); + const arr = Array.isArray(cases?.cases) ? cases.cases : []; + arr.forEach((c) => map.set(c.id, c)); return map; }, [cases]); const filtered = useMemo(() => { - if (!appointments) return []; + if (!Array.isArray(appointments)) return []; return appointments .filter((a) => { if (caseFilter !== "all" && a.case_id !== caseFilter) return false; @@ -91,7 +92,7 @@ export function AppointmentList({ onEdit }: AppointmentListProps) { const grouped = useMemo(() => groupByDate(filtered), [filtered]); const counts = useMemo(() => { - if (!appointments) return { today: 0, thisWeek: 0, total: 0 }; + if (!Array.isArray(appointments)) return { today: 0, thisWeek: 0, total: 0 }; let today = 0; let thisWeek = 0; for (const a of appointments) { @@ -148,7 +149,7 @@ export function AppointmentList({ onEdit }: AppointmentListProps) { ))} - {cases?.cases && cases.cases.length > 0 && ( + {Array.isArray(cases?.cases) && cases.cases.length > 0 && ( - {cases && cases.length > 0 && ( + {Array.isArray(cases) && cases.length > 0 && (