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>
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>
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>
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>
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>
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>