diff --git a/src/app/insights/pulse/page.tsx b/src/app/insights/pulse/page.tsx index 81c888314..f99f89681 100644 --- a/src/app/insights/pulse/page.tsx +++ b/src/app/insights/pulse/page.tsx @@ -1,7 +1,7 @@ "use client"; import Link from "next/link"; -import { ArrowRight, Gauge, Heart } from "lucide-react"; +import { Gauge, Heart } from "lucide-react"; import { useAuth } from "@/hooks/use-auth"; import { useInsightsAnalytics } from "@/hooks/use-insights-analytics"; @@ -19,9 +19,9 @@ import { MeasurementDiversityNudge } from "@/components/insights/measurement-div import { MetricTargetSummary } from "@/components/insights/metric-target-summary"; import { EcgCrossLink } from "@/components/insights/ecg-cross-link"; import { SubPageShell } from "@/components/insights/sub-page-shell"; -import { TileHeader } from "@/components/insights/tile-header"; import dynamic from "next/dynamic"; import { ChartSkeleton } from "@/components/charts/chart-skeleton"; +import { CrossLinkCard } from "@/components/insights/cross-link-card"; // Load the intraday area chart through the shared chart-runtime boundary (the // one recharts chunk), not as a direct import — a direct `from "recharts"` @@ -199,27 +199,17 @@ export default function InsightsPulsPage() { the full chart plus the plain-language assessment — rather than duplicating a second, divergent VO₂ max view here. - v1.12.8 — the card now leads with the canonical `` so the - `Gauge` glyph reads in the foreground colour at the same size and - position as every other tile header, with the body + CTA beneath. */} + The card is the shared `CrossLinkCard`: the canonical `TileHeader` + (foreground `Gauge` glyph), the body and the CTA beneath. */} {hasVo2 ? ( - - - - {t("insights.vo2Max.cardioLinkBody")} - - - {t("insights.vo2Max.cardioLinkCta")} - - + slot="vo2-cardio-link" + icon={Gauge} + title={t("insights.vo2Max.cardioLinkTitle")} + body={t("insights.vo2Max.cardioLinkBody")} + cta={t("insights.vo2Max.cardioLinkCta")} + /> ) : null} ); diff --git a/src/components/insights/cross-link-card.tsx b/src/components/insights/cross-link-card.tsx new file mode 100644 index 000000000..b738783af --- /dev/null +++ b/src/components/insights/cross-link-card.tsx @@ -0,0 +1,68 @@ +import type { CSSProperties } from "react"; +import Link from "next/link"; +import { ArrowRight, type LucideIcon } from "lucide-react"; + +import { Card } from "@/components/ui/card"; +import { TileHeader } from "@/components/insights/tile-header"; +import { cn } from "@/lib/utils"; + +/** + * A whole card that points to another page: the tile header, one meta line, + * and the call to action. The pulse page's way to VO₂ max and the ECG pointer + * on the heart pages were two hand-rolled copies of it, one of them without + * the `md:p-6` step every other card on the page has. + * + * The link is the outer element and carries the focus ring; the surface is a + * `Card`, so padding, radius and shadow are the card contract. The compact + * card rhythm (`gap-1.5`) keeps the three lines together. + */ +export function CrossLinkCard({ + href, + slot, + icon, + title, + body, + cta, + className, + cardClassName, + style, +}: { + href: string; + slot: string; + icon: LucideIcon; + title: string; + body: string; + cta: string; + className?: string; + /** Classes on the card surface (an identity edge such as `metric-accent`). */ + cardClassName?: string; + style?: CSSProperties; +}) { + return ( + + + + + {body} + + + {cta} + + + + ); +} diff --git a/src/components/insights/ecg-cross-link.tsx b/src/components/insights/ecg-cross-link.tsx index d0d30407c..b8794420a 100644 --- a/src/components/insights/ecg-cross-link.tsx +++ b/src/components/insights/ecg-cross-link.tsx @@ -1,11 +1,9 @@ "use client"; -import Link from "next/link"; -import { ArrowRight, HeartPulse } from "lucide-react"; +import { HeartPulse } from "lucide-react"; import { useTranslations } from "@/lib/i18n/context"; -import { cn } from "@/lib/utils"; -import { TileHeader } from "@/components/insights/tile-header"; +import { CrossLinkCard } from "@/components/insights/cross-link-card"; import { useEcgList } from "./use-ecg-list"; /** @@ -61,27 +59,20 @@ export function EcgCrossLink({ enabled = true, className }: EcgCrossLinkProps) { : null; return ( - - - - {resultText ? `${countText} ${resultText}` : countText} - - - {t("insights.ecg.crossLink.cta")} - - + /> ); }