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

ERS

Employee Renewal System (ERS)

ERS is an internal renewal workflow system built with Next.js App Router + Prisma + Postgres.

README.md

Updated Apr 1, 2026, 4:45 AM

Employee Renewal System (ERS)

ERS is an internal renewal workflow system built with Next.js App Router + Prisma + Postgres.

Specs

  • docs/specs/ERS-v0.1-spec.md
  • docs/specs/ERS-v0.2-build-pack.md
  • docs/specs/ERS-v0.3-runnable-pack.md

Stack

  • Next.js (App Router, TypeScript)
  • Prisma + PostgreSQL
  • Local filesystem uploads (UPLOAD_BASE_DIR)
  • JWT cookie auth (email + password)
  • Resend-backed durable email outbox
  • PDF signature stamping with pdf-lib

Environment

Set values in .env (and in /etc/ers-prod.env if you run via ers-prod.service):

  • DATABASE_URL
  • AUTH_JWT_SECRET
  • AUTH_COOKIE_NAME
  • UPLOAD_BASE_DIR
  • UPLOAD_MAX_MB
  • NEXT_PUBLIC_APP_VERSION
  • RESEND_API_KEY
  • EMAIL_FROM
  • NEXTAUTH_URL (public app base URL used in account email login links, e.g. https://review.r32a.com)
  • OPENAI_API_KEY (optional, for smarter offer-letter extraction)
  • OPENAI_MODEL (optional, default gpt-4.1-mini)
  • INTERNAL_API_SECRET
  • BOOTSTRAP_CEO_EMAIL (required for npm run db:seed)
  • BOOTSTRAP_CEO_NAME (required for npm run db:seed)
  • BOOTSTRAP_CEO_PASSWORD (required for npm run db:seed)
  • BOOTSTRAP_COMPANY_NAME (optional, used by npm run db:seed)

Install

npm install
npm run prisma:generate

Database Setup

Set bootstrap values before running the seed:

export BOOTSTRAP_CEO_EMAIL="ceo@company.com"
export BOOTSTRAP_CEO_NAME="Chief Executive Officer"
export BOOTSTRAP_CEO_PASSWORD="replace-with-a-strong-password"
export BOOTSTRAP_COMPANY_NAME="Your Company Name"

Then run:

npm run prisma:migrate
npm run db:seed

Run

npm run dev

Open http://localhost:3000.

npm run db:seed bootstraps the initial CEO account and default settings. It also removes the legacy seeded @ers.local demo accounts if they are still present.

Core URLs

  • /login
  • /hr/renewals
  • /manager/requests
  • /ceo/renewals
  • /ceo/signature
  • /admin/settings
  • /admin/audit
  • /admin/users
  • /notifications

User Administration

  • CEO can create users from /admin/users.
  • CEO can activate/deactivate users from /admin/users.
  • CEO can delete other users from /admin/users.
  • Safeguards:
    • CEO cannot delete their own account.
    • Last remaining CEO account cannot be deleted.
    • If a user has related records, delete is blocked and you should deactivate instead.

Offer Letter Autofill

  • HR can upload an offer-letter PDF on /hr/renewals/new and click Extract & Prefill.
  • If OPENAI_API_KEY is configured, extraction uses OpenAI structured parsing with heuristic fallback.
  • Uploading OFFER_LETTER in a case detail also auto-maps extracted fields to RenewalCase.
  • On /hr/renewals/new, HR can provide Manager Email For Magic Link (and optional manager name) and choose to send the manager justification magic link immediately after case creation.

Email Dispatch

Outbound emails are queued in EmailOutbox. Dispatch endpoint:

  • POST /api/internal/email/dispatch
  • Header: x-internal-secret: <INTERNAL_API_SECRET>

Recommended VPS cron (every minute):

* * * * * curl -X POST https://your-domain/api/internal/email/dispatch -H "x-internal-secret: your-secret"

Email-triggering flows:

  • Creating a user from /admin/users queues an account email to the new user (email, role, temporary password, and login URL) and immediately attempts delivery. Login URL in that email is built from NEXTAUTH_URL (production: https://review.r32a.com/login).
  • Requesting manager justification queues email to the selected manager email and the linked internal manager account email (deduped), including both guest link and manager portal link, and immediately attempts delivery. Re-sending the request generates a new guest link without invalidating previously sent links; all outstanding guest links are revoked automatically after the first successful guest submission.
  • /api/internal/email/dispatch is still used for retries and any pending/failed outbox emails.

Notes

  • HR/Manager cannot modify locked cases (locked_at set by CEO decision).
  • CEO approval requires latest offer letter, performance snapshot, and manager justification.
  • Signature stamping is enabled via /api/renewals/:id/sign-pdf with multi-placement payload.
  • Signing is allowed only when case status is APPROVED.
  • After signing, case status becomes SIGNED (closed/read-only) and HR users are notified via outbox email.