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

W8 Implementation: Hiring Manager Review (Scorecard + Submit + Escalate)

**Status**: ✅ Complete (Backend + UI + Documentation) **Version**: 1.0 **Date**: January 26, 2025

docs/W8-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.

W8 Implementation: Hiring Manager Review (Scorecard + Submit + Escalate)

Status: ✅ Complete (Backend + UI + Documentation) Version: 1.0 Date: January 26, 2025


A. Summary

Implement manager review workflow where assigned hiring managers fill a scorecard (6 categories, 1-5 rating scale), save drafts, submit with validation (≥3 rated, summary required, comments for low ratings ≤2), lock edits, and escalate to SMO. HR/SMO can view read-only.

Key Design Principle: Manager-only editing with completion gates

  • Status progression: MANAGER_EVAL_PENDING → (submit) → MANAGER_REVIEWED → (escalate) → TO_SMO
  • Default scorecard with 6 built-in categories (extensible to template system in W9)
  • Average score calculation (auto-updated in real-time)

B. Routes

UI Page

  • Location: /candidates/[id]?tab=manager-review
  • Component: Client-side tab in candidate detail page

API Endpoints

  1. GET /api/candidates/[id]/manager-review

    • Fetch draft or empty structure
    • Returns isEditable based on role + status
  2. PUT /api/candidates/[id]/manager-review

    • Save draft (no outcome validation for draft)
    • Returns updated review with parsed responses
  3. POST /api/candidates/[id]/manager-review/submit

    • Submit with strict validation (≥3 rated, summary required)
    • Status transition: MANAGER_EVAL_PENDING → MANAGER_REVIEWED
    • Returns review + updated candidate
  4. POST /api/candidates/[id]/escalate-to-smo

    • Escalate to SMO (only after submitted)
    • Status transition: MANAGER_REVIEWED → TO_SMO
    • Returns updated candidate

C. Data Model Changes

New Enums

enum ManagerReviewMode {
  QUICK  // V1: 6-question template (future: FULL, CUSTOM from W9)
}

enum ManagerRecommendation {
  PROCEED  // Offer stage
  KIV      // Keep in view
  REJECT   // Not moving forward
}

CandidateStatus Enum Update

enum CandidateStatus {
  NEW
  HR_SCREENED
  MANAGER_EVAL_PENDING        // ← Updated flow (was MANAGER_EVAL_COMPLETED)
  MANAGER_REVIEWED            // ← New status for submitted review
  TO_SMO
  DECISION_MADE
  COMPLETED
}

New Model: ManagerReview

model ManagerReview {
  id                    String                @id @default(cuid())
  candidateId           String                @unique
  candidate             Candidate             @relation(fields: [candidateId], references: [id], onDelete: Cascade)
  
  // Scorecard content
  mode                  ManagerReviewMode     @default(QUICK)
  responsesJson         String                @db.Text  // JSON: [{categoryId, categoryName, rating, comment}]
  recommendation        ManagerRecommendation?
  summaryNotes          String?               // Required on submit
  
  // Submission tracking
  submittedAt           DateTime?             // NULL while draft, set on submit
  submittedByUserId     String?
  submittedBy           User?                 @relation("ReviewSubmittedBy", fields: [submittedByUserId], references: [id], onDelete: SetNull)
  
  createdAt             DateTime              @default(now())
  updatedAt             DateTime              @updatedAt
  
  @@index([candidateId])
  @@index([submittedByUserId])
  @@index([submittedAt])
}

Enhanced Candidate Model

model Candidate {
  // ... existing fields ...
  managerReview         ManagerReview?        // One-to-one relation
}

Enhanced User Model

model User {
  // ... existing fields ...
  submittedReviews      ManagerReview[]       @relation("ReviewSubmittedBy")
}

New Audit Event Types

enum AuditEventType {
  // ... existing ...
  MANAGER_REVIEW_DRAFT_SAVED       // PUT save draft
  MANAGER_REVIEW_SUBMITTED         // POST submit (status transition)
  SENT_TO_SMO                       // POST escalate (status transition)
}

