Commit graph

367 commits

Author SHA1 Message Date
90a831790d Merge pull request 'fix(automation): issue watcher must not open PRs without tests' (#138) from fix/watcher-test-gate into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #138
2026-06-25 22:24:54 +00:00
a5248e5c23 fix(automation): issue watcher must not open PRs without tests
All checks were successful
PR checks / checks (pull_request) Successful in 48s
PR checks / integration (pull_request) Successful in 30s
PR #137 shipped a code change under App/app with no test — the test-presence
gate in pr-checks.yml would reject it, but the watcher opened the PR anyway.

Add the same gate to the watcher's fix phase: before pushing/opening a PR, run
the pr-checks.yml test-presence check against the branch diff. If code under
App/(app|lib|components|hooks) changed with no accompanying test, the watcher
does NOT open a PR — it marks the issue claude-failed and comments, so the
queue can retry. Never raises a PR the CI would immediately fail.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 03:47:59 +05:30
6c5aebd3fb Merge pull request 'chore(staging): enable submitter-view-all PO history flag on staging' (#135) from claude/determined-borg-f034f5 into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #135
2026-06-25 21:42:03 +00:00
e4f7f6623f chore(staging): enable submitter-view-all PO history flag on staging
All checks were successful
PR checks / checks (pull_request) Successful in 48s
PR checks / integration (pull_request) Successful in 31s
Add NEXT_PUBLIC_SUBMITTER_VIEW_ALL_ENABLED=true to the staging env so
submitters (TECHNICAL/MANNING) can read all POs and open the History page
on the staging instance for testing ahead of a prod rollout. The flag is
written into the freshly-provisioned .env heredoc and also appended
idempotently to already-provisioned staging .envs on the next refresh.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 03:10:19 +05:30
7980d68a7a Merge pull request 'fix: New PO Project Code Selection' (#126) from claude/issue-124 into master
All checks were successful
Refresh staging / refresh (push) Successful in 8s
Reviewed-on: #126
2026-06-25 21:23:28 +00:00
87fbeecf52 Merge branch 'master' into claude/issue-124
All checks were successful
PR checks / checks (pull_request) Successful in 49s
PR checks / integration (pull_request) Successful in 30s
2026-06-25 21:23:01 +00:00
02c0806d35 refactor(po): admin-managed Project Codes instead of a static list
All checks were successful
PR checks / checks (pull_request) Successful in 51s
PR checks / integration (pull_request) Successful in 32s
Replaces the hardcoded PROJECT_CODES array with an admin-managed
`ProjectCode` model, mirroring the Delivery Locations pattern (PR #100):

- ProjectCode model (unique `code` + isActive) + migration seeding the
  five previously-hardcoded codes; PO.projectCode stays a free-text
  snapshot (no FK) so history/exports/imports are unchanged.
- manage_project_codes permission (Manager + SuperUser + Admin).
- /admin/project-codes CRUD screen (table + Add/Edit + activate/delete)
  and an Administration sidebar link.
- ProjectCodeField now takes `options` from the active codes; the three
  PO forms + pages fetch them from the DB. Static list removed.
- Unit test reworked to the options API; CRUD integration test added;
  documented in App/CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 02:52:03 +05:30
86ccc3e29b Merge pull request 'fix: PO History Search Options' (#123) from claude/issue-121 into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #123
2026-06-25 20:59:34 +00:00
6cfcd20c45 Merge remote-tracking branch 'origin/master' into work-issue-121
All checks were successful
PR checks / checks (pull_request) Successful in 50s
PR checks / integration (pull_request) Successful in 30s
# Conflicts:
#	CHANGELOG.md
2026-06-26 01:07:16 +05:30
Claude (review-bot)
d0e43135f8 feat(reports): drill from cost centre / accounting code into PO History (#126)
All checks were successful
PR checks / checks (pull_request) Successful in 49s
PR checks / integration (pull_request) Successful in 31s
Report detail pages now link to the underlying POs, addressing the PR #126
review comment: drilling into a cost centre or accounting code opens PO
History pre-filtered to that dimension and the period in view.

- Cost Centre / Accounting Code detail pages gain a "View POs" link.
- periodRange() maps the on-screen period onto History's approved-date
  window (weekly→month, monthly→FY, yearly→full span); spend is dated by
  approvedAt.
- PO History gains an accountId filter (any tree node, expanded to leaves
  via accountLeafIds()) matching PO-level OR line-item accounts — the same
  basis the reports use.
- History page + CSV/PDF export share one buildPoHistoryWhere() builder so
  they never diverge.
- Tests: unit (periodRange, accountLeafIds) + integration (History account
  filter across PO-level/line-item, with the approved window).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 01:03:17 +05:30
fda87e5b13 Merge pull request 'Multiple fixes for code review automation pipeline' (#134) from feat/pr-review-watcher-followups into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #134
2026-06-25 19:25:03 +00:00
0814b040e1 fix(automation): bound + detach the Claude run so a hang can't wedge or stick
All checks were successful
PR checks / checks (pull_request) Successful in 49s
PR checks / integration (pull_request) Successful in 30s
First live run on PR #126 left the foreground shell stuck: Claude pushed the
commit itself and then did not exit, so the supervisor code after it (push +
ack + handled marker) never ran. Under cron that also holds the flock lock,
freezing every later run, and the comment never gets marked handled (so it
would be re-processed forever).

- Wrap the Claude invocation in `setsid timeout -k 30s "$CLAUDE_TIMEOUT"`
  (default 30m). `setsid` detaches from the controlling terminal so a lingering
  child can't stick an interactive run; `timeout` returns control to the
  supervisor, which still pushes any commits (idempotent if Claude pushed) and
  writes the handled marker. A timed-out run (rc=124) is logged, not fatal.
- Give this watcher its own dev port (devPort, default 3101) distinct from the
  issue watcher's 3100, and reap it after each run -- no cross-watcher kill.
- Reinforce the prompt: stop any dev server and END THE TURN; never push.

Adds claudeTimeout + devPort to the example config and documents both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 16:27:22 +05:30
6e5b42932b feat(automation): one-command updater for the deployed PR-review watcher
update-pr-review-watcher.sh refreshes the deployed script from the repo via a
dedicated self-update checkout (~/pr-review-watcher/.src) that never races the
issue watcher's work clone. Reads the live config for auth, never clobbers the
config (real token), and self-updates. Optional ref arg for pre-merge testing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 16:01:28 +05:30
fa0e004691 Merge pull request 'fix(automation): scan all Claude PRs for review comments; drop author-based bot filter' (#131) from claude/busy-boyd-b16092 into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #131
2026-06-24 10:28:16 +00:00
2fa382185f Merge pull request 'docs: changelog + PdfService README (reports, email-to-vendor, microservices)' (#130) from docs/changelog-microservices into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #130
2026-06-24 10:27:26 +00:00
8b56c79f5f docs: changelog + PdfService README for reports, email-to-vendor, microservices
All checks were successful
PR checks / checks (pull_request) Successful in 48s
PR checks / integration (pull_request) Successful in 31s
The repo CHANGELOG had fallen behind. Adds [Unreleased] entries for the recently
shipped work and a README for the previously undocumented PdfService.

Added (changelog):
- Reports — Purchasing spend analytics (cost centres + accounting codes).
- Email PO to vendor + the PdfService microservice (cached per PO).
- EpfoService + PdfService microservices and their release auto-deploy.
- Unsaved-changes prompt on PO create/edit (#18).
- Crew login-on-hire for management ranks.
- Delivery Locations (#19), T&C catalogue (#11), advance payment (#92).

Fixed (changelog):
- "Email to vendor" never rendered (auth middleware bounced the svc fetch) — #127.
- Reports charts all one colour (RSC client/server boundary) — #120.

New: PdfService/README.md (endpoints, token/origin security, env, app integration).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:53:05 +05:30
e1907e6aec fix(automation): scan all Claude PRs for review comments; drop author-based bot filter
All checks were successful
PR checks / checks (pull_request) Successful in 49s
PR checks / integration (pull_request) Successful in 32s
Two issues surfaced on first live run:

- The per-run cap truncated the PR list to the lowest-numbered PR, so a
  comment on a higher-numbered PR was never scanned. The cap now limits only
  how many PRs Claude RUNS on; comment-less PRs are skipped for free, so newer
  PRs are never crowded out.
- The bot posts as the repo owner's account, so excluding "the bot's own
  comments" by author also excluded the owner's legitimate review comments
  (and required a read:user token scope, which 403'd). Replaced with a guard
  that excludes only comments carrying the HANDLED_TAG marker -- robust even
  when the bot and the reviewer are the same account. The /user call is gone.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 15:48:48 +05:30
70152b0a5e Merge pull request 'feat(automation): watcher that addresses claude-review: comments on Claude PRs' (#129) from claude/busy-boyd-b16092 into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #129
2026-06-24 10:08:01 +00:00
7acd86e3dd feat(automation): watcher that addresses claude-review: comments on Claude PRs
All checks were successful
PR checks / checks (pull_request) Successful in 47s
PR checks / integration (pull_request) Successful in 32s
Sibling to claude-issue-watcher.sh: polls open Claude-raised PRs (head
branch under claude/, or labelled claude-pr) for review comments carrying
the marker `claude-review:` — in the PR conversation, review summaries, or
inline on-file comments — and runs headless Claude Code on the PR's own
branch to address them, pushing the follow-up commit(s) to the same branch.

- Authorization gate: only repo collaborators (write access) + the owner can
  trigger it; the bot's own comments are ignored.
- Idempotent: handled comments are tracked by a hidden marker on the bot's
  acknowledgements, so the 10-min poll never redoes a comment.
- Own clone (~/pelagia-pr-review), config, and lock so it never races the
  issue watcher. Token needs write:repository + write:issue.

Adds the script, an example config, .gitignore entries for the live
config/lock, and an automation/README.md section with deploy + cron steps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 15:21:04 +05:30
262ae5830b Merge pull request 'feat(pdf): cache the PO PDF per vendor email (reuse copy, refresh 7-day link)' (#128) from fix/po-pdf-cache into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #128
2026-06-24 09:47:58 +00:00
a9fd927c1f feat(pdf): cache the PO PDF per vendor email, refresh only the link timer
All checks were successful
PR checks / checks (pull_request) Successful in 47s
PR checks / integration (pull_request) Successful in 31s
Previously every "Email to vendor" click re-rendered the PO via PdfService and
re-uploaded to R2 under a timestamped key — wasteful, and it orphaned a new
object each time.

Now the PDF is stored at a deterministic per-PO key (buildPoPdfKey →
po-pdf/<poId>/<slug>.pdf). On each send, statObject() checks for an existing
copy: if it exists and is at least as new as the PO's updatedAt, it's reused
(no re-render, no re-upload) and only a fresh presigned URL is minted —
refreshing the 7-day download timer. It re-renders only when there's no copy
yet or the PO changed since the cached one (so an edited PO never emails a
stale PDF).

- lib/storage.ts: buildPoPdfKey (deterministic) + statObject (HEAD/stat, no
  body transfer; null when absent).
- email-actions.ts: reuse-or-render decision keyed on updatedAt; always
  re-presign.
- Tests: +2 (reuse-on-second-send-only-refreshes-link, re-render-when-changed).
  email-vendor suite 8 green; tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:01:25 +05:30
7a4c1c7f62 Merge pull request 'fix(pdf): let PdfService reach the export route past auth middleware' (#127) from fix/pdf-export-middleware into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Deploy release to production / deploy (push) Successful in 1m30s
Reviewed-on: #127
2026-06-24 09:27:35 +00:00
d1af1e6b12 fix(pdf): let PdfService reach the PO export route past auth middleware
All checks were successful
PR checks / checks (pull_request) Successful in 49s
PR checks / integration (pull_request) Successful in 31s
"Email PO to vendor" (issue #14) relies on PdfService fetching
/api/po/<id>/export?...&svc=<token> WITHOUT a user session, authenticating
with a `svc` token that matches PDF_SERVICE_TOKEN. The route handler validates
that token, but the auth middleware runs first and its matcher doesn't exempt
the export route — so every unauthenticated fetch was redirected to /login
(307) and the svc bypass never executed. Net effect: the feature could never
render a real PDF on any deployed env, even with the service configured.

Fix: middleware now lets exactly `/api/po/<id>/export` through when its `svc`
query param matches `process.env.PDF_SERVICE_TOKEN` (the route handler still
re-validates it — defense in depth). Everything else stays auth-gated. The
match lives in a dependency-free, edge-safe, unit-tested helper
(lib/pdf-export-auth.ts); middleware already reads server env at runtime via
auth()/NEXTAUTH_SECRET, so reading PDF_SERVICE_TOKEN there is consistent.

Verified on a running build: correct svc + real PO -> 200, correct svc + bogus
PO -> 404 (handler ran), wrong/no svc -> 307 (still gated). 324 unit tests
green; tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:55:40 +05:30
Claude (auto-fix)
4ed27d668b feat(po): Project Code dropdown on PO forms
All checks were successful
PR checks / checks (pull_request) Successful in 51s
PR checks / integration (pull_request) Successful in 29s
Replace the free-text Project Code input with a native <select> carrying a
fixed list of project codes (Petronet LNG Cochin, COMACOE Trombay, Haldia
Reach, Haldia MMT, COMACOE Mandvi) plus an empty "— none —" option, across
all three PO forms (new / edit / manager-edit).

- Add a shared PROJECT_CODES constant in lib/validations/po.ts as the single
  source of truth.
- Add a reusable <ProjectCodeField> (mirrors <DeliveryLocationField>): plain
  HTML select keeping name="projectCode" so the server actions are unchanged.
- The field stays optional; projectCode remains a nullable free-text snapshot
  (no schema/migration, no validation tightening) so legacy/imported values
  are not rejected. On edit, a current value not in the list is preserved as a
  leading "(current)" option so it is never silently dropped.
- Add unit tests for the new field.

Fixes #124

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:35:59 +05:30
Claude (auto-fix)
3e8f5fb0c7 feat(history): add Accounting Code search filter to PO History
All checks were successful
PR checks / checks (pull_request) Successful in 49s
PR checks / integration (pull_request) Successful in 30s
PO History had no way to narrow by accounting code. Add an "Accounting
Code" filter (the shared type-to-search combobox) alongside Cost Centre,
backed by the PO-level account already included in the query.

- history/page.tsx: read `accountId` searchParam, fetch selectable leaf
  accounting codes (active, no children) via buildAccountGroups, apply
  `where.accountId`, thread the param into pagination + export links, and
  surface an Accounting Code column for context.
- history-filters.tsx: new SearchableSelect control wired into
  buildParams/apply/clear/hasFilters like the Cost Centre select.
- api/reports/export: apply the same `accountId` filter so CSV/PDF export
  respects the on-screen filter.
- tests/staging: verify picking a code drives an `accountId` query param.

Fixes #121

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:18:00 +05:30
2fcb207add Merge pull request 'fix(reports): charts rendered one colour — RSC client/server boundary bug' (#120) from fix/reports-series-colors into master
All checks were successful
Refresh staging / refresh (push) Successful in 8s
Deploy release to production / deploy (push) Successful in 1m35s
Reviewed-on: #120
2026-06-24 07:05:39 +00:00
34143b5e75 fix(reports): chart series all rendered one colour (RSC boundary bug)
Some checks failed
PR checks / checks (pull_request) Failing after 15s
PR checks / integration (pull_request) Successful in 41s
The comparison charts (and detail-page breakdown swatches) rendered every
series in recharts' default colour instead of the per-item palette.

Root cause: `SERIES_COLORS` was defined in `components/reports/charts.tsx`,
which is a "use client" module. The report **pages are server components** and
imported the palette from it. A plain value imported from a client module into
a server component is a client-reference proxy, not the real array — so
`SERIES_COLORS[i % SERIES_COLORS.length]` was `SERIES_COLORS[NaN]` → undefined,
every line got `stroke={undefined}`, and recharts fell back to #3182bd. (The
literal `strokeWidth={2}` still applied, which is why only the colour was wrong.
It passed jsdom tests because those import the array directly, not across the
RSC boundary.)

Fix: move the palette to a dependency-free shared module `lib/report-colors.ts`
(no "use client", no server-only imports) that resolves to the real array in
both server and client graphs. `charts.tsx` and all four report pages import it
from there. It can't live in `lib/reports.ts` (that imports Prisma `db`, which
must not enter the client bundle).

Verified in a real browser: line strokes now cycle the 10-colour palette
(#2563eb, #16a34a, …) instead of a uniform #3182bd.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 12:31:40 +05:30
cf69292be3 Merge pull request 'test(staging): feature-level verification of closed issues + seeded test users' (#119) from feat/staging-issue-verification into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #119
2026-06-24 06:46:17 +00:00
a72e980558 test(staging): feature-level verification of closed issues + seeded test users
All checks were successful
PR checks / checks (pull_request) Successful in 46s
PR checks / integration (pull_request) Successful in 32s
Adds a Playwright suite (App/tests/staging/) that logs into the running staging
instance (ppms-staging, :3200) and verifies each closed portal issue is actually
fixed — feature level, driving the real UI, one spec per issue.

To make credential login possible against the prod-mirror pelagia_test (which only
holds real, mostly SSO-only users), prisma/seed-test-users.ts idempotently seeds one
known-password @pelagia.local user per role, and automation/refresh-test-db.sh runs
it after every daily refresh so the logins persist on staging.

Result against staging: 41 passed, 1 skipped (#10 — no attachment data on staging).
Two closed issues were found NOT fixed and are recorded as documented test.fail():
  - #13 Accounts "payments completed this month" card is absent.
  - #24/#40 logout tooltip still reads "Sign out" (pipeline test issues).

Docs/TESTING.md documents the suite, the seeded users, how to run it against
staging, and the full issue -> script mapping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 11:49:48 +05:30
ee8313e10c Merge pull request 'fix(reports): one colour per item in the comparison chart (yearly too)' (#118) from fix/reports-chart-colors into master
All checks were successful
Refresh staging / refresh (push) Successful in 8s
Reviewed-on: #118
2026-06-24 06:18:13 +00:00
0e9d06fe71 fix(reports): colour the comparison chart per item in yearly mode too
Some checks failed
PR checks / checks (pull_request) Failing after 4s
PR checks / integration (pull_request) Successful in 30s
The monthly/weekly comparison already drew one colour per item (series =
items). Yearly mode instead coloured by financial year (series = FYs, items on
the x-axis), so multiple cost centres / accounting codes in the same yearly
graph shared colours. Unify all three granularities to series = items: the
x-axis is months / weeks / FYs and each item keeps its own distinct colour
(yearly becomes grouped bars per item rather than per year).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:47:12 +05:30
91349f7564 Merge pull request 'feat(reports): Purchasing spend analytics (Cost Centres + Accounting Codes)' (#117) from feat/reports into master
All checks were successful
Refresh staging / refresh (push) Successful in 8s
Reviewed-on: #117
2026-06-24 06:03:28 +00:00
47ac2c7813 feat(reports): weekly granularity, custom compare, line-item allocation
All checks were successful
PR checks / checks (pull_request) Successful in 48s
PR checks / integration (pull_request) Successful in 31s
Picks up the three pieces deferred from the initial reports PR:

#3 Line-item account allocation — allocatePoSpend() splits each PO across the
accounting codes its line items carry (line accountId, falling back to the
PO-level account), proportionally so per-PO rows sum back to totalAmount. The
accounting-code report now attributes multi-account POs correctly. SpendRow
gains poId; poCount is now distinct POs, not row count.

#2 Custom "Add to graph" — tick rows on either index (SelectCheckbox links
write ?sel=id1,id2), then "Compare selected" (?cmp=1) shows a custom comparison
of just those entities. Fully server-rendered + shareable; export honours sel.

#1 Weekly granularity — a third Granularity that focuses one FY month and
buckets spend by week-of-month (W1–W5) from approvedAt, with a Month picker in
the toolbar. Real buckets (not the mockup's synthetic split).

All three are URL-driven like the rest, so no client fetching. Charts/KPIs/
detail trends all branch on the new mode.

Tests: +8 unit cases (allocation proportional/fallback/empty, weekly buckets,
sel parse/toggle, month + granularity parsing); fixture updated for poId/week.
Full unit suite 311 green; tsc clean. Smoke-tested weekly + custom-compare +
exports end-to-end (all 200). Docs + wiki updated to mark them implemented.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 11:25:05 +05:30
8c6bbd8304 feat(reports): Purchasing spend analytics (Cost Centres + Accounting Codes)
All checks were successful
PR checks / checks (pull_request) Successful in 46s
PR checks / integration (pull_request) Successful in 31s
Implements the wiki "Reports Mockup" as a Reports → Purchasing sidebar section,
wired to real approved-PO spend.

Two report families, each index → drill/detail:
- Cost Centres (/reports/cost-centres) — spend compared across vessels; row
  opens a cost-centre report with a Top-accounting-codes breakdown re-pivotable
  by tier (Heading/Sub/Leaf) + Top-N.
- Accounting Codes (/reports/accounting-codes) — drills the Account tree
  (headings → sub → leaves) via ?parent=; a leaf opens its report broken down by
  cost centre (or, for a non-leaf, by sub-account).

Shared: a pinned filter toolbar (Granularity Monthly/Yearly, Financial Year,
Show Top5/Top10/Bottom5/All) whose values live in the URL query so the server
component re-renders — no client fetching. KPI tiles, recharts comparison/trend/
breakdown charts, per-row trend sparklines, and CSV export (/api/reports/spend).

- lib/reports.ts: the pure, unit-tested aggregation core. Spend = a PO once it
  reaches POST_APPROVAL_STATUSES, dated by approvedAt, valued at totalAmount
  (the dashboard's basis); Indian Apr–Mar FY; each PO's leaf accountId rolled up
  to parents. One query in getReportDataset(), everything else pure.
- Sidebar: new collapsible "Reports" section with a "Purchasing" subheading
  (subgroup support added to the Section model). Gated by view_analytics
  (Manager/SuperUser/Auditor/Admin); export by the same.

Deferred (documented): synthetic Weekly granularity, the "Add to graph" custom
multi-select, and line-item-level account allocation (v1 uses the PO-level
account). Sites are not cost centres — only vessels.

Tests: 11 unit cases for the aggregation core + 3 sidebar cases for the Reports
section. Full unit suite 303 green; tsc clean. Smoke-tested all routes end to
end against seed data (index/drill/detail/export 200; non-analytics role 307/403).

Wiki: "Reports Mockup" marked implemented; "Pages and Navigation" lists the new
routes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 07:52:23 +05:30
a08ed68569 Merge pull request 'fix: Make GST captcha a popup' (#115) from claude/issue-114 into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #115
2026-06-24 01:19:51 +00:00
56497a0d20 Merge pull request 'feat(po): prompt to save as draft when leaving with unsaved changes' (#116) from feat/po-draft-prompt into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #116
2026-06-24 01:14:20 +00:00
7d4ad6a9b8 feat(po): prompt to save as draft when leaving with unsaved changes
All checks were successful
PR checks / checks (pull_request) Successful in 44s
PR checks / integration (pull_request) Successful in 32s
Closes #18. Navigating away from a PO create/edit screen with unsaved
changes could silently lose in-progress work. The forms now track a dirty
flag and guard both navigation paths:

- Hard navigations (refresh / tab close / external link) → the browser's
  native "Leave site?" prompt via beforeunload.
- In-app navigations (sidebar / header / any internal link) → a capture-phase
  click interceptor opens a modal offering Save as draft / Discard changes /
  Stay on page. Save as draft runs the form's existing draft save (which
  redirects to the PO); Discard continues to the intended destination.

The guard (components/po/unsaved-changes-guard.tsx) is reusable and wired into
both new-po-form and edit-po-form. dirty is cleared before a successful submit
so saving never trips the prompt. SPA back-button (popstate) is left to
beforeunload only; the manager inline-edit panel is out of scope (saves in
place, no draft concept).

Tests: 7 new unit cases for the guard (intercept-when-dirty, no-op-when-clean,
external links pass through, Stay/Discard/Save actions, beforeunload arming).
Unit suite 296 green; tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 06:37:33 +05:30
Claude (auto-fix)
55ae1d46d0 fix(vendors): move GSTIN CAPTCHA into a popup
All checks were successful
PR checks / checks (pull_request) Successful in 46s
PR checks / integration (pull_request) Successful in 31s
The GSTIN lookup rendered its CAPTCHA (image + 6-digit input + Verify /
New image) inline inside the Add/Edit Vendor dialog. AdminDialog has no
internal scroll and is vertically centred, so the taller form pushed its
footer (Cancel / Create Vendor / Save) off-screen and out of reach.

Extract the CAPTCHA into a dedicated popup (CaptchaPopup) overlaid on the
vendor form at z-[60] with an explicit Cancel button and a ✕ close
control. It handles Escape on the capture phase so dismissing the CAPTCHA
does not also close the underlying form. In-flight CAPTCHA errors now
show inside the popup (it stays open so the user can retry / get a new
image); the success line still lands on the main form. The form footer is
never displaced.

Adds a unit test covering popup open on Look up, Cancel closing only the
popup, and a successful verify populating the fields.

Fixes #114
2026-06-24 06:37:29 +05:30
21df005ab6 Merge pull request 'feat(sidebar): group Purchase Order links under Purchasing' (#113) from feat/sidebar-purchasing-section into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #113
2026-06-24 00:45:23 +00:00
502411afe6 Merge pull request 'chore(crewing): expand abbreviated rank names (+ seed ranks to prod)' (#111) from feat/expand-rank-names into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Reviewed-on: #111
2026-06-24 00:45:10 +00:00
2e8fd67805 test(sidebar): cover PO links moved under Purchasing + renames
All checks were successful
PR checks / checks (pull_request) Successful in 44s
PR checks / integration (pull_request) Successful in 31s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 06:10:55 +05:30
29118aa88e feat(sidebar): group Purchase Order links under Purchasing
Some checks failed
PR checks / checks (pull_request) Failing after 3s
PR checks / integration (pull_request) Successful in 31s
Move New PO, Closed POs, Import PO and History into the Purchasing
section and rename them: "New PO" to "New Purchase Order", "Import PO"
to "Import Purchase Order", "History" to "Purchase Order History".
Per-role visibility is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 06:08:49 +05:30
d25a600566 chore(crewing): expand abbreviated rank names in rank-data
All checks were successful
PR checks / checks (pull_request) Successful in 44s
PR checks / integration (pull_request) Successful in 31s
Fully expand the five abbreviated rank names so the canonical seed (which
upserts ranks by code and overwrites name) matches the names loaded into prod:
- PM → Project Manager
- Assistant PM → Assistant Project Manager
- Sr. Dredge Operator → Senior Dredge Operator
- Jr. Dredge Operator → Junior Dredge Operator
- Sr. Fabricator → Senior Fabricator

Hierarchy, codes, category, isSeafarer and grantsLogin are unchanged. (The
prod Rank table was seeded with these 19 ranks out-of-band; this keeps the
source of truth in sync so a future seed won't revert the names.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 05:39:14 +05:30
7245bb1962 Merge pull request 'fix: On new PO screen Vendors should have search' (#110) from claude/issue-109 into master
All checks were successful
Refresh staging / refresh (push) Successful in 7s
Deploy release to production / deploy (push) Successful in 1m30s
Reviewed-on: #110
2026-06-23 23:52:47 +00:00
c710fe5d73 Merge pull request 'feat(po): catalogue line items on approval + move /inventory/{items,vendors} ? /catalogue' (#108) from feat/catalog-on-approval into master
All checks were successful
Refresh staging / refresh (push) Successful in 8s
Reviewed-on: #108
2026-06-23 23:47:49 +00:00
Claude (auto-fix)
c503f839e8 feat(po): make vendor field a searchable combobox
All checks were successful
PR checks / checks (pull_request) Successful in 46s
PR checks / integration (pull_request) Successful in 31s
The vendor field on the PO forms was a plain native <select>, forcing
users to scroll the full vendor list. Mirror the item-search UX with a
searchable combobox that filters by vendor name and formal code
(vendorId), case-insensitively. The vendor list is already client-side,
so this is a pure in-memory filter — no API or DB change.

New VendorSelect component (components/ui/vendor-select.tsx) is a
self-contained portal-rendered combobox posting a hidden vendorId input,
so it drops into all three PO forms unchanged on the server:
- po/new/new-po-form
- po/[id]/edit/edit-po-form
- approvals/[id]/manager-edit-po-form

Preserves the optional field, "No vendor selected" empty option, and the
"{name} (CODE)" / "(unverified)" label. Unverified vendors (null code)
remain findable by name. Adds unit tests for the filter logic and
component behaviour.

Fixes #109
2026-06-24 05:16:57 +05:30
2bdf3a6536 chore: rename e2e folder to catalogue + drop /inventory redirects
All checks were successful
PR checks / checks (pull_request) Successful in 43s
PR checks / integration (pull_request) Successful in 31s
- tests/e2e/inventory → tests/e2e/catalogue (folder name only; playwright globs
  ./tests/e2e so nothing else changes).
- remove the next.config redirects from /inventory/{items,vendors}: the old
  routes are intentionally left free for a future feature.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 05:06:44 +05:30
d7b455ab7d refactor(routes): move /inventory/{items,vendors} → /catalogue/{items,vendors}
All checks were successful
PR checks / checks (pull_request) Successful in 44s
PR checks / integration (pull_request) Successful in 31s
Renames the product-catalogue pages (items + vendors, incl. their [id] detail
pages) out of /inventory into /catalogue. /inventory/cart is unchanged. All
internal links, redirects, revalidatePath calls, sidebar nav, and tests are
updated; next.config redirects keep old /inventory/{items,vendors}[/...] URLs
working (permanent) so existing bookmarks don't 404.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 05:04:29 +05:30
70f3230c36 feat(po): register line items in the product catalogue on approval
All checks were successful
PR checks / checks (pull_request) Successful in 43s
PR checks / integration (pull_request) Successful in 31s
Previously a PO's free-text line items only became reusable catalogue products
(/inventory/items) on full payment (markPaid → syncProductCatalog). An approved-
but-unpaid PO's items weren't selectable for further POs yet.

- extract syncProductCatalog into lib/product-catalog.ts (shared).
- call it from approvePo so approved items are immediately catalogued (create
  product by name if unknown, link the line item, upsert last/per-vendor price);
  payment still re-syncs to refresh prices. Idempotent.
- test: approving a PO with a free-text line creates + links the product and
  records the per-vendor price.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 04:59:47 +05:30
85805754b5 Merge pull request 'feat(po): user-defined T&C categories + dynamic PO terms editor (#11 follow-up)' (#107) from feat/terms-dynamic-editor into master
All checks were successful
Refresh staging / refresh (push) Successful in 8s
Reviewed-on: #107
2026-06-23 23:27:47 +00:00