- Restructure: move backend from new_project/ to backend/ - Add full React/TypeScript frontend (37 pages, 17 services, 16 type defs, 11 query hooks) - Add docs/ with SRS specs, user stories, and workflow documentation - Update .gitignore for new directory layout Workflows implemented: WF1 User Signup, WF2 Placement Test, WF3 Exam Configuration, WF4 General English Exam, WF5 Course Generation, WF6 Entity Student Onboarding, AI Course Generation, Adaptive Learning Engine UI, White-Label Branding, Score Release Made-with: Cursor
911 lines
37 KiB
Markdown
911 lines
37 KiB
Markdown
# EnCoach Frontend API Migration SRS
|
|
|
|
> **SUPERSEDED** -- This document has been replaced by `ENCOACH_UNIFIED_SRS.md` (v2.0). The migration is complete. The old `ielts-ui` Next.js frontend has been replaced by `encoach_frontend_new_v2` (React 18 + Vite + TypeScript), deployed at `http://5.189.151.117:3000`.
|
|
|
|
## ielts-ui Migration Guide: Next.js API Routes to Odoo 19
|
|
|
|
**Version:** 1.0
|
|
**Date:** March 11, 2026
|
|
**Status:** ~~Active~~ **SUPERSEDED**
|
|
**Audience:** Frontend developer(s) maintaining ielts-ui
|
|
**Companion document:** `ODOO_MIGRATION_SRS.md` (backend Odoo specification)
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Migration Overview](#1-migration-overview)
|
|
2. [Authentication Migration](#2-authentication-migration)
|
|
3. [API Base URL Change](#3-api-base-url-change)
|
|
4. [Endpoint-by-Endpoint Migration Map](#4-endpoint-by-endpoint-migration-map)
|
|
5. [SSR Migration Strategy](#5-ssr-migration-strategy)
|
|
6. [Files to Delete](#6-files-to-delete)
|
|
7. [Migration Checklist](#7-migration-checklist)
|
|
|
|
---
|
|
|
|
## 1. Migration Overview
|
|
|
|
### 1.1 What Is Changing
|
|
|
|
The ielts-ui Next.js application currently serves two roles:
|
|
|
|
1. **Browser UI** -- React pages, components, Zustand stores, hooks
|
|
2. **BFF (Backend-for-Frontend)** -- 108 API route files in `src/pages/api/` that query MongoDB directly, manage iron-session cookies, and proxy AI requests
|
|
|
|
After migration, ielts-ui serves **only the Browser UI**. All backend logic moves to Odoo 19. The `src/pages/api/` directory and all server-side database utilities are eliminated entirely.
|
|
|
|
### 1.2 Current Architecture (what the frontend sees)
|
|
|
|
```
|
|
Browser (React)
|
|
│
|
|
├── axios/SWR/fetch calls to /api/* (same origin)
|
|
│
|
|
└── Next.js API Routes (src/pages/api/)
|
|
├── iron-session cookie auth
|
|
├── MongoDB queries (via src/utils/*.be.ts)
|
|
└── Proxy to AI backend (via BACKEND_URL)
|
|
```
|
|
|
|
### 1.3 Target Architecture (what the frontend sees)
|
|
|
|
```
|
|
Browser (React)
|
|
│
|
|
├── axios/SWR/fetch calls to ODOO_URL/api/* (cross-origin)
|
|
│ └── Authorization: Bearer <JWT>
|
|
│
|
|
└── Odoo 19 (separate service)
|
|
├── JWT auth
|
|
├── PostgreSQL (replaces MongoDB)
|
|
└── All AI/ML handled internally by Odoo
|
|
```
|
|
|
|
### 1.4 What Does NOT Change
|
|
|
|
- All React page components, layouts, and UI rendering
|
|
- Zustand stores (`src/stores/exam/`, `src/stores/preferencesStore.ts`, etc.)
|
|
- All components in `src/components/` (they still call the same API shapes -- only the base URL and auth mechanism change)
|
|
- Styling (Tailwind, DaisyUI)
|
|
- TypeScript interfaces in `src/interfaces/`
|
|
- Client-side routing
|
|
- Strapi CMS and the landing page
|
|
|
|
### 1.5 Guiding Principle
|
|
|
|
Odoo's REST API is designed to return the **same JSON shapes** the frontend currently expects. If the frontend calls `GET /api/users/list` and expects `{ users: [...], total: N }`, Odoo's `GET /api/users/list` returns the same shape. The migration is primarily a **transport change** (base URL + auth header), not a data format change.
|
|
|
|
---
|
|
|
|
## 2. Authentication Migration
|
|
|
|
This is the most significant structural change.
|
|
|
|
### 2.1 Current Auth Pattern
|
|
|
|
| Aspect | Current Implementation |
|
|
|--------|----------------------|
|
|
| **Identity provider** | Firebase Auth (`signInWithEmailAndPassword`) |
|
|
| **Session** | iron-session (encrypted httpOnly cookie named `eCrop/ielts`) |
|
|
| **Client auth** | Automatic -- browser sends cookie on same-origin requests |
|
|
| **SSR auth** | `withIronSessionSsr` + `getServerSideProps` reads `req.session.user` |
|
|
| **User hook** | `useUser()` calls `GET /api/user` via SWR; cookie sent automatically |
|
|
| **Config** | `src/lib/session.ts` (`SECRET_COOKIE_PASSWORD`, `cookieName`) |
|
|
|
|
### 2.2 Target Auth Pattern
|
|
|
|
| Aspect | Target Implementation |
|
|
|--------|----------------------|
|
|
| **Identity provider** | Odoo 19 native auth (`res.users`) |
|
|
| **Session** | JWT token issued by Odoo |
|
|
| **Client auth** | `Authorization: Bearer <token>` header on every request |
|
|
| **SSR auth** | JWT stored in httpOnly cookie; `getServerSideProps` reads cookie and calls Odoo |
|
|
| **User hook** | `useUser()` calls `GET ODOO_URL/api/user` with JWT header |
|
|
| **Config** | `NEXT_PUBLIC_API_URL` env var |
|
|
|
|
### 2.3 New Auth Infrastructure
|
|
|
|
Create a new file `src/lib/api.ts`:
|
|
|
|
```typescript
|
|
import axios from "axios";
|
|
|
|
const api = axios.create({
|
|
baseURL: process.env.NEXT_PUBLIC_API_URL,
|
|
withCredentials: true,
|
|
});
|
|
|
|
api.interceptors.request.use((config) => {
|
|
const token = getToken();
|
|
if (token) {
|
|
config.headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
return config;
|
|
});
|
|
|
|
api.interceptors.response.use(
|
|
(response) => response,
|
|
(error) => {
|
|
if (error.response?.status === 401) {
|
|
clearToken();
|
|
if (typeof window !== "undefined") {
|
|
window.location.href = "/login";
|
|
}
|
|
}
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
export default api;
|
|
```
|
|
|
|
JWT storage helpers (also in `src/lib/api.ts` or a separate `src/lib/auth.ts`):
|
|
|
|
```typescript
|
|
import Cookies from "js-cookie";
|
|
|
|
const TOKEN_KEY = "encoach_token";
|
|
|
|
export function setToken(token: string) {
|
|
Cookies.set(TOKEN_KEY, token, { secure: true, sameSite: "lax" });
|
|
}
|
|
|
|
export function getToken(): string | undefined {
|
|
return Cookies.get(TOKEN_KEY);
|
|
}
|
|
|
|
export function clearToken() {
|
|
Cookies.remove(TOKEN_KEY);
|
|
}
|
|
```
|
|
|
|
**Note:** Using a cookie for JWT storage (rather than localStorage) is recommended because `getServerSideProps` can read cookies from the request headers, enabling SSR auth. The cookie is NOT httpOnly in this approach (so JS can read it for the axios interceptor). If httpOnly is required, use a thin proxy or Next.js middleware to inject the header.
|
|
|
|
### 2.4 Files That Change for Auth
|
|
|
|
| File | Current | Target |
|
|
|------|---------|--------|
|
|
| `src/pages/login.tsx` | `axios.post("/api/login", { email, password })` | `api.post("/api/login", { email, password })` -- store JWT from response |
|
|
| `src/pages/register.tsx` | `fetch` to `/api/register` (SSR) + code lookup | `api.post("/api/register", payload)` -- store JWT from response |
|
|
| `src/pages/official-exam.tsx` | `axios.post("/api/logout")` | `api.post("/api/logout")` + `clearToken()` |
|
|
| `src/components/Sidebar.tsx` | `axios.post("/api/logout")` | `api.post("/api/logout")` + `clearToken()` |
|
|
| `src/components/MobileMenu.tsx` | `axios.post("/api/logout")` | `api.post("/api/logout")` + `clearToken()` |
|
|
| `src/utils/email.ts` | `axios.post("/api/reset/sendVerification")` | `api.post("/api/reset/sendVerification")` |
|
|
| `src/hooks/useUser.tsx` | SWR fetcher uses `axios.get("/api/user")` | SWR fetcher uses `api.get("/api/user")` |
|
|
| `src/lib/session.ts` | iron-session config | **DELETE** -- replaced by `src/lib/api.ts` |
|
|
|
|
### 2.5 Login Flow Change
|
|
|
|
**Current:**
|
|
1. User submits email + password
|
|
2. `POST /api/login` (same origin)
|
|
3. API route calls Firebase Auth, looks up user in MongoDB, saves to iron-session
|
|
4. Response: user JSON + `Set-Cookie` header
|
|
5. `mutateUser(response.data)` updates SWR cache
|
|
|
|
**Target:**
|
|
1. User submits email + password
|
|
2. `POST ODOO_URL/api/login` (cross-origin)
|
|
3. Odoo validates credentials, returns `{ user: {...}, token: "jwt..." }`
|
|
4. Frontend stores JWT via `setToken(response.data.token)`
|
|
5. `mutateUser(response.data.user)` updates SWR cache
|
|
|
|
**Login page change (`src/pages/login.tsx`):**
|
|
|
|
```typescript
|
|
// BEFORE
|
|
const response = await axios.post("/api/login", { email, password });
|
|
mutateUser(response.data);
|
|
|
|
// AFTER
|
|
import api, { setToken } from "@/lib/api";
|
|
const response = await api.post("/api/login", { email, password });
|
|
setToken(response.data.token);
|
|
mutateUser(response.data.user);
|
|
```
|
|
|
|
### 2.6 Logout Flow Change
|
|
|
|
**Current:** `POST /api/logout` destroys iron-session.
|
|
|
|
**Target:** `POST ODOO_URL/api/logout` (optional server-side invalidation) + `clearToken()` client-side.
|
|
|
|
```typescript
|
|
// BEFORE
|
|
await axios.post("/api/logout");
|
|
mutateUser(null);
|
|
router.push("/login");
|
|
|
|
// AFTER
|
|
import api, { clearToken } from "@/lib/api";
|
|
await api.post("/api/logout").catch(() => {});
|
|
clearToken();
|
|
mutateUser(null);
|
|
router.push("/login");
|
|
```
|
|
|
|
---
|
|
|
|
## 3. API Base URL Change
|
|
|
|
### 3.1 Environment Variable
|
|
|
|
Add to `.env.local`:
|
|
|
|
```
|
|
NEXT_PUBLIC_API_URL=https://api.encoach.com
|
|
```
|
|
|
|
Development:
|
|
|
|
```
|
|
NEXT_PUBLIC_API_URL=http://localhost:8069
|
|
```
|
|
|
|
### 3.2 Shared Axios Instance
|
|
|
|
Replace all direct `axios` imports with the shared `api` instance from `src/lib/api.ts` (defined in Section 2.3).
|
|
|
|
**Search and replace pattern across the codebase:**
|
|
|
|
```typescript
|
|
// BEFORE (scattered across ~40 files)
|
|
import axios from "axios";
|
|
axios.get("/api/...");
|
|
axios.post("/api/...");
|
|
axios.patch("/api/...");
|
|
axios.delete("/api/...");
|
|
|
|
// AFTER
|
|
import api from "@/lib/api";
|
|
api.get("/api/...");
|
|
api.post("/api/...");
|
|
api.patch("/api/...");
|
|
api.delete("/api/...");
|
|
```
|
|
|
|
The `/api/...` path stays the same -- the `baseURL` on the axios instance prepends the Odoo host.
|
|
|
|
### 3.3 SWR Fetcher Update
|
|
|
|
Update `src/hooks/useUser.tsx`:
|
|
|
|
```typescript
|
|
// BEFORE
|
|
const fetcher = (url: string) => axios.get(url).then((res) => res.data);
|
|
|
|
// AFTER
|
|
import api from "@/lib/api";
|
|
const fetcher = (url: string) => api.get(url).then((res) => res.data);
|
|
```
|
|
|
|
### 3.4 fetch() Calls
|
|
|
|
A few files use `fetch()` instead of axios (primarily `src/components/ExamEditor/ImportExam/WordUploader.tsx`). These also need the base URL and auth header:
|
|
|
|
```typescript
|
|
// BEFORE
|
|
const res = await fetch(`/api/exam/${module}/import/`, { method: "POST", body: formData });
|
|
|
|
// AFTER
|
|
import { getToken } from "@/lib/api";
|
|
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/exam/${module}/import/`, {
|
|
method: "POST",
|
|
body: formData,
|
|
headers: { Authorization: `Bearer ${getToken()}` },
|
|
});
|
|
```
|
|
|
|
**Note:** `fetch()` calls to blob URLs (e.g., `fetch(blobUrl)` for audio/video previews) do NOT change. Only calls to `/api/*` endpoints change.
|
|
|
|
---
|
|
|
|
## 4. Endpoint-by-Endpoint Migration Map
|
|
|
|
For each API call the frontend makes, this section specifies the current URL, new Odoo URL, HTTP method, and every file that makes the call.
|
|
|
|
**Convention:** Where the Odoo URL is the same path as the current URL (just different host), only the base URL changes. These are marked "Path unchanged." Where the Odoo URL differs, the new path is shown.
|
|
|
|
### 4.1 Auth
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 1 | POST | `/api/login` | Path unchanged | `src/pages/login.tsx` |
|
|
| 2 | POST | `/api/logout` | Path unchanged | `src/pages/official-exam.tsx`, `src/components/Sidebar.tsx`, `src/components/MobileMenu.tsx` |
|
|
| 3 | POST | `/api/reset` | Path unchanged | `src/pages/login.tsx` |
|
|
| 4 | POST | `/api/reset/sendVerification` | Path unchanged | `src/utils/email.ts` |
|
|
|
|
**Response change for login:** Odoo returns `{ user: {...}, token: "jwt..." }` instead of just the user object. Frontend must extract and store the token.
|
|
|
|
### 4.2 User
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 5 | GET | `/api/user` | Path unchanged | `src/hooks/useUser.tsx` (SWR) |
|
|
| 6 | DELETE | `/api/user` | Path unchanged | (called from useUser context) |
|
|
| 7 | POST | `/api/users/update` | `PATCH /api/users/update` | `src/pages/profile.tsx`, `src/components/UserCard.tsx` |
|
|
| 8 | GET | `/api/users/list` | Path unchanged | `src/utils/groups.ts` |
|
|
| 9 | GET | `/api/users/{id}` | Path unchanged | `src/utils/groups.ts` |
|
|
| 10 | POST | `/api/users/controller` | Path unchanged | `src/components/Imports/StudentClassroomTransfer.tsx` |
|
|
| 11 | GET | `/api/users/balance` | Path unchanged | `src/hooks/useUserBalance.tsx` (if used) |
|
|
|
|
**Note on #7:** The current frontend uses `POST` for user updates. Odoo convention is `PATCH`. Either Odoo can accept both, or the frontend changes the method.
|
|
|
|
### 4.3 Registration / Codes
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 12 | POST | `/api/register` | Path unchanged | `src/pages/register.tsx` (SSR currently; move to client-side) |
|
|
| 13 | GET | `/api/code/{code}` | Path unchanged | `src/pages/register.tsx` |
|
|
|
|
**Note on #12:** Registration is currently handled in `getServerSideProps`. With Odoo, move registration to a client-side form submission. The register page should call `api.post("/api/register", payload)` and store the returned JWT.
|
|
|
|
### 4.4 Exams
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 14 | GET | `/api/exam` | Path unchanged | `src/hooks/useExams.tsx` |
|
|
| 15 | GET | `/api/exam/{module}?{params}` | Path unchanged | `src/utils/exams.ts` (`getExam`) |
|
|
| 16 | GET | `/api/exam/{module}/{id}` | Path unchanged | `src/utils/exams.ts` (`getExamById`) |
|
|
| 17 | POST | `/api/exam/{module}` | Path unchanged | `src/components/ExamEditor/SettingsEditor/reading/index.tsx`, `writing/index.tsx`, `listening/index.tsx`, `speaking/index.tsx`, `level.tsx` |
|
|
| 18 | PATCH | `/api/exam/{module}/{id}` | Path unchanged | `src/pages/(admin)/Lists/ExamList.tsx`, `src/pages/approval-workflows/[id]/index.tsx` |
|
|
| 19 | DELETE | `/api/exam/{module}/{id}` | Path unchanged | `src/pages/(admin)/Lists/ExamList.tsx` |
|
|
| 20 | GET | `/api/exam/avatars` | Path unchanged | `src/pages/generation.tsx` |
|
|
| 21 | GET/POST | `/api/exam/generate/{module}/{sectionId}` | Path unchanged | `src/components/ExamEditor/SettingsEditor/Shared/Generate.ts` |
|
|
| 22 | POST | `/api/exam/media/speaking` | Path unchanged | `src/components/ExamEditor/SettingsEditor/Shared/generateVideos.ts` |
|
|
| 23 | POST | `/api/exam/media/listening` | Path unchanged | `src/components/ExamEditor/SettingsEditor/listening/components.tsx` |
|
|
| 24 | POST | `/api/exam/media/instructions` | Path unchanged | `src/components/ExamEditor/Standalone/ListeningInstructions/index.tsx` |
|
|
| 25 | POST | `/api/exam/{module}/import/` | Path unchanged | `src/components/ExamEditor/ImportExam/WordUploader.tsx` (uses `fetch`) |
|
|
|
|
### 4.5 Evaluation / Grading
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 26 | POST | `/api/evaluate/writing` | Path unchanged | `src/utils/evaluation.ts` |
|
|
| 27 | POST | `/api/evaluate/speaking` | Path unchanged | `src/utils/evaluation.ts` |
|
|
| 28 | POST | `/api/evaluate/interactiveSpeaking` | Path unchanged | `src/utils/evaluation.ts` |
|
|
| 29 | GET | `/api/evaluate/status?...` | Path unchanged | `src/utils/evaluation.ts` (polling) |
|
|
| 30 | GET | `/api/evaluate/fetchSolutions?...` | Path unchanged | `src/utils/evaluation.ts` |
|
|
| 31 | POST | `/api/grading` | Path unchanged | `src/pages/api/grading/index.ts` (if called by frontend) |
|
|
| 32 | POST | `/api/grading/multiple` | Path unchanged | (if called by frontend) |
|
|
|
|
### 4.6 Storage
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 33 | POST | `/api/storage` | Path unchanged | `src/components/ExamEditor/SettingsEditor/writing/index.tsx`, `speaking/index.tsx`, `listening/index.tsx`, `level.tsx` |
|
|
| 34 | POST | `/api/storage/delete` | `DELETE /api/storage/{id}` (or keep POST) | `src/utils/evaluation.ts` |
|
|
|
|
**Note:** Odoo will use `ir.attachment` for file storage. The response must return a publicly accessible URL for the uploaded file, matching the current behavior.
|
|
|
|
### 4.7 Speaking Audio
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 35 | POST | `/api/speaking` | Path unchanged | `src/components/Solutions/Speaking.tsx`, `src/components/Solutions/InteractiveSpeaking.tsx` |
|
|
|
|
### 4.8 Sessions
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 36 | POST | `/api/sessions` | Path unchanged | `src/stores/exam/index.ts` (Zustand `saveSession`) |
|
|
| 37 | DELETE | `/api/sessions/{id}` | Path unchanged | `src/components/Medium/SessionCard.tsx` |
|
|
|
|
### 4.9 Stats
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 38 | POST | `/api/stats` | Path unchanged | `src/stores/exam/index.ts` (Zustand `saveStats`) |
|
|
| 39 | GET | `/api/stats/{id}` | Path unchanged | `src/pages/training/[id]/index.tsx` |
|
|
| 40 | POST | `/api/statistical` | Path unchanged | `src/pages/statistical.tsx` |
|
|
|
|
### 4.10 Training
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 41 | POST | `/api/training` | Path unchanged | `src/pages/training/index.tsx` |
|
|
| 42 | GET | `/api/training/{id}` | Path unchanged | `src/pages/training/[id]/index.tsx` |
|
|
| 43 | GET | `/api/training/walkthrough` | Path unchanged | `src/pages/training/[id]/index.tsx` |
|
|
|
|
### 4.11 Groups
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 44 | GET | `/api/groups?participant={userId}` | Path unchanged | `src/utils/groups.ts` |
|
|
| 45 | POST | `/api/groups` | Path unchanged | `src/pages/classrooms/create.tsx` |
|
|
| 46 | PATCH | `/api/groups/{id}` | Path unchanged | `src/pages/classrooms/[id].tsx` |
|
|
| 47 | DELETE | `/api/groups/{id}` | Path unchanged | `src/pages/classrooms/[id].tsx` |
|
|
|
|
### 4.12 Entities
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 48 | POST | `/api/entities` | Path unchanged | `src/pages/entities/create.tsx` |
|
|
| 49 | PATCH | `/api/entities/{id}` | Path unchanged | `src/pages/entities/[id]/index.tsx` |
|
|
| 50 | DELETE | `/api/entities/{id}` | Path unchanged | `src/pages/entities/[id]/index.tsx` |
|
|
| 51 | PATCH | `/api/entities/{id}/users` | Path unchanged | `src/pages/entities/[id]/index.tsx` |
|
|
|
|
### 4.13 Roles
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 52 | POST | `/api/roles` | Path unchanged | `src/pages/entities/[id]/roles/index.tsx` |
|
|
| 53 | PATCH | `/api/roles/{id}` | Path unchanged | `src/pages/entities/[id]/roles/[role].tsx` |
|
|
| 54 | DELETE | `/api/roles/{id}` | Path unchanged | `src/pages/entities/[id]/roles/[role].tsx` |
|
|
| 55 | POST | `/api/roles/{id}/users` | Path unchanged | `src/pages/entities/[id]/index.tsx` |
|
|
|
|
### 4.14 Permissions
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 56 | PATCH | `/api/permissions/{id}` | Path unchanged | `src/pages/permissions/[id].tsx` |
|
|
|
|
### 4.15 Payments
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 57 | POST | `/api/payments` | Path unchanged | `src/pages/payment-record.tsx` |
|
|
| 58 | PATCH | `/api/payments/{id}` | Path unchanged | `src/pages/payment-record.tsx` |
|
|
| 59 | DELETE | `/api/payments/{id}` | Path unchanged | `src/pages/payment-record.tsx` |
|
|
| 60 | GET/POST/DELETE | `/api/payments/files/{type}/{paymentId}` | Path unchanged | `src/components/PaymentAssetManager.tsx` |
|
|
| 61 | POST | `/api/paymob` | Path unchanged | `src/components/PaymobPayment.tsx` |
|
|
|
|
**Bug fix opportunity:** In `src/pages/payment-record.tsx`, the PATCH call uses `api/payments/${id}` without a leading `/`. When switching to the shared axios instance with a `baseURL`, this will work correctly. But verify during testing.
|
|
|
|
### 4.16 Assignments
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 62 | POST | `/api/assignments` | Path unchanged | `src/pages/assignments/creator/index.tsx` |
|
|
| 63 | PATCH | `/api/assignments/{id}` | Path unchanged | `src/pages/assignments/creator/[id].tsx`, `src/pages/assignments/[id].tsx` |
|
|
| 64 | DELETE | `/api/assignments/{id}` | Path unchanged | `src/pages/assignments/creator/[id].tsx`, `src/pages/assignments/[id].tsx` |
|
|
| 65 | POST | `/api/assignments/{id}/start` | Path unchanged | `src/pages/assignments/creator/[id].tsx`, `src/pages/assignments/[id].tsx` |
|
|
|
|
### 4.17 Invites
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 66 | GET | `/api/invites/{decision}/{id}` | `POST /api/invites/{decision}/{id}` | `src/components/Medium/InviteCard.tsx`, `src/components/Medium/InviteWithUserCard.tsx` |
|
|
|
|
**Note:** Currently uses GET for accept/decline actions (side-effectful GET). Odoo should accept POST. Consider updating the frontend to use POST -- it's a more correct HTTP method for state-changing operations.
|
|
|
|
### 4.18 Approval Workflows
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 67 | POST | `/api/approval-workflows/create` | Path unchanged | `src/pages/approval-workflows/create.tsx` |
|
|
| 68 | PUT | `/api/approval-workflows/{id}` | Path unchanged | `src/pages/approval-workflows/[id]/index.tsx` |
|
|
| 69 | PUT | `/api/approval-workflows/{id}/edit` | Path unchanged | `src/pages/approval-workflows/[id]/edit.tsx` |
|
|
| 70 | DELETE | `/api/approval-workflows/{id}` | Path unchanged | `src/pages/approval-workflows/index.tsx` |
|
|
|
|
### 4.19 Transcription
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 71 | POST | `/api/transcribe` | Path unchanged | (called from evaluation utils if needed) |
|
|
|
|
### 4.20 Batch Users
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 72 | POST | `/api/batch_users` | Path unchanged | (admin import page) |
|
|
|
|
### 4.21 Make User (Admin)
|
|
|
|
| # | Method | Current URL | Odoo URL | Files |
|
|
|---|--------|-------------|----------|-------|
|
|
| 73 | POST | `/api/make_user` | Path unchanged | (admin user creation) |
|
|
|
|
---
|
|
|
|
## 5. SSR Migration Strategy
|
|
|
|
### 5.1 Current SSR Auth Pattern
|
|
|
|
~35 pages use this pattern:
|
|
|
|
```typescript
|
|
import { withIronSessionSsr } from "iron-session/next";
|
|
import { sessionOptions } from "@/lib/session";
|
|
|
|
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
|
|
const user = await requestUser(req, res);
|
|
if (!user || !user.isVerified) return redirect("/login");
|
|
return { props: { user } };
|
|
}, sessionOptions);
|
|
```
|
|
|
|
### 5.2 Recommended: JWT Cookie + Server-Side Validation (Option A)
|
|
|
|
Store the JWT in a regular cookie (set by `js-cookie` on login). In `getServerSideProps`, read the cookie from the request and call Odoo to validate.
|
|
|
|
**New helper `src/lib/ssr-auth.ts`:**
|
|
|
|
```typescript
|
|
import type { GetServerSidePropsContext, GetServerSidePropsResult } from "next";
|
|
|
|
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
|
const TOKEN_COOKIE = "encoach_token";
|
|
|
|
export function withAuth<P extends Record<string, unknown>>(
|
|
handler: (ctx: GetServerSidePropsContext, user: User) => Promise<GetServerSidePropsResult<P>>
|
|
) {
|
|
return async (ctx: GetServerSidePropsContext): Promise<GetServerSidePropsResult<P>> => {
|
|
const token = ctx.req.cookies[TOKEN_COOKIE];
|
|
if (!token) {
|
|
return { redirect: { destination: "/login", permanent: false } };
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(`${API_URL}/api/user`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
if (!res.ok) throw new Error("Unauthorized");
|
|
const user = await res.json();
|
|
if (!user.isVerified) {
|
|
return { redirect: { destination: "/login", permanent: false } };
|
|
}
|
|
return handler(ctx, user);
|
|
} catch {
|
|
return { redirect: { destination: "/login", permanent: false } };
|
|
}
|
|
};
|
|
}
|
|
```
|
|
|
|
**Page migration example:**
|
|
|
|
```typescript
|
|
// BEFORE
|
|
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
|
|
const user = await requestUser(req, res);
|
|
if (!user) return redirect("/login");
|
|
// ... fetch additional data using user ...
|
|
return { props: { user, someData } };
|
|
}, sessionOptions);
|
|
|
|
// AFTER
|
|
export const getServerSideProps = withAuth(async (ctx, user) => {
|
|
// ... fetch additional data using user ...
|
|
return { props: { user, someData } };
|
|
});
|
|
```
|
|
|
|
### 5.3 Alternative: Client-Only Auth (Option B)
|
|
|
|
Remove all `getServerSideProps` auth checks. Every page uses `useUser({ redirectTo: "/login" })` to protect itself.
|
|
|
|
**Pros:** Simpler; no SSR auth at all.
|
|
**Cons:** Brief flash of unauthenticated content; pages that fetch data server-side need to move data fetching to client-side.
|
|
|
|
### 5.4 Pages Requiring SSR Migration
|
|
|
|
Every page below currently uses `withIronSessionSsr` and needs to switch to `withAuth` (Option A) or remove SSR auth (Option B):
|
|
|
|
| Page | SSR Data Fetching |
|
|
|------|-------------------|
|
|
| `src/pages/index.tsx` | User only (redirects to dashboard) |
|
|
| `src/pages/login.tsx` | Reverse guard (redirect if already logged in) |
|
|
| `src/pages/dashboard/index.tsx` | User only |
|
|
| `src/pages/dashboard/teacher.tsx` | User + groups + assignments + stats |
|
|
| `src/pages/dashboard/student.tsx` | User + assignments + stats |
|
|
| `src/pages/dashboard/corporate.tsx` | User + entity data |
|
|
| `src/pages/dashboard/mastercorporate.tsx` | User + multi-entity data |
|
|
| `src/pages/dashboard/admin.tsx` | User + platform stats |
|
|
| `src/pages/dashboard/developer.tsx` | User |
|
|
| `src/pages/users/index.tsx` | User + user list |
|
|
| `src/pages/users/performance.tsx` | User + performance data |
|
|
| `src/pages/training/index.tsx` | User |
|
|
| `src/pages/training/[id]/index.tsx` | User + training data |
|
|
| `src/pages/tickets.tsx` | User + tickets |
|
|
| `src/pages/stats.tsx` | User + stats |
|
|
| `src/pages/statistical.tsx` | User |
|
|
| `src/pages/settings.tsx` | User |
|
|
| `src/pages/record.tsx` | User |
|
|
| `src/pages/profile.tsx` | User |
|
|
| `src/pages/popout.tsx` | User (session-based) |
|
|
| `src/pages/permissions/index.tsx` | User + permissions |
|
|
| `src/pages/permissions/[id].tsx` | User + permission detail |
|
|
| `src/pages/payment.tsx` | User |
|
|
| `src/pages/payment-record.tsx` | User + payments |
|
|
| `src/pages/official-exam.tsx` | User |
|
|
| `src/pages/generation.tsx` | User |
|
|
| `src/pages/exercises.tsx` | User + exam data |
|
|
| `src/pages/exam.tsx` | User + exam + session data |
|
|
| `src/pages/entities/index.tsx` | User + entities |
|
|
| `src/pages/entities/create.tsx` | User |
|
|
| `src/pages/entities/[id]/index.tsx` | User + entity detail |
|
|
| `src/pages/entities/[id]/roles/index.tsx` | User + roles |
|
|
| `src/pages/entities/[id]/roles/[role].tsx` | User + role detail |
|
|
| `src/pages/classrooms/*.tsx` | User + groups |
|
|
| `src/pages/assignments/*.tsx` | User + assignments |
|
|
| `src/pages/approval-workflows/*.tsx` | User + workflows |
|
|
|
|
**For pages that only need user auth (no server-side data):** Replace `getServerSideProps` with client-side `useUser({ redirectTo: "/login" })`.
|
|
|
|
**For pages that fetch additional data server-side:** Use `withAuth` and make the data-fetching calls to Odoo using the JWT token from the cookie.
|
|
|
|
---
|
|
|
|
## 6. Files to Delete
|
|
|
|
After migration, the following files are no longer needed. They represent the BFF layer being replaced by Odoo.
|
|
|
|
### 6.1 API Route Files (108 files)
|
|
|
|
Delete the entire `src/pages/api/` directory:
|
|
|
|
**Auth & User:**
|
|
- `src/pages/api/login.ts`
|
|
- `src/pages/api/logout.ts`
|
|
- `src/pages/api/register.ts`
|
|
- `src/pages/api/user.ts`
|
|
- `src/pages/api/make_user.ts`
|
|
- `src/pages/api/batch_users.ts`
|
|
- `src/pages/api/reset/index.ts`
|
|
- `src/pages/api/reset/confirm.ts`
|
|
- `src/pages/api/reset/sendVerification.ts`
|
|
- `src/pages/api/reset/verify.ts`
|
|
- `src/pages/api/users/update.ts`
|
|
- `src/pages/api/users/search.ts`
|
|
- `src/pages/api/users/list.ts`
|
|
- `src/pages/api/users/controller.ts`
|
|
- `src/pages/api/users/balance.ts`
|
|
- `src/pages/api/users/agents/index.ts`
|
|
- `src/pages/api/users/agents/[code].ts`
|
|
- `src/pages/api/users/[id].ts`
|
|
|
|
**Exams:**
|
|
- `src/pages/api/exam/index.ts`
|
|
- `src/pages/api/exam/upload.ts`
|
|
- `src/pages/api/exam/avatars.ts`
|
|
- `src/pages/api/exam/generate/[...module].ts`
|
|
- `src/pages/api/exam/media/[...module].ts`
|
|
- `src/pages/api/exam/media/poll.ts`
|
|
- `src/pages/api/exam/media/instructions.ts`
|
|
- `src/pages/api/exam/[module]/index.ts`
|
|
- `src/pages/api/exam/[module]/import.ts`
|
|
- `src/pages/api/exam/[module]/[id].ts`
|
|
|
|
**Evaluation:**
|
|
- `src/pages/api/evaluate/writing.ts`
|
|
- `src/pages/api/evaluate/speaking.ts`
|
|
- `src/pages/api/evaluate/interactiveSpeaking.ts`
|
|
- `src/pages/api/evaluate/status.ts`
|
|
- `src/pages/api/evaluate/fetchSolutions.ts`
|
|
|
|
**Grading:**
|
|
- `src/pages/api/grading/index.ts`
|
|
- `src/pages/api/grading/multiple.ts`
|
|
|
|
**Stats & Sessions:**
|
|
- `src/pages/api/stats/index.ts`
|
|
- `src/pages/api/stats/update.ts`
|
|
- `src/pages/api/stats/disabled.ts`
|
|
- `src/pages/api/stats/user/[user].ts`
|
|
- `src/pages/api/stats/session/[session].ts`
|
|
- `src/pages/api/stats/[id]/index.ts`
|
|
- `src/pages/api/stats/[id]/[export]/pdf.tsx`
|
|
- `src/pages/api/statistical.ts`
|
|
- `src/pages/api/sessions/index.ts`
|
|
- `src/pages/api/sessions/[id].ts`
|
|
|
|
**Training:**
|
|
- `src/pages/api/training/index.ts`
|
|
- `src/pages/api/training/[id].ts`
|
|
- `src/pages/api/training/user/[user].ts`
|
|
- `src/pages/api/training/walkthrough/index.ts`
|
|
|
|
**Entities & Groups:**
|
|
- `src/pages/api/entities/index.ts`
|
|
- `src/pages/api/entities/users.ts`
|
|
- `src/pages/api/entities/groups.ts`
|
|
- `src/pages/api/entities/[id]/index.ts`
|
|
- `src/pages/api/entities/[id]/users.ts`
|
|
- `src/pages/api/groups/index.ts`
|
|
- `src/pages/api/groups/controller.ts`
|
|
- `src/pages/api/groups/[id].ts`
|
|
|
|
**Roles & Permissions:**
|
|
- `src/pages/api/roles/index.ts`
|
|
- `src/pages/api/roles/[id]/index.ts`
|
|
- `src/pages/api/roles/[id]/users.ts`
|
|
- `src/pages/api/permissions/index.ts`
|
|
- `src/pages/api/permissions/bootstrap.ts`
|
|
- `src/pages/api/permissions/[id].ts`
|
|
|
|
**Invites & Codes:**
|
|
- `src/pages/api/invites/index.ts`
|
|
- `src/pages/api/invites/[id].ts`
|
|
- `src/pages/api/invites/accept/[id].ts`
|
|
- `src/pages/api/invites/decline/[id].ts`
|
|
- `src/pages/api/code/index.ts`
|
|
- `src/pages/api/code/entities.ts`
|
|
- `src/pages/api/code/[id].ts`
|
|
|
|
**Payments:**
|
|
- `src/pages/api/stripe.ts`
|
|
- `src/pages/api/paypal/index.ts`
|
|
- `src/pages/api/paypal/approve.ts`
|
|
- `src/pages/api/paypal/raas.ts`
|
|
- `src/pages/api/paymob/index.ts`
|
|
- `src/pages/api/paymob/webhook.ts`
|
|
- `src/pages/api/payments/index.ts`
|
|
- `src/pages/api/payments/[id].ts`
|
|
- `src/pages/api/payments/assigned.ts`
|
|
- `src/pages/api/payments/paypal.ts`
|
|
- `src/pages/api/payments/files/[type]/[paymentId].ts`
|
|
- `src/pages/api/packages/index.ts`
|
|
- `src/pages/api/packages/[id].ts`
|
|
- `src/pages/api/discounts/index.ts`
|
|
- `src/pages/api/discounts/[id].ts`
|
|
|
|
**Assignments:**
|
|
- `src/pages/api/assignments/index.ts`
|
|
- `src/pages/api/assignments/corporate/index.ts`
|
|
- `src/pages/api/assignments/corporate/[id].ts`
|
|
- `src/pages/api/assignments/[id]/index.ts`
|
|
- `src/pages/api/assignments/[id]/start.ts`
|
|
- `src/pages/api/assignments/[id]/release.ts`
|
|
- `src/pages/api/assignments/[id]/archive.tsx`
|
|
- `src/pages/api/assignments/[id]/unarchive.tsx`
|
|
- `src/pages/api/assignments/[id]/[export]/pdf.tsx`
|
|
- `src/pages/api/assignments/[id]/[export]/excel.ts`
|
|
|
|
**Approval Workflows:**
|
|
- `src/pages/api/approval-workflows/index.ts`
|
|
- `src/pages/api/approval-workflows/create.ts`
|
|
- `src/pages/api/approval-workflows/[id]/index.ts`
|
|
- `src/pages/api/approval-workflows/[id]/edit.ts`
|
|
|
|
**Other:**
|
|
- `src/pages/api/storage/index.ts`
|
|
- `src/pages/api/storage/insert.ts`
|
|
- `src/pages/api/storage/delete.ts`
|
|
- `src/pages/api/speaking.ts`
|
|
- `src/pages/api/transcribe/index.ts`
|
|
- `src/pages/api/tickets/index.ts`
|
|
- `src/pages/api/tickets/[id].ts`
|
|
- `src/pages/api/tickets/assignedToUser/index.ts`
|
|
- `src/pages/api/hello.ts`
|
|
|
|
### 6.2 Backend Utility Files (14 files)
|
|
|
|
These files contain MongoDB query logic used by API routes. Delete all:
|
|
|
|
- `src/utils/approval.workflows.be.ts`
|
|
- `src/utils/assignments.be.ts`
|
|
- `src/utils/codes.be.ts`
|
|
- `src/utils/disabled.be.ts`
|
|
- `src/utils/entities.be.ts`
|
|
- `src/utils/exams.be.ts`
|
|
- `src/utils/grading.be.ts`
|
|
- `src/utils/groups.be.ts`
|
|
- `src/utils/invites.be.ts`
|
|
- `src/utils/permissions.be.ts`
|
|
- `src/utils/roles.be.ts`
|
|
- `src/utils/sessions.be.ts`
|
|
- `src/utils/stats.be.ts`
|
|
- `src/utils/users.be.ts`
|
|
|
|
### 6.3 Library Files (3 files)
|
|
|
|
- `src/lib/session.ts` -- iron-session config (replaced by `src/lib/api.ts`)
|
|
- `src/lib/mongodb.ts` -- MongoDB connection (no longer needed)
|
|
- `src/lib/createWorkflowsOnExamCreation.ts` -- server-side workflow logic (moved to Odoo)
|
|
|
|
**Keep:**
|
|
- `src/lib/utils.ts` -- general client-side utilities
|
|
- `src/lib/formidable-serverless.d.ts` -- only needed if any remaining file upload logic stays; likely deletable
|
|
|
|
### 6.4 Firebase Files
|
|
|
|
If Odoo replaces Firebase Auth entirely, also remove:
|
|
|
|
- `src/firebase/` directory (Firebase client config)
|
|
- `src/constants/platform.json` (Firebase service account -- server-side only)
|
|
- `src/constants/staging.json` (Firebase service account -- server-side only)
|
|
|
|
### 6.5 Dependencies to Remove from package.json
|
|
|
|
```
|
|
iron-session
|
|
firebase
|
|
firebase-admin
|
|
mongodb
|
|
@beam-australia/react-env (if no longer needed for client env)
|
|
bcrypt
|
|
formidable
|
|
```
|
|
|
|
### 6.6 Dependencies to Add
|
|
|
|
```
|
|
js-cookie (JWT cookie storage)
|
|
@types/js-cookie (TypeScript types)
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Migration Checklist
|
|
|
|
A step-by-step checklist for the frontend developer. Complete in order.
|
|
|
|
### Phase 1: Infrastructure Setup
|
|
|
|
- [ ] **1.1** Add `NEXT_PUBLIC_API_URL` to `.env.local` (point to Odoo dev instance)
|
|
- [ ] **1.2** Create `src/lib/api.ts` with shared axios instance, base URL, and JWT interceptor (Section 2.3)
|
|
- [ ] **1.3** Create `src/lib/auth.ts` with `setToken()`, `getToken()`, `clearToken()` helpers
|
|
- [ ] **1.4** Add `js-cookie` dependency: `yarn add js-cookie @types/js-cookie`
|
|
- [ ] **1.5** Create `src/lib/ssr-auth.ts` with `withAuth()` helper for SSR (Section 5.2)
|
|
|
|
### Phase 2: Auth Migration
|
|
|
|
- [ ] **2.1** Update `src/pages/login.tsx`: use `api.post()`, store JWT, extract user from response
|
|
- [ ] **2.2** Update `src/pages/register.tsx`: move registration to client-side, use `api.post()`
|
|
- [ ] **2.3** Update logout calls in `Sidebar.tsx`, `MobileMenu.tsx`, `official-exam.tsx`: use `api.post()` + `clearToken()`
|
|
- [ ] **2.4** Update `src/hooks/useUser.tsx`: use `api` instance for SWR fetcher
|
|
- [ ] **2.5** Update `src/utils/email.ts`: use `api` instance
|
|
- [ ] **2.6** Verify login/logout/register work end-to-end with Odoo
|
|
|
|
### Phase 3: SSR Migration
|
|
|
|
- [ ] **3.1** For each page in the SSR page list (Section 5.4), replace `withIronSessionSsr` with `withAuth` or client-side `useUser({ redirectTo: "/login" })`
|
|
- [ ] **3.2** Update pages that fetch server-side data to call Odoo with the JWT token
|
|
- [ ] **3.3** Verify all ~35 protected pages redirect correctly when not authenticated
|
|
- [ ] **3.4** Verify the login page redirects correctly when already authenticated
|
|
|
|
### Phase 4: API Call Migration (domain by domain)
|
|
|
|
For each domain, replace `axios` with `api` from `@/lib/api`:
|
|
|
|
- [ ] **4.1** User endpoints (`src/pages/profile.tsx`, `src/components/UserCard.tsx`, `src/utils/groups.ts`, `src/components/Imports/StudentClassroomTransfer.tsx`)
|
|
- [ ] **4.2** Exam endpoints (`src/utils/exams.ts`, `src/hooks/useExams.tsx`, `src/pages/generation.tsx`, all `ExamEditor` files, `ExamList.tsx`)
|
|
- [ ] **4.3** Evaluation endpoints (`src/utils/evaluation.ts`)
|
|
- [ ] **4.4** Storage endpoints (all `ExamEditor/SettingsEditor` files, `src/utils/evaluation.ts`)
|
|
- [ ] **4.5** Speaking audio (`src/components/Solutions/Speaking.tsx`, `InteractiveSpeaking.tsx`)
|
|
- [ ] **4.6** Sessions (`src/stores/exam/index.ts`, `src/components/Medium/SessionCard.tsx`)
|
|
- [ ] **4.7** Stats (`src/stores/exam/index.ts`, `src/pages/training/[id]/index.tsx`, `src/pages/statistical.tsx`)
|
|
- [ ] **4.8** Training (`src/pages/training/index.tsx`, `src/pages/training/[id]/index.tsx`)
|
|
- [ ] **4.9** Groups (`src/utils/groups.ts`, `src/pages/classrooms/create.tsx`, `src/pages/classrooms/[id].tsx`)
|
|
- [ ] **4.10** Entities (`src/pages/entities/create.tsx`, `src/pages/entities/[id]/index.tsx`)
|
|
- [ ] **4.11** Roles (`src/pages/entities/[id]/roles/index.tsx`, `[role].tsx`)
|
|
- [ ] **4.12** Permissions (`src/pages/permissions/[id].tsx`)
|
|
- [ ] **4.13** Payments (`src/pages/payment-record.tsx`, `src/components/PaymentAssetManager.tsx`, `src/components/PaymobPayment.tsx`)
|
|
- [ ] **4.14** Assignments (`src/pages/assignments/creator/index.tsx`, `[id].tsx`, `src/pages/assignments/[id].tsx`)
|
|
- [ ] **4.15** Invites (`src/components/Medium/InviteCard.tsx`, `InviteWithUserCard.tsx`)
|
|
- [ ] **4.16** Approval workflows (`src/pages/approval-workflows/*.tsx`)
|
|
- [ ] **4.17** Codes (`src/pages/register.tsx`)
|
|
- [ ] **4.18** Remaining: transcribe, batch_users, make_user, statistical
|
|
- [ ] **4.19** Update `WordUploader.tsx` `fetch()` call with base URL and auth header
|
|
|
|
### Phase 5: Cleanup
|
|
|
|
- [ ] **5.1** Delete all 108 files in `src/pages/api/`
|
|
- [ ] **5.2** Delete all 14 `src/utils/*.be.ts` files
|
|
- [ ] **5.3** Delete `src/lib/session.ts`, `src/lib/mongodb.ts`, `src/lib/createWorkflowsOnExamCreation.ts`
|
|
- [ ] **5.4** Delete `src/firebase/` directory (if Firebase Auth fully removed)
|
|
- [ ] **5.5** Delete `src/constants/platform.json`, `src/constants/staging.json`
|
|
- [ ] **5.6** Remove unused dependencies from `package.json`: `iron-session`, `firebase`, `firebase-admin`, `mongodb`, `bcrypt`, `formidable`
|
|
- [ ] **5.7** Run `yarn install` to update lockfile
|
|
- [ ] **5.8** Run TypeScript compiler (`yarn tsc --noEmit`) and fix any remaining type errors
|
|
- [ ] **5.9** Run linter (`yarn lint`) and fix issues
|
|
|
|
### Phase 6: Testing
|
|
|
|
- [ ] **6.1** Test login flow (email + password -> JWT stored -> user loaded)
|
|
- [ ] **6.2** Test registration flow (individual and corporate)
|
|
- [ ] **6.3** Test logout (token cleared, redirect to login)
|
|
- [ ] **6.4** Test SSR protection (unauthenticated access to protected page -> redirect)
|
|
- [ ] **6.5** Test exam taking flow (load exam, answer questions, submit, save stats)
|
|
- [ ] **6.6** Test writing/speaking grading (submit, poll, receive result)
|
|
- [ ] **6.7** Test entity/group/classroom management (CRUD operations)
|
|
- [ ] **6.8** Test assignment lifecycle (create, start, release, archive)
|
|
- [ ] **6.9** Test payment flows (Stripe, PayPal, Paymob)
|
|
- [ ] **6.10** Test file uploads (exam media, profile pictures)
|
|
- [ ] **6.11** Test training content generation
|
|
- [ ] **6.12** Test ticket creation (from platform and landing page)
|
|
- [ ] **6.13** Test admin user management (create, update, batch import)
|
|
- [ ] **6.14** Cross-browser test (Chrome, Firefox, Safari)
|