5.8 KiB
Phase 1 Signup page — how it works
This document describes 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 exportSignup) - Route:
/phase1/signup(seefe01/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)
- Form screen — collect name, school email, password, role → submit.
- 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_OPTIONSarray 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:
- Clear
error. fullName— non-empty after trim.email— non-empty after trim.- Domain check —
validateDomain():- Must contain
@. - Part after
@(lower case, trimmed) must equal one ofump.edu.vnorumc.edu.vn(hardcoded hint listALLOWED_DOMAINS_HINT).
- Must contain
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
Alertabove the submit button. - Success screen uses a non-destructive
AlertforsuccessMsg.
Styling / layout notes
- Form: centered card on
bg-muted/20, max widthmax-w-md. - Success: full-height centered layout with gradient background and slightly richer card styling (backdrop blur, green mail icon).
- Accessibility: labels use
htmlFormatching inputid; password field hasminLength={8}andrequiredin addition to JS checks.
Checklist for a faithful re-implementation
- Same route or intentionally different — document if you move it.
register+resendVerifyendpoints, 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,verifyEmailfe01/src/App.tsx— route wiringfe01/src/components/auth/SignupForm.tsx— richer signup used on main/login+/signup(different product path)