# Pelagia Portal — Build Progress Last updated: 2026-05-05 Legend: ✅ Complete · ⚠️ Partial (works but incomplete) · ❌ Not started --- ## Infrastructure & Config | Item | Status | Notes | |---|---|---| | Next.js 15 App Router project | ✅ | Turbopack dev, strict TS | | Tailwind CSS v4 + design tokens | ✅ | Colour palette matches spec | | Prisma schema — all models | ✅ | Product, gstRate, piQuotationNo/Date, requisitionNo/Date, placeOfDelivery, tcDelivery/tcDispatch/tcInspection/tcTransitInsurance/tcPaymentTerms/tcOthers, address/gstin/contactMobile on Vendor — all migrated (latest: `structured_tc_fields`) | | Database seed script | ✅ | 5 users, 3 vessels, 3 accounts, 3 vendors, 3 sample POs, 4 products | | Dev / production environment split | ✅ | `NODE_ENV` gates R2 vs local storage, Resend vs console log | | `.env.example` + `.env` / `.env.local` docs | ✅ | README covers full setup | | README with dev + prod setup guide | ✅ | | | CI/CD (GitHub Actions) | ❌ | No workflow files yet | --- ## Authentication & Authorisation | Item | Status | Notes | |---|---|---| | Login page (credentials) | ✅ | Email + password, bcrypt hash | | NextAuth.js v5 session | ✅ | Database sessions, role in JWT | | Auth middleware (route protection) | ✅ | Redirects unauthenticated to `/login` | | Role permissions matrix (`lib/permissions.ts`) | ✅ | All 7 roles; `manage_products` added for Admin | | SSO / Azure AD login | ❌ | Open question — credentials only for v1 | --- ## PO State Machine (`lib/po-state-machine.ts`) | Item | Status | Notes | |---|---|---| | All 10 statuses defined | ✅ | | | All transitions with role guards | ✅ | | | `canPerformAction`, `getAvailableActions` helpers | ✅ | | | `requiresNote` helper | ✅ | | | Unit tests | ✅ | `tests/unit/po-state-machine.test.ts` | --- ## Email Notifications (`lib/notifier.ts`) | Item | Status | Notes | |---|---|---| | Notifier module (Resend in prod, console in dev) | ✅ | | | Email templates (all 7 events) | ✅ | React Email components in `/emails/` | | Notify on PO submitted | ✅ | Notifies managers + submitter (confirmation) | | Notify on PO approved / rejected / edits requested | ✅ | Per notification matrix | | Notify on vendor ID requested / provided | ✅ | | | Notify on payment processed | ✅ | Notifies managers + accounts on processing; submitter + managers on confirmed | | Notify on receipt confirmed | ✅ | Notifies managers + accounts | --- ## File Storage (`lib/storage.ts`) | Item | Status | Notes | |---|---|---| | Dev local storage (`/api/files/dev/[...key]`) | ✅ | Auth-gated, path-traversal protected | | Prod Cloudflare R2 presigned URLs | ✅ | Upload + download URL generation | | Sign API (`/api/files/sign`) | ✅ | Returns `{ uploadUrl, key }` | | File upload UI on New PO form | ✅ | `FileUploader` component + `uploadAndLinkFiles` utility; PO created first, then files signed+uploaded+linked | | File upload UI on Receipt form | ✅ | Same pattern, type `"receipt"` | | Document download links on PO detail | ✅ | `PoDetail` made async; `generateDownloadUrl` called server-side; filenames are clickable links | --- ## Pages & Server Actions ### Login | Item | Status | |---|---| | Login page + credentials form | ✅ | ### Dashboard | Item | Status | Notes | |---|---|---| | Submitter dashboard (stat cards + New PO CTA) | ✅ | Recent orders table; link to My Orders | | Manager dashboard (stat cards) | ✅ | | | Manager dashboard — approved POs listing | ✅ | Recent approved/in-progress POs table with vessel, status, amount | | Manager dashboard — spend by vessel breakdown | ✅ | Recharts bar chart (top 5 vessels) | | Manager spend-by-vessel bar chart | ✅ | `SpendCharts` component using Recharts | | Manager spend-by-month bar chart | ✅ | Last 12 months, bar chart | | Accounts dashboard (stat cards) | ✅ | | | Auditor / Admin / generic dashboard | ✅ | Single total-count card | ### New PO | Item | Status | Notes | |---|---|---| | Form (title, vessel, account, vendor, project code, date) | ✅ | | | Line items — UoM dropdown (15 options) | ✅ | Replaces free-text unit field | | Line items — Size field | ✅ | Optional free-text | | Line items — GST rate per item (0 / 5 / 12 / 18 / 28%) | ✅ | Default 18%; taxable / GST / grand-total breakdown shown live in editor | | PI / Quotation No. + Date | ✅ | Separate section on form | | Vessel / Office Requisition No. + Date | ✅ | Separate section on form | | Place of Delivery | ✅ | Pre-filled with company delivery address; editable | | Terms & Conditions — structured fields | ✅ | Fixed line 1 (read-only); separate inputs for Delivery, Dispatch Instructions, Inspection, Transit Insurance, Payment Terms, Others (multiline); all pre-filled with defaults | | Currency defaults to INR | ✅ | Changed from USD → INR throughout | | Save as draft | ✅ | | | Submit for approval (→ MGR_REVIEW) | ✅ | | | Document file upload | ✅ | Drag-and-drop + click; signed upload after PO creation | ### PO Detail (`/po/[id]`) | Item | Status | Notes | |---|---|---| | Full detail view (info, vendor, line items, activity trail) | ✅ | | | New fields displayed (PI/Quotation, Requisition, Delivery, Approved By) | ✅ | | | Line items with GST breakdown (taxable / GST / grand total) | ✅ | | | Vendor detail (address, GSTIN, contact name + mobile + email) | ✅ | | | Terms & Conditions block (structured display) | ✅ | Fixed line 1 always shown; each labeled field (Delivery, Dispatch, etc.) on its own line | | Export PDF button | ✅ | `/api/po/[id]/export?format=pdf` — auto-triggers print dialog; matches Sample_PO layout | | Export XLSX button | ✅ | `/api/po/[id]/export?format=xlsx` — SheetJS; matches Sample_PO column layout | | Confirm Receipt CTA (PAID_DELIVERED state) | ✅ | | | Document download links | ✅ | Clickable links with server-generated presigned/dev URLs | | Edit PO link (DRAFT / EDITS_REQUESTED) | ✅ | | ### PO Edit (`/po/[id]/edit`) | Item | Status | Notes | |---|---|---| | Edit page | ✅ | Pre-populated form including all new fields | | Update draft action | ✅ | | | Resubmit after edits | ✅ | Shows manager note banner + Resubmit button | ### Vendor ID Flow | Item | Status | Notes | |---|---|---| | Request vendor ID action (manager) | ✅ | Sets status to `VENDOR_ID_PENDING` | | Provide vendor ID UI (submitter / manager) | ✅ | Inline form on PO detail for VENDOR_ID_PENDING | | Provide vendor ID server action | ✅ | Transitions back to `MGR_REVIEW`, notifies managers | ### Approvals | Item | Status | Notes | |---|---|---| | Approval queue list | ✅ | | | PO detail + decision view | ✅ | | | Approve / Approve+Note actions | ✅ | | | Reject action | ✅ | | | Request edits action | ✅ | | | Request vendor ID action | ✅ | | | Manager edit line items (amber edit mode) | ✅ | Saves original snapshot to POAction; diff shown with strike-through | | Search / filter (PO number, vessel, submitter, date) | ✅ | URL search params; `ApprovalsSearch` client component | ### Payments | Item | Status | Notes | |---|---|---| | Payment queue list (MGR_APPROVED + SENT_FOR_PAYMENT) | ✅ | | | Start payment processing (MGR_APPROVED → SENT_FOR_PAYMENT) | ✅ | Step 1 | | Confirm payment sent with ref (SENT_FOR_PAYMENT → PAID_DELIVERED) | ✅ | Step 2 — auto-updates Product.lastPrice for linked line items | ### My Orders (`/my-orders`) | Item | Status | Notes | |---|---|---| | My Orders page — all POs with open/past grouping | ✅ | Links to individual PO detail; shows manager note inline | ### Receipt Confirmation | Item | Status | Notes | |---|---|---| | Notes + file receipt confirmation | ✅ | Closes PO to `CLOSED`; optional file attachment via `FileUploader` | ### History | Item | Status | Notes | |---|---|---| | All-POs list (latest 200 by default) | ✅ | | | CSV export | ✅ | Respects active filters | | PDF export (bulk) | ✅ | Print-optimised HTML page (`/api/reports/export?format=pdf`); auto-triggers browser print dialog | | Date range filter | ✅ | URL search params; `HistoryFilters` client component | | Vessel / status filters | ✅ | Same component | ### Admin — User Management | Item | Status | Notes | |---|---|---| | User list | ✅ | | | Add user form + action | ✅ | `AdminDialog` modal; bcrypt password hash | | Edit user form + action | ✅ | Optional password change | | Deactivate / reactivate user | ✅ | Cannot deactivate own account | ### Admin — Vendor Management | Item | Status | Notes | |---|---|---| | Vendor list | ✅ | | | Add vendor form + action | ✅ | `AdminDialog` modal; sets `isVerified` if vendorId provided | | Edit vendor form + action | ✅ | Includes address, GSTIN, contact mobile | | Deactivate / reactivate vendor | ✅ | | ### Admin — Vessel Management | Item | Status | Notes | |---|---|---| | Vessel list page | ✅ | `/admin/vessels` | | Add vessel form + action | ✅ | Name-only cost centre creation | | Edit vessel form + action | ✅ | | | Deactivate / reactivate vessel | ✅ | | ### Admin — Account Management | Item | Status | Notes | |---|---|---| | Account list page | ✅ | `/admin/accounts` | | Add account form + action | ✅ | Code uniqueness check | | Edit account form + action | ✅ | | | Deactivate / reactivate account | ✅ | | ### Admin — Product Catalogue (`/admin/products`) | Item | Status | Notes | |---|---|---| | Product list (code, name, last price, last vendor) | ✅ | Last price/vendor read-only — auto-populated on payment | | Create product action | ✅ | Code must be unique | | Toggle active/inactive action | ✅ | | | Add product form (UI) | ✅ | `AdminDialog` modal wired to existing `createProduct` action | --- ## Type Safety | Item | Status | Notes | |---|---|---| | Discriminated union narrowing in `approval-actions.tsx` | ✅ | Typed as `{ ok: true } \| { error: string } \| undefined`, checked with `"error" in result` | | Union narrowing in `receipt-form.tsx` | ✅ | | | Union narrowing in `new-po-form.tsx` | ✅ | | | Buffer type in `api/files/dev/route.ts` | ✅ | Cast to `Uint8Array` | | New PO fields in Prisma types | ⚠️ | Migration applied; Prisma client types stale — resolve by restarting dev server and running `pnpm db:generate` | --- ## Testing | Item | Status | Notes | |---|---|---| | Unit: state machine | ✅ | `tests/unit/po-state-machine.test.ts` — 21 tests | | Unit: permissions | ✅ | `tests/unit/permissions.test.ts` — 11 tests | | Unit: utility functions | ✅ | `tests/unit/utils.test.ts` — 17 tests (formatCurrency INR, formatDate, generatePoNumber, status labels/variants) | | Unit: Zod validation schemas | ✅ | `tests/unit/validations.test.ts` — 24 tests (createPoSchema, lineItemSchema, TC fields, defaults) | | Component: PoStatusBadge | ✅ | `tests/unit/po-status-badge.test.tsx` — 17 tests (all 10 statuses) | | Component: LineItemsEditor | ✅ | `tests/unit/po-line-items-editor.test.tsx` — 20 tests (add/remove rows, GST calc, totals, read-only, diff mode) | | Integration: PO creation (S-01, S-02, S-03) | ✅ | `tests/integration/create-po.test.ts` — 9 tests; uses real DB + mocked auth/notifier | | Integration: Approval actions (M-02, M-03, M-04, S-06, S-07) | ✅ | `tests/integration/approval-actions.test.ts` — 14 tests | | Integration: Payment actions (A-01, A-02) | ✅ | `tests/integration/payment-actions.test.ts` — 8 tests | | E2E: authentication flow | ✅ | `tests/e2e/auth.spec.ts` — 6 tests | | E2E: submitter journey (S-01 to S-08) | ✅ | `tests/e2e/submitter-journey.spec.ts` — 8 tests | | E2E: manager approvals (M-01 to M-04) | ✅ | `tests/e2e/manager-approvals.spec.ts` — 9 tests | | E2E: accounts payment (A-01, A-02) | ✅ | `tests/e2e/accounts-payment.spec.ts` — 6 tests | | E2E: PO export (PDF + XLSX) | ✅ | `tests/e2e/po-export.spec.ts` — 8 tests (buttons, endpoints, content, auth) | | Accessibility (axe-core + Playwright) | ❌ | Not yet written | --- ## Summary | Category | Total items tracked | ✅ Done | ⚠️ Partial | ❌ Pending | |---|---|---|---|---| | Infrastructure | 8 | 7 | 0 | 1 | | Auth & Permissions | 5 | 4 | 0 | 1 | | State Machine | 5 | 5 | 0 | 0 | | Email Notifications | 7 | 7 | 0 | 0 | | File Storage | 6 | 6 | 0 | 0 | | Pages & Actions | 83 | 83 | 0 | 0 | | Type Safety | 5 | 4 | 1 | 0 | | Testing | 15 | 14 | 0 | 1 | | **Total** | **134** | **130 (97%)** | **1 (1%)** | **3 (2%)** | --- ## Remaining Items 1. **Prisma client regeneration** — restart dev server, then `pnpm db:generate` (DLL was locked during the last generate; migration is fully applied, no data loss) 2. **CI/CD** — GitHub Actions workflow: lint, type-check, test, build on PR; deploy on merge 3. **Accessibility tests** — axe-core + Playwright 4. **SSO / Azure AD** — credentials-only for v1; open question for v2 --- ## Not in Spec (deferred) - SSO / Azure AD login - Discount field on PO - Account Group / Account Code hierarchy (codes currently flat) - Cost centre vs vessel distinction (currently same entity) - Vendor deactivation cascade — warn if vendor has open POs - Approval queue — show VENDOR_ID_PENDING alongside MGR_REVIEW - Vendor required before approval can be granted (currently advisory only)