🐛 (usage) Archive typebot to be able to compute usage
This commit is contained in:
committed by
Baptiste Arnaud
parent
75ca255af2
commit
15dbc9577d
@@ -26,6 +26,7 @@ import { useWorkspace } from 'contexts/WorkspaceContext'
|
|||||||
import { EmojiOrImageIcon } from 'components/shared/EmojiOrImageIcon'
|
import { EmojiOrImageIcon } from 'components/shared/EmojiOrImageIcon'
|
||||||
import { WorkspaceSettingsModal } from './WorkspaceSettingsModal'
|
import { WorkspaceSettingsModal } from './WorkspaceSettingsModal'
|
||||||
import { isNotDefined } from 'utils'
|
import { isNotDefined } from 'utils'
|
||||||
|
import { PlanTag } from 'components/shared/PlanTag'
|
||||||
|
|
||||||
export const DashboardHeader = () => {
|
export const DashboardHeader = () => {
|
||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
@@ -90,9 +91,13 @@ export const DashboardHeader = () => {
|
|||||||
/>
|
/>
|
||||||
</SkeletonCircle>
|
</SkeletonCircle>
|
||||||
{workspace && (
|
{workspace && (
|
||||||
|
<>
|
||||||
<Text noOfLines={1} maxW="200px">
|
<Text noOfLines={1} maxW="200px">
|
||||||
{workspace.name}
|
{workspace.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
<PlanTag plan={workspace.plan}/>
|
||||||
|
</>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
<ChevronLeftIcon transform="rotate(-90deg)" />
|
<ChevronLeftIcon transform="rotate(-90deg)" />
|
||||||
</HStack>
|
</HStack>
|
||||||
@@ -112,6 +117,7 @@ export const DashboardHeader = () => {
|
|||||||
defaultIcon={HardDriveIcon}
|
defaultIcon={HardDriveIcon}
|
||||||
/>
|
/>
|
||||||
<Text>{workspace.name}</Text>
|
<Text>{workspace.name}</Text>
|
||||||
|
<PlanTag plan={workspace.plan}/>
|
||||||
</HStack>
|
</HStack>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Button, HStack, useDisclosure, Text } from '@chakra-ui/react'
|
import { Button, HStack, useDisclosure, Text } from '@chakra-ui/react'
|
||||||
import { FolderPlusIcon } from 'assets/icons'
|
import { FolderPlusIcon } from 'assets/icons'
|
||||||
|
import { LockTag } from 'components/shared/LockTag'
|
||||||
import {
|
import {
|
||||||
LimitReached,
|
LimitReached,
|
||||||
ChangePlanModal,
|
ChangePlanModal,
|
||||||
} from 'components/shared/modals/ChangePlanModal'
|
} from 'components/shared/modals/ChangePlanModal'
|
||||||
import { PlanTag } from 'components/shared/PlanTag'
|
|
||||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||||
import { Plan } from 'db'
|
import { Plan } from 'db'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
@@ -28,7 +28,7 @@ export const CreateFolderButton = ({ isLoading, onClick }: Props) => {
|
|||||||
>
|
>
|
||||||
<HStack>
|
<HStack>
|
||||||
<Text>Create a folder</Text>
|
<Text>Create a folder</Text>
|
||||||
{isFreePlan(workspace) && <PlanTag plan={Plan.STARTER} />}
|
{isFreePlan(workspace) && <LockTag plan={Plan.STARTER} />}
|
||||||
</HStack>
|
</HStack>
|
||||||
<ChangePlanModal
|
<ChangePlanModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const CurrentSubscriptionContent = ({
|
|||||||
return (
|
return (
|
||||||
<Stack gap="2">
|
<Stack gap="2">
|
||||||
<Heading fontSize="3xl">Subscription</Heading>
|
<Heading fontSize="3xl">Subscription</Heading>
|
||||||
<HStack>
|
<HStack data-testid="current-subscription">
|
||||||
<Text>Current workspace subscription: </Text>
|
<Text>Current workspace subscription: </Text>
|
||||||
{isCancelling ? (
|
{isCancelling ? (
|
||||||
<Spinner color="gray.500" size="xs" />
|
<Spinner color="gray.500" size="xs" />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { HStack, Text, Tooltip } from '@chakra-ui/react'
|
import { HStack, Text, Tooltip } from '@chakra-ui/react'
|
||||||
import { PlanTag } from 'components/shared/PlanTag'
|
import { LockTag } from 'components/shared/LockTag'
|
||||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||||
import { Plan } from 'db'
|
import { Plan } from 'db'
|
||||||
import {
|
import {
|
||||||
@@ -54,7 +54,7 @@ export const BlockTypeLabel = ({ type }: Props): JSX.Element => {
|
|||||||
<Tooltip label="Upload Files">
|
<Tooltip label="Upload Files">
|
||||||
<HStack>
|
<HStack>
|
||||||
<Text>File</Text>
|
<Text>File</Text>
|
||||||
{isFreePlan(workspace) && <PlanTag plan={Plan.STARTER} />}
|
{isFreePlan(workspace) && <LockTag plan={Plan.STARTER} />}
|
||||||
</HStack>
|
</HStack>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Flex, FormLabel, Stack, Switch, useDisclosure } from '@chakra-ui/react'
|
import { Flex, FormLabel, Stack, Switch, useDisclosure } from '@chakra-ui/react'
|
||||||
|
import { LockTag } from 'components/shared/LockTag'
|
||||||
import {
|
import {
|
||||||
ChangePlanModal,
|
ChangePlanModal,
|
||||||
LimitReached,
|
LimitReached,
|
||||||
} from 'components/shared/modals/ChangePlanModal'
|
} from 'components/shared/modals/ChangePlanModal'
|
||||||
import { PlanTag } from 'components/shared/PlanTag'
|
|
||||||
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
|
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
|
||||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||||
import { Plan } from 'db'
|
import { Plan } from 'db'
|
||||||
@@ -66,7 +66,7 @@ export const GeneralSettingsForm = ({
|
|||||||
>
|
>
|
||||||
<FormLabel htmlFor="branding" mb="0">
|
<FormLabel htmlFor="branding" mb="0">
|
||||||
Typebot.io branding{' '}
|
Typebot.io branding{' '}
|
||||||
{isWorkspaceFreePlan && <PlanTag plan={Plan.STARTER} />}
|
{isWorkspaceFreePlan && <LockTag plan={Plan.STARTER} />}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Switch
|
<Switch
|
||||||
id="branding"
|
id="branding"
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import {
|
|||||||
import { TrashIcon } from 'assets/icons'
|
import { TrashIcon } from 'assets/icons'
|
||||||
import { UpgradeButton } from 'components/shared/buttons/UpgradeButton'
|
import { UpgradeButton } from 'components/shared/buttons/UpgradeButton'
|
||||||
import { useToast } from 'components/shared/hooks/useToast'
|
import { useToast } from 'components/shared/hooks/useToast'
|
||||||
|
import { LockTag } from 'components/shared/LockTag'
|
||||||
import { LimitReached } from 'components/shared/modals/ChangePlanModal'
|
import { LimitReached } from 'components/shared/modals/ChangePlanModal'
|
||||||
import { PlanTag } from 'components/shared/PlanTag'
|
|
||||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||||
import { useWorkspace } from 'contexts/WorkspaceContext'
|
import { useWorkspace } from 'contexts/WorkspaceContext'
|
||||||
import { Plan } from 'db'
|
import { Plan } from 'db'
|
||||||
@@ -82,19 +82,22 @@ export const ShareContent = () => {
|
|||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
)}
|
)}
|
||||||
{isWorkspaceProPlan(workspace) &&
|
{isNotDefined(typebot?.customDomain) ? (
|
||||||
isNotDefined(typebot?.customDomain) ? (
|
<>
|
||||||
<CustomDomainsDropdown
|
{isWorkspaceProPlan(workspace) ? (
|
||||||
onCustomDomainSelect={handleCustomDomainChange}
|
<CustomDomainsDropdown
|
||||||
/>
|
onCustomDomainSelect={handleCustomDomainChange}
|
||||||
) : (
|
/>
|
||||||
<UpgradeButton
|
) : (
|
||||||
colorScheme="gray"
|
<UpgradeButton
|
||||||
limitReachedType={LimitReached.CUSTOM_DOMAIN}
|
colorScheme="gray"
|
||||||
>
|
limitReachedType={LimitReached.CUSTOM_DOMAIN}
|
||||||
<Text mr="2">Add my domain</Text> <PlanTag plan={Plan.PRO} />
|
>
|
||||||
</UpgradeButton>
|
<Text mr="2">Add my domain</Text> <LockTag plan={Plan.PRO} />
|
||||||
)}
|
</UpgradeButton>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Stack spacing={4}>
|
<Stack spacing={4}>
|
||||||
|
|||||||
14
apps/builder/components/shared/LockTag.tsx
Normal file
14
apps/builder/components/shared/LockTag.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Tag, TagProps } from '@chakra-ui/react'
|
||||||
|
import { LockedIcon } from 'assets/icons'
|
||||||
|
import { Plan } from 'db'
|
||||||
|
import { planColorSchemes } from './PlanTag'
|
||||||
|
|
||||||
|
export const LockTag = ({ plan, ...props }: { plan?: Plan } & TagProps) => (
|
||||||
|
<Tag
|
||||||
|
colorScheme={plan ? planColorSchemes[plan] : 'gray'}
|
||||||
|
data-testid={`${plan?.toLowerCase()}-lock-tag`}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<LockedIcon />
|
||||||
|
</Tag>
|
||||||
|
)
|
||||||
@@ -1,18 +1,34 @@
|
|||||||
import { Tag, TagProps } from '@chakra-ui/react'
|
import { Tag, TagProps, ThemeTypings } from '@chakra-ui/react'
|
||||||
import { Plan } from 'db'
|
import { Plan } from 'db'
|
||||||
|
|
||||||
|
export const planColorSchemes: Record<Plan, ThemeTypings['colorSchemes']> = {
|
||||||
|
[Plan.LIFETIME]: 'purple',
|
||||||
|
[Plan.PRO]: 'blue',
|
||||||
|
[Plan.OFFERED]: 'orange',
|
||||||
|
[Plan.STARTER]: 'orange',
|
||||||
|
[Plan.FREE]: 'gray',
|
||||||
|
}
|
||||||
|
|
||||||
export const PlanTag = ({ plan, ...props }: { plan?: Plan } & TagProps) => {
|
export const PlanTag = ({ plan, ...props }: { plan?: Plan } & TagProps) => {
|
||||||
switch (plan) {
|
switch (plan) {
|
||||||
case Plan.LIFETIME: {
|
case Plan.LIFETIME: {
|
||||||
return (
|
return (
|
||||||
<Tag colorScheme="purple" data-testid="lifetime-plan-tag" {...props}>
|
<Tag
|
||||||
|
colorScheme={planColorSchemes[plan]}
|
||||||
|
data-testid="lifetime-plan-tag"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
Lifetime
|
Lifetime
|
||||||
</Tag>
|
</Tag>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case Plan.PRO: {
|
case Plan.PRO: {
|
||||||
return (
|
return (
|
||||||
<Tag colorScheme="blue" data-testid="pro-plan-tag" {...props}>
|
<Tag
|
||||||
|
colorScheme={planColorSchemes[plan]}
|
||||||
|
data-testid="pro-plan-tag"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
Pro
|
Pro
|
||||||
</Tag>
|
</Tag>
|
||||||
)
|
)
|
||||||
@@ -20,14 +36,22 @@ export const PlanTag = ({ plan, ...props }: { plan?: Plan } & TagProps) => {
|
|||||||
case Plan.OFFERED:
|
case Plan.OFFERED:
|
||||||
case Plan.STARTER: {
|
case Plan.STARTER: {
|
||||||
return (
|
return (
|
||||||
<Tag colorScheme="orange" data-testid="starter-plan-tag" {...props}>
|
<Tag
|
||||||
|
colorScheme={planColorSchemes[plan]}
|
||||||
|
data-testid="starter-plan-tag"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
Starter
|
Starter
|
||||||
</Tag>
|
</Tag>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return (
|
return (
|
||||||
<Tag colorScheme="gray" data-testid="free-plan-tag" {...props}>
|
<Tag
|
||||||
|
colorScheme={planColorSchemes[Plan.FREE]}
|
||||||
|
data-testid="free-plan-tag"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
Free
|
Free
|
||||||
</Tag>
|
</Tag>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -139,13 +139,14 @@ export const WorkspaceContext = ({ children }: WorkspaceContextProps) => {
|
|||||||
if (!currentWorkspace || !workspaces || workspaces.length < 2) return
|
if (!currentWorkspace || !workspaces || workspaces.length < 2) return
|
||||||
const { data } = await deleteWorkspace(currentWorkspace.id)
|
const { data } = await deleteWorkspace(currentWorkspace.id)
|
||||||
if (!data || !currentWorkspace) return
|
if (!data || !currentWorkspace) return
|
||||||
setCurrentWorkspace(workspaces[0])
|
const newWorkspaces = (workspaces ?? []).filter((w) =>
|
||||||
|
w.id === currentWorkspace.id
|
||||||
|
? { ...data.workspace, members: w.members }
|
||||||
|
: w
|
||||||
|
)
|
||||||
|
setCurrentWorkspace(newWorkspaces[0])
|
||||||
mutate({
|
mutate({
|
||||||
workspaces: (workspaces ?? []).filter((w) =>
|
workspaces: newWorkspaces,
|
||||||
w.id === currentWorkspace.id
|
|
||||||
? { ...data.workspace, members: w.members }
|
|
||||||
: w
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
{
|
{
|
||||||
workspace: { members: { some: { userId: user.id } } },
|
workspace: { members: { some: { userId: user.id } } },
|
||||||
id: { in: typebotIds },
|
id: { in: typebotIds },
|
||||||
|
isArchived: { not: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: { in: typebotIds },
|
id: { in: typebotIds },
|
||||||
@@ -39,6 +40,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
isArchived: { not: true },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -51,6 +53,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
where: {
|
where: {
|
||||||
OR: [
|
OR: [
|
||||||
{
|
{
|
||||||
|
isArchived: { not: true },
|
||||||
folderId,
|
folderId,
|
||||||
workspace: {
|
workspace: {
|
||||||
id: workspaceId,
|
id: workspaceId,
|
||||||
@@ -63,6 +66,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
isArchived: { not: true },
|
||||||
workspace: {
|
workspace: {
|
||||||
id: workspaceId,
|
id: workspaceId,
|
||||||
members: {
|
members: {
|
||||||
|
|||||||
@@ -13,13 +13,17 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
const typebotId = req.query.typebotId as string
|
const typebotId = req.query.typebotId as string
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const typebot = await prisma.typebot.findFirst({
|
const typebot = await prisma.typebot.findFirst({
|
||||||
where: canReadTypebot(typebotId, user),
|
where: {
|
||||||
|
...canReadTypebot(typebotId, user),
|
||||||
|
isArchived: { not: true },
|
||||||
|
},
|
||||||
include: {
|
include: {
|
||||||
publishedTypebot: true,
|
publishedTypebot: true,
|
||||||
collaborators: { select: { userId: true, type: true } },
|
collaborators: { select: { userId: true, type: true } },
|
||||||
webhooks: true,
|
webhooks: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
console.log(typebot)
|
||||||
if (!typebot) return res.send({ typebot: null })
|
if (!typebot) return res.send({ typebot: null })
|
||||||
const { publishedTypebot, collaborators, webhooks, ...restOfTypebot } =
|
const { publishedTypebot, collaborators, webhooks, ...restOfTypebot } =
|
||||||
typebot
|
typebot
|
||||||
@@ -35,8 +39,9 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.method === 'DELETE') {
|
if (req.method === 'DELETE') {
|
||||||
const typebots = await prisma.typebot.deleteMany({
|
const typebots = await prisma.typebot.updateMany({
|
||||||
where: canWriteTypebot(typebotId, user),
|
where: canWriteTypebot(typebotId, user),
|
||||||
|
data: { isArchived: true },
|
||||||
})
|
})
|
||||||
await archiveResults(res)({
|
await archiveResults(res)({
|
||||||
typebotId,
|
typebotId,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { withSentry } from '@sentry/nextjs'
|
|||||||
import { Prisma, Workspace, WorkspaceRole } from 'db'
|
import { Prisma, Workspace, WorkspaceRole } from 'db'
|
||||||
import prisma from 'libs/prisma'
|
import prisma from 'libs/prisma'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { archiveResults, getAuthenticatedUser } from 'services/api/utils'
|
import { getAuthenticatedUser } from 'services/api/utils'
|
||||||
import { methodNotAllowed, notAuthenticated } from 'utils/api'
|
import { methodNotAllowed, notAuthenticated } from 'utils/api'
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
@@ -28,23 +28,9 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
id,
|
id,
|
||||||
members: { some: { userId: user.id, role: WorkspaceRole.ADMIN } },
|
members: { some: { userId: user.id, role: WorkspaceRole.ADMIN } },
|
||||||
}
|
}
|
||||||
const deletedTypebots = await prisma.typebot.findMany({
|
|
||||||
where: {
|
|
||||||
workspace: workspaceFilter,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
await prisma.workspace.deleteMany({
|
await prisma.workspace.deleteMany({
|
||||||
where: workspaceFilter,
|
where: workspaceFilter,
|
||||||
})
|
})
|
||||||
await Promise.all(
|
|
||||||
deletedTypebots.map((typebot) =>
|
|
||||||
archiveResults(res)({
|
|
||||||
typebotId: typebot.id,
|
|
||||||
user,
|
|
||||||
resultsFilter: { typebotId: typebot.id },
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
message: 'success',
|
message: 'success',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -102,14 +102,16 @@ export const setupDatabase = async () => {
|
|||||||
return setupCredentials()
|
return setupCredentials()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setupWorkspaces = async () =>
|
export const setupWorkspaces = async () => {
|
||||||
prisma.workspace.createMany({
|
await prisma.workspace.create({
|
||||||
|
data: {
|
||||||
|
id: freeWorkspaceId,
|
||||||
|
name: 'Free workspace',
|
||||||
|
plan: Plan.FREE,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await prisma.workspace.createMany({
|
||||||
data: [
|
data: [
|
||||||
{
|
|
||||||
id: freeWorkspaceId,
|
|
||||||
name: 'Free workspace',
|
|
||||||
plan: Plan.FREE,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: starterWorkspaceId,
|
id: starterWorkspaceId,
|
||||||
name: 'Starter workspace',
|
name: 'Starter workspace',
|
||||||
@@ -128,6 +130,7 @@ export const setupWorkspaces = async () =>
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const createWorkspaces = async (workspaces: Partial<Workspace>[]) => {
|
export const createWorkspaces = async (workspaces: Partial<Workspace>[]) => {
|
||||||
const workspaceIds = workspaces.map((workspace) => workspace.id ?? cuid())
|
const workspaceIds = workspaces.map((workspace) => workspace.id ?? cuid())
|
||||||
@@ -231,11 +234,15 @@ export const getSignedInUser = (email: string) =>
|
|||||||
prisma.user.findFirst({ where: { email } })
|
prisma.user.findFirst({ where: { email } })
|
||||||
|
|
||||||
export const createTypebots = async (partialTypebots: Partial<Typebot>[]) => {
|
export const createTypebots = async (partialTypebots: Partial<Typebot>[]) => {
|
||||||
|
const typebotsWithId = partialTypebots.map((typebot) => ({
|
||||||
|
...typebot,
|
||||||
|
id: typebot.id ?? cuid(),
|
||||||
|
}))
|
||||||
await prisma.typebot.createMany({
|
await prisma.typebot.createMany({
|
||||||
data: partialTypebots.map(parseTestTypebot),
|
data: typebotsWithId.map(parseTestTypebot),
|
||||||
})
|
})
|
||||||
return prisma.publicTypebot.createMany({
|
return prisma.publicTypebot.createMany({
|
||||||
data: partialTypebots.map((t) =>
|
data: typebotsWithId.map((t) =>
|
||||||
parseTypebotToPublicTypebot(t.id + '-public', parseTestTypebot(t))
|
parseTypebotToPublicTypebot(t.id + '-public', parseTestTypebot(t))
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
@@ -304,7 +311,7 @@ const parseTypebotToPublicTypebot = (
|
|||||||
})
|
})
|
||||||
|
|
||||||
const parseTestTypebot = (partialTypebot: Partial<Typebot>): Typebot => ({
|
const parseTestTypebot = (partialTypebot: Partial<Typebot>): Typebot => ({
|
||||||
id: partialTypebot.id ?? 'typebot',
|
id: cuid(),
|
||||||
workspaceId: proWorkspaceId,
|
workspaceId: proWorkspaceId,
|
||||||
folderId: null,
|
folderId: null,
|
||||||
name: 'My typebot',
|
name: 'My typebot',
|
||||||
|
|||||||
@@ -165,9 +165,13 @@ test('plan changes should work', async ({ page }) => {
|
|||||||
await page.goto('/typebots')
|
await page.goto('/typebots')
|
||||||
await page.click('text=Settings & Members')
|
await page.click('text=Settings & Members')
|
||||||
await page.click('text=Billing & Usage')
|
await page.click('text=Billing & Usage')
|
||||||
await expect(page.locator('[data-testid="pro-plan-tag"]')).toBeVisible()
|
await expect(page.locator('[data-testid="current-subscription"]')).toHaveText(
|
||||||
|
'Current workspace subscription: ProCancel my subscription'
|
||||||
|
)
|
||||||
await page.click('button >> text="Cancel my subscription"')
|
await page.click('button >> text="Cancel my subscription"')
|
||||||
await expect(page.locator('[data-testid="free-plan-tag"]')).toBeVisible()
|
await expect(page.locator('[data-testid="current-subscription"]')).toHaveText(
|
||||||
|
'Current workspace subscription: Free'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should display invoices', async ({ page }) => {
|
test('should display invoices', async ({ page }) => {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ test.describe('Starter workspace', () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
await page.goto(`/typebots/${typebotId}/share`)
|
await page.goto(`/typebots/${typebotId}/share`)
|
||||||
await expect(page.locator('text=Pro')).toBeVisible()
|
await expect(page.locator('[data-testid="pro-lock-tag"]')).toBeVisible()
|
||||||
await page.click('text=Add my domain')
|
await page.click('text=Add my domain')
|
||||||
await expect(
|
await expect(
|
||||||
page.locator(
|
page.locator(
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ test.describe('Free user', () => {
|
|||||||
await page.goto('/typebots')
|
await page.goto('/typebots')
|
||||||
await page.click('text="Pro workspace"')
|
await page.click('text="Pro workspace"')
|
||||||
await page.click('text="Free workspace"')
|
await page.click('text="Free workspace"')
|
||||||
await expect(page.locator('[data-testid="starter-plan-tag"]')).toBeVisible()
|
await expect(page.locator('[data-testid="starter-lock-tag"]')).toBeVisible()
|
||||||
await page.click('text=Create a folder')
|
await page.click('text=Create a folder')
|
||||||
await expect(
|
await expect(
|
||||||
page.locator(
|
page.locator(
|
||||||
|
|||||||
@@ -135,7 +135,9 @@ test.describe.parallel('Settings page', () => {
|
|||||||
typebotViewer(page).locator('text="What\'s your name?"')
|
typebotViewer(page).locator('text="What\'s your name?"')
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await page.click('button:has-text("General")')
|
await page.click('button:has-text("General")')
|
||||||
await expect(page.locator('text=Starter')).toBeVisible()
|
await expect(
|
||||||
|
page.locator('[data-testid="starter-lock-tag"]')
|
||||||
|
).toBeVisible()
|
||||||
await page.click('text=Typebot.io branding')
|
await page.click('text=Typebot.io branding')
|
||||||
await expect(
|
await expect(
|
||||||
page.locator(
|
page.locator(
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ test('can manage members', async ({ page }) => {
|
|||||||
|
|
||||||
test("can't add new members when limit is reached", async ({ page }) => {
|
test("can't add new members when limit is reached", async ({ page }) => {
|
||||||
await page.goto('/typebots')
|
await page.goto('/typebots')
|
||||||
await page.click('text="Pro workspace"')
|
await page.click('text="My awesome workspace"')
|
||||||
await page.click('text="Free workspace"')
|
await page.click('text="Free workspace"')
|
||||||
await page.click('text=Settings & Members')
|
await page.click('text=Settings & Members')
|
||||||
await page.click('text="Members"')
|
await page.click('text="Members"')
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Result" DROP CONSTRAINT "Result_typebotId_fkey";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Result" ALTER COLUMN "isArchived" SET DEFAULT false;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Typebot" ADD COLUMN "isArchived" BOOLEAN NOT NULL DEFAULT false;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Result" ADD CONSTRAINT "Result_typebotId_fkey" FOREIGN KEY ("typebotId") REFERENCES "Typebot"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -66,23 +66,23 @@ model ApiToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Workspace {
|
model Workspace {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
icon String?
|
icon String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
plan Plan @default(FREE)
|
plan Plan @default(FREE)
|
||||||
stripeId String? @unique
|
stripeId String? @unique
|
||||||
credentials Credentials[]
|
credentials Credentials[]
|
||||||
customDomains CustomDomain[]
|
customDomains CustomDomain[]
|
||||||
folders DashboardFolder[]
|
folders DashboardFolder[]
|
||||||
members MemberInWorkspace[]
|
members MemberInWorkspace[]
|
||||||
typebots Typebot[]
|
typebots Typebot[]
|
||||||
invitations WorkspaceInvitation[]
|
invitations WorkspaceInvitation[]
|
||||||
additionalChatsIndex Int @default(0)
|
additionalChatsIndex Int @default(0)
|
||||||
additionalStorageIndex Int @default(0)
|
additionalStorageIndex Int @default(0)
|
||||||
chatsLimitFirstEmailSentAt DateTime?
|
chatsLimitFirstEmailSentAt DateTime?
|
||||||
storageLimitFirstEmailSentAt DateTime?
|
storageLimitFirstEmailSentAt DateTime?
|
||||||
chatsLimitSecondEmailSentAt DateTime?
|
chatsLimitSecondEmailSentAt DateTime?
|
||||||
storageLimitSecondEmailSentAt DateTime?
|
storageLimitSecondEmailSentAt DateTime?
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,6 +168,7 @@ model Typebot {
|
|||||||
publishedTypebot PublicTypebot?
|
publishedTypebot PublicTypebot?
|
||||||
results Result[]
|
results Result[]
|
||||||
webhooks Webhook[]
|
webhooks Webhook[]
|
||||||
|
isArchived Boolean @default(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
model Invitation {
|
model Invitation {
|
||||||
@@ -211,8 +212,8 @@ model Result {
|
|||||||
variables Json[]
|
variables Json[]
|
||||||
isCompleted Boolean
|
isCompleted Boolean
|
||||||
hasStarted Boolean?
|
hasStarted Boolean?
|
||||||
isArchived Boolean?
|
isArchived Boolean? @default(false)
|
||||||
typebot Typebot @relation(fields: [typebotId], references: [id], onDelete: NoAction)
|
typebot Typebot @relation(fields: [typebotId], references: [id], onDelete: Cascade)
|
||||||
answers Answer[]
|
answers Answer[]
|
||||||
logs Log[]
|
logs Log[]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user