Skip to main content

Frontend Architecture

apps/web is a single React app that serves four personas:

  • consumer
  • client
  • pos_operator
  • admin

Runtime Stack​

  • React 18
  • Vite 5
  • React Router 6
  • Zustand for auth state
  • Supabase JS with PKCE auth
  • React Query is present but not the primary data layer

Bootstrap Sequence​

src/main.tsx mounts the app through the router and query client. App.tsx then:

  1. Tries to load the current session profile.
  2. Listens for auth state changes.
  3. Redirects into the correct role shell once profile state is known.

Auth and Profile State​

src/lib/auth-profile.ts calls GET /api/v1/auth/me and retries transient failures with backoff.

The auth store keeps:

  • user identity
  • role and status
  • tenant IDs
  • app access flags
  • onboarding state
  • profile loading and error state

Route Guarding​

The app uses layered guards:

  1. PkceRedirectLayout preserves auth callback codes.
  2. ProtectedRoute requires a session.
  3. CanUseAppRoute blocks suspended or pending users.
  4. RoleRoute restricts persona-specific pages.
  5. AuthRedirector applies top-level redirects.

Role Layouts​

The shell changes by persona:

  • clients get a collapsible merchant sidebar
  • admins get a collapsible admin sidebar
  • POS operators get a compact top bar
  • consumers get a bottom tab bar

Route Map​

Shared and status routes​

  • /login
  • /auth/callback
  • /profile
  • /profile-error
  • /pending-approval
  • /account-blocked

Consumer routes​

  • /wallet
  • /history
  • /offers
  • /offers/:campaignId
  • /redeem
  • /preferences/marketing

Merchant and admin routes​

  • /dashboard
  • /analytics
  • /analytics/:tenantId
  • /rewards
  • /promotions
  • /campaigns
  • /campaigns/new
  • /campaigns/:campaignId
  • /pos
  • /store-operators
  • /transactions
  • /admin/tenants
  • /admin/users
  • /admin/audit-logs
  • /onboarding

API Client Contract​

src/lib/api.ts wraps fetch and automatically attaches the Supabase bearer token.

It also turns API errors into a structured ApiRequestError so callers can handle retries and auth failures consistently.

The current UI uses CSS variables and shared primitives from src/components/ui.tsx.

Notable layout choices:

  • merchant and admin shells use sidebars
  • consumer navigation is mobile-first
  • POS operators get a compact operational view
  • tenant branding, including the program name and brand colors, is loaded from the API and rendered in the shell

What the Frontend Does Not Own​

The frontend does not own any security boundary. It only reflects what the backend already decided:

  • role
  • tenant membership
  • onboarding status
  • account approval or block state

The backend remains the source of truth for every authorization decision.

Written byDhruv Doshi