Commit graph

131 commits

Author SHA1 Message Date
4cb927cbd0 feat(companies): add invoiceEmail field separate from contact email
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 01:32:17 +05:30
e308d86e93 feat: Companies — multi-company PO support with admin CRUD and export integration
Schema:
- New Company model (name, gstNumber, address, telephone, mobile, email, invoiceAddress, isActive)
- PurchaseOrder.companyId FK (optional, SET NULL on company delete)
- Migration: 20260530000003_add_company

Admin:
- /admin/companies page with full CRUD (create, edit, deactivate, delete)
- Companies table shows name, GST, contact details, status
- Companies link added to Admin section of sidebar (Briefcase icon)

PO forms (new / edit / import / manager-edit):
- Company dropdown appears at the top of Order Information when companies exist
- Pre-populated with first active company; selection persisted to DB via companyId

Import form:
- parseSheet() now extracts companyName from Excel row 1 (col A)
- Import preview auto-matches detected company name against known companies
- Shows detected name as a hint; user can override before saving

Export (PDF + XLSX):
- Company constants (CO_NAME, CO_ADDR, CO_TEL, INV_ADDR, INV_GST) are now
  derived from the linked Company record when present, falling back to the
  original Pelagia Marine hardcoded defaults when no company is set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 19:31:34 +05:30
b43d44b59a fix(seed+tests): correct vessel/cost-centre names, update unit tests
seed-prod.ts:
- Correct vessel list: Head Office, PMS Kochi, CSD H&R 1/3/4, CSD Champion,
  CSD Hanuman, Kavaratti, Laccadives, Thinnakara, Thillaakam, GD3000
- Add System Admin user (admin@pelagia.local / admin1234) via bcrypt

Unit tests:
- po-import-parser: assert on line item .name rather than .description
- po-line-items-editor: fix placeholder text assertions, add .name to
  LineItemInput fixtures, add two new GST 0% calculation tests
- validations: add .name to line item fixtures; update createPoSchema
  assertions to reference costCentreRef; mark description as optional

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:50:23 +05:30
280966a369 refactor: revert cost centre to vessels only, remove vessel-site link
Cost Centre on PO forms now shows only Vessels (plain vesselId field).
Sites are a separate concept and not selectable as cost centres.

- PurchaseOrder.vesselId is required again (NOT NULL restored)
- Vessel.siteId and vessel->site relation removed from schema
- DB migration: drops Vessel.siteId column, restores PO.vesselId NOT NULL
- All PO forms (new/edit/import/manager-edit): plain vessel <select> with
  code-prefixed labels (e.g. "HNR1 — HNR 1")
- History, approvals, dashboard, my-orders, payments: back to vesselId
  filter params and po.vessel.name display
- Admin vessels: removed Site column and site-assignment dropdown
- Admin sites detail page: removed "Assigned Vessels" section
- Sites table: removed Vessels count column (no longer linked)
- seed-prod.ts and seed.ts: vessels created without siteId
- SearchableSelect accounting code picker retained from previous commit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 18:14:24 +05:30
565f9d5833 feat: searchable accounting code picker + cost centres grouped by site
Accounting Code search (new/edit/import/manager-edit PO forms):
- New SearchableSelect component (components/ui/searchable-select.tsx):
  type-to-filter by code or name, results grouped by sub-category with
  sticky headers, highlighted selected item, clear button, Escape/outside-click
  to dismiss
- Replaces the plain <select> for the main Accounting Code field on all PO forms
- LineItemsEditor per-row account column also uses SearchableSelect (compact size)
  when multi-account mode is active

Cost Centre dropdown reorganised by site:
- New type CostCentreGroup replaces flat CostCentreOption
- Each site becomes an <optgroup> label (unselectable); the site itself is the
  first selectable option inside ("Haldia (Site)"), followed by its vessels
- Vessels with no site assigned appear under an "Unassigned Vessels" group
- Shared helpers buildCostCentreGroups() and buildAccountGroups() in
  lib/cost-centre-groups.ts — used by all four PO form pages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 17:54:43 +05:30
0e3a79ecd4 feat: production seed script for Pelagia Marine (pnpm db:seed:prod)
Idempotent seed with real company data:
- 14 users (SSO/no-password): Accounts, Managers, Technical, Manning
- 7 sites: Head Office (HOFC), PMS Kochi, Laccadives, Haldia,
  Thilakkam, Kavaratti, Thinnakara
- 10 vessels assigned to their respective sites:
  HNR1-4, Champion, Hanunam, Sejal, Sejal 2, GD 3000, Thilakkam
- Full accounting code hierarchy (Rev. 01/251227) — 300+ codes across
  7 top categories via two-pass upsert to wire parent links

Run with: pnpm db:seed:prod

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 04:26:38 +05:30
a1b77d8b00 feat(vessels): editable custom code when creating a vessel
- Add Vessel dialog now shows an editable Code field pre-filled with the
  next auto-generated code (e.g. SITE-004) — user can change it freely
- Edit Vessel dialog keeps the code read-only (changing codes on existing
  data would break PO references)
