- Added VesselTransferReviewPage for reviewing and managing vessel transfer requests in backoffice. - Created VesselTransferApplicationPage for submitting new ownership transfer requests in portal. - Introduced mock data for vessels and transfer requests in both portal and backoffice. - Updated navigation and layout to include vessel transfer sections in both applications. - Added internationalization support for vessel transfer labels in English and Amharic. - Documented the implementation details and suggested API surface for future integration.
4.3 KiB
Vessel Ownership Transfer — mock implementation notes (for API integration)
Portal (owner-facing) + Backoffice (officer-facing) built as mock UI only, matching each app's existing mock-driven review-workflow convention. No backend calls anywhere. This doc exists to make wiring the real API fast — it says exactly what to replace and where.
What exists
Portal (apps/portal/src/app/features/vessel-transfer/)
mock.ts— types +MOCK_VESSELS,MOCK_REQUESTS,addTransferRequest()pages/VesselTransferPage.tsx— list/status view (/vessel-transfers)pages/VesselTransferApplicationPage.tsx— 4-step submit wizard (/vessel-transfers/apply)
Backoffice (apps/backoffice/src/app/features/vessel-transfer/)
pages/VesselTransferQueuePage.tsx— types +MOCK_REQUESTS, queue table, stats, quick-view drawer (/vessel-transfers)pages/VesselTransferReviewPage.tsx— full review, approve/reject, doc view/download (/vessel-transfers/:id)
Each app defines its own separate mock array (codebase convention: no shared domain types in libs/). They are not the same objects and don't sync — submitting in portal does not appear in the backoffice queue, and vice versa. That's the main thing a real API fixes.
Data shape mismatch to resolve
Portal's TransferRequest.currentOwner / newOwnerName are plain strings. Backoffice's TransferRequest.currentOwner / newOwner are full objects ({ name, idOrTin, phone, email, address }). The real API should standardize on the backoffice shape — it's the superset the officer review page needs.
Known gap: VesselTransferApplicationPage.tsx's wizard collects newOwnerIdOrTin, newOwnerPhone, newOwnerEmail, newOwnerAddress and shows them in the review step, but addTransferRequest() only persists newOwnerName — the rest are captured in UI state and silently dropped on submit. When wiring the real POST, send all of them (the fields already exist in component state, just not in the payload today).
Suggested API surface
GET /vessels?ownerId=:id&status=approved— portal wizard's vessel<Select>(replacesMOCK_VESSELS.filter(v => v.approved)inVesselTransferApplicationPage.tsx).POST /vessel-transfers— submit. Body:vesselId, newOwner: {name, idOrTin, phone, email, address}, reason, document (file upload). ReplacesaddTransferRequest()in portal'smock.ts.GET /vessel-transfers?ownerId=:id— portal's "My Transfer Requests" list (VesselTransferPage.tsx).GET /vessel-transfers(officer, all + filters) — backoffice queue (VesselTransferQueuePage.tsx); search/status-filter can move server-side or stay client-side over the fetched page.GET /vessel-transfers/:id— backoffice review page and portal status detail both key off this.PATCH /vessel-transfers/:id— officer action. Body:{ status: 'Under Review' | 'Approved' | 'Rejected', remarks? }. Replaces the in-placerecord.status = ...mutation inVesselTransferReviewPage.tsx. Backend should enforce remarks-required-on-Rejected (UI already gates this, but don't trust client-only validation).- Document storage: real upload + signed URL for View, and a real download endpoint — today
getDocUrl()in bothCoCReviewPage-styleDocViewers and this feature'sVesselTransferReviewPage.tsxfake it with a hardcoded base64 PDF / placehold.co image. - SMS/email: triggered server-side on submit and on every status change — UI currently just shows a toast claiming this happened (
notify.success('... SMS and email ...')); no actual send anywhere.
Where to swap mock for real calls
- Portal:
apps/portal/src/app/features/vessel-transfer/mock.ts(whole file), plus theuseState/local reads ofMOCK_VESSELS/MOCK_REQUESTSin both page files. - Backoffice: the exported
MOCK_REQUESTS/types block at the top ofVesselTransferQueuePage.tsx, plus the directrecord.status = ...mutation inVesselTransferReviewPage.tsx(swap for a mutation call + refetch/cache-invalidate). - Both apps already have an RTK Query base (
@ema-platform/api→baseApi.injectEndpoints, seeapps/portal/.../payment/api/payment-api.tsorapps/backoffice/.../certification/api/certification-api.ts) — follow that pattern rather than the ad-hocuseApiQuery/useApiMutationescape hatch.