diff --git a/apps/builder/components/dashboard/WorkspaceSettingsModal/BillingContent/CurrentSubscriptionContent.tsx b/apps/builder/components/dashboard/WorkspaceSettingsModal/BillingContent/CurrentSubscriptionContent.tsx
index 854057ac710..ca755b44c8f 100644
--- a/apps/builder/components/dashboard/WorkspaceSettingsModal/BillingContent/CurrentSubscriptionContent.tsx
+++ b/apps/builder/components/dashboard/WorkspaceSettingsModal/BillingContent/CurrentSubscriptionContent.tsx
@@ -7,6 +7,7 @@ import {
Button,
Heading,
} from '@chakra-ui/react'
+import { useToast } from 'components/shared/hooks/useToast'
import { PlanTag } from 'components/shared/PlanTag'
import { Plan } from 'db'
import React, { useState } from 'react'
@@ -26,38 +27,48 @@ export const CurrentSubscriptionContent = ({
const [isCancelling, setIsCancelling] = useState(false)
const [isRedirectingToBillingPortal, setIsRedirectingToBillingPortal] =
useState(false)
+ const { showToast } = useToast()
const cancelSubscription = async () => {
if (!stripeId) return
setIsCancelling(true)
- await cancelSubscriptionQuery(stripeId)
+ const { error } = await cancelSubscriptionQuery(stripeId)
+ if (error) {
+ showToast({ description: error.message })
+ return
+ }
onCancelSuccess()
setIsCancelling(false)
}
const isSubscribed = (plan === Plan.STARTER || plan === Plan.PRO) && stripeId
- if (isCancelling) return
return (
Subscription
Current workspace subscription:
-
- {isSubscribed && (
-
- Cancel my subscription
-
+ {isCancelling ? (
+
+ ) : (
+ <>
+
+ {isSubscribed && (
+
+ Cancel my subscription
+
+ )}
+ >
)}
- {isSubscribed && (
+ {isSubscribed && !isCancelling && (
<>
diff --git a/apps/builder/components/shared/ChangePlanForm/ChangePlanForm.tsx b/apps/builder/components/shared/ChangePlanForm/ChangePlanForm.tsx
index bf0dc2243e9..dc576c3f7da 100644
--- a/apps/builder/components/shared/ChangePlanForm/ChangePlanForm.tsx
+++ b/apps/builder/components/shared/ChangePlanForm/ChangePlanForm.tsx
@@ -35,7 +35,7 @@ export const ChangePlanForm = () => {
selectedStorageLimitIndex === undefined
)
return
- await pay({
+ const response = await pay({
stripeId: workspace.stripeId ?? undefined,
user,
plan,
@@ -43,6 +43,10 @@ export const ChangePlanForm = () => {
additionalChats: selectedChatsLimitIndex,
additionalStorage: selectedStorageLimitIndex,
})
+ if (typeof response === 'object' && response?.error) {
+ showToast({ description: response.error.message })
+ return
+ }
refreshCurrentSubscriptionInfo({
additionalChatsIndex: selectedChatsLimitIndex,
additionalStorageIndex: selectedStorageLimitIndex,
diff --git a/apps/builder/components/shared/ChangePlanForm/queries/updatePlan.tsx b/apps/builder/components/shared/ChangePlanForm/queries/updatePlan.tsx
index 439d535fedc..91f225ba4a3 100644
--- a/apps/builder/components/shared/ChangePlanForm/queries/updatePlan.tsx
+++ b/apps/builder/components/shared/ChangePlanForm/queries/updatePlan.tsx
@@ -20,7 +20,7 @@ type UpgradeProps = {
export const pay = async ({
stripeId,
...props
-}: UpgradeProps): Promise<{ newPlan: Plan } | undefined | void> =>
+}: UpgradeProps): Promise<{ newPlan?: Plan; error?: Error } | void> =>
isDefined(stripeId)
? updatePlan({ ...props, stripeId })
: redirectToCheckout(props)
@@ -31,13 +31,13 @@ export const updatePlan = async ({
workspaceId,
additionalChats,
additionalStorage,
-}: Omit): Promise<{ newPlan: Plan } | undefined> => {
+}: Omit): Promise<{ newPlan?: Plan; error?: Error }> => {
const { data, error } = await sendRequest<{ message: string }>({
method: 'PUT',
url: '/api/stripe/subscription',
body: { workspaceId, plan, stripeId, additionalChats, additionalStorage },
})
- if (error || !data) return
+ if (error || !data) return { error }
return { newPlan: plan }
}
diff --git a/apps/builder/pages/_app.tsx b/apps/builder/pages/_app.tsx
index 891bd0c20a4..30c88eedba5 100644
--- a/apps/builder/pages/_app.tsx
+++ b/apps/builder/pages/_app.tsx
@@ -18,6 +18,7 @@ import { SupportBubble } from 'components/shared/SupportBubble'
import { WorkspaceContext } from 'contexts/WorkspaceContext'
import { toTitleCase } from 'utils'
import { Session } from 'next-auth'
+import { Plan } from 'db'
const { ToastContainer, toast } = createStandaloneToast(customTheme)
@@ -35,7 +36,14 @@ const App = ({
}, [pathname])
useEffect(() => {
- displayStripeCallbackMessage(query.stripe?.toString(), toast)
+ const newPlan = query.stripe?.toString()
+ if (newPlan === Plan.STARTER || newPlan === Plan.PRO)
+ toast({
+ position: 'bottom-right',
+ status: 'success',
+ title: 'Upgrade success!',
+ description: `Workspace upgraded to ${toTitleCase(status)} 🎉`,
+ })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isReady])
@@ -68,19 +76,4 @@ const App = ({
)
}
-const displayStripeCallbackMessage = (
- status: string | undefined,
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- toast: any
-) => {
- if (status && ['pro', 'team'].includes(status)) {
- toast({
- position: 'bottom-right',
- status: 'success',
- title: 'Upgrade success!',
- description: `Workspace upgraded to ${toTitleCase(status)} 🎉`,
- })
- }
-}
-
export default App
diff --git a/apps/builder/pages/api/stripe/subscription.ts b/apps/builder/pages/api/stripe/subscription.ts
index f15fd744896..2af21c4dd4a 100644
--- a/apps/builder/pages/api/stripe/subscription.ts
+++ b/apps/builder/pages/api/stripe/subscription.ts
@@ -54,12 +54,12 @@ const getSubscriptionDetails =
})
return {
additionalChatsIndex:
- subscriptions.data[0].items.data.find(
+ subscriptions.data[0]?.items.data.find(
(item) =>
item.price.id === process.env.STRIPE_ADDITIONAL_CHATS_PRICE_ID
)?.quantity ?? 0,
additionalStorageIndex:
- subscriptions.data[0].items.data.find(
+ subscriptions.data[0]?.items.data.find(
(item) =>
item.price.id === process.env.STRIPE_ADDITIONAL_STORAGE_PRICE_ID
)?.quantity ?? 0,
@@ -100,33 +100,34 @@ const createCheckoutSession = (req: NextApiRequest) => {
}
const updateSubscription = async (req: NextApiRequest) => {
- const { customerId, plan, workspaceId, additionalChats, additionalStorage } =
- (typeof req.body === 'string' ? JSON.parse(req.body) : req.body) as {
- customerId: string
- workspaceId: string
- additionalChats: number
- additionalStorage: number
- plan: 'STARTER' | 'PRO'
- }
+ const { stripeId, plan, workspaceId, additionalChats, additionalStorage } = (
+ typeof req.body === 'string' ? JSON.parse(req.body) : req.body
+ ) as {
+ stripeId: string
+ workspaceId: string
+ additionalChats: number
+ additionalStorage: number
+ plan: 'STARTER' | 'PRO'
+ }
if (!process.env.STRIPE_SECRET_KEY)
throw Error('STRIPE_SECRET_KEY var is missing')
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2022-08-01',
})
const { data } = await stripe.subscriptions.list({
- customer: customerId,
+ customer: stripeId,
})
- const subscription = data[0]
- const currentStarterPlanItemId = subscription.items.data.find(
+ const subscription = data[0] as Stripe.Subscription | undefined
+ const currentStarterPlanItemId = subscription?.items.data.find(
(item) => item.price.id === process.env.STRIPE_STARTER_PRICE_ID
)?.id
- const currentProPlanItemId = subscription.items.data.find(
+ const currentProPlanItemId = subscription?.items.data.find(
(item) => item.price.id === process.env.STRIPE_PRO_PRICE_ID
)?.id
- const currentAdditionalChatsItemId = subscription.items.data.find(
+ const currentAdditionalChatsItemId = subscription?.items.data.find(
(item) => item.price.id === process.env.STRIPE_ADDITIONAL_CHATS_PRICE_ID
)?.id
- const currentAdditionalStorageItemId = subscription.items.data.find(
+ const currentAdditionalStorageItemId = subscription?.items.data.find(
(item) => item.price.id === process.env.STRIPE_ADDITIONAL_STORAGE_PRICE_ID
)?.id
const items = [
@@ -155,9 +156,18 @@ const updateSubscription = async (req: NextApiRequest) => {
deleted: additionalStorage === 0,
},
].filter(isDefined)
- await stripe.subscriptions.update(subscription.id, {
- items,
- })
+
+ if (subscription) {
+ await stripe.subscriptions.update(subscription.id, {
+ items,
+ })
+ } else {
+ await stripe.subscriptions.create({
+ customer: stripeId,
+ items,
+ })
+ }
+
await prisma.workspace.update({
where: { id: workspaceId },
data: {
@@ -187,7 +197,10 @@ const cancelSubscription =
const existingSubscription = await stripe.subscriptions.list({
customer: workspace.stripeId,
})
- await stripe.subscriptions.del(existingSubscription.data[0].id)
+ const currentSubscriptionId = existingSubscription.data[0]?.id
+ if (currentSubscriptionId)
+ await stripe.subscriptions.del(currentSubscriptionId)
+
await prisma.workspace.update({
where: { id: workspace.id },
data: {
diff --git a/apps/builder/playwright/services/database.ts b/apps/builder/playwright/services/database.ts
index 173b3331950..ba65f8412a7 100644
--- a/apps/builder/playwright/services/database.ts
+++ b/apps/builder/playwright/services/database.ts
@@ -18,7 +18,7 @@ import {
Workspace,
} from 'db'
import { readFileSync } from 'fs'
-import { createFakeResults } from 'utils'
+import { injectFakeResults } from 'utils'
import { encrypt } from 'utils/api'
import Stripe from 'stripe'
@@ -75,7 +75,10 @@ export const addSubscriptionToWorkspace = async (
customer: stripeId,
items,
default_payment_method: paymentId,
- currency: 'usd',
+ currency: 'eur',
+ })
+ await stripe.customers.update(stripeId, {
+ invoice_settings: { default_payment_method: paymentId },
})
await prisma.workspace.update({
where: { id: workspaceId },
@@ -264,7 +267,7 @@ export const updateUser = (data: Partial) =>
},
})
-export const createResults = createFakeResults(prisma)
+export const createResults = injectFakeResults(prisma)
export const createFolder = (workspaceId: string, name: string) =>
prisma.dashboardFolder.create({
diff --git a/apps/viewer/playwright/services/database.ts b/apps/viewer/playwright/services/database.ts
index 4ab7f6f9d52..d3439f8e4af 100644
--- a/apps/viewer/playwright/services/database.ts
+++ b/apps/viewer/playwright/services/database.ts
@@ -10,7 +10,7 @@ import {
} from 'models'
import { GraphNavigation, Plan, PrismaClient, WorkspaceRole } from 'db'
import { readFileSync } from 'fs'
-import { createFakeResults } from 'utils'
+import { injectFakeResults } from 'utils'
import { encrypt } from 'utils/api'
const prisma = new PrismaClient()
@@ -221,7 +221,7 @@ export const importTypebotInDatabase = async (
})
}
-export const createResults = createFakeResults(prisma)
+export const createResults = injectFakeResults(prisma)
export const createSmtpCredentials = (
id: string,
diff --git a/packages/scripts/index.ts b/packages/scripts/index.ts
index ec5a2f047be..2b497177acf 100644
--- a/packages/scripts/index.ts
+++ b/packages/scripts/index.ts
@@ -1,18 +1,23 @@
import { PrismaClient } from 'db'
import path from 'path'
-import fs from 'fs'
+import { injectFakeResults } from 'utils'
require('dotenv').config({
path: path.join(
__dirname,
- process.env.NODE_ENV === 'production'
- ? '.env.production'
- : process.env.NODE_ENV === 'staging'
- ? '.env.staging'
- : '.env.local'
+ process.env.NODE_ENV === 'staging' ? '.env.staging' : '.env.local'
),
})
-const main = async () => {}
+const prisma = new PrismaClient()
+
+const main = async () => {
+ await injectFakeResults(prisma)({
+ count: 150,
+ typebotId: 'cl89sq4vb030109laivd9ck97',
+ isChronological: false,
+ idPrefix: 'batch2',
+ })
+}
main().then()
diff --git a/packages/scripts/package.json b/packages/scripts/package.json
index f2a5b7ac425..63776df8e85 100644
--- a/packages/scripts/package.json
+++ b/packages/scripts/package.json
@@ -7,9 +7,7 @@
"scripts": {
"start:local": "ts-node index.ts",
"start:staging": "NODE_ENV=staging ts-node index.ts",
- "start:prod": "NODE_ENV=production ts-node index.ts",
- "start:workspaces:migration": "ts-node workspaceMigration.ts",
- "start:workspaces:migration:recover": "ts-node workspaceMigrationRecover.ts"
+ "start:prod": "NODE_ENV=production ts-node index.ts"
},
"devDependencies": {
"@types/node": "18.7.16",
@@ -17,6 +15,6 @@
"models": "workspace:*",
"ts-node": "^10.9.1",
"typescript": "^4.8.3",
- "utils": "*"
+ "utils": "workspace:*"
}
}
diff --git a/packages/scripts/prepareEmojis.ts b/packages/scripts/prepareEmojis.ts
deleted file mode 100644
index ac1b4dc929c..00000000000
--- a/packages/scripts/prepareEmojis.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import fs from 'fs'
-
-export const prepareEmojis = () => {
- const emojiData = JSON.parse(fs.readFileSync('./emojiData.json', 'utf8'))
- const strippedEmojiData = {
- 'Smileys & Emotion': emojiData['Smileys & Emotion'].map(
- (emoji: { emoji: any }) => emoji.emoji
- ),
- 'People & Body': emojiData['People & Body'].map(
- (emoji: { emoji: any }) => emoji.emoji
- ),
- 'Animals & Nature': emojiData['Animals & Nature'].map(
- (emoji: { emoji: any }) => emoji.emoji
- ),
- 'Food & Drink': emojiData['Food & Drink'].map(
- (emoji: { emoji: any }) => emoji.emoji
- ),
- 'Travel & Places': emojiData['Travel & Places'].map(
- (emoji: { emoji: any }) => emoji.emoji
- ),
- Activities: emojiData['Activities'].map(
- (emoji: { emoji: any }) => emoji.emoji
- ),
- Objects: emojiData['Objects'].map((emoji: { emoji: any }) => emoji.emoji),
- Symbols: emojiData['Symbols'].map((emoji: { emoji: any }) => emoji.emoji),
- Flags: emojiData['Flags'].map((emoji: { emoji: any }) => emoji.emoji),
- }
- fs.writeFileSync(
- 'strippedEmojis.json',
- JSON.stringify(strippedEmojiData),
- 'utf8'
- )
-}
diff --git a/packages/utils/playwright.ts b/packages/utils/playwright.ts
index 7b3cd2a20ff..3772d00e85f 100644
--- a/packages/utils/playwright.ts
+++ b/packages/utils/playwright.ts
@@ -8,7 +8,7 @@ type CreateFakeResultsProps = {
fakeStorage?: number
}
-export const createFakeResults =
+export const injectFakeResults =
(prisma: PrismaClient) =>
async ({
count,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cc9557ff2d8..e9013eccddc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -520,7 +520,7 @@ importers:
models: workspace:*
ts-node: ^10.9.1
typescript: ^4.8.3
- utils: '*'
+ utils: workspace:*
devDependencies:
'@types/node': 18.7.16
db: link:../db