fix(insights): style the fallback trend caption like the advisor caption

Extract the advisor caption's bordered card + icon + foreground prose
shell into a shared TrendCaptionCard and render the additive-metric
fallback caption through it, so the Trends-row captions read with one
typography instead of the plain muted line standing out below the
chart. The two paths now share a single presentational shell and can't
drift apart.
This commit is contained in:
Marc-André Bombeck committed 2026-06-02 10:00:48 +02:00
1 parent be90c4b9ad
commit ae82b3e484
2 files changed
+74 -37

No files matched your search

+63 -30
View File
@@ -43,6 +43,55 @@ export type TrendAnnotationConfidenceBand = "low" | "moderate" | "high";
export type TrendAnnotationStatus = "pending" | "needs_data" | "generated";
/**
* Shared presentational shell for a filled trend caption — the bordered
* card + Sparkles affordance + `text-foreground` prose treatment. Both
* the advisor-authored annotation (the legacy triple) and the additive
* metric's standard description (`captionKey`) render through this shell
* so the Trends row reads with a single typographic rhythm and the two
* paths can't drift apart. `children` carries the line(s) below the
* caption prose (e.g. a confidence chip). `slot` / `metric` pass through
* to the wrapper's `data-*` hooks so each call site keeps its own
* test-stable selector.
*/
export function TrendCaptionCard({
text,
slot,
metric,
children,
}: {
text: string;
slot: string;
metric: string;
children?: React.ReactNode;
}) {
return (
<div
data-slot={slot}
data-metric={metric}
className="border-border/60 bg-card/40 flex items-start gap-2 rounded-md border p-3"
>
<Sparkles
className="text-dracula-purple mt-0.5 h-3.5 w-3.5 shrink-0"
aria-hidden="true"
/>
<div className="min-w-0 flex-1 space-y-1">
{/* v1.4.28 R3c-Insights — `line-clamp-3` bounds caption
variance (FB-K2). A 1-sentence BP annotation paints
~3 lines wrapped; a 4-line mood annotation used to push
the cell taller, which `auto-rows-fr` propagated to
every neighbour cell. With the clamp the longest
annotation ends with an ellipsis at 3 lines and the
row stays at a single visual rhythm. */}
<p className="text-foreground line-clamp-3 text-xs leading-snug">
{text}
</p>
{children}
</div>
</div>
);
}
interface TrendAnnotationProps {
/** The metric this annotation describes. Drives the empty-state copy. */
metric: "bp" | "weight" | "mood";
@@ -122,36 +171,20 @@ export function TrendAnnotation({
}
return (
<div
data-slot="trend-annotation"
data-metric={metric}
className="border-border/60 bg-card/40 flex items-start gap-2 rounded-md border p-3"
<TrendCaptionCard
slot="trend-annotation"
metric={metric}
text={stripChartTokens(annotation)}
>
<Sparkles
className="text-dracula-purple mt-0.5 h-3.5 w-3.5 shrink-0"
aria-hidden="true"
/>
<div className="min-w-0 flex-1 space-y-1">
{/* v1.4.28 R3c-Insights — `line-clamp-3` bounds caption
variance (FB-K2). A 1-sentence BP annotation paints
~3 lines wrapped; a 4-line mood annotation used to push
the cell taller, which `auto-rows-fr` propagated to
every neighbour cell. With the clamp the longest
annotation ends with an ellipsis at 3 lines and the
row stays at a single visual rhythm. */}
<p className="text-foreground line-clamp-3 text-xs leading-snug">
{stripChartTokens(annotation)}
</p>
{confidence ? (
<Badge
data-slot="trend-annotation-confidence"
variant="outline"
className={cn("text-[10px]", CONFIDENCE_BADGE_CLASS[confidence])}
>
{t(CONFIDENCE_LABEL_KEY[confidence])}
</Badge>
) : null}
</div>
</div>
{confidence ? (
<Badge
data-slot="trend-annotation-confidence"
variant="outline"
className={cn("text-[10px]", CONFIDENCE_BADGE_CLASS[confidence])}
>
{t(CONFIDENCE_LABEL_KEY[confidence])}
</Badge>
) : null}
</TrendCaptionCard>
);
}
+11 -7
View File
@@ -13,6 +13,7 @@ import {
} from "@/lib/insights/trend-chart-select";
import {
TrendAnnotation,
TrendCaptionCard,
type TrendAnnotationConfidenceBand,
type TrendAnnotationStatus,
} from "./trend-annotation";
@@ -216,13 +217,16 @@ export function TrendsRow({
// v1.8.6 W8 — additive metrics carry no advisor
// annotation. Paint the metric's standard one-line
// description so the card is never caption-less.
<p
data-slot="trends-row-caption"
data-metric={config.metric}
className="text-muted-foreground line-clamp-3 text-xs"
>
{t(config.captionKey)}
</p>
// v1.8.7 W-E — render that description through the same
// `<TrendCaptionCard>` shell the advisor caption uses, so
// the fallback caption shares the bordered card, Sparkles
// affordance, and `text-foreground` typography rather than
// standing out as a plain muted line.
<TrendCaptionCard
slot="trends-row-caption"
metric={config.metric}
text={t(config.captionKey)}
/>
)}
</div>
);