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

W6: HR Upload Resume - Implementation Complete ✅

W6 is the entry point to the hiring system. HR users upload a resume (PDF/DOC/DOCX), enter a position, and the system creates a Candidate record (status=NEW) + Document record, then redirects to the candidate detail page (W7).

W6-README.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.

W6: HR Upload Resume - Implementation Complete ✅

Quick Start

What is W6?

W6 is the entry point to the hiring system. HR users upload a resume (PDF/DOC/DOCX), enter a position, and the system creates a Candidate record (status=NEW) + Document record, then redirects to the candidate detail page (W7).

Files Summary

  • 2 API endpoints (presign + create candidate)
  • 2 UI components (form + dropzone)
  • 2 utility libraries (S3 + code generator)
  • Updated database schema (Candidate + CandidateDocument models)
  • 4 comprehensive docs (spec + setup + guide + code structure)

Deployment (5 steps)

  1. Database Migration

    cd /Users/rezafahmi/projectweb-nextjs
    export $(cat .env.local | xargs)
    npx prisma migrate dev --name add_candidates_and_documents
    
  2. Install AWS SDK

    npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
    
  3. Configure AWS Add to .env.local:

    AWS_REGION=us-east-1
    AWS_ACCESS_KEY_ID=your-key
    AWS_SECRET_ACCESS_KEY=your-secret
    S3_BUCKET_NAME=offers-review
    
  4. Start Dev Server

    npm run dev
    
  5. Test

Key Files

FilePurposeLines
src/app/api/uploads/presign/route.tsS3 presign endpoint54
src/app/api/candidates/route.tsCreate candidate endpoint104
src/app/(app)/upload-resume/page.tsxMain form312
src/app/(app)/upload-resume/_components/Dropzone.tsxDrag-drop component124
src/lib/storage/s3.tsS3 helper42
src/lib/candidates/code.tsCode generator7
prisma/schema.prismaDatabase models+60

How It Works

User selects PDF + position
        ↓
POST /api/uploads/presign
        ↓
Get presigned S3 URL + storageKey
        ↓
PUT file directly to S3
        ↓
POST /api/candidates with storageKey
        ↓
Create Candidate (NEW) + Document (RESUME) + audit logs
        ↓
Auto-redirect to /candidates/{id}

RBAC

  • Only HR and Admin can access /upload-resume
  • Only HR and Admin can call /api/candidates
  • Other roles get 403 Forbidden

Validation

  • File types: PDF, DOC, DOCX only
  • File size: Max 10MB
  • Position: Required
  • Name: Optional

Audit Events

  • CANDIDATE_CREATED – Logs candidateCode, status, position
  • RESUME_UPLOADED – Logs filename, size, storage location

Documentation

  • docs/W6-UPLOAD-RESUME-IMPLEMENTATION.md – Full specification
  • docs/W6-IMPLEMENTATION-SUMMARY.md – Overview + integration points
  • docs/W6-SETUP-GUIDE.md – Migration + deployment + testing
  • docs/W6-CODE-STRUCTURE.md – File tree + code snippets
  • W6-DELIVERY-SUMMARY.md – This project delivery summary

Testing Checklist

  • Migration applied
  • AWS credentials configured
  • Dev server running
  • HR can upload PDF
  • Redirect to /candidates/{id} works
  • Database records created
  • Audit logs exist
  • File in S3
  • RBAC works (403 for other roles)
  • Error handling (invalid type, >10MB)

Common Issues

"DATABASE_URL not found"

export $(cat .env.local | xargs)

"Cannot find module @aws-sdk/..."

npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

"Can't reach database server" Ensure PostgreSQL is running:

psql projectweb -U postgres -c "SELECT 1"

API Endpoints

POST /api/uploads/presign

Request: { "filename": "resume.pdf", "mimeType": "application/pdf", "sizeBytes": 524288 }
Response: { "uploadUrl": "https://...", "storageKey": "candidates/...", "expiresIn": 3600 }
Auth: HR/Admin only

POST /api/candidates

Request: {
  "applyingFor": "Software Engineer",
  "fullName": "John Doe",
  "resume": { "filename": "...", "mimeType": "...", "sizeBytes": 524288, "storageKey": "..." }
}
Response: { "candidateId": "cuid...", "candidateCode": "CAND-2026-45678" }
Auth: HR/Admin only

Database Schema

Candidate

  • id (PK)
  • candidateCode (UNIQUE, CAND-YYYY-#####)
  • fullName (nullable)
  • applyingFor (required)
  • status (NEW by default)
  • createdByUserId (FK to User)
  • createdAt, updatedAt

CandidateDocument

  • id (PK)
  • candidateId (FK)
  • category (RESUME)
  • filename, mimeType, sizeBytes
  • storageKey (S3 path)
  • version (1)
  • uploadedByUserId (FK to User)
  • createdAt

Integration

Upstream: W5 (Dashboard) – Can link to /upload-resume for HR

Downstream: W7 (Candidate Intake) – Auto-redirects from W6, loads candidate detail + resume

Same Pattern: W3 (Access Requests) – Same RBAC (HR/Admin only)

Performance

  • Upload: 10MB max, presigned URL expires in 1 hour
  • DB: Indexed on candidateCode, status, createdByUserId
  • Audit: Non-blocking (failure doesn't break operation)

Security

  • JWT authentication (httpOnly cookie)
  • Role-based access control (403 if not HR/Admin)
  • File validation (type + size, client + server)
  • S3 presigned URLs (1-hour expiry)
  • Audit trail for compliance

What's Next

  1. Apply migration
  2. Configure AWS
  3. Test end-to-end
  4. Deploy to staging
  5. Implement W7 (Candidate Intake)

Support

See documentation in docs/ folder:

  • Stuck? Check W6-SETUP-GUIDE.md
  • Need code? Check W6-CODE-STRUCTURE.md
  • Full spec? Check W6-UPLOAD-RESUME-IMPLEMENTATION.md

W6 is ready for deployment! 🚀

For questions, refer to the comprehensive documentation provided.