Add course demo button

feat/course-demo
Kamran Ahmed 5 days ago
parent e5e43de98a
commit c40b378202
  1. 6
      src/components/AuthenticationFlow/CourseLoginPopup.tsx
  2. 103
      src/components/SQLCourse/BuyButton.tsx

@ -12,6 +12,7 @@ type CourseLoginPopupProps = {
};
export const CHECKOUT_AFTER_LOGIN_KEY = 'checkoutAfterLogin';
export const SAMPLE_AFTER_LOGIN_KEY = 'sampleAfterLogin';
export function CourseLoginPopup(props: CourseLoginPopupProps) {
const { onClose: parentOnClose, checkoutAfterLogin = true } = props;
@ -27,6 +28,7 @@ export function CourseLoginPopup(props: CourseLoginPopupProps) {
// if user didn't login and closed the popup, we remove the checkoutAfterLogin flag
// so that login from other buttons on course page will trigger purchase
localStorage.removeItem(CHECKOUT_AFTER_LOGIN_KEY);
localStorage.removeItem(SAMPLE_AFTER_LOGIN_KEY);
parentOnClose();
}
@ -40,7 +42,7 @@ export function CourseLoginPopup(props: CourseLoginPopupProps) {
if (emailNature) {
const emailHeader = (
<div className="mb-7 text-center">
<p className="mb-3.5 pt-2 text-2xl font-semibold leading-5 text-slate-900">
<p className="mb-3.5 pt-2 text-2xl leading-5 font-semibold text-slate-900">
{emailNature === 'login'
? 'Login to your account'
: 'Create an account'}
@ -80,7 +82,7 @@ export function CourseLoginPopup(props: CourseLoginPopupProps) {
return (
<Modal onClose={onClose} bodyClassName="p-5 h-auto">
<div className="mb-7 text-center">
<p className="mb-3.5 pt-2 text-2xl font-semibold leading-5 text-slate-900">
<p className="mb-3.5 pt-2 text-2xl leading-5 font-semibold text-slate-900">
Create or login to Enroll
</p>
<p className="mt-2 text-sm leading-4 text-slate-600">

@ -1,5 +1,5 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { ArrowRightIcon, Play } from 'lucide-react';
import { ArrowRightIcon, BookOpen, Play } from 'lucide-react';
import { useEffect, useState } from 'react';
import { cn } from '../../lib/classname';
import {
@ -10,12 +10,14 @@ import {
import { coursePriceOptions } from '../../queries/billing';
import { courseProgressOptions } from '../../queries/course-progress';
import { queryClient } from '../../stores/query-client';
import { CourseLoginPopup } from '../AuthenticationFlow/CourseLoginPopup';
import {
CourseLoginPopup,
SAMPLE_AFTER_LOGIN_KEY,
} from '../AuthenticationFlow/CourseLoginPopup';
import { useToast } from '../../hooks/use-toast';
import { httpPost } from '../../lib/query-http';
import { deleteUrlParam, getUrlParams } from '../../lib/browser';
import { VideoModal } from '../VideoModal';
import { showLoginPopup } from '../../lib/popup';
export const SQL_COURSE_SLUG = 'sql';
@ -96,7 +98,13 @@ export function BuyButton(props: BuyButtonProps) {
useEffect(() => {
const urlParams = getUrlParams();
const shouldTriggerPurchase = urlParams[COURSE_PURCHASE_PARAM] === '1';
if (shouldTriggerPurchase) {
const shouldTriggerSample =
localStorage.getItem(SAMPLE_AFTER_LOGIN_KEY) === '1';
if (shouldTriggerSample) {
localStorage.removeItem(SAMPLE_AFTER_LOGIN_KEY);
window.location.href = `${import.meta.env.PUBLIC_COURSE_APP_URL}/${SQL_COURSE_SLUG}`;
} else if (shouldTriggerPurchase) {
deleteUrlParam(COURSE_PURCHASE_PARAM);
initPurchase();
}
@ -163,6 +171,16 @@ export function BuyButton(props: BuyButtonProps) {
initPurchase();
}
function onReadSampleClick() {
if (!isLoggedIn()) {
localStorage.setItem(SAMPLE_AFTER_LOGIN_KEY, '1');
setIsLoginPopupOpen(true);
return;
}
window.location.href = `${import.meta.env.PUBLIC_COURSE_APP_URL}/${SQL_COURSE_SLUG}`;
}
const courseLoginPopup = isLoginPopupOpen && (
<CourseLoginPopup onClose={() => setIsLoginPopupOpen(false)} />
);
@ -177,43 +195,54 @@ export function BuyButton(props: BuyButtonProps) {
onClose={() => setIsVideoModalOpen(false)}
/>
)}
<button
onClick={onBuyClick}
disabled={isLoadingPricing}
className={cn(
'group relative 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 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}
<div className="flex flex-row">
<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>
) : (
<span>${coursePricing?.regionalPrice}</span>
)}
<ArrowRightIcon className="h-5 w-5 transition-transform duration-300 ease-out group-hover:translate-x-1" />
) : (
<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}
className="group relative inline-flex 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"
>
<span className="relative flex items-center gap-2">
<BookOpen className="h-5 w-5" />
Read Sample
</span>
)}
</button>
</button>
</div>
{!isLoadingPricing && (
<span className="absolute top-full z-50 flex w-[300px] translate-y-3 flex-row items-center justify-center text-sm text-yellow-400">
<span className="absolute top-full z-50 flex w-[300px] 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)}

Loading…
Cancel
Save