Skip to content

Commit

Permalink
Adding Cursor Landing Page
Browse files Browse the repository at this point in the history
  • Loading branch information
johnlindquist committed Jan 27, 2025
1 parent 4808064 commit e8cc5bb
Show file tree
Hide file tree
Showing 24 changed files with 1,421 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/pages/bootcamp/ai/components/Conclusion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use client'
import {motion} from 'framer-motion'
import {fadeInUp} from './animations'

export default function Conclusion() {
return (
<section className="py-32 relative">
<div className="container mx-auto px-4 text-center relative z-10">
<motion.div {...fadeInUp} className="max-w-3xl mx-auto">
<h2 className="mb-4 text-3xl font-bold text-center text-white">
Ready to Join Our AI Mastery Bootcamp?
</h2>
<p className="mb-8 text-center text-gray-400 mx-auto">
Secure your spot in this unique, 20-day immersive training
experience. You&apos;ll learn alongside a focused cohort of
developers, all committed to mastering practical AI implementation.
Enter your email below to be first in line when registration opens
for our next intensive.
</p>
</motion.div>
</div>
</section>
)
}
94 changes: 94 additions & 0 deletions src/pages/bootcamp/ai/components/Features.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use client'
import {motion} from 'framer-motion'
import {Cpu, Database, Users, Calendar, Shield, Cloud} from 'lucide-react'
import {fadeInUp, staggerContainer, staggerItem} from './animations'

const features = [
{
id: 'ai-integration',
title: 'Practical AI Integration',
description:
'Quickly integrate AI capabilities into new or existing applications using modern frameworks and tools.',
icon: Cpu,
},
{
id: 'data-handling',
title: 'Intelligent Data Handling',
description:
'Implement vector search and semantic querying to give your users quick, context-aware answers—no matter the data set.',
icon: Database,
},
{
id: 'multi-agent',
title: 'Multi-Agent Systems',
description:
'Build specialized agents (e.g., for support, data analysis, or automation) that work in tandem to serve different business needs.',
icon: Users,
},
{
id: 'accountability',
title: 'Daily Accountability',
description:
'Participate in 1-hour live workshop sessions plus evening assignments, ensuring steady progress. Get real-time feedback from John and fellow participants in a shared cohort environment.',
icon: Calendar,
},
{
id: 'security',
title: 'Testing & Security',
description:
'Learn best practices for automated testing of AI outputs and safeguarding sensitive data. Keep your enterprise environment secure and compliant.',
icon: Shield,
},
{
id: 'deployment',
title: 'Production-Ready Deployment',
description:
'Deploy your AI-driven solutions confidently—either to a cloud platform or Docker environment—with built-in logging and analytics for ongoing improvements.',
icon: Cloud,
},
]

export default function Features() {
return (
<section className="py-32 relative">
<div className="container mx-auto px-4 relative z-10">
<motion.h2
{...fadeInUp}
className="mb-16 text-3xl font-bold text-center text-white"
>
What You&apos;ll Learn
</motion.h2>
<motion.div
variants={staggerContainer}
initial="hidden"
whileInView="show"
viewport={{once: true}}
className="grid gap-8 md:grid-cols-2 lg:grid-cols-3"
>
{features.map((feature) => {
const Icon = feature.icon
return (
<motion.div
key={feature.id}
variants={staggerItem}
className="group relative bg-white dark:bg-gray-800 border-2 border-gray-200 dark:border-gray-800 rounded-lg p-6 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors drop-shadow-lg"
>
<div className="relative">
<div className="flex items-center mb-4 gap-2">
<Icon className="w-6 h-6 text-[var(--accent-9)]" />
<h3 className="text-lg font-semibold dark:text-white text-gray-900">
{feature.title}
</h3>
</div>
<p className="text-gray-500 dark:text-gray-400">
{feature.description}
</p>
</div>
</motion.div>
)
})}
</motion.div>
</div>
</section>
)
}
119 changes: 119 additions & 0 deletions src/pages/bootcamp/ai/components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'use client'
import Link from 'next/link'
import {motion, AnimatePresence} from 'framer-motion'
import {fadeInUp, scaleIn} from './animations'
import {useState, useEffect} from 'react'
import '../styles.css'
import {ArrowCircleDownIcon} from '@heroicons/react/solid'
import {useTheme} from 'next-themes'
import {cn} from '@/ui/utils'

const phrases = [
'Level Up Your Skills With',
'Join Our AI Deep Dive Using',
'Master Real-World Apps With',
'Transform Projects Using',
'Start Your AI Journey With',
'Join Our Hands-On Lab Using',
'Build Production Apps With',
'Power Your Future Using',
]

const AnimatedPhrase = ({text}: {text: string}) => (
<motion.span
initial={{opacity: 0, y: -20}}
animate={{opacity: 1, y: 0}}
exit={{opacity: 0, y: 20}}
transition={{duration: 0.5}}
className="absolute left-0 right-0"
>
{text}
</motion.span>
)

function scrollToSignup(e: React.MouseEvent<HTMLAnchorElement>) {
e.preventDefault()
document.querySelector('#signup')?.scrollIntoView({behavior: 'smooth'})
}

export default function Hero() {
const {theme} = useTheme()
const [phraseIndex, setPhraseIndex] = useState(0)

useEffect(() => {
const timer = setInterval(() => {
setPhraseIndex((current) => (current + 1) % phrases.length)
}, 3000)
return () => clearInterval(timer)
}, [])

return (
<section className="py-12 md:py-20 text-center relative overflow-hidden">
<div
className={cn(
'absolute inset-0',
theme === 'light' && 'pattern-dots-light',
theme === 'dark' && 'pattern-dots',
)}
/>
<motion.div {...scaleIn} className="relative max-w-4xl mx-auto px-4">
<motion.h1
{...fadeInUp}
className="relative mb-6 text-4xl font-extrabold tracking-tight dark:text-white sm:text-5xl md:text-6xl leading-tight"
>
<span className="relative h-[1.2em] block mb-2">
<AnimatePresence mode="wait">
<AnimatedPhrase key={phraseIndex} text={phrases[phraseIndex]} />
</AnimatePresence>
</span>
<span className="dark:text-gray-400 text-gray-800 drop-shadow-lg dark:drop-shadow-lg">
AI
</span>{' '}
in Just{' '}
<span className="dark:text-gray-400 text-gray-800 drop-shadow-lg dark:drop-shadow-lg">
20 Days
</span>
</motion.h1>

<motion.p
{...fadeInUp}
transition={{delay: 0.1}}
className="relative mb-8 text-lg md:text-xl text-gray-500 max-w-3xl mx-auto leading-relaxed"
>
Join{' '}
<span className="text-gray-900 dark:text-white font-medium">
John Lindquist
</span>
, founder of egghead.io, for an immersive, hands-on program that will
revolutionize your dev workflow. In just{' '}
<span className="text-gray-900 dark:text-white font-medium">
20 days
</span>
, you&apos;ll master building real-world AI applications that automate
the tedious, amplify your capabilities, and{' '}
<span className="text-gray-900 dark:text-white font-medium">
transform how your team ships software
</span>
.
</motion.p>

<motion.div
{...fadeInUp}
transition={{delay: 0.3, type: 'spring', stiffness: 200}}
className="relative"
>
<Link
href="#signup"
onClick={scrollToSignup}
className="group flex flex-col items-center justify-center w-fit mx-auto hover:cursor-pointer"
>
<p className="relative inline-flex items-center justify-center rounded-md bg-[var(--accent-9)] px-8 py-3 text-base font-semibold text-black dark:text-white transition-all duration-200 group-hover:bg-[var(--accent-10)] group-hover:scale-105 focus:outline-none focus:ring-2 focus:ring-[var(--accent-9)] focus:ring-offset-2 focus:ring-offset-gray-900">
Join the Waitlist
</p>
<ArrowCircleDownIcon className="group-hover:scale-105 w-8 h-8 transition-all duration-200" />
</Link>
</motion.div>
</motion.div>
</section>
)
}
47 changes: 47 additions & 0 deletions src/pages/bootcamp/ai/components/Instructor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use client'
import {motion} from 'framer-motion'
import Image from 'next/image'
import {fadeInUp} from './animations'

export default function Instructor() {
return (
<section className="py-32 relative">
<div className="container mx-auto px-4 text-center relative z-10">
<motion.div {...fadeInUp} className="max-w-3xl mx-auto">
<div className="mx-auto flex w-full max-w-3xl flex-col-reverse items-center justify-between gap-10 px-6 sm:gap-20 md:flex-row">
<div>
<h2 className="mb-8 text-3xl font-bold dark:text-white flex flex-col items-center sm:items-start">
<span className="font-heading dark:text-primary text-sm uppercase tracking-widest">
{' '}
Your Instructor
</span>
<span className="font-heading text-3xl font-semibold sm:text-4xl">
John Lindquist
</span>
</h2>
<p className="mb-8 text-lg text-gray-500 dark:text-gray-400 text-left bg-white dark:bg-gray-900/80">
John Lindquist is a recognized leader in developer education. He
founded egghead.io—an innovative platform that has guided
thousands of coders from novices to industry experts. With years
of practical teaching experience and a genuine passion for
helping others succeed, John will provide the clarity and
support you need to master AI development.
</p>
<p className="text-md text-gray-500 dark:text-gray-400 text-left bg-white dark:bg-gray-900/80">
Join John and a supportive community of developers as you build
the skills needed to create impactful AI solutions.
</p>
</div>
<Image
src="https://res.cloudinary.com/dg3gyk0gu/image/upload/v1683164538/assets/john.webp"
alt="John Lindquist"
width={300}
height={300}
className="rounded-lg"
/>
</div>
</motion.div>
</div>
</section>
)
}
103 changes: 103 additions & 0 deletions src/pages/bootcamp/ai/components/SignUpForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
'use client'

import {useState} from 'react'
import {Button} from './ui/button'
import {Input} from './ui/input'
import {motion} from 'framer-motion'
import useCio from '@/hooks/use-cio'
import {trpc} from '@/app/_trpc/client'

export default function SignUpForm() {
const [email, setEmail] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)
const {subscriber} = useCio()
const identify = trpc.customerIO.identify.useMutation({
onSuccess: (data) => {
console.log('IDENTIFY', data)
},
onError: (error) => {
console.log('ERROR', error)
},
})

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()

if (isSubmitting) return

setIsSubmitting(true)
let subscriberId = subscriber?.id

try {
console.log('Submitting email:', email)

const currentDateTime = Math.floor(Date.now() * 0.001) // Customer.io uses seconds with their UNIX epoch timestamps

if (!subscriberId) {
identify.mutateAsync({
email,
selectedInterests: {ai_bootcamp_waitlist: currentDateTime},
})
} else {
identify.mutateAsync({
id: subscriberId,
selectedInterests: {ai_bootcamp_waitlist: currentDateTime},
})
}

setEmail('')
console.log('Successfully submitted email')
} catch (error) {
console.error('Failed to submit:', error)
} finally {
setIsSubmitting(false)
}
}

return (
<section id="signup" className="py-32 relative">
<div className="container mx-auto px-4 relative z-10">
<motion.div
initial={{opacity: 1, y: 20}}
whileInView={{opacity: 1, y: 0}}
viewport={{once: true}}
className="max-w-2xl mx-auto"
>
<h2 className="mb-4 text-3xl font-bold text-center dark:text-white text-gray-900">
Ready to Build a Team of AI Devs?
</h2>
<p className="mb-8 text-center text-gray-500 dark:text-gray-400 mx-auto">
Secure your spot in this unique, 20-day cohort-based workshop.
You&apos;ll learn alongside a supportive community of developers,
all on the same journey to master AI. Enter your email below and be
the first to know when registration opens.
</p>
<form onSubmit={handleSubmit} className="max-w-md mx-auto">
<div className="flex space-x-2">
<Input
type="email"
placeholder="Enter your email to stay informed"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
disabled={isSubmitting}
className="flex-grow bg-gray-50 dark:bg-gray-800/50 dark:border-gray-800 border-gray-200 text-gray-900 dark:text-white placeholder:text-gray-500"
/>
<Button
type="submit"
disabled={isSubmitting}
className="bg-blue-500 text-white font-semibold transition-all duration-200 hover:scale-105 disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSubmitting ? 'Joining...' : 'Join Waitlist'}
</Button>
</div>
</form>
<p className="mt-4 text-center text-sm text-gray-500">
We&apos;ll send you all the details—no spam, just practical info on
how to join.
</p>
</motion.div>
</div>
</section>
)
}
Loading

0 comments on commit e8cc5bb

Please sign in to comment.