import { useState } from 'react'; import AddIcon from '@mui/icons-material/Add'; import CloseIcon from '@mui/icons-material/Close'; import LocalOfferIcon from '@mui/icons-material/LocalOffer'; import { Alert, Box, Button, CircularProgress, IconButton, InputAdornment, Skeleton, Stack, TextField, Typography, } from '@mui/material'; import { useLocaleContext } from '@arcblock/ux/lib/Locale/context'; import { formatCouponTerms } from '../../../libs/util'; interface PromotionInputProps { promotion: { applied: boolean; code: string | null; active: boolean; inactiveReason: string | null; apply: (code: string) => Promise<{ success: boolean; error?: string }>; remove: () => Promise; }; discounts: any[]; discountAmount: string | null; // eslint-disable-next-line react/require-default-props currency?: any; /** Start with input field visible (skip the "Add promotion code" button) */ initialShowInput?: boolean; /** Show skeleton for the discount amount while switching */ isAmountLoading?: boolean; } export default function PromotionInput({ promotion, discounts, discountAmount, currency = null, initialShowInput = false, isAmountLoading = false, }: PromotionInputProps) { const { t, locale } = useLocaleContext(); const [showInput, setShowInput] = useState(false); const [code, setCode] = useState(''); const [applying, setApplying] = useState(false); const [error, setError] = useState(''); // When initialShowInput is true, always show the input (e.g. inside a drawer) const effectiveShowInput = initialShowInput || showInput; const handleApply = async () => { if (!code.trim()) return; setApplying(true); setError(''); const result = await promotion.apply(code.trim()); if (!result.success) { setError(result.error || 'Invalid code'); } else { setCode(''); setShowInput(false); } setApplying(false); }; const handleKeyPress = (event: React.KeyboardEvent) => { if (event.key === 'Enter' && !applying && code.trim()) { handleApply(); } }; // Display applied discounts if (discounts?.length > 0) { return ( {discounts.map((disc: any, i: number) => { const discCode = disc.promotion_code_details?.code || disc.verification_data?.code || disc.promotion_code || ''; const coupon = disc.coupon_details || {}; const description = coupon && currency ? formatCouponTerms(coupon, currency, locale) : ''; return ( (theme.palette.mode === 'dark' ? 'rgba(18,184,134,0.1)' : '#ebfef5'), px: 1.5, py: 0.5, borderRadius: '8px', border: '1px solid', borderColor: (theme) => (theme.palette.mode === 'dark' ? 'rgba(18,184,134,0.2)' : '#d3f9e8'), }}> {discCode} {description && ( · {description} )} {isAmountLoading ? ( ) : ( -{discountAmount || '0'} )} ); })} ); } // Add promo code — consistent height: button and input share same container height if (!promotion.active) return null; return ( {effectiveShowInput ? ( { // Don't collapse if initialShowInput is forced (e.g. inside a drawer) if (initialShowInput) return; if (!e.currentTarget.contains(e.relatedTarget as Node) && !code.trim()) { setShowInput(false); } }}> setCode(e.target.value)} onKeyPress={handleKeyPress} placeholder={t('payment.checkout.promotion.placeholder')} disabled={applying} autoFocus slotProps={{ input: { endAdornment: ( ), }, }} sx={{ '& .MuiOutlinedInput-root': { pr: 1, borderRadius: '8px', height: 36 }, '& .MuiOutlinedInput-input': { py: '6px', fontSize: 13 }, }} /> {error && ( {error} )} ) : ( )} ); }