feat(account): ✨ Add coupon code input
This commit is contained in:
@@ -1,12 +1,44 @@
|
|||||||
import { Stack, Heading, HStack, Button, Text } from '@chakra-ui/react'
|
import {
|
||||||
|
Stack,
|
||||||
|
Heading,
|
||||||
|
HStack,
|
||||||
|
Button,
|
||||||
|
Text,
|
||||||
|
Input,
|
||||||
|
useToast,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||||
import { useUser } from 'contexts/UserContext'
|
import { useUser } from 'contexts/UserContext'
|
||||||
import { Plan } from 'db'
|
import { Plan } from 'db'
|
||||||
import React from 'react'
|
import { useRouter } from 'next/router'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import { redeemCoupon } from 'services/coupons'
|
||||||
import { SubscriptionTag } from './SubscriptionTag'
|
import { SubscriptionTag } from './SubscriptionTag'
|
||||||
|
|
||||||
export const BillingSection = () => {
|
export const BillingSection = () => {
|
||||||
|
const { reload } = useRouter()
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
|
||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
|
const toast = useToast({
|
||||||
|
position: 'top-right',
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleCouponCodeRedeem = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const target = e.target as typeof e.target & {
|
||||||
|
coupon: { value: string }
|
||||||
|
}
|
||||||
|
setIsLoading(true)
|
||||||
|
const { data, error } = await redeemCoupon(target.coupon.value)
|
||||||
|
if (error) toast({ title: error.name, description: error.message })
|
||||||
|
else {
|
||||||
|
toast({ description: data?.message })
|
||||||
|
setTimeout(reload, 1000)
|
||||||
|
}
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack direction="row" spacing="10" justifyContent={'space-between'}>
|
<Stack direction="row" spacing="10" justifyContent={'space-between'}>
|
||||||
<Heading as="h2" fontSize="xl">
|
<Heading as="h2" fontSize="xl">
|
||||||
@@ -25,6 +57,14 @@ export const BillingSection = () => {
|
|||||||
{user?.plan === Plan.FREE && (
|
{user?.plan === Plan.FREE && (
|
||||||
<Button colorScheme="blue">Upgrade</Button>
|
<Button colorScheme="blue">Upgrade</Button>
|
||||||
)}
|
)}
|
||||||
|
{user?.plan === Plan.FREE && (
|
||||||
|
<HStack as="form" onSubmit={handleCouponCodeRedeem}>
|
||||||
|
<Input name="coupon" placeholder="Coupon code..." />
|
||||||
|
<Button type="submit" isLoading={isLoading}>
|
||||||
|
Redeem
|
||||||
|
</Button>
|
||||||
|
</HStack>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
|
|||||||
31
apps/builder/pages/api/coupons/redeem.ts
Normal file
31
apps/builder/pages/api/coupons/redeem.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Prisma, User } from 'db'
|
||||||
|
import prisma from 'libs/prisma'
|
||||||
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
import { getSession } from 'next-auth/react'
|
||||||
|
|
||||||
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
if (req.method === 'POST') {
|
||||||
|
const session = await getSession({ req })
|
||||||
|
|
||||||
|
if (!session?.user)
|
||||||
|
return res.status(401).json({ message: 'Not authenticated' })
|
||||||
|
|
||||||
|
const user = session.user as User
|
||||||
|
const { code } = JSON.parse(req.body)
|
||||||
|
const coupon = await prisma.coupon.findFirst({
|
||||||
|
where: { code, dateRedeemed: null },
|
||||||
|
})
|
||||||
|
if (!coupon) return res.status(404).send({ message: 'Coupon not found' })
|
||||||
|
await prisma.user.update({
|
||||||
|
where: { id: user.id },
|
||||||
|
data: coupon.userPropertiesToUpdate as Prisma.UserUncheckedUpdateInput,
|
||||||
|
})
|
||||||
|
await prisma.coupon.update({
|
||||||
|
where: { code },
|
||||||
|
data: { dateRedeemed: new Date() },
|
||||||
|
})
|
||||||
|
return res.send({ message: 'Coupon redeemed 🎊' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default handler
|
||||||
8
apps/builder/services/coupons.ts
Normal file
8
apps/builder/services/coupons.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { sendRequest } from 'utils'
|
||||||
|
|
||||||
|
export const redeemCoupon = async (code: string) =>
|
||||||
|
sendRequest<{ message: string }>({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/api/coupons/redeem',
|
||||||
|
body: { code },
|
||||||
|
})
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "dotenv -e .env yarn prisma db push && BROWSER=none yarn prisma studio",
|
"dev": "dotenv -e .env yarn prisma db push && BROWSER=none yarn prisma studio",
|
||||||
"build": "yarn migration:push",
|
"build": "yarn migration:push",
|
||||||
"migration:push": "dotenv -e ../../.env yarn prisma db push",
|
"migration:push": "dotenv -e .env yarn prisma db push",
|
||||||
"migration:create": "dotenv -e ../../.env yarn prisma migrate dev",
|
"migration:create": "dotenv -e .env yarn prisma migrate dev",
|
||||||
"migration:reset": "dotenv -e ../../.env yarn prisma migrate reset"
|
"migration:reset": "dotenv -e .env yarn prisma migrate reset"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ model User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Credentials {
|
model Credentials {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
ownerId String
|
ownerId String
|
||||||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||||
data String // Encrypted data
|
data String // Encrypted data
|
||||||
name String
|
name String
|
||||||
type String
|
type String
|
||||||
@@ -143,3 +143,9 @@ model Answer {
|
|||||||
|
|
||||||
@@unique([resultId, blockId, stepId])
|
@@unique([resultId, blockId, stepId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Coupon {
|
||||||
|
userPropertiesToUpdate Json
|
||||||
|
code String @id
|
||||||
|
dateRedeemed DateTime?
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user