fix: checkout UX — no auto-select payment method, stable price table during loading, add spacing before submit button
This commit is contained in:
@@ -57,7 +57,7 @@ const mollieFieldBase =
|
|||||||
export default function PaymentForm({ plan, interval, limit, country, vatId }: PaymentFormProps) {
|
export default function PaymentForm({ plan, interval, limit, country, vatId }: PaymentFormProps) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const [selectedMethod, setSelectedMethod] = useState('card')
|
const [selectedMethod, setSelectedMethod] = useState('')
|
||||||
const [mollieReady, setMollieReady] = useState(false)
|
const [mollieReady, setMollieReady] = useState(false)
|
||||||
const [mollieError, setMollieError] = useState(false)
|
const [mollieError, setMollieError] = useState(false)
|
||||||
const [formError, setFormError] = useState<string | null>(null)
|
const [formError, setFormError] = useState<string | null>(null)
|
||||||
@@ -141,6 +141,11 @@ export default function PaymentForm({ plan, interval, limit, country, vatId }: P
|
|||||||
setSubmitted(true)
|
setSubmitted(true)
|
||||||
setFormError(null)
|
setFormError(null)
|
||||||
|
|
||||||
|
if (!selectedMethod) {
|
||||||
|
setFormError('Please select a payment method')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!country) {
|
if (!country) {
|
||||||
setFormError('Please select your country')
|
setFormError('Please select your country')
|
||||||
return
|
return
|
||||||
@@ -319,8 +324,8 @@ export default function PaymentForm({ plan, interval, limit, country, vatId }: P
|
|||||||
{/* Submit */}
|
{/* Submit */}
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submitting || (isCard && !mollieReady && !mollieError)}
|
disabled={submitting || !selectedMethod || (isCard && !mollieReady && !mollieError)}
|
||||||
className="w-full rounded-lg bg-brand-orange px-4 py-3 text-sm font-semibold text-white transition-colors hover:bg-brand-orange/90 disabled:opacity-50 disabled:cursor-not-allowed"
|
className="mt-4 w-full rounded-lg bg-brand-orange px-4 py-3 text-sm font-semibold text-white transition-colors hover:bg-brand-orange/90 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{submitting ? 'Processing...' : 'Start free trial'}
|
{submitting ? 'Processing...' : 'Start free trial'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -185,13 +185,8 @@ export default function PlanSummary({ plan, interval, limit, country, vatId, onC
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Price breakdown */}
|
{/* Price breakdown */}
|
||||||
<div className="pt-2 border-t border-neutral-800">
|
<div className={`pt-2 border-t border-neutral-800 transition-opacity duration-200 ${vatLoading ? 'opacity-50' : 'opacity-100'}`}>
|
||||||
{vatLoading && !vatResult ? (
|
{vatResult ? (
|
||||||
<div className="flex items-center gap-2 text-sm text-neutral-400">
|
|
||||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-neutral-600 border-t-white" />
|
|
||||||
Calculating VAT...
|
|
||||||
</div>
|
|
||||||
) : vatResult ? (
|
|
||||||
<div className="space-y-1.5 text-sm">
|
<div className="space-y-1.5 text-sm">
|
||||||
<div className="flex justify-between text-neutral-400">
|
<div className="flex justify-between text-neutral-400">
|
||||||
<span>Subtotal ({tierLabel} pageviews)</span>
|
<span>Subtotal ({tierLabel} pageviews)</span>
|
||||||
@@ -220,15 +215,15 @@ export default function PlanSummary({ plan, interval, limit, country, vatId, onC
|
|||||||
<div className="space-y-1.5 text-sm">
|
<div className="space-y-1.5 text-sm">
|
||||||
<div className="flex justify-between text-neutral-400">
|
<div className="flex justify-between text-neutral-400">
|
||||||
<span>Subtotal ({tierLabel} pageviews)</span>
|
<span>Subtotal ({tierLabel} pageviews)</span>
|
||||||
<span>€{isYearly ? baseDisplay.toFixed(2) : baseDisplay.toFixed(2)}</span>
|
<span>€{baseDisplay.toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-neutral-500 text-xs">
|
<div className="flex justify-between text-neutral-500 text-xs">
|
||||||
<span>VAT</span>
|
<span>VAT</span>
|
||||||
<span>Select country</span>
|
<span>{vatLoading ? 'Calculating...' : 'Select country'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between font-semibold text-white pt-1 border-t border-neutral-800">
|
<div className="flex justify-between font-semibold text-white pt-1 border-t border-neutral-800">
|
||||||
<span>Total {isYearly ? '/year' : '/mo'} <span className="font-normal text-neutral-500 text-xs">excl. VAT</span></span>
|
<span>Total {isYearly ? '/year' : '/mo'} <span className="font-normal text-neutral-500 text-xs">excl. VAT</span></span>
|
||||||
<span>€{isYearly ? baseDisplay.toFixed(2) : baseDisplay.toFixed(2)}</span>
|
<span>€{baseDisplay.toFixed(2)}</span>
|
||||||
</div>
|
</div>
|
||||||
{isYearly && (
|
{isYearly && (
|
||||||
<p className="text-xs text-neutral-500">€{(baseDisplay / 12).toFixed(2)}/mo · Save 1 month</p>
|
<p className="text-xs text-neutral-500">€{(baseDisplay / 12).toFixed(2)}/mo · Save 1 month</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user