1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-09-09 00:30:40 +02:00
This commit is contained in:
Arik Chakma
2025-06-30 20:38:00 +06:00
parent d1c317b889
commit 5c75efc8e5
4 changed files with 136 additions and 96 deletions

View File

@@ -1,4 +1,5 @@
import { AwardIcon } from 'lucide-react';
import { RoadmapDetailsPopover } from './RoadmapDetailsPopover';
export function AuthorCredentials() {
return (
@@ -25,9 +26,10 @@ export function AuthorCredentials() {
</svg>
#2 Most Starred Developer
</span>
<span className="inline-flex items-center gap-1.5 rounded-full bg-yellow-500/10 px-3 py-1">
<span className="inline-flex w-fit items-center gap-1.5 rounded-full bg-yellow-500/10 px-3 py-1">
<AwardIcon className="size-4 text-yellow-500/80" />
Founder roadmap.sh
<RoadmapDetailsPopover />
</span>
</div>
</div>

View File

@@ -41,10 +41,11 @@ type CreateCheckoutSessionResponse = {
type BuyButtonProps = {
variant?: 'main' | 'floating' | 'top-nav';
floatingClassName?: string;
};
export function BuyButton(props: BuyButtonProps) {
const { variant = 'main' } = props;
const { variant = 'main', floatingClassName } = props;
const [isFakeLoading, setIsFakeLoading] = useState(true);
const [isLoginPopupOpen, setIsLoginPopupOpen] = useState(false);
@@ -248,81 +249,84 @@ export function BuyButton(props: BuyButtonProps) {
if (variant === 'main') {
return (
<div className="relative flex w-full flex-col items-center gap-2 md:w-auto">
<>
{courseLoginPopup}
{isVideoModalOpen && (
<VideoModal
videoId="6S1CcF-ngeQ"
onClose={() => setIsVideoModalOpen(false)}
/>
)}
<div className="relative flex flex-col gap-2 md:flex-row md:gap-0">
{!isLoadingPricing && !isAlreadyEnrolled && mainCouponAlert}
<button
onClick={onBuyClick}
disabled={isLoadingPricing}
className={cn(
'group relative mr-2 inline-flex w-full min-w-[235px] items-center justify-center overflow-hidden rounded-xl bg-linear-to-r from-yellow-500 to-yellow-300 px-8 py-3 text-base font-semibold text-black transition-all duration-300 ease-out hover:scale-[1.02] hover:shadow-[0_0_30px_rgba(234,179,8,0.4)] focus:outline-hidden active:ring-0 md:w-auto md:rounded-full md:text-lg',
(isLoadingPricing || isCreatingCheckoutSession) &&
'striped-loader-yellow pointer-events-none mr-4 scale-105 bg-yellow-500',
)}
>
{isLoadingPricing ? (
<span className="relative flex items-center gap-2">&nbsp;</span>
) : isAlreadyEnrolled ? (
<span className="relative flex items-center gap-2">
Start Learning
</span>
) : (
<span className="relative flex items-center gap-2">
Buy now for{' '}
{coursePricing?.isEligibleForDiscount ? (
<span className="flex items-center gap-2">
<span className="hidden text-base line-through opacity-75 md:inline">
${coursePricing?.fullPrice}
</span>
<span className="text-base md:text-xl">
${coursePricing?.regionalPrice}
</span>
</span>
) : (
<span>${coursePricing?.regionalPrice}</span>
)}
<ArrowRightIcon className="h-5 w-5 transition-transform duration-300 ease-out group-hover:translate-x-1" />
</span>
)}
</button>
<button
onClick={onReadSampleClick}
data-demo-button
className={cn(
'group relative hidden items-center justify-center overflow-hidden rounded-xl border border-yellow-500/30 bg-transparent px-6 py-3 text-base font-medium text-yellow-500 transition-all duration-300 ease-out hover:bg-yellow-500/10 focus:outline-hidden active:ring-0 md:rounded-full',
{
'hidden lg:inline-flex':
!isLoadingPricing && !isAlreadyEnrolled,
},
)}
>
<span className="relative flex items-center gap-2">
<MousePointerClick className="h-5 w-5" />
Access Demo
</span>
</button>
</div>
<div className="relative flex w-full flex-col items-center gap-2 md:w-auto">
{isVideoModalOpen && (
<VideoModal
videoId="6S1CcF-ngeQ"
onClose={() => setIsVideoModalOpen(false)}
/>
)}
<div className="relative flex flex-col gap-2 md:flex-row md:gap-0">
{!isLoadingPricing && !isAlreadyEnrolled && mainCouponAlert}
{!isLoadingPricing && (
<span className="absolute top-full z-50 flex w-max translate-y-4 flex-row items-center justify-center text-sm text-yellow-400">
Lifetime access <span className="mx-2">&middot;</span>{' '}
<button
onClick={() => setIsVideoModalOpen(true)}
className="flex cursor-pointer flex-row items-center gap-1.5 underline underline-offset-4 hover:text-yellow-500"
onClick={onBuyClick}
disabled={isLoadingPricing}
className={cn(
'group relative mr-2 inline-flex w-full min-w-[235px] items-center justify-center overflow-hidden rounded-xl bg-linear-to-r from-yellow-500 to-yellow-300 px-8 py-3 text-base font-semibold text-black transition-all duration-300 ease-out hover:scale-[1.02] hover:shadow-[0_0_30px_rgba(234,179,8,0.4)] focus:outline-hidden active:ring-0 md:w-auto md:rounded-full md:text-lg',
(isLoadingPricing || isCreatingCheckoutSession) &&
'striped-loader-yellow pointer-events-none mr-4 scale-105 bg-yellow-500',
)}
>
<Play className="size-3 fill-current" /> Watch Video (3 min)
{isLoadingPricing ? (
<span className="relative flex items-center gap-2">&nbsp;</span>
) : isAlreadyEnrolled ? (
<span className="relative flex items-center gap-2">
Start Learning
</span>
) : (
<span className="relative flex items-center gap-2">
Buy now for{' '}
{coursePricing?.isEligibleForDiscount ? (
<span className="flex items-center gap-2">
<span className="hidden text-base line-through opacity-75 md:inline">
${coursePricing?.fullPrice}
</span>
<span className="text-base md:text-xl">
${coursePricing?.regionalPrice}
</span>
</span>
) : (
<span>${coursePricing?.regionalPrice}</span>
)}
<ArrowRightIcon className="h-5 w-5 transition-transform duration-300 ease-out group-hover:translate-x-1" />
</span>
)}
</button>
</span>
)}
</div>
<button
onClick={onReadSampleClick}
data-demo-button
className={cn(
'group relative hidden items-center justify-center overflow-hidden rounded-xl border border-yellow-500/30 bg-transparent px-6 py-3 text-base font-medium text-yellow-500 transition-all duration-300 ease-out hover:bg-yellow-500/10 focus:outline-hidden active:ring-0 md:rounded-full',
{
'hidden lg:inline-flex':
!isLoadingPricing && !isAlreadyEnrolled,
},
)}
>
<span className="relative flex items-center gap-2">
<MousePointerClick className="h-5 w-5" />
Access Demo
</span>
</button>
</div>
{!isLoadingPricing && (
<span className="absolute top-full z-50 flex w-max translate-y-4 flex-row items-center justify-center text-sm text-yellow-400">
Lifetime access <span className="mx-2">&middot;</span>{' '}
<button
onClick={() => setIsVideoModalOpen(true)}
className="flex cursor-pointer flex-row items-center gap-1.5 underline underline-offset-4 hover:text-yellow-500"
>
<Play className="size-3 fill-current" /> Watch Video (3 min)
</button>
</span>
)}
</div>
</>
);
}
@@ -339,32 +343,39 @@ export function BuyButton(props: BuyButtonProps) {
}
return (
<div className="relative flex flex-col items-center gap-2">
<>
{courseLoginPopup}
<button
onClick={onBuyClick}
disabled={isLoadingPricing}
<div
className={cn(
'group relative inline-flex min-w-[220px] items-center justify-center overflow-hidden rounded-full bg-[rgb(168,85,247)] px-8 py-3 font-medium text-white transition-all duration-300 ease-out hover:scale-[1.02] hover:shadow-[0_0_30px_rgba(168,85,247,0.4)] focus:outline-hidden',
(isLoadingPricing || isCreatingCheckoutSession) &&
'striped-loader pointer-events-none bg-[rgb(168,85,247)]',
'relative flex flex-col items-center gap-2',
floatingClassName,
)}
>
{isLoadingPricing ? (
<span className="relative flex items-center gap-2">&nbsp;</span>
) : isAlreadyEnrolled ? (
<span className="relative flex items-center gap-2">
Start Learning
</span>
) : (
<span className="relative flex items-center gap-2">
<span className="hidden md:inline">Start learning now</span>
<span className="inline md:hidden">Start now</span>- $
{coursePricing?.regionalPrice}
<ArrowRightIcon className="h-5 w-5 transition-transform duration-300 ease-out group-hover:translate-x-1" />
</span>
)}
</button>
</div>
<button
onClick={onBuyClick}
disabled={isLoadingPricing}
className={cn(
'group relative inline-flex min-w-[220px] items-center justify-center overflow-hidden rounded-full bg-[rgb(168,85,247)] px-8 py-3 font-medium text-white transition-all duration-300 ease-out hover:scale-[1.02] hover:shadow-[0_0_30px_rgba(168,85,247,0.4)] focus:outline-hidden',
(isLoadingPricing || isCreatingCheckoutSession) &&
'striped-loader pointer-events-none bg-[rgb(168,85,247)]',
)}
>
{isLoadingPricing ? (
<span className="relative flex items-center gap-2">&nbsp;</span>
) : isAlreadyEnrolled ? (
<span className="relative flex items-center gap-2">
Start Learning
</span>
) : (
<span className="relative flex items-center gap-2">
<span className="hidden md:inline">Start learning now</span>
<span className="inline md:hidden">Start now</span>- $
{coursePricing?.regionalPrice}
<ArrowRightIcon className="h-5 w-5 transition-transform duration-300 ease-out group-hover:translate-x-1" />
</span>
)}
</button>
</div>
</>
);
}

View File

@@ -16,8 +16,8 @@ export function PurchaseBanner() {
</span>
</div>
<div className="order-2 -translate-x-2 lg:order-0">
<BuyButton variant="floating" />
<div className="order-2 lg:order-0">
<BuyButton variant="floating" floatingClassName="-translate-x-2" />
</div>
<div className="flex flex-col items-center gap-2">

View File

@@ -0,0 +1,27 @@
import { InfoIcon } from 'lucide-react';
import { Popover, PopoverContent, PopoverTrigger } from '../Popover';
export function RoadmapDetailsPopover() {
return (
<Popover>
<PopoverTrigger>
<InfoIcon className="size-4 text-yellow-500/80 hover:text-yellow-500" />
</PopoverTrigger>
<PopoverContent
className="border-zinc-700 bg-zinc-900 px-2.5 text-sm text-zinc-200"
side="top"
align="start"
>
<a
href="/"
className="text-blue-400 underline hover:text-blue-500 focus:text-blue-500"
>
roadmap.sh
</a>{' '}
provides community-curated roadmaps, study plans, paths, and resources
for developers and IT professionals. Serving 2M+ registered users, it is
the 6th most-starred open source project on GitHub
</PopoverContent>
</Popover>
);
}