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 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-06-20 12:19:44 +02:00
parent 09a0e44b30
commit 80bd18a630
+47 -31
View File
@@ -381,33 +381,21 @@ export default function DashboardPage() {
} }
} }
async function loadDashboardData() { async function loadCalendarData(widgets: Widget[]) {
setDashboardError(null);
try { 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", { const allSourcesResponse = await fetch("/api/calendar/source", {
cache: "no-store" cache: "no-store"
}); });
const allSourcesData = await parseJsonResponse<{ sources: CalendarSource[] }>(allSourcesResponse); const allSourcesData = await parseJsonResponse<{ sources: CalendarSource[] }>(allSourcesResponse);
setCalendarSources(allSourcesData.sources);
const calendarWidgets = widgetsData.widgets.filter((widget) => widget.type === "calendar"); const calendarWidgets = widgets.filter((widget) => widget.type === "calendar");
const calendarLoads = await Promise.all(
if (calendarWidgets.length === 0) {
return;
}
const calendarLoads = await Promise.allSettled(
calendarWidgets.map(async (widget) => { calendarWidgets.map(async (widget) => {
const [configResponse, eventsResponse] = await Promise.all([ const [configResponse, eventsResponse] = await Promise.all([
fetch(`/api/calendar/source?widgetId=${encodeURIComponent(widget.id)}`, { fetch(`/api/calendar/source?widgetId=${encodeURIComponent(widget.id)}`, {
@@ -439,21 +427,49 @@ export default function DashboardPage() {
const nextSelectionsByWidget: Record<string, string[]> = {}; const nextSelectionsByWidget: Record<string, string[]> = {};
const nextEventsCountByWidget: Record<string, number> = {}; const nextEventsCountByWidget: Record<string, number> = {};
calendarLoads.forEach((calendarLoad) => { calendarLoads.forEach((result) => {
nextEventsByWidget[calendarLoad.widgetId] = calendarLoad.events; if (result.status === "fulfilled") {
nextErrorsByWidget[calendarLoad.widgetId] = calendarLoad.error; nextEventsByWidget[result.value.widgetId] = result.value.events;
nextSelectionsByWidget[calendarLoad.widgetId] = calendarLoad.selectedSourceIds; nextErrorsByWidget[result.value.widgetId] = result.value.error;
nextEventsCountByWidget[calendarLoad.widgetId] = calendarLoad.nextEventsCount; 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); setSettings(settingsData.settings);
setWidgets(widgetsData.widgets); setWidgets(widgetsData.widgets);
setNotes(notesData.notes); setNotes(notesData.notes);
setCalendarEventsByWidget(nextEventsByWidget);
setCalendarErrorsByWidget(nextErrorsByWidget); void loadCalendarData(widgetsData.widgets);
setCalendarSources(allSourcesData.sources);
setCalendarSelectionsByWidget(nextSelectionsByWidget);
setCalendarNextEventsCountByWidget(nextEventsCountByWidget);
} catch (error) { } catch (error) {
setDashboardError(error instanceof Error ? error.message : "Dashboard konnte nicht geladen werden."); setDashboardError(error instanceof Error ? error.message : "Dashboard konnte nicht geladen werden.");
} }