mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
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:
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 /
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user