Frontend Guide (PeerZoom)
Stack: React + TypeScript, Vite, Axios, shadcn/ui, React Router v6, React Query (or React Context)
Directory Structure
src/
├── components/
│ ├── ui/ — Generic UI elements (shadcn/ui based)
│ ├── layout/ — Page wrappers (DashboardLayout, AuthLayout)
│ ├── jobs/ — Job CRUD components (JobForm, ScreeningQuestionsConfig, ...)
│ ├── interviews/ — Interview scheduling (ScheduleInterviewForm, ...)
│ └── room/ — Live room components (VideoGrid, CodeEditor, ...)
├── pages/
│ ├── recruiter/ — Recruiter dashboard pages
│ ├── candidate/ — Candidate dashboard pages (Phase 2+)
│ └── public/ — Token-gated public pages (ScreeningSession, DsaSession, ...)
├── services/ — API layer (Axios instances + typed wrappers)
├── contexts/ — Auth, Media, Sidebar global state
└── routes/ — React Router definitions + ProtectedRouteAPI Base
The Axios instance in src/services/auth.service.ts uses VITE_SERVER_URL + '/v1' as the base URL. All service files use this instance.
// src/services/auth.service.ts
const axiosInstance = axios.create({
baseURL: import.meta.env.VITE_SERVER_URL + '/v1',
});
// Attach Firebase idToken to every request
axiosInstance.interceptors.request.use(async (config) => {
const token = await auth.currentUser?.getIdToken();
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});Routing
React Router v6. Key route groups:
| Route | Component | Auth |
|---|---|---|
/ | Landing or dashboard redirect | Public |
/login | Login page | Public |
/jobs | Recruiter job list | ProtectedRoute (recruiter) |
/jobs/new | Create job | ProtectedRoute |
/interviews/:id/feedback | Feedback form (Phase 1B) | ProtectedRoute |
/interview-questions | Question bank page (Phase 1B) | ProtectedRoute |
/analytics | Analytics dashboard (Phase 6) | ProtectedRoute |
/settings/organization | Org settings (Phase 5) | ProtectedRoute (admin) |
/settings/billing | Billing (Phase 5) | ProtectedRoute (admin) |
/candidate/dashboard | Candidate dashboard (Phase 2) | ProtectedRoute (candidate) |
/candidate/pipeline/:id | Application detail (Phase 2) | ProtectedRoute (candidate) |
/candidate/interviews/:id | Past session view (Phase 2) | ProtectedRoute (candidate) |
/candidate/screening | Screening session (?token=TOKEN) | Public (token in query param) |
/candidate/decline/:token | Decline confirmation page | Public (token in path) |
/dsa/:token | DSA session (Phase 3) | Public (token-gated) |
/ai-session/:token | AI session (Phase 4) | Public (token-gated) |
/lobby/:roomId | Live interview lobby | Public (meeting link) |
/room/:roomId | Live interview room | Public (meeting link) |
The candidate pipeline list and candidate detail view are not standalone routes. They are embedded inside
JobDetails.tsxas a tab (CandidatePipelineTab) and aResponsiveSheetmodal (CandidateDetailPanel). The legacy routes/dashboard/jobs/:jobId/candidatesand/dashboard/jobs/:jobId/candidates/:pipelineIdexist inApp.tsxbut are not linked from the main UI.
Contexts
| Context | Purpose |
|---|---|
AuthContext | Firebase auth state, current user/role |
MediaContext | Local camera/mic streams and permissions |
SidebarContext | Responsive layout sidebar state |
StageTypeContext (Phase 0) | Cached GET /v1/stage-types response for the session |
The "No Hardcoding" Rule
The frontend must never hardcode:
- Stage type names, keys, or display strings
- Which form fields appear for each stage type
- Which tools are enabled in the room
- Min/max question counts
- Whether questions are interviewer-only
- Whether feedback is required
- Plan names or feature comparisons
All of this comes from GET /v1/stage-types and GET /v1/plans. See the full table in Architecture docs.
Key Services
| File | Purpose |
|---|---|
src/services/auth.service.ts | Axios instance + auth interceptor |
src/services/stageType.service.ts | stageTypeApi.getAll() |
src/services/job.service.ts | Job CRUD |
src/services/interview.service.ts | Interview scheduling + feedback |
src/services/question.service.ts | Screening question CRUD (targets /screening-questions) |
src/services/pipeline.service.ts | Pipeline management (pipelineApi) |
src/services/screening.service.ts | Public screening session API — uses a separate no-auth Axios instance |
src/services/candidateDashboard.service.ts | Candidate-facing API (Phase 2) |