import { useSyncExternalStore } from 'react'; /** * The seat standing the server reported with this practice's entitlements: paid (seats as enabled), lapsed (only * freeSeatPractitionerId holds a seat) or unknown (the server could not read it and admits no seat). Null until the * entitlements arrive, and then nothing is withheld here: the server decides every seat-governed write. */ export interface SeatStanding { standing: 'paid' | 'lapsed' | 'unknown'; freeSeatPractitionerId?: string; } /** * The billing owner a request about the free seat is sent for: its practice named, its lifetime's signal, and an * admission that throws once that owner has ended (another viewer, practice or session). */ export interface SeatScope { owner: unknown; organizationId?: string; signal: AbortSignal; admit: () => void; live: () => boolean; } let current: SeatStanding | null = null; // The billing owner now in force, and the one the stored standing was published for. A standing is readable only // while they are the same, so a change of owner conceals the old answer in the very render that changes it. let owner: unknown = null; let storedFor: unknown = null; let scopeOf: (() => SeatScope) | null = null; const listeners = new Set<() => void>(); const notify = () => listeners.forEach((listener) => listener()); export const getSeatStanding = (): SeatStanding | null => (storedFor === owner ? current : null); export const currentSeatOwner = (): unknown => owner; /** Publishes a standing; with forOwner, only while that owner is the current one. */ export const setSeatStanding = (next: SeatStanding | null, forOwner?: unknown): void => { if (forOwner !== undefined && forOwner !== owner) return; const shown = getSeatStanding(); storedFor = owner; current = next; if (next?.standing !== shown?.standing || next?.freeSeatPractitionerId !== shown?.freeSeatPractitionerId) notify(); }; /** * Names the billing owner now in force, from its render, so everything rendered beneath it reads under that owner. * Notifies nothing; the owner's layout effect calls announceSeatOwner for subscribers that did not render. */ export const bindSeatOwner = (token: unknown): void => { owner = token; }; export const announceSeatOwner = (): void => notify(); /** A new billing owner withholds everything the previous one was told. */ export const openSeatOwner = (token: unknown): void => { if (owner === token) return; owner = token; notify(); }; /** An owner that ends takes its standing with it, unless another owner has already replaced it. */ export const retireSeatOwner = (token: unknown): void => { if (owner !== token) return; owner = null; current = null; storedFor = null; notify(); }; /** The billing owner lends its admission to free-seat requests; returns the unregister. */ export const registerSeatScope = (factory: () => SeatScope): (() => void) => { scopeOf = factory; return () => { if (scopeOf === factory) scopeOf = null; }; }; export const currentSeatScope = (): SeatScope | null => scopeOf?.() ?? null; export const useSeatStanding = (): SeatStanding | null => useSyncExternalStore( (listener) => { listeners.add(listener); return () => { listeners.delete(listener); }; }, getSeatStanding, ); /** * Whether this practice's seat capacity admits the practitioner, beside the clinician flag the caller still checks. * The caller passes the standing it subscribed to, so a change of standing renders again. */ export const seatCapacityAdmits = (practitionerId: string | undefined, seat: SeatStanding | null): boolean => { if (!seat || seat.standing === 'paid') return true; if (seat.standing === 'unknown' || !practitionerId) return false; return seat.freeSeatPractitionerId === practitionerId; }; export const practitionerIdOfRole = (role: { practitioner?: { reference?: string } } | undefined | null): string | undefined => { const reference = role?.practitioner?.reference ?? ''; return reference.startsWith('Practitioner/') ? reference.slice('Practitioner/'.length) || undefined : undefined; }; /** * A person's clinician seat as it stands now, from their stored clinician flag and the practice's current standing: * held (paid), free-keeper (lapsed, and they keep the free seat), kept (lapsed, flag kept for when the practice pays), * checking (no standing for this owner and person yet) or unknown (the server could not read the standing). */ export type EffectiveSeat = 'none' | 'held' | 'free-keeper' | 'kept' | 'checking' | 'unknown'; export const effectiveSeat = (configured: boolean, practitionerId: string | undefined, seat: SeatStanding | null): EffectiveSeat => { if (!configured) return 'none'; if (!practitionerId || !seat) return 'checking'; if (seat.standing === 'paid') return 'held'; if (seat.standing === 'unknown') return 'unknown'; return seat.freeSeatPractitionerId === practitionerId ? 'free-keeper' : 'kept'; }; export const LAPSED_SEAT_REASON = "Your practice's subscription has lapsed, so only the clinician who keeps the free seat can do this. An administrator can choose who keeps it in Staff, on that clinician's Workspaces tab.";