RF
Riza Fauzi
HomeExperienceProjectsEducation
LinkedInContactDownload CV
01/Overview01 — 06
Overview01Description02Details03Tech stack04Screens05Contact06
← All projects01/Bibit Backoffice
Bibit.id Logo
WORK · 19Bibit.id

Bibit Backoffice.

Internal backoffice behind KYC verification and settlement for Indonesia's leading mutual fund app.

Web · Mutual Fund App · 2020 — 2022 · Lead Frontend Engineer
02/Description

About the project.

Bibit serves millions of retail investors, and its KYC, settlement, and business teams run on this backoffice. I led the five-person frontend team behind it, reporting to the engineering manager and CTO: a six-month, zero-downtime migration from a legacy React SPA to Next.js at 99% uptime, atomic design that cut code duplication by ~40%, and SonarCloud quality gates held above 90%.

03/Details

Challenges & decisions.

2. The Rewrite Story (Context)

Legacy app (2021–2023): A React SPA built with create-react-app, manual CSS (no system), and React-Redux. It worked for 2 years, but as the ops team grew from 5 to 30+ users across Settlement, KYC, and external stakeholders, the cracks showed.

Why we had to rewrite:

  • Bundle size: 2.4MB main chunk. Dashboard took 6s to become interactive on office WiFi.

  • CSS chaos: 8,000+ lines of manual CSS, naming collisions, no design consistency between Settlement and KYC modules.

  • Redux boilerplate: Adding a single filter to the transaction table required touching 5 files (action, reducer, saga, types, component).

  • No SSR: Login page, report exports, and auth redirects were all client-side—janky and insecure.

  • Stakeholder expansion: External auditors and partner banks needed limited access. The old auth model couldn’t support modular, role-aware UI without massive hacks.

Decision: Incremental rewrite was riskier than a clean rebuild. We froze features for one quarter and rebuilt on Next.js 14.


3. Old vs. New Stack — Comparison Table

Table

Aspect

Old Stack

New Stack

Why We Migrated

Framework

React 17 SPA (CRA)

Next.js 14 (App Router)

SSR for auth/login pages; nested layouts for Settlement vs KYC modules; API routes for lightweight BFF patterns

State Management

React-Redux + Redux-Saga

Zustand

Redux was 70% boilerplate. Zustand = 1 file per store, no providers, no sagas, persisted auth state effortlessly

Styling

Manual CSS files

Ant Design + Custom Tokens

AntD gave us 90% of complex UI (tables, forms, date-pickers) out of the box. Customized via

ConfigProvider

to match brand

Data Fetching

Redux-Saga + Axios

Axios + Zustand stores

Centralized API layer with interceptors (token refresh, error handling). Zustand stores hold server state + UI state together

Routing

React Router

Next.js App Router

File-based routing reduced config; route groups isolated

