marmar-context / web / context / src / clinician / today / Lists.tsx
Lists.tsx
Raw
import type { PatientSummary } from "../../api/types";
import { dayLabel, whenLabel } from "../../logic/format";
import { checkInPill } from "../../logic/pills";
import { checkInTab, dosesThisWeek, recentCheckIns } from "../../logic/today";
import { EmptyState, SectionHeader } from "../kit";
import { tabPath } from "../patient/links";
import { DoseBar, PatientAvatar, StatePill } from "../patients/bits";
import { Link } from "../router";

const SHOWN = 5;
const row =
  "grid items-center gap-3 px-4 py-3 text-inherit no-underline transition-colors outline-none hover:bg-tile focus-visible:shadow-[inset_0_0_0_2px_var(--color-brand)]";
const list = "m-0 flex list-none flex-col overflow-hidden rounded-2xl bg-page p-0 shadow-card";

/** Doses taken this week for patients who share them, the lowest share first. */
export function DosesThisWeek({ patients }: { patients: PatientSummary[] }) {
  const rows = dosesThisWeek(patients);
  return (
    <section aria-label="Doses this week" className="flex min-w-0 flex-col gap-3">
      <SectionHeader title="Doses this week" count={rows.length} to={rows.length > SHOWN ? "/app/patients" : undefined} />
      {rows.length ? (
        <ul className={list}>
          {rows.slice(0, SHOWN).map((r) => (
            <li key={r.patient.id} className="border-b border-hairline last:border-b-0">
              <Link to={tabPath(r.patient.id, "shared")} className={`${row} grid-cols-[32px_minmax(0,1fr)_auto]`}>
                <PatientAvatar name={r.patient.name} />
                <span className="flex min-w-0 flex-col gap-2">
                  <span className="truncate text-[14px] leading-5 font-medium text-ink">{r.patient.name}</span>
                  <DoseBar taken={r.taken} expected={r.expected} className="max-w-48" />
                </span>
                <span className="flex flex-col items-end">
                  <span className="text-[14px] leading-5 font-medium text-ink tabular-nums">
                    {r.taken} <span className="font-normal text-subtle">of {r.expected}</span>
                  </span>
                  <span className="text-[12px] text-muted tabular-nums">{r.onTime} on time</span>
                </span>
              </Link>
            </li>
          ))}
        </ul>
      ) : (
        <EmptyState title="No doses shared this week" body="Patients who share doses taken from MARMAR Health show here, the lowest share first." />
      )}
    </section>
  );
}

/** Each patient's latest check-in, newest first: a Yasmar call with its state, or a daily check-in. */
export function RecentCheckIns({ patients, now }: { patients: PatientSummary[]; now: Date }) {
  const rows = recentCheckIns(patients);
  return (
    <section aria-label="Recent check-ins" className="flex min-w-0 flex-col gap-3">
      <SectionHeader title="Recent check-ins" count={rows.length} />
      {rows.length ? (
        <ul className={list}>
          {rows.slice(0, SHOWN).map((p) => {
            const last = p.lastCheckIn!;
            const kind = last.kind === "yasmar_call" ? "Yasmar call" : "Daily check-in";
            return (
              <li key={p.id} className="border-b border-hairline last:border-b-0">
                <Link to={tabPath(p.id, checkInTab(p))} className={`${row} grid-cols-[32px_minmax(0,1fr)_auto] sm:grid-cols-[32px_minmax(0,1fr)_auto_auto]`}>
                  <PatientAvatar name={p.name} />
                  <span className="flex min-w-0 flex-col">
                    <span className="truncate text-[14px] leading-5 font-medium text-ink">{p.name}</span>
                    <span className="truncate text-[12.5px] text-muted">
                      {kind}
                      <span className="sm:hidden"> · {dayLabel(last.at, now)}</span>
                    </span>
                  </span>
                  {last.status ? <StatePill pill={checkInPill(last.status)} /> : <span />}
                  <span className="hidden font-mono text-[12px] whitespace-nowrap text-subtle sm:block">{whenLabel(last.at, now)}</span>
                </Link>
              </li>
            );
          })}
        </ul>
      ) : (
        <EmptyState title="No check-ins yet" body="Yasmar calls and the daily check-ins patients share show here." />
      )}
    </section>
  );
}