perf: batch-load app status history server-side to eliminate N+1 requests
- Load all app sparkline history in a single server query - Pass preloadedHistory to AppCard to skip client-side fetch - Polish empty state with icon, hint text, and add button
This commit is contained in:
@@ -20,34 +20,43 @@
|
||||
checkedAt: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
app: AppWithStatus;
|
||||
interface PreloadedHistory {
|
||||
history: StatusPoint[];
|
||||
uptimePercent: number;
|
||||
}
|
||||
|
||||
let { app }: Props = $props();
|
||||
interface Props {
|
||||
app: AppWithStatus;
|
||||
preloadedHistory?: PreloadedHistory | null;
|
||||
}
|
||||
|
||||
let historyData: StatusPoint[] = $state([]);
|
||||
let uptimePercent: number | null = $state(null);
|
||||
let historyLoading = $state(true);
|
||||
let { app, preloadedHistory = null }: Props = $props();
|
||||
|
||||
let historyData: StatusPoint[] = $state(preloadedHistory?.history ?? []);
|
||||
let uptimePercent: number | null = $state(preloadedHistory?.uptimePercent ?? null);
|
||||
let historyLoading = $state(!preloadedHistory);
|
||||
|
||||
const currentStatus = $derived(app.statuses?.[0]?.status ?? 'unknown');
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const res = await fetch(`/api/apps/${app.id}/history`);
|
||||
if (res.ok) {
|
||||
const json = await res.json();
|
||||
if (json.success && json.data) {
|
||||
historyData = json.data.history ?? [];
|
||||
uptimePercent = json.data.uptimePercent ?? null;
|
||||
// Only fetch client-side if no preloaded data was provided
|
||||
if (!preloadedHistory) {
|
||||
onMount(async () => {
|
||||
try {
|
||||
const res = await fetch(`/api/apps/${app.id}/history`);
|
||||
if (res.ok) {
|
||||
const json = await res.json();
|
||||
if (json.success && json.data) {
|
||||
historyData = json.data.history ?? [];
|
||||
uptimePercent = json.data.uptimePercent ?? null;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Silently fail — sparkline is non-critical
|
||||
} finally {
|
||||
historyLoading = false;
|
||||
}
|
||||
} catch {
|
||||
// Silently fail — sparkline is non-critical
|
||||
} finally {
|
||||
historyLoading = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const iconDisplay = $derived.by(() => {
|
||||
if (!app.icon) return null;
|
||||
|
||||
Reference in New Issue
Block a user