- createVessel action: uses submitted code if provided, auto-generates if
  left blank, and validates uniqueness before saving

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 04:14:03 +05:30
0d17672ea9 feat(accounts): hierarchical accounting codes with 6-digit format and category tree
- Account model gains parentId (self-referential, 3 levels: TopCategory → SubCategory → Item)
- DB migration: adds parentId FK column to Account table
- Code format changed from PREFIX-NNN to 6-digit numeric (e.g. 100101)
- Seeded all 300+ accounting codes from the official chart (Rev. 01/251227) across
  7 top categories: Capital Expenses, Business Development, Office Admin, Project
  Expenses, Manning, Technical, Bunker/Lubes
- Admin Accounting Code page: collapsible tree view (top category > sub-category > items),
  inline search, Add/Edit dialogs with parent selector and 6-digit code field
- All PO forms (new, edit, import, manager-edit): accounting code dropdown now shows
  only leaf items grouped in <optgroup> by sub-category, labelled "TopCat › SubCat"
- Seed data updated: old flat account codes replaced by mapped leaf codes from new hierarchy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 03:27:31 +05:30
cc7251e6b7 feat: Cost Centre covers vessels and sites, vessel codes, Accounting Code rename, vessel-site assignment
- Undo Vessel→Cost Centre rename in admin (admin shows "Vessel Management" again)
- Sidebar: "Cost Centres"→"Vessels", "Accounts"→"Accounting Codes"
- PO forms (new/edit/import/manager-edit) now show both Vessels (with code) and Sites in the
  Cost Centre dropdown, encoded as v:<id> / s:<id> via a costCentreRef field
- vesselId on PurchaseOrder is now nullable; siteId is set when a site is the cost centre
- History, approvals, dashboard, my-orders, payments display vessel.name ?? site.name as Cost Centre
- History and approvals cost centre filters use costCentreRef URL param supporting both types
- Admin vessel form: adds Site assignment dropdown
- Admin accounts: renamed to "Accounting Code" throughout (pages, forms, sidebar)
- PO detail and exports: "Account" label renamed to "Accounting Code"
- Site detail: "Assigned Vessels (Cost Centres)" heading; vessel detail breadcrumb fixed
- Create PO links from vessel/site detail use ?costCentreRef= param
- Export routes handle costCentreRef filter param (with legacy vesselId fallback)
- DB migration: ALTER TABLE PurchaseOrder ALTER COLUMN vesselId DROP NOT NULL
- CLAUDE.md updated with Cost Centre Model documentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 03:04:29 +05:30
3f3e1e6423 feat(admin): confirm activate/deactivate via modal popup across all tables
Replace immediate server action calls with ConfirmDialog modals for
activate/deactivate on all 6 admin tables (users, vendors, vessels,
sites, accounts, products). Delete already used DeleteConfirmDialog;
this adds the same pattern for reversible toggle actions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 03:07:04 +05:30
d27ec9152c feat(admin): collapse row actions into ⋯ dropdown menu
Replace per-row inline action buttons (Edit, Activate/Deactivate, Delete,
Grant SuperUser) across all six admin tables with a Radix DropdownMenu
triggered by a ⋯ button. Introduces RowActionsMenu/Item/DestructiveItem/
Separator primitives and a DeleteConfirmDialog modal. Each Edit*Button
gains controlled open/onOpenChange props so the dialog can be driven from
the table's per-row ActionsMenu sub-component. Toggle and delete actions
use useTransition + router.refresh() directly in the table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 02:58:54 +05:30
9758dcd8ab feat(admin): add client-side search, sort, and filter chips to all admin tables
Adds a reusable useTableControls hook and TableControls/SortableTh
components, then wires them into all six admin table pages (users,
vendors, vessels, sites, accounts, products). Each page now supports
a global search bar, clickable sortable column headers with ↑/↓/⇅
indicators, and role/status filter chips — all purely client-side with
no URL params or server round-trips. Server pages continue to fetch the
full list and pass it as props to a new *-table.tsx Client Component.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 02:46:52 +05:30
bff9696b7b fix(profile): allow empty current password when setting password for first time
SSO users have no passwordHash and should be able to set a local password
without providing a current one. Users with an existing password still
must verify it. Removes the client-side required attribute and updates
the server-side logic accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 00:14:46 +05:30
a16f418e71 fix(admin): guard user deletion against all FK constraints
The delete action was only checking for submitted POs, leaving POAction
(actorId) and ItemConsumption (recordedById) to throw FK constraint
errors at the DB level. Now returns a clear error for each case and
also cleans up SuperUserRequest rows (requester + resolver) inside the
transaction before deleting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 23:20:18 +05:30
bef401aa76 fix(auth): display OAuth errors on the login page
Maps NextAuth error codes (OAuthCallbackError, AccessDenied, OAuthSignin)
from the ?error= query param to user-friendly messages shown above the
Microsoft sign-in button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 23:15:27 +05:30
352b14cec7 fix(build): run prisma generate before next build
Ensures the Prisma client types are always regenerated from the current
schema before compilation. Without this, schema changes (e.g. making
passwordHash nullable) cause TypeScript errors on the build server if
prisma generate hasn't been run manually after pulling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 22:55:44 +05:30
56817a7d86 feat(auth): add Microsoft 365 SSO via Azure Entra ID
Adds the Microsoft Entra ID provider to NextAuth alongside the existing
credentials provider. Sign-in is restricted to Pelagia Marine's M365
tenant via the issuer URL; access is further gated by requiring a
matching active user record in the DB (DB-managed roles remain unchanged).

