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

W2 Implementation: Request Access

Implemented a public request access page where users without accounts can submit access requests. The page includes form validation, duplicate checking, audit logging, and a success confirmation screen with a unique request code.

W2-REQUEST-ACCESS-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.

W2 Implementation: Request Access

A. Summary

Implemented a public request access page where users without accounts can submit access requests. The page includes form validation, duplicate checking, audit logging, and a success confirmation screen with a unique request code.

B. Routes Implemented

  • GET /request-access – Public request access page (no auth required)
  • POST /api/access-requests – Public API endpoint for submitting access requests

C. Data Model Changes

Prisma Schema Updates

Added to prisma/schema.prisma:

New Enums:

  • RequestedRole – HR, MANAGER, SMO, ADMIN
  • AccessRequestStatus – PENDING, APPROVED, REJECTED

New Model:

model AccessRequest {
  id               String                @id @default(cuid())
  requestCode      String                @unique
  fullName         String
  workEmail        String                @unique
  department       String
  requestedRole    RequestedRole
  reason           String                @db.Text
  managerEmail     String?
  status           AccessRequestStatus   @default(PENDING)
  decidedByUserId  String?
  decidedBy        User?                 @relation(fields: [decidedByUserId], references: [id])
  decidedAt        DateTime?
  decisionNote     String?
  createdAt        DateTime              @default(now())
  updatedAt        DateTime              @updatedAt
}

Updated Model:

  • AuditLog.userId changed from required to nullable (to support public/system audit events)

Updated Enum:

  • AuditEventType added ACCESS_REQUEST_CREATED

Migration Steps

cd /Users/rezafahmi/projectweb-nextjs

# Create and apply migration
npx prisma migrate dev --name add_access_requests

# Verify schema
npx prisma studio

D. UI Components Created

Components

Form Features

  • Client-side validation for all fields
  • Real-time error clearing on user input
  • Character counter for reason field
  • Success state shows unique request code
  • Role descriptions in info panel
  • Responsive layout (stacks on mobile)

E. API Logic Created

Files

Endpoint Details

POST /api/access-requests

  • Request:

    {
      "fullName": "string",
      "workEmail": "string",
      "department": "string",
      "requestedRole": "HR|MANAGER|SMO|ADMIN",
      "reason": "string (min 20 chars)",
      "managerEmail": "string (optional)"
    }
    
  • Response (Success – 201):

    {
      "requestCode": "REQ-2026-12345",
      "status": "PENDING"
    }
    
  • Response (Error):

    • 400: Validation error
      {
        "error": "Validation failed",
        "details": { ... }
      }
      
    • 409: Duplicate user or pending request
      { "error": "You already have an account. Try Forgot Password." }
      // OR
      { "error": "You already have a pending request." }
      
    • 500: Server error

Server-side Flow

  1. Parse and validate request body against Zod schema
  2. Normalize email to lowercase
  3. Check if user exists with that email → 409 if yes
  4. Check if PENDING access request exists for that email → 409 if yes
  5. Generate unique request code (REQ-YYYY-#####)
  6. Create AccessRequest with status = PENDING
  7. Log ACCESS_REQUEST_CREATED audit event
  8. Return 201 with requestCode + status

F. RBAC Checks

  • /request-access is public (no authentication required)
  • POST /api/access-requests is public (no authentication required)
  • Both endpoints accept requests from any user (including unauthenticated)

G. Audit Events

ACCESS_REQUEST_CREATED

{
  "eventType": "ACCESS_REQUEST_CREATED",
  "userId": null,
  "details": {
    "requestCode": "REQ-2026-12345",
    "workEmail": "user@example.com",
    "accessRequestId": "cuid..."
  },
  "createdAt": "2026-01-23T..."
}

Note: userId is null for public actions; future wireframes (W3, W16) will log admin actions with actual userId.

H. Test Checklist

Setup

  1. Create .env.local with DATABASE_URL and JWT_SECRET (if not already done)
  2. Run npx prisma migrate dev --name add_access_requests
  3. Run npm run dev to start development server

Manual Tests

Basic Form Validation

  • Navigate to http://localhost:3000/request-access
  • Try submitting empty form → shows "X is required" errors
  • Enter invalid email → shows "Please enter a valid email"
  • Enter reason < 20 chars → shows "Reason must be at least 20 characters"
  • Enter invalid manager email → shows "Please enter a valid manager email"
  • Verify character counter updates as you type reason

Successful Submission

  • Fill form with valid data:
    • Full Name: "John Doe"
    • Work Email: "john@example.com"
    • Department: "Engineering"
    • Requested Role: "Hiring Manager"
    • Reason: "I need to evaluate candidates for our engineering team"
    • Manager Email: (leave blank)
  • Click Submit Request
  • Verify success page shows:
    • "Request Submitted" message
    • Reference ID like "REQ-2026-#####"
    • "You'll receive an email once approved"
    • "← Back to Sign In" link works

Duplicate Handling

  • Submit request for email: test@example.com
  • Try submitting again with same email → error "You already have a pending request."
  • Verify database shows PENDING request

Existing User Handling

  • Try submitting with an email that already has a User account (e.g., admin@example.com from seed)
  • Verify error: "You already have an account. Try Forgot Password."

Responsive Design

  • Test on mobile/tablet → form and info panel stack vertically
  • Test on desktop → form on left (2/3), info panel on right (1/3)

Database Verification

# Open Prisma Studio
npx prisma studio

# Verify AccessRequest table
# - Check requestCode is unique (REQ-YYYY-#####)
# - Check all submitted requests have status PENDING
# - Check workEmail is lowercase

# Verify AuditLog table
# - Check ACCESS_REQUEST_CREATED events exist
# - Check userId is null for public actions
# - Check details contains requestCode and workEmail

API Direct Testing (curl/Postman)

# Valid request
curl -X POST http://localhost:3000/api/access-requests \
  -H "Content-Type: application/json" \
  -d '{
    "fullName": "Jane Smith",
    "workEmail": "jane@example.com",
    "department": "HR",
    "requestedRole": "HR",
    "reason": "I need access to review candidate resumes and screen applications",
    "managerEmail": "manager@example.com"
  }'

# Should return 201 with requestCode

# Duplicate request
curl -X POST http://localhost:3000/api/access-requests \
  -H "Content-Type: application/json" \
  -d '{"fullName": "Jane Smith", ...}'

# Should return 409 with error message

# Invalid data
curl -X POST http://localhost:3000/api/access-requests \
  -H "Content-Type: application/json" \
  -d '{"fullName": "", ...}'

# Should return 400 with validation details

Next Steps (W3)

  • Implement W3: Admin Access Requests approval interface
  • Add email notifications for pending requests
  • Add decision workflow (approve/reject)

Code Structure Summary

src/
├── app/
│   ├── api/
│   │   └── access-requests/
│   │       └── route.ts          (POST endpoint)
│   └── request-access/
│       └── page.tsx              (public page)
├── components/
│   └── RequestAccessForm.tsx      (form component)
└── lib/
    ├── audit.ts                  (updated for nullable userId)
    ├── validation/
    │   └── schemas.ts            (Zod validation)
    └── ... (existing auth, password, prisma files)

prisma/
├── schema.prisma                 (updated with AccessRequest + enums)
└── migrations/
    └── [timestamp]_add_access_requests/