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).
This commit is contained in:
m
2026-03-25 18:34:11 +01:00
parent e635efa71e
commit 50bfa3deb4
14 changed files with 42 additions and 33 deletions

View File

@@ -27,7 +27,7 @@ export default function AIExtractPage() {
queryFn: () => api.get<PaginatedResponse<Case>>("/cases"),
});
const cases = casesData?.data ?? [];
const cases = Array.isArray(casesData?.data) ? casesData.data : [];
async function handleExtract(file: File | null, text: string) {
setIsExtracting(true);

View File

@@ -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 (
<div className="animate-fade-in">
@@ -205,7 +205,7 @@ export default function CaseDetailPage() {
{caseDetail.deadlines_count}
</span>
)}
{tab.key === "parties" && caseDetail.parties.length > 0 && (
{tab.key === "parties" && Array.isArray(caseDetail.parties) && caseDetail.parties.length > 0 && (
<span className="ml-1 rounded-full bg-neutral-100 px-1.5 py-0.5 text-xs text-neutral-500">
{caseDetail.parties.length}
</span>
@@ -217,7 +217,7 @@ export default function CaseDetailPage() {
<div className="mt-6">
{activeTab === "timeline" && (
<CaseTimeline events={caseDetail.recent_events ?? []} />
<CaseTimeline events={Array.isArray(caseDetail.recent_events) ? caseDetail.recent_events : []} />
)}
{activeTab === "deadlines" && (
@@ -229,7 +229,7 @@ export default function CaseDetailPage() {
)}
{activeTab === "parties" && (
<PartyList caseId={id} parties={caseDetail.parties ?? []} />
<PartyList caseId={id} parties={Array.isArray(caseDetail.parties) ? caseDetail.parties : []} />
)}
</div>
</div>

View File

@@ -68,7 +68,7 @@ export default function CasesPage() {
},
});
const cases = data?.cases ?? [];
const cases = Array.isArray(data?.cases) ? data.cases : [];
return (
<div className="animate-fade-in">

View File

@@ -80,17 +80,17 @@ export default function DashboardPage() {
</p>
</div>
<DeadlineTrafficLights data={data.deadline_summary} />
<DeadlineTrafficLights data={data.deadline_summary ?? { overdue_count: 0, due_this_week: 0, due_next_week: 0, ok_count: 0 }} />
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
<div className="lg:col-span-2">
<UpcomingTimeline
deadlines={data.upcoming_deadlines}
appointments={data.upcoming_appointments}
deadlines={Array.isArray(data.upcoming_deadlines) ? data.upcoming_deadlines : []}
appointments={Array.isArray(data.upcoming_appointments) ? data.upcoming_appointments : []}
/>
</div>
<div className="space-y-6">
<CaseOverviewGrid data={data.case_summary} />
<CaseOverviewGrid data={data.case_summary ?? { active_count: 0, new_this_month: 0, closed_count: 0 }} />
<AISummaryCard data={data} />
<QuickActions />
</div>

View File

@@ -66,7 +66,7 @@ export default function FristenPage() {
{view === "list" ? (
<DeadlineList />
) : (
<DeadlineCalendarView deadlines={deadlines || []} />
<DeadlineCalendarView deadlines={Array.isArray(deadlines) ? deadlines : []} />
)}
</div>
);

View File

@@ -84,7 +84,7 @@ export default function TerminePage() {
<AppointmentList onEdit={handleEdit} />
) : (
<AppointmentCalendar
appointments={appointments || []}
appointments={Array.isArray(appointments) ? appointments : []}
onAppointmentClick={handleEdit}
/>
)}