- auth.ts: add MicrosoftEntra provider, signIn callback (DB lookup),
  async jwt callback to populate id/role on first SSO sign-in
- login-form.tsx: add primary "Sign in with Microsoft 365" button with
  Microsoft logo; credentials form kept as a fallback below a divider
- prisma: make passwordHash nullable (migration applied) to allow
  SSO-only users without a local password
- admin/users: password is now optional when creating a user — leave
  blank for SSO-only accounts
- profile/actions: return a clear error if an SSO user (no passwordHash)
  attempts to use the change-password form
- .env.example: document AZURE_AD_CLIENT_ID/SECRET/TENANT_ID

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 22:48:37 +05:30
6d8f376949 chore(progress): mark ID autogeneration as done
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 15:23:35 +05:30
b31fe7374c fix(ui): remove PMS taglines from login and mobile pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 15:23:23 +05:30
a2c35d0a93 feat(admin): auto-generate structured IDs for users, vendors, accounts and cost centres
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>
2026-05-27 15:02:50 +05:30
49ba6e8be5 fix(ui): use defined theme colors for danger/warning buttons
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>
2026-05-27 13:11:10 +05:30
3e5d11c4ae feat(po): show note author name on manager note banners
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>
2026-05-27 04:41:53 +05:30
c92f136b09 fix(ui): reset pending state on success for all save/confirm buttons
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>
2026-05-27 04:36:29 +05:30
5ad85417d9 fix(payments): only log PRODUCT_PRICE_UPDATED when price actually changed
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>
2026-05-27 04:28:40 +05:30
d4007ee222 feat(my-orders): rename to Closed Purchase Orders, show only closed POs
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>
2026-05-27 04:21:53 +05:30
cf9ff40262 feat(payments): partial/advance payment support
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>
2026-05-27 04:17:19 +05:30
7169d52885 fix(notifications): rationalise who gets which email
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>
2026-05-27 03:45:40 +05:30
d82d18bac2 feat(email): rich PO details, action buttons, Purchase Order in subject
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>
2026-05-27 03:39:49 +05:30
a8e3b5d69b fix(diff): show GST rate strikethrough when submitter changes it on edit
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>
2026-05-27 03:34:54 +05:30
e0c97cbf5d chore(progress): log bug notes from 26/05 session
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 03:32:47 +05:30
987b0aedfa fix(mobile): prevent notification dropdown overflow on small screens
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>
2026-05-27 03:32:37 +05:30
91f369da9e fix(my-orders): surface PARTIALLY_CLOSED POs in Open Orders
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>
2026-05-27 03:32:26 +05:30
1c5727850a fix(gst): 0% GST rate no longer falls back to 18%
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>
2026-05-27 00:00:51 +05:30
e61f052062 fix(static): prevent mobile layout overflow 2026-05-22 18:11:31 +05:30
04f4dcd210 Merge branch 'master' of ssh://87.76.191.133/home/shad0w/git/Peliagia_Portal 2026-05-22 17:41:14 +05:30
d54df54f12 feat: add static Pelagia Marine site 2026-05-22 17:39:27 +05:30
4d3fd32f13 fix: preserve tracked request typing 2026-05-22 17:39:26 +05:30
43e0696143 chore: update Claude local permissions 2026-05-22 17:39:26 +05:30
165ccef757 chore: ignore local AI tool artifacts 2026-05-22 17:38:54 +05:30
5f874cf2a2 pnpm workspace allow build scripts 2026-05-22 17:17:15 +05:30
efca4687ca Updated gitignore 2026-05-22 17:16:40 +05:30
c186ea0862 docs: consolidate design notes and test report 2026-05-22 17:15:38 +05:30
934979750f test(e2e): harden PO form selectors 2026-05-22 17:15:17 +05:30
32ea27331c fix(po): tighten filters and export data 2026-05-22 17:15:03 +05:30
2c39f0225f feat(vessels): remove IMO number tracking 2026-05-22 17:14:40 +05:30
1ea22df2f7 fix(auth): stabilize login page rendering 2026-05-22 17:14:21 +05:30
7f5ebd2f15 feat: add static Pelagia Marine site 2026-05-22 16:54:49 +05:30
81869d4f61 docs: add portal follow-up tasks 2026-05-22 16:26:43 +05:30
ec63ac781f fix: preserve tracked request typing 2026-05-22 16:26:30 +05:30
58f366d62d chore: update Claude local permissions 2026-05-22 16:26:13 +05:30