pelagia-portal/App
Hardik 938ff6df89 test+ci: green the test baseline and make type-check + unit tests hard gates
Green-lights the test suite so the PR checks can enforce it:
- Fix the NextAuth v5 auth() mock typing across all integration tests (cast to a
  simple async fn so mockResolvedValue accepts the session) — clears ~86 errors.
- Fix stale test values: intent 'resubmit'->'submit' / 'save'->'draft'; ParsedImportLine
  .description -> .name; approvepo -> approvePo; add missing beforeEach/beforeAll imports.
- permissions: MANAGER *can* process_payment (intentional since e1340b9) — update the
  stale assertion.
- po-import-parser: skip the Sample_PO.xlsx fixture tests when the file is absent (it
  lives outside the repo); synthetic-workbook tests still cover the parser.

type-check is now 0 errors and unit tests pass (167 passed, 13 skipped). pr-checks.yml
flips type-check (whole project) and unit tests to HARD gates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 13:03:54 +05:30
..
app Merge branch 'master' into claude/issue-31 2026-06-19 06:59:39 +00:00
components Merge pull request 'fix: PO details: show all attachments, grouped by type' (#27) from claude/issue-10 into master 2026-06-19 06:57:08 +00:00
emails chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
lib fix(po): show all attachments grouped by type on PO details 2026-06-19 04:43:44 +05:30
prisma feat(po): allow submitter to set an optional PO date 2026-06-16 13:06:12 +05:30
scripts chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
tests test+ci: green the test baseline and make type-check + unit tests hard gates 2026-06-19 13:03:54 +05:30
.env.example docs: document the issue-to-deploy pipeline, staging, and test DB 2026-06-19 12:07:55 +05:30
.gitignore chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
auth.ts feat(auth): add Microsoft 365 SSO via Azure Entra ID 2026-05-28 22:48:37 +05:30
CLAUDE.md docs: document the issue-to-deploy pipeline, staging, and test DB 2026-06-19 12:07:55 +05:30
middleware.ts chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
next.config.ts chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
package.json feat: production seed script for Pelagia Marine (pnpm db:seed:prod) 2026-05-30 04:26:38 +05:30
playwright.config.ts chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
pnpm-lock.yaml chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
pnpm-workspace.yaml pnpm workspace allow build scripts 2026-05-22 17:17:15 +05:30
postcss.config.mjs chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
README.md docs: document the issue-to-deploy pipeline, staging, and test DB 2026-06-19 12:07:55 +05:30
requirements.txt chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
tsconfig.json chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
vitest.config.ts chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30
vitest.integration.config.ts chore: restructure repo — flatten App/pelagia-portal to App, rename Prototype→Wireframe and Spec→Design 2026-05-18 23:18:58 +05:30

Pelagia Portal

An internal purchase order management system for a maritime vessel-operations company. Digitises the full PO lifecycle — from crew requisition through manager approval, vendor validation, accounts payment, and receipt confirmation — replacing ad-hoc email chains and spreadsheets with a single auditable workflow.

Tech Stack

Layer Technology
Framework Next.js 15 (App Router)
Language TypeScript 5 (strict)
Database PostgreSQL 16 via Prisma 5
Auth NextAuth.js v5 (credentials)
Styling Tailwind CSS v4 + shadcn/ui
File Storage Cloudflare R2 (production) / local filesystem (development)
Email Resend (production) / console log (development)

Prerequisites

Tool Required Version
Node.js >= 20.11.0 LTS
pnpm >= 9.0.0
PostgreSQL >= 16 (local or Docker)

Install pnpm if you don't have it:

npm install -g pnpm

Development Setup

In development mode the app requires only a database and auth secret — Cloudflare R2 and Resend are not needed. File uploads are saved to .dev-uploads/ on your local machine, and emails are printed to the terminal instead of being sent.

1. Install dependencies

pnpm install

2. Configure environment

Copy the example file and fill in the two required values:

cp .env.example .env.local

Minimum .env.local for development:

NEXTAUTH_SECRET=<generate with: openssl rand -base64 32>
NEXTAUTH_URL=http://localhost:3000
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/pelagia_portal"

The R2 and Resend variables are not needed in development and can be left as placeholders.

3. Set up the database

Create the database (if it doesn't exist yet), run migrations, and seed sample data:

pnpm db:migrate     # runs prisma migrate dev
pnpm db:seed        # seeds vessels, accounts, vendors, and demo users

To inspect the database with a GUI:

pnpm db:studio      # opens Prisma Studio at http://localhost:5555

4. Start the dev server

pnpm dev

The app will be available at http://localhost:3000.

Email behaviour in dev: all notification emails are logged to the terminal in place of actual delivery. Look for lines starting with 📧 [DEV EMAIL].

File upload behaviour in dev: uploaded files are written to .dev-uploads/ at the project root. This directory is git-ignored.


Serving in Production

Production requires all environment variables to be set, including Cloudflare R2 credentials and a Resend API key.

1. Configure environment

Set the following variables in your hosting platform (Vercel, etc.) or in .env.local for a self-hosted deploy:

# Auth
NEXTAUTH_SECRET=<strong random secret>
NEXTAUTH_URL=https://your-domain.com

# Database
DATABASE_URL=postgresql://<user>:<password>@<host>:<port>/<db>

# Cloudflare R2
R2_ACCOUNT_ID=<your cloudflare account id>
R2_ACCESS_KEY_ID=<r2 access key>
R2_SECRET_ACCESS_KEY=<r2 secret key>
R2_BUCKET_NAME=pelagia-portal
R2_PUBLIC_URL=https://<bucket>.<account>.r2.cloudflarestorage.com

# Email
RESEND_API_KEY=re_<your key>
EMAIL_FROM=noreply@yourdomain.com
EMAIL_FROM_NAME="Pelagia Portal"

# Report Issue button -> files a Forgejo issue (optional; token needs write:issue)
FORGEJO_URL=https://git.example.com
FORGEJO_REPO=owner/repo
FORGEJO_TOKEN=<forgejo access token>

# Non-prod banner (leave UNSET in production). When set, a fixed
# "INTERNAL DEV / STAGING - NOT PRODUCTION" banner is shown.
# NEXT_PUBLIC_ENV_LABEL="INTERNAL DEV / STAGING - NOT PRODUCTION"

2. Run database migrations

pnpm db:migrate:deploy    # runs prisma migrate deploy (safe for production)

3. Build and start

pnpm build
pnpm start

The app listens on port 3000 by default. Point your reverse proxy (nginx, Caddy, etc.) or hosting platform to that port.


Operations & Automation

This repo carries its own self-hosted issue-to-deploy pipeline (Forgejo + Claude Code on the pms1 server). The full design and runbook live in ../automation/README.md. In short:

  • Report Issue button (portal header) files a Forgejo issue tagged portal.
  • A watcher triages each issue (Claude posts a requirements breakdown and routes it to claude-queue or interactive), then for queued issues implements a fix and opens a PR.
  • Merging a PR and pushing a release tag (vX.Y.Z) triggers a Forgejo Actions runner that deploys to production.
  • A staging instance (automation/staging-up.sh, pm2 ppms-staging on port 3200, SSH-tunnel only) runs the latest master against a daily prod-mirror test DB (pelagia_test) for smoke testing before tagging a release.

Operational scripts live under ../automation/: claude-issue-watcher.sh (watcher), refresh-test-db.sh (nightly test-DB refresh), staging-up.sh (staging), and staging-tunnel.cmd (Windows tunnel launcher).


Database Management

Command Purpose
pnpm db:migrate Create and run a new migration (dev only)
pnpm db:migrate:deploy Apply pending migrations without prompting (CI/production)
pnpm db:push Push schema changes without a migration file (prototyping only)
pnpm db:seed Seed sample data
pnpm db:studio Open Prisma Studio GUI
pnpm db:reset Drop and recreate the database, then re-seed (dev only)

Testing

pnpm test           # unit + integration tests (Vitest)
pnpm test:watch     # watch mode
pnpm test:e2e       # end-to-end tests (Playwright)
pnpm test:e2e:ui    # Playwright with interactive UI

Other Scripts

pnpm lint           # ESLint
pnpm type-check     # tsc --noEmit
pnpm email:preview  # live-preview email templates at http://localhost:3001

Project Structure

pelagia-portal/
├── app/                    # Next.js App Router pages and API routes
│   ├── (auth)/             # Login
│   ├── (portal)/           # Authenticated shell (sidebar + header)
│   │   ├── dashboard/
│   │   ├── po/             # PO creation, detail, edit
│   │   ├── approvals/      # Manager approval queue
│   │   ├── payments/       # Accounts payment queue
│   │   ├── history/        # Audit trail
│   │   └── admin/          # User, vessel, account, vendor management
│   └── api/
│       ├── auth/           # NextAuth endpoints
│       ├── files/sign/     # Generate presigned upload URL (production)
│       ├── files/dev/      # Local file upload/download handler (dev only)
│       └── reports/export/ # CSV / PDF export
├── components/             # Shared UI components (shadcn/ui + custom)
├── lib/                    # Business logic
│   ├── po-state-machine.ts # All PO state transitions enforced here
│   ├── permissions.ts      # Role → allowed-action map
│   ├── notifier.ts         # Email dispatch (Resend in prod, console in dev)
│   ├── storage.ts          # File storage (R2 in prod, local in dev)
│   └── validations/        # Zod schemas
├── emails/                 # React Email templates
├── prisma/                 # Schema and migrations
└── tests/                  # Vitest unit/integration + Playwright E2E

Roles

Role Description
Technical Deck/engine crew — create and submit POs, confirm receipt
Manning Crew-management staff — same as Technical
Manager Review, approve, reject, request edits
Accounts Process payment for approved POs
SuperUser Combined Technical + Manning + Manager authority
Auditor Read-only access to all records and reports
Admin Manage users, vessels, accounts, and vendors

User accounts are provisioned by an Admin; there is no self-registration.