Skip to content

Commit

Permalink
feat(bootcamp): adding entire landing page with ugly styles
Browse files Browse the repository at this point in the history
  • Loading branch information
johnlindquist committed Jan 16, 2025
1 parent dc70eef commit fe0a25b
Show file tree
Hide file tree
Showing 14 changed files with 642 additions and 10 deletions.
5 changes: 5 additions & 0 deletions src/components/GridBackground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {FC} from 'react'

export const GridBackground: FC = () => (
<div className="absolute inset-0 bg-[linear-gradient(to_right,#ffffff06_1px,transparent_1px),linear-gradient(to_bottom,#ffffff06_1px,transparent_1px)] bg-[size:64px_64px]" />
)
24 changes: 24 additions & 0 deletions src/pages/bootcamp/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>
)
}
92 changes: 92 additions & 0 deletions src/pages/bootcamp/components/Features.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
'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="inline-flex items-center justify-center w-12 h-12 mb-4">
<Icon className="w-6 h-6 text-[var(--accent-9)]" />
</div>
<h3 className="text-lg font-semibold text-white mb-2">
{feature.title}
</h3>
<p className="text-gray-400">{feature.description}</p>
</div>
</motion.div>
)
})}
</motion.div>
</div>
</section>
)
}
95 changes: 95 additions & 0 deletions src/pages/bootcamp/components/Hero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
'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'

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>
)

export default function Hero() {
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="absolute inset-0 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 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-300 max-w-3xl mx-auto leading-relaxed"
>
Join <span className="emphasis-text">John Lindquist</span>, founder of{' '}
egghead.io, for an immersive, hands-on program that will revolutionize
your dev workflow. In just{' '}
<span className="emphasis-text">20 days</span>, you&apos;ll master
building real-world AI applications that automate the tedious, amplify
your capabilities, and{' '}
<span className="emphasis-text">
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"
className="group 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 hover:bg-[var(--accent-10)] 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
</Link>
</motion.div>
</motion.div>
</section>
)
}
29 changes: 29 additions & 0 deletions src/pages/bootcamp/components/Instructor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client'
import {motion} from 'framer-motion'
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">
<h2 className="mb-8 text-3xl font-bold text-white">
Meet Your Instructor, John Lindquist
</h2>
<p className="mb-8 text-lg text-gray-400">
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">
Join John and a supportive community of developers as you build the
skills needed to create impactful AI solutions.
</p>
</motion.div>
</div>
</section>
)
}
86 changes: 86 additions & 0 deletions src/pages/bootcamp/components/SignUpForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'use client'

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

export default function SignUpForm() {
const [email, setEmail] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)
const {cioIdentify} = useCio()

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

if (isSubmitting) return

setIsSubmitting(true)
const subscriberId = nanoid()

try {
console.log('Submitting email:', email)
await cioIdentify(subscriberId, {
email,
created_at: Math.floor(Date.now() / 1000),
source: 'landing_page_waitlist',
})

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 text-white">
Ready to Build a Team of AI Devs?
</h2>
<p className="mb-8 text-center 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-[#0A0A0A] border-gray-800 text-white placeholder:text-gray-500"
/>
<Button
type="submit"
disabled={isSubmitting}
className="bg-[var(--accent-9)] hover:bg-[var(--accent-10)] text-black 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 fe0a25b

Please sign in to comment.