perf(gamification,dashboard,insights): cut achievements cold rebuilds and lazy-load three charts

Achievements: the bucket was 60 s hard-TTL while the app-wide unlock
notifier polls every 2 minutes, so every poll landed on an expired
entry and fired a cold ~400 ms rebuild in the foreground. Add a
10-minute stale-while-revalidate window and read the route through
cachedSwr; the route already persists pendingUnlocks outside the cached
factory, so a stale serve never skips an unlock write.

Achievements builder: the vitals array ran through several full-array
passes, each re-deriving a Berlin day-key (and hour) per row via
Intl.DateTimeFormat. Compute each row's day-key once and thread the
parallel array into the green-day, weekly-consistency, engagement, and
hidden passes; the hidden pass reuses a single precomputed hour array.
Results are byte-identical to the per-row derivation (parity test added).

dashboard/summary: read through cachedSwr so the iOS-polled endpoint
serves its existing 1 h stale window instead of re-paying the cold
rebuild every 60 s on the plain cached path.

Charts: route the three recharts consumers that bypassed the shared
next/dynamic boundary (lab biomarker chart, sleep stage stacked bar,
recommendation rationale charts) through dynamic imports with matching
loading skeletons, taking recharts off the first-load JS for
/labs/[id], /insights/sleep, and the insights recommendations surface.
This commit is contained in:
Marc-André Bombeck committed 2026-06-20 19:04:31 +02:00
1 parent 4aebaaf399
commit f3af5d361a
10 files changed
+357 -35

No files matched your search

