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