Skip to content

Commit

Permalink
feat: add new pricing table with free trial
Browse files Browse the repository at this point in the history
also record refcode in the plan gate, too
  • Loading branch information
travis committed Nov 6, 2024
1 parent 1f6c1fa commit ccdefc1
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 40 deletions.
16 changes: 16 additions & 0 deletions src/app/referrals/referredby/[email]/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getRequestContext } from "@cloudflare/next-on-pages"

export const runtime = "edge"

export async function GET (
request: Request,
{ params: { email } }: { params: { email: string } }
) {
const result = await getRequestContext().env.REFERRALS.
prepare(`SELECT refcode FROM referrals WHERE email = ?`).
bind(email).
first()
return Response.json({
refcode: result?.refcode
})
}
24 changes: 24 additions & 0 deletions src/app/referrals/referredby/create/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { getRequestContext } from "@cloudflare/next-on-pages"
import { generateRefcode } from "@/lib/referrals"

export const runtime = "edge"

export async function POST(request: Request){
const form = await request.formData()
const email = form.get('email')
const refcode = generateRefcode()
const referralsDB = getRequestContext().env.REFERRALS
const insertStmt = referralsDB.prepare(`
INSERT INTO users (email, refcode)
VALUES (?, ?)
`).bind(email, refcode)
const result = await insertStmt.run()
if (result.error) {
return {
errors: [result.error]
}
}
return Response.json({
refcode
})
}
24 changes: 3 additions & 21 deletions src/components/Authenticator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import {
Authenticator as AuthCore,
useAuthenticator
} from '@w3ui/react'
import { useSearchParams } from 'next/navigation'
import { Logo } from '../brand'
import { TopLevelLoader } from './Loader'
import { useEffect } from 'react'
import { createReferral } from '@/lib/referrals'

import { useRecordRefcode } from '@/lib/referrals/hooks'

export function AuthenticationForm (): JSX.Element {
const [{ submitted }] = useAuthenticator()
Expand Down Expand Up @@ -39,25 +38,8 @@ export function AuthenticationForm (): JSX.Element {
)
}

function useURLRefcode () {
const searchParams = useSearchParams()
return searchParams.get('refcode')
}

