Users: employeeId auto-generated from role prefix (TCH/MAN/ACC/MGR/SUP/AUD/ADM)
followed by next sequential number; shown read-only in edit form, removed
from create form. Cost Centres: new code field (SITE-001 ...) added to
Vessel model with migration + backfill; auto-generated on create, read-only
in edit. Vendors and Accounts: code/vendorId inputs pre-filled with the
next suggested ID (VND-001, ACC-001) from the server page; user can override
with any PREFIX-NUMBER format, validated by regex.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The theme only defines danger/warning/success as flat colors plus -50/-100/-700
shades. bg-danger-600 and bg-warning-600 don't exist so those buttons rendered
with a transparent background, making white text invisible until hover revealed
bg-danger-700. Replaced with bg-danger / bg-warning which are defined.
Also fixed border-danger-200/400 and text-danger-600 (undefined) on the
Delete and Confirm Delete buttons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Derives the author from the most recent EDITS_REQUESTED / REJECTED /
APPROVED action that carries a note. PO detail banner now shows 'Note from
[name]', edit-page banner shows 'Edits requested by [name]', and the
closed-orders list prefixes the truncated note with the author's name.
No schema changes required - uses the already-fetched actions with actor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Parent button components (EditVendorButton, EditAccountButton, etc.) stay
mounted even when their AdminDialog closes — so pending was never cleared
on success, causing buttons to show 'Saving...' on the next open. The
payment confirm button (no dialog) was stuck in 'Confirming...' until the
page re-render eventually unmounted it. Added setPending(false) before
setOpen/router.refresh in every success path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the action was logged unconditionally for every line item on
every payment confirmation, even when the price was identical to what was
already on the product. Now we compare unitPrice to the stored lastPrice
before deciding to record the change. New products being registered for
the first time are also excluded since that is not a price update.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Open POs are already visible on the dashboard. The /my-orders page now
fetches and displays only completed/closed statuses (MGR_APPROVED through
REJECTED) and is labelled 'Closed Purchase Orders' throughout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allow accounts to record partial/advance payments against a PO before
full delivery. A new PARTIALLY_PAID status tracks in-progress payment;
paidAmount accumulates across multiple markPaid calls. PO only closes
when both paidAmount >= totalAmount AND all line items are delivered.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PO_SUBMITTED: managers only, submitter no longer copied
PAYMENT_SENT: submitter only (it is a receipt prompt, not a manager action)
PARTIAL_RECEIPT_CONFIRMED: managers now notified via new event type
RECEIPT_CONFIRMED: unchanged (managers + accounts)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Subject line now reads 'Purchase Order XXXX' instead of 'PO XXXX'.
Each email includes a recipient-specific deep-link action button,
a PO summary card (number, title, submitter, vendor, vessel, cost centre, total),
and a line items table with qty/unit price/GST/line total.
notify() fetches enriched data internally so no call sites change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
qtyChanged/priceChanged/nameChanged were tracked for the diff view but
gstChanged was missing, so an edited GST % showed only the new value
with no strikethrough of the old one. Added gstChanged detection and
two-line rendering (old % struck through, new % in amber) to match the
other diff columns.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The dropdown was w-96 (384 px) anchored right-0 to the bell button.
On phones the bell sits left of the logout icon so the panel bled off
the left viewport edge. Changed to w-80/sm:w-96 with a 100vw safety cap.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PARTIALLY_CLOSED was missing from the open-filter so affected POs
disappeared from the submitter''s My Orders view entirely, making it
impossible to confirm remaining deliveries.
Also hardens confirmReceipt() against negative delivery quantities
and extends partial-receipt.spec.ts with US-8c/8d/8e covering the
full PARTIALLY_CLOSED revisit flow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parseFloat('0') is falsy in JS so `|| 0.18` silently discarded the user's
explicit 0% selection. Replaced with an explicit empty-string guard.
Adds e2e spec gst-rate.spec.ts covering all five GST rates (0/5/12/18/28%).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Increase the Prisma interactive transaction timeout from the default 5s
to 30s so that the four sequential nullification + delete queries complete
reliably on a seeded database (P2028 timeout was the root cause).
Wrap the transaction in a try/catch so that if a timeout does still occur
the user sees "Delete timed out — please try again." instead of an
unhandled 500 that previously manifested as the misleading "referenced in
submitted or active purchase orders" error message.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
test-report-2026-05-17.md — Run summary for the 2026-05-17 session:
64 passed, 3 flaky, 2 skipped, 61 failed (all in pre-existing specs
with legacy selectors). Includes per-spec results, root causes for
every failure category, and recommended fixes.
e2e-test-framework.md — Developer reference covering stack, directory
layout, playwright.config.ts rationale (workers: 2, why bcrypt
floods the server), shared helpers, selector conventions (PO form
has no htmlFor bindings), mobile viewport pattern, and future
improvements including auth state sharing.
e2e-test-plan.md — Feature coverage matrix mapping all 21 user story
groups to their spec files and roles, individual test case tables,
regression trigger checklist by code area, gap analysis, and
planned CI configuration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers 21 user story groups extracted from the last 15+ feature commits:
- rebrand.spec.ts — PPMS branding on login page, sidebar, and tab title
- dashboard/po-status-badges.js — color-coded status badges for submitter & manager
- po-submit-button.spec.ts — Submit for Approval button visibility on DRAFT POs
- notification-bell.spec.ts — in-app bell icon, unread badge, and panel open
- export-gate.spec.ts — export buttons gated on MGR_APPROVED+ status; 403 on pre-approval
- payment-history.spec.ts — /payments/history accessible to ACCOUNTS; redirects others
- partial-receipt.spec.ts — per-item delivery tracking UI on paid POs
- vendor-auto-verify.spec.ts — vendor verification status visible in admin
- admin-bordered-buttons.spec.ts — Edit/Deactivate/Delete have border classes on admin pages
- profile.spec.ts — profile page loads for all roles; signature section for MANAGER/SUPERUSER
- inventory/items-tags.spec.ts — Cheapest/Closest tags and auto-sort by distance
- inventory/cart-icon.spec.ts — cart header icon with badge; item/vendor detail pages
- mobile/desktop-required.spec.ts — Desktop Required overlay for non-mobile roles + sign-out
- mobile/manager-approvals.spec.ts — mobile card layout; edit form hidden; action buttons visible
- mobile/accounts-payments.spec.ts — ACCOUNTS payments queue and buttons on mobile
- mobile/bottom-nav.spec.ts — Home/Approvals/Profile tabs for MANAGER; Home/Payments/Profile for ACCOUNTS
- approvals-edit-highlight.spec.ts — diff indicators on resubmitted POs
Also adds shared helpers/login.ts with USERS constants, login(), createDraftPo(),
and submitPo() — all using name-attribute selectors since PO form labels have no
htmlFor binding. Playwright config updated: workers capped at 2 locally (was
unlimited) to prevent auth concurrency failures, and retries set to 1 locally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prepend a Home tab (linking to /dashboard) to the bottom navigation bar
for both Manager/SuperUser and Accounts roles, giving one-tap access to
the dashboard from any mobile screen.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Users on mobile who see the "Desktop Required" wall had no way to log out.
Added a Sign out button using next-auth signOut (requires "use client").
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ACCOUNTS to MOBILE_ROLES so the desktop-required wall no longer
blocks them on small screens.
- MobileBottomNav is now role-aware: ACCOUNTS gets Payments + Profile tabs;
MANAGER/SUPERUSER keep Approvals + Profile. Role prop threaded from
layout → MobileBottomNav.
- PaymentActions (both MGR_APPROVED and SENT_FOR_PAYMENT states) stacks
vertically on small screens — input takes full width, button below it —
then reverts to the horizontal inline layout at sm+ breakpoint.
- Payments page card bottom row (status badge + action) stacks on mobile
(flex-col sm:flex-row) so the reference input isn't squashed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a responsive mobile experience scoped to MANAGER and SUPERUSER roles:
Layout:
- Sidebar hidden on small screens (md: breakpoint)
- New MobileHeader (logo + notification bell + sign out) visible on mobile
- New MobileBottomNav (Approvals + Profile tabs) pinned to bottom on mobile
- New DesktopRequired overlay shown to all other roles on small screens —
a fixed full-screen message directing them to use a desktop browser
Approvals queue:
- Desktop: existing table layout (hidden md:block)
- Mobile: tap-to-review card stack (md:hidden) — shows PO number, title,
submitter, cost centre, amount, and a full-width Review button
PO approval detail:
- ManagerEditPoForm (direct field editing) hidden on mobile; still available
on desktop (direct edits not required per spec)
- ApprovalActions buttons stack full-width on mobile, row on sm+
- Paddings reduced on small screens throughout
PO detail component:
- Order Details and Vendor grids switch from grid-cols-2 → grid-cols-1
on small screens (sm:grid-cols-2 restores two columns at 640px+)
- Section padding reduced on mobile (p-3 md:p-6, p-4 md:p-6)
- Line items table already had overflow-x-auto — no change needed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Edit, Deactivate/Activate, and Delete actions in all admin table rows
were styled as plain text links (coloured text, no border or background).
Replaced with small pill-shaped bordered buttons that have a clear visual
affordance as interactive controls:
- Edit → blue tinted border/bg (primary-50 / primary-200)
- Deactivate → red tinted border/bg (danger-50 / danger-200)
- Activate → green tinted border/bg (success-50 / success-200)
- Delete → white bg, red border; confirm state = solid red
- Cancel → white bg, neutral border
Applies to: accounts, cost centres (vessels), users, vendors, products,
and the shared ConfirmDeleteButton component.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When accounts confirms a payment (SENT_FOR_PAYMENT → PAID_DELIVERED),
set Vendor.isVerified = true for the PO's vendor. The field already
exists in the schema (default false); this closes the loop so vendors
who have transacted at least once are marked verified automatically
without manual admin intervention.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rename the application to PPMS across all user-facing surfaces:
- Browser title / metadata
- Login page (with tagline: "PMS — it runs the ship.")
- Sidebar logo text
- Email header, footer, and body copy in all transactional emails
- PDF/XLSX workbook creator field
- Reports export page title and heading
- Notifier FROM display name and default email domain
Legal company name ("Pelagia Marine Services Pvt. Ltd.") on PO documents
is unchanged — that is the issuing entity, not the application brand.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Export API returns 403 for any PO not yet approved (DRAFT, SUBMITTED,
MGR_REVIEW, EDITS_REQUESTED, VENDOR_ID_PENDING, REJECTED) — only
MGR_APPROVED, SENT_FOR_PAYMENT, PAID_DELIVERED, PARTIALLY_CLOSED and
CLOSED are exportable.
- The submitter's name is no longer used as a signatory fallback; since
export is blocked until after manager approval an approver always exists.
- Export PDF / Export XLSX buttons are hidden in po-detail for pre-approval
statuses, so users never encounter the 403 through normal UI flows.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Logging (GstService):
- JSON-structured log lines: { ts, level, msg, ...ctx } — one per line,
machine-parseable by any log aggregator (datadog, loki, etc.)
- LOG_LEVEL env var (DEBUG|INFO|WARN|ERROR, default INFO) — set DEBUG to
see every captcha fetch, raw GST response body, and page console event
- WARN and ERROR lines go to stderr; INFO/DEBUG go to stdout so process
supervisors can separate them
- Every log line carries relevant context: reqId, sessionId, gstin, ms, etc.
- errCtx() helper extracts errName, errMsg, and first 6 stack frames from
any thrown value — no more bare String(e)
- elapsed() helper records wall-clock ms for every expensive step:
browser launch, page navigation, captcha fetch, GST API call
- Request/response middleware: every HTTP request logs method, path,
reqId, status, and duration; status >= 500 logs at ERROR, >= 400 at WARN
- Playwright page listeners: console errors/warnings, pageerror,
requestfailed, and HTTP 4xx/5xx on GST portal endpoints
- process.on(uncaughtException) and process.on(unhandledRejection) so
unexpected crashes surface in logs instead of silently dying
- Browser "disconnected" event logged; _browser reset so next request
auto-relaunches without manual restart
- SESSION_TTL_MS configurable via env (default 3 min)
- closeSession() logs the reason (success / errorCode / exception / etc.)
- GET /health now returns browserConnected, per-session captchaCount,
expiresInMs, and lastUsedMsAgo for operational visibility
Multiple captchas per session:
- Session now holds captchas: CaptchaEntry[] (ordered oldest→newest)
so every image fetched in a session is kept for traceability
- GET /captcha/:sessionId — new endpoint that calls /services/captcha
again within the SAME browser context (no page reload, ~200ms vs ~5s)
and appends a new CaptchaEntry; resets TTL; returns totalCaptchas
- POST /search on SWEB_9034 (wrong captcha) no longer closes the session —
returns { canRefresh: true, sessionId } so the caller can hit
GET /captcha/:sessionId for a fresh image and retry immediately
- All other error paths (SWEB_9000, network error, no data) still close
the session as before
Next.js proxy (app/api/gst/captcha/route.ts):
- GET /api/gst/captcha?refresh=<sessionId> proxies to the new
GET /captcha/:sessionId endpoint on GstService
- Plain GET /api/gst/captcha still creates a new session as before
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Notification links now route each recipient to the page where they
need to take action, not just the PO detail:
Manager / SuperUser:
PO_SUBMITTED, VENDOR_ID_PROVIDED → /approvals/[id] (approval queue)
Accounts:
PO_APPROVED / PO_APPROVED_WITH_NOTE → /payments (payment queue)
Submitter:
EDITS_REQUESTED → /po/[id]/edit (open the edit form directly)
PAYMENT_SENT → /po/[id]/receipt (open the receipt confirmation)
All other events / recipients → /po/[id]
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A draft PO had no way to be submitted without creating a brand new PO.
Two surfaces are now fixed:
Edit page (/po/[id]/edit):
- updatePo action now accepts intent "submit" (alongside "save" and
"resubmit"), which transitions a DRAFT PO to MGR_REVIEW, creates a
SUBMITTED action, and fires notifications — identical behaviour to
the new-PO submit flow
- EditPoForm shows a primary "Submit for Approval" button when the PO
is in DRAFT status; "Resubmit for Approval" remains for EDITS_REQUESTED
PO detail page (/po/[id]):
- New submitDraftPo server action in po/[id]/actions.ts handles the
same DRAFT → MGR_REVIEW transition without requiring the full edit form
- New SubmitDraftButton client component renders next to the Edit link
in the detail header for DRAFT POs owned by the current user
- Button order: Edit · Submit for Approval · Discard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In-app notification bodies now lead directly with the PO number
(e.g. "PO-2024-12345 approved" → "PO-2024-12345 approved") without the
word "PO" repeated before the identifier, since the ID already makes
the context obvious.
All notification links now route to /po/[id] so clicking any
notification takes the user straight to the relevant PO detail page,
regardless of their role or the event type.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>