mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
refactor(coach): unify launch glyph and propagate medication row shape
The per-target Coach button rode the MessageCircle glyph; hero strip, inline pill, layout FAB and daily-briefing CTA all paint Sparkles. The lone outlier swaps to Sparkles so the Coach-launch glyph reads as one icon vocabulary across the app. Separately, the insights/medikamente per-medication card painted its own header recipe (text-sm title with a Pill glyph and dose on line 2); routing the same row through MedicationCardHeader matches the canonical FB-G1 two-line shape used on the medications page. The streak chip rides the stateBadges slot.
This commit is contained in:
1 parent
e5cb74b459
commit
9768066354
3 files changed
+39
-31
No files matched your search
@@ -8,7 +8,7 @@ import { useAuth } from "@/hooks/use-auth";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { EmptyState } from "@/components/ui/empty-state";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { ComplianceHeatmap } from "@/components/charts/compliance-heatmap";
|
||||
@@ -16,6 +16,7 @@ import { CoachLaunchButton } from "@/components/insights/coach-launch-button";
|
||||
import { InsightStatusCard } from "@/components/insights/insight-status-card";
|
||||
import { SubPageShell } from "@/components/insights/sub-page-shell";
|
||||
import { TherapyTimeline } from "@/components/insights/therapy-timeline";
|
||||
import { MedicationCardHeader } from "@/components/medications/MedicationCardHeader";
|
||||
|
||||
/**
|
||||
* v1.4.25 W4 — `/insights/medikamente`.
|
||||
@@ -155,31 +156,31 @@ export default function InsightsMedikamentePage() {
|
||||
>
|
||||
{medications.map((med) => {
|
||||
const medicationSummary = medicationSummaryById.get(med.id);
|
||||
// UI-H3 — route every medication-list row (medications page
|
||||
// + insights/medikamente) through `<MedicationCardHeader>`
|
||||
// so the surface stays one shape: `{name} {dose}` on line 1
|
||||
// + outline category badge on line 2. The streak chip rides
|
||||
// the `stateBadges` slot so a strong streak still surfaces.
|
||||
const categoryLabel =
|
||||
med.category === "BLOOD_PRESSURE"
|
||||
? t("medications.categoryBloodPressure")
|
||||
: med.category === "VITAMIN"
|
||||
? t("medications.categoryVitamin")
|
||||
: t("medications.categoryOther");
|
||||
const streakBadge =
|
||||
med.streak > 0 ? (
|
||||
<Badge variant="outline" className="shrink-0 text-xs">
|
||||
{t("insights.dayStreak", { count: med.streak })}
|
||||
</Badge>
|
||||
) : null;
|
||||
return (
|
||||
<Card key={med.id}>
|
||||
<CardHeader className="pb-2">
|
||||
{/* v1.4.27 MB7 / MA2-F8 — `min-w-0` on the title
|
||||
wrapper + `truncate` on the CardTitle keeps long
|
||||
medication names (e.g. "Semaglutid Wegovy 0.5 mg
|
||||
weekly") from pushing the streak badge off the
|
||||
card on Galaxy Fold / Pixel 5. The Pill icon
|
||||
+ CardTitle pair shrinks to fit; the badge stays
|
||||
on the right at its natural width. */}
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<Pill className="text-dracula-orange h-4 w-4 shrink-0" />
|
||||
<CardTitle className="truncate text-sm font-medium">
|
||||
{med.name}
|
||||
</CardTitle>
|
||||
</div>
|
||||
{med.streak > 0 && (
|
||||
<Badge variant="outline" className="shrink-0 text-xs">
|
||||
{t("insights.dayStreak", { count: med.streak })}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-muted-foreground text-xs">{med.dose}</p>
|
||||
</CardHeader>
|
||||
<MedicationCardHeader
|
||||
name={med.name}
|
||||
dose={med.dose}
|
||||
categoryLabel={categoryLabel}
|
||||
stateBadges={streakBadge}
|
||||
/>
|
||||
<CardContent className="space-y-3">
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex justify-between text-xs">
|
||||
|
||||
@@ -6,8 +6,8 @@ import { TargetCoachButton } from "../target-coach-button";
|
||||
|
||||
/**
|
||||
* v1.4.25 W3e — gate test for the per-card Coach CTA. The button
|
||||
* MUST disappear when `aiEnabled` is false (Marc directive: no
|
||||
* broken-button state for users with no AI provider configured).
|
||||
* MUST disappear when `aiEnabled` is false (the maintainer's rule:
|
||||
* no broken-button state for users with no provider configured).
|
||||
*/
|
||||
|
||||
function render(props: Parameters<typeof TargetCoachButton>[0]) {
|
||||
@@ -33,8 +33,12 @@ describe("<TargetCoachButton>", () => {
|
||||
// still surface "Ask Coach" / "Coach fragen".
|
||||
expect(html).toContain('aria-label="Ask Coach"');
|
||||
expect(html).toContain('title="Ask Coach"');
|
||||
// No visible text label — only the chat-bubble glyph.
|
||||
// No visible text label — only the Sparkles glyph.
|
||||
expect(html).not.toMatch(/>Ask Coach</);
|
||||
// UI-H2 — the Coach-launch glyph is one Sparkles vocabulary
|
||||
// across hero strip, inline pill, layout FAB and per-card icon.
|
||||
expect(html).toMatch(/lucide-sparkles/i);
|
||||
expect(html).not.toMatch(/lucide-message-circle/i);
|
||||
});
|
||||
|
||||
it("lifts the tap target to the 44 px WCAG 2.5.5 floor", () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { MessageCircle } from "lucide-react";
|
||||
import { Sparkles } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -24,8 +24,11 @@ import type { CoachScope, CoachScopeSource } from "@/lib/ai/coach/types";
|
||||
* a status pill, range bar, consistency strip, headline number, edit
|
||||
* cog and a source link, the labelled pill dominated the visual
|
||||
* hierarchy. Dropping the label retains the affordance via the
|
||||
* chat-bubble glyph + `aria-label` while shrinking the optical weight
|
||||
* to a per-card icon button.
|
||||
* Sparkles glyph + `aria-label` while shrinking the optical weight
|
||||
* to a per-card icon button. UI-H2 — the glyph is the same Sparkles
|
||||
* the hero strip, the inline pill and the layout-level FAB use, so
|
||||
* the per-card Coach launch reads on one icon vocabulary across the
|
||||
* app.
|
||||
*/
|
||||
export interface TargetCoachButtonProps {
|
||||
/**
|
||||
@@ -85,7 +88,7 @@ export function TargetCoachButton({
|
||||
// Coach drawer cluster). Glyph stays `size-4`.
|
||||
className={cn("min-h-11 min-w-11", className)}
|
||||
>
|
||||
<MessageCircle className="size-4" aria-hidden="true" />
|
||||
<Sparkles className="size-4" aria-hidden="true" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user