- 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
394 lines
20 KiB
Markdown
394 lines
20 KiB
Markdown
# **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**
|
|
|
|
* [Vite Documentation](https://vitejs.dev/)
|
|
* [Tailwind CSS Documentation](https://tailwindcss.com/)
|
|
* [shadcn/ui Documentation](https://ui.shadcn.com/)
|
|
* [TanStack Query Documentation](https://tanstack.com/query/latest)
|
|
* [React Router Documentation](https://reactrouter.com/)
|
|
|
|
## **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.
|
|
|
|
```dockerfile
|
|
# ... 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.
|
|
|
|
```dockerfile
|
|
# 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.**
|
|
|
|
```yaml
|
|
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 |
|
|
|
|
### **11.5 Recommended Execution Order**
|
|
|
|
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
|