Skip to content

Commit

Permalink
Merge pull request #11 from yun-cheng/feat/home-page-pricing-card-fun…
Browse files Browse the repository at this point in the history
…tionality

feat(HomePage): add PricingCard funtionality
  • Loading branch information
yun-cheng authored Apr 29, 2024
2 parents bf85040 + 3f3dc42 commit d4720c8
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 63 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/firebase-hosting-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ jobs:
node-version: 20
cache: 'pnpm'
- run: pnpm install && pnpm build
env:
VITE_COINGECKO_API_KEY: '${{ secrets.VITE_COINGECKO_API_KEY }}'
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/firebase-hosting-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ jobs:
node-version: 20
cache: 'pnpm'
- run: pnpm install && pnpm build
env:
VITE_COINGECKO_API_KEY: '${{ secrets.VITE_COINGECKO_API_KEY }}'
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
Expand Down
16 changes: 16 additions & 0 deletions src/api/fetchCroPrice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { VITE_COINGECKO_API_KEY } = import.meta.env

type ResponseData = {
'crypto-com-chain': {
usd: number
}
}

export default async function fetchCroPrice() {
const response = await fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=crypto-com-chain&vs_currencies=usd&x_cg_demo_api_key=${VITE_COINGECKO_API_KEY}`
)
const data = (await response.json()) as ResponseData

return data['crypto-com-chain'].usd
}
36 changes: 36 additions & 0 deletions src/atoms/pricing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { atom } from 'jotai'

const planPricing = [
{
pageViews: '10K',
price: 8
},
{
pageViews: '50K',
price: 12
},
{
pageViews: '100K',
price: 16
},
{
pageViews: '500K',
price: 24
},
{
pageViews: '1M',
price: 36
}
]

export const planIdAtom = atom(2)

export const isYearlyBillingAtom = atom(false)

export const pageViewsAtom = atom(get => planPricing[get(planIdAtom)].pageViews)

export const priceAtom = atom(get => {
return (
planPricing[get(planIdAtom)].price * (get(isYearlyBillingAtom) ? 0.75 : 1)
)
})
30 changes: 18 additions & 12 deletions src/components/PricingCard/PricingCardPageViews.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { pageViewsAtom } from 'atoms/pricing'
import { useAtomValue } from 'jotai'
import type { HTMLAttributes } from 'react'
import { forwardRef } from 'react'
import cn from 'utils/cn'

const PricingCardPageViews = forwardRef<
HTMLDivElement,
HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
'font-extrabold tracking-[1.7px] sm:text-[14px] sm:tracking-[2px]',
className
)}
{...props}
>
100K PAGEVIEWS
</div>
))
>(({ className, ...props }, ref) => {
const pageViews = useAtomValue(pageViewsAtom)

return (
<div
ref={ref}
className={cn(
'font-extrabold tracking-[1.7px] sm:text-[14px] sm:tracking-[2px]',
className
)}
{...props}
>
{pageViews} PAGEVIEWS
</div>
)
})

export default PricingCardPageViews
67 changes: 43 additions & 24 deletions src/components/PricingCard/PricingCardPrice.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,56 @@
import { useQuery } from '@tanstack/react-query'
import fetchCroPrice from 'api/fetchCroPrice'
import { priceAtom } from 'atoms/pricing'
import { useAtomValue } from 'jotai'
import type { HTMLAttributes } from 'react'
import { forwardRef } from 'react'
import cn from 'utils/cn'

const PricingCardPrice = forwardRef<
HTMLDivElement,
HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={className} {...props}>
<div
className={cn('-translate-x-px', 'flex items-center gap-[10px] sm:gap-2')}
>
<span className='text-[32px] font-extrabold leading-none -tracking-[.8px] text-dark-desaturated-blue sm:text-[40px] sm:-tracking-[1px]'>
$16.00
</span>
<span
>(({ className, ...props }, ref) => {
const price = useAtomValue(priceAtom)

const { data: croToUsd } = useQuery({
queryKey: ['fetchCroPrice'],
queryFn: fetchCroPrice,
staleTime: 60_000
})

const croPrice = croToUsd ? Math.round(price / croToUsd).toString() : ''

return (
<div ref={ref} className={className} {...props}>
<div
className={cn(
'translate-y-px sm:translate-y-0',
'text-[14px] sm:text-[16px]'
'-translate-x-px',
'flex items-center gap-[10px] sm:gap-2'
)}
>
/ month
</span>
</div>
<div
className={cn(
'mt-[2px] flex justify-end sm:mt-[7px]',
'whitespace-pre-wrap text-[10px] tracking-[.2px] sm:text-[14px] sm:-tracking-[.4px]'
)}
>
{`or ~32 CRO `}
<span className='-translate-x-px sm:translate-x-0'>/ month</span>
<span className='text-[32px] font-extrabold leading-none -tracking-[.8px] text-dark-desaturated-blue sm:text-[40px] sm:-tracking-[1px]'>
${price}.00
</span>
<span
className={cn(
'translate-y-px sm:translate-y-0',
'text-[14px] sm:text-[16px]'
)}
>
/ month
</span>
</div>
<div
className={cn(
'mt-[2px] flex justify-end sm:mt-[7px]',
'whitespace-pre-wrap text-[10px] tracking-[.2px] sm:text-[14px] sm:-tracking-[.4px]'
)}
>
{`or ~${croPrice} CRO `}
<span className='-translate-x-px sm:translate-x-0'>/ month</span>
</div>
</div>
</div>
))
)
})

export default PricingCardPrice
20 changes: 15 additions & 5 deletions src/components/PricingCard/PricingCardSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { planIdAtom } from 'atoms/pricing'
import { Slider } from 'components/ui/Slider'
import { useAtom } from 'jotai'
import { forwardRef, type HTMLAttributes } from 'react'
import cn from 'utils/cn'

const PriceCardSlider = forwardRef<
HTMLDivElement,
HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn('w-full', className)} {...props}>
<Slider defaultValue={[2]} max={4} />
</div>
))
>(({ className, ...props }, ref) => {
const [planId, setPlanId] = useAtom(planIdAtom)

const onValueChange = (value: number[]) => {
setPlanId(value[0])
}

return (
<div ref={ref} className={cn('w-full', className)} {...props}>
<Slider value={[planId]} max={4} onValueChange={onValueChange} />
</div>
)
})

export default PriceCardSlider
54 changes: 33 additions & 21 deletions src/components/PricingCard/PricingCardSwitch.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,46 @@
import { isYearlyBillingAtom } from 'atoms/pricing'
import { Switch } from 'components/ui/Switch'
import { useAtom } from 'jotai'
import { forwardRef, type HTMLAttributes } from 'react'
import cn from 'utils/cn'

const PricingCardSwitch = forwardRef<
HTMLDivElement,
HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('flex items-center justify-end', className)}
{...props}
>
<div className='sm:-translate-y-px'>Monthly Billing</div>
<Switch className='ml-[13px] mr-3 sm:ml-[17px] sm:mr-4' />
<div className={cn('sm:-translate-y-px', 'mr-[5px] sm:mr-[9px]')}>
Yearly Billing
</div>
>(({ className, ...props }, ref) => {
const [isYearlyBilling, setIsYearlyBilling] = useAtom(isYearlyBillingAtom)

const onCheckedChange = (checked: boolean) => setIsYearlyBilling(checked)

return (
<div
className={cn(
'-translate-y-px',
'flex h-[19px] w-[42px] items-center justify-center rounded-lg bg-light-grayish-red sm:w-20',
'text-[10px] font-extrabold text-light-red'
)}
ref={ref}
className={cn('flex items-center justify-end', className)}
{...props}
>
<div className='-translate-x-px'>
<span className='hidden sm:block'>25% discount</span>
<span className='block sm:hidden'>-25%</span>
<div className='sm:-translate-y-px'>Monthly Billing</div>
<Switch
className='ml-[13px] mr-3 sm:ml-[17px] sm:mr-4'
checked={isYearlyBilling}
onCheckedChange={onCheckedChange}
/>
<div className={cn('sm:-translate-y-px', 'mr-[5px] sm:mr-[9px]')}>
Yearly Billing
</div>
<div
className={cn(
'-translate-y-px',
'flex h-[19px] w-[42px] items-center justify-center rounded-lg bg-light-grayish-red sm:w-20',
'text-[10px] font-extrabold text-light-red'
)}
>
<div className='-translate-x-px'>
<span className='hidden sm:block'>25% discount</span>
<span className='block sm:hidden'>-25%</span>
</div>
</div>
</div>
</div>
))
)
})

export default PricingCardSwitch
2 changes: 1 addition & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import './index.css'

registerSW()

const MAX_RETRIES = 1
const MAX_RETRIES = 5
const queryClient = new QueryClient({
defaultOptions: {
queries: {
Expand Down

0 comments on commit d4720c8

Please sign in to comment.