Backoffice can now "Request changes" on a pending settings change
request without rejecting it outright: a new ChangesRequested status
keeps the row open so the customer's next edit appends into the same
request instead of starting a fresh cycle, and the reviewer's note
persists across that round instead of being cleared on resubmit.
Version History and Review History (previously two separate,
differently-shaped lists) are merged into one chronological timeline
under a new History tab, including document changes shown as a real
previous-vs-current diff (both files openable).
Bug fixes surfaced while wiring this up:
- Replacing a single-file document slot left the old file live
alongside the new one instead of retiring it (customer settings +
onboarding uploads).
- The "previous" file in a document diff 404'd once superseded —
the preview route now also matches soft-deleted records.
- A document replace was recorded twice in the timeline (once at
upload, once again at change-request approval).
Ticket #238 — pre-approval edits and document uploads write straight
to the live company row with no approval gate and, until now, no
trace. Adds an append-only company_revisions log (diffed field
changes, document uploads) recorded from updateProfile and
uploadCompanyDocuments, exposed via GET /companies/:id/revisions and
shown as "Version history" on the backoffice customer detail page.
- Add TransferFulfillModal for fulfilling wagon transfer requests.
- Create TransferRequestFormModal for filing new wagon transfer requests.
- Introduce TransferCloseShortModal for closing requests that cannot be fully fulfilled.
- Develop WagonTransfersPage to manage and display wagon transfer requests.
- Implement utility functions for handling wagon transfer request data and UI components.
- Enhance UI with Mantine components for better user experience.
- Introduced HazardDeclarationPanel component to display dangerous goods declaration details.
- Updated URL constants to include CLEARANCE_PROCEED endpoint for re-requesting operations.
- Enhanced permissions to include hazardous approval roles for contract approvals.
- Integrated HazardDeclarationPanel into ContractRequestDetailPage and ContractClearanceDetailPage.
- Added proceedToOperation method in bookings service for handling operation re-requests.
- Updated contract forms and schemas to include hazard class and UN number fields.
- Implemented validation for hazardous contracts in the contract creation flow.
- Added expiry notice functionality for contracts nearing validity end.
- Created tests for expiry notice calculations and labels.
- Updated UI components to reflect hazardous cargo information and validation errors.
- Introduced StampUpload component for uploading company stamp images.
- Integrated stamp upload in contract signing modal, supporting PNG and JPG formats.
- Implemented validation for file type and size (max 5 MB).
- Added visual feedback for drag-and-drop functionality.
- Updated contract-related pages to handle duplicate contract alerts and pricing notices.
- Enhanced contract expiry management with a nightly sweep service.
- Added unit tests for new features and updated existing tests for contract handling.
Join truck_types via vehicles.truck_type_id (normalized legacy
vehicle_type only as fallback) so type renames can't unmatch detention
rules and FK-less vehicles keep billing.
- Added functionality to move containers between wagons in the train scheduling system.
- Introduced API endpoint and service method to handle container movement.
- Updated component to support drag-and-drop for rearranging containers.
- Enhanced to allow moving containers to other wagons via a context menu.
- Implemented UI feedback for container movement actions, including loading states and success/error notifications.
- Updated relevant types and constants to accommodate new container movement logic.
- Added tests for the rule engine to ensure proper handling of hazardous bookings.
Two review-workflow gaps for freight customer onboarding:
Request for change per document. Backoffice can now flag a single uploaded
document (company document, profile licence, or POA delegation letter) with a
note the customer sees, instead of rejecting the whole role over it. Adds
review_status/review_note/reviewed_by/reviewed_at to freight.files (migration
AddFileReviewStatus, partial index for the gate), a POST
documents/:fileId/request-change endpoint, the backoffice action + modal, and a
portal banner/badge so the customer knows what to re-upload. Re-uploading clears
the flag. Approving a role is blocked while any of its documents has an open
correction; the gate check and the status write share a pessimistic write lock
on the company row (as does the change-request write) so a correction can never
slip in between the check and the profile going Active.
Resubmission is visible to reviewers. When a customer resubmits a rejected role
or amends a change request, backoffice staff are notified (allBackoffice inbox
item, deep-linked to the customer) and the resubmission surfaces in a new
"Pending changes" list view + KPI, since such companies are status = active and
never matched the pending-approval filter.
- Introduced DEACTIVATED status for trains, allowing staff to park trains indefinitely.
- Implemented methods to deactivate and reactivate trains in the TrainBuilderService.
- Added UI components for train deactivation and reactivation in TrainBuilderDetailPage.
- Created a dropdown setting for admin-managed import train numbers, with corresponding migrations.
- Updated yard code length to accommodate soft-delete suffix.
- Enhanced train status handling to include DEACTIVATED state.
Intercity bookings never get their own train — they ride whichever import/export
train passes through their corridor — so the work is scattered across other
people's schedules and there was nowhere to see it as a whole. The per-schedule
ride-along panel answers "what can THIS train carry"; this answers "what is
happening to intercity cargo".
Purely additive: the existing ride-along panel and the schedule detail page are
untouched, and loading/unloading still happens there, where the train's position
is confirmed. This is a read-only view that points back to it.
Each row carries both ends' facility status, because a booking whose origin or
destination has no equipment can never be worked there — the operator should see
that while the train is still coming, not when the load is refused. Those
bookings are counted and called out.
New GET /train-scheduling/intercity/bookings; the type is IntercityRideAlongRow,
not IntercityBookingRow, which already means the per-schedule candidate row.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Added parameter to and for server-side free-text search on contract reference, company name, and booking details.
- Introduced new validation errors in for container clashes and space issues when creating bookings.
- Implemented paginated dropdown settings retrieval in .
- Updated to fetch active yards using a new method that handles pagination.
- Enhanced with a method to fetch all records by walking through pages.
- Refactored to support filtering and pagination in schedule listings.
- Improved to return a paginated list of facilities.
- Updated UI components in and to utilize debounced search inputs for better performance.
- Added alerts in to inform users about booking constraints related to splits and capacity.
- Enhanced to display notifications for split bookings and capacity usage.
"Auto Load Ready Items" now opens a train picker (pre-dispatch trains with
these bookings assigned, via the existing loadable-trains flow). No train
available -> no auto-loading, with a clear notice. Loading goes through the
existing per-wagon load path, so items without an allocated wagon are skipped
with a reason.
The train association is stored on the existing warehouse_loadings table
(no new table needed): new train_schedule_id column + a note recording train
number, origin -> destination, and departure time; wagon_id becomes nullable.
The trainless load-passed-export endpoint, its frontend wiring, and the unused
useLoadPassedExport hook are removed.
Migration 2100000000000 (idempotent) also applied to the dev database.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>