diff --git a/src/app/(app)/pricing/page.tsx b/src/app/(app)/pricing/page.tsx deleted file mode 100644 index b2f0e77..0000000 --- a/src/app/(app)/pricing/page.tsx +++ /dev/null @@ -1,271 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/Card'; -import { Button } from '@/components/ui/Button'; -import { Badge } from '@/components/ui/Badge'; -import { showToast } from '@/components/ui/Toast'; -import { useRouter } from 'next/navigation'; -import { BillingToggle } from '@/components/ui/BillingToggle'; - -// Note: Metadata is defined in a separate metadata.ts file for client components -// or the parent layout should be updated to allow indexing for this specific page. - -export default function PricingPage() { - const router = useRouter(); - const [loading, setLoading] = useState(null); - const [currentPlan, setCurrentPlan] = useState('FREE'); - const [currentInterval, setCurrentInterval] = useState<'month' | 'year' | null>(null); - const [billingPeriod, setBillingPeriod] = useState<'month' | 'year'>('month'); - - useEffect(() => { - // Fetch current user plan - const fetchUserPlan = async () => { - try { - const response = await fetch('/api/user/plan'); - if (response.ok) { - const data = await response.json(); - setCurrentPlan(data.plan || 'FREE'); - setCurrentInterval(data.interval || null); - } - } catch (error) { - console.error('Error fetching user plan:', error); - } - }; - - fetchUserPlan(); - }, []); - - const handleUpgrade = async (plan: 'PRO' | 'BUSINESS') => { - setLoading(plan); - - try { - const response = await fetch('/api/stripe/create-checkout-session', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - plan, - billingInterval: billingPeriod === 'month' ? 'month' : 'year', - }), - }); - - if (!response.ok) { - throw new Error('Failed to create checkout session'); - } - - const { url } = await response.json(); - window.location.href = url; - } catch (error) { - console.error('Error creating checkout session:', error); - showToast('Failed to start checkout. Please try again.', 'error'); - setLoading(null); - } - }; - - const handleDowngrade = async () => { - // Show confirmation dialog - const confirmed = window.confirm( - 'Are you sure you want to downgrade to the Free plan? Your subscription will be canceled immediately and you will lose access to premium features.' - ); - - if (!confirmed) { - return; - } - - setLoading('FREE'); - - try { - const response = await fetch('/api/stripe/cancel-subscription', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(error.error || 'Failed to cancel subscription'); - } - - showToast('Successfully downgraded to Free plan', 'success'); - - // Refresh to update the plan - setTimeout(() => { - window.location.reload(); - }, 1500); - } catch (error: any) { - console.error('Error canceling subscription:', error); - showToast(error.message || 'Failed to downgrade. Please try again.', 'error'); - setLoading(null); - } - }; - - // Helper function to check if this is the user's exact current plan (plan + interval) - const isCurrentPlanWithInterval = (planType: string, interval: 'month' | 'year') => { - return currentPlan === planType && currentInterval === interval; - }; - - // Helper function to check if user has this plan but different interval - const hasPlanDifferentInterval = (planType: string) => { - return currentPlan === planType && currentInterval && currentInterval !== billingPeriod; - }; - - const selectedInterval = billingPeriod === 'month' ? 'month' : 'year'; - - const plans = [ - { - key: 'free', - name: 'Free', - price: '€0', - period: 'forever', - showDiscount: false, - features: [ - '3 dynamic QR codes', - 'Unlimited static QR codes', - 'Basic scan tracking', - 'Standard QR design templates', - 'Download as SVG/PNG', - ], - buttonText: currentPlan === 'FREE' ? 'Current Plan' : 'Downgrade to Free', - buttonVariant: 'outline' as const, - disabled: currentPlan === 'FREE', - popular: false, - onDowngrade: handleDowngrade, - }, - { - key: 'pro', - name: 'Pro', - price: billingPeriod === 'month' ? '€9' : '€90', - period: billingPeriod === 'month' ? 'per month' : 'per year', - showDiscount: billingPeriod === 'year', - features: [ - '50 dynamic QR codes', - 'Unlimited static QR codes', - 'Advanced analytics (scans, devices, locations)', - 'Custom branding (colors & logos)', - ], - buttonText: isCurrentPlanWithInterval('PRO', selectedInterval) - ? 'Current Plan' - : hasPlanDifferentInterval('PRO') - ? `Switch to ${billingPeriod === 'month' ? 'Monthly' : 'Yearly'}` - : 'Upgrade to Pro', - buttonVariant: 'primary' as const, - disabled: isCurrentPlanWithInterval('PRO', selectedInterval), - popular: true, - onUpgrade: () => handleUpgrade('PRO'), - }, - { - key: 'business', - name: 'Business', - price: billingPeriod === 'month' ? '€29' : '€290', - period: billingPeriod === 'month' ? 'per month' : 'per year', - showDiscount: billingPeriod === 'year', - features: [ - '500 dynamic QR codes', - 'Unlimited static QR codes', - 'Everything from Pro', - 'Bulk QR Creation (up to 1,000)', - 'Priority email support', - 'Advanced tracking & insights', - ], - buttonText: isCurrentPlanWithInterval('BUSINESS', selectedInterval) - ? 'Current Plan' - : hasPlanDifferentInterval('BUSINESS') - ? `Switch to ${billingPeriod === 'month' ? 'Monthly' : 'Yearly'}` - : 'Upgrade to Business', - buttonVariant: 'primary' as const, - disabled: isCurrentPlanWithInterval('BUSINESS', selectedInterval), - popular: false, - onUpgrade: () => handleUpgrade('BUSINESS'), - }, - ]; - - return ( -
-
-

- Choose Your Plan -

-

- Select the perfect plan for your QR code needs -

-
- -
- -
- -
- {plans.map((plan) => ( - - {plan.popular && ( -
- - Most Popular - -
- )} - - - - {plan.name} - -
-
- - {plan.price} - - - {plan.period} - -
- {plan.showDiscount && ( - - Save 16% - - )} -
-
- - -
    - {plan.features.map((feature: string, index: number) => ( -
  • - - - - {feature} -
  • - ))} -
- - -
-
- ))} -
- -
-

- All plans include unlimited static QR codes and basic customization. -

-

- Need help choosing? Contact our team -

-
-
- ); -} diff --git a/src/app/vcard/page.tsx b/src/app/vcard/page.tsx index 9caad7c..181c168 100644 --- a/src/app/vcard/page.tsx +++ b/src/app/vcard/page.tsx @@ -1,9 +1,9 @@ 'use client'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, Suspense } from 'react'; import { useSearchParams } from 'next/navigation'; -export default function VCardPage() { +function VCardContent() { const searchParams = useSearchParams(); const [isLoading, setIsLoading] = useState(true); const [firstName, setFirstName] = useState(''); @@ -272,3 +272,26 @@ END:VCARD`; ); } + +export default function VCardPage() { + return ( + +
+
👤
+

Loading...

+
+ + }> + +
+ ); +} +