'use client' import { useState, useEffect } from 'react' import { useSearchParams } 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 { createCheckoutSession } from '@/lib/api/billing' // 1. Define Plans with IDs and Site Limits const PLANS = [ { id: 'solo', name: 'Solo', description: 'For personal sites and freelancers', features: [ '1 site', '1 year data retention', 'Email reports', '100% Data ownership' ] }, { id: 'team', name: 'Team', description: 'For startups and growing agencies', features: [ 'Up to 5 sites', '2 year data retention', 'Team dashboard', 'Shared links' ] }, { id: 'business', name: 'Business', description: 'For large organizations', features: [ 'Up to 10 sites', '3 years data retention', 'Priority support', 'Custom events' ] } ] // 2. Define Explicit Pricing per Tier (approx 20% cheaper than Plausible) // Includes intermediate steps: 50k, 250k, 2.5M const TRAFFIC_TIERS = [ { label: '10k', value: 10000, prices: { solo: 7, team: 11, business: 15 } }, { label: '50k', value: 50000, prices: { solo: 11, team: 19, business: 27 } }, { label: '100k', value: 100000, prices: { solo: 15, team: 23, business: 31 } }, { label: '250k', value: 250000, prices: { solo: 25, team: 39, business: 59 } }, { label: '500k', value: 500000, prices: { solo: 39, team: 59, business: 79 } }, { label: '1M', value: 1000000, prices: { solo: 55, team: 79, business: 111 } }, { label: '2.5M', value: 2500000, prices: { solo: 79, team: 119, business: 169 } }, { label: '5M', value: 5000000, prices: { solo: 103, team: 155, business: 207 } }, { label: '10M', value: 10000000, prices: { solo: 135, team: 199, business: 269 } }, { label: '10M+', value: 10000001, prices: { solo: null, team: null, business: null } }, ] export default function PricingSection() { const searchParams = useSearchParams() const [isYearly, setIsYearly] = useState(false) const [sliderIndex, setSliderIndex] = useState(2) // Default to 100k (index 2) const [loadingPlan, setLoadingPlan] = useState(null) const { user } = useAuth() // * Show toast when redirected from Stripe Checkout with canceled=true useEffect(() => { if (searchParams.get('canceled') === 'true') { toast.info('Checkout was canceled. You can try again whenever you’re ready.') const url = new URL(window.location.href) url.searchParams.delete('canceled') window.history.replaceState({}, '', url.pathname + url.search) } }, [searchParams]) // * Check for pending checkout on mount/auth useEffect(() => { if (!user) return const pendingCheckout = localStorage.getItem('pulse_pending_checkout') if (pendingCheckout) { try { const intent = JSON.parse(pendingCheckout) // Restore UI state if (typeof intent.sliderIndex === 'number') setSliderIndex(intent.sliderIndex) if (typeof intent.isYearly === 'boolean') setIsYearly(intent.isYearly) // Trigger checkout handleSubscribe(intent.planId, { interval: intent.interval, limit: intent.limit }) // Clear intent localStorage.removeItem('pulse_pending_checkout') } catch (e) { console.error('Failed to parse pending checkout', e) localStorage.removeItem('pulse_pending_checkout') } } }, [user]) const currentTraffic = TRAFFIC_TIERS[sliderIndex] // Helper to get all price details const getPriceDetails = (planId: string) => { const basePrice = currentTraffic.prices[planId as keyof typeof currentTraffic.prices] // Handle "Custom" if (basePrice === null || basePrice === undefined) return null const yearlyTotal = basePrice * 11 // 1 month free (pay for 11) const effectiveMonthly = Math.round(yearlyTotal / 12) return { baseMonthly: basePrice, yearlyTotal: yearlyTotal, effectiveMonthly: effectiveMonthly } } const handleSubscribe = async (planId: string, options?: { interval?: string, limit?: number }) => { try { setLoadingPlan(planId) // 1. If not logged in, redirect to login/signup if (!user) { // Store checkout intent const intent = { planId, interval: isYearly ? 'year' : 'month', limit: currentTraffic.value, sliderIndex, // Store UI state to restore it isYearly // Store UI state to restore it } localStorage.setItem('pulse_pending_checkout', JSON.stringify(intent)) initiateOAuthFlow() return } // 2. Call backend to create checkout session const interval = options?.interval || (isYearly ? 'year' : 'month') const limit = options?.limit || currentTraffic.value const { url } = await createCheckoutSession({ plan_id: planId, interval, limit, }) // 3. Redirect to Stripe Checkout if (url) { window.location.href = url } else { throw new Error('No checkout URL returned') } } catch (error: unknown) { console.error('Checkout error:', error) toast.error('Failed to start checkout — please try again') } finally { setLoadingPlan(null) } } return (

Transparent Pricing

Scale with your traffic. No hidden fees.

{/* Unified Container */} {/* Top Toolbar */}
10k Up to {currentTraffic.label} monthly pageviews 10M+
setSliderIndex(parseInt(e.target.value))} aria-label="Monthly pageview limit" aria-valuetext={`${currentTraffic.label} pageviews per month`} className="w-full h-2 bg-neutral-200 rounded-lg appearance-none cursor-pointer dark:bg-neutral-700 accent-brand-orange focus:outline-none focus:ring-2 focus:ring-brand-orange focus:ring-offset-2" />
Get 1 month free with yearly
{/* Pricing Grid */}
{PLANS.map((plan) => { const priceDetails = getPriceDetails(plan.id) const isTeam = plan.id === 'team' return (
{isTeam && ( <>
Most Popular )}

{plan.name}

{plan.description}

{priceDetails ? ( isYearly ? (
€{priceDetails.yearlyTotal} /year
€{priceDetails.baseMonthly}/mo €{priceDetails.effectiveMonthly}/mo
) : (
€{priceDetails.baseMonthly} /mo
) ) : (
Custom
)}
    {plan.features.map((feature) => (
  • {feature}
  • ))}
) })} {/* Enterprise Section */}

Enterprise

For high volume sites and custom needs

Custom
    {[ 'Everything in Business', '10+ sites', 'Unlimited team members', 'SLA & Priority Support', 'Managed Proxy', 'Raw data export' ].map((feature) => (
  • {feature}
  • ))}
) }