bookwiz.io / app / auth / complete-session / page.tsx
page.tsx
Raw
'use client'

import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import { supabase } from '@/lib/supabase'
import { SparklesIcon, CheckCircleIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline'
import { motion, AnimatePresence } from 'framer-motion'
import Image from 'next/image'

// Magical floating orbs component - matching login page
const MagicalOrbs = () => {
  return (
    <div className="absolute inset-0 overflow-hidden pointer-events-none">
      {[...Array(12)].map((_, i) => (
        <div
          key={i}
          className="absolute rounded-full animate-pulse"
          style={{
            left: `${Math.random() * 100}%`,
            top: `${Math.random() * 100}%`,
            width: `${4 + Math.random() * 8}px`,
            height: `${4 + Math.random() * 8}px`,
            background: `radial-gradient(circle, rgba(20, 184, 166, 0.6) 0%, rgba(6, 182, 212, 0.3) 50%, transparent 100%)`,
            animationDelay: `${Math.random() * 4}s`,
            animationDuration: `${3 + Math.random() * 2}s`,
          }}
        />
      ))}
    </div>
  )
}

// Enhanced loading spinner
const LoadingSpinner = () => (
  <div className="relative">
    <div className="animate-spin rounded-full h-12 w-12 border-2 border-teal-200/20">
      <div className="absolute inset-0 rounded-full border-2 border-transparent border-t-teal-400 animate-spin"></div>
    </div>
    <div className="absolute inset-0 rounded-full border-2 border-transparent border-r-cyan-400 animate-spin animation-delay-75"></div>
  </div>
)

export default function CompleteSessionPage() {
  const router = useRouter()
  const [status, setStatus] = useState<'processing' | 'success' | 'error'>('processing')
  const [error, setError] = useState<string>('')
  const [progress, setProgress] = useState(0)

  useEffect(() => {
    // Simulate progress for better UX
    const progressInterval = setInterval(() => {
      setProgress(prev => {
        if (prev >= 90) return prev
        return prev + Math.random() * 15
      })
    }, 100)

    const completeSession = async () => {
      try {
        // Get tokens from sessionStorage
        const tokensString = sessionStorage.getItem('supabase_auth_tokens')
        
        if (!tokensString) {
          throw new Error('No authentication tokens found')
        }

        const tokens = JSON.parse(tokensString)
        
        if (!tokens.access_token || !tokens.refresh_token) {
          throw new Error('Invalid token data')
        }

        console.log('Setting up session with tokens...')
        setProgress(70)

        // Set the session in Supabase
        const { data, error: sessionError } = await supabase.auth.setSession({
          access_token: tokens.access_token,
          refresh_token: tokens.refresh_token
        })

        if (sessionError) {
          throw new Error(`Session setup failed: ${sessionError.message}`)
        }

        if (data.session) {
          console.log('Session established successfully for:', data.user?.email)
          
          // Clean up stored tokens
          sessionStorage.removeItem('supabase_auth_tokens')
          
          setProgress(100)
          clearInterval(progressInterval)
          setStatus('success')
          
          // Redirect to dashboard after a brief delay
          setTimeout(() => {
            router.push('/dashboard')
          }, 1500)
        } else {
          throw new Error('No session created despite valid tokens')
        }

      } catch (err) {
        clearInterval(progressInterval)
        console.error('Session completion failed:', err)
        setError(err instanceof Error ? err.message : 'Unknown error')
        setStatus('error')
        
        // Clean up stored tokens on error
        sessionStorage.removeItem('supabase_auth_tokens')
        
        // Redirect to error page after delay
        setTimeout(() => {
          router.push(`/auth/auth-code-error?error=session_setup_failed&description=${encodeURIComponent(error)}`)
        }, 3000)
      }
    }

    // Add a small delay for better UX
    setTimeout(() => {
      completeSession()
    }, 500)

    return () => clearInterval(progressInterval)
  }, [router, error])



  return (
    <div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 relative overflow-hidden">
      <MagicalOrbs />
      <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-teal-500/5 via-transparent to-transparent" />
      
      <div className="max-w-md w-full mx-4">
        <AnimatePresence mode="wait">
          <motion.div
            key={status}
            initial={{ opacity: 0, scale: 0.95 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={{ opacity: 0, scale: 0.95 }}
            transition={{ duration: 0.5 }}
            className="w-full bg-white/10 dark:bg-slate-800/30 backdrop-blur-xl rounded-2xl shadow-2xl border border-white/20 dark:border-teal-600/20 p-8 text-center"
            style={{
              boxShadow: '0 0 30px rgba(15, 118, 110, 0.15), 0 0 60px rgba(124, 58, 237, 0.1)'
            }}
          >
            
            {status === 'processing' && (
              <>
                <motion.div 
                  className="flex justify-center mb-6"
                  initial={{ scale: 0, rotate: -180 }}
                  animate={{ scale: 1, rotate: 0 }}
                  transition={{ delay: 0.2, duration: 0.5, type: "spring", stiffness: 260, damping: 20 }}
                >
                  <LoadingSpinner />
                </motion.div>

                <motion.div
                  initial={{ opacity: 0, y: 10 }}
                  animate={{ opacity: 1, y: 0 }}
                  transition={{ delay: 0.3, duration: 0.5 }}
                >
                  <div className="flex justify-center mb-4">
                    <Image 
                      src="/images/logo-glyph-white.png" 
                      alt="Bookwiz Glyph" 
                      width={32}
                      height={32}
                      className="opacity-80"
                      style={{ filter: 'drop-shadow(0 0 8px rgba(124, 58, 237, 0.4))' }}
                    />
                  </div>
                  
                  <h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
                    Completing Sign-In
                  </h1>
                  <p className="text-gray-600 dark:text-gray-300 mb-6">
                    Setting up your magical session...
                  </p>

                  {/* Progress bar */}
                  <div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2 mb-4 overflow-hidden">
                    <motion.div 
                      className="h-2 bg-gradient-to-r from-teal-500 to-cyan-500 rounded-full"
                      initial={{ width: 0 }}
                      animate={{ width: `${progress}%` }}
                      transition={{ duration: 0.3 }}
                    />
                  </div>
                  
                  <p className="text-sm text-gray-500 dark:text-gray-400">
                    {progress < 30 ? 'Authenticating...' : 
                     progress < 70 ? 'Validating tokens...' : 
                     progress < 95 ? 'Creating session...' : 'Almost ready...'}
                  </p>
                </motion.div>
              </>
            )}

            {status === 'success' && (
              <>
                <motion.div 
                  className="flex justify-center mb-6"
                  initial={{ scale: 0, rotate: -180 }}
                  animate={{ scale: 1, rotate: 0 }}
                  transition={{ delay: 0.2, duration: 0.5, type: "spring", stiffness: 260, damping: 20 }}
                >
                  <CheckCircleIcon className="h-12 w-12 text-green-500" />
                </motion.div>

                <motion.div
                  initial={{ opacity: 0, y: 10 }}
                  animate={{ opacity: 1, y: 0 }}
                  transition={{ delay: 0.3, duration: 0.5 }}
                >
                  <h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
                     Welcome to Bookwiz!
                  </h1>
                  <p className="text-gray-600 dark:text-gray-300 mb-4">
                    Your account is ready. Redirecting to your spellbooks...
                  </p>
                  
                  <div className="flex justify-center">
                    <div className="animate-pulse text-teal-500">
                      <SparklesIcon className="h-6 w-6" />
                    </div>
                  </div>
                </motion.div>
              </>
            )}

            {status === 'error' && (
              <>
                <motion.div 
                  className="flex justify-center mb-6"
                  initial={{ scale: 0, rotate: -180 }}
                  animate={{ scale: 1, rotate: 0 }}
                  transition={{ delay: 0.2, duration: 0.5, type: "spring", stiffness: 260, damping: 20 }}
                >
                  <ExclamationTriangleIcon className="h-12 w-12 text-red-500" />
                </motion.div>

                <motion.div
                  initial={{ opacity: 0, y: 10 }}
                  animate={{ opacity: 1, y: 0 }}
                  transition={{ delay: 0.3, duration: 0.5 }}
                >
                  <h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">
                    🚫 Session Setup Failed
                  </h1>
                  <p className="text-gray-600 dark:text-gray-300 mb-4">
                    There was an issue completing your sign-in:
                  </p>
                  <div className="text-sm text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 rounded-lg p-3 mb-4">
                    {error}
                  </div>
                  <p className="text-xs text-gray-500 dark:text-gray-400">
                    Redirecting to error page...
                  </p>
                </motion.div>
              </>
            )}

          </motion.div>
        </AnimatePresence>
      </div>
    </div>
  )
}