mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
fix(insights): normalize stat-strip and target panel header rhythm
The stat-strip and target-summary panels were bare card-styled divs with a flat space-y gap, so their tile header sat at a different offset from the Card-wrapped tiles (assessment, mood, medication) on the same subpage spine. Wrap both in Card / CardHeader pb-2 / CardContent so the header-to-body offset matches. All content and data-slots are preserved; the panel semantics ride on role + aria-label. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f1f6018f6e
commit
036370fc88
2 files changed
+63
-45
No files matched your search
@@ -4,6 +4,7 @@ import type { ComponentType } from "react";
|
||||
import { Sigma } from "lucide-react";
|
||||
|
||||
import { useFormatters, useTranslations } from "@/lib/i18n/context";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { TileHeader } from "@/components/insights/tile-header";
|
||||
import type { DataSummary } from "@/lib/analytics/trends";
|
||||
|
||||
@@ -100,35 +101,44 @@ export function MetricStatStrip({
|
||||
];
|
||||
|
||||
return (
|
||||
<section
|
||||
// Card-wrapped so the header-to-body offset matches the `<CardHeader
|
||||
// pb-2>` tiles on the same subpage spine (assessment, mood, medication)
|
||||
// rather than the old bare-div `space-y-3`. The grid keeps its own
|
||||
// data-slots; the section semantics ride on `role` + `aria-label`.
|
||||
<Card
|
||||
data-slot="metric-stat-strip"
|
||||
role="group"
|
||||
aria-label={
|
||||
seriesLabel
|
||||
? `${t("insights.subPage.stats.label")} — ${seriesLabel}`
|
||||
: t("insights.subPage.stats.label")
|
||||
}
|
||||
className="bg-card border-border space-y-3 rounded-xl border p-4"
|
||||
className="gap-2 py-4 md:gap-3 md:py-5"
|
||||
>
|
||||
{seriesLabel ? (
|
||||
<TileHeader icon={icon ?? Sigma} title={seriesLabel} />
|
||||
<CardHeader className="pb-2">
|
||||
<TileHeader icon={icon ?? Sigma} title={seriesLabel} />
|
||||
</CardHeader>
|
||||
) : null}
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4 [&>div]:min-h-[56px]">
|
||||
{cells.map((cell) => (
|
||||
<div
|
||||
key={cell.key}
|
||||
data-slot="metric-stat"
|
||||
data-stat={cell.key}
|
||||
className="space-y-1"
|
||||
>
|
||||
<p className="text-muted-foreground text-[10px] tracking-wide uppercase">
|
||||
{cell.label}
|
||||
</p>
|
||||
<p className="text-lg font-semibold tabular-nums">
|
||||
{format(cell.value)}
|
||||
</p>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4 [&>div]:min-h-[56px]">
|
||||
{cells.map((cell) => (
|
||||
<div
|
||||
key={cell.key}
|
||||
data-slot="metric-stat"
|
||||
data-stat={cell.key}
|
||||
className="space-y-1"
|
||||
>
|
||||
<p className="text-muted-foreground text-[10px] tracking-wide uppercase">
|
||||
{cell.label}
|
||||
</p>
|
||||
<p className="text-lg font-semibold tabular-nums">
|
||||
{format(cell.value)}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { useAuth } from "@/hooks/use-auth";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { convertGlucose, resolveGlucoseUnit } from "@/lib/glucose";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { RangeBar } from "@/components/targets/range-bar";
|
||||
import { ConsistencyStrip } from "@/components/targets/consistency-strip";
|
||||
import { TargetStatusPill } from "@/components/targets/target-status-pill";
|
||||
@@ -297,10 +298,14 @@ function TargetReferencePanel({
|
||||
target.consistency7d.length > 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
// Card-wrapped so the header-to-body offset matches the `<CardHeader
|
||||
// pb-2>` tiles on the same subpage spine rather than the old bare-div
|
||||
// `space-y-3`. The body keeps its inter-row `space-y-3` inside
|
||||
// `CardContent`; every data-slot is preserved.
|
||||
<Card
|
||||
data-slot="metric-target-summary"
|
||||
data-target-type={target.type}
|
||||
className="bg-card border-border space-y-3 rounded-xl border p-4"
|
||||
className="gap-2 py-4 md:gap-3 md:py-5"
|
||||
>
|
||||
{/* Row 1: the canonical tile header — a single-word "Ziel" / "Target"
|
||||
heading (the range numbers live on the range bar below, so the
|
||||
@@ -309,27 +314,29 @@ function TargetReferencePanel({
|
||||
per-context label (Fasting / Postprandial / …) carries real,
|
||||
non-duplicative info, so it stays as a sub-caption under the
|
||||
header. */}
|
||||
<TileHeader
|
||||
icon={Target}
|
||||
title={t("insights.target.heading")}
|
||||
right={
|
||||
target.classification ? (
|
||||
<TargetStatusPill
|
||||
classification={target.classification}
|
||||
range={range}
|
||||
unit={unit}
|
||||
source={target.source}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
{heading ? (
|
||||
<p className="text-muted-foreground text-[0.6875rem] font-medium tracking-[0.06em] uppercase">
|
||||
{heading}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
{/* Row 2: range bar — where today's value sits inside the band. */}
|
||||
<CardHeader className="pb-2">
|
||||
<TileHeader
|
||||
icon={Target}
|
||||
title={t("insights.target.heading")}
|
||||
right={
|
||||
target.classification ? (
|
||||
<TargetStatusPill
|
||||
classification={target.classification}
|
||||
range={range}
|
||||
unit={unit}
|
||||
source={target.source}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
{heading ? (
|
||||
<p className="text-muted-foreground text-[0.6875rem] font-medium tracking-[0.06em] uppercase">
|
||||
{heading}
|
||||
</p>
|
||||
) : null}
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{/* Row 2: range bar — where today's value sits inside the band. */}
|
||||
{target.current != null ? (
|
||||
<RangeBar
|
||||
value={target.current}
|
||||
@@ -393,7 +400,8 @@ function TargetReferencePanel({
|
||||
{t("targets.sourceLabel", { source: target.source })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user