fix(medications): clipped table corners, one customize glyph, the view choice moves home

The table wrapper clips its children now — the sticky name column's
opaque cell background squared off the left rounded corners. The
dashboard, insights and medications headers share a wrench as their
customize glyph, leaving the slider icon to the target and overlay
controls that already wear it. The header view toggle retires; the
cards-or-table choice lives in the medications settings section.
This commit is contained in:
Marc-André Bombeck committed 2026-06-12 20:23:43 +02:00
1 parent 853ff61520
commit 8b0fbfb522
5 files changed
+30 -33

No files matched your search

@@ -111,29 +111,22 @@ describe("/medications — view renders from the persisted preference", () => {
expect(html).not.toContain("<table");
expect(html).toContain('data-slot="card"');
// Toggle present, group named, the cards segment pressed.
expect(html).toContain('aria-label="View"');
expect(html).toMatch(
/data-slot="medications-view-cards"[^>]*aria-pressed="true"|aria-pressed="true"[^>]*data-slot="medications-view-cards"/,
);
expect(html).toMatch(
/data-slot="medications-view-table"[^>]*aria-pressed="false"|aria-pressed="false"[^>]*data-slot="medications-view-table"/,
);
// v1.16.11 — the view choice lives in /settings/medications only;
// the page header carries no toggle.
expect(html).not.toContain('data-slot="medications-view-cards"');
});
it("renders the table for view=table", () => {
const html = renderPage({ version: 1, view: "table", order: [] });
expect(html).toContain("<table");
expect(html).toMatch(
/data-slot="medications-view-table"[^>]*aria-pressed="true"|aria-pressed="true"[^>]*data-slot="medications-view-table"/,
);
expect(html).not.toContain('data-slot="medications-view-table"');
});
it("links the header customize glyph to /settings/medications", () => {
// v1.16.10 — same entry-point logic as the dashboard and insights
// headers: the Settings2 ghost icon is a plain link to the page's
// settings section (view preference + manual order live there).
// v1.16.11 — same entry-point logic as the dashboard and insights
// headers: one wrench ghost icon linking to the page's settings
// section (view preference + manual order live there).
const html = renderPage({ version: 1, view: "cards", order: [] });
expect(html).toContain('href="/settings/medications"');
expect(html).toContain('aria-label="Customize medications"');
+9 -13
View File
@@ -15,7 +15,6 @@ import {
MedicationTable,
MedicationTableSkeleton,
} from "@/components/medications/medication-table";
import { MedicationViewToggle } from "@/components/medications/medication-view-toggle";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
@@ -26,7 +25,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { EmptyState } from "@/components/ui/empty-state";
import { Skeleton } from "@/components/ui/skeleton";
import { CheckCircle2, Loader2, Pill, Plus, Settings2 } from "lucide-react";
import { CheckCircle2, Loader2, Pill, Plus, Wrench } from "lucide-react";
import { apiGet } from "@/lib/api/api-fetch";
import { useMedicationListLayout } from "@/lib/queries/use-medication-list-layout";
import { applyMedicationOrder } from "@/lib/medications/medication-order";
@@ -154,8 +153,7 @@ export default function MedicationsPage() {
// manual medication order, server-side per user
// (`GET`/`PUT /api/medications/layout`). The toggle writes
// optimistically; the order editor lives at /settings/medications.
const { layout, isLayoutLoading, setView } =
useMedicationListLayout(isAuthenticated);
const { layout, isLayoutLoading } = useMedicationListLayout(isAuthenticated);
useEffect(() => {
if (shouldOpenFromUrl) {
@@ -264,14 +262,12 @@ export default function MedicationsPage() {
</p>
</div>
<div className="flex shrink-0 items-center gap-2">
{/* v1.16.10 — card ⇄ table view toggle, persisted server-side
per user. The Settings2 glyph next to it is the customize
shortcut to /settings/medications (view preference + the
manual-order editor), exactly like the dashboard and
insights headers link to their settings sections, in the
same slot (left of the add button) and with the same
responsive 44-px mobile tap floor. */}
<MedicationViewToggle view={layout.view} onChange={setView} />
{/* v1.16.11 — the wrench is the one customize entry point:
it links to /settings/medications, which owns the view
preference (cards ⇄ table) and the manual-order editor.
Same glyph, slot (left of the add button) and responsive
44-px mobile tap floor as the dashboard and insights
headers. */}
<Button
asChild
variant="ghost"
@@ -283,7 +279,7 @@ export default function MedicationsPage() {
aria-label={t("medications.customize")}
title={t("medications.customize")}
>
<Settings2 className="h-4 w-4" aria-hidden="true" />
<Wrench className="h-4 w-4" aria-hidden="true" />
</Link>
</Button>
{/* v1.14.0 — the "Add" button is now a choice: log an intake
@@ -14,7 +14,7 @@
*/
import { useMemo } from "react";
import Link from "next/link";
import { Activity, Pill, Plus, Settings2, Waves } from "lucide-react";
import { Activity, Pill, Plus, Waves, Wrench } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
@@ -114,7 +114,7 @@ export function DashboardHeader({
aria-label={t("dashboard.customizeDashboard")}
title={t("dashboard.customizeDashboard")}
>
<Settings2 className="h-4 w-4" aria-hidden="true" />
<Wrench className="h-4 w-4" aria-hidden="true" />
</Link>
</Button>
<DropdownMenu>
@@ -9,7 +9,7 @@ import {
ChevronRight,
Loader2,
RefreshCw,
Settings2,
Wrench,
} from "lucide-react";
import { toast } from "sonner";
@@ -806,7 +806,7 @@ function InsightsTabStripImpl({
"focus-visible:ring-ring/50 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none",
)}
>
<Settings2 className="h-4 w-4" aria-hidden="true" />
<Wrench className="h-4 w-4" aria-hidden="true" />
</Link>
)}
{onRegenerate && (
@@ -262,7 +262,12 @@ export function MedicationTable({
}
return (
<div className="bg-card border-border rounded-xl border">
// `overflow-hidden` is load-bearing: the sticky name column paints
// an opaque `bg-card` cell background that would otherwise square
// off the wrapper's rounded corners on the left edge. Horizontal
// scrolling lives on the inner table-container (`overflow-x-auto`),
// so clipping here costs nothing.
<div className="bg-card border-border overflow-hidden rounded-xl border">
<Table>
<caption className="sr-only">{t("medications.tableCaption")}</caption>
<TableHeader>
@@ -636,7 +641,10 @@ function MedicationTableRowItem({
*/
export function MedicationTableSkeleton({ rows = 4 }: { rows?: number }) {
return (
<div className="bg-card border-border rounded-xl border" aria-hidden="true">
<div
className="bg-card border-border overflow-hidden rounded-xl border"
aria-hidden="true"
>
<Table>
<TableHeader>
<TableRow>