update pay function
This commit is contained in:
144
boilerplate-chakra-pro-main/components/sidebar/Sidebar.tsx
Normal file
144
boilerplate-chakra-pro-main/components/sidebar/Sidebar.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
renderThumb,
|
||||
renderTrack,
|
||||
renderView
|
||||
} from '@/components/scrollbar/Scrollbar';
|
||||
import Content from '@/components/sidebar/components/Content';
|
||||
import { ApiKeyContext } from '@/contexts/layout';
|
||||
import { IRoute } from '@/types/types';
|
||||
import { isWindowAvailable } from '@/utils/navigation';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
Icon,
|
||||
useColorModeValue,
|
||||
DrawerOverlay,
|
||||
useDisclosure,
|
||||
DrawerContent,
|
||||
DrawerCloseButton
|
||||
} from '@chakra-ui/react';
|
||||
import React, { PropsWithChildren, useContext } from 'react';
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import { IoMenuOutline } from 'react-icons/io5';
|
||||
|
||||
export interface SidebarProps extends PropsWithChildren {
|
||||
routes: IRoute[];
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
function Sidebar(props: SidebarProps) {
|
||||
const { routes } = props;
|
||||
const { apiKey, setApiKey } = useContext(ApiKeyContext);
|
||||
let variantChange = '0.2s linear';
|
||||
let shadow = useColorModeValue(
|
||||
'14px 17px 40px 4px rgba(112, 144, 176, 0.08)',
|
||||
'unset'
|
||||
);
|
||||
let sidebarBg = useColorModeValue('white', 'navy.800');
|
||||
let sidebarRadius = '14px';
|
||||
let sidebarMargins = '0px';
|
||||
return (
|
||||
<Box display={{ base: 'none', xl: 'block' }} position="fixed" minH="100%">
|
||||
<Box
|
||||
bg={sidebarBg}
|
||||
transition={variantChange}
|
||||
w="285px"
|
||||
ms={{
|
||||
sm: '16px'
|
||||
}}
|
||||
my={{
|
||||
sm: '16px'
|
||||
}}
|
||||
h="calc(100vh - 32px)"
|
||||
m={sidebarMargins}
|
||||
borderRadius={sidebarRadius}
|
||||
minH="100%"
|
||||
overflowX="hidden"
|
||||
boxShadow={shadow}
|
||||
>
|
||||
<Scrollbars
|
||||
autoHide
|
||||
renderTrackVertical={renderTrack}
|
||||
renderThumbVertical={renderThumb}
|
||||
renderView={renderView}
|
||||
universal={true}
|
||||
>
|
||||
<Content setApiKey={setApiKey} routes={routes} />
|
||||
</Scrollbars>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// -------------- Sidebar Function for Navbar burger --------------
|
||||
export function SidebarResponsive(props: SidebarProps) {
|
||||
let sidebarBackgroundColor = useColorModeValue('white', 'navy.800');
|
||||
let menuColor = useColorModeValue('gray.400', 'white');
|
||||
const { apiKey, setApiKey } = useContext(ApiKeyContext);
|
||||
// SIDEBAR
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
const { routes } = props;
|
||||
return (
|
||||
<Flex display={{ sm: 'flex', xl: 'none' }} alignItems="center">
|
||||
<Flex w="max-content" h="max-content" onClick={onOpen}>
|
||||
<Icon
|
||||
as={IoMenuOutline}
|
||||
color={menuColor}
|
||||
my="auto"
|
||||
w="20px"
|
||||
h="20px"
|
||||
me="10px"
|
||||
_hover={{ cursor: 'pointer' }}
|
||||
/>
|
||||
</Flex>
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
placement={
|
||||
isWindowAvailable() && document.documentElement.dir === 'rtl'
|
||||
? 'right'
|
||||
: 'left'
|
||||
}
|
||||
>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent
|
||||
w="285px"
|
||||
maxW="285px"
|
||||
ms={{
|
||||
sm: '16px'
|
||||
}}
|
||||
my={{
|
||||
sm: '16px'
|
||||
}}
|
||||
borderRadius="16px"
|
||||
bg={sidebarBackgroundColor}
|
||||
>
|
||||
<DrawerCloseButton
|
||||
zIndex="3"
|
||||
onClick={onClose}
|
||||
_focus={{ boxShadow: 'none' }}
|
||||
_hover={{ boxShadow: 'none' }}
|
||||
/>
|
||||
<DrawerBody maxW="285px" px="0rem" pb="0">
|
||||
<Scrollbars
|
||||
autoHide
|
||||
renderTrackVertical={renderTrack}
|
||||
renderThumbVertical={renderThumb}
|
||||
renderView={renderView}
|
||||
universal={true}
|
||||
>
|
||||
<Content setApiKey={setApiKey} routes={routes} />
|
||||
</Scrollbars>
|
||||
</DrawerBody>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default Sidebar;
|
||||
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import logo from '/public/logo-horizon-boilerplate.png';
|
||||
import { HSeparator } from '@/components/separator/Separator';
|
||||
import { Flex, Image, Link, Text } from '@chakra-ui/react';
|
||||
|
||||
export function SidebarBrand() {
|
||||
return (
|
||||
<Flex alignItems="center" flexDirection="column" ps="24px">
|
||||
<Link
|
||||
display={'flex'}
|
||||
alignItems="center"
|
||||
justifyContent={'center'}
|
||||
href="/"
|
||||
mb="34px"
|
||||
>
|
||||
<Image
|
||||
alt=" "
|
||||
w="36px"
|
||||
src={logo.src}
|
||||
/>
|
||||
<Text
|
||||
ms="8px"
|
||||
me="10px"
|
||||
fontWeight="800"
|
||||
fontSize={'17px'}
|
||||
letterSpacing="-0.2px"
|
||||
color="#120F43"
|
||||
>
|
||||
Horizon UI Boilerplate
|
||||
</Text>
|
||||
</Link>
|
||||
<HSeparator mb="20px" w="310px" />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarBrand;
|
||||
@@ -0,0 +1,111 @@
|
||||
'use client';
|
||||
|
||||
// Custom components
|
||||
import { useSupabase } from '@/app/supabase-provider';
|
||||
import Brand from '@/components/sidebar/components/Brand';
|
||||
import Links from '@/components/sidebar/components/Links';
|
||||
import SidebarCard from '@/components/sidebar/components/SidebarCard';
|
||||
import { UserContext } from '@/contexts/layout';
|
||||
import { IRoute } from '@/types/types';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
Icon,
|
||||
Stack,
|
||||
Text,
|
||||
useColorModeValue
|
||||
} from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { PropsWithChildren, useContext } from 'react';
|
||||
import { FiLogOut } from 'react-icons/fi';
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
interface SidebarContent extends PropsWithChildren {
|
||||
routes: IRoute[];
|
||||
[x: string]: any;
|
||||
}
|
||||
function SidebarContent(props: SidebarContent) {
|
||||
const router = useRouter();
|
||||
const { supabase } = useSupabase();
|
||||
const { routes, setApiKey } = props;
|
||||
const user = useContext(UserContext);
|
||||
console.log(user.user_metadata.avatar_url);
|
||||
const textColor = useColorModeValue('#120F43', 'white');
|
||||
const borderColor = useColorModeValue('gray.200', 'whiteAlpha.300');
|
||||
const shadowPillBar = useColorModeValue(
|
||||
'4px 17px 40px 4px rgba(112, 144, 176, 0.08)',
|
||||
'none'
|
||||
);
|
||||
return (
|
||||
<Flex
|
||||
direction="column"
|
||||
height="100%"
|
||||
pt="36px"
|
||||
pb="26px"
|
||||
borderRadius="30px"
|
||||
maxW="285px"
|
||||
w="100%"
|
||||
>
|
||||
<Brand />
|
||||
<Stack direction="column" mb="auto" mt="8px" ps="20px" pe="16px">
|
||||
<Box ps="0px" pe={{ md: '0px', '2xl': '0px' }}>
|
||||
<Links routes={routes} />
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
<Box
|
||||
mt="60px"
|
||||
width={'100%'}
|
||||
display={'flex'}
|
||||
justifyContent={'center'}
|
||||
ps="20px"
|
||||
pe="20px"
|
||||
>
|
||||
<SidebarCard />
|
||||
</Box>
|
||||
<Flex
|
||||
mt="20px"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
boxShadow={shadowPillBar}
|
||||
borderRadius="30px"
|
||||
p="14px"
|
||||
px="34px"
|
||||
>
|
||||
<Avatar
|
||||
h="34px"
|
||||
w="34px"
|
||||
me="10px"
|
||||
src={user.user_metadata.avatar_url}
|
||||
/>
|
||||
<Text color={textColor} fontSize="sm" fontWeight="700" me="10px">
|
||||
{user.user_metadata.full_name ? user.user_metadata.full_name : 'User'}
|
||||
</Text>
|
||||
<Button
|
||||
ms="auto"
|
||||
variant="transparent"
|
||||
border="1px solid"
|
||||
borderColor={borderColor}
|
||||
borderRadius="full"
|
||||
w="34px"
|
||||
h="34px"
|
||||
px="0px"
|
||||
minW="34px"
|
||||
justifyContent={'center'}
|
||||
alignItems="center"
|
||||
onClick={(e) => {
|
||||
supabase.auth.signOut();
|
||||
router.push('/');
|
||||
}}
|
||||
>
|
||||
<Icon as={FiLogOut} width="16px" height="16px" color="inherit" />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default SidebarContent;
|
||||
@@ -0,0 +1,823 @@
|
||||
'use client';
|
||||
|
||||
/* eslint-disable */
|
||||
import NavLink from '@/components/link/NavLink';
|
||||
import {
|
||||
PlanContext,
|
||||
ProductsContext,
|
||||
UserContext,
|
||||
UserDetailsContext
|
||||
} from '@/contexts/layout';
|
||||
import modalImage from '@/public/Modal.png';
|
||||
import { IRoute } from '@/types/types';
|
||||
import { Database } from '@/types_db';
|
||||
import { getRedirectMethod } from '@/utils/auth-helpers/settings';
|
||||
import { getErrorRedirect } from '@/utils/helpers';
|
||||
import { getStripe } from '@/utils/stripe/client';
|
||||
import { checkoutWithStripe } from '@/utils/stripe/server';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
HStack,
|
||||
Icon,
|
||||
Image,
|
||||
List,
|
||||
ListItem,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
useColorModeValue,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { PropsWithChildren, useCallback, useContext, useState } from 'react';
|
||||
import { FaCircle } from 'react-icons/fa';
|
||||
import { IoIosStar, IoMdAdd } from 'react-icons/io';
|
||||
import { MdCheckCircle, MdChevronRight } from 'react-icons/md';
|
||||
|
||||
interface SidebarLinksProps extends PropsWithChildren {
|
||||
routes: IRoute[];
|
||||
[x: string]: any;
|
||||
}
|
||||
type Price = Database['public']['Tables']['prices']['Row'];
|
||||
|
||||
export function SidebarLinks(props: SidebarLinksProps) {
|
||||
const pathname = usePathname();
|
||||
const textColor = useColorModeValue('#120F43', 'white');
|
||||
let activeColor = useColorModeValue('#120F43', 'white');
|
||||
let inactiveColor = useColorModeValue('gray.500', 'gray.500');
|
||||
let borderColor = useColorModeValue('gray.200', 'whiteAlpha.300');
|
||||
let activeIcon = useColorModeValue('brand.500', 'white');
|
||||
let iconColor = useColorModeValue('#120F43', 'white');
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
// verifies if routeName is the one active (in browser input)
|
||||
const activeRoute = useCallback(
|
||||
(route: string | { [x: string]: any }) => {
|
||||
let foundActive = 0;
|
||||
if (typeof route === 'string') {
|
||||
return pathname?.includes(route);
|
||||
} else if (route?.items) {
|
||||
route.items.map((item: { [x: string]: any }) => {
|
||||
if (pathname?.includes(item.path)) foundActive = foundActive + 1;
|
||||
});
|
||||
}
|
||||
if (foundActive > 0) return true;
|
||||
else return false;
|
||||
},
|
||||
[pathname]
|
||||
);
|
||||
|
||||
const router = getRedirectMethod() === 'client' ? useRouter() : null;
|
||||
const { routes } = props;
|
||||
const user = useContext(UserContext);
|
||||
const [priceIdLoading, setPriceIdLoading] = useState<string>();
|
||||
const { plan, setPlan } = useContext(PlanContext);
|
||||
const products = useContext(ProductsContext);
|
||||
const currentPath = usePathname();
|
||||
|
||||
// this function creates the links and collapses that appear in the sidebar (left menu)
|
||||
const createLinks = (routes: IRoute[]) => {
|
||||
const handleCheckout = async (price: Price) => {
|
||||
setPriceIdLoading(price.id);
|
||||
|
||||
if (!user) {
|
||||
setPriceIdLoading(undefined);
|
||||
return router.push('/dashboard/signin/signup');
|
||||
}
|
||||
|
||||
const { errorRedirect, sessionId } = await checkoutWithStripe(
|
||||
price,
|
||||
currentPath
|
||||
);
|
||||
|
||||
if (errorRedirect) {
|
||||
setPriceIdLoading(undefined);
|
||||
return router.push(errorRedirect);
|
||||
}
|
||||
|
||||
if (!sessionId) {
|
||||
setPriceIdLoading(undefined);
|
||||
return router.push(
|
||||
getErrorRedirect(
|
||||
currentPath,
|
||||
'An unknown error occurred.',
|
||||
'Please try again later or contact a system administrator.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const stripe = await getStripe();
|
||||
stripe?.redirectToCheckout({ sessionId });
|
||||
|
||||
setPriceIdLoading(undefined);
|
||||
};
|
||||
return routes.map((route, key) => {
|
||||
if (route.collapse && !route.invisible) {
|
||||
return (
|
||||
<Accordion
|
||||
allowToggle
|
||||
defaultIndex={activeRoute(route) ? 0 : 1}
|
||||
key={key}
|
||||
>
|
||||
<AccordionItem border="none" mb="14px" key={key}>
|
||||
<AccordionButton
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
mb="4px"
|
||||
justifyContent="center"
|
||||
_hover={{
|
||||
bg: 'unset'
|
||||
}}
|
||||
_focus={{
|
||||
boxShadow: 'none'
|
||||
}}
|
||||
borderRadius="8px"
|
||||
w="100%"
|
||||
py="0px"
|
||||
ms={0}
|
||||
>
|
||||
{route.icon ? (
|
||||
<Flex align="center" justifyContent="space-between" w="100%">
|
||||
<HStack spacing={activeRoute(route) ? '22px' : '26px'}>
|
||||
<Flex
|
||||
w="100%"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Box
|
||||
color={
|
||||
activeRoute(route) ? activeIcon : inactiveColor
|
||||
}
|
||||
me="12px"
|
||||
mt="6px"
|
||||
>
|
||||
{route.icon}
|
||||
</Box>
|
||||
<Text
|
||||
me="auto"
|
||||
color={activeRoute(route) ? activeColor : 'gray.500'}
|
||||
fontWeight="500"
|
||||
letterSpacing="0px"
|
||||
fontSize="sm"
|
||||
>
|
||||
{route.name}
|
||||
</Text>
|
||||
</Flex>
|
||||
</HStack>
|
||||
<AccordionIcon ms="auto" color={'gray.500'} />
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex pt="0px" pb="10px" alignItems="center" w="100%">
|
||||
<HStack
|
||||
spacing={
|
||||
activeRoute(route.path.toLowerCase()) ? '22px' : '26px'
|
||||
}
|
||||
ps="32px"
|
||||
>
|
||||
<Text
|
||||
me="auto"
|
||||
color={
|
||||
activeRoute(route.path.toLowerCase())
|
||||
? activeColor
|
||||
: inactiveColor
|
||||
}
|
||||
fontWeight="500"
|
||||
letterSpacing="0px"
|
||||
fontSize="sm"
|
||||
>
|
||||
{route.name}
|
||||
</Text>
|
||||
</HStack>
|
||||
<AccordionIcon ms="auto" color={'gray.500'} />
|
||||
</Flex>
|
||||
)}
|
||||
</AccordionButton>
|
||||
<AccordionPanel py="0px" ps={'8px'}>
|
||||
<List>
|
||||
{
|
||||
route.icon && route.items
|
||||
? createLinks(route.items) // for bullet accordion links
|
||||
: route.items
|
||||
? createAccordionLinks(route.items)
|
||||
: '' // for non-bullet accordion links
|
||||
}
|
||||
</List>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
} else if (!route.invisible) {
|
||||
return (
|
||||
<Box key={key}>
|
||||
{route.icon ? (
|
||||
<Flex
|
||||
align="center"
|
||||
justifyContent="space-between"
|
||||
w="100%"
|
||||
maxW="100%"
|
||||
ps="17px"
|
||||
mb="0px"
|
||||
>
|
||||
<HStack
|
||||
w="100%"
|
||||
mb="14px"
|
||||
spacing={
|
||||
activeRoute(route.path.toLowerCase()) ? '22px' : '26px'
|
||||
}
|
||||
>
|
||||
{route.path.includes('premium') && !props.subscription ? (
|
||||
<Flex w="100%">
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay bg="rgba(0, 0, 0, 0.85)" />
|
||||
<ModalContent
|
||||
mx="8px"
|
||||
bg="transparent"
|
||||
boxShadow="unset"
|
||||
maxW="unset"
|
||||
w="unset"
|
||||
>
|
||||
<ModalBody p="0px" position={'relative'}>
|
||||
<Flex>
|
||||
<Image
|
||||
display={{ base: 'none', md: 'block' }}
|
||||
zIndex="98"
|
||||
borderLeftRadius="16px"
|
||||
src={modalImage.src}
|
||||
w="340px"
|
||||
/>
|
||||
<Flex
|
||||
bg="white"
|
||||
borderLeftRadius={{ base: '16px', md: '0px' }}
|
||||
borderRightRadius="16px"
|
||||
direction={'column'}
|
||||
px={{ base: '30px', md: '42px' }}
|
||||
py="34px"
|
||||
w={{ md: '412px', lg: '456px' }}
|
||||
minW={{ md: '412px', lg: '456px' }}
|
||||
>
|
||||
<Text
|
||||
fontSize="26px"
|
||||
fontWeight={'800'}
|
||||
color={textColor}
|
||||
mb="12px"
|
||||
>
|
||||
Upgrade to Unlimited
|
||||
</Text>
|
||||
<Text
|
||||
mb="24px"
|
||||
fontWeight="500"
|
||||
fontSize="md"
|
||||
color="gray.500"
|
||||
>
|
||||
Get access to all features and generate
|
||||
premium and exclusive essays with our
|
||||
unlimited plan!
|
||||
</Text>
|
||||
{/* Features */}
|
||||
<Flex
|
||||
w={{ base: '100%', xl: '80%' }}
|
||||
direction="column"
|
||||
>
|
||||
<Flex alignItems="center" mb="20px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Access to 12+ Essay types
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb="20px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Up to 1500 words per Essay
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb="20px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Academic Citation formats (APA, etc.)
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb="20px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Academic Levels (Master, etc.)
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb="30px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Essay Tones (Academic, etc.)
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{/* YEARLY */}
|
||||
<Flex
|
||||
onClick={() =>
|
||||
setPlan({
|
||||
product: 'prod_PtTJ6R3RnzmIPX',
|
||||
price: 'price_1P3gMyGx8VbJPRgzkoB6Fp8F'
|
||||
})
|
||||
}
|
||||
transition="0.15s linear"
|
||||
align="center"
|
||||
position="relative"
|
||||
border="1px solid"
|
||||
borderColor={
|
||||
plan.product === 'prod_PtTJ6R3RnzmIPX'
|
||||
? 'brand.500'
|
||||
: borderColor
|
||||
}
|
||||
borderRadius="10px"
|
||||
w="100%"
|
||||
py="14px"
|
||||
px="14px"
|
||||
cursor="pointer"
|
||||
mb="20px"
|
||||
>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight={'700'}
|
||||
color="#120F43"
|
||||
mb="2x"
|
||||
ms="8px"
|
||||
me="8px"
|
||||
>
|
||||
Yearly
|
||||
</Text>
|
||||
<Badge
|
||||
display={{
|
||||
base: 'flex',
|
||||
lg: 'none',
|
||||
xl: 'flex'
|
||||
}}
|
||||
colorScheme="green"
|
||||
borderRadius="4px"
|
||||
color="green.500"
|
||||
textTransform={'none'}
|
||||
letterSpacing="0px"
|
||||
px="0px"
|
||||
w="max-content"
|
||||
>
|
||||
Save 35%
|
||||
</Badge>
|
||||
<Text
|
||||
display="flex"
|
||||
ms="auto"
|
||||
fontSize="md"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
fontWeight="600"
|
||||
lineHeight="100%"
|
||||
>
|
||||
$5.75
|
||||
<Text
|
||||
fontSize={'14px'}
|
||||
color="gray.500"
|
||||
fontWeight="500"
|
||||
ms="4px"
|
||||
as="span"
|
||||
>
|
||||
/month
|
||||
</Text>
|
||||
</Text>
|
||||
</Flex>
|
||||
{/* END YEARLY */}
|
||||
{/* MONTHLY */}
|
||||
<Flex
|
||||
onClick={() =>
|
||||
setPlan({
|
||||
product: 'prod_PtTCPDFZbburMa',
|
||||
price: 'price_1P3gGXGx8VbJPRgzdEZODy8K'
|
||||
})
|
||||
}
|
||||
transition="0.15s linear"
|
||||
align="center"
|
||||
position="relative"
|
||||
border="1px solid"
|
||||
borderColor={
|
||||
plan.product === 'prod_PtTCPDFZbburMa'
|
||||
? 'brand.500'
|
||||
: borderColor
|
||||
}
|
||||
borderRadius="10px"
|
||||
w="100%"
|
||||
py="16px"
|
||||
px="14px"
|
||||
cursor="pointer"
|
||||
mb="28px"
|
||||
>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight={'700'}
|
||||
color="#120F43"
|
||||
mb="2x"
|
||||
ms="8px"
|
||||
me="4px"
|
||||
>
|
||||
Monthly
|
||||
</Text>
|
||||
<Text
|
||||
display="flex"
|
||||
ms="auto"
|
||||
fontSize="md"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
fontWeight="600"
|
||||
lineHeight="100%"
|
||||
>
|
||||
$9
|
||||
<Text
|
||||
fontSize={'14px'}
|
||||
color="gray.500"
|
||||
fontWeight="500"
|
||||
ms="4px"
|
||||
as="span"
|
||||
>
|
||||
/month
|
||||
</Text>
|
||||
</Text>
|
||||
</Flex>
|
||||
{/* END MONTHLY */}
|
||||
{products.map((product: any) => {
|
||||
const price = product?.prices?.find(
|
||||
(price: any) => price.id === plan.price
|
||||
);
|
||||
if (product.id === plan.product) {
|
||||
if (!price) return null;
|
||||
return (
|
||||
<Button
|
||||
py="20px"
|
||||
px="16px"
|
||||
fontSize="sm"
|
||||
variant="primary"
|
||||
borderRadius="45px"
|
||||
w={{ base: '100%' }}
|
||||
h="54px"
|
||||
mb="28px"
|
||||
_hover={{
|
||||
boxShadow:
|
||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
||||
bg:
|
||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
||||
_disabled: {
|
||||
bg:
|
||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
||||
}
|
||||
}}
|
||||
onClick={() => handleCheckout(price)}
|
||||
>
|
||||
Upgrade now
|
||||
<Icon
|
||||
as={MdChevronRight}
|
||||
mt="2px"
|
||||
h="16px"
|
||||
w="16px"
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
})}
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color="gray.500"
|
||||
fontWeight={'500'}
|
||||
mx="auto"
|
||||
mb="5px"
|
||||
>
|
||||
Used by 80,000+ users monthly
|
||||
</Text>
|
||||
<Flex
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
mx="auto"
|
||||
>
|
||||
<Icon
|
||||
me="1px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Icon
|
||||
me="1px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Icon
|
||||
me="1px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Icon
|
||||
me="1px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Icon
|
||||
me="6px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="800"
|
||||
h="100%"
|
||||
color={textColor}
|
||||
>
|
||||
4.9
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
<ModalCloseButton
|
||||
borderRadius="full"
|
||||
color="#120F43"
|
||||
bg="#F4F6FB !important"
|
||||
_hover={{ bg: '#E9EDF6 !important' }}
|
||||
_focus={{ bg: '#F4F6FB !important' }}
|
||||
_active={{ bg: '#F4F6FB !important' }}
|
||||
zIndex="99"
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<Flex
|
||||
w="100%"
|
||||
cursor={'pointer'}
|
||||
onClick={() => onOpen()}
|
||||
>
|
||||
<Flex
|
||||
w="100%"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Box
|
||||
color={
|
||||
activeRoute(route.path.toLowerCase())
|
||||
? activeIcon
|
||||
: inactiveColor
|
||||
}
|
||||
me="12px"
|
||||
mt="6px"
|
||||
>
|
||||
{route.icon}
|
||||
</Box>
|
||||
<Text
|
||||
me="auto"
|
||||
color={
|
||||
activeRoute(route.path.toLowerCase())
|
||||
? activeColor
|
||||
: 'gray.500'
|
||||
}
|
||||
fontWeight={
|
||||
activeRoute(route.path.toLowerCase())
|
||||
? '700'
|
||||
: '500'
|
||||
}
|
||||
letterSpacing="0px"
|
||||
fontSize="sm"
|
||||
>
|
||||
{route.name}
|
||||
</Text>
|
||||
{route.rightElement ? (
|
||||
<Flex
|
||||
border="1px solid"
|
||||
borderColor={borderColor}
|
||||
borderRadius="full"
|
||||
w="34px"
|
||||
h="34px"
|
||||
justify={'center'}
|
||||
align="center"
|
||||
color={iconColor}
|
||||
ms="auto"
|
||||
me="10px"
|
||||
>
|
||||
<Icon
|
||||
as={IoMdAdd}
|
||||
width="20px"
|
||||
height="20px"
|
||||
color="inherit"
|
||||
/>
|
||||
</Flex>
|
||||
) : null}
|
||||
<Badge
|
||||
display={{ base: 'flex', lg: 'none', xl: 'flex' }}
|
||||
colorScheme="brand"
|
||||
borderRadius="25px"
|
||||
color="brand.500"
|
||||
textTransform={'none'}
|
||||
letterSpacing="0px"
|
||||
px="8px"
|
||||
>
|
||||
PRO
|
||||
</Badge>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
) : (
|
||||
<NavLink
|
||||
href={
|
||||
route.layout ? route.layout + route.path : route.path
|
||||
}
|
||||
key={key}
|
||||
styles={{ width: '100%' }}
|
||||
>
|
||||
<Flex
|
||||
w="100%"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Box
|
||||
color={
|
||||
activeRoute(route.path.toLowerCase())
|
||||
? activeIcon
|
||||
: inactiveColor
|
||||
}
|
||||
me="12px"
|
||||
mt="6px"
|
||||
>
|
||||
{route.icon}
|
||||
</Box>
|
||||
<Text
|
||||
me="auto"
|
||||
color={
|
||||
activeRoute(route.path.toLowerCase())
|
||||
? activeColor
|
||||
: 'gray.500'
|
||||
}
|
||||
fontWeight={
|
||||
activeRoute(route.path.toLowerCase())
|
||||
? '700'
|
||||
: '500'
|
||||
}
|
||||
letterSpacing="0px"
|
||||
fontSize="sm"
|
||||
>
|
||||
{route.name}
|
||||
</Text>
|
||||
{route.rightElement ? (
|
||||
<Flex
|
||||
border="1px solid"
|
||||
borderColor={borderColor}
|
||||
borderRadius="full"
|
||||
w="34px"
|
||||
h="34px"
|
||||
justify={'center'}
|
||||
align="center"
|
||||
color={iconColor}
|
||||
ms="auto"
|
||||
me="10px"
|
||||
>
|
||||
<Icon
|
||||
as={IoMdAdd}
|
||||
width="20px"
|
||||
height="20px"
|
||||
color="inherit"
|
||||
/>
|
||||
</Flex>
|
||||
) : null}
|
||||
</Flex>
|
||||
</NavLink>
|
||||
)}
|
||||
</HStack>
|
||||
</Flex>
|
||||
) : (
|
||||
<ListItem ms={0}>
|
||||
<Flex ps="32px" alignItems="center" mb="8px">
|
||||
<NavLink
|
||||
href={route.layout ? route.layout + route.path : route.path}
|
||||
key={key}
|
||||
>
|
||||
<Text
|
||||
color={
|
||||
activeRoute(route.path.toLowerCase())
|
||||
? activeColor
|
||||
: inactiveColor
|
||||
}
|
||||
fontWeight="500"
|
||||
fontSize="xs"
|
||||
>
|
||||
{route.name}
|
||||
</Text>
|
||||
</NavLink>
|
||||
</Flex>
|
||||
</ListItem>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
// this function creates the links from the secondary accordions (for example auth -> sign-in -> default)
|
||||
const createAccordionLinks = (routes: IRoute[]) => {
|
||||
return routes.map((route: IRoute, key: number) => {
|
||||
return (
|
||||
<ListItem
|
||||
ms="28px"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
mb="10px"
|
||||
key={key}
|
||||
>
|
||||
<NavLink href={route.layout + route.path} key={key}>
|
||||
<Icon w="6px" h="6px" me="8px" as={FaCircle} color={activeIcon} />
|
||||
<Text
|
||||
color={
|
||||
activeRoute(route.path.toLowerCase())
|
||||
? activeColor
|
||||
: inactiveColor
|
||||
}
|
||||
fontWeight={
|
||||
activeRoute(route.path.toLowerCase()) ? 'bold' : 'normal'
|
||||
}
|
||||
fontSize="sm"
|
||||
>
|
||||
{route.name}
|
||||
</Text>
|
||||
</NavLink>
|
||||
</ListItem>
|
||||
);
|
||||
});
|
||||
};
|
||||
// BRAND
|
||||
return <>{createLinks(routes)}</>;
|
||||
}
|
||||
|
||||
export default SidebarLinks;
|
||||
@@ -0,0 +1,556 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
ProductsContext,
|
||||
SubscriptionContext,
|
||||
UserContext
|
||||
} from '@/contexts/layout';
|
||||
import modalImage from '@/public/Modal.png';
|
||||
import SidebarImage from '@/public/SidebarBadge.png';
|
||||
import { Database } from '@/types_db';
|
||||
import { getErrorRedirect } from '@/utils/helpers';
|
||||
import { getStripe } from '@/utils/stripe/client';
|
||||
import { checkoutWithStripe } from '@/utils/stripe/server';
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Flex,
|
||||
Icon,
|
||||
Image,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
ModalContent,
|
||||
ModalOverlay,
|
||||
Text,
|
||||
useColorModeValue,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useContext, useState } from 'react';
|
||||
import { IoIosStar } from 'react-icons/io';
|
||||
import { MdCheckCircle, MdChevronRight } from 'react-icons/md';
|
||||
|
||||
type Price = Database['public']['Tables']['prices']['Row'];
|
||||
interface SidebarCard {
|
||||
[x: string]: any;
|
||||
}
|
||||
export default function SidebarCard(props: SidebarCard) {
|
||||
const [priceIdLoading, setPriceIdLoading] = useState<string>();
|
||||
const textColor = useColorModeValue('#120F43', 'white');
|
||||
const currentPath = usePathname();
|
||||
const products = useContext(ProductsContext);
|
||||
const subscription = useContext(SubscriptionContext);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [plan, setPlan] = useState({
|
||||
product: 'prod_PtTCPDFZbburMa',
|
||||
price: 'price_1P3gGXGx8VbJPRgzdEZODy8K'
|
||||
});
|
||||
const borderColor = 'secondaryGray.200';
|
||||
const router = useRouter();
|
||||
const handleCheckout = async (price: Price) => {
|
||||
setPriceIdLoading(price.id);
|
||||
const user = useContext(UserContext);
|
||||
|
||||
if (!user) {
|
||||
setPriceIdLoading(undefined);
|
||||
return router.push('/dashboard/signin/signup');
|
||||
}
|
||||
|
||||
const { errorRedirect, sessionId } = await checkoutWithStripe(
|
||||
price,
|
||||
currentPath
|
||||
);
|
||||
|
||||
if (errorRedirect) {
|
||||
setPriceIdLoading(undefined);
|
||||
return router.push(errorRedirect);
|
||||
}
|
||||
|
||||
if (!sessionId) {
|
||||
setPriceIdLoading(undefined);
|
||||
return router.push(
|
||||
getErrorRedirect(
|
||||
currentPath,
|
||||
'An unknown error occurred.',
|
||||
'Please try again later or contact a system administrator.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const stripe = await getStripe();
|
||||
stripe?.redirectToCheckout({ sessionId });
|
||||
|
||||
setPriceIdLoading(undefined);
|
||||
};
|
||||
|
||||
if (subscription) {
|
||||
// -------------- PRO User Card --------------
|
||||
return (
|
||||
<Flex
|
||||
align="center"
|
||||
position="relative"
|
||||
border="1px solid"
|
||||
borderColor={borderColor}
|
||||
borderRadius="16px"
|
||||
w="100%"
|
||||
py="16px"
|
||||
px="14px"
|
||||
>
|
||||
<Image alt=" " src={SidebarImage.src} maxW="27px" me="10px" />
|
||||
<Flex direction="column" justify="center" w="100%">
|
||||
<Text fontSize="sm" fontWeight={'700'} color="#120F43" mb="2x">
|
||||
PRO Member
|
||||
</Text>
|
||||
<Text fontWeight={'500'} fontSize="sm" color="gray.500">
|
||||
Unlimited plan active
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
} else {
|
||||
// -------------- Free User Card --------------
|
||||
return (
|
||||
<Flex
|
||||
justify="center"
|
||||
direction="column"
|
||||
align="center"
|
||||
position="relative"
|
||||
border="1px solid"
|
||||
borderColor={borderColor}
|
||||
borderRadius="16px"
|
||||
w="100%"
|
||||
py="20px"
|
||||
px="18px"
|
||||
>
|
||||
<Image alt=" " src={SidebarImage.src} maxW="54px" />
|
||||
<Flex direction="column" mb="12px" w="100%" pt="16px">
|
||||
<Text
|
||||
fontSize="lg"
|
||||
fontWeight={'700'}
|
||||
color="#120F43"
|
||||
mb="10px"
|
||||
textAlign={'center'}
|
||||
>
|
||||
Upgrade to Unlimited
|
||||
</Text>
|
||||
<Text
|
||||
textAlign={'center'}
|
||||
fontWeight={'500'}
|
||||
fontSize="sm"
|
||||
color="gray.500"
|
||||
mb="14px"
|
||||
>
|
||||
Generate premium Essays by upgrading to an unlimited plan!
|
||||
</Text>
|
||||
</Flex>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onOpen();
|
||||
}}
|
||||
py="20px"
|
||||
px="16px"
|
||||
fontSize="sm"
|
||||
variant="primary"
|
||||
borderRadius="45px"
|
||||
w={{ base: '100%' }}
|
||||
h="54px"
|
||||
mb="14px"
|
||||
_hover={{
|
||||
boxShadow: '0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
||||
bg:
|
||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
||||
_disabled: {
|
||||
bg: 'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
||||
}
|
||||
}}
|
||||
>
|
||||
Go unlimited for just $9
|
||||
</Button>
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay bg="rgba(0, 0, 0, 0.85)" />
|
||||
<ModalContent
|
||||
mx="8px"
|
||||
bg="transparent"
|
||||
boxShadow="unset"
|
||||
maxW="unset"
|
||||
w="unset"
|
||||
>
|
||||
<ModalBody p="0px" position={'relative'}>
|
||||
<Flex>
|
||||
<Image
|
||||
display={{ base: 'none', md: 'block' }}
|
||||
zIndex="98"
|
||||
borderLeftRadius="16px"
|
||||
src={modalImage.src}
|
||||
w="340px"
|
||||
alt=" "
|
||||
/>
|
||||
<Flex
|
||||
bg="white"
|
||||
borderLeftRadius={{ base: '16px', md: '0px' }}
|
||||
borderRightRadius="16px"
|
||||
direction={'column'}
|
||||
px={{ base: '30px', md: '42px' }}
|
||||
py="34px"
|
||||
w={{ md: '412px', lg: '456px' }}
|
||||
minW={{ md: '412px', lg: '456px' }}
|
||||
>
|
||||
<Text
|
||||
fontSize="26px"
|
||||
fontWeight={'800'}
|
||||
color={textColor}
|
||||
mb="12px"
|
||||
>
|
||||
Upgrade to Unlimited
|
||||
</Text>
|
||||
<Text
|
||||
mb="24px"
|
||||
fontWeight="500"
|
||||
fontSize="md"
|
||||
color="gray.500"
|
||||
>
|
||||
Get access to all features and generate premium and
|
||||
exclusive essays with our unlimited plan!
|
||||
</Text>
|
||||
{/* Features */}
|
||||
<Flex w={{ base: '100%', xl: '80%' }} direction="column">
|
||||
<Flex alignItems="center" mb="20px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Access to 12+ Essay types
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb="20px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Up to 1500 words per Essay
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb="20px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Academic Citation formats (APA, etc.)
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb="20px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Academic Levels (Master, etc.)
|
||||
</Text>
|
||||
</Flex>
|
||||
<Flex alignItems="center" mb="30px">
|
||||
<Icon
|
||||
me="10px"
|
||||
w="20px"
|
||||
h="20px"
|
||||
color={'green.500'}
|
||||
as={MdCheckCircle}
|
||||
/>
|
||||
<Text
|
||||
as="span"
|
||||
fontSize="sm"
|
||||
fontWeight="600"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
>
|
||||
Essay Tones (Academic, etc.)
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{/* YEARLY */}
|
||||
<Flex
|
||||
onClick={() =>
|
||||
setPlan({
|
||||
product: 'prod_PtTJ6R3RnzmIPX',
|
||||
price: 'price_1P3gMyGx8VbJPRgzkoB6Fp8F'
|
||||
})
|
||||
}
|
||||
transition="0.15s linear"
|
||||
align="center"
|
||||
position="relative"
|
||||
border="1px solid"
|
||||
borderColor={
|
||||
plan.product === 'prod_PtTJ6R3RnzmIPX'
|
||||
? 'brand.500'
|
||||
: borderColor
|
||||
}
|
||||
borderRadius="10px"
|
||||
w="100%"
|
||||
py="14px"
|
||||
px="14px"
|
||||
cursor="pointer"
|
||||
mb="20px"
|
||||
>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight={'700'}
|
||||
color="#120F43"
|
||||
mb="2x"
|
||||
ms="8px"
|
||||
me="8px"
|
||||
>
|
||||
Yearly
|
||||
</Text>
|
||||
<Badge
|
||||
display={{
|
||||
base: 'flex',
|
||||
lg: 'none',
|
||||
xl: 'flex'
|
||||
}}
|
||||
colorScheme="green"
|
||||
borderRadius="4px"
|
||||
color="green.500"
|
||||
textTransform={'none'}
|
||||
letterSpacing="0px"
|
||||
px="0px"
|
||||
w="max-content"
|
||||
>
|
||||
Save 35%
|
||||
</Badge>
|
||||
<Text
|
||||
display="flex"
|
||||
ms="auto"
|
||||
fontSize="md"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
fontWeight="600"
|
||||
lineHeight="100%"
|
||||
>
|
||||
$5.75
|
||||
<Text
|
||||
fontSize={'14px'}
|
||||
color="gray.500"
|
||||
fontWeight="500"
|
||||
ms="4px"
|
||||
as="span"
|
||||
>
|
||||
/month
|
||||
</Text>
|
||||
</Text>
|
||||
</Flex>
|
||||
{/* END YEARLY */}
|
||||
{/* MONTHLY */}
|
||||
<Flex
|
||||
onClick={() =>
|
||||
setPlan({
|
||||
product: 'prod_PtTCPDFZbburMa',
|
||||
price: 'price_1P3gGXGx8VbJPRgzdEZODy8K'
|
||||
})
|
||||
}
|
||||
transition="0.15s linear"
|
||||
align="center"
|
||||
position="relative"
|
||||
border="1px solid"
|
||||
borderColor={
|
||||
plan.product === 'prod_PtTCPDFZbburMa'
|
||||
? 'brand.500'
|
||||
: borderColor
|
||||
}
|
||||
borderRadius="10px"
|
||||
w="100%"
|
||||
py="16px"
|
||||
px="14px"
|
||||
cursor="pointer"
|
||||
mb="28px"
|
||||
>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight={'700'}
|
||||
color="#120F43"
|
||||
mb="2x"
|
||||
ms="8px"
|
||||
me="4px"
|
||||
>
|
||||
Monthly
|
||||
</Text>
|
||||
<Text
|
||||
display="flex"
|
||||
ms="auto"
|
||||
fontSize="md"
|
||||
color={textColor}
|
||||
letterSpacing="0px"
|
||||
fontWeight="600"
|
||||
lineHeight="100%"
|
||||
>
|
||||
$9
|
||||
<Text
|
||||
fontSize={'14px'}
|
||||
color="gray.500"
|
||||
fontWeight="500"
|
||||
ms="4px"
|
||||
as="span"
|
||||
>
|
||||
/month
|
||||
</Text>
|
||||
</Text>
|
||||
</Flex>
|
||||
{/* END MONTHLY */}
|
||||
{products.map((product: any) => {
|
||||
const price = product?.prices?.find(
|
||||
(price: any) => price.id === plan.price
|
||||
);
|
||||
if (product.id === plan.product) {
|
||||
if (!price) return null;
|
||||
return (
|
||||
<Button
|
||||
key={product.id}
|
||||
py="20px"
|
||||
px="16px"
|
||||
fontSize="sm"
|
||||
variant="primary"
|
||||
borderRadius="45px"
|
||||
w={{ base: '100%' }}
|
||||
h="54px"
|
||||
mb="28px"
|
||||
_hover={{
|
||||
boxShadow:
|
||||
'0px 21px 27px -10px rgba(96, 60, 255, 0.48) !important',
|
||||
bg:
|
||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%) !important',
|
||||
_disabled: {
|
||||
bg:
|
||||
'linear-gradient(15.46deg, #4A25E1 26.3%, #7B5AFF 86.4%)'
|
||||
}
|
||||
}}
|
||||
onClick={() => handleCheckout(price)}
|
||||
>
|
||||
Upgrade now
|
||||
<Icon
|
||||
as={MdChevronRight}
|
||||
mt="2px"
|
||||
h="16px"
|
||||
w="16px"
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
})}
|
||||
<Text
|
||||
fontSize="xs"
|
||||
color="gray.500"
|
||||
fontWeight={'500'}
|
||||
mx="auto"
|
||||
mb="5px"
|
||||
>
|
||||
Used by 80,000+ users monthly
|
||||
</Text>
|
||||
<Flex direction="row" alignItems="center" mx="auto">
|
||||
<Icon
|
||||
me="1px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Icon
|
||||
me="1px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Icon
|
||||
me="1px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Icon
|
||||
me="1px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Icon
|
||||
me="6px"
|
||||
w="16px"
|
||||
h="16px"
|
||||
color="orange.500"
|
||||
as={IoIosStar}
|
||||
/>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="800"
|
||||
h="100%"
|
||||
color={textColor}
|
||||
>
|
||||
4.9
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
<ModalCloseButton
|
||||
borderRadius="full"
|
||||
color="#120F43"
|
||||
bg="#F4F6FB !important"
|
||||
_hover={{ bg: '#E9EDF6 !important' }}
|
||||
_focus={{ bg: '#F4F6FB !important' }}
|
||||
_active={{ bg: '#F4F6FB !important' }}
|
||||
zIndex="99"
|
||||
/>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
<Text
|
||||
textAlign={'center'}
|
||||
fontWeight={'500'}
|
||||
fontSize="xs"
|
||||
color="gray.500"
|
||||
>
|
||||
Join 80,000+ users now
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user