Files
yellow-bank-soal/REACT_Migration_Plan.md
Dwindi Ramadhana ab86c254d1 docs: Add REACT_Migration_Plan.md with feasibility assessment
- Fixed broken Markdown formatting (removed excessive backslashes)
- Added Section 11: Feasibility Assessment
  - Current state summary (8 items already in place)
  - Identified gaps (6 items)
  - Detailed gap analysis for session timer, monorepo, nested routes
  - Feasibility score: 7/10
  - Recommended execution order (Phase 0-4)
  - Summary with challenges and strengths
2026-06-17 21:09:40 +07:00

20 KiB

Frontend Migration Plan: React + Tailwind + shadcn/ui

Project: IRT Bank Soal

Target Architecture: Decoupled (FastAPI Backend + React SPA Frontend)

Date Prepared: 2026-06-17

1. Executive Summary

This document outlines the strategic plan to migrate the user interface of the IRT Bank Soal project from a server-rendered application (FastAPI-Admin & Jinja/Web) to a modern Single Page Application (SPA) architecture using React, Tailwind CSS, and shadcn/ui components. This migration aims to significantly enhance UI responsiveness—especially for the Computer Adaptive Testing (CAT) feature—and streamline the development of future interactive features.

2. Target Frontend Tech Stack

Category Primary Technology Rationale
Framework Vite + React (TypeScript) Fast build times, responsive Hot Module Replacement (HMR), industry standard for SPAs.
Styling Tailwind CSS Utility-first styling approach, accelerating the UI slicing process.
UI Components shadcn/ui Accessible headless components (Radix UI) that are copy-pasteable and fully customizable via Tailwind.
Data Fetching TanStack Query (React Query) Server-state management, caching, automatic retry logic for unstable connections, and elegant loading/error state handling.
State Management Zustand / React Context Lightweight client-state storage (e.g., WordPress JWT tokens, X-Website-ID, UI themes). Employs persist middleware for crash/reload recovery.
Routing React Router DOM Seamless navigation using BrowserRouter for clean URLs. Enables robust Nested Routing required for the new hierarchical admin structure.
Form Handling React Hook Form + Zod Strict input validation and type safety (aligning perfectly with Pydantic models in the backend).

3. Repository Restructuring (Monorepo Approach)

It is highly recommended to use a single repository (monorepo) structure with separate folders for the frontend and backend to simplify version control.

yellow-bank-soal/
├── backend/                  # Existing FastAPI application folder (app/, alembic/, etc.)
│   ├── Dockerfile            # Patched Backend Dockerfile
│   ├── app/
│   ├── requirements.txt
│   └── ...
├── frontend/                 # New React application
│   ├── Dockerfile            # New Frontend Dockerfile (Multi-stage Nginx)
│   ├── .env.example          # VITE_API_BASE_URL references
│   ├── src/
│   ├── package.json
│   └── ...
└── docker-compose.yml        # Orchestrates ALL services (API, UI, Redis, Celery)

4. Migration Phases

The migration will be divided into 4 sequential phases to minimize disruption to the existing system.

Phase 1: Backend Preparation (API Readiness)

