import { useEffect, useState, type ReactNode } from 'react'; import { useTenant } from '../../services/TenantService.tsx'; import { useAuthServiceContext } from '../../services/AuthService.tsx'; import { useOrganizationOwnerService } from '../../services/OrganizationOwnerService.tsx'; import { readInvitationToken } from '../ClinicianInvitationNotice/invitationToken'; import OrganizationOwnerNotice from '../OrganizationOwnerNotice/OrganizationOwnerNotice'; import './TenantGate.css'; // What the owner read said for a member the gateway refused here. `claim`: nobody owns the practice. `owned`: // somebody does, so the way back is their re-admission. `refused`: the read was refused too; the screens below stand. // `held`: no answer (a failure, or no boolean `needsOwner`), which says nothing about the owner, so no claim. type OwnerRecovery = 'checking' | 'claim' | 'owned' | 'refused' | 'held'; // A member whose roles here are all unproved is refused /tenant, yet the authorizer admits them to exactly the // owner read, claim and confirm. So once the gateway has refused, ask the owner read. const useOwnerRecovery = (organizationId: string | undefined) => { const { fetchOwner } = useOrganizationOwnerService(); const [answer, setAnswer] = useState<{ organizationId: string; attempt: number; recovery: OwnerRecovery } | undefined>(undefined); const [attempt, setAttempt] = useState(0); useEffect(() => { if (!organizationId) return; let live = true; const settle = (recovery: OwnerRecovery) => { if (live) setAnswer({ organizationId, attempt, recovery }); }; fetchOwner(organizationId) .then((state) => { const needsOwner = (state as { needsOwner?: unknown } | undefined)?.needsOwner; settle(needsOwner === true ? 'claim' : needsOwner === false ? 'owned' : 'held'); }) .catch((error: unknown) => settle((error as { status?: unknown } | null)?.status === 403 ? 'refused' : 'held')); return () => { live = false; }; }, [organizationId, attempt, fetchOwner]); const recovery: OwnerRecovery = !organizationId ? 'refused' : answer?.organizationId === organizationId && answer.attempt === attempt ? answer.recovery : 'checking'; return { recovery, askAgain: () => setAttempt((n) => n + 1) }; }; // admin-v2 "If Akashic cannot confirm the mode": a signed-in workspace is shown only once the API has // said live or sandbox. Neither claim is made while checking or after a failed read; the server's own // authorization is unchanged - this only withholds the page. export const TenantGate = ({ children }: { children: ReactNode }) => { const { kind, settled, retry, noAccess, accessEnded } = useTenant(); const { logout, wpOrganization } = useAuthServiceContext(); const organizationId = noAccess ? wpOrganization?.current?.id : undefined; const { recovery, askAgain } = useOwnerRecovery(organizationId); if (kind !== 'unknown') return <>{children}>; if (settled === false) { return (
Checking which mode this is…
To answer it, sign out, then open the invitation link from your email again.
Checking your access to this practice…
Your sign-in doesn't include a current staff role here. Try again; if it keeps failing, ask the practice's administrator.
This practice has an owner. Ask them to add you back to the staff.
: null; // The token names no current role here: ended, or not started yet. Nothing here tells which, so the // wording claims neither, and does not offer another sign-in, which would land here again. if (noAccess && accessEnded) { return (Your sign-in doesn't include a current staff role here. If you think you should have access, ask the practice's administrator.
{readmission}Sign out and sign in again; if it continues, ask the practice's administrator.
{readmission}We cannot safely show this workspace until the connection is checked. Try again. If it keeps failing, sign out and contact support.