marmar-context / web / context / src / clinician / settings / Clinic.tsx
Clinic.tsx
Raw
import { useState } from "react";
import { clinicianApi } from "../../api/client";
import type { ClinicProfile } from "../../api/types";
import { formatDate } from "../../flow";
import { isInternationalNumber } from "../../logic/access";
import { Callout, Field, Input, Panel, StatusPill } from "../kit";
import { useClinician } from "../session";
import { errorText } from "../ui";
import { Group, SaveRow } from "./bits";

type Contact = NonNullable<ClinicProfile["backupContact"]>;
const NUMBER_HINT = "With its country code, for example +234 803 123 4567.";

/** ACC-9: the clinic's details, its backup alert contact (YCI-29) and the emergency numbers Yasmar gives. Only the owner saves. */
export function ClinicSection({ onSaved }: { onSaved: (message: string) => void }) {
  const { clinic, setClinic } = useClinician();
  const [place, setPlace] = useState<ClinicProfile>(clinic);
  const [error, setError] = useState("");
  const [saving, setSaving] = useState(false);
  const owner = clinic.role === "owner";
  const backup: Contact = place.backupContact ?? { name: "", phone: "", email: "" };
  const setBackup = (change: Partial<Contact>) => setPlace({ ...place, backupContact: { ...backup, ...change } });
  const phoneWrong = backup.phone.trim() !== "" && !isInternationalNumber(backup.phone);

  const save = async () => {
    if (phoneWrong) return setError("Enter the backup contact's phone with its country code, for example +234 803 123 4567.");
    setSaving(true);
    try {
      const empty = !backup.name.trim() && !backup.phone.trim() && !backup.email.trim();
      const saved = await clinicianApi.updateClinic(
        { name: place.name, address: place.address, phone: place.phone, backupContact: empty ? null : backup, emergencyNumbers: place.emergencyNumbers },
        clinic,
      );
      setClinic(saved);
      setPlace({ ...saved, openingHours: place.openingHours, alertWatchHours: place.alertWatchHours });
      setError("");
      onSaved("Clinic saved.");
    } catch (e) {
      setError(errorText(e));
    } finally {
      setSaving(false);
    }
  };

  return (
    <>
      {!owner && <Callout icon="lock-line">Only the clinic's owner can change these details.</Callout>}
      <Panel title="Your clinic" icon="hospital-line" iconClass="text-green-icon">
        <fieldset disabled={!owner} className="m-0 flex min-w-0 flex-col gap-5 border-0 p-0">
          <legend className="sr-only">Clinic details</legend>
          <Group title="Details">
            <Field id="clinic-name" label="Name">
              <Input id="clinic-name" value={place.name} onChange={(e) => setPlace({ ...place, name: e.target.value })} />
            </Field>
            <Field id="clinic-address" label="Address">
              <Input id="clinic-address" value={place.address} onChange={(e) => setPlace({ ...place, address: e.target.value })} />
            </Field>
            <Field id="clinic-phone" label="Clinic phone" hint="Shown to the patient after a check-in.">
              <Input id="clinic-phone" type="tel" value={place.phone} aria-describedby="clinic-phone-hint" onChange={(e) => setPlace({ ...place, phone: e.target.value })} />
            </Field>
          </Group>
          <Group
            title="Backup contact for urgent alerts"
            id="backup-help"
            note="If you don't acknowledge an urgent note within 15 minutes, this person gets it too: by email, and by SMS once MARMAR sends SMS."
          >
            <div className="grid gap-4 sm:grid-cols-2" role="group" aria-describedby="backup-help">
              <Field id="backup-name" label="Name">
                <Input id="backup-name" autoComplete="off" value={backup.name} onChange={(e) => setBackup({ name: e.target.value })} />
              </Field>
              <Field id="backup-email" label="Email">
                <Input id="backup-email" type="email" inputMode="email" value={backup.email} onChange={(e) => setBackup({ email: e.target.value })} />
              </Field>
              <Field id="backup-phone" label="Phone" hint={NUMBER_HINT} className="sm:col-span-2">
                <Input
                  id="backup-phone"
                  type="tel"
                  inputMode="tel"
                  placeholder="+234"
                  value={backup.phone}
                  aria-invalid={phoneWrong ? true : undefined}
                  aria-describedby="backup-phone-hint"
                  onChange={(e) => setBackup({ phone: e.target.value })}
                />
              </Field>
            </div>
          </Group>
        </fieldset>
        <SaveRow error={error} busy={saving} disabled={!owner} onSave={() => void save()} label="Save clinic" />
      </Panel>
      <Panel title="Emergency numbers Yasmar gives" icon="alarm-warning-line" iconClass="text-red-icon" flush>
        <ul className="m-0 flex list-none flex-col p-0">
          {place.emergencyNumbers.map((n) => (
            <li key={n.number} className="flex flex-wrap items-center justify-between gap-x-4 gap-y-1.5 px-5 py-3 shadow-[inset_0_1px_0_var(--color-hairline)]">
              <span className="flex min-w-0 items-baseline gap-2.5">
                <b className="font-mono text-[14px] font-medium text-ink">{n.number}</b>
                <span className="text-[13px] text-muted">{n.label}</span>
              </span>
              {n.testedAt ? (
                <StatusPill tone="green" icon="checkbox-circle-fill" size="sm">
                  Test-dialled {formatDate(n.testedAt.slice(0, 10))}
                </StatusPill>
              ) : (
                <StatusPill tone="grey" icon="time-fill" size="sm">
                  Not test-dialled yet
                </StatusPill>
              )}
            </li>
          ))}
        </ul>
        {place.emergencyNumbers.every((n) => n.source === "registry") && place.emergencyNumbers.length > 0 && (
          <p className="m-0 px-5 pt-3 pb-2 text-[12.5px] leading-[18px] text-muted shadow-[inset_0_1px_0_var(--color-hairline)]">
            These are the signed registry's numbers for {place.country}. MARMAR support sets the clinic's own numbers once they are test-dialled.
          </p>
        )}
      </Panel>
    </>
  );
}