Focus on preparing FastAPI to securely communicate with an external React application.

  1. CORS Configuration: Update ALLOWED_ORIGINS in app/core/config.py to permit frontend origins (e.g., http://localhost:5173 for development and the target production domain).
  2. Endpoint Audit & Restructuring: Ensure all administrative functionalities are exposed via RESTful API endpoints. Crucially, restructure the API endpoints to match the new UI hierarchy (e.g., GET /api/v1/admin/tryouts/{id}/questions instead of a standalone /questions endpoint).
  3. Data Scrubbing: Strictly ensure that endpoints like /api/v1/session/{id}/next-item omit sensitive fields such as correct_answer, ctt_p, and irt_b from the response payload to prevent client-side cheating.

Phase 2: Frontend Scaffolding & Design System

Focus on project initialization and establishing the UI foundation.

  1. Vite Initialization: Run npm create vite@latest frontend -- --template react-ts.

  2. Environment Variables Setup: Define VITE_API_BASE_URL in frontend .env so Axios knows exactly where to route the API requests across different environments.

  3. Tailwind & shadcn Setup:

    • Install Tailwind CSS and configure tailwind.config.js.
    • Run npx shadcn-ui@latest init to set up base CSS variables and utility functions.
  4. Core Components (shadcn): Install frequently used foundational components:
    npx shadcn-ui@latest add button card input label dialog alert table tabs progress radio-group toast collapsible accordion badge

  5. API Client Setup: Configure an Axios instance to universally append:

    • The Authorization Bearer Token (from WordPress).
    • The X-Website-ID header for multi-tenant isolation.

Phase 3: Student Portal Construction (Core Business Flow)

Focus on the primary user interaction: executing the adaptive tryout.

  1. Tryout Listing: A view displaying available tryouts for the user based on their X-Website-ID.
  2. Exam Dashboard (Session) & Asynchronous Forms:
    • UI: Utilize shadcn's Card for the question area, RadioGroup for options, and a Progress bar for tracking exam status.
    • No-Reload Submissions (AJAX): Completely replace legacy PHP $_POST form actions. Forms will use e.preventDefault() to stop browser reloads. Submissions to /adaptive/respond will be handled asynchronously in the background via TanStack Query.
    • Instant Feedback (Toast): Upon submitting an answer, the UI will instantly display a non-intrusive shadcn Toast notification and smoothly render the next question.
  3. State Recovery & Timer Security (Crucial):
    • Anti-Refresh: Use Zustand's persist middleware to save the current session ID and active question ID into localStorage. If the user accidentally hits F5 or closes the tab, the React app can instantly resume the exam state.
    • Server-Synced Timer: Do not rely on the client's Date.now(). Fetch the exam's exact server-side end time (expires_at) from the FastAPI backend and calculate the countdown on the frontend based on that fixed timestamp.
  4. Result Page: A summary view to display the Raw Score (NM) and Normalized Score (NN) upon exam completion.

Phase 4: Admin Panel Construction (Hierarchy-Driven Redesign)

Based on the ADMIN_TRYOUT_RESTRUCTURE_PLAN, the admin UI will shift from a scattered menu to a deeply nested, Tryout-centric navigation leveraging React Router DOM.

  1. Tree-Based Root Navigation (/admin/tryouts):
    • Replace standard data tables with an interactive Tree/Collapsible layout grouped by Websites.
    • Implement Stat Cards inline for each Tryout (showing NM, NN averages, and Calibration Progress).
    • Add a global [+ Import Tryout] button/modal directly in the header of this tree view.
  2. Nested Tryout Workspaces:
    • Utilize React Router's nested routing to build drill-down pages maintaining the parent Tryout context:
      • /admin/tryout/:id/attempts (filtered DataTable of sessions)
      • /admin/tryout/:id/normalization (settings form to update NM/NN targets)
      • /admin/tryout/:id/questions (filtered DataTable of basis questions)
  3. Question AI Workspace (/admin/tryout/:id/questions/:questionId/workspace):
    • Build a dedicated tabbed interface using shadcn Tabs (Generate, Review, Batch).
    • Provide seamless integration with the OpenRouter AI generation API endpoints.

5. UI/UX Design Guidelines (Human-Centric Approach)

To resolve the "developer-centric" nature of the legacy system, the frontend must adopt a "Human POV" ensuring the dashboard is intuitive, workflow-oriented, and actionable.

5.1. Dashboard Layout Re-imagination

Shift away from displaying raw database counts. The new Home Dashboard (/admin/dashboard) must include:

  • Personalized Greeting: E.g., "Good Morning, Admin! Last login..."
  • Actionable System Overview: Display meaningful KPIs (Active Tryouts, Average Scores, Completion Rates).
  • Attention Needed (Alerts): A dedicated section highlighting urgent tasks (e.g., "23 questions need calibration", "5 AI questions pending review").
  • Quick Actions: Prominent buttons for daily workflows ([Import Tryout], [Generate AI]).

5.2. Visual Indicators & Color Coding

Use Tailwind CSS classes to create consistent, semantic color coding across the application.

  • Difficulty Badges:
    • Easy (p > 0.70): Green (bg-green-100 text-green-800)
    • Medium (0.30 ≤ p ≤ 0.70): Yellow (bg-yellow-100 text-yellow-800)
    • Hard (p < 0.30): Red (bg-red-100 text-red-800)
  • Calibration Status:
    • Ready (≥90%): Green Checkmark icon / Progress Bar
    • Partial (50-89%): Yellow Warning icon / Progress Bar
    • Needs Data (<50%): Red Cross icon / Progress Bar

5.3. Terminology Mapping (System to UI)

While the FastAPI backend retains technical database names, the React frontend must translate these terms into human-readable labels:

System Term (Backend) UI Label (Frontend) Context
Session Student Attempt Table headers, Navigation
Calibration Question Quality Dashboards, Menus
IRT Adaptive Scoring Tryout Settings
CTT Standard Scoring Tryout Settings
NM (Nilai Mentah) Raw Score Reports, Attempt Lists
NN (Nilai Nasional) Normalized Score Reports, Attempt Lists
p-value Difficulty Score Question Data Grids

6. Key shadcn/ui Component Mapping

Feature Requirement Recommended shadcn/ui Component Usage / Context
Tryouts Hierarchy Map Collapsible, Accordion Creates the nested tree structure for Websites -> Tryouts list.
Tryout Stat Cards Card, Badge, Progress Displays quick metrics (Participants, NM avg, Calibration progress) in the tree.
Visual Indicators Badge Colored badges for difficulty levels and calibration status.
Nested Navigation Tabs Switches between "Generate", "Review", and "Batch" in the Question Workspace.
Question & Options View Card, RadioGroup Wraps the question stem and manages A/B/C/D selections.
Form Feedback & Notices Toast Displays non-blocking success/error messages asynchronously without page reloads.
Alerts / Errors Alert Displays prominent API error messages (e.g., lost internet connection).
Submit Confirmation AlertDialog Prompts the user before calling /session/complete to prevent accidental submissions.
Item/Student Roster DataTable (Table) Renders data grids for Attempts and Questions with server-side sort/filter capabilities.
Tryout Settings Switch, Form Configures CTT/IRT parameters and Normalization targets.

7. Security Checklist

  • Client-Side Authorization: Implement Route Guards via React Router to prevent unauthorized access to active exam sessions and admin pages.
  • Sensitive Data Protection: Ensure the client-state manager (Zustand) never stores data the student shouldn't see (e.g., b values, p-values, or correct answers).
  • HTML Sanitization: If the question text (stem) contains rich HTML (from an editor), process it through a library like dompurify before rendering it via dangerouslySetInnerHTML to prevent XSS attacks.

8. References

9. Deployment & Routing Strategy

Since the React application operates as a standalone frontend and relies on WordPress exclusively via API for data integration, the URL routing must be handled cleanly without interference from WordPress.

Chosen Strategy: BrowserRouter (HTML5 History API)

  • URL Format: https://app.domain.com/session/123 (Clean, SEO-friendly URLs)
  • Rationale: As a standalone deployment, there are no conflicts with WordPress's internal rewrite rules or .htaccess. BrowserRouter provides the standard React routing experience.
  • Server Requirement (Crucial): To prevent 404 Not Found errors when users manually refresh a page (e.g., hitting F5 on /session/123), the web server hosting the built React files must be configured to redirect all missing paths back to index.html.

10. Docker & Containerization Strategy

To support the decoupled architecture and the existing AI features, the deployment process will utilize Docker Compose to orchestrate the backend, frontend, Redis, and Celery workers.

10.1. Backend Dockerfile Patch (backend/Dockerfile)

The existing Dockerfile is well-structured but needs a minor patch for production. The --reload flag must be removed as it consumes excessive resources.

# ... existing setup ...  
COPY requirements.txt .  
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# PATCH: Removed the '--reload' flag for production readiness  
CMD ["sh", "-c", "alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port 8000"]

10.2. Frontend Dockerfile (frontend/Dockerfile)

A new multi-stage Dockerfile is required for the React application. Stage 1 compiles the application using Node.js, and Stage 2 serves the static files using a lightweight Nginx server configured for BrowserRouter.

# Stage 1: Build the React application  
FROM node:20-alpine AS builder  
WORKDIR /app  
COPY package*.json ./  
RUN npm ci  
COPY . .  
# Using ARG to inject API URL during the build phase  
ARG VITE_API_BASE_URL  
ENV VITE_API_BASE_URL=$VITE_API_BASE_URL  
RUN npm run build

# Stage 2: Serve with Nginx  
FROM nginx:alpine  
# Copy the built assets from Stage 1  
COPY --from=builder /app/dist /usr/share/nginx/html

# Add a custom Nginx configuration to support BrowserRouter (catch-all rule)  
RUN echo 'server { \  
    listen 80; \  
    location / { \  
        root /usr/share/nginx/html; \  
        index index.html index.htm; \  
        try_files $uri $uri/ /index.html; \  
    } \  
}' > /etc/nginx/conf.d/default.conf

