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() {
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<string, string[]> = {};
const nextEventsCountByWidget: Record<string, number> = {};
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.");
}