perf: logbook memoization and pagination, debounced realtime, lazy exceljs

- Precompute per-entry spend/caffeine/sugar map; paginate ledger at 50 rows
- Debounce realtime refetches (500ms trailing) and suppress during bulk import
- Show import progress (Importing X of N)
- Dynamic-import exceljs: entry chunk 1640 -> 700 kB
- Skeleton placeholders for initial load on Overview/Trends/Logbook
This commit is contained in:
nh9961
2026-07-19 21:00:51 +00:00
parent bb5750e928
commit 9e8f3c55d6
12 changed files with 247 additions and 61 deletions
+9 -2
View File
@@ -4,11 +4,12 @@ import { humanDateTime } from "../lib/metrics";
import { MODAL_TRANSITION } from "../lib/motion";
import { MiniMetric } from "./MetricTiles";
import { Modal } from "./Modal";
import type { ImportPreview, ImportPreviewRow } from "../types";
import type { ImportPreview, ImportPreviewRow, ImportProgress } from "../types";
type ImportPreviewModalProps = {
busy: boolean;
preview: ImportPreview | null;
importProgress: ImportProgress | null;
onClose: () => void;
onConfirm: () => void;
};
@@ -16,6 +17,7 @@ type ImportPreviewModalProps = {
export function ImportPreviewModal({
busy,
preview,
importProgress,
onClose,
onConfirm,
}: ImportPreviewModalProps) {
@@ -101,7 +103,12 @@ export function ImportPreviewModal({
))}
</div>
<div className="mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
<div className="mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:items-center sm:justify-end">
{busy && importProgress ? (
<p className="text-center text-sm text-slate-400 sm:mr-auto" role="status" aria-live="polite">
Importing {importProgress.done} of {importProgress.total}
</p>
) : null}
<button className="secondary-button justify-center" type="button" onClick={onClose}>
Cancel
</button>