EXPOSE 80  
CMD ["nginx", "-g", "daemon off;"]

10.3. Full Orchestration (docker-compose.yml)

Place this at the root of the monorepo to run the complete ecosystem. This includes Redis and Celery which are required for the OpenRouter AI generation feature outlined in the PRD.

version: '3.8'

services:
  # 1. FastAPI Backend
  backend:
    build:   
      context: ./backend
    ports:
      - "8000:8000"
    env_file:
      - ./backend/.env
    depends_on:
      - redis
    restart: unless-stopped

  # 2. Redis Message Broker (Required by Celery)
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    restart: unless-stopped

  # 3. Celery Worker (For AI Generation Background Tasks)
  celery_worker:
    build:   
      context: ./backend
    command: ["celery", "-A", "app.core.celery_app", "worker", "--loglevel=info"]
    env_file:
      - ./backend/.env
    depends_on:
      - backend
      - redis
    restart: unless-stopped

  # 4. React Frontend SPA
  frontend:
    build:
      context: ./frontend
      args:
        # Inject the backend API URL into the React build
        VITE_API_BASE_URL: "https://api.yourdomain.com/api/v1"
    ports:
      - "80:80"
    depends_on:
      - backend
    restart: unless-stopped

11. Feasibility Assessment

Assessed: 2026-06-17

