Codex 5.3 Refactor Note: Canonical refactor plan: docs/CODEX-5.3-REFACTOR-PLAN.md. This document is retained for historical and implementation context during the refactor.
W12 CANDIDATE DETAIL - OVERVIEW SHELL + TAB NAVIGATION
A. SUMMARY
What is W12?
W12 implements the Candidate Detail Page as a comprehensive "shell" that hosts multiple tabs for different workflows. The page provides:
- Sticky Header - Shows candidate summary, status badge, and role-based primary action button
- Tab Navigation - Accessible tabs for Overview, HR Screening, Manager Review, SMO Decision, Documents (stub), Audit (stub)
- Overview Tab - Shows candidate snapshot and summaries of all processes
- Deep Link Support - URLs with
?tab=query parameter navigate directly to specific tabs - Role-Aware Defaults - Automatically opens the most relevant tab based on user role and candidate status
User Value:
- Single unified page for all candidate-related activities
- Quick navigation between workflows without context switching
- Immediate visibility of process status (HR screening, manager review, SMO decision)
- Consistent layout ready for future extensions (W13 Documents, W14 Audit)
Wireframe Reference: W12 Candidate Detail (Overview Shell + Tab Navigation)
B. ROUTES
Pages
/candidates/[id](authenticated)- Replaces existing minimal page with full-featured detail shell
- Accepts optional
?tab=query parameter - Redirects to default tab based on user role and candidate status if no tab specified
API Endpoints (Enhanced)
GET /api/candidates/[id](enhanced response)- Added:
hrScreening,managerReview,decision,resumesummaries - Includes RBAC checks for Manager and SMO roles
- Returns candidate snapshot + all summaries in single response
- Added:
C. DATA MODEL CHANGES
No new tables required for W12.
Existing relations verified:
- Candidate → HrScreening (one-to-one)
- Candidate → ManagerReview (one-to-one)
- Candidate → Decision (one-to-one)
- Candidate → CandidateDocument (one-to-many, RESUME type)
- Candidate → User (hiringManager, many-to-one)
Migration: None needed
D. UI COMPONENTS
Files to Create
-
src/app/(app)/candidates/[id]/page.tsx(170 lines, updated)- Main detail page shell
- Coordinates header, tabs, and tab content
- Manages tab state and URL synchronization
- Fetches candidate data and user role
- Handles default tab logic per role/status
-
src/app/(app)/candidates/[id]/_components/CandidateHeader.tsx(180 lines, new)- Sticky header with candidate name, code, status badge
- Applying For position
- Assigned Manager name
- Primary action button (role/status dependent)
- Back button
- Props: candidate, userRole, onTabChange
-
src/app/(app)/candidates/[id]/_components/Tabs.tsx(50 lines, new)- Tab navigation bar with 6 tabs
- Active tab highlighting
- Props: activeTab, onTabChange
-
src/app/(app)/candidates/[id]/_tabs/OverviewTab.tsx(280 lines, new)- Card A: Candidate Snapshot (fullName, applyingFor, email, phone, source, notes, dates, stage owner)
- Card B: Resume (filename, upload date, View/Download button, link to Documents tab)
- Card C: HR Screening Summary (outcome, notes, completedAt, link to HR Screening tab)
- Card D: Manager Review Summary (recommendation, score average, submittedAt, link to Manager Review tab)
- Card E: SMO Decision Summary (decision, decidedAt, link to SMO Decision tab)
- Each card shows "Not completed" if data unavailable
- Props: candidate, summaries, userRole, onTabChange
-
src/app/(app)/candidates/[id]/_tabs/DocumentsTab.tsx(15 lines, new, stub)- Placeholder: "Documents coming in W13"
- No real functionality for W12
-
src/app/(app)/candidates/[id]/_tabs/AuditTab.tsx(15 lines, new, stub)- Placeholder: "Audit trail coming in W14"
- No real functionality for W12
Files Already Exist (Re-used)
src/app/(app)/candidates/[id]/_tabs/HrScreeningTab.tsx(existing W7)src/app/(app)/candidates/[id]/_tabs/ManagerReviewTab.tsx(existing W8)src/app/(app)/candidates/[id]/_tabs/SmoDecisionTab.tsx(existing W10)
E. API LOGIC
Enhanced GET /api/candidates/[id]
Current Behavior (before W12):
GET /api/candidates/:id
→ Returns: {id, candidateCode, fullName, applyingFor, email, phone, source, notes, status, hiringManagerId, hiringManager, createdAt, updatedAt}
New Behavior (W12):
GET /api/candidates/:id
→ Returns: {
id, candidateCode, fullName, applyingFor, email, phone, source, notes, status,
hiringManagerId, hiringManager, createdAt, updatedAt,
// NEW SUMMARIES:
hrScreening: {
outcome, notes, completedAt, createdAt
},
managerReview: {
recommendation, score (average of scores array), submittedAt, createdAt
},
decision: {
decision, notes, decidedAt, decidedByUserId
},
resume: {
filename, uploadedAt, url
}
}
Implementation:
- Parse candidate ID from route params
- Fetch candidate with required fields
- Apply RBAC checks:
- Manager: must match hiringManagerId, 403 if not
- SMO: status must be in [TO_SMO, APPROVED, REJECTED, KIV], 403 if not
- HR/Admin: no restriction
- Fetch related data in parallel:
- HrScreening (unique by candidateId)
- ManagerReview (unique by candidateId)
- Decision (unique by candidateId)
- CandidateDocument (filtered by RESUME type, latest first)
- Build summaries using helper functions from
src/lib/candidates/summary.ts - Return combined response
Helper Functions (src/lib/candidates/summary.ts)
getAverageScore(scores: number[]): number
- Calculates average of scores array
- Rounds to 1 decimal place
getLatestResume(documents: CandidateDocument[]): CandidateDocument | null
- Filters documents by type RESUME
- Returns most recent by uploadedAt
getResumeDisplay(resume): { filename, uploadedAt, url }
- Formats resume for display
getCurrentStageOwner(status: string): string
- Maps status to human-readable stage owner (HR, Manager, SMO, etc.)
buildCandidateSummaries(hrScreening, managerReview, decision, documents)
- Combines summaries into response object
- Handles null values gracefully
F. RBAC CHECKS
Access Control Matrix
| Role | Can View | Can Access Tab | Default Tab | Restrictions |
|---|---|---|---|---|
| HR | All candidates | All tabs | hr-screening (if NEW/HR_SCREENED) | None |
| Manager | Only assigned (hiringManagerId = userId) | All tabs | manager-review (if MANAGER_EVAL_PENDING/REVIEWED) | Returns 403 if not assigned |
| SMO | TO_SMO + decided (APPROVED, REJECTED, KIV) | All tabs | smo-decision (if TO_SMO/decided) | Returns 403 if wrong status |
| Admin | All candidates | All tabs | overview | None |
| Candidate | Own only | overview only | overview | Returns 403 for others |
Implementation in GET /api/candidates/[id]
// After fetching candidate:
if (payload.role === 'MANAGER' && candidate.hiringManagerId !== payload.userId) {
return 403 Forbidden
}
if (payload.role === 'SMO' && !['TO_SMO', 'APPROVED', 'REJECTED', 'KIV'].includes(candidate.status)) {
return 403 Forbidden
}
// HR and Admin have no restriction
Default Tab Logic (client-side)
If URL has ?tab=X:
- Use X (if valid)
Else:
- If role=HR and status in [NEW, HR_SCREENED]:
- Default: hr-screening
- Else if role=MANAGER and status in [MANAGER_EVAL_PENDING, MANAGER_REVIEWED]:
- Default: manager-review
- Else if role=SMO and status in [TO_SMO, APPROVED, REJECTED, KIV]:
- Default: smo-decision
- Else:
- Default: overview
G. AUDIT EVENTS
No new audit events for W12 viewing operations.
Viewing candidate detail does not generate audit logs. Audit logs are generated by WRITE operations in child tabs:
- HR Screening completion → logged in W7
- Manager Review submission → logged in W8
- SMO Decision finalization → logged in W10
H. TEST CHECKLIST
Test 1: Page Loads for Authorized User
- Login as HR user
- Navigate to /candidates/[id] (valid candidate)
- Page loads with header, tabs, and overview content
- No errors in console
Test 2: Header Displays Correctly
- Candidate name appears (or "Unnamed Candidate" if null)
- Candidate code displayed
- Status badge shows current status with correct color
- Applying For position displayed
- Manager name displayed (if assigned)
- Back button exists
Test 3: Primary Action Button (HR User, NEW Status)
- Login as HR user
- View candidate with status=NEW
- Primary button shows "Start HR Screening"
- Click button → navigates to hr-screening tab
Test 4: Primary Action Button (HR User, HR_SCREENED without Manager)
- Login as HR user
- View candidate with status=HR_SCREENED and no manager
- Primary button shows "Assign Manager"
- Click button → navigates to hr-screening tab
Test 5: Primary Action Button (Manager User, MANAGER_EVAL_PENDING)
- Login as Manager user
- View assigned candidate with status=MANAGER_EVAL_PENDING
- Primary button shows "Open Scorecard"
- Click button → navigates to manager-review tab
Test 6: Primary Action Button (SMO User, TO_SMO)
- Login as SMO user
- View candidate with status=TO_SMO
- Primary button shows "Open Decision"
- Click button → navigates to smo-decision tab
Test 7: Tab Navigation
- Click each tab (overview, hr-screening, manager-review, smo-decision, documents, audit)
- Content changes appropriately
- URL updates with ?tab= parameter
- Tab highlighting changes correctly
Test 8: Overview Tab - Candidate Snapshot Card
- Overview tab shows all candidate fields: fullName, applyingFor, email, phone, source, notes
- Shows Created/Updated timestamps
- Shows Current Stage Owner (derived from status)
Test 9: Overview Tab - Resume Card (With Resume)
- If resume exists, shows filename
- Shows upload date
- Has "View/Download" button (placeholder)
- Has "Open Documents Tab" link
Test 10: Overview Tab - Resume Card (No Resume)
- If no resume, shows "No resume uploaded yet"
- Has "Upload Resume" button
Test 11: Overview Tab - HR Screening Summary (Completed)
- Shows outcome, notes, completedAt
- Has "Open HR Screening Tab" link
- Data matches HrScreening record
Test 12: Overview Tab - HR Screening Summary (Not Completed)
- Shows "Not completed" if no HrScreening record
Test 13: Overview Tab - Manager Review Summary (Completed)
- Shows recommendation, score (average), submittedAt
- Score correctly calculates average of scores array
- Has "Open Manager Review Tab" link
Test 14: Overview Tab - Manager Review Summary (Not Completed)
- Shows "Not completed" if no ManagerReview record
Test 15: Overview Tab - SMO Decision Summary (Completed)
- Shows decision, notes, decidedAt
- Has "Open SMO Decision Tab" link
Test 16: Overview Tab - SMO Decision Summary (Not Completed)
- Shows "Not completed" if no Decision record
Test 17: Default Tab Logic - HR User (NEW Status)
- Login as HR user
- View candidate with status=NEW
- Page loads with hr-screening tab active
- No ?tab= in URL
Test 18: Default Tab Logic - Manager User (MANAGER_EVAL_PENDING Status)
- Login as Manager user
- View assigned candidate with status=MANAGER_EVAL_PENDING
- Page loads with manager-review tab active
Test 19: Default Tab Logic - SMO User (TO_SMO Status)
- Login as SMO user
- View candidate with status=TO_SMO
- Page loads with smo-decision tab active
Test 20: Deep Link with Tab Parameter
- Navigate to /candidates/[id]?tab=documents
- Documents tab loads as active
- URL parameter persists on tab changes
Test 21: RBAC - Manager Cannot View Other's Candidate
- Login as Manager A
- Try to navigate to /candidates/[id] for candidate assigned to Manager B
- Page shows "You do not have permission to view this candidate"
- Returns 403 error
Test 22: RBAC - SMO Cannot View Wrong Status
- Login as SMO user
- Try to navigate to /candidates/[id] with status=NEW (not in [TO_SMO, APPROVED, REJECTED, KIV])
- Page shows "You do not have permission to view this candidate"
- Returns 403 error
Test 23: RBAC - HR Can View Any Candidate
- Login as HR user
- View candidates with all statuses (NEW, HR_SCREENED, etc.)
- All load successfully
- No permission errors
Test 24: RBAC - Admin Can View Any Candidate
- Login as Admin user
- View candidates with all statuses
- All load successfully
Test 25: Documents Tab (Stub)
- Click Documents tab
- Shows placeholder: "Documents coming in W13"
- No errors
Test 26: Audit Tab (Stub)
- Click Audit tab
- Shows placeholder: "Audit trail coming in W14"
- No errors
Test 27: Existing Tabs Still Work
- HR Screening tab functions (existing W7 flow)
- Manager Review tab functions (existing W8 flow)
- SMO Decision tab functions (existing W10 flow)
- No regression in existing functionality
Test 28: 404 for Non-Existent Candidate
- Navigate to /candidates/[invalid-id]
- Page shows error: "Candidate not found"
- Back button works
Test 29: Back Button Navigation
- Open candidate detail from /candidates list
- Click Back button
- Returns to /candidates list
- Maintains filter/pagination state
Test 30: Sticky Header Behavior
- Scroll down page with long overview content
- Header remains sticky at top
- Tabs scroll below header
- No z-index conflicts
ACCEPTANCE CRITERIA CHECKLIST
- Page renders at /candidates/[id]
- Header shows candidate summary (name, code, status, position, manager)
- Tab navigation shows 6 tabs
- Overview tab shows 5 cards (snapshot, resume, HR summary, Manager summary, SMO summary)
- Default tab logic works per role/status
- Deep links via ?tab= parameter work
- Documents and Audit tabs exist as placeholders
- Existing tabs (HR Screening, Manager Review, SMO Decision) still work
- RBAC enforced in API for Manager and SMO
- Primary action button appears and works correctly
- All 30 test cases pass
Status: Ready for Implementation & Testing