import { useState, type ReactNode } from "react";
import type { DoseCounts, SharedDoses } from "../../api/types";
import { doseHeadline, doseKinds, doseLine, doseStack, type DoseKind, type DosePeriod } from "../../logic/charts";
import { scheduleLabel } from "../../logic/format";
import { cn, Segmented } from "../kit";
const fill: Record<DoseKind, string> = {
onTime: "bg-green-icon",
late: "bg-amber-icon",
skipped: "bg-subtle",
missed: "bg-red-icon",
rest: "bg-hairline shadow-[inset_0_0_0_1px_var(--color-line)]",
};
const periods: { value: DosePeriod; label: string }[] = [
{ value: "week", label: "Week" },
{ value: "last30", label: "30 days" },
];
/** A stacked bar out of what was due; the line under it says the same in words. */
function DoseRow({ name, schedule, counts, period }: { name: string; schedule: string | null; counts: DoseCounts; period: DosePeriod }) {
const stack = doseStack(counts);
return (
<li className="flex flex-col gap-1.5 py-3 shadow-[inset_0_-1px_0_var(--color-hairline)] last:shadow-none">
<div className="flex items-baseline justify-between gap-3">
<span className="min-w-0 text-[13.5px] leading-5">
<strong className="font-medium text-ink">{name}</strong>
{schedule && <span className="text-muted"> · {scheduleLabel(schedule)}</span>}
</span>
<span className="shrink-0 font-mono text-[12px] text-muted tabular-nums">
{stack.taken}/{stack.expected}
</span>
</div>
<div aria-hidden="true" className="flex h-2.5 gap-[2px]">
{stack.segments.length ? (
stack.segments.map((s) => <span key={s.key} className={cn("min-w-1 rounded-full", fill[s.key])} style={{ flexGrow: s.count, flexBasis: 0 }} />)
) : (
<span className={cn("flex-1 rounded-full", fill.rest)} />
)}
</div>
<p className="m-0 text-[12.5px] leading-[18px] text-muted">
{period === "week" ? "This week" : "Last 30 days"}: {doseLine(counts)}
</p>
{stack.over && (
<p className="m-0 text-[12.5px] leading-[18px] text-amber-text">
More doses were logged than were due: {stack.logged} logged, {stack.expected} due. The bar shows what was logged.
</p>
)}
</li>
);
}
/** Doses taken: the figure for the period, then one bar per medicine, then the colours in words. */
export function DoseBars({ doses, notes }: { doses: SharedDoses; notes?: ReactNode }) {
const [period, setPeriod] = useState<DosePeriod>("week");
const head = doseHeadline(doses, period);
return (
<div className="flex flex-col gap-4">
<div className="flex flex-wrap items-start justify-between gap-3">
<div className="flex min-w-0 flex-col gap-1">
<p className="m-0 flex flex-wrap items-baseline gap-x-2">
{head.percent === null ? (
<span className="text-[28px] leading-9 font-semibold tracking-[-0.02em] text-ink">None due</span>
) : (
<>
<span className="text-[38px] leading-[42px] font-semibold tracking-[-0.03em] text-ink tabular-nums">
{head.percent}
<span className="text-subtle">%</span>
</span>
<span className="text-[15px] font-medium text-subtle">taken {head.when}</span>
</>
)}
</p>
<p className="m-0 text-[13.5px] leading-5 text-body">{head.sentence}</p>
</div>
<Segmented label="Show doses for" options={periods} value={period} onChange={setPeriod} />
</div>
{notes}
{doses.perMedicine.length > 0 && (
<ul aria-label="Doses for each medicine" className="m-0 flex list-none flex-col p-0">
{doses.perMedicine.map((m) => (
<DoseRow key={m.medicineId} name={m.name} schedule={m.schedule} counts={period === "week" ? m.week : m.last30} period={period} />
))}
</ul>
)}
<ul aria-label="What the colours mean" className="m-0 flex list-none flex-wrap gap-x-4 gap-y-1.5 p-0 text-[12px] text-muted">
{doseKinds.map((k) => (
<li key={k.key} className="flex items-center gap-1.5">
<span aria-hidden="true" className={cn("size-2 rounded-[2px]", fill[k.key])} />
{k.label}
</li>
))}
</ul>
</div>
);
}