"use client"; import { CalendarDays, FlaskConical, ListChecks, Pill, Trash2, Users, } from "lucide-react"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogPanel, DialogPopup, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { ROLE_LABELS } from "@/lib/access"; import { authClient } from "@/lib/auth-client"; import { PROVISIONABLE_ROLES, rolePermissionSummary } from "@/lib/roles"; import { SPECIALTIES, setStaffPassword, specialtyLabel, updateStaffSpecialty, } from "@/lib/staff"; import { notify } from "@/lib/toast"; // Roles that can carry a clinical specialty (i.e. treat patients). const PROVIDER_ROLES = new Set(["owner", "admin", "doctor", "member"]); // Icon shown next to each permission resource row. const RESOURCE_ICONS: Record = { patient: , appointment: , prescription: , task: , lab: , }; // One row of /api/staff — shared with the Care Team panel. export type StaffMember = { id: string; userId: string; role: string; name: string | null; email: string | null; username: string | null; specialty: string | null; }; function roleLabel(role?: string | null): string { if (!role) return ROLE_LABELS.member; return (ROLE_LABELS as Record)[role] ?? role; } function initials(name?: string | null, email?: string | null): string { const source = name?.trim() || email?.trim() || "?"; return ( source .split(/\s+/) .map((w) => w[0]) .filter(Boolean) .join("") .slice(0, 2) .toUpperCase() || "?" ); } type Props = { member: StaffMember | null; open: boolean; onOpenChange: (open: boolean) => void; // True when the viewer may change/remove this member (admin, not self, not an // owner). When false the dialog is read-only. editable: boolean; onChanged: () => void; onRemove: (member: StaffMember) => void; }; // Admin-facing detail view for a single clinic member: shows who they are, what // their role lets them do, and (when editable) lets an admin change the role — // which swaps the whole permission bundle — or remove them. export function EmployeeDetailDialog({ member, open, onOpenChange, editable, onChanged, onRemove, }: Props) { const { t } = useTranslation(); const [role, setRole] = useState(member?.role ?? ""); const [saving, setSaving] = useState(false); const [specialty, setSpecialty] = useState(member?.specialty ?? ""); const [savingSpecialty, setSavingSpecialty] = useState(false); const [newPw, setNewPw] = useState(""); const [confirmPw, setConfirmPw] = useState(""); const [savingPw, setSavingPw] = useState(false); useEffect(() => { setRole(member?.role ?? ""); setSpecialty(member?.specialty ?? ""); setNewPw(""); setConfirmPw(""); }, [member?.id, member?.role, member?.specialty]); const summary = rolePermissionSummary(member?.role); const secondary = member?.username ? `@${member.username}` : member?.email; // Keep the member's current role selectable even if it isn't admin-assignable // (e.g. the "member"/Clinician role). const roleOptions = Array.from( new Set([member?.role, ...PROVISIONABLE_ROLES].filter(Boolean) as string[]), ); const changeRole = async () => { if (!member || saving || role === member.role) return; setSaving(true); const { error } = await authClient.organization.updateMemberRole({ memberId: member.id, role, }); setSaving(false); if (error) { notify.error( t("settings.careTeam.employee.roleFailedTitle"), error.message ?? t("settings.careTeam.employee.roleFailedBody"), ); return; } notify.success( t("settings.careTeam.employee.roleUpdatedTitle"), t("settings.careTeam.employee.roleUpdatedBody", { name: member.name ?? member.email ?? "", role: roleLabel(role), }), ); onChanged(); onOpenChange(false); }; const saveSpecialty = async () => { if (!member || savingSpecialty) return; const next = specialty || null; if (next === (member.specialty ?? null)) return; setSavingSpecialty(true); try { await updateStaffSpecialty(member.userId, next); notify.success( t("settings.careTeam.employee.specialtyUpdatedTitle"), t("settings.careTeam.employee.specialtyUpdatedBody", { name: member.name ?? member.email ?? "", }), ); onChanged(); } catch { notify.error( t("settings.careTeam.employee.specialtyFailedTitle"), t("settings.careTeam.employee.specialtyFailedBody"), ); } finally { setSavingSpecialty(false); } }; const resetPassword = async () => { if (!member || savingPw) return; if (newPw.length < 12) { notify.error( t("settings.careTeam.employee.pwTooShortTitle"), t("settings.careTeam.employee.pwTooShortBody"), ); return; } if (newPw !== confirmPw) { notify.error( t("settings.careTeam.employee.pwMismatchTitle"), t("settings.careTeam.employee.pwMismatchBody"), ); return; } setSavingPw(true); try { await setStaffPassword(member.userId, newPw); notify.success( t("settings.careTeam.employee.pwUpdatedTitle"), t("settings.careTeam.employee.pwUpdatedBody", { name: member.name ?? member.email ?? "", }), ); setNewPw(""); setConfirmPw(""); } catch { notify.error( t("settings.careTeam.employee.pwFailedTitle"), t("settings.careTeam.employee.pwFailedBody"), ); } finally { setSavingPw(false); } }; const showSpecialty = PROVIDER_ROLES.has(member?.role ?? ""); const specialtyOptions = [ { value: "", label: t("settings.careTeam.employee.noSpecialty") }, ...SPECIALTIES.map((s) => ({ value: s, label: t(`settings.careTeam.specialties.${s}`), })), ]; return ( {t("settings.careTeam.employee.title")} {t("settings.careTeam.employee.description")}
{initials(member?.name, member?.email)}

{member?.name || member?.email || member?.userId}

{secondary && (

{secondary}

)} {showSpecialty && specialtyLabel(t, member?.specialty) && (

{specialtyLabel(t, member?.specialty)}

)}
{roleLabel(member?.role)}
{t("settings.careTeam.employee.permissions")}
{summary.map(({ resource, actions }) => (
{RESOURCE_ICONS[resource]} {t(`settings.careTeam.employee.resources.${resource}`)} {actions.length === 0 ? ( {t("settings.careTeam.employee.noAccess")} ) : ( actions.map((a) => ( {t(`settings.careTeam.employee.actions.${a}`)} )) )}
))}
{editable && (
{t("settings.careTeam.employee.changeRole")}
)} {editable && showSpecialty && (
{t("settings.careTeam.employee.specialty")}
)} {editable && (
{t("settings.careTeam.employee.resetPassword")}

{t("settings.careTeam.employee.resetPasswordHint")}

setNewPw(e.target.value)} placeholder={t("settings.careTeam.employee.newPassword")} type="password" value={newPw} />
setConfirmPw(e.target.value)} placeholder={t("settings.careTeam.employee.confirmPassword")} type="password" value={confirmPw} />
)}
{editable && member && ( )} }> {t("settings.careTeam.employee.close")}
); }