import { plusCircle } from '@wordpress/icons'; import { Button, Card, CardBody, Flex, Icon, Modal, Notice, TextControl, } from '@wordpress/components'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { __, sprintf } from '@wordpress/i18n'; import { formatCurrency } from '../utils'; import { PrepayAPI, PrePayBalance, TopUpOrder } from '../../types'; import { Spinner } from '@woocommerce/components'; import PaymentCore from './payment/payment-core'; import z from 'zod'; import { useWindowSize } from '../hooks'; interface PrepayTopUpProps { prepayBalance: PrePayBalance; loading?: boolean; error?: string | null; onTopUpSuccess?: () => void; createTopUp: PrepayAPI['createTopUp']; Wrapper: React.ComponentType<{ children: React.ReactNode }>; } const customeAmountSchema = z .number() .min(20, { message: 'Amount must be at least £20' }) .max(1000000, { message: 'Amount must be less than or equal to £1,000,000', }); export default function PrepayTopUp({ prepayBalance, createTopUp, loading = false, error = null, onTopUpSuccess, Wrapper, }: PrepayTopUpProps) { const [isCustomTopUpModalOpen, setIsCustomTopUpModalOpen] = useState(false); const [topUpAmount, setTopUpAmount] = useState(20); const [topUpSuccess, setTopUpSuccess] = useState(false); const [topUpOrder, setTopUpOrder] = useState(null); const [validationError, setValidationError] = useState(null); const { isSmOrUp, isLgOrUp } = useWindowSize(); // Sync topUpAmount when prepayBalance loads useEffect(() => { if (prepayBalance?.minimumTopUp) { setTopUpAmount(prepayBalance.minimumTopUp); } }, [prepayBalance?.minimumTopUp]); const topUpCurrency = prepayBalance?.currency || 'GBP'; const minimumTopUp = prepayBalance?.minimumTopUp || 20; const topUpAmounts = useMemo( () => prepayBalance?.minimumTopUp ? [1, 2.5, 5].map((m) => prepayBalance.minimumTopUp * m) : [], [prepayBalance?.minimumTopUp] ); const topUpDescription = useMemo( () => prepayBalance && typeof prepayBalance.freePercentage === 'number' && typeof prepayBalance.freeThreshold === 'number' ? sprintf( /* translators: 1: percent, 2: formatted money threshold */ __( 'Get up to %1$s%% extra every time you top up %2$s or more.', 'parcel2go-shipping' ), String(prepayBalance.freePercentage), formatCurrency( prepayBalance.freeThreshold, prepayBalance.currency ) ) : '', [ prepayBalance?.freePercentage, prepayBalance?.freeThreshold, prepayBalance?.currency, ] ); const handlePaymentSuccess = useCallback(() => { setTopUpSuccess(true); setTopUpOrder(null); onTopUpSuccess?.(); }, [onTopUpSuccess]); const closePaymentModal = useCallback(() => { setTopUpOrder(null); }, []); const handleAddBalance = useCallback( async (amount: number) => { const validation = customeAmountSchema.safeParse(amount); if (!validation.success) { setValidationError( validation.error.issues[0]?.message || 'Invalid amount' ); return; } const result = await createTopUp(amount, minimumTopUp); if (result) { setTopUpOrder(result); } if (isCustomTopUpModalOpen) { setIsCustomTopUpModalOpen(false); } }, [createTopUp, minimumTopUp, isCustomTopUpModalOpen] ); if (error) return null; return ( {__('Choose top up amount', 'parcel2go-shipping')} {loading && !error ? ( ) : ( <>

{topUpDescription}

{(error || validationError) && ( {error || validationError} )} {topUpSuccess && ( {__( 'Prepay balance topped up successfully', 'parcel2go-shipping' )} )} )}
{!loading && !error && (
{topUpAmounts.map((amt) => ( ))}
)}
{isCustomTopUpModalOpen && !topUpOrder && ( setIsCustomTopUpModalOpen(false)} shouldCloseOnClickOutside={false} >

{topUpDescription}

setTopUpAmount(Number(value)) } min={minimumTopUp} step={1} />
{(error || validationError) && ( {error || validationError} )}
)} {topUpOrder && ( )}
); }