Skip to main content

Contexts

Two React Contexts hold everything the app needs to know globally. They're both mounted in App.js:

<TenantProvider>
<AuthProvider>
<App />
</AuthProvider>
</TenantProvider>

AuthContext

ui/src/contexts/AuthContext.js

State

FieldTypeSource
currentUser{ id, name, email, role, accountType, tenantId } | nulllocalStorage vetty.session on mount, or login() response
loadingbooleanAsync auth ops
isAuthenticatedbooleanDerived from currentUser

Methods

  • login(email, password) — calls services.auth.login(), normalizes the user, pushes tenants into TenantContext, stores the session in localStorage (vetty.session) and the token in access_token.
  • logout() — clears localStorage (vetty.session, vetty.currentTenant, vetty.tenants, access_token), calls services.auth.logout(), nulls out state.
  • setSession(user, tenants, currentTenant, token) — internal helper used by login/register flows.

LocalStorage keys it owns

KeyContents
vetty.sessionJSON blob: { user, tenants, currentTenant, token }
access_tokenThe bearer token used by HttpClient

Remember: the browser's localStorage is readable by same-origin JavaScript. Sensitive info shouldn't land there. Today only the token + normalized user live there; roles are derived from the tenant pivot, not spoofable by the client.

TenantContext

ui/src/contexts/TenantContext.js

State

FieldTypePurpose
currentTenantTenant | nullActive clinic
tenantsTenant[]All clinics the user can pick from

Methods

  • switchTenant(tenantId) — finds the tenant, sets it as currentTenant, persists it to vetty.currentTenant.
  • syncTenants(incomingTenants, preferredTenantId?) — called from AuthContext.login(); replaces the list and optionally selects a tenant. If no preferred id is provided, the tenant marked is_default = true wins; otherwise the first in the list.
  • clearTenantState() — used on logout.

LocalStorage keys it owns

KeyContents
vetty.currentTenantJSON serialized Tenant
vetty.tenantsJSON serialized Tenant[]

Usage from components

import { useAuth } from '../../contexts/AuthContext';
import { useTenant } from '../../contexts/TenantContext';

function SomeComponent() {
const { currentUser, logout } = useAuth();
const { currentTenant, tenants, switchTenant } = useTenant();

// … render
}

Both contexts expose a use* hook that throws if consumed outside the provider — a pattern that catches forgotten wrappers early.

Keeping context in sync with the API

  • After every login() the user's full tenant list is synced to TenantContext via syncTenants(). This overwrites anything stale from a prior session.
  • switchTenant() only touches TenantContext — it does not call the backend. The next tenant-scoped API call will use the new currentTenant.id naturally.
  • logout() sweeps both contexts so no residual state remains if a different user logs in on the same machine.