Migration

Name: add_manager_review_and_escalation

Changes:

  • Add ManagerReviewMode enum
  • Add ManagerRecommendation enum
  • Update CandidateStatus enum (MANAGER_EVAL_COMPLETED → MANAGER_REVIEWED)
  • Create ManagerReview table
  • Add managerReview relation to Candidate
  • Add submittedReviews relation to User
  • Add 3 audit event types

D. UI Components

Candidate Detail Page (Updated)

File: src/app/(app)/candidates/[id]/page.tsx

Changes:

  • Import ManagerReviewTab component
  • Add tab routing: ?tab=manager-review (via useSearchParams)
  • Tab navigation button for "Manager Review"
  • Tab content conditionally renders ManagerReviewTab

Manager Review Tab (New)

File: src/app/(app)/candidates/[id]/_tabs/ManagerReviewTab.tsx (650 lines)

Architecture:

  • Client component with local state management
  • Fetches review on mount via GET endpoint
  • Auto-calculates average score (real-time)
  • Section A: Read-only snapshot
  • Section B: Interactive scorecard (6 categories)
  • Section C: Recommendation + summary notes
  • Header actions: Save Draft, Submit Review, Escalate to SMO

State Management:

  • review: Fetched ManagerReview object or null
  • responses: Map of categoryId → {rating, comment}
  • recommendation: Selected recommendation (PROCEED/KIV/REJECT)
  • summaryNotes: Summary textarea value
  • submitted: Boolean flag for submission state
  • expandedSections: Toggle state for 3 sections
  • loading, saving: UI states

Section A: Candidate Snapshot (Read-only)

Fields:

  • Position (applyingFor)
  • Current Status (status)
  • Assigned Manager (hiringManager.fullName, email)

Behavior:

  • Non-editable, display-only
  • Shows context of who is reviewing

Section B: Manager Scorecard

6 Built-in Categories:

  1. Technical / Domain Fit
  2. Execution / Delivery
  3. Communication
  4. Ownership / Accountability
  5. Team Fit / Collaboration
  6. Learning Ability / Adaptability

Rating Interface:

  • 5 buttons (1-5) per category
  • Selected rating highlighted in blue
  • Real-time average score calculation shown in header

Conditional Comments:

  • If rating ≤2, comment textarea appears (red border)
  • Label: "Comment (required for ratings ≤2)"
  • Required on submit if rating ≤2

Auto-calculation:

  • Average score = (sum of rated values) / (number of rated categories)
  • Displayed in section header, updated in real-time

Section C: Recommendation & Notes

Recommendation Radio Buttons:

  • PROCEED: "Proceed to offer"
  • KIV: "Keep in view"
  • REJECT: "Reject"

Summary Notes:

  • Required field (validation enforced on submit)
  • Max 2000 characters
  • Placeholder: "Summary of your assessment..."

Submission Status:

  • After submit, show green info box: "Review Submitted - Submitted on [date]"
  • Fields become read-only (disabled state)

Header Actions

Save Draft Button:

  • Label: "Save Draft"
  • Gray background
  • Enabled if status = MANAGER_EVAL_PENDING (draft mode)
  • Disabled after submit

Submit Review Button (Primary):

  • Label: "Submit Review"
  • Blue background
  • Enabled if status = MANAGER_EVAL_PENDING and validation passes
  • Disabled after submit
  • On success: transition to MANAGER_REVIEWED, lock editing

Escalate to SMO Button (Shown after submit):

  • Label: "Escalate to SMO"
  • Purple background
  • Enabled only after submit (submittedAt not null)
  • Opens confirmation modal
  • On confirm: transition to TO_SMO

Escalate Modal

  • Title: "Escalate to SMO?"
  • Message: "Are you sure you want to escalate this candidate to SMO for decision? This action cannot be undone."
  • Cancel button, Escalate button
  • Disabled state during submission

Read-only View (HR/SMO)

  • All form fields disabled (gray background, cursor: not-allowed)
  • Header action buttons hidden or disabled
  • Display message: "You can view but not edit this review"