/settlement/*

and

/kyc/*

modules

RBAC / UI Permissions

Hardcoded

if (role === 'admin')

Permission matrix in Zustand +

<PermissionGate>

Roles grew from 3 to 8 (Settlement Team, Settlement Manager, KYC, KYC Manager, Auditor, Partner Bank, etc.). Hardcoding was unmaintainable

Approval Flows

Hardcoded modal logic per page

Reusable

useApprovalFlow

hook + dynamic config

Settlement and KYC have different hierarchies. Hook reads workflow config from API; no frontend deploy needed to change rules

Build / Deploy

Manual Webpack

Vercel + CI/CD

Automatic previews per PR; zero-downtime deploys


4. Architecture Diagrams

A. Component Architecture (Post-Rewrite)

plainCopy┌─ Next.js App Router │ ├─ (auth) group ── Login / Reset Password (RSC, minimal JS) │ ├─ (dashboard) group ── Layout Shell │ │ ├─ Sidebar ── Menu items filtered by Zustand auth role │ │ ├─ Header ── User badge, notifications │ │ └─ Module Routes │ │ ├─ /settlement │ │ │ ├─ TransactionTable (customized AntD Table) │ │ │ ├─ BulkActionBar ── <PermissionGate> │ │ │ └─ ApprovalDrawer ── powered by useApprovalFlow() │ │ └─ /kyc │ │ ├─ SubmissionQueue │ │ ├─ DocumentViewer │ │ └─ VerificationForm (customized AntD Form) │ └─ Shared Layer │ ├─ <PermissionGate> ── renders children or null based on matrix │ ├─ useAuthStore (Zustand) ── user, token, role permissions │ ├─ useSettlementStore (Zustand) ── local + server state │ └─ apiClient (Axios instance) ── interceptors, base URL, headers

B. Data Flow (Simplified)

plainCopyUser clicks "Approve" in Settlement Drawer ↓ <<PermissionGate> checks useAuthStore.roleMatrix ↓ useApprovalFlow() validates: "Can this user approve at this hierarchy level?" ↓ Optimistic UI update in useSettlementStore ↓ apiClient.post('/settlements/approve') → Axios interceptor attaches JWT ↓ Response → store updated → AntD notification → UI re-renders ↓ WebSocket broadcast updates other users' queues in real-time


5. Key Challenges & Solutions (Rewrite Edition)

Challenge 1: Migrating State from Redux to Zustand

Problem: 40+ Redux actions/reducers for settlement data alone. Migrating everything at once would stall the team for weeks.

Solution: Strangler Fig pattern. We built Zustand stores alongside Redux, then migrated one module at a time (Settlement first, then KYC). Used a compatibility layer so components could consume both during transition.

Outcome: Zero-downtime migration. Team shipped features throughout the rewrite.


Challenge 2: Replacing 8,000 Lines of Manual CSS with AntD Tokens

Problem: Legacy CSS had class name collisions (table-wrapper defined differently in 3 files). Stakeholders complained Settlement and KYC "felt like different apps."

Solution: Audited all CSS, mapped recurring values to AntD ConfigProvider tokens (colorPrimary, borderRadius, fontSize). Built a thin wrapper component library (<<AppTable />, <AppForm />, <AppModal />) that enforced consistency.

Outcome: Deleted 6,200 lines of CSS. Both modules now share one visual language. New features take 30% less styling time.


Challenge 3: Role-Aware UI for 8 User Types

Problem: Settlement Team, Settlement Manager, KYC Officer, KYC Manager, Auditor, Partner Bank, Admin, Read-Only Viewer. Hardcoding role === 'x' was no longer viable.

Solution: Backend serves a permission matrix at login. Frontend stores it in useAuthStore. A single <PermissionGate required={['settlements.approve']}> component handles all UI gating. Approval buttons render dynamically based on hierarchy config.

Outcome: Adding "Partner Bank Viewer" required zero frontend code—just a backend config update.


Challenge 4: 50k-Row Transaction Table Performance

Problem: The old Redux + manual table re-rendered everything on every filter change. Browser froze.

Solution: AntD Table + server-side filtering/sorting + virtualized scroll for large datasets. Zustand store only holds viewport-visible data.

Outcome: Filter response: 4s → 280ms. Scroll: 60fps.

Overview

An internal web-based backoffice system supporting operational, compliance, and business teams in a regulated fintech environment. The platform needed to scale quickly while maintaining code quality, consistency, and ease of use for internal stakeholders.


Product & Technical Context

The existing codebase had grown organically, leading to duplicated UI patterns and slower development cycles. Improving maintainability and consistency became critical as new features and teams were added.


Key Challenges

  • Inconsistent UI patterns slowed development and increased maintenance overhead

  • Lack of standardized components made scaling the platform difficult

  • Limited visibility into code quality and technical debt


My Role & Responsibilities

  • Took ownership of frontend architecture and component standardization

  • Collaborated with multiple teams to align UI patterns with operational workflows

  • Mentored engineers on best practices and component-driven development


What I Did

  • Designed and implemented a scalable UI system based on Atomic Design principles

  • Built reusable component libraries to reduce duplication and improve consistency

  • Introduced SonarCloud for automated code quality analysis and technical debt tracking

  • Improved development workflows to support faster iteration and onboarding


Impact & Results

  • Reduced development time for new UI features by approximately 30%

  • Improved maintainability and reduced duplicated logic across the codebase

  • Increased overall code quality and reduced critical issues flagged by static analysis

  • Enabled faster onboarding and collaboration for new engineers

04/Tech stack

Built with.

01NextJS02ReactJS03HTML04CSS
05/Screens

A look inside.

Desktop01 screens
Backoffice registration queue of customers rejected by S-Invest, with filters and blurred customer data.
01 · Registration queue
←
PREVIOUS PROJECTIndonesia Butuh Anak Muda.
NEXT PROJECTBibit App.
→
Open to roles

Let's work together.

Frontend & Mobile Engineer (React, React Native)

Contact meDownload CV ↓
RF
Frontend & Mobile Engineer (React, React Native)mrizafauzi.r@gmail.com
SECTIONSExperienceProjectsEducation
ELSEWHERELinkedIn ↗GitHub ↗Instagram ↗
© 2026 Riza Fauzi