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-Tokenheader matched against cookie). - Transparent Token Refresh: Intercepts
401 Unauthorizedresponses, exchanges refresh tokens, and replays queued requests without disrupting the user. - Rate Limiting: Handles
429 Too Many Requestswith automatic exponential backoff.
- CSRF Protection: Automatically synchronizes double-submit CSRF tokens (
@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 Role | Key Capabilities |
|---|---|
SuperAdmin | Full platform control, operator configuration, database flag overrides. |
OperationsManager | Approve manual disbursements (> N500,000 threshold), manage bank float. |
Underwriter | Review complex borrower loan applications, audit bank statements. |
CollectionsOfficer | Manage dunning queues, review Promise-to-Pay cases, approve restructurings. |
SupportAgent | Respond to user support tickets, view customer interaction logs. |
ComplianceOfficer | Audit 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-authenticationpaired 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
Understand Asynq workers, transactional outbox events, and production operations.
Financial Safety & LedgerReview the double-entry audit mirror and decimal money discipline.
Domain Models & LifecyclesExplore the lifecycles of tickets, loans, KYC, and scoring.
Developer SetupSet up your machine and run the full stack locally.