Namespaces the OAuth landing path in all three places it exists: the API's
ack controller, both web apps' routes, and the redirect_uri env values.
A bare /callback claimed a generic top-level path in every app for one
provider's redirect.
The API side needed care. The ack controller moves to @Controller
('fayda/callback'), and the global-prefix exclusion has to name that exact
path — setGlobalPrefix's exclude is an exact route match, not a subtree, so
excluding "fayda" would have left /fayda/callback served at
/api/fayda/callback and 404ing at the registered redirect_uri, while
reading as though it covered everything under /fayda. Naming the full path
also keeps /api/fayda/verification/* prefixed, which every client calls.
Also drops a stale comment on the portal's callback route describing the
popup that no longer exists, and records why the route is public: behind
RequireAuth the onboarding gate redirects to /portal before the code+state
exchange can run.
NOT verified at runtime — this changes route registration, so boot the API
and confirm GET /fayda/callback answers un-prefixed and
/api/fayda/verification/start still resolves before relying on it.
Deploying this requires registering the new redirect_uri with eSignet
first; FAYDA_WEB_REDIRECT_URI, FAYDA_PORTAL_REDIRECT_URI and any mobile
client must be updated in step or verification breaks with a redirect_uri
mismatch.
The verification popup was opened after the /start round-trip, by which
point the click's user activation is spent — iOS Safari blocks it outright,
so mobile customers could never verify. Replace the popup with a full-page
redirect: the panel stashes {subject, returnTo} in sessionStorage and
navigates the tab to eSignet, and /callback completes the code+state
exchange itself before returning the user where they were.
This drops the postMessage listener, the popup-closed poller and the
pop-up-blocked branch. onVerified goes with them: the app boots fresh on
the way back, so the target page refetches rather than being pushed to.
Hide the DARS delegation upload until the PoA is Fayda-verified. The paper
authorises the representative the verification names, so it has nothing to
authorise before one exists — and it has to stop being required while
hidden, or the save blocks on a control the customer cannot see. Freight
forwarders are still held to having a PoA by the step's verification gate
and by the API. This also removes the one thing the redirect could not
carry across: a staged File cannot be serialised to sessionStorage, and
there is now never one pending before verification.
Unsaved text typed since the last step-save is still lost on redirect; the
wizard's per-step persistence covers everything already advanced past.
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 #181 — eTrade auto-fetch already existed; adds a client-side
download of the fetched record (eTrade returns data, not a document)
on the portal onboarding step and the backoffice customer detail page.
Ticket #422 — surface Pending/Suspended/Approved company status as a
banner, branching AccountReviewBanner on companyStatus instead of only
per-profile review state.
- FaydaVerifyPanel + /callback popup flow for owner and poa
- general manager is a plain typed role again, offers "same as
verified owner" copy instead of being fayda-verified itself
- company step gates on owner verification (ethiopian) or typed
passport number (foreign); poa step gates on poa verification
- settings tabs (company profile, general manager, poa) updated to
match
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Added functionality to cancel contracts, allowing users to provide a reason for cancellation.
- Updated contract statuses to include SUSPENDED and changed CLOSED to COMPLETED.
- Enhanced the UI to reflect the new cancellation option and updated messaging for contract statuses.
- Refactored contract booking actions to accommodate changes in booking logic for ONE_TIME and GENERAL contracts.
- Removed clearance document management from the contract detail page, as it is now handled per booking.
- Introduced a SQL script to reset bookings and train schedules for development purposes.
- 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.
The documents step of CompanyProfileForm uploads company documents via
onUploadDocuments() and then triggers submit synchronously in the same
nextStep() call. The setDocumentFiles({}) that clears the staged files has
not re-rendered by the time finishMutation's closure runs, so reading
documentFiles there re-sent the exact same files and created a duplicate
row per document.
Drop the company-document upload from finishMutation — the documents step
already persisted them. Licenses stay, since they have no auto-upload path.