- App/README.md: add FORGEJO_*/NEXT_PUBLIC_ENV_LABEL env vars and an 'Operations & Automation' section pointing to automation/README.md. - App/CLAUDE.md: complete the env var list (AZURE_AD_*, FORGEJO_*, GST_SERVICE_URL, NEXT_PUBLIC_ENV_LABEL) and note the prod-mirror test DB used by autofix/staging. - .env.example: document NEXT_PUBLIC_ENV_LABEL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
246 lines
7.9 KiB
Markdown
246 lines
7.9 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
pnpm install
|
|
```
|
|
|
|
### 2. Configure environment
|
|
|
|
Copy the example file and fill in the two required values:
|
|
|
|
```bash
|
|
cp .env.example .env.local
|
|
```
|
|
|
|
Minimum `.env.local` for development:
|
|
|
|
```env
|
|
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:
|
|
|
|
```bash
|
|
pnpm db:migrate # runs prisma migrate dev
|
|
pnpm db:seed # seeds vessels, accounts, vendors, and demo users
|
|
```
|
|
|
|
To inspect the database with a GUI:
|
|
|
|
```bash
|
|
pnpm db:studio # opens Prisma Studio at http://localhost:5555
|
|
```
|
|
|
|
### 4. Start the dev server
|
|
|
|
```bash
|
|
pnpm dev
|
|
```
|
|
|
|
The app will be available at [http://localhost:3000](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:
|
|
|
|
```env
|
|
# 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
|
|
|
|
```bash
|
|
pnpm db:migrate:deploy # runs prisma migrate deploy (safe for production)
|
|
```
|
|
|
|
### 3. Build and start
|
|
|
|
```bash
|
|
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`](../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/`](../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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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.
|