@@ -33,6 +33,29 @@ vi.mock("@/components/charts/mood-chart", () => ({
),
}));
// v1.18.11 (W5 perf) — the rationale charts now route through `next/dynamic`
// so recharts is off the insights first-load JS. Under `renderToStaticMarkup`
// a `dynamic(..., { ssr:false })` boundary paints its loading skeleton (it
// never resolves the lazy chunk synchronously). For the chart-SELECTION
// assertions below we stub `next/dynamic` to a passthrough that renders the
// (already-mocked) underlying chart component eagerly, so the test still
// verifies the card picks the right chart kind for a metric. Production keeps
// the real lazy boundary + skeleton.
vi.mock("next/dynamic", async () => {
const health = await import("@/components/charts/health-chart");
const mood = await import("@/components/charts/mood-chart");
return {
default: (loader: unknown) => {
// Identify which chart the loader targets by its source text.
const src = String(loader);
const Comp = (
src.includes("mood-chart") ? mood.MoodChart : health.HealthChart
) as React.ComponentType<Record<string, unknown>>;
return (props: Record<string, unknown>) => <Comp {...props} />;
},
};
});
vi.mock("@tanstack/react-query", () => ({
useQuery: () => ({ data: null, isLoading: false }),
useMutation: () => ({
@@ -1,6 +1,6 @@
"use client";
import { useState } from "react";
import { useState, type ComponentProps } from "react";
import { ChevronDown, ChevronUp, ExternalLink } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { useTranslations } from "@/lib/i18n/context";
@@ -9,8 +9,10 @@ import type {
InsightRecommendation,
InsightRecommendationRationale,
} from "@/lib/ai/types";
import { HealthChart } from "@/components/charts/health-chart";
import { MoodChart } from "@/components/charts/mood-chart";
import dynamic from "next/dynamic";
import { ChartErrorBoundary } from "@/components/charts/chart-error-state";
import { ChartSkeleton } from "@/components/charts/chart-skeleton";
import { importWithRetry } from "@/lib/retry-import";
import { getMedicalReferenceById } from "@/lib/ai/medical-references";
import { stripChartTokens } from "@/lib/insights/chart-tokens";
import type { Locale } from "@/lib/i18n/config";
@@ -21,6 +23,40 @@ import {
} from "./recommendation-feedback";
import { ConfidenceMeter } from "./confidence-meter";
/**
* v1.18.11 (W5 perf) — route both rationale mini-charts through
* `next/dynamic` so recharts no longer rides the main insights surface's
* first-load JS. Each chart only paints inside an expanded rationale block,
* so deferring it costs nothing visible and matches the loading skeleton
* the canonical `health-chart-dynamic.tsx` boundary uses (`mini` here,
* since both charts render with the `mini` prop in the rationale card).
*/
const HealthChartLazy = dynamic(
() =>
importWithRetry(() => import("@/components/charts/health-chart")).then(
(mod) => ({ default: mod.HealthChart }),
),
{ ssr: false, loading: () => <ChartSkeleton mini /> },
);
const HealthChart = (props: ComponentProps<typeof HealthChartLazy>) => (
<ChartErrorBoundary>
<HealthChartLazy {...props} />
</ChartErrorBoundary>
);
const MoodChartLazy = dynamic(
() =>
importWithRetry(() => import("@/components/charts/mood-chart")).then(
(mod) => ({ default: mod.MoodChart }),
),
{ ssr: false, loading: () => <ChartSkeleton mini /> },
);
const MoodChart = (props: ComponentProps<typeof MoodChartLazy>) => (
<ChartErrorBoundary>
<MoodChartLazy {...props} />
</ChartErrorBoundary>
);
/**
* Collapsible recommendation card with severity badge, confidence
* meter, and an expandable rationale block (window / compared-to /
+28 -4
View File
@@ -1,6 +1,7 @@
"use client";
import Link from "next/link";
import type { ComponentProps } from "react";
import { Moon } from "lucide-react";
import { useAuth } from "@/hooks/use-auth";
@@ -10,12 +11,35 @@ import { useAnalyticsQuery } from "@/lib/queries/use-analytics-query";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { EmptyState } from "@/components/ui/empty-state";
import {
SleepStageStackedBar,
type SleepStageBreakdown,
} from "./sleep-stage-stacked-bar";
import dynamic from "next/dynamic";
import { ChartErrorBoundary } from "@/components/charts/chart-error-state";
import { ChartSkeleton } from "@/components/charts/chart-skeleton";
import { importWithRetry } from "@/lib/retry-import";
import type { SleepStageBreakdown } from "./sleep-stage-stacked-bar";
import { SleepDurationChart } from "./sleep-duration-chart";
// v1.18.11 (W5 perf) — the stage stacked bar is the only recharts consumer
// on this page (the duration chart already routes through
// `health-chart-dynamic`). Defer it through `next/dynamic` so recharts is
// off `/insights/sleep`'s first-load JS; the `<ChartSkeleton>` loading shell
// matches the card the bar paints so the layout stays stable.
const SleepStageStackedBarLazy = dynamic(
() =>
importWithRetry(() => import("./sleep-stage-stacked-bar")).then((mod) => ({
default: mod.SleepStageStackedBar,
})),
{ ssr: false, loading: () => <ChartSkeleton /> },
);
function SleepStageStackedBar(
props: ComponentProps<typeof SleepStageStackedBarLazy>,
) {
return (
<ChartErrorBoundary>
<SleepStageStackedBarLazy {...props} />
</ChartErrorBoundary>
);
}
/**
* v1.4.25 W4c — Sleep insights composition.
*
+27 -1
View File
@@ -18,8 +18,13 @@ import { formatLabValue } from "@/lib/labs/format-value";
import { useTranslations } from "@/lib/i18n/context";
import { queryKeys } from "@/lib/query-keys";
import dynamic from "next/dynamic";
import type { ComponentProps } from "react";
import { ChartErrorBoundary } from "@/components/charts/chart-error-state";
import { ChartSkeleton } from "@/components/charts/chart-skeleton";
import { importWithRetry } from "@/lib/retry-import";
import { BiomarkerForm } from "./biomarker-form";
import { LabBiomarkerChart } from "./lab-biomarker-chart";
import { LabForm } from "./lab-form";
import { LabHistoryList } from "./lab-history-list";
import { ReferenceRangeBadge } from "./reference-range-badge";
@@ -29,6 +34,27 @@ import type {
LabResultListResponse,
} from "./types";
// v1.18.11 (W5 perf) — defer the recharts biomarker chart through
// `next/dynamic` so recharts is off `/labs/[biomarkerId]`'s first-load JS.
// The chart only paints once the reading list resolves; the `<ChartSkeleton>`
// loading shell matches the in-card chart footprint so the layout is stable.
const LabBiomarkerChartLazy = dynamic(
() =>
importWithRetry(() => import("./lab-biomarker-chart")).then((mod) => ({
default: mod.LabBiomarkerChart,
})),
{ ssr: false, loading: () => <ChartSkeleton /> },
);
function LabBiomarkerChart(
props: ComponentProps<typeof LabBiomarkerChartLazy>,
) {
return (
<ChartErrorBoundary>
<LabBiomarkerChartLazy {...props} />
</ChartErrorBoundary>
);
}
/**
* v1.18.1 — per-biomarker detail: heading + current-value badge, the proper
* dashboard-style chart with the reference band, and the editable reading