Skip to main content

Web, Staff, Mobile & Shared Frontends

Roja delivers a unified financial user experience across three client surfaces powered by a shared foundation of type-safe API clients, authentication interceptors, and design tokens.

┌─────────────────────────────────────────────────────────────────────────────┐
│ CLIENT SURFACES │
│ │
│ apps/web (Next.js App Router) apps/staff (Next.js Pages) mobile/ (Expo)
└──────────┬───────────────────────────────┬─────────────────────────────┬────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ SHARED PACKAGES & UTILITIES │
│ │
│ @roja/client ─── Strict TypeScript OpenAPI SDK (Types, APIs, Schemas) │
│ @roja/common ─── Axios Factory, Auth Interceptors, CSRF, TanStack Query, │
│ Money Helpers (exact decimal string arithmetic) │
└─────────────────────────────────────────────────────────────────────────────┘

The @roja/common Shared Foundation​

Located in packages/common/, @roja/common is the central runtime layer shared across apps/web, apps/staff, and mobile/. Before writing any custom utility or API wrapper in an application workspace, check if @roja/common already exports it.

Core Modules​

  • @roja/common/api: Shared Axios client factory (createApiClient).
    • CSRF Protection: Automatically synchronizes double-submit CSRF tokens (X-CSRF-Token header matched against cookie).
    • Transparent Token Refresh: Intercepts 401 Unauthorized responses, exchanges refresh tokens, and replays queued requests without disrupting the user.
    • Rate Limiting: Handles 429 Too Many Requests with automatic exponential backoff.
  • @roja/common/money: Canonical monetary formatting utilities (formatMoney, nairaToKobo, koboToNaira). Operates strictly on exact decimal strings (e.g. "50000.00"), never converting to IEEE-754 floating-point numbers.
  • @roja/common/query: TanStack Query v5 provider and query client setup with standard cache invalidation and retry policies.
  • @roja/common/passkeys & @roja/common/twoFactor: Client factories for WebAuthn passkey registration/authentication and TOTP two-factor verification.
  • @roja/common/kyc & @roja/common/kycPrompt: Step calculators that determine which verification tier is required for a requested loan amount.

Customer Web App (apps/web)​

The customer web portal is built on Next.js App Router with React 19, React Compiler, and Tailwind CSS v4.

Architecture & Routing​

  • Route Groups:
    • (marketing): Public marketing landing pages, interest rate calculators, and FAQ.
    • (app): Protected authenticated application shell:
      • /tickets: Marketplace view, ticket creation wizard, counter-offer negotiations.
      • /loans: Active loans, repayment amortization schedules, and Mono DirectPay settlement.
      • /kyc: Multi-tier identity verification wizard with live camera selfie capture.
      • /wallet: Bank account linking, transaction history, and payout accounts.
      • /score: Roja Score dashboard, credit tier breakdown, and score history.
  • Authentication: HTTP-only session cookies paired with Redis session storage. Supports passwordless WebAuthn passkey authentication with biometric unlock.
  • Component Primitives: Radix UI headless primitives styled with Tailwind CSS v4 design tokens (--color-*).

Staff Operations Dashboard (apps/staff)​

The staff dashboard is Roja's back-office operations suite, built with Next.js Pages Router and Tailwind CSS v4.

Role-Based Access Control (RBAC)​

Staff authorization is strictly enforced using roles generated from the OpenAPI spec (backend/spec/roles.gen.go):

Staff RoleKey Capabilities
SuperAdminFull platform control, operator configuration, database flag overrides.
OperationsManagerApprove manual disbursements (> N500,000 threshold), manage bank float.
UnderwriterReview complex borrower loan applications, audit bank statements.
CollectionsOfficerManage dunning queues, review Promise-to-Pay cases, approve restructurings.
SupportAgentRespond to user support tickets, view customer interaction logs.
ComplianceOfficerAudit KYC verification overrides, inspect PEP/sanctions flags.

Operational Workflows​

  • Payout Approval Queue: Large loan disbursements exceeding automated risk thresholds enter a dual-control staff sign-off queue before funds move across banking rails.
  • Collections Case Manager: Track delinquent loans across the 6-stage dunning lifecycle and record debtor agreements.
  • Markov Model Simulator: Interactive tool allowing risk officers to simulate default rates across historical cohorts by adjusting score thresholds.

Mobile Application (mobile/)​

Roja's mobile app is a cross-platform application for iOS and Android built with Expo SDK, expo-router, and NativeWind.

Mobile Technical Choices​

  • Typed Navigation (expo-router): File-system based typed routing with strictly typed path parameters.
  • Biometric Security: Biometric unlock (Face ID / Android Fingerprint) using expo-local-authentication paired with token storage in iOS Keychain / Android Keystore (expo-secure-store).
  • WebView Bridges:
    • Mono Connect: Embedded webview for Open Banking authentication.
    • Dojah Widget: Facial liveness capture and biometric verification.
  • Push Notifications: Expo Push service with custom notification handlers that route incoming alerts directly into ticket negotiations or loan payment reminders.
  • Offline Tolerance: Persistent TanStack Query cache allows borrowers to view current loan schedules and ticket statuses even in spotty network conditions.

Google Play Personal Loans Policy Compliance​

Google Play enforces strict policies for personal loan applications. The Android manifest in mobile/app.config.ts strictly restricts permissions to Camera and Microphone (required for KYC). Access to Contacts, SMS history, Call Logs, and Fine Location is prohibited and will cause store rejection.


Storybook Workspaces​

Roja maintains an isolated component development and visual testing environment. Because Next.js and React Native have incompatible bundler architectures, Storybook runs as two distinct instances:

tools/storybook-web/ ──► Web & Staff Components (Next.js / Vite / Tailwind v4)
tools/storybook-mobile/ ──► Mobile Components (React Native Web / NativeWind)

Running Storybook​

# Launch interactive selector
task storybook

# Run web instance (http://localhost:6006)
task storybook-web

# Run mobile instance (http://localhost:6007)
task storybook-mobile

Storybook Testing & Smoke Tests​

Every UI component has co-located story files (*.stories.tsx). Storybook tests are executed during CI using Vitest:

bun --filter @roja/storybook-web test

This imports every story via @storybook/react's composeStories and runs a DOM render smoke test in JSDOM, guaranteeing that styling tokens and UI primitives do not throw render exceptions.

Hosting & Deploys​

Both Storybook instances are deployed as static Cloudflare Workers:

  • Web: https://storybook.roja.dev
  • Mobile: https://storybook-mobile.roja.dev

Next Steps​