Gating Messages

  • If status = NEW/HR_SCREENED: "Manager review is not available until HR assigns a manager and completes screening."
  • If status = TO_SMO/DECISION_MADE: "Review has been escalated to SMO. No further edits allowed."

Styling:

  • Expandable sections (arrow rotates)
  • Form inputs: gray border, focus blue ring
  • Rating buttons: 40px circle, hover effect
  • Comments field: red border, red background (for low ratings only)
  • Average score badge: blue background in header
  • Submission status: green info box

E. API Logic

1. GET /api/candidates/[id]/manager-review

Purpose: Fetch review draft or empty structure

RBAC:

  • Assigned manager: Can view & edit (isEditable = true if status = MANAGER_EVAL_PENDING)
  • HR/ADMIN: Can view read-only (isEditable = false)
  • SMO: Can view read-only (isEditable = false)

Response (200 OK):

{
  "id": "hrs-...",
  "candidateId": "cm-...",
  "mode": "QUICK",
  "responses": [
    {
      "categoryId": "technical",
      "categoryName": "Technical / Domain Fit",
      "rating": 4,
      "comment": ""
    },
    {
      "categoryId": "execution",
      "categoryName": "Execution / Delivery",
      "rating": 2,
      "comment": "Needs improvement in project delivery"
    }
  ],
  "recommendation": null,
  "summaryNotes": "",
  "submittedAt": null,
  "submittedByUserId": null,
  "isEditable": true
}

If no review exists yet: Returns empty structure with all categories at default (rating: null, comment: '')

Error Responses:

  • 401: Unauthorized
  • 403: Forbidden (not assigned manager/HR/SMO)
  • 404: Candidate not found

2. PUT /api/candidates/[id]/manager-review

Purpose: Save draft scorecard (allows partial state, no validation for draft)

RBAC: Assigned manager or ADMIN only

Request Body:

{
  "responses": [
    {
      "categoryId": "technical",
      "categoryName": "Technical / Domain Fit",
      "rating": 4,
      "comment": null
    },
    {
      "categoryId": "execution",
      "categoryName": "Execution / Delivery",
      "rating": 2,
      "comment": "Needs improvement"
    }
  ],
  "recommendation": null,
  "summaryNotes": "Solid technical foundation..."
}

Validation:

  • No validation on draft (allows incomplete state)
  • Ratings must be 1-5 if provided
  • Responses format validated (categoryId, categoryName, rating, comment)

Response (200 OK): Updated review object with all fields

Behavior:

  • Creates ManagerReview if not exists
  • Updates existing review if exists
  • Stores responses as JSON in database
  • Logs MANAGER_REVIEW_DRAFT_SAVED event
  • Preserves previous recommendation/summaryNotes if not provided in request

Error Responses:

  • 400: Validation failed
  • 401: Unauthorized
  • 403: Forbidden (not assigned manager/admin)
  • 404: Candidate not found
  • 409: Candidate not in MANAGER_EVAL_PENDING status

3. POST /api/candidates/[id]/manager-review/submit

Purpose: Submit review with strict validation (locks editing, transitions status)

RBAC: Assigned manager or ADMIN only

Request Body: Same as PUT

{
  "responses": [
    { "categoryId": "technical", "categoryName": "Technical / Domain Fit", "rating": 4, "comment": null },
    { "categoryId": "execution", "categoryName": "Execution / Delivery", "rating": 3, "comment": "" },
    { "categoryId": "communication", "categoryName": "Communication", "rating": 5, "comment": null },
    { "categoryId": "ownership", "categoryName": "Ownership / Accountability", "rating": 4, "comment": null },
    { "categoryId": "team_fit", "categoryName": "Team Fit / Collaboration", "rating": 3, "comment": "" },
    { "categoryId": "learning", "categoryName": "Learning Ability / Adaptability", "rating": 4, "comment": null }
  ],
  "recommendation": "PROCEED",
  "summaryNotes": "Strong candidate with solid technical background and good communication skills..."
}