11.1 Current State Summary

Item Status Notes
FastAPI Backend Complete Well-structured with routers, models, services
CORS Configuration Configured ALLOWED_ORIGINS in app/core/config.py - just needs to add Vite dev server port (localhost:5173)
Session API Endpoints Complete GET /api/v1/session/{id}/next_item, POST /session/{id}/submit_answer, POST /session/{id}/complete
Tryout API Endpoints Complete GET /tryout, GET /tryout/{id}/config, PUT /tryout/{id}/normalization
Admin Endpoints Complete Calibration, AI toggle, normalization reset
Redis/Celery Setup In docker-compose.dev.yml Used for AI generation
WordPress Auth Integration In place X-Website-ID header support via app/core/auth.py
Data Scrubbing (Security) Done Session endpoints already omit sensitive fields (correct_answer, ctt_p, irt_b)

11.2 Identified Gaps

Item Status Action Required
No frontend/ folder Missing Create React app from scratch
No root docker-compose.yml Missing Currently only docker-compose.dev.yml exists
Backend Dockerfile location ⚠️ Inconsistent Current Dockerfile is at root, needs to be moved to backend/
expires_at in session ⚠️ Not found Session model may not have server-side end time for timer sync
Nested admin routes ⚠️ Flat structure Current admin routes are flat, need hierarchical restructuring
Monorepo structure ⚠️ Not set up Root currently IS the backend, needs folder restructuring

11.3 Detailed Gap Analysis

Gap 1: Session Timer Implementation

The plan mentions fetching expires_at from the backend for server-synced timers. However, the Session model (app/models/session.py) does not have an explicit expires_at field. Only start_time exists.

Action needed: Add expires_at field to Session model and update session creation endpoint.

Gap 2: Monorepo Structure

Current repository layout:

yellow-bank-soal/          # Root = backend
├── app/                    # FastAPI app
├── Dockerfile              # Backend Dockerfile at root
├── docker-compose.dev.yml  # Dev setup

Plan requires:

yellow-bank-soal/           # Root = monorepo
├── backend/               # Move current root to backend/
├── frontend/              # New React app
└── docker-compose.yml      # New orchestration

Action needed: Significant file reorganization - move existing files to backend/ subfolder.

Gap 3: Nested Admin Routes

Current admin endpoints are flat:

  • /api/v1/admin/{tryout_id}/calibrate
  • /api/v1/tryout/{tryout_id}/config

Plan requires nested structure:

  • /api/v1/admin/tryouts/{id}/questions (doesn't exist)
  • /api/v1/admin/tryout/{id}/attempts (doesn't exist)

Action needed: Create new nested router structure in app/routers/admin/.

11.4 Feasibility Score: 7/10

Category Score Notes
Backend API Readiness 8/10 Core endpoints exist, minor gaps in session expiration
Infrastructure 6/10 Needs restructuring for monorepo
Auth Integration 9/10 WordPress JWT + X-Website-ID already in place
Docker Setup 5/10 Need new docker-compose.yml + frontend Dockerfile
Data Security 9/10 Already scrubbing sensitive fields
  1. Phase 0: Repository Restructuring (High Impact)

    • Move current root contents → backend/
    • Create frontend/ with Vite scaffold
    • Update Dockerfile references
    • Create root docker-compose.yml
  2. Phase 1: Backend Additions

    • Add expires_at to Session model
    • Create nested admin endpoints (/admin/tryout/:id/questions, etc.)
    • Update CORS for localhost:5173
  3. Phase 2-4: Frontend Build (Follow original plan)

11.6 Summary

The migration plan is doable but requires significant upfront work on repository restructuring and a few backend additions. The core FastAPI infrastructure is solid, and the auth/scoring logic is already well-implemented.

Main challenges:

  1. Monorepo migration - moving existing code to backend/ subfolder
  2. Session expiration tracking - adding server-side timer (expires_at)
  3. Nested admin routes - restructuring some API endpoints

Strengths:

  • Complete session/tryout API already exists
  • Data scrubbing already implemented
  • WordPress integration already in place
  • Redis/Celery for AI already configured