Title and subtitle grew unbounded, relying only on flex-squeeze from action
buttons to ever truncate. Caps the container at 640px and gives the
subtitle the same truncate treatment the title already had, so a long
page title or description (report descriptions in particular) always
ellipses instead of stretching the header.
Swaps ReportView's bespoke ReportFilters for the same FilterBar/useFilters
combo BookingRequestsPage uses — pills, saved-view-ready state, URL sync.
toFilterDefs() maps the report catalog's own filter vocabulary
(daterange/date/select/multiselect/text) onto FilterDef, matched against
every report definition's backend param handling. The search box only
shows for reports that actually implement `search` server-side, so it's
not a dead control on the rest. ReportFilters.tsx is now dead, removed.
DateBody always initialized its op state to DEFAULT_OP.date ("between"),
ignoring a def's `operators` restriction. A single-operator exact-date
filter (e.g. `operators: ["before"]`) opened on the range UI with no way
off it, since OperatorSelect hides itself when there's only one choice.
Default to the def's first allowed operator instead.
- Add migration for consolidation approvals table and status enum
- Create ConsolidationApprovalService to handle approval logic
- Implement repository for managing consolidation approvals
- Add entity for consolidation approval with necessary fields
- Develop frontend components for displaying and managing consolidation approvals
- Create tests for consolidation approval service to ensure correct behavior
- Add migration for consolidation approvals table and status enum
- Create ConsolidationApprovalService to handle approval logic
- Implement repository for managing consolidation approvals
- Add entity for consolidation approval with necessary fields
- Develop frontend components for displaying and managing consolidation approvals
- Create tests for consolidation approval service to ensure correct behavior
- Added support for viewing and managing consolidated bookings in BookingRequestDetailPage.
- Enhanced BookingRequestsPage to display paired bookings in a single row.
- Introduced pairedDecision method in bookings service to handle decisions for both halves of a consolidated pair.
- Updated contracts service to include methods for manual consolidation of odd-20ft bookings.
- Created new components for selecting and editing consolidation partners.
- Added tests for paired decision logic and manual consolidation scenarios.
- Updated UI to reflect changes in booking handling and provide user feedback for odd container counts.
Confirmed live 2026-08-17: buyer profiles store just the sub-city name
("Bole", "Arada") as their woreda, never the source CSV's specific
example-woreda name ("Bole Woreda 01") — hit for three different
buyers in a row (Bole, Arada, Lemi Kura) before any got past this
check. Since the CSV lists exactly one representative woreda per
Addis sub-city, alias the bare name to that same code instead of
waiting on a fuller table — use what's already been supplied first.
Lemi Kura itself isn't in the source CSV at all (one of Addis
Ababa's newer sub-cities) — still needs its real code from MoR.
The switch clears the registration but not the tax number, so the company step
reopens with the old TIN and RHF re-seeds the field when the profile refetch
lands. A TIN typed before that arrived was silently replaced by the stored one
and the lookup ran against the wrong number.
The spec now waits for the rehydrated VAT value, then asserts the field holds
what it typed before waiting on eTrade.
A resumed onboarding asked for the per-role business licence again and refused
to submit until it was uploaded a second time, however many were already on
file. `getInfo` never populates `companyProfiles[].licenseFiles`, and the
wizard validates against exactly that — so every profile looked empty, while
`markOnboardingComplete` would have accepted the application as it stood.
Validation now reads `licenseProfiles[].uploaded` from the onboarding
requirements: the server's own verdict, already fetched by the step, and the
same source the API enforces on submit.
Reachable today by anyone who resumes onboarding after a licence upload, and by
every company that switches back to eTrade registration — which is where the
e2e suite hit it.
Five journeys, one file each, every one of them crossing from the portal into
the backoffice and cross-checking the database rather than the screen:
- ethiopian eTrade verified, Fayda, approved, contract wizard reachable —
including the dead end a TIN with no trade licence is for an
ordinary company
- investor foreign investment licence: nothing on file at eTrade, typed
registration, passport identity, per-role licence still owed,
and the backoffice's manual-entry badge and banner
- cooperative no foreign option, no freight-forwarder role, the co-operative
document set, no licence cards, its own badge and banner
- switch_back settings → switch to eTrade → registration cleared, company
pending, wizard reopened on the company step → re-run through
eTrade → the flag is gone from the backoffice
- guards the refused combinations, and the mid-wizard un-tick that has
to clear the typed registration
Replaces the old onboarding.cy.ts (removed a commit earlier by accident of a
staged deletion): it drove a wizard shape that no longer exists — Fayda before
the company step, a "Personnel" step — so it could only ever have been red.
Notes for whoever edits these next. Attach files to the FIRST empty dropzone,
never by index — SmartFileInput removes the input once a file is on it. Resolve
the company from the database after any cross-origin hop, never from module
state: Cypress re-evaluates the spec bundle and Date.now() with it, which is
what latestJourney's run-stamp cutoff is for. And the deliberate eTrade 400 is
ignored as an uncaught exception — the portal handles that outcome on screen
but leaves the rejected request unhandled at the promise level.