Merge w11/perf into release/v1.18.11

Serve achievements via SWR and precompute Berlin day-keys to kill the per-row
Intl cost; switch dashboard/summary to cachedSwr; lazy-load three charts that
bypassed the dynamic boundary.
This commit is contained in:
Marc-André Bombeck committed 2026-06-20 19:05:27 +02:00
commit 6975849bec
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