marmar-context / web / context / src / clinician / patient / PatientActions.tsx
PatientActions.tsx
Raw
import { useId } from "react";
import { clinicianApi } from "../../api/client";
import type { PatientDetail } from "../../api/types";
import { checkInEligibility } from "../../logic/eligibility";
import { Button, Icon, Menu, MenuContent, MenuItem, MenuLabel, MenuTrigger, Tip } from "../kit";
import { Link, navigate } from "../router";
import { tabPath } from "./links";

/** One green action (ask Yasmar, or open the call already running), Add records quiet, the rest under ⋯. */
export function PatientActions({ patient }: { patient: PatientDetail }) {
  const hintId = useId();
  const eligibility = checkInEligibility(patient);
  const blocked = !eligibility.allowed ? eligibility : null;
  const openCall = blocked?.code === "active_check_in" && patient.activeCheckInId;
  const askPath = `/app/patients/${patient.id}/check-ins/new${patient.lastCheckId ? `?checkId=${encodeURIComponent(patient.lastCheckId)}` : ""}`;
  return (
    <div className="flex flex-col items-start gap-1.5 max-sm:w-full max-sm:items-stretch">
      <div className="flex flex-wrap justify-end gap-2 max-sm:justify-start">
        <Button variant="quiet" asChild className="no-underline max-sm:flex-1">
          <Link to={`${tabPath(patient.id, "records")}?add=1`}>
            Add records <Icon name="upload-2-line" size={15} />
          </Link>
        </Button>
        {!blocked ? (
          <Button variant="primary" asChild className="no-underline max-sm:order-first max-sm:basis-full">
            <Link to={askPath}>
              <Icon name="phone-line" size={15} />
              Ask Yasmar to check in
            </Link>
          </Button>
        ) : openCall ? (
          <Button variant="primary" asChild className="no-underline max-sm:order-first max-sm:basis-full">
            <Link to={`/app/patients/${patient.id}/check-ins/${patient.activeCheckInId}`}>
              <Icon name="live-line" size={15} />
              Open the check-in
            </Link>
          </Button>
        ) : (
          <Button variant="primary" disabled aria-describedby={hintId} className="max-sm:order-first max-sm:basis-full">
            <Icon name="phone-line" size={15} />
            Ask Yasmar to check in
          </Button>
        )}
        <MoreActions patient={patient} />
      </div>
      {blocked && !openCall && (
        <p id={hintId} className="m-0 w-0 min-w-full text-[12.5px] leading-[18px] text-muted">
          {blocked.message}
        </p>
      )}
    </div>
  );
}

function MoreActions({ patient }: { patient: PatientDetail }) {
  // The backend sends a printable page; the browser's Print saves it as a PDF.
  const exportUrl = patient.lastCheckId ? clinicianApi.exportUrl(patient.lastCheckId, "pdf") : null;
  return (
    <Menu>
      <Tip label="More actions">
        <MenuTrigger asChild>
          <Button variant="quiet" size="icon" aria-label="More actions">
            <Icon name="more-2-line" size={16} />
          </Button>
        </MenuTrigger>
      </Tip>
      <MenuContent align="end">
        <MenuItem icon="printer-line" disabled={!exportUrl} onSelect={() => exportUrl && window.open(exportUrl, "_blank", "noopener,noreferrer")}>
          Export the last check<span className="sr-only"> (opens a printable page)</span>
        </MenuItem>
        {!exportUrl && <MenuLabel>Run a check to export it</MenuLabel>}
        <MenuItem icon="history-line" onSelect={() => navigate(tabPath(patient.id, "activity"))}>
          See the activity
        </MenuItem>
      </MenuContent>
    </Menu>
  );
}