feat: pricing and welcome CTAs now redirect to /checkout page
This commit is contained in:
@@ -2,14 +2,12 @@
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { logger } from '@/lib/utils/logger'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import { useSearchParams, useRouter } from 'next/navigation'
|
||||
import { motion } from 'framer-motion'
|
||||
import { Button, CheckCircleIcon } from '@ciphera-net/ui'
|
||||
import { useAuth } from '@/lib/auth/context'
|
||||
import { initiateOAuthFlow } from '@/lib/api/oauth'
|
||||
import { toast } from '@ciphera-net/ui'
|
||||
import { COUNTRY_OPTIONS } from '@/lib/countries'
|
||||
import { createCheckoutSession } from '@/lib/api/billing'
|
||||
|
||||
// 1. Define Plans with IDs and Site Limits
|
||||
const PLANS = [
|
||||
@@ -103,20 +101,12 @@ const TRAFFIC_TIERS = [
|
||||
},
|
||||
]
|
||||
|
||||
const PRICING_COUNTRY_OPTIONS = [
|
||||
...COUNTRY_OPTIONS.map((c) => ({ code: c.value, label: c.label })),
|
||||
{ code: 'OTHER', label: 'Other' },
|
||||
]
|
||||
|
||||
export default function PricingSection() {
|
||||
const searchParams = useSearchParams()
|
||||
const router = useRouter()
|
||||
const [isYearly, setIsYearly] = useState(false)
|
||||
const [sliderIndex, setSliderIndex] = useState(2) // Default to 100k (index 2)
|
||||
const [loadingPlan, setLoadingPlan] = useState<string | null>(null)
|
||||
const [checkoutCountry, setCheckoutCountry] = useState('')
|
||||
const [checkoutVatId, setCheckoutVatId] = useState('')
|
||||
const [showCheckoutForm, setShowCheckoutForm] = useState(false)
|
||||
const [pendingCheckout, setPendingCheckout] = useState<{ planId: string; interval: string; limit: number } | null>(null)
|
||||
const { user } = useAuth()
|
||||
|
||||
// * Show toast when redirected from Mollie Checkout with canceled=true
|
||||
@@ -176,7 +166,7 @@ export default function PricingSection() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubscribe = async (planId: string, options?: { interval?: string, limit?: number }) => {
|
||||
const handleSubscribe = (planId: string, options?: { interval?: string, limit?: number }) => {
|
||||
// 1. If not logged in, redirect to login/signup
|
||||
if (!user) {
|
||||
const intent = {
|
||||
@@ -191,49 +181,10 @@ export default function PricingSection() {
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Show checkout form to collect country + optional VAT
|
||||
const interval = options?.interval || (isYearly ? 'year' : 'month')
|
||||
const limit = options?.limit || currentTraffic.value
|
||||
setPendingCheckout({ planId, interval, limit })
|
||||
setCheckoutCountry('')
|
||||
setCheckoutVatId('')
|
||||
setShowCheckoutForm(true)
|
||||
}
|
||||
|
||||
const handleCheckoutSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (!pendingCheckout || !checkoutCountry) return
|
||||
|
||||
try {
|
||||
setLoadingPlan(pendingCheckout.planId)
|
||||
|
||||
const { url } = await createCheckoutSession({
|
||||
plan_id: pendingCheckout.planId,
|
||||
interval: pendingCheckout.interval,
|
||||
limit: pendingCheckout.limit,
|
||||
country: checkoutCountry,
|
||||
vat_id: checkoutVatId || undefined,
|
||||
})
|
||||
|
||||
if (url) {
|
||||
window.location.href = url
|
||||
} else {
|
||||
throw new Error('No checkout URL returned')
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
logger.error('Checkout error:', error)
|
||||
toast.error('Failed to start checkout — please try again')
|
||||
} finally {
|
||||
setLoadingPlan(null)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCheckoutCancel = () => {
|
||||
setShowCheckoutForm(false)
|
||||
setPendingCheckout(null)
|
||||
setCheckoutCountry('')
|
||||
setCheckoutVatId('')
|
||||
setLoadingPlan(null)
|
||||
// 2. Navigate to embedded checkout page
|
||||
const selectedInterval = options?.interval || (isYearly ? 'year' : 'month')
|
||||
const selectedLimit = options?.limit || currentTraffic.value
|
||||
router.push(`/checkout?plan=${planId}&interval=${selectedInterval}&limit=${selectedLimit}`)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -463,84 +414,6 @@ export default function PricingSection() {
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Checkout Country / VAT Modal */}
|
||||
{showCheckoutForm && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center px-4">
|
||||
<div
|
||||
className="absolute inset-0 bg-black/60"
|
||||
onClick={handleCheckoutCancel}
|
||||
/>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className="relative w-full max-w-md border border-neutral-800 rounded-2xl bg-neutral-900 p-6 shadow-xl"
|
||||
>
|
||||
<h3 className="text-lg font-bold text-white mb-1">
|
||||
Billing details
|
||||
</h3>
|
||||
<p className="text-sm text-neutral-400 mb-6">
|
||||
Select your country to calculate the correct tax rate.
|
||||
</p>
|
||||
|
||||
<form onSubmit={handleCheckoutSubmit} className="space-y-4">
|
||||
{/* Country */}
|
||||
<div>
|
||||
<label htmlFor="checkout-country" className="block text-sm font-medium text-neutral-300 mb-1.5">
|
||||
Country <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<select
|
||||
id="checkout-country"
|
||||
required
|
||||
value={checkoutCountry}
|
||||
onChange={(e) => setCheckoutCountry(e.target.value)}
|
||||
className="w-full rounded-lg border border-neutral-700 bg-neutral-800 px-3 py-2.5 text-sm text-white placeholder-neutral-500 focus:border-brand-orange focus:outline-none focus:ring-1 focus:ring-brand-orange transition-colors"
|
||||
>
|
||||
<option value="" disabled>Select a country</option>
|
||||
{PRICING_COUNTRY_OPTIONS.map((c) => (
|
||||
<option key={c.code} value={c.code}>{c.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* VAT ID */}
|
||||
<div>
|
||||
<label htmlFor="checkout-vat" className="block text-sm font-medium text-neutral-300 mb-1.5">
|
||||
VAT ID <span className="text-neutral-500">(optional)</span>
|
||||
</label>
|
||||
<input
|
||||
id="checkout-vat"
|
||||
type="text"
|
||||
value={checkoutVatId}
|
||||
onChange={(e) => setCheckoutVatId(e.target.value)}
|
||||
placeholder="e.g. BE0123456789"
|
||||
className="w-full rounded-lg border border-neutral-700 bg-neutral-800 px-3 py-2.5 text-sm text-white placeholder-neutral-500 focus:border-brand-orange focus:outline-none focus:ring-1 focus:ring-brand-orange transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-3 pt-2">
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={!checkoutCountry || !!loadingPlan}
|
||||
className="flex-1"
|
||||
>
|
||||
{loadingPlan ? 'Loading...' : 'Continue to payment'}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={handleCheckoutCancel}
|
||||
className="flex-1"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</motion.div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user