From 80bd18a63020dde8afeb75303a41601c4dd1736f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 20 Jun 2026 12:19:44 +0200 Subject: [PATCH] Load calendar data async so slow/failing sources don't block page load Split loadDashboardData into two phases: core data (settings, widgets, notes) renders immediately, then calendar events load in the background. Uses Promise.allSettled so individual calendar source failures don't affect other sources or the dashboard. Co-Authored-By: Claude Sonnet 4.6 --- src/app/page.tsx | 78 +++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 60afeca..67e97f2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -381,33 +381,21 @@ export default function DashboardPage() { } } - async function loadDashboardData() { - setDashboardError(null); - + async function loadCalendarData(widgets: Widget[]) { try { - const [settingsResponse, widgetsResponse, notesResponse] = await Promise.all([ - fetch("/api/settings", { - cache: "no-store" - }), - fetch("/api/widgets", { - cache: "no-store" - }), - fetch("/api/notes", { - cache: "no-store" - }) - ]); - - const settingsData = await parseJsonResponse<{ settings: Settings }>(settingsResponse); - const widgetsData = await parseJsonResponse<{ widgets: Widget[] }>(widgetsResponse); - const notesData = await parseJsonResponse<{ notes: NoteBoardItem[] }>(notesResponse); - const allSourcesResponse = await fetch("/api/calendar/source", { cache: "no-store" }); const allSourcesData = await parseJsonResponse<{ sources: CalendarSource[] }>(allSourcesResponse); + setCalendarSources(allSourcesData.sources); - const calendarWidgets = widgetsData.widgets.filter((widget) => widget.type === "calendar"); - const calendarLoads = await Promise.all( + const calendarWidgets = widgets.filter((widget) => widget.type === "calendar"); + + if (calendarWidgets.length === 0) { + return; + } + + const calendarLoads = await Promise.allSettled( calendarWidgets.map(async (widget) => { const [configResponse, eventsResponse] = await Promise.all([ fetch(`/api/calendar/source?widgetId=${encodeURIComponent(widget.id)}`, { @@ -439,21 +427,49 @@ export default function DashboardPage() { const nextSelectionsByWidget: Record = {}; const nextEventsCountByWidget: Record = {}; - calendarLoads.forEach((calendarLoad) => { - nextEventsByWidget[calendarLoad.widgetId] = calendarLoad.events; - nextErrorsByWidget[calendarLoad.widgetId] = calendarLoad.error; - nextSelectionsByWidget[calendarLoad.widgetId] = calendarLoad.selectedSourceIds; - nextEventsCountByWidget[calendarLoad.widgetId] = calendarLoad.nextEventsCount; + calendarLoads.forEach((result) => { + if (result.status === "fulfilled") { + nextEventsByWidget[result.value.widgetId] = result.value.events; + nextErrorsByWidget[result.value.widgetId] = result.value.error; + nextSelectionsByWidget[result.value.widgetId] = result.value.selectedSourceIds; + nextEventsCountByWidget[result.value.widgetId] = result.value.nextEventsCount; + } }); + setCalendarEventsByWidget(nextEventsByWidget); + setCalendarErrorsByWidget(nextErrorsByWidget); + setCalendarSelectionsByWidget(nextSelectionsByWidget); + setCalendarNextEventsCountByWidget(nextEventsCountByWidget); + } catch { + // calendar load failure must not block dashboard + } + } + + async function loadDashboardData() { + setDashboardError(null); + + try { + const [settingsResponse, widgetsResponse, notesResponse] = await Promise.all([ + fetch("/api/settings", { + cache: "no-store" + }), + fetch("/api/widgets", { + cache: "no-store" + }), + fetch("/api/notes", { + cache: "no-store" + }) + ]); + + const settingsData = await parseJsonResponse<{ settings: Settings }>(settingsResponse); + const widgetsData = await parseJsonResponse<{ widgets: Widget[] }>(widgetsResponse); + const notesData = await parseJsonResponse<{ notes: NoteBoardItem[] }>(notesResponse); + setSettings(settingsData.settings); setWidgets(widgetsData.widgets); setNotes(notesData.notes); - setCalendarEventsByWidget(nextEventsByWidget); - setCalendarErrorsByWidget(nextErrorsByWidget); - setCalendarSources(allSourcesData.sources); - setCalendarSelectionsByWidget(nextSelectionsByWidget); - setCalendarNextEventsCountByWidget(nextEventsCountByWidget); + + void loadCalendarData(widgetsData.widgets); } catch (error) { setDashboardError(error instanceof Error ? error.message : "Dashboard konnte nicht geladen werden."); }