Notifications
The portal notifies stakeholders at every PO state transition — by email and
via an in-app notification bell. Email dispatch is centralised in
lib/notifier.ts and called only from state-machine side-effects, never
directly from UI handlers. Every notification is also persisted to the
Notification table for audit, in both prod and dev.
Email: prod vs dev
- Production — templates in
emails/(React Email), rendered server-side with@react-email/renderand sent via the Resend SDK. - Development — the recipient, subject, and body are printed to the
terminal (lines prefixed
📧 [DEV EMAIL]); no Resend key required.
The switch is automatic on NODE_ENV. Preview templates live with
pnpm email:preview (http://localhost:3001).
Email templates (emails/)
| Template | Sent on |
|---|---|
po-submitted.tsx |
PO submitted → Manager |
po-approved.tsx |
PO approved → Submitter + Accounts |
po-rejected.tsx |
PO rejected → Submitter (with reason) |
edits-requested.tsx |
Edits requested → Submitter (with note) |
vendor-id-needed.tsx |
Vendor ID requested → Submitter |
payment-processed.tsx |
Payment sent / paid → Submitter + Manager |
receipt-confirmed.tsx |
Receipt confirmed → Manager + Accounts |
layout.tsx |
Shared email shell |
Event → recipient matrix
Driven by the side-effects declared per transition in the state machine:
| Event | Side-effect | Notified |
|---|---|---|
| Submit / resubmit | EMAIL_MANAGER |
Manager(s) |
| Vendor ID requested | EMAIL_SUBMITTER |
Submitter |
| Vendor ID provided | EMAIL_MANAGER |
Manager |
| Edits requested | EMAIL_SUBMITTER |
Submitter (with note) |
| Approve / Approve+Note | EMAIL_SUBMITTER, EMAIL_ACCOUNTS |
Submitter + Accounts |
| Reject | EMAIL_SUBMITTER |
Submitter (with reason) |
| Payment sent | EMAIL_SUBMITTER_AND_MANAGER |
Submitter + Manager |
| Marked paid | EMAIL_SUBMITTER, EMAIL_MANAGER |
Submitter + Manager |
| Receipt confirmed (full) | EMAIL_MANAGER, EMAIL_ACCOUNTS |
Manager + Accounts |
Partial-payment / partial-receipt transitions carry no email side-effects; they update state silently until the full settlement event fires.
In-app notification bell
Every signed-in user sees a bell in the header with an unread count
badge. The dropdown lists recent items; each may carry a link to the relevant
PO. Backed by the Notification model (isRead, link fields) and served by:
/api/notifications(GET) — the current user's notifications/api/notifications/read(POST) — mark notifications read
Report Issue
Separate from PO notifications: any signed-in user can file a bug from the header
via the Report Issue button (components/layout/report-issue-button.tsx →
report-issue-actions.ts → lib/forgejo.ts), which creates a Forgejo issue
labelled portal. That kicks off the
Issue-to-Deploy Pipeline. Requires FORGEJO_URL,
FORGEJO_REPO, FORGEJO_TOKEN (token scope write:issue).
Pelagia Portal (PPMS)
Overview
Build & Run
System
Product
- Feature Catalogue
- Pages and Navigation
- Workflows
- Purchase Orders
- Vendors and GST Lookup
- Inventory and Catalogue
- Inventory on Approval
- Notifications
- File Storage
- Design System
Planned
Quality
Ops
Engineering
Pelagia Portal (PPMS) — internal purchase-order management. Self-hosted on pms1, live at pms.pelagiamarine.com. This wiki tracks the shipped product; authoritative sources are the repo code, App/CLAUDE.md, Docs/, and CHANGELOG.md.