From 3d6deb03f6561cae4b2d9abb4085d69765b8ffe4 Mon Sep 17 00:00:00 2001 From: estifanos Date: Sat, 22 Aug 2026 07:58:08 +0000 Subject: [PATCH] feat: add payment and bypass actions to the in-flight vessel registration application table --- .../vessel-registration/inFlightColumns.tsx | 61 ++++++++++++++++--- .../pages/VesselRegistrationPage.tsx | 32 ++++++++++ 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/apps/portal/src/app/features/vessel-registration/inFlightColumns.tsx b/apps/portal/src/app/features/vessel-registration/inFlightColumns.tsx index 43dc3e382..c03cbe472 100644 --- a/apps/portal/src/app/features/vessel-registration/inFlightColumns.tsx +++ b/apps/portal/src/app/features/vessel-registration/inFlightColumns.tsx @@ -12,7 +12,14 @@ import { /** Columns for the applicant's in-flight vessel registration applications. */ export function inFlightColumns( t: TFunction, - deps: { onOpen: (app: LicenseApplication) => void }, + deps: { + onOpen: (app: LicenseApplication) => void; + onPay: (app: LicenseApplication) => void; + onBypass: (app: LicenseApplication) => void; + isPaying: boolean; + bypassEnabled: boolean; + bypassing: boolean; + }, ): AdvancedColumn[] { return [ { @@ -38,15 +45,49 @@ export function inFlightColumns( }, { header: t('applications.table.progress'), - size: 140, - cell: ({ row }) => ( - - ), + size: 300, + cell: ({ row }) => { + const app = row.original; + return ( + + + {/* The fee stops the registration dead, so the payment action sits + on the bar rather than being hidden behind View. */} + {app.status === 'PAYMENT_PENDING' && ( + <> + + {deps.bypassEnabled && ( + + )} + + )} + + ); + }, }, { header: '', diff --git a/apps/portal/src/app/features/vessel-registration/pages/VesselRegistrationPage.tsx b/apps/portal/src/app/features/vessel-registration/pages/VesselRegistrationPage.tsx index 7f896900b..4bf751431 100644 --- a/apps/portal/src/app/features/vessel-registration/pages/VesselRegistrationPage.tsx +++ b/apps/portal/src/app/features/vessel-registration/pages/VesselRegistrationPage.tsx @@ -30,11 +30,16 @@ import { import { StatusBadge, AdvancedTable } from '@ema-platform/ui'; import { inFlightColumns } from '../inFlightColumns'; import { + extractErrorMessage, TERMINAL_STATUSES, useApiMutation, + useBypassPaymentMutation, useGetMyApplicationsQuery, + useGetPaymentCapabilitiesQuery, } from '@ema-platform/api'; +import { notifications } from '@mantine/notifications'; import { authStorage } from '@ema-platform/auth'; +import { useApplicationPayment } from '../../payments/hooks/useApplicationPayment'; // --------------------------------------------------------------------------- // Types @@ -132,6 +137,9 @@ export function VesselRegistrationPage() { const navigate = useNavigate(); const { t } = useTranslation(); const { data: applications, isFetching, refetch } = useGetMyApplicationsQuery(); + const { pay, isPaying } = useApplicationPayment(); + const { data: capabilities } = useGetPaymentCapabilitiesQuery(); + const [bypassPayment, { isLoading: bypassing }] = useBypassPaymentMutation(); const [page, setPage] = useState(0); const [registration, setRegistration] = useState(null); const [fetchTrigger] = useApiMutation(); @@ -155,9 +163,33 @@ export function VesselRegistrationPage() { !TERMINAL_STATUSES.includes(app.status), ); + async function handleBypass(applicationId: string) { + try { + const result = await bypassPayment(applicationId).unwrap(); + notifications.show({ + color: 'teal', + title: 'Payment bypassed', + message: `Application is now ${result.status.replace(/_/g, ' ').toLowerCase()}.`, + }); + refetch(); + } catch (err) { + notifications.show({ + color: 'red', + title: 'Bypass failed', + message: extractErrorMessage(err), + }); + } + } + const columns = inFlightColumns(t, { onOpen: (app) => navigate(`/licensing/${REGISTRATION_TYPE_KEY}/applications/${app.id}`), + // Paying leaves the SPA for Telebirr — a provider hand-off, not a route change. + onPay: (app) => pay(app.id), + onBypass: (app) => handleBypass(app.id), + isPaying, + bypassEnabled: capabilities?.bypassEnabled ?? false, + bypassing, }); const certs = registration?.category === 'Sea-going Vessel (International)'