Handover workspace

ERS, Todo, OfferReview, and Docu in one view

Imported from live server docs, code structure, and deployment notes.

Apr 3, 2026, 12:38 PM

OfferReview

W12 CANDIDATE DETAIL - OVERVIEW SHELL + TAB NAVIGATION

**What is W12?**

W12-IMPLEMENTATION.md

Updated Feb 19, 2026, 6:59 AM

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:

  1. Sticky Header - Shows candidate summary, status badge, and role-based primary action button
  2. Tab Navigation - Accessible tabs for Overview, HR Screening, Manager Review, SMO Decision, Documents (stub), Audit (stub)
  3. Overview Tab - Shows candidate snapshot and summaries of all processes
  4. Deep Link Support - URLs with ?tab= query parameter navigate directly to specific tabs
  5. 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, resume summaries
    • Includes RBAC checks for Manager and SMO roles
    • Returns candidate snapshot + all summaries in single response

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

  1. 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
  2. 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
  3. src/app/(app)/candidates/[id]/_components/Tabs.tsx (50 lines, new)

    • Tab navigation bar with 6 tabs
    • Active tab highlighting
    • Props: activeTab, onTabChange
  4. 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
  5. src/app/(app)/candidates/[id]/_tabs/DocumentsTab.tsx (15 lines, new, stub)

    • Placeholder: "Documents coming in W13"
    • No real functionality for W12
  6. 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:

  1. Parse candidate ID from route params
  2. Fetch candidate with required fields
  3. 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
  4. Fetch related data in parallel:
    • HrScreening (unique by candidateId)
    • ManagerReview (unique by candidateId)
    • Decision (unique by candidateId)
    • CandidateDocument (filtered by RESUME type, latest first)
  5. Build summaries using helper functions from src/lib/candidates/summary.ts
  6. 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

RoleCan ViewCan Access TabDefault TabRestrictions
HRAll candidatesAll tabshr-screening (if NEW/HR_SCREENED)None
ManagerOnly assigned (hiringManagerId = userId)All tabsmanager-review (if MANAGER_EVAL_PENDING/REVIEWED)Returns 403 if not assigned
SMOTO_SMO + decided (APPROVED, REJECTED, KIV)All tabssmo-decision (if TO_SMO/decided)Returns 403 if wrong status
AdminAll candidatesAll tabsoverviewNone
CandidateOwn onlyoverview onlyoverviewReturns 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