Validation (Strict - all errors block submission):

  1. Minimum Rated Categories: ≥3 categories must be answered

    • Error: "At least 3 categories must be rated"
  2. Summary Notes: Required and non-empty

    • Error: "Summary notes are required"
  3. Conditional Comments: For all responses with rating ≤2, comment required

    • Error: "Comments required for ratings of 2 or below"
  4. Status Check: Candidate must be in MANAGER_EVAL_PENDING

    • Error: "Candidate must be in MANAGER_EVAL_PENDING status"

Response (200 OK):

{
  "review": {
    "id": "hrs-...",
    "candidateId": "cm-...",
    "mode": "QUICK",
    "responses": [...],
    "recommendation": "PROCEED",
    "summaryNotes": "Strong candidate...",
    "submittedAt": "2025-01-26T14:30:00Z",
    "submittedByUserId": "user-...",
    "averageScore": "3.83",
    "isEditable": false
  },
  "candidate": {
    "id": "cm-...",
    "status": "MANAGER_REVIEWED",
    "candidateCode": "HR-001",
    "fullName": "John Doe",
    "hiringManager": {...}
  }
}

Behavior:

  • Creates or updates ManagerReview
  • Sets submittedAt timestamp
  • Sets submittedByUserId to current user ID
  • Calculates average score from all rated categories
  • Status Transition: MANAGER_EVAL_PENDING → MANAGER_REVIEWED
  • Logs MANAGER_REVIEW_SUBMITTED event with recommendation, score, answered count
  • Logs CANDIDATE_STATUS_CHANGED event (dual logging for audit trail)

Error Responses:

  • 400: Validation failed (with specific error message)
  • 401: Unauthorized
  • 403: Forbidden (not assigned manager/admin)
  • 404: Candidate not found
  • 409: Candidate not in MANAGER_EVAL_PENDING status

4. POST /api/candidates/[id]/escalate-to-smo

Purpose: Escalate candidate to SMO after manager review submitted

RBAC: Assigned manager or ADMIN only

Request Body: Empty (no parameters)

Validation:

  1. Status Check: Candidate must be in MANAGER_REVIEWED status

    • Error: "Candidate must be in MANAGER_REVIEWED status to escalate to SMO"
  2. Submitted Check: Manager review must have submittedAt timestamp

    • Error: "Manager review must be submitted before escalating"

Response (200 OK):

{
  "id": "cm-...",
  "status": "TO_SMO",
  "candidateCode": "HR-001",
  "fullName": "John Doe",
  "hiringManager": {
    "id": "user-...",
    "fullName": "Alice Johnson",
    "email": "alice@company.com"
  },
  "message": "Candidate escalated to SMO successfully"
}

Behavior:

  • Status Transition: MANAGER_REVIEWED → TO_SMO
  • Logs SENT_TO_SMO event with manager name/email
  • Non-blocking logging (doesn't affect API response)
  • No email sent (notifications future feature)

Error Responses:

  • 401: Unauthorized
  • 403: Forbidden (not assigned manager/admin)
  • 404: Candidate not found
  • 409: Candidate not in MANAGER_REVIEWED status OR review not submitted

F. RBAC Checks

Role-Based Permissions Matrix

RoleGET ReviewPUT DraftPOST SubmitPOST EscalateView (Read-only)
Manager (assigned)✅ Edit✅ Edit✅ Submit✅ EscalateN/A (edits)
Manager (other)
HR✅ (R/O)
SMO✅ (R/O)
ADMIN✅ Edit✅ Edit✅ Submit✅ EscalateN/A (edits)
USER

Enforcement Points

Backend (API routes):

  • Check user.id === candidate.hiringManagerId for edit operations
  • Check user.role === 'ADMIN' for admin override
  • Check ['HR', 'SMO', 'ADMIN'].includes(user.role) for read-only access
  • Enforce status checks (MANAGER_EVAL_PENDING for drafts/submit, MANAGER_REVIEWED for escalate)

Frontend (UI component):

  • Get userId from session/localStorage
  • Compare against candidate.hiringManagerId
  • Show action buttons only if assigned manager
  • Show fields as disabled if not assigned manager
  • Show read-only message if user is HR/SMO/Admin

G. Audit Logging

Events

1. MANAGER_REVIEW_DRAFT_SAVED

  • Trigger: PUT /api/candidates/[id]/manager-review
  • Metadata:
    {
      "candidateId": "cm-...",
      "candidateCode": "HR-001",
      "answeredCategories": 3
    }
    
  • Actor: Manager or Admin
  • Timestamp: Automatic
  • Blocking: No (non-blocking audit)

2. MANAGER_REVIEW_SUBMITTED

  • Trigger: POST /api/candidates/[id]/manager-review/submit
  • Metadata:
    {
      "candidateId": "cm-...",
      "candidateCode": "HR-001",
      "recommendation": "PROCEED",
      "averageScore": "3.83",
      "answeredCategories": 6
    }
    
  • Actor: Manager or Admin
  • Timestamp: Automatic
  • Related Event: CANDIDATE_STATUS_CHANGED (dual logged)

3. SENT_TO_SMO

  • Trigger: POST /api/candidates/[id]/escalate-to-smo
  • Metadata:
    {
      "candidateId": "cm-...",
      "candidateCode": "HR-001",
      "managerName": "Alice Johnson",
      "managerEmail": "alice@company.com"
    }
    
  • Actor: Manager or Admin
  • Timestamp: Automatic
  • Related Event: CANDIDATE_STATUS_CHANGED (if tracked separately)

Related Event: CANDIDATE_STATUS_CHANGED

  • Trigger: Auto-logged by POST submit
  • Metadata:
    {
      "candidateId": "cm-...",
      "candidateCode": "HR-001",
      "oldStatus": "MANAGER_EVAL_PENDING",
      "newStatus": "MANAGER_REVIEWED",
      "trigger": "manager_review_submitted"
    }
    

H. Manual Testing Checklist

Pre-flight Setup

  • Database running (Postgres)
  • Prisma migrations applied: npx prisma migrate dev --name add_manager_review_and_escalation
  • API server running: npm run dev
  • Have 2+ users created: 1 HR, 1 Manager
  • Have 1 candidate with status MANAGER_EVAL_PENDING (HR screening completed, manager assigned)

Test Case 1: Manager Opens Review & Sees Empty Scorecard

Setup: Candidate at MANAGER_EVAL_PENDING, logged in as assigned manager

Steps:

  1. Navigate to /candidates/{candidateId}?tab=manager-review
  2. Verify all 6 categories visible with no ratings
  3. Verify Section A shows: Position, Status, Assigned Manager (self)
  4. Verify header shows: Save Draft, Submit Review buttons (enabled)
  5. Verify "Escalate to SMO" button NOT visible (hidden until submit)
  6. Verify average score shows "0" or empty

Expected:

  • All form fields editable
  • No error messages
  • Scorecard ready for input

Test Case 2: Manager Rates Scorecard & Saves Draft

Setup: Continue from Test Case 1

Steps:

  1. Rate "Technical / Domain Fit": 4
  2. Rate "Communication": 2 (triggers comment requirement)
  3. Verify comment textarea appears for Communication (red border)
  4. Add comment: "Good communication in meetings"
  5. Rate "Ownership / Accountability": 5
  6. Verify average score updates to ~3.67 (4+2+5)/3)
  7. Click "Save Draft"
  8. Verify toast/alert: "Draft saved successfully"
  9. Refresh page
  10. Verify all ratings and comment persisted

Expected:

  • Real-time average score calculation
  • Comment field appears/disappears based on rating
  • Draft saved and reloadable
  • No status change (still MANAGER_EVAL_PENDING)
  • Audit log shows MANAGER_REVIEW_DRAFT_SAVED event

Test Case 3: Submit Without Validation (Error Cases)

Setup: Continue from Test Case 2 (3 categories rated)

Steps:

  1. Try to submit without Recommendation selected
    • Expected: No validation on recommendation (optional)
  2. Try to submit without Summary Notes
    • Expected: Toast error: "Summary notes are required"
  3. Rate one more category (4th) with rating 2, no comment
  4. Try to submit
    • Expected: Toast error: "Comments required for ratings of 2 or lower"
  5. Add comment for the rating 2
  6. Rate 3rd category as 1 (poor), no comment
  7. Try to submit
    • Expected: Toast error: "Comments required for ratings of 2 or lower"
  8. Add comment for rating 1
  9. Now try submit with ≥3 rated + summary + comments for low ratings
    • Expected: Success

Expected:

  • Clear, specific error messages
  • No status change until validation passes
  • User can fix and retry

Test Case 4: Submit Successfully (Status Transition)

Setup: Scorecard with ≥3 rated + comments for low ratings + summary

Steps:

  1. Ensure at least 3 categories rated
  2. Add Summary Notes: "Strong candidate with solid technical foundation"
  3. Select Recommendation: "PROCEED"
  4. Click "Submit Review"
  5. Verify toast: "Review submitted successfully"
  6. Verify status badge changes: MANAGER_EVAL_PENDING → MANAGER_REVIEWED
  7. Verify all form fields become disabled (gray, cursor: not-allowed)
  8. Verify green info box: "Review Submitted - Submitted on [date]"
  9. Verify "Save Draft" & "Submit Review" buttons disappear
  10. Verify "Escalate to SMO" button NOW appears & enabled
  11. Refresh page, verify state persisted

Expected:

  • Status transitioned to MANAGER_REVIEWED
  • Editing locked (all fields disabled)
  • Escalate button available
  • Audit logs: MANAGER_REVIEW_SUBMITTED + CANDIDATE_STATUS_CHANGED events

Test Case 5: Escalate to SMO

Setup: Candidate at MANAGER_REVIEWED with submitted review

Steps:

  1. Click "Escalate to SMO" button
  2. Verify confirmation modal: "Are you sure you want to escalate..."
  3. Click "Cancel" → modal closes, status unchanged
  4. Click "Escalate to SMO" button again
  5. Click "Escalate" button in modal
  6. Verify toast: "Candidate escalated to SMO successfully"
  7. Verify status badge: MANAGER_REVIEWED → TO_SMO
  8. Verify "Escalate to SMO" button disappears
  9. Verify message: "Review has been escalated to SMO. No further edits allowed."
  10. Refresh page, verify status persisted

Expected:

  • Status transitioned to TO_SMO
  • Escalate button removed
  • Audit log shows SENT_TO_SMO event with manager details

Test Case 6: HR/SMO Can View But Not Edit

Setup: Candidate at MANAGER_EVAL_PENDING, logged in as HR user

Steps:

  1. Navigate to /candidates/{candidateId}?tab=manager-review
  2. Verify Section B scorecard fields visible but DISABLED
  3. Try to click rating button → no response (disabled)
  4. Try to type in comment field → field non-interactive
  5. Verify "Save Draft" button HIDDEN or DISABLED
  6. Verify "Submit Review" button HIDDEN or DISABLED
  7. Verify "Escalate to SMO" button HIDDEN
  8. Verify Section A, B, C all visible but read-only
  9. Verify gray background on all form inputs

Expected:

  • Complete read-only view for HR/SMO
  • No editing capability
  • All information visible for review purposes
  • Clear indication of read-only state

Test Case 7: Manager Review at Wrong Status (Error)

Setup: Candidate at NEW status

Steps:

  1. Try to navigate to manager review tab
  2. Verify message: "Manager review is not available until HR assigns a manager and completes screening."
  3. Try to call PUT endpoint: PUT /api/candidates/{candidateId}/manager-review
  4. Verify error 409: "Cannot edit manager review at this stage"

Expected:

  • UI shows appropriate gating message
  • API enforces status validation
  • No accidental review creation at wrong status

Test Case 8: Only Assigned Manager Can Edit

Setup: Candidate assigned to Manager A, logged in as Manager B

Steps:

  1. Navigate to /candidates/{candidateId}?tab=manager-review
  2. Verify read-only view (fields disabled)
  3. Try to call PUT endpoint as Manager B
  4. Verify error 403: "Forbidden"
  5. Log in as Manager A (assigned manager)
  6. Verify editable view (fields enabled)
  7. Save draft successfully

Expected:

  • Only assigned manager can edit
  • Other managers get read-only view
  • Clear RBAC enforcement

Test Case 9: Admin Can Do Everything

Setup: Candidate at MANAGER_EVAL_PENDING, logged in as ADMIN

Steps:

  1. Edit and save draft (as assigned manager would)
  2. Submit review successfully
  3. Escalate to SMO successfully
  4. No permission errors at any step

Expected:

  • Admin bypasses role check
  • Admin can act on behalf of manager
  • Full control over workflow

Test Case 10: Audit Trail Verification

Setup: Complete Test Cases 2-5 (draft → submit → escalate)

Steps:

  1. Query audit logs for candidate
  2. Verify events in order:
    • MANAGER_REVIEW_DRAFT_SAVED (test 2)
    • MANAGER_REVIEW_SUBMITTED (test 4)
    • CANDIDATE_STATUS_CHANGED (MANAGER_EVAL_PENDING → MANAGER_REVIEWED) (test 4)
    • SENT_TO_SMO (test 5)
  3. Verify metadata contains:
    • candidateId, candidateCode
    • recommendation, averageScore, answeredCategories
    • managerName, managerEmail (for escalate)
  4. Verify actor user ID matches logged-in user
  5. Verify timestamps in ascending order

Expected:

  • Complete audit trail
  • Metadata matches operations
  • All events non-blocking (didn't delay responses)

Test Case 11: Average Score Calculation

Setup: Scorecard with varied ratings

Steps:

  1. Rate categories: 5, 4, 3, 2, 2, 1 (6 categories)
  2. Verify average shown: (5+4+3+2+2+1)/6 = 2.83
  3. Update rating 1 to 4 (remove lowest)
  4. Verify average updates to: (5+4+3+2+2+4)/6 = 3.33
  5. Remove rating from one category (set to null/unrated)
  6. Verify average recalculates excluding unrated: (5+4+3+2+2+4)/5 = 4.0

Expected:

  • Real-time average calculation
  • Only counted rated categories (excludes null)
  • Precision to 2 decimal places

Test Case 12: UI/UX - Expandable Sections

Setup: Manager review tab open

Steps:

  1. Verify all 3 sections expanded by default
  2. Click Section A header → collapses (chevron rotates)
  3. Content hidden, can click to expand again
  4. Toggle sections multiple times
  5. Verify independent toggling (expanding B doesn't collapse A)

Expected:

  • Smooth section collapse/expand
  • Chevron icon rotation animation
  • Independent state per section

Post-Flight Checks

  • No TypeScript compilation errors
  • No console errors during testing
  • All status transitions working correctly
  • Audit logs complete and accurate
  • Average score calculation accurate
  • RBAC enforcement strict (no unauthorized access)
  • Performance acceptable (< 1s for API calls)
  • UI responsive and accessible

Summary

W8 successfully implements manager review with:

Complete Backend: 4 API endpoints with validation, RBAC, audit logging ✅ Complete Frontend: Candidate detail tab + ManagerReviewTab component with 3 sections ✅ Database Schema: ManagerReview model + enums + relationships ✅ Validation: Zod schemas with ≥3 criteria rule, summary required, comments for low ratings ✅ Status Progression: MANAGER_EVAL_PENDING → MANAGER_REVIEWED → TO_SMO ✅ Scorecard: 6 built-in categories with 1-5 rating scale, extensible for W9 templates ✅ RBAC: Only assigned manager can edit; HR/SMO/Admin read-only ✅ Audit Trail: 3 event types with detailed metadata ✅ Real-time UX: Average score calculation, conditional comments, expandable sections

Status: Production ready pending Postgres database availability

Next Steps (W9+):

  • Custom scorecard templates per role
  • SMO decision workflow (W10)
  • Notifications integration
  • Reporting dashboards