feat: add checkout action banner
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-12 13:35:43 +01:00
parent 02363792c8
commit cceed361b7
6 changed files with 58 additions and 4 deletions

View File

@@ -52,6 +52,7 @@ export default function MobileBillingPage() {
const [pendingCheckout, setPendingCheckout] = React.useState<PendingCheckout | null>(() => loadPendingCheckout());
const [checkoutStatus, setCheckoutStatus] = React.useState<string | null>(null);
const [checkoutStatusReason, setCheckoutStatusReason] = React.useState<string | null>(null);
const [checkoutActionUrl, setCheckoutActionUrl] = React.useState<string | null>(null);
const lastCheckoutStatusRef = React.useRef<string | null>(null);
const packagesRef = React.useRef<HTMLDivElement | null>(null);
const invoicesRef = React.useRef<HTMLDivElement | null>(null);
@@ -179,6 +180,7 @@ export default function MobileBillingPage() {
if (!pendingCheckout?.checkoutSessionId) {
setCheckoutStatus(null);
setCheckoutStatusReason(null);
setCheckoutActionUrl(null);
lastCheckoutStatusRef.current = null;
return;
}
@@ -194,6 +196,7 @@ export default function MobileBillingPage() {
}
setCheckoutStatus(result.status);
setCheckoutStatusReason(result.reason ?? null);
setCheckoutActionUrl(typeof result.checkout_url === 'string' ? result.checkout_url : null);
const lastStatus = lastCheckoutStatusRef.current;
lastCheckoutStatusRef.current = result.status;
@@ -290,7 +293,43 @@ export default function MobileBillingPage() {
</XStack>
</MobileCard>
) : null}
{pendingCheckout && checkoutStatus !== 'failed' && checkoutStatus !== 'cancelled' ? (
{pendingCheckout && checkoutStatus === 'requires_customer_action' ? (
<MobileCard borderColor={accentSoft} backgroundColor={accentSoft} space="$2">
<XStack alignItems="center" justifyContent="space-between">
<YStack space="$0.5" flex={1}>
<Text fontSize="$sm" fontWeight="800" color={textStrong}>
{t('billing.checkoutActionTitle', 'Action required')}
</Text>
<Text fontSize="$xs" color={muted}>
{t('billing.checkoutActionBody', 'Complete your payment to activate the package.')}
</Text>
</YStack>
<PillBadge tone="warning">
{t('billing.checkoutActionBadge', 'Action needed')}
</PillBadge>
</XStack>
<XStack space="$2">
<CTAButton
label={t('billing.checkoutActionButton', 'Continue checkout')}
onPress={() => {
if (checkoutActionUrl && typeof window !== 'undefined') {
window.open(checkoutActionUrl, '_blank', 'noopener');
return;
}
navigate(adminPath('/mobile/billing/shop'));
}}
fullWidth={false}
/>
<CTAButton
label={t('billing.checkoutFailedDismiss', 'Dismiss')}
tone="ghost"
onPress={() => persistPendingCheckout(null)}
fullWidth={false}
/>
</XStack>
</MobileCard>
) : null}
{pendingCheckout && checkoutStatus !== 'failed' && checkoutStatus !== 'cancelled' && checkoutStatus !== 'requires_customer_action' ? (
<MobileCard borderColor={accentSoft} backgroundColor={accentSoft} space="$2">
<XStack alignItems="center" justifyContent="space-between">
<YStack space="$0.5" flex={1}>