'use client'; import { HeroVideoSection } from '@/components/home/sections/hero-video-section'; import { siteConfig } from '@/lib/home'; import { ArrowRight, Github, X, AlertCircle } from 'lucide-react'; import { FlickeringGrid } from '@/components/home/ui/flickering-grid'; import { useMediaQuery } from '@/hooks/use-media-query'; import { useState, useEffect, useRef, FormEvent } from 'react'; import { useScroll } from 'motion/react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { useAuth } from '@/components/AuthProvider'; import { createProject, createThread, addUserMessage, startAgent, BillingError, } from '@/lib/api'; import { generateThreadName } from '@/lib/actions/threads'; import GoogleSignIn from '@/components/GoogleSignIn'; import { Input } from '@/components/ui/input'; import { SubmitButton } from '@/components/ui/submit-button'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogOverlay, } from '@/components/ui/dialog'; import { BillingErrorAlert } from '@/components/billing/usage-limit-alert'; import { useBillingError } from '@/hooks/useBillingError'; import { useAccounts } from '@/hooks/use-accounts'; import { isLocalMode, config } from '@/lib/config'; import { toast } from 'sonner'; import { useModal } from '@/hooks/use-modal-store'; // Custom dialog overlay with blur effect const BlurredDialogOverlay = () => ( ); // Constant for localStorage key to ensure consistency const PENDING_PROMPT_KEY = 'pendingAgentPrompt'; export function HeroSection() { const { hero } = siteConfig; const tablet = useMediaQuery('(max-width: 1024px)'); const [mounted, setMounted] = useState(false); const [isScrolling, setIsScrolling] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const scrollTimeout = useRef(null); const { scrollY } = useScroll(); const [inputValue, setInputValue] = useState(''); const router = useRouter(); const { user, isLoading } = useAuth(); const { billingError, handleBillingError, clearBillingError } = useBillingError(); const { data: accounts } = useAccounts(); const personalAccount = accounts?.find((account) => account.personal_account); const { onOpen } = useModal(); // Auth dialog state const [authDialogOpen, setAuthDialogOpen] = useState(false); const [authError, setAuthError] = useState(null); useEffect(() => { setMounted(true); }, []); // Detect when scrolling is active to reduce animation complexity useEffect(() => { const unsubscribe = scrollY.on('change', () => { setIsScrolling(true); // Clear any existing timeout if (scrollTimeout.current) { clearTimeout(scrollTimeout.current); } // Set a new timeout scrollTimeout.current = setTimeout(() => { setIsScrolling(false); }, 300); // Wait 300ms after scroll stops }); return () => { unsubscribe(); if (scrollTimeout.current) { clearTimeout(scrollTimeout.current); } }; }, [scrollY]); // Store the input value when auth dialog opens useEffect(() => { if (authDialogOpen && inputValue.trim()) { localStorage.setItem(PENDING_PROMPT_KEY, inputValue.trim()); } }, [authDialogOpen, inputValue]); // Close dialog and redirect when user authenticates useEffect(() => { if (authDialogOpen && user && !isLoading) { setAuthDialogOpen(false); router.push('/dashboard'); } }, [user, isLoading, authDialogOpen, router]); // Create an agent with the provided prompt const createAgentWithPrompt = async () => { if (!inputValue.trim() || isSubmitting) return; setIsSubmitting(true); try { // Generate a name for the project using GPT const projectName = await generateThreadName(inputValue); // 1. Create a new project with the GPT-generated name const newAgent = await createProject({ name: projectName, description: '', }); // 2. Create a new thread for this project const thread = await createThread(newAgent.id); // 3. Add the user message to the thread await addUserMessage(thread.thread_id, inputValue.trim()); // 4. Start the agent with the thread ID await startAgent(thread.thread_id, { stream: true, }); // 5. Navigate to the new agent's thread page router.push(`/agents/${thread.thread_id}`); // Clear input on success setInputValue(''); } catch (error: any) { console.error('Error creating agent:', error); // Check specifically for BillingError (402) if (error instanceof BillingError) { console.log('Handling BillingError from hero section:', error.detail); // Open the payment required dialog modal instead of showing the alert onOpen("paymentRequiredDialog"); // Don't show toast for billing errors } else { // Handle other errors (e.g., network, other API errors) const isConnectionError = error instanceof TypeError && error.message.includes('Failed to fetch'); if (!isLocalMode() || isConnectionError) { toast.error( error.message || 'Failed to create agent. Please try again.', ); } } } finally { setIsSubmitting(false); } }; // Handle form submission const handleSubmit = async (e?: FormEvent) => { if (e) { e.preventDefault(); e.stopPropagation(); // Stop event propagation to prevent dialog closing } if (!inputValue.trim() || isSubmitting) return; // If user is not logged in, save prompt and show auth dialog if (!user && !isLoading) { // Save prompt to localStorage BEFORE showing the dialog localStorage.setItem(PENDING_PROMPT_KEY, inputValue.trim()); setAuthDialogOpen(true); return; } // User is logged in, create the agent createAgentWithPrompt(); }; // Handle Enter key press const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault(); // Prevent default form submission e.stopPropagation(); // Stop event propagation handleSubmit(); } }; // Handle auth form submission const handleSignIn = async (prevState: any, formData: FormData) => { setAuthError(null); try { // Implement sign in logic here const email = formData.get('email') as string; const password = formData.get('password') as string; // Add the returnUrl to the form data for proper redirection formData.append('returnUrl', '/dashboard'); // Call your authentication function here // Return any error state return { message: 'Invalid credentials' }; } catch (error) { console.error('Sign in error:', error); setAuthError( error instanceof Error ? error.message : 'An error occurred', ); return { message: 'An error occurred during sign in' }; } }; return (
{/* Left side flickering grid with gradient fades */}
{/* Horizontal fade from left to right */}
{/* Vertical fade from top */}
{/* Vertical fade to bottom */}
{/* Right side flickering grid with gradient fades */}
{/* Horizontal fade from right to left */}
{/* Vertical fade from top */}
{/* Vertical fade to bottom */}
{/* Center content background with rounded bottom */}
{/*

{hero.badgeIcon} {hero.badge}

*/} {hero.badgeIcon} {hero.badge}

Suna , your AI Employee.

{hero.description}

{/* ChatGPT-like input with glow effect */}
setInputValue(e.target.value)} onKeyDown={handleKeyDown} placeholder={hero.inputPlaceholder} className="flex-1 h-12 md:h-14 rounded-full px-2 bg-transparent focus:outline-none text-sm md:text-base py-2" disabled={isSubmitting} />
{/* Subtle glow effect */}
{/* Auth Dialog */}
Sign in to continue {/* */}
Sign in or create an account to talk with Suna
{/* Auth error message */} {authError && (
{authError}
)} {/* Google Sign In */}
{/* Divider */}
or continue with email
{/* Sign in form */}
Sign in setAuthDialogOpen(false)} > Create new account
setAuthDialogOpen(false)} > More sign in options
By continuing, you agree to our{' '} Terms of Service {' '} and{' '} Privacy Policy
{/* Add Billing Error Alert here */}
); }