1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-08-31 21:11:44 +02:00

Purchase banner

This commit is contained in:
Kamran Ahmed
2025-07-01 02:25:33 +01:00
parent 7dc01ea8c6
commit 2cb3bf9562
2 changed files with 169 additions and 115 deletions

View File

@@ -1,33 +1,85 @@
import { CheckIcon, Star } from 'lucide-react';
import { BuyButton } from './BuyButton';
import { Rating } from '../Rating/Rating';
import { cn } from '../../lib/classname';
import { useEffect, useRef, useState } from 'react';
export function PurchaseBanner() {
return (
<div className="sticky top-4 z-50 mt-16.5 flex w-full flex-col gap-4 rounded-2xl bg-yellow-950 p-5 shadow-lg ring-1 ring-yellow-500/40 lg:flex-row lg:items-center lg:justify-between">
<div className="order-3 flex w-full flex-col items-center gap-2 lg:order-0 lg:w-fit lg:items-start">
{[
'7-Day Money-Back Guarantee',
'Lifetime access & updates'
].map((text, index) => (
<span key={index} className="inline-flex items-center gap-1.5 text-yellow-500">
<CheckIcon className="size-5 stroke-[2.5]" />
{text}
const bannerRef = useRef<HTMLDivElement>(null);
const [isOutOfView, setIsOutOfView] = useState(false);
useEffect(() => {
const handleScroll = () => {
if (!bannerRef.current) return;
const bannerRect = bannerRef.current.getBoundingClientRect();
const bannerBottom = bannerRect.bottom;
// Banner is out of view when its bottom is above the viewport
setIsOutOfView(bannerBottom < 0);
};
window.addEventListener('scroll', handleScroll);
handleScroll(); // Check initial state
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
const Banner = (props: {
className?: string;
ref?: React.RefObject<HTMLDivElement | null>;
}) => {
return (
<div
ref={props.ref}
className={cn(
'top-4 z-50 mt-16.5 flex w-full flex-col gap-4 rounded-2xl bg-yellow-950 p-5 shadow-lg ring-1 ring-yellow-500/40 lg:sticky lg:flex-row lg:items-center lg:justify-between',
props.className,
)}
>
<div className="order-3 flex w-full flex-col items-center gap-2 lg:order-0 lg:w-fit lg:items-start">
{['7-Day Money-Back Guarantee', 'Lifetime access & updates'].map(
(text, index) => (
<span
key={index}
className="inline-flex items-center gap-1.5 text-yellow-500"
>
<CheckIcon className="size-5 stroke-[2.5]" />
{text}
</span>
),
)}
</div>
<div className="order-2 lg:order-0">
<BuyButton
variant="floating"
floatingClassName="translate-x-0 lg:-translate-x-5"
/>
</div>
<div className="flex flex-col items-center gap-2">
<Rating rating={4.9} className="hidden lg:flex" />
<span className="flex items-center gap-1 text-base font-semibold text-yellow-500">
<Star className="block size-4 fill-current lg:hidden" />
4.9 avg. Review
</span>
))}
</div>
</div>
<div className="order-2 lg:order-0">
<BuyButton variant="floating" floatingClassName="translate-x-0 lg:-translate-x-5" />
</div>
<div className="flex flex-col items-center gap-2">
<Rating rating={4.9} className="hidden lg:flex" />
<span className="text-base font-semibold text-yellow-500 flex items-center gap-1">
<Star className="size-4 block lg:hidden fill-current" />
4.9 avg. Review
</span>
</div>
</div>
);
};
return (
<>
<Banner ref={bannerRef} />
<Banner
className={cn(
'fixed top-[unset] right-0 bottom-0 left-0 rounded-none lg:hidden',
isOutOfView ? 'flex' : 'hidden',
)}
/>
</>
);
}

View File

@@ -1,7 +1,8 @@
import {
BrainIcon,
CodeIcon,
FileQuestionIcon, NotebookTextIcon
FileQuestionIcon,
NotebookTextIcon,
} from 'lucide-react';
import { Spotlight } from '../SQLCourse/Spotlight';
import { RoadmapLogoIcon } from '../ReactIcons/RoadmapLogo';
@@ -19,105 +20,106 @@ import { sqlCourseChapters } from '../SQLCourse/SQLCoursePage';
export function SQLCourseVariantPage() {
return (
<>
<div className="relative flex grow flex-col items-center bg-linear-to-b from-zinc-900 to-zinc-950 px-4 pt-3 pb-52 text-zinc-400 md:px-10 md:pt-8">
<div className="mx-auto mt-7 w-full max-w-5xl md:mt-20">
<div className="relative">
<Spotlight className="top-[-200px] left-[-170px]" fill="#EAB308" />
<div className="relative flex grow flex-col items-center bg-linear-to-b from-zinc-900 to-zinc-950 px-4 pt-3 pb-52 text-zinc-400 md:px-10 md:pt-8">
<div className="mx-auto mt-7 w-full max-w-5xl md:mt-20">
<div className="relative">
<Spotlight className="top-[-200px] left-[-170px]" fill="#EAB308" />
<div className="flex flex-col gap-7 sm:flex-row sm:items-center">
<a
href="https://roadmap.sh"
target="_blank"
className="transition-opacity hover:opacity-100"
>
<RoadmapLogoIcon className="size-12 sm:size-22" />
</a>
<div className="flex flex-col items-start gap-2.5">
<h1 className="text-3xl font-bold tracking-tight text-white sm:text-4xl md:text-6xl">
Master SQL Queries
</h1>
<p className="text-left text-balance text-xl text-zinc-300 md:text-2xl">
Complete course with AI Tutor, real-world challenges and more
</p>
</div>
</div>
<p className="my-5 text-xl leading-relaxed text-zinc-300 md:my-14 lg:text-xl">
Get certified for SQL queries and ready to deploy your
newly-gained skill in 30 days. Perfect for developers, data
analysts, and anyone working with data. Level up risk-free with a
7-day money-back guarantee.
</p>
<div className="flex flex-col-reverse gap-7 lg:gap-14 lg:flex-row">
<div className="w-full shrink-0 flex-row-reverse items-start justify-between gap-3 text-lg md:flex lg:w-auto lg:flex-col">
<div className="flex flex-col gap-2 lg:gap-4 mb-7 lg:mb-0">
{[
{ Icon: NotebookTextIcon, text: '55+ Lessons' },
{ Icon: FileQuestionIcon, text: '100+ Challenges' },
{ Icon: BrainIcon, text: 'AI Tutor' },
{ Icon: CodeIcon, text: 'Integrated IDE' },
].map(({ Icon, text }, index) => (
<div key={index} className="flex flex-row items-center gap-2 text-base lg:text-xl text-zinc-300">
<Icon className="size-5 lg:size-6 text-yellow-400" />
<span>{text}</span>
</div>
))}
</div>
<AuthorCredentials />
</div>
<PlatformDemo />
<div className="flex flex-col gap-7 sm:flex-row sm:items-center">
<a
href="https://roadmap.sh"
target="_blank"
className="transition-opacity hover:opacity-100"
>
<RoadmapLogoIcon className="size-12 sm:size-22" />
</a>
<div className="flex flex-col items-start gap-2.5">
<h1 className="text-3xl font-bold tracking-tight text-white sm:text-4xl md:text-6xl">
Master SQL Queries
</h1>
<p className="text-left text-xl text-balance text-zinc-300 md:text-2xl">
Complete course with AI Tutor, real-world challenges and more
</p>
</div>
</div>
<PurchaseBanner />
<p className="my-5 text-xl leading-relaxed text-zinc-300 md:my-14 lg:text-xl">
Get certified for SQL queries and ready to deploy your newly-gained
skill in 30 days. Perfect for developers, data analysts, and anyone
working with data. Level up risk-free with a 7-day money-back
guarantee.
</p>
<ReviewCarousel />
<div className="flex flex-col-reverse gap-7 lg:flex-row lg:gap-14">
<div className="w-full shrink-0 flex-row-reverse items-start justify-between gap-3 text-lg md:flex lg:w-auto lg:flex-col">
<div className="mb-7 flex flex-col gap-2 lg:mb-0 lg:gap-4">
{[
{ Icon: NotebookTextIcon, text: '55+ Lessons' },
{ Icon: FileQuestionIcon, text: '100+ Challenges' },
{ Icon: BrainIcon, text: 'AI Tutor' },
{ Icon: CodeIcon, text: 'Integrated IDE' },
].map(({ Icon, text }, index) => (
<div
key={index}
className="flex flex-row items-center gap-2 text-base text-zinc-300 lg:text-xl"
>
<Icon className="size-5 text-yellow-400 lg:size-6" />
<span>{text}</span>
</div>
))}
</div>
<CourseFeatures />
<AuthorCredentials />
</div>
<MeetYourInstructor />
<SectionHeader
title="Course Overview"
description="This SQL programming class is designed to help you go from beginner to expert through hands-on practice with real-world scenarios, mastering everything from basic to complex queries."
className="mt-8 md:mt-24"
/>
<div className="mx-auto mt-8 w-full max-w-3xl space-y-4 md:mt-12">
{sqlCourseChapters.map((chapter, index) => (
<ChapterRow key={index} counter={index + 1} {...chapter} />
))}
</div>
<SectionHeader
title="Ready to master SQL?"
description="Start learning SQL queries risk-free with a 7-day money-back guarantee."
className="mt-8 md:mt-24"
/>
<div className="mx-auto mt-8 w-full">
<BuyButton variant="floating" />
</div>
<FAQSection />
<div className="mx-auto mt-12 w-full max-w-3xl text-left md:mt-9">
<p className="flex flex-col items-center justify-center gap-2 text-sm md:flex-row md:gap-0">
<a href="/terms" target="_blank" className="text-zinc-500">
Terms of Use
</a>
<span className="mx-4 hidden md:block">&middot;</span>
<a href="/privacy" target="_blank" className="text-zinc-500">
Privacy Policy
</a>
</p>
<PlatformDemo />
</div>
</div>
<PurchaseBanner />
<ReviewCarousel />
<CourseFeatures />
<MeetYourInstructor />
<SectionHeader
title="Course Overview"
description="This SQL programming class is designed to help you go from beginner to expert through hands-on practice with real-world scenarios, mastering everything from basic to complex queries."
className="mt-8 md:mt-24"
/>
<div className="mx-auto mt-8 w-full max-w-3xl space-y-4 md:mt-12">
{sqlCourseChapters.map((chapter, index) => (
<ChapterRow key={index} counter={index + 1} {...chapter} />
))}
</div>
<SectionHeader
title="Ready to master SQL?"
description="Start learning SQL queries risk-free with a 7-day money-back guarantee."
className="mt-8 md:mt-24"
/>
<div className="mx-auto mt-8 w-full">
<BuyButton variant="floating" />
</div>
<FAQSection />
<div className="mx-auto mt-12 w-full max-w-3xl text-left md:mt-9">
<p className="flex flex-col items-center justify-center gap-2 text-sm md:flex-row md:gap-0">
<a href="/terms" target="_blank" className="text-zinc-500">
Terms of Use
</a>
<span className="mx-4 hidden md:block">&middot;</span>
<a href="/privacy" target="_blank" className="text-zinc-500">
Privacy Policy
</a>
</p>
</div>
</div>
</>
</div>
);
}