Add WhatsApp integration beta test (#722)

Related to #401
This commit is contained in:
Baptiste Arnaud
2023-08-29 10:01:28 +02:00
parent 036b407a11
commit b852b4af0b
136 changed files with 6694 additions and 5383 deletions

View File

@@ -32,11 +32,17 @@ import { trpc } from '@/lib/trpc'
import { useToast } from '@/hooks/useToast'
import { parseDefaultPublicId } from '../helpers/parseDefaultPublicId'
export const PublishButton = (props: ButtonProps) => {
type Props = ButtonProps & {
isMoreMenuDisabled?: boolean
}
export const PublishButton = ({
isMoreMenuDisabled = false,
...props
}: Props) => {
const t = useI18n()
const warningTextColor = useColorModeValue('red.300', 'red.600')
const { workspace } = useWorkspace()
const { push, query } = useRouter()
const { push, query, pathname } = useRouter()
const { isOpen, onOpen, onClose } = useDisclosure()
const {
isPublished,
@@ -66,7 +72,8 @@ export const PublishButton = (props: ButtonProps) => {
refetchPublishedTypebot({
typebotId: typebot?.id as string,
})
if (!publishedTypebot) push(`/typebots/${query.typebotId}/share`)
if (!publishedTypebot && !pathname.endsWith('share'))
push(`/typebots/${query.typebotId}/share`)
},
})
@@ -153,7 +160,9 @@ export const PublishButton = (props: ButtonProps) => {
isLoading={isPublishing || isUnpublishing}
isDisabled={isPublished || isSavingLoading}
onClick={handlePublishClick}
borderRightRadius={publishedTypebot ? 0 : undefined}
borderRightRadius={
publishedTypebot && !isMoreMenuDisabled ? 0 : undefined
}
{...props}
>
{isPublished
@@ -164,7 +173,7 @@ export const PublishButton = (props: ButtonProps) => {
</Button>
</Tooltip>
{publishedTypebot && (
{!isMoreMenuDisabled && publishedTypebot && (
<Menu>
<MenuButton
as={IconButton}

View File

@@ -68,7 +68,7 @@ export const SharePage = () => {
const checkIfPublicIdIsValid = async (publicId: string) => {
const isLongerThanAllowed = publicId.length >= 4
if (!isLongerThanAllowed && isCloudProdInstance) {
if (!isLongerThanAllowed && isCloudProdInstance()) {
showToast({
description: 'Should be longer than 4 characters',
})

View File

@@ -39,6 +39,10 @@ import { FlutterFlowLogo } from './logos/FlutterFlowLogo'
import { FlutterFlowModal } from './modals/FlutterFlowModal'
import { NextjsLogo } from './logos/NextjsLogo'
import { NextjsModal } from './modals/Nextjs/NextjsModal'
import { WhatsAppLogo } from '@/components/logos/WhatsAppLogo'
import { WhatsAppModal } from './modals/WhatsAppModal/WhatsAppModal'
import { ParentModalProvider } from '@/features/graph/providers/ParentModalProvider'
import { getFeatureFlags } from '@/features/telemetry/posthog'
export type ModalProps = {
publicId: string
@@ -79,6 +83,19 @@ export const EmbedButton = ({
}
export const integrationsList = [
(props: Pick<ModalProps, 'publicId' | 'isPublished'>) => {
if (getFeatureFlags().includes('whatsApp'))
return (
<ParentModalProvider>
<EmbedButton
logo={<WhatsAppLogo height={100} width="70px" />}
label="WhatsApp"
Modal={WhatsAppModal}
{...props}
/>
</ParentModalProvider>
)
},
(props: Pick<ModalProps, 'publicId' | 'isPublished'>) => (
<EmbedButton
logo={<WordpressLogo height={100} width="70px" />}

View File

@@ -0,0 +1,70 @@
import { HStack, Text } from '@chakra-ui/react'
import { DropdownList } from '@/components/DropdownList'
import { ComparisonOperators } from '@typebot.io/schemas'
import { TableListItemProps } from '@/components/TableList'
import { TextInput } from '@/components/inputs'
import { WhatsAppComparison } from '@typebot.io/schemas/features/whatsapp'
export const WhatsAppComparisonItem = ({
item,
onItemChange,
}: TableListItemProps<WhatsAppComparison>) => {
const handleSelectComparisonOperator = (
comparisonOperator: ComparisonOperators
) => {
if (comparisonOperator === item.comparisonOperator) return
onItemChange({ ...item, comparisonOperator })
}
const handleChangeValue = (value: string) => {
if (value === item.value) return
onItemChange({ ...item, value })
}
return (
<HStack p="4" rounded="md" flex="1" borderWidth="1px">
<Text flexShrink={0}>User message</Text>
<DropdownList
currentItem={item.comparisonOperator}
onItemSelect={handleSelectComparisonOperator}
items={Object.values(ComparisonOperators)}
placeholder="Select an operator"
size="sm"
flexShrink={0}
/>
{item.comparisonOperator !== ComparisonOperators.IS_SET &&
item.comparisonOperator !== ComparisonOperators.IS_EMPTY && (
<TextInput
defaultValue={item.value ?? ''}
onChange={handleChangeValue}
placeholder={parseValuePlaceholder(item.comparisonOperator)}
withVariableButton={false}
size="sm"
/>
)}
</HStack>
)
}
const parseValuePlaceholder = (
operator: ComparisonOperators | undefined
): string => {
switch (operator) {
case ComparisonOperators.NOT_EQUAL:
case ComparisonOperators.EQUAL:
case ComparisonOperators.CONTAINS:
case ComparisonOperators.STARTS_WITH:
case ComparisonOperators.ENDS_WITH:
case ComparisonOperators.NOT_CONTAINS:
case undefined:
return 'Type a value...'
case ComparisonOperators.LESS:
case ComparisonOperators.GREATER:
return 'Type a number...'
case ComparisonOperators.IS_SET:
case ComparisonOperators.IS_EMPTY:
return ''
case ComparisonOperators.MATCHES_REGEX:
case ComparisonOperators.NOT_MATCH_REGEX:
return '^[0-9]+$'
}
}

View File

@@ -0,0 +1,502 @@
import { CopyButton } from '@/components/CopyButton'
import { TextLink } from '@/components/TextLink'
import { ChevronLeftIcon, ExternalLinkIcon } from '@/components/icons'
import { TextInput } from '@/components/inputs/TextInput'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { useToast } from '@/hooks/useToast'
import { trpc, trpcVanilla } from '@/lib/trpc'
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
Stack,
ModalFooter,
Stepper,
useSteps,
Step,
StepIndicator,
Box,
StepIcon,
StepNumber,
StepSeparator,
StepStatus,
StepTitle,
UnorderedList,
ListItem,
Text,
Image,
Button,
HStack,
IconButton,
Heading,
OrderedList,
Link,
Code,
Input,
InputGroup,
InputRightElement,
} from '@chakra-ui/react'
import { env } from '@typebot.io/env'
import { getViewerUrl, isEmpty, isNotEmpty } from '@typebot.io/lib/utils'
import React, { useState } from 'react'
const steps = [
{ title: 'Requirements' },
{ title: 'User Token' },
{ title: 'Phone Number' },
{ title: 'Webhook' },
]
type Props = {
isOpen: boolean
onClose: () => void
onNewCredentials: (id: string) => void
}
export const WhatsAppCredentialsModal = ({
isOpen,
onClose,
onNewCredentials,
}: Props) => {
const { workspace } = useWorkspace()
const { showToast } = useToast()
const { activeStep, goToNext, goToPrevious, setActiveStep } = useSteps({
index: 0,
count: steps.length,
})
const [systemUserAccessToken, setSystemUserAccessToken] = useState('')
const [phoneNumberId, setPhoneNumberId] = useState('')
const [phoneNumberName, setPhoneNumberName] = useState('')
const [verificationToken, setVerificationToken] = useState('')
const [isVerifying, setIsVerifying] = useState(false)
const [isCreating, setIsCreating] = useState(false)
const {
credentials: {
listCredentials: { refetch: refetchCredentials },
},
} = trpc.useContext()
const { mutate } = trpc.credentials.createCredentials.useMutation({
onMutate: () => setIsCreating(true),
onSettled: () => setIsCreating(false),
onError: (err) => {
showToast({
description: err.message,
status: 'error',
})
},
onSuccess: (data) => {
refetchCredentials()
onNewCredentials(data.credentialsId)
onClose()
resetForm()
},
})
const { data: tokenInfoData } = trpc.whatsApp.getSystemTokenInfo.useQuery(
{
token: systemUserAccessToken,
},
{ enabled: isNotEmpty(systemUserAccessToken) }
)
const resetForm = () => {
setActiveStep(0)
setSystemUserAccessToken('')
setPhoneNumberId('')
}
const createMetaCredentials = async () => {
if (!workspace) return
mutate({
credentials: {
type: 'whatsApp',
workspaceId: workspace.id,
name: phoneNumberName,
data: {
systemUserAccessToken,
phoneNumberId,
},
},
})
}
const isTokenValid = async () => {
setIsVerifying(true)
try {
const { expiresAt, scopes } =
await trpcVanilla.whatsApp.getSystemTokenInfo.query({
token: systemUserAccessToken,
})
if (expiresAt !== 0) {
showToast({
description:
'Token expiration was not set to *never*. Create the token again with the correct expiration.',
})
return false
}
if (
['whatsapp_business_management', 'whatsapp_business_messaging'].find(
(scope) => !scopes.includes(scope)
)
) {
showToast({
description: 'Token does not have all the necessary scopes',
})
return false
}
} catch (err) {
setIsVerifying(false)
showToast({
description: 'Could not get system info',
})
return false
}
setIsVerifying(false)
return true
}
const isPhoneNumberAvailable = async () => {
setIsVerifying(true)
try {
const { name } = await trpcVanilla.whatsApp.getPhoneNumber.query({
systemToken: systemUserAccessToken,
phoneNumberId,
})
setPhoneNumberName(name)
try {
const { message } =
await trpcVanilla.whatsApp.verifyIfPhoneNumberAvailable.query({
phoneNumberDisplayName: name,
})
if (message === 'taken') {
setIsVerifying(false)
showToast({
description: 'Phone number is already registered on Typebot',
})
return false
}
const { verificationToken } =
await trpcVanilla.whatsApp.generateVerificationToken.mutate()
setVerificationToken(verificationToken)
} catch (err) {
console.error(err)
setIsVerifying(false)
showToast({
description: 'Could not verify if phone number is available',
})
return false
}
} catch (err) {
console.error(err)
setIsVerifying(false)
showToast({
description: 'Could not get phone number info',
})
return false
}
setIsVerifying(false)
return true
}
const goToNextStep = async () => {
if (activeStep === steps.length - 1) return createMetaCredentials()
if (activeStep === 1 && !(await isTokenValid())) return
if (activeStep === 2 && !(await isPhoneNumberAvailable())) return
goToNext()
}
return (
<Modal isOpen={isOpen} onClose={onClose} size="3xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>
<HStack h="40px">
{activeStep > 0 && (
<IconButton
icon={<ChevronLeftIcon />}
aria-label={'Go back'}
variant="ghost"
onClick={goToPrevious}
/>
)}
<Heading size="md">Add a WhatsApp phone number</Heading>
</HStack>
</ModalHeader>
<ModalCloseButton />
<ModalBody as={Stack} spacing="10">
<Stepper index={activeStep} size="sm" pt="4">
{steps.map((step, index) => (
<Step key={index}>
<StepIndicator>
<StepStatus
complete={<StepIcon />}
incomplete={<StepNumber />}
active={<StepNumber />}
/>
</StepIndicator>
<Box flexShrink="0">
<StepTitle>{step.title}</StepTitle>
</Box>
<StepSeparator />
</Step>
))}
</Stepper>
{activeStep === 0 && <Requirements />}
{activeStep === 1 && (
<SystemUserToken
initialToken={systemUserAccessToken}
setToken={setSystemUserAccessToken}
/>
)}
{activeStep === 2 && (
<PhoneNumber
appId={tokenInfoData?.appId}
initialPhoneNumberId={phoneNumberId}
setPhoneNumberId={setPhoneNumberId}
/>
)}
{activeStep === 3 && (
<Webhook
appId={tokenInfoData?.appId}
verificationToken={verificationToken}
phoneNumberId={phoneNumberId}
/>
)}
</ModalBody>
<ModalFooter>
<Button
onClick={goToNextStep}
colorScheme="blue"
isDisabled={
(activeStep === 1 && isEmpty(systemUserAccessToken)) ||
(activeStep === 2 && isEmpty(phoneNumberId))
}
isLoading={isVerifying || isCreating}
>
{activeStep === steps.length - 1 ? 'Submit' : 'Continue'}
</Button>
</ModalFooter>
</ModalContent>
</Modal>
)
}
const Requirements = () => (
<Stack spacing={4}>
<Text>
Make sure you have{' '}
<TextLink
href="https://developers.facebook.com/docs/whatsapp/cloud-api/get-started#set-up-developer-assets"
isExternal
>
created a WhatsApp Business Account
</TextLink>
. You should be able to get to this page:
</Text>
<Image
src="/images/whatsapp-quickstart-page.png"
alt="WhatsApp quickstart page"
rounded="md"
/>
<Text>
You can find your Meta apps here:{' '}
<TextLink href="https://developers.facebook.com/apps" isExternal>
https://developers.facebook.com/apps
</TextLink>
.
</Text>
</Stack>
)
const SystemUserToken = ({
initialToken,
setToken,
}: {
initialToken: string
setToken: (id: string) => void
}) => (
<OrderedList spacing={4}>
<ListItem>
Go to your{' '}
<Button
as={Link}
href="https://business.facebook.com/settings/system-users"
isExternal
rightIcon={<ExternalLinkIcon />}
size="sm"
>
System users page
</Button>
</ListItem>
<ListItem>
Create a new user by clicking on <Code>Add</Code>
</ListItem>
<ListItem>
Fill it with any name and give it the <Code>Admin</Code> role
</ListItem>
<ListItem>
<Stack>
<Text>
Click on <Code>Add assets</Code>. Under <Code>Apps</Code>, look for
your previously created app, select it and check{' '}
<Code>Manage app</Code>
</Text>
<Image
src="/images/meta-system-user-assets.png"
alt="Meta system user assets"
rounded="md"
/>
</Stack>
</ListItem>
<ListItem>
<Stack spacing={4}>
<Text>
Now, click on <Code>Generate new token</Code>. Select your app.
</Text>
<UnorderedList spacing={4}>
<ListItem>
Token expiration: <Code>Never</Code>
</ListItem>
<ListItem>
Available Permissions: <Code>whatsapp_business_messaging</Code>,{' '}
<Code>whatsapp_business_management</Code>{' '}
</ListItem>
</UnorderedList>
</Stack>
</ListItem>
<ListItem>Copy and paste the generated token:</ListItem>
<TextInput
isRequired
label="System User Token"
defaultValue={initialToken}
onChange={(val) => setToken(val.trim())}
withVariableButton={false}
debounceTimeout={0}
/>
</OrderedList>
)
const PhoneNumber = ({
appId,
initialPhoneNumberId,
setPhoneNumberId,
}: {
appId?: string
initialPhoneNumberId: string
setPhoneNumberId: (id: string) => void
}) => (
<OrderedList spacing={4}>
<ListItem>
<HStack>
<Text>
Go to your{' '}
<TextLink
href={`https://developers.facebook.com/apps/${appId}/whatsapp-business/wa-dev-console`}
isExternal
>
WhatsApp Dev Console
</TextLink>
</Text>
</HStack>
</ListItem>
<ListItem>
Add your phone number by clicking on the <Code>Add phone number</Code>{' '}
button.
</ListItem>
<ListItem>
<Stack>
<Text>
Select a phone number and paste the associated{' '}
<Code>Phone number ID</Code> and{' '}
<Code>WhatsApp Business Account ID</Code>:
</Text>
<HStack>
<TextInput
label="Phone number ID"
defaultValue={initialPhoneNumberId}
withVariableButton={false}
debounceTimeout={0}
isRequired
onChange={setPhoneNumberId}
/>
</HStack>
<Image
src="/images/whatsapp-phone-selection.png"
alt="WA phone selection"
/>
</Stack>
</ListItem>
</OrderedList>
)
const Webhook = ({
appId,
verificationToken,
phoneNumberId,
}: {
appId?: string
verificationToken: string
phoneNumberId: string
}) => {
const { workspace } = useWorkspace()
const webhookUrl = `${
env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ?? getViewerUrl()
}/api/v1/workspaces/${
workspace?.id
}/whatsapp/phoneNumbers/${phoneNumberId}/webhook`
return (
<Stack spacing={6}>
<Text>
In your{' '}
<TextLink
href={`https://developers.facebook.com/apps/${appId}/whatsapp-business/wa-settings`}
isExternal
>
WhatsApp Settings page
</TextLink>
, click on the Edit button and insert the following values:
</Text>
<UnorderedList spacing={6}>
<ListItem>
<HStack>
<Text flexShrink={0}>Callback URL:</Text>
<InputGroup size="sm">
<Input type={'text'} defaultValue={webhookUrl} />
<InputRightElement width="60px">
<CopyButton size="sm" textToCopy={webhookUrl} />
</InputRightElement>
</InputGroup>
</HStack>
</ListItem>
<ListItem>
<HStack>
<Text flexShrink={0}>Verify Token:</Text>
<InputGroup size="sm">
<Input type={'text'} defaultValue={verificationToken} />
<InputRightElement width="60px">
<CopyButton size="sm" textToCopy={verificationToken} />
</InputRightElement>
</InputGroup>
</HStack>
</ListItem>
<ListItem>
<HStack>
<Text flexShrink={0}>
Webhook fields: check <Code>messages</Code>
</Text>
</HStack>
</ListItem>
</UnorderedList>
</Stack>
)
}

View File

@@ -0,0 +1,233 @@
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
Heading,
ModalCloseButton,
ModalBody,
ModalFooter,
Stack,
Text,
OrderedList,
ListItem,
HStack,
useDisclosure,
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Flex,
} from '@chakra-ui/react'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { CredentialsDropdown } from '@/features/credentials/components/CredentialsDropdown'
import { ModalProps } from '../../EmbedButton'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { WhatsAppCredentialsModal } from './WhatsAppCredentialsModal'
import { TextLink } from '@/components/TextLink'
import { PublishButton } from '../../../PublishButton'
import { useParentModal } from '@/features/graph/providers/ParentModalProvider'
import { trpc } from '@/lib/trpc'
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
import { isDefined } from '@typebot.io/lib/utils'
import { TableList } from '@/components/TableList'
import { Comparison, LogicalOperator } from '@typebot.io/schemas'
import { DropdownList } from '@/components/DropdownList'
import { WhatsAppComparisonItem } from './WhatsAppComparisonItem'
import { AlertInfo } from '@/components/AlertInfo'
export const WhatsAppModal = ({ isOpen, onClose }: ModalProps): JSX.Element => {
const { typebot, updateTypebot, isPublished } = useTypebot()
const { ref } = useParentModal()
const { workspace } = useWorkspace()
const {
isOpen: isCredentialsModalOpen,
onOpen,
onClose: onCredentialsModalClose,
} = useDisclosure()
const whatsAppSettings = typebot?.settings.whatsApp
const { data: phoneNumberData } = trpc.whatsApp.getPhoneNumber.useQuery(
{
credentialsId: whatsAppSettings?.credentialsId as string,
},
{
enabled: !!whatsAppSettings?.credentialsId,
}
)
const toggleEnableWhatsApp = (isChecked: boolean) => {
if (!phoneNumberData?.id) return
updateTypebot({
updates: { whatsAppPhoneNumberId: isChecked ? phoneNumberData.id : null },
save: true,
})
}
const updateCredentialsId = (credentialsId: string | undefined) => {
if (!typebot) return
updateTypebot({
updates: {
settings: {
...typebot.settings,
whatsApp: {
...typebot.settings.whatsApp,
credentialsId,
},
},
},
})
}
const updateStartConditionComparisons = (comparisons: Comparison[]) => {
if (!typebot) return
updateTypebot({
updates: {
settings: {
...typebot.settings,
whatsApp: {
...typebot.settings.whatsApp,
startCondition: {
logicalOperator:
typebot.settings.whatsApp?.startCondition?.logicalOperator ??
LogicalOperator.AND,
comparisons,
},
},
},
},
})
}
const updateStartConditionLogicalOperator = (
logicalOperator: LogicalOperator
) => {
if (!typebot) return
updateTypebot({
updates: {
settings: {
...typebot.settings,
whatsApp: {
...typebot.settings.whatsApp,
startCondition: {
comparisons:
typebot.settings.whatsApp?.startCondition?.comparisons ?? [],
logicalOperator,
},
},
},
},
})
}
return (
<Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay />
<ModalContent ref={ref}>
<ModalHeader>
<Heading size="md">WhatsApp</Heading>
</ModalHeader>
<ModalCloseButton />
<ModalBody as={Stack} spacing="6">
{!isPublished && phoneNumberData?.id && (
<AlertInfo>You have modifications that can be published.</AlertInfo>
)}
<OrderedList spacing={4} pl="4">
<ListItem>
<HStack>
<Text>Select a phone number:</Text>
{workspace && (
<>
<WhatsAppCredentialsModal
isOpen={isCredentialsModalOpen}
onClose={onCredentialsModalClose}
onNewCredentials={updateCredentialsId}
/>
<CredentialsDropdown
type="whatsApp"
workspaceId={workspace.id}
currentCredentialsId={whatsAppSettings?.credentialsId}
onCredentialsSelect={updateCredentialsId}
onCreateNewClick={onOpen}
credentialsName="WA phone number"
size="sm"
/>
</>
)}
</HStack>
</ListItem>
{typebot?.settings.whatsApp?.credentialsId && (
<>
<ListItem>
<Accordion allowToggle>
<AccordionItem>
<AccordionButton justifyContent="space-between">
Start flow only if
<AccordionIcon />
</AccordionButton>
<AccordionPanel as={Stack} spacing="4" pt="4">
<TableList<Comparison>
initialItems={
whatsAppSettings?.startCondition?.comparisons ?? []
}
onItemsChange={updateStartConditionComparisons}
Item={WhatsAppComparisonItem}
ComponentBetweenItems={() => (
<Flex justify="center">
<DropdownList
currentItem={
whatsAppSettings?.startCondition
?.logicalOperator
}
onItemSelect={
updateStartConditionLogicalOperator
}
items={Object.values(LogicalOperator)}
size="sm"
/>
</Flex>
)}
addLabel="Add a comparison"
/>
</AccordionPanel>
</AccordionItem>
</Accordion>
</ListItem>
<ListItem>
<HStack>
<Text>Publish your bot:</Text>
<PublishButton size="sm" isMoreMenuDisabled />
</HStack>
</ListItem>
<ListItem>
<SwitchWithLabel
label="Enable WhatsApp integration"
initialValue={
isDefined(typebot?.whatsAppPhoneNumberId) ? true : false
}
onCheckChange={toggleEnableWhatsApp}
justifyContent="flex-start"
/>
</ListItem>
{phoneNumberData?.id && (
<ListItem>
<TextLink
href={`https://wa.me/${phoneNumberData.name}?text=Start`}
isExternal
>
Try it out
</TextLink>
</ListItem>
)}
</>
)}
</OrderedList>
</ModalBody>
<ModalFooter />
</ModalContent>
</Modal>
)
}

View File

@@ -76,7 +76,7 @@ const parseWordpressShortcode = ({
publicId: string
}) => {
return `[typebot typebot="${publicId}"${
isCloudProdInstance ? '' : ` host="${getViewerUrl()}"`
isCloudProdInstance() ? '' : ` host="${getViewerUrl()}"`
}${width ? ` width="${width}"` : ''}${height ? ` height="${height}"` : ''}]
`
}

View File

@@ -41,7 +41,7 @@ export const parseReactBotProps = ({ typebot, apiHost }: BotProps) => {
return `${typebotLine} ${apiHostLine}`
}
export const typebotImportCode = isCloudProdInstance
export const typebotImportCode = isCloudProdInstance()
? `import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@0.1/dist/web.js'`
: `import Typebot from 'https://cdn.jsdelivr.net/npm/@typebot.io/js@${packageJson.version}/dist/web.js'`
@@ -64,6 +64,6 @@ export const parseApiHost = (
export const parseApiHostValue = (
customDomain: Typebot['customDomain'] | undefined
) => {
if (isCloudProdInstance) return
if (isCloudProdInstance()) return
return parseApiHost(customDomain)
}