mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 22:32:04 +00:00
The medications page gains a compact table beside the card view — one row per medication with the same served-due status pill, next-intake line, cadence-scaled adherence bars, dose-derived stock and the take and skip actions the cards use, all from the payloads the cards already read; the list response aggregates stock in the same round trip. Columns sort tri-state with nulls last, the default order is yours to arrange — the same drag dialog the dashboard and insights use, applied to both views — and the chosen view and order persist per user on their own settings column with field-scoped writes, so toggling the view never touches the order. Inactive medications pin to the end, the first column stays put under horizontal scroll, and the loading skeleton follows the view you left.
74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { LayoutGrid, Table as TableIcon } from "lucide-react";
|
|
|
|
import { useTranslations } from "@/lib/i18n/context";
|
|
import { cn } from "@/lib/utils";
|
|
import type { MedicationListView } from "@/lib/medication-list-layout";
|
|
|
|
/**
|
|
* v1.16.10 — icon-only card/table view toggle for the /medications page
|
|
* header. A small two-segment control: the active segment carries the
|
|
* filled surface, the inactive one stays ghost. `aria-pressed` announces
|
|
* the state per segment; the group is named for screen readers. Segments
|
|
* keep the 44-px mobile tap floor (`size-11`), shrinking to the standard
|
|
* 36-px control height on desktop like the neighbouring Add button.
|
|
*/
|
|
interface MedicationViewToggleProps {
|
|
view: MedicationListView;
|
|
onChange: (view: MedicationListView) => void;
|
|
}
|
|
|
|
export function MedicationViewToggle({
|
|
view,
|
|
onChange,
|
|
}: MedicationViewToggleProps) {
|
|
const { t } = useTranslations();
|
|
|
|
const segments = [
|
|
{
|
|
value: "cards" as const,
|
|
label: t("medications.viewCards"),
|
|
icon: LayoutGrid,
|
|
},
|
|
{
|
|
value: "table" as const,
|
|
label: t("medications.viewTable"),
|
|
icon: TableIcon,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div
|
|
role="group"
|
|
aria-label={t("medications.viewToggleLabel")}
|
|
className="border-border bg-background/30 inline-flex shrink-0 items-center rounded-md border p-0.5"
|
|
>
|
|
{segments.map(({ value, label, icon: Icon }) => {
|
|
const active = view === value;
|
|
return (
|
|
<button
|
|
key={value}
|
|
type="button"
|
|
onClick={() => {
|
|
if (!active) onChange(value);
|
|
}}
|
|
aria-pressed={active}
|
|
aria-label={label}
|
|
title={label}
|
|
data-slot={`medications-view-${value}`}
|
|
className={cn(
|
|
"focus-visible:ring-ring inline-flex size-10 items-center justify-center rounded-[5px] transition-colors focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none sm:size-8",
|
|
active
|
|
? "bg-secondary text-secondary-foreground shadow-sm"
|
|
: "text-muted-foreground hover:text-foreground",
|
|
)}
|
|
>
|
|
<Icon className="size-4" aria-hidden="true" />
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|