# Phase 1 Signup page — how it works This document describes [`Signup.tsx`](./Signup.tsx): a **standalone registration screen** for the `fe01` “phase1” demo flow. Use it as a spec when re-implementing the same behavior elsewhere. ## Where it lives in the app - **Component:** `fe01/src/phase1/pages/Signup.tsx` (default export `Signup`) - **Route:** `/phase1/signup` (see `fe01/src/App.tsx`) This is **not** the same as the main app’s tabbed login/signup at `/signup`, which uses `SignupForm` (`fe01/src/components/auth/SignupForm.tsx`) with extra fields and optional OTP. The phase1 page is intentionally simpler. ## Dependencies | Area | What | |------|------| | **Router** | `react-router-dom`: `Link`, `useNavigate` — success screen uses `navigate('/login')` (root login, not phase1). Link at bottom goes to `/phase1/login`. | | **API** | `authService` from `@/phase1/lib/auth-service` — `register()` and `resendVerify()`. | | **UI** | shadcn-style: `Button`, `Input`, `Label`, `Card*`, `Alert`, `Select`; icons from `lucide-react`. | **Base URL:** `authService` uses `import.meta.env.VITE_API_URL` or falls back to `http://localhost:3000` (see `auth-service.ts`). Ensure your re-implementation uses the same env variable or your chosen API base. ## User-facing flow (two screens) 1. **Form screen** — collect name, school email, password, role → submit. 2. **Success screen** — shown when registration succeeds: explains that a verification email was sent, offers **resend verification** and **back to login**. There is **no** auto-redirect after signup; the user must verify email via the link in mail (handled by backend + a separate verify route elsewhere, e.g. `/verify-email?userId=...&token=...`). ## State model | State | Type | Role | |-------|------|------| | `email`, `password`, `fullName` | `string` | Controlled form fields | | `role` | `'Admin' \| 'Editor' \| 'Viewer'` | Default `'Viewer'` | | `error` | `string` | Inline validation / API error message | | `loading` | `boolean` | Disables inputs and shows spinner on submit/resend | | `successMsg` | `string \| null` | Message from API after successful register or resend | | `registeredEmail` | `string \| null` | Email used for resend; together with `successMsg` gates the success UI | **Success UI condition:** `successMsg && registeredEmail` — both must be set after a successful `register()`. ## Role picker - **Canonical values** sent to API: `Admin`, `Editor`, `Viewer` (must match backend). - **Display:** Vietnamese labels + icons + a one-line **description** under the select (`ROLE_OPTIONS` array in the component). Re-implementing: keep the **API values** exactly as the backend expects; only labels/descriptions are presentation. ## Client-side validation (before `register`) Order in `handleSubmit`: 1. Clear `error`. 2. `fullName` — non-empty after trim. 3. `email` — non-empty after trim. 4. **Domain check** — `validateDomain()`: - Must contain `@`. - Part after `@` (lower case, trimmed) must equal one of **`ump.edu.vn`** or **`umc.edu.vn`** (hardcoded hint list `ALLOWED_DOMAINS_HINT`). 5. `password` — length ≥ 8. **Important:** This list is a **UX hint**. The file comment states the server re-validates using `SystemSettings` / `Auth.AllowedEmailDomains`. Your copy can change the hint list, but must stay aligned with server policy. **On submit:** email is sent as `email.trim().toLowerCase()`; `fullName` is `fullName.trim()`. ## API: `register` **Call:** `authService.register({ email, password, fullName, role })` **HTTP:** `POST {API_URL}/auth/register` **Headers:** `Content-Type: application/json`, `X-Client-Origin: window.location.origin` **Body:** JSON `{ email, password, fullName, role }` with `role` one of `Admin` | `Editor` | `Viewer`. **On failure (`!res.ok`):** `authService` returns `{ success: false, error: string }` — page sets `error` and stops. **On success:** `{ success: true, userId, email, role, emailSent, message }` — page sets `registeredEmail` from `res.email`, `successMsg` from `res.message`, does **not** set tokens (registration does not log the user in). ## API: resend verification **When:** User is on success screen and clicks “Gửi lại email xác nhận”. **Call:** `authService.resendVerify(registeredEmail)` **HTTP:** `POST {API_URL}/auth/resend-verify` with JSON `{ email }`, same `X-Client-Origin` pattern. **On response:** Page updates `successMsg` with `res.message` (does not clear the success layout). ## Error display - Errors render in a destructive `Alert` above the submit button. - Success screen uses a non-destructive `Alert` for `successMsg`. ## Styling / layout notes - **Form:** centered card on `bg-muted/20`, max width `max-w-md`. - **Success:** full-height centered layout with gradient background and slightly richer card styling (backdrop blur, green mail icon). - **Accessibility:** labels use `htmlFor` matching input `id`; password field has `minLength={8}` and `required` in addition to JS checks. ## Checklist for a faithful re-implementation - [ ] Same route or intentionally different — document if you move it. - [ ] `register` + `resendVerify` endpoints, payloads, and headers (`X-Client-Origin`, `credentials: 'include'` if cookies matter for your stack). - [ ] Two-step UI: form → “check your email” with resend + login navigation. - [ ] Client validation order and domain hint list (or replace hint with server-driven list if you add an endpoint). - [ ] Role enum matches backend; Vietnamese copy optional. - [ ] Distinguish this simple flow from `SignupForm` (OTP/extra fields) if both exist in the product. ## Related files - `fe01/src/phase1/lib/auth-service.ts` — `register`, `resendVerify`, `verifyEmail` - `fe01/src/App.tsx` — route wiring - `fe01/src/components/auth/SignupForm.tsx` — richer signup used on main `/login` + `/signup` (different product path)