marmar-context / web / context / src / clinician / settings / Profile.tsx
Profile.tsx
Raw
import { useState } from "react";
import { clinicianApi } from "../../api/client";
import type { ClinicianProfile, Pronoun } from "../../api/types";
import { formatDate } from "../../flow";
import { isInternationalNumber } from "../../logic/access";
import { pronouns } from "../../logic/format";
import { ChoiceChips, Field, Hint, Icon, Input, Panel, StatusPill } from "../kit";
import { useClinician } from "../session";
import { errorText } from "../ui";
import { Facts, SaveRow } from "./bits";

/** ACC-8: what Yasmar calls the clinician, their mobile, and the licence MARMAR checked. */
export function ProfileSection({ onSaved }: { onSaved: (message: string) => void }) {
  const { me, setMe } = useClinician();
  const [profile, setProfile] = useState<ClinicianProfile>(me);
  const [error, setError] = useState("");
  const [saving, setSaving] = useState(false);
  const he = pronouns(profile.pronoun);

  const save = async () => {
    if (!profile.spokenName.trim()) return setError("Say how Yasmar should say your name.");
    const mobile = profile.mobilePhone?.trim() ?? "";
    if (mobile && !isInternationalNumber(mobile)) return setError("Enter your mobile with its country code, for example +234 803 123 4567.");
    setSaving(true);
    try {
      const saved = await clinicianApi.updateMe({
        title: profile.title,
        spokenName: profile.spokenName,
        pronoun: profile.pronoun,
        mobilePhone: mobile || null,
        idleLockMinutes: profile.idleLockMinutes,
      });
      setMe(saved);
      setProfile(saved);
      setError("");
      onSaved("Profile saved.");
    } catch (e) {
      setError(errorText(e));
    } finally {
      setSaving(false);
    }
  };

  return (
    <>
      <Panel title="Your profile" icon="user-3-line" iconClass="text-blue-icon">
        <div className="grid gap-4 sm:grid-cols-2">
          <Field id="profile-title" label="Title">
            <Input id="profile-title" value={profile.title} onChange={(e) => setProfile({ ...profile, title: e.target.value })} />
          </Field>
          <div className="flex min-w-0 flex-col gap-1.5">
            <span className="text-[12.5px] font-medium text-cell">Name on the register</span>
            <span className="flex h-[38px] items-center rounded-[10px] bg-tile px-3 text-[14px] text-ink">{me.fullName}</span>
            <Hint>MARMAR changes it if your licence does.</Hint>
          </div>
        </div>
        <Field id="profile-spoken" label="How Yasmar says your name">
          <Input id="profile-spoken" value={profile.spokenName} onChange={(e) => setProfile({ ...profile, spokenName: e.target.value })} />
        </Field>
        <Field id="profile-mobile" label="Your mobile" hint="For urgent notes by SMS, once MARMAR sends SMS. With its country code, for example +234 803 123 4567.">
          <Input
            id="profile-mobile"
            type="tel"
            inputMode="tel"
            autoComplete="tel"
            placeholder="+234"
            value={profile.mobilePhone ?? ""}
            aria-describedby="profile-mobile-hint"
            onChange={(e) => setProfile({ ...profile, mobilePhone: e.target.value })}
          />
        </Field>
        <ChoiceChips<Pronoun>
          legend="How Yasmar refers to you"
          name="pronoun"
          describedBy="pronoun-help"
          options={(["he", "she", "they"] as Pronoun[]).map((p) => ({ value: p, label: p }))}
          value={profile.pronoun}
          onChange={(p) => setProfile({ ...profile, pronoun: p })}
        />
        <div id="pronoun-help" className="flex gap-2.5 rounded-xl bg-tile px-3.5 py-3 text-[13px] leading-[19px] text-body">
          <Icon name="user-voice-line" size={16} className="mt-px text-purple-icon" />
          <p className="m-0">
            {profile.pronoun
              ? `Yasmar says: "I'm calling for ${profile.spokenName || "you"}'s clinic. ${he.subject[0].toUpperCase() + he.subject.slice(1)} may want to see you in clinic soon."`
              : "Choose how Yasmar refers to you. Until you do, Yasmar uses your name."}{" "}
            <span className="text-muted">We never guess this from your name.</span>
          </p>
        </div>
        <SaveRow error={error} busy={saving} onSave={() => void save()} label="Save profile" />
      </Panel>
      <Panel title="Your licence" icon="id-card-line" iconClass="text-indigo-icon" note="MARMAR checks it with the register">
        <Facts
          rows={[
            ["Profession", me.profession === "doctor" ? "Doctor" : "Pharmacist"],
            ["Licence", <span className="font-mono text-[13px]">{me.licensingBody} {me.licenceNumber}</span>],
            ["Valid until", me.licenceExpiresAt ? formatDate(me.licenceExpiresAt.slice(0, 10)) : "Not on file"],
            [
              "Checked",
              me.verifiedAt ? (
                <StatusPill tone="green" icon="checkbox-circle-fill" size="sm">
                  Verified {formatDate(me.verifiedAt.slice(0, 10))}
                </StatusPill>
              ) : (
                <StatusPill tone="amber" icon="hourglass-fill" size="sm">
                  Being checked
                </StatusPill>
              ),
            ],
            ["Photo", me.photoUrl ? "Added" : "None (optional)"],
          ]}
        />
      </Panel>
    </>
  );
}