bookwiz.io / app / dashboard / billing / page.tsx
page.tsx
Raw
'use client'

import { CheckCircleIcon, XCircleIcon, SparklesIcon, CreditCardIcon, ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'
import { useSubscription } from '@/lib/hooks/useSubscription'
import { usePortalSession } from '@/lib/hooks/usePortalSession'
import DashboardLayout from '@/components/dashboard/DashboardLayout'
import { formatDate, formatPrice } from '@/lib/stripe'
import { useState, useEffect } from 'react'
import { useSearchParams } from 'next/navigation'
import Link from 'next/link'

export default function DashboardBillingPage() {
  const { 
    subscription, 
    isActive, 
    isPastDue, 
    isCanceled, 
    willCancelAtPeriodEnd,
    getCurrentPlan,
    canManageBilling,
    refreshAfterCheckout
  } = useSubscription()
  
  const { createPortalSession, loading: portalLoading } = usePortalSession()
  const [isManaging, setIsManaging] = useState(false)
  const searchParams = useSearchParams()

  // Refresh subscription data if this is a successful checkout redirect
  useEffect(() => {
    const isSuccess = searchParams.get('success') === 'true'
    if (isSuccess) {
      console.log('Detected successful checkout, refreshing subscription data...')
      refreshAfterCheckout()
    }
  }, [searchParams, refreshAfterCheckout])

  const currentPlan = getCurrentPlan()

  // Determine if this is annual billing based on price_id
  const isAnnualBilling = subscription?.price_id ? 
    currentPlan?.pricing.annually.priceId === subscription.price_id : false

  // Get the appropriate price to display
  const getCurrentPrice = () => {
    if (!currentPlan) return null
    if (isAnnualBilling) {
      return currentPlan.pricing.annually.price / 12 // Show monthly equivalent
    }
    return currentPlan.pricing.monthly.price
  }

  const getStatusBadge = () => {
    if (isActive) {
      return <span className="px-3 py-1 text-xs font-medium bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 rounded-full backdrop-blur-sm">Active</span>
    }
    if (isPastDue) {
      return <span className="px-3 py-1 text-xs font-medium bg-amber-500/20 text-amber-300 border border-amber-500/30 rounded-full backdrop-blur-sm">Past Due</span>
    }
    if (isCanceled) {
      return <span className="px-3 py-1 text-xs font-medium bg-red-500/20 text-red-300 border border-red-500/30 rounded-full backdrop-blur-sm">Canceled</span>
    }
    return <span className="px-3 py-1 text-xs font-medium bg-gray-500/20 text-gray-300 border border-gray-500/30 rounded-full backdrop-blur-sm">Free</span>
  }

  const handleManageSubscription = async () => {
    try {
      console.log('Button clicked! Starting portal session...')
      setIsManaging(true)
      await createPortalSession()
      console.log('Portal session completed successfully')
    } catch (error) {
      console.error('Failed to open customer portal:', error)
      // Show user-friendly error
      alert(`Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`)
    } finally {
      setIsManaging(false)
    }
  }

  const currentPrice = getCurrentPrice()

  return (
    <DashboardLayout 
      title="Billing & Plans" 
      subtitle="Manage your subscription and billing information"
    >

      <div className="space-y-8">
        {/* Current Plan */}
        <div className="bg-gray-900/40 backdrop-blur-sm border border-gray-800/50 rounded-2xl overflow-hidden">
          <div className="px-4 sm:px-6 lg:px-8 py-6 lg:py-8 bg-gradient-to-r from-teal-500/10 to-cyan-500/10 border-b border-gray-800/50">
            <div className="flex flex-col space-y-4 lg:flex-row lg:items-center lg:justify-between lg:space-y-0">
              <div className="flex flex-col space-y-4 sm:flex-row sm:items-center sm:space-y-0 sm:space-x-4 lg:space-x-6">
                <div className="p-3 sm:p-4 bg-gradient-to-br from-teal-500/20 to-cyan-500/20 rounded-xl border border-teal-500/30 self-start">
                  <SparklesIcon className="h-6 w-6 sm:h-8 sm:w-8 text-teal-400" />
                </div>
                <div className="min-w-0 flex-1">
                  <h2 className="text-xl sm:text-2xl lg:text-3xl font-bold text-white mb-2">
                    {currentPlan ? currentPlan.name : 'Free Plan'}
                  </h2>
                  <div className="flex flex-col space-y-2 sm:flex-row sm:items-center sm:space-y-0 sm:space-x-3">
                    <span className="text-gray-300 text-sm sm:text-base">Your current subscription plan</span>
                    {getStatusBadge()}
                  </div>
                  {subscription?.current_period_end && (
                    <p className="text-gray-400 text-xs sm:text-sm mt-2">
                      {willCancelAtPeriodEnd ? 'Cancels' : 'Renews'} on {formatDate(subscription.current_period_end)}
                      {isAnnualBilling && <span className="ml-2 px-2 py-1 bg-teal-500/20 text-teal-300 rounded-md text-xs">(Annual Plan)</span>}
                    </p>
                  )}
                </div>
              </div>
              <div className="flex flex-col items-start lg:items-end space-y-4">
                {currentPrice ? (
                  <div className="text-left lg:text-right">
                    <span className="text-2xl sm:text-3xl lg:text-4xl font-bold text-white">{formatPrice(currentPrice)}</span>
                    <span className="text-gray-300 text-base sm:text-lg">/month</span>
                    {isAnnualBilling && (
                      <div className="text-xs sm:text-sm text-gray-400 mt-1">
                        ({formatPrice(currentPlan!.pricing.annually.price)}/year)
                      </div>
                    )}
                  </div>
                ) : (
                  <div className="text-left lg:text-right">
                    <span className="text-2xl sm:text-3xl lg:text-4xl font-bold text-white">$0</span>
                    <span className="text-gray-300 text-base sm:text-lg">/month</span>
                  </div>
                )}
                <div className="flex flex-col sm:flex-row gap-3 w-full lg:w-auto">
                  <a 
                    href="/pricing" 
                    className="px-4 sm:px-6 py-2.5 sm:py-3 text-xs sm:text-sm font-medium text-white bg-gradient-to-r from-teal-500 to-cyan-500 hover:from-teal-600 hover:to-cyan-600 rounded-xl transition-all duration-200 shadow-lg hover:shadow-xl text-center"
                  >
                    {currentPlan ? 'Change Plan' : 'Upgrade Plan'}
                  </a>
                  {canManageBilling() && (
                    <button 
                      onClick={handleManageSubscription}
                      disabled={isManaging || portalLoading}
                      className="px-4 sm:px-6 py-2.5 sm:py-3 text-xs sm:text-sm font-medium text-gray-300 bg-gray-800/50 hover:bg-gray-700/50 border border-gray-700/50 rounded-xl transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
                    >
                      <CreditCardIcon className="w-4 h-4" />
                      {isManaging || portalLoading ? 'Loading...' : 'Manage Subscription'}
                      <ArrowTopRightOnSquareIcon className="w-3 h-3" />
                    </button>
                  )}
                </div>
              </div>
            </div>
          </div>

          {/* Subscription Details */}
          {subscription && (
            <div className="px-4 sm:px-6 lg:px-8 py-6 border-b border-gray-800/50">
              <h3 className="text-lg font-semibold text-white mb-4">Subscription Details</h3>
              <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
                <div className="space-y-2">
                  <div className="text-sm text-gray-400">Billing Cycle</div>
                  <div className="text-white font-medium">
                    {isAnnualBilling ? 'Annual' : 'Monthly'}
                  </div>
                </div>
                <div className="space-y-2">
                  <div className="text-sm text-gray-400">Period Start</div>
                  <div className="text-white font-medium">
                    {formatDate(subscription.current_period_start)}
                  </div>
                </div>
                <div className="space-y-2">
                  <div className="text-sm text-gray-400">Period End</div>
                  <div className="text-white font-medium">
                    {formatDate(subscription.current_period_end)}
                  </div>
                </div>
                <div className="space-y-2">
                  <div className="text-sm text-gray-400">Status</div>
                  <div className="font-medium">
                    {getStatusBadge()}
                  </div>
                </div>
              </div>
            </div>
          )}

          {/* Plan Features */}
          <div className="px-4 sm:px-6 lg:px-8 py-6 lg:py-8">
            <h3 className="text-lg sm:text-xl font-bold text-white mb-4 sm:mb-6">Plan Features</h3>
            <div className="grid grid-cols-1 sm:grid-cols-2 gap-3 sm:gap-4">
              {currentPlan ? (
                currentPlan.features.map((feature, index) => (
                  <div key={index} className="flex items-center text-gray-300 text-sm sm:text-base">
                    <CheckCircleIcon className="w-4 h-4 sm:w-5 sm:h-5 mr-2 sm:mr-3 text-emerald-400 flex-shrink-0" />
                    <span>{feature}</span>
                  </div>
                ))
              ) : (
                <>
                  <div className="flex items-center text-gray-300 text-sm sm:text-base">
                    <CheckCircleIcon className="w-4 h-4 sm:w-5 sm:h-5 mr-2 sm:mr-3 text-emerald-400 flex-shrink-0" />
                    <span>Unlimited writing projects</span>
                  </div>
                  <div className="flex items-center text-gray-300 text-sm sm:text-base">
                    <CheckCircleIcon className="w-4 h-4 sm:w-5 sm:h-5 mr-2 sm:mr-3 text-emerald-400 flex-shrink-0" />
                    <span>Basic AI assistance</span>
                  </div>
                  <div className="flex items-center text-gray-400 text-sm sm:text-base">
                    <XCircleIcon className="w-4 h-4 sm:w-5 sm:h-5 mr-2 sm:mr-3 text-gray-500 flex-shrink-0" />
                    <span>🧠 Smart AI features</span>
                  </div>
                  <div className="flex items-center text-gray-400 text-sm sm:text-base">
                    <XCircleIcon className="w-4 h-4 sm:w-5 sm:h-5 mr-2 sm:mr-3 text-gray-500 flex-shrink-0" />
                    <span>Priority support</span>
                  </div>
                </>
              )}
            </div>

            {/* Subscription Status Messages */}
            {willCancelAtPeriodEnd && (
              <div className="mt-6 sm:mt-8 p-4 sm:p-6 bg-amber-500/10 border border-amber-500/30 rounded-xl backdrop-blur-sm">
                <h4 className="text-amber-300 font-semibold mb-2 flex items-center text-sm sm:text-base">
                  <XCircleIcon className="w-4 h-4 sm:w-5 sm:h-5 mr-2 flex-shrink-0" />
                  Subscription Ending
                </h4>
                <p className="text-amber-200 text-xs sm:text-sm">
                  Your subscription will end on {formatDate(subscription!.current_period_end)}. 
                  You'll lose access to premium features after this date.
                </p>
              </div>
            )}

            {isPastDue && (
              <div className="mt-6 sm:mt-8 p-4 sm:p-6 bg-red-500/10 border border-red-500/30 rounded-xl backdrop-blur-sm">
                <h4 className="text-red-300 font-semibold mb-2 flex items-center text-sm sm:text-base">
                  <XCircleIcon className="w-4 h-4 sm:w-5 sm:h-5 mr-2 flex-shrink-0" />
                  Payment Issue
                </h4>
                <p className="text-red-200 text-xs sm:text-sm">
                  There's an issue with your payment method. Please update your payment information to continue using premium features.
                </p>
              </div>
            )}
          </div>
        </div>

        {/* Usage Link */}
        <div className="bg-gray-900/40 backdrop-blur-sm border border-gray-800/50 rounded-2xl p-6">
          <div className="flex items-center justify-between">
            <div>
              <h3 className="text-lg font-bold text-white mb-2">Usage Analytics</h3>
              <p className="text-gray-400 text-sm">Track your AI model usage and consumption patterns</p>
            </div>
            <Link
              href="/dashboard/usage"
              className="px-4 py-2 text-sm font-medium text-teal-300 bg-teal-500/10 hover:bg-teal-500/20 border border-teal-500/30 rounded-lg transition-all duration-200 flex items-center gap-2"
            >
              View Usage Analytics 
            </Link>
          </div>
        </div>
      </div>
    </DashboardLayout>
  )
}