export function AuthenticationSubmitted (): JSX.Element {
const [{ email }] = useAuthenticator()
const refcode = useURLRefcode()
useEffect(() => {
(async function createReferralForEmailAndReferrer () {
if (email && refcode) {
const formData = new FormData()
formData.append('email', email)
formData.append('refcode', refcode)
console.log(`recording ${email} as referred by ${refcode}`)
await createReferral(formData)
}
})()
}, [email, refcode])
useRecordRefcode()

return (
<div className='authenticator'>
Expand Down
51 changes: 35 additions & 16 deletions src/components/PlanGate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,56 @@

import { ReactNode, useState } from 'react'
import { useW3 } from '@w3ui/react'
import StripePricingTable from './PricingTable';
import StripePricingTable, { StripeTrialPricingTable } from './PricingTable';
import { TopLevelLoader } from './Loader';
import { Logo } from '@/brand';
import { usePlan } from '@/hooks';
import { useRecordRefcode, useReferredBy } from '@/lib/referrals/hooks';

export function PlanGate ({ children }: { children: ReactNode }): ReactNode {
const [{ accounts }] = useW3()
const email = accounts[0]?.toEmail()
const { data: plan, error } = usePlan(accounts[0])
const { referredBy } = useRecordRefcode()
if (!plan && !error) {
return <TopLevelLoader />
}

if (!plan?.product) {
return (
<div className="flex flex-col justify-center items-center min-h-screen">
<div className='my-6'><Logo /></div>
<div className="max-w-screen-lg font-epilogue text-black text-center bg-white border border-hot-red rounded-2xl overflow-hidden p5 mx-4 mb-4">
<div className='px-6 py-6 lg:px-24'>
<h1 className="my-4 font-bold">Welcome, {accounts[0]?.toEmail()}!</h1>
<p className='my-4'>
To get started you&apos;ll need to sign up for a subscription. If you choose
the starter plan we won&apos;t charge your credit card, but we do need a card on file
before we will store your bits.
</p>
<p className='my-4'>
Pick a plan below and complete the Stripe signup flow to get started!
</p>
</div>
<StripePricingTable />
</div>
</div>
{referredBy ? (
<>
<div className='px-6 py-6 lg:px-24'>
<h1 className="my-4 font-bold">Welcome, {email}!</h1>
<p className='my-4'>
Congratulations! You are eligible for a free trial of our Lite or Business subscriptions. That means
we won&apos;t charge you anything today. We do need you to provide a valid credit card before we can start your
trial - pick a plan below and complete the checkout flow to get started!
</p>
</div>
<StripeTrialPricingTable />
</>
) : (
<>
<div className='px-6 py-6 lg:px-24'>
<h1 className="my-4 font-bold">Welcome, {email}!</h1>
<p className='my-4'>
To get started you&apos;ll need to sign up for a subscription. If you choose
the starter plan we won&apos;t charge your credit card, but we do need a card on file
before we will store your bits.
</p>
<p className='my-4'>
Pick a plan below and complete the Stripe checkout flow to get started!
</p>
</div>
<StripePricingTable />
</>
)
}
</div >
</div >
)
}

Expand Down
15 changes: 15 additions & 0 deletions src/components/PricingTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,18 @@ export default function StripePricingTable ({ className = '' }) {
</>
)
}

export function StripeTrialPricingTable ({ className = '' }) {
const [{ accounts }] = useW3()
return (
<>
<Script src="https://js.stripe.com/v3/pricing-table.js" />
{createElement('stripe-pricing-table', {
'pricing-table-id': process.env.NEXT_PUBLIC_STRIPE_TRIAL_PRICING_TABLE_ID,
'publishable-key': process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
'customer-email': accounts[0]?.toEmail(),
className
}, '')}
</>
)
}
44 changes: 41 additions & 3 deletions src/lib/referrals/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use client'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import useSWR from "swr"
import { useW3 } from "@w3ui/react"
import { createRefcode } from '../referrals'
import { useAuthenticator, useW3 } from "@w3ui/react"
import { createRefcode, createReferral } from '../referrals'
import { useSearchParams } from 'next/navigation'

interface RefcodeResult {
refcode: string
Expand Down Expand Up @@ -35,4 +36,41 @@ export function useReferrals () {
referrerEmail, setReferrerEmail, accountEmail, email,
refcode, createRefcode, mutateRefcode, referrals, referralLink
}
}

export function useReferredBy () {
const [{ accounts }] = useW3()
const account = accounts[0]
const email = account?.toEmail()
const { data: referredByResult, isLoading: referredByIsLoading } = useSWR<RefcodeResult>(email && `/referrals/referredby/${encodeURIComponent(email)}`, fetcher)
const referredBy = referredByResult?.refcode
return {
referredBy
}
}

function useURLRefcode () {
const searchParams = useSearchParams()
return searchParams.get('refcode')
}

export function useRecordRefcode () {
const [{ email }] = useAuthenticator()
const urlRefcode = useURLRefcode()
useEffect(() => {
(async function createReferralForEmailAndReferrer () {
if (email && urlRefcode) {
const formData = new FormData()
formData.append('email', email)
formData.append('refcode', urlRefcode)
console.log(`recording ${email} as referred by ${urlRefcode}`)
await createReferral(formData)
}
})()
}, [email, urlRefcode])

const { data: referredByResult, isLoading } = useSWR<RefcodeResult>(email && `/referrals/referredby/${encodeURIComponent(email)}`, fetcher)
const referredBy = referredByResult?.refcode

return { referredBy: referredBy || urlRefcode, isLoading }
}

0 comments on commit ccdefc1

Please sign in to comment.