refactor(dashboard): promote GLP-1 schedule dates to a header pill row and drop the green seam

The faint dracula-green left-seam on the outer wrapper was decoration
without a semantic tie to the schedule rows beside it. The Syringe-icon
in the title row already carries the 'active therapy' signal at a
glance.

Replace the <dl> grid with a soft-green pill bar that groups Last- and
Next-Injection as one cohesive 'therapy schedule' unit. Each pill keeps
its data-slot anchor (glp1-tile-last / glp1-tile-next) so existing
tests stay in lockstep; new test asserts the data-slot='glp1-tile-schedule'
wrapper renders and the border-l-dracula-green/60 + border-l-2 classes
no longer appear on the outer wrapper.
This commit is contained in:
Marc-André Bombeck committed 2026-05-15 16:44:39 +02:00
1 parent 1d8b80698e
commit 053d678ac7
2 files changed
+85 -34

No files matched your search

@@ -321,6 +321,46 @@ describe("<Glp1Tile>", () => {
);
});
it("v1.4.27 B1 — promotes the schedule to a pill row and drops the green seam", () => {
queryReturn = {
data: {
active: true,
medications: [
{
name: "Mounjaro",
genericName: "tirzepatide",
medicationId: "med-1",
currentDose: {
value: 7.5,
unit: "mg",
since: "2026-04-01",
weeksOnDose: 6,
},
doseHistory: [],
lastInjection: { date: "2026-05-10", site: null, weeksAgo: 0 },
nextInjection: { date: "2026-05-17", daysAway: 3 },
startWeight: 92.0,
currentWeight: 87.8,
weightDeltaKg: -4.2,
weightSeries: [{ date: "2026-05-13", weight: 87.8 }],
injectionDates: ["2026-05-10"],
},
],
},
isPending: false,
};
const html = render();
// Schedule wrapper is the new pill row, not the old <dl>.
expect(html).toContain('data-slot="glp1-tile-schedule"');
expect(html).not.toContain("<dl");
// Pills still carry the per-pill slot anchors the existing tests use.
expect(html).toContain('data-slot="glp1-tile-last"');
expect(html).toContain('data-slot="glp1-tile-next"');
// The green left-seam classes drop from the outer wrapper.
expect(html).not.toContain("border-l-dracula-green/60");
expect(html).not.toContain("border-l-2");
});
it("v1.4.27 B1 — surfaces the level-unavailable hint when no medicationId is wired", () => {
// Some legacy meds don't have a Medication.id surfaced through the
// dashboard route (the route returns medicationId: null). The
+45 -34
View File
@@ -246,11 +246,12 @@ export function Glp1Tile() {
data-medication-id={med.medicationId ?? ""}
className={cn(
"bg-card/65 relative overflow-hidden rounded-xl border px-4 py-4 shadow-sm backdrop-blur-sm",
// A faint green seam on the left edge so the tile reads as
// "active therapy" at a glance without needing to read the
// header. The same green is used for the injection-day markers
// below — visual consistency across the tile.
"border-l-dracula-green/60 border-l-2",
// v1.4.27 B1 — the green left-seam drops. The Syringe-icon in
// the title row already carries the "active therapy" signal;
// the seam was decoration without a semantic tie to the
// schedule dates next to it. The schedule pill row below now
// groups the two dates visually so the user reads them as one
// cohesive unit.
)}
>
<div className="flex items-center gap-2 pb-3">
@@ -280,43 +281,53 @@ export function Glp1Tile() {
)}
</div>
<dl className="text-muted-foreground grid grid-cols-1 gap-y-1 pb-3 text-xs sm:grid-cols-2 sm:gap-x-4">
{med.lastInjection && (
<div className="flex items-center gap-1.5">
<Calendar className="h-3 w-3 shrink-0" aria-hidden="true" />
<dt className="font-medium">
{t("dashboard.glp1.lastInjection")}:
</dt>
<dd
{/* v1.4.27 B1 — schedule pill row promotes the two injection
dates to a header band. Pills carry a soft dracula-green tint
so the two dates read as one cohesive "therapy schedule"
unit; the old <dl> grid + arbitrary green seam are gone. */}
{(med.lastInjection || med.nextInjection) && (
<div
data-slot="glp1-tile-schedule"
className="flex flex-wrap items-center gap-1.5 pb-3 text-xs"
>
{med.lastInjection && (
<span
data-slot="glp1-tile-last"
className="text-foreground tabular-nums"
className="border-dracula-green/30 bg-dracula-green/10 text-foreground inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 tabular-nums"
>
{dateWithWeekday(med.lastInjection.date)}
</dd>
</div>
)}
{med.nextInjection && (
<div className="flex items-center gap-1.5">
<Syringe className="h-3 w-3 shrink-0" aria-hidden="true" />
<dt className="font-medium">
{t("dashboard.glp1.nextInjection")}:
</dt>
<dd
<Calendar
className="text-dracula-green/80 h-3 w-3 shrink-0"
aria-hidden="true"
/>
<span className="text-muted-foreground font-medium">
{t("dashboard.glp1.lastInjection")}:
</span>
<span>{dateWithWeekday(med.lastInjection.date)}</span>
</span>
)}
{med.nextInjection && (
<span
data-slot="glp1-tile-next"
className="text-foreground tabular-nums"
className="border-dracula-green/30 bg-dracula-green/10 text-foreground inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 tabular-nums"
>
{dateWithWeekday(med.nextInjection.date)}
<span className="text-muted-foreground ml-1">
(
<Syringe
className="text-dracula-green/80 h-3 w-3 shrink-0"
aria-hidden="true"
/>
<span className="text-muted-foreground font-medium">
{t("dashboard.glp1.nextInjection")}:
</span>
<span>{dateWithWeekday(med.nextInjection.date)}</span>
<span className="text-muted-foreground">
·{" "}
{t("dashboard.glp1.inDays", {
count: med.nextInjection.daysAway,
})}
)
</span>
</dd>
</div>
)}
</dl>
</span>
)}
</div>
)}
{/* v1.4.27 B1 — chart pane carrying a two-tab segmented control
(Drug-Level default / Weight) plus a 7d / 30d / 90d / All