mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 05:42:19 +00:00
refactor(charts): finish HealthChartDynamic migration on trends row
Two consumers (trends-row.tsx + vo2-max-chart-row.tsx) still hand-rolled the same dynamic() block plus a degenerate local ChartSkeleton shadow that bypassed the canonical primitive. Both files now import HealthChartDynamic; the local ChartSkeleton drops from trends-row and vo2-max-chart-row in favour of the shared chart-skeleton module.
This commit is contained in:
1 parent
9768066354
commit
29e9f958f7
3 files changed
+24
-50
No files matched your search
@@ -5,15 +5,15 @@ import dynamic from "next/dynamic";
|
||||
import { ChartSkeleton } from "@/components/charts/chart-skeleton";
|
||||
|
||||
/**
|
||||
* v1.4.28 R3d (BK-F-M2) — single `next/dynamic` boundary for the
|
||||
* v1.4.28 (BK-F-M2) — single `next/dynamic` boundary for the
|
||||
* canonical `<HealthChart>` component.
|
||||
*
|
||||
* Pre-fix, six call sites (`src/app/page.tsx`, the five insights
|
||||
* sub-pages, and `sleep-duration-chart.tsx`) duplicated the same
|
||||
* Pre-fix, eight call sites (`src/app/page.tsx`, five insights
|
||||
* sub-pages, `sleep-duration-chart.tsx`, `trends-row.tsx`, and
|
||||
* `vo2-max-chart-row.tsx`) duplicated the same
|
||||
* `dynamic(() => import("@/components/charts/health-chart"), { ... })`
|
||||
* incantation. The duplication meant the loading skeleton + `ssr: false`
|
||||
* flag had to be re-wired on every caller (R1.2 H4 paid for this once;
|
||||
* this commit prevents future regressions).
|
||||
* flag had to be re-wired on every caller.
|
||||
*
|
||||
* Re-exporting through this module gives every consumer the same
|
||||
* pre-configured `<ChartSkeleton>`-backed lazy boundary. Consumers
|
||||
|
||||
@@ -3,52 +3,36 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { ChartSkeleton } from "@/components/charts/chart-skeleton";
|
||||
import { HealthChartDynamic } from "@/components/charts/health-chart-dynamic";
|
||||
import {
|
||||
TrendAnnotation,
|
||||
type TrendAnnotationConfidenceBand,
|
||||
} from "./trend-annotation";
|
||||
|
||||
/**
|
||||
* v1.4.20 phase B3 — Trends row.
|
||||
*
|
||||
* Renders three small charts (BP / Weight / Mood) above a one-sentence
|
||||
* AI annotation each. Recharts is ~108 KiB Brotli, so we mirror the
|
||||
* `<ScatterCorrelationChart>` defer-load pattern from `/insights` for
|
||||
* the two HealthChart-backed cards. The MoodChart already has its own
|
||||
* fetch wired so it composes naturally.
|
||||
* Trends row — three small charts (BP / Weight / Mood) above a
|
||||
* one-sentence assessment each. Recharts is ~108 KiB Brotli, so the
|
||||
* three charts ride the shared lazy-import boundary
|
||||
* (`<HealthChartDynamicDynamic>` for BP + weight; MoodChart still resolves
|
||||
* locally because no shared re-export exists yet for it).
|
||||
*
|
||||
* Layout:
|
||||
* - `<md`: single column, full width
|
||||
* - `>=md`: 3-up grid, equal column tracks
|
||||
*
|
||||
* Annotations come from `trendAnnotations.{bp,weight,mood}` on the AI
|
||||
* advisor payload. When a metric's annotation is null, the
|
||||
* Annotations come from `trendAnnotations.{bp,weight,mood}` on the
|
||||
* Insights payload. When a metric's annotation is null, the
|
||||
* `<TrendAnnotation>` empty state hints at the gap without breaking
|
||||
* the row's visual rhythm.
|
||||
*/
|
||||
|
||||
// v1.4.22 W5 reconcile (S-04) — both `dynamic()` calls used the same
|
||||
// pulse-skeleton placeholder. Hoist so the loading affordance stays
|
||||
// in lock-step (cleared against `feedback_charts_visual_identity.md`
|
||||
// — placeholder only, no chart visual change).
|
||||
const ChartSkeleton = () => (
|
||||
<div className="bg-muted/40 h-[220px] w-full animate-pulse rounded-md motion-reduce:animate-none" />
|
||||
);
|
||||
|
||||
const HealthChart = dynamic(
|
||||
() =>
|
||||
import("@/components/charts/health-chart").then((mod) => ({
|
||||
default: mod.HealthChart,
|
||||
})),
|
||||
{ ssr: false, loading: ChartSkeleton },
|
||||
);
|
||||
|
||||
const MoodChart = dynamic(
|
||||
() =>
|
||||
import("@/components/charts/mood-chart").then((mod) => ({
|
||||
default: mod.MoodChart,
|
||||
})),
|
||||
{ ssr: false, loading: ChartSkeleton },
|
||||
{ ssr: false, loading: () => <ChartSkeleton /> },
|
||||
);
|
||||
|
||||
interface TrendsRowProps {
|
||||
@@ -127,14 +111,14 @@ export function TrendsRow({ annotations, confidence }: TrendsRowProps) {
|
||||
data-metric="bp"
|
||||
className="flex h-full flex-col gap-2 md:min-h-[300px]"
|
||||
>
|
||||
{/* v1.4.28 R3c-Insights — fixed chart slot. `<HealthChart>`
|
||||
{/* v1.4.28 R3c-Insights — fixed chart slot. `<HealthChartDynamic>`
|
||||
mini paints its own 140 px chart band; this wrapper pins
|
||||
the total chart-envelope height so the mood tile's Card
|
||||
wrapper (which carries a heavier shell on a default
|
||||
shadcn Card) lines up with the BP/weight tiles' lighter
|
||||
shell. Both chart types ship the same data-slot now. */}
|
||||
<div data-slot="trends-row-chart-slot" className="shrink-0">
|
||||
<HealthChart
|
||||
<HealthChartDynamic
|
||||
types={["BLOOD_PRESSURE_SYS", "BLOOD_PRESSURE_DIA"]}
|
||||
title={t("charts.bloodPressure")}
|
||||
colors={["#ff79c6", "#8be9fd"]}
|
||||
@@ -156,7 +140,7 @@ export function TrendsRow({ annotations, confidence }: TrendsRowProps) {
|
||||
className="flex h-full flex-col gap-2 md:min-h-[300px]"
|
||||
>
|
||||
<div data-slot="trends-row-chart-slot" className="shrink-0">
|
||||
<HealthChart
|
||||
<HealthChartDynamic
|
||||
types={["WEIGHT"]}
|
||||
title={t("charts.weight")}
|
||||
colors={["#bd93f9"]}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { ArrowDown, ArrowRight, ArrowUp, Gauge, Minus } from "lucide-react";
|
||||
|
||||
import { useTranslations, useFormatters } from "@/lib/i18n/context";
|
||||
import type { ComparisonBaseline } from "@/lib/dashboard-layout";
|
||||
import type { DataSummary } from "@/lib/analytics/trends";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { HealthChartDynamic } from "@/components/charts/health-chart-dynamic";
|
||||
|
||||
/**
|
||||
* v1.4.25 W16a — VO2 max chart-row card for `/insights/puls`.
|
||||
@@ -22,22 +22,12 @@ import { cn } from "@/lib/utils";
|
||||
* The card stays mounted even when `summary` reports zero samples — a
|
||||
* brand-new account gets the "no data yet" hint rather than a missing
|
||||
* surface, matching the dashboard tile's opt-in pattern.
|
||||
*
|
||||
* The HealthChart mounts behind the shared `<HealthChartDynamic>`
|
||||
* boundary so the lazy-import + skeleton-loading state stays in
|
||||
* lock-step with the other Insights chart consumers.
|
||||
*/
|
||||
|
||||
// Mirror the Recharts defer-load + skeleton used by `<TrendsRow>` so the
|
||||
// VO2 chart-row doesn't blow up the Insights bundle.
|
||||
const ChartSkeleton = () => (
|
||||
<div className="bg-muted/40 h-[220px] w-full animate-pulse rounded-md motion-reduce:animate-none" />
|
||||
);
|
||||
|
||||
const HealthChart = dynamic(
|
||||
() =>
|
||||
import("@/components/charts/health-chart").then((mod) => ({
|
||||
default: mod.HealthChart,
|
||||
})),
|
||||
{ ssr: false, loading: ChartSkeleton },
|
||||
);
|
||||
|
||||
interface Vo2MaxChartRowProps {
|
||||
/**
|
||||
* Analytics summary for `MeasurementType=VO2_MAX`. Null is allowed so
|
||||
@@ -205,7 +195,7 @@ export function Vo2MaxChartRow({
|
||||
the dashboard VO2 tile and from the pulse chart sitting above
|
||||
it on the same page. */}
|
||||
{hasData ? (
|
||||
<HealthChart
|
||||
<HealthChartDynamic
|
||||
chartKey="vo2Max"
|
||||
types={["VO2_MAX"]}
|
||||
title={title}
|
||||
|
||||
Reference in new issue
Block a user