⚡ Auto continue bot on whatsApp if starting block is input (#849)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ### Summary by CodeRabbit **New Features:** - Added WhatsApp integration feature to the Pro plan. **Refactor:** - Introduced the ability to exclude specific plans from being displayed in the Change Plan Modal. - Renamed the function `isProPlan` to `hasProPerks`, enhancing code readability and maintainability. - Updated the `EmbedButton` component to handle a new `lockTagPlan` property and use the `modal` function instead of the `Modal` component. **Chore:** - Removed the `whatsAppPhoneNumberId` field from the `Typebot` model across various files, simplifying the data structure of the model. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -8,19 +8,23 @@ import {
|
|||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
|
import {
|
||||||
|
ChangePlanModal,
|
||||||
|
ChangePlanModalProps,
|
||||||
|
} from '@/features/billing/components/ChangePlanModal'
|
||||||
import { useI18n } from '@/locales'
|
import { useI18n } from '@/locales'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
contentLabel: React.ReactNode
|
contentLabel: React.ReactNode
|
||||||
buttonLabel?: string
|
buttonLabel?: string
|
||||||
type?: string
|
} & AlertProps &
|
||||||
} & AlertProps
|
Pick<ChangePlanModalProps, 'type' | 'excludedPlans'>
|
||||||
|
|
||||||
export const UnlockPlanAlertInfo = ({
|
export const UnlockPlanAlertInfo = ({
|
||||||
contentLabel,
|
contentLabel,
|
||||||
buttonLabel,
|
buttonLabel,
|
||||||
type,
|
type,
|
||||||
|
excludedPlans,
|
||||||
...props
|
...props
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
@@ -45,7 +49,12 @@ export const UnlockPlanAlertInfo = ({
|
|||||||
>
|
>
|
||||||
{buttonLabel ?? t('billing.upgradeAlert.buttonDefaultLabel')}
|
{buttonLabel ?? t('billing.upgradeAlert.buttonDefaultLabel')}
|
||||||
</Button>
|
</Button>
|
||||||
<ChangePlanModal isOpen={isOpen} onClose={onClose} type={type} />
|
<ChangePlanModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
type={type}
|
||||||
|
excludedPlans={excludedPlans}
|
||||||
|
/>
|
||||||
</Alert>
|
</Alert>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export const AnalyticsGraphContainer = ({ stats }: { stats?: Stats }) => {
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
type={t('billing.limitMessage.analytics')}
|
type={t('billing.limitMessage.analytics')}
|
||||||
|
excludedPlans={['STARTER']}
|
||||||
/>
|
/>
|
||||||
<StatsCards stats={stats} pos="absolute" />
|
<StatsCards stats={stats} pos="absolute" />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ import { StripeClimateLogo } from './StripeClimateLogo'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workspace: Workspace
|
workspace: Workspace
|
||||||
|
excludedPlans?: ('STARTER' | 'PRO')[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChangePlanForm = ({ workspace }: Props) => {
|
export const ChangePlanForm = ({ workspace, excludedPlans }: Props) => {
|
||||||
const scopedT = useScopedI18n('billing')
|
const scopedT = useScopedI18n('billing')
|
||||||
|
|
||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
@@ -133,6 +134,7 @@ export const ChangePlanForm = ({ workspace }: Props) => {
|
|||||||
</HStack>
|
</HStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<HStack alignItems="stretch" spacing="4" w="full">
|
<HStack alignItems="stretch" spacing="4" w="full">
|
||||||
|
{excludedPlans?.includes('STARTER') ? null : (
|
||||||
<StarterPlanPricingCard
|
<StarterPlanPricingCard
|
||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
currentSubscription={{ isYearly: data.subscription?.isYearly }}
|
currentSubscription={{ isYearly: data.subscription?.isYearly }}
|
||||||
@@ -143,7 +145,9 @@ export const ChangePlanForm = ({ workspace }: Props) => {
|
|||||||
isLoading={isUpdatingSubscription}
|
isLoading={isUpdatingSubscription}
|
||||||
currency={data.subscription?.currency}
|
currency={data.subscription?.currency}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{excludedPlans?.includes('PRO') ? null : (
|
||||||
<ProPlanPricingCard
|
<ProPlanPricingCard
|
||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
currentSubscription={{ isYearly: data.subscription?.isYearly }}
|
currentSubscription={{ isYearly: data.subscription?.isYearly }}
|
||||||
@@ -154,6 +158,7 @@ export const ChangePlanForm = ({ workspace }: Props) => {
|
|||||||
isLoading={isUpdatingSubscription}
|
isLoading={isUpdatingSubscription}
|
||||||
currency={data.subscription?.currency}
|
currency={data.subscription?.currency}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import {
|
|||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import { ChangePlanForm } from './ChangePlanForm'
|
import { ChangePlanForm } from './ChangePlanForm'
|
||||||
|
|
||||||
type ChangePlanModalProps = {
|
export type ChangePlanModalProps = {
|
||||||
type?: string
|
type?: string
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
|
excludedPlans?: ('STARTER' | 'PRO')[]
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,11 +24,16 @@ export const ChangePlanModal = ({
|
|||||||
onClose,
|
onClose,
|
||||||
isOpen,
|
isOpen,
|
||||||
type,
|
type,
|
||||||
|
excludedPlans,
|
||||||
}: ChangePlanModalProps) => {
|
}: ChangePlanModalProps) => {
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const { workspace } = useWorkspace()
|
const { workspace } = useWorkspace()
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onClose={onClose} size="2xl">
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
size={excludedPlans ? 'lg' : '2xl'}
|
||||||
|
>
|
||||||
<ModalOverlay />
|
<ModalOverlay />
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalBody as={Stack} spacing="6" pt="10">
|
<ModalBody as={Stack} spacing="6" pt="10">
|
||||||
@@ -36,7 +42,12 @@ export const ChangePlanModal = ({
|
|||||||
{t('billing.upgradeLimitLabel', { type: type })}
|
{t('billing.upgradeLimitLabel', { type: type })}
|
||||||
</AlertInfo>
|
</AlertInfo>
|
||||||
)}
|
)}
|
||||||
{workspace && <ChangePlanForm workspace={workspace} />}
|
{workspace && (
|
||||||
|
<ChangePlanForm
|
||||||
|
workspace={workspace}
|
||||||
|
excludedPlans={excludedPlans}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ export const ProPlanPricingCard = ({
|
|||||||
</Text>
|
</Text>
|
||||||
<MoreInfoTooltip>{scopedT('chatsTooltip')}</MoreInfoTooltip>
|
<MoreInfoTooltip>{scopedT('chatsTooltip')}</MoreInfoTooltip>
|
||||||
</HStack>,
|
</HStack>,
|
||||||
|
scopedT('pro.whatsAppIntegration'),
|
||||||
scopedT('pro.customDomains'),
|
scopedT('pro.customDomains'),
|
||||||
scopedT('pro.analytics'),
|
scopedT('pro.analytics'),
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -5,9 +5,16 @@ import { isNotDefined } from '@typebot.io/lib'
|
|||||||
import { ChangePlanModal } from './ChangePlanModal'
|
import { ChangePlanModal } from './ChangePlanModal'
|
||||||
import { useI18n } from '@/locales'
|
import { useI18n } from '@/locales'
|
||||||
|
|
||||||
type Props = { limitReachedType?: string } & ButtonProps
|
type Props = {
|
||||||
|
limitReachedType?: string
|
||||||
|
excludedPlans?: ('STARTER' | 'PRO')[]
|
||||||
|
} & ButtonProps
|
||||||
|
|
||||||
export const UpgradeButton = ({ limitReachedType, ...props }: Props) => {
|
export const UpgradeButton = ({
|
||||||
|
limitReachedType,
|
||||||
|
excludedPlans,
|
||||||
|
...props
|
||||||
|
}: Props) => {
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||||
const { workspace } = useWorkspace()
|
const { workspace } = useWorkspace()
|
||||||
@@ -23,6 +30,7 @@ export const UpgradeButton = ({ limitReachedType, ...props }: Props) => {
|
|||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
type={limitReachedType}
|
type={limitReachedType}
|
||||||
|
excludedPlans={excludedPlans}
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { isDefined } from '@typebot.io/lib'
|
import { isDefined } from '@typebot.io/lib'
|
||||||
import { Workspace, Plan } from '@typebot.io/prisma'
|
import { Workspace, Plan } from '@typebot.io/prisma'
|
||||||
|
|
||||||
export const isProPlan = (workspace?: Pick<Workspace, 'plan'>) =>
|
export const hasProPerks = (workspace?: Pick<Workspace, 'plan'>) =>
|
||||||
isDefined(workspace) &&
|
isDefined(workspace) &&
|
||||||
(workspace.plan === Plan.PRO ||
|
(workspace.plan === Plan.PRO ||
|
||||||
workspace.plan === Plan.LIFETIME ||
|
workspace.plan === Plan.LIFETIME ||
|
||||||
@@ -11,7 +11,7 @@ import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
|||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { useEndpoints } from '../../providers/EndpointsProvider'
|
import { useEndpoints } from '../../providers/EndpointsProvider'
|
||||||
import { useGroupsCoordinates } from '../../providers/GroupsCoordinateProvider'
|
import { useGroupsCoordinates } from '../../providers/GroupsCoordinateProvider'
|
||||||
import { isProPlan } from '@/features/billing/helpers/isProPlan'
|
import { hasProPerks } from '@/features/billing/helpers/hasProPerks'
|
||||||
import { computeDropOffPath } from '../../helpers/computeDropOffPath'
|
import { computeDropOffPath } from '../../helpers/computeDropOffPath'
|
||||||
import { computeSourceCoordinates } from '../../helpers/computeSourceCoordinates'
|
import { computeSourceCoordinates } from '../../helpers/computeSourceCoordinates'
|
||||||
import { TotalAnswersInBlock } from '@typebot.io/schemas/features/analytics'
|
import { TotalAnswersInBlock } from '@typebot.io/schemas/features/analytics'
|
||||||
@@ -64,7 +64,7 @@ export const DropOffEdge = ({
|
|||||||
[blockId, totalAnswersInBlocks]
|
[blockId, totalAnswersInBlocks]
|
||||||
)
|
)
|
||||||
|
|
||||||
const isWorkspaceProPlan = isProPlan(workspace)
|
const isWorkspaceProPlan = hasProPerks(workspace)
|
||||||
|
|
||||||
const { totalDroppedUser, dropOffRate } = useMemo(() => {
|
const { totalDroppedUser, dropOffRate } = useMemo(() => {
|
||||||
if (!publishedTypebot || currentBlock?.total === undefined)
|
if (!publishedTypebot || currentBlock?.total === undefined)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { integrationsList } from './embeds/EmbedButton'
|
|||||||
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
||||||
import { LockTag } from '@/features/billing/components/LockTag'
|
import { LockTag } from '@/features/billing/components/LockTag'
|
||||||
import { UpgradeButton } from '@/features/billing/components/UpgradeButton'
|
import { UpgradeButton } from '@/features/billing/components/UpgradeButton'
|
||||||
import { isProPlan } from '@/features/billing/helpers/isProPlan'
|
import { hasProPerks } from '@/features/billing/helpers/hasProPerks'
|
||||||
import { CustomDomainsDropdown } from '@/features/customDomains/components/CustomDomainsDropdown'
|
import { CustomDomainsDropdown } from '@/features/customDomains/components/CustomDomainsDropdown'
|
||||||
import { TypebotHeader } from '@/features/editor/components/TypebotHeader'
|
import { TypebotHeader } from '@/features/editor/components/TypebotHeader'
|
||||||
import { parseDefaultPublicId } from '../helpers/parseDefaultPublicId'
|
import { parseDefaultPublicId } from '../helpers/parseDefaultPublicId'
|
||||||
@@ -130,7 +130,7 @@ export const SharePage = () => {
|
|||||||
{isNotDefined(typebot?.customDomain) &&
|
{isNotDefined(typebot?.customDomain) &&
|
||||||
env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME ? (
|
env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME ? (
|
||||||
<>
|
<>
|
||||||
{isProPlan(workspace) ? (
|
{hasProPerks(workspace) ? (
|
||||||
<CustomDomainsDropdown
|
<CustomDomainsDropdown
|
||||||
onCustomDomainSelect={handleCustomDomainChange}
|
onCustomDomainSelect={handleCustomDomainChange}
|
||||||
/>
|
/>
|
||||||
@@ -138,6 +138,7 @@ export const SharePage = () => {
|
|||||||
<UpgradeButton
|
<UpgradeButton
|
||||||
colorScheme="gray"
|
colorScheme="gray"
|
||||||
limitReachedType={t('billing.limitMessage.customDomain')}
|
limitReachedType={t('billing.limitMessage.customDomain')}
|
||||||
|
excludedPlans={[Plan.STARTER]}
|
||||||
>
|
>
|
||||||
<Text mr="2">Add my domain</Text>{' '}
|
<Text mr="2">Add my domain</Text>{' '}
|
||||||
<LockTag plan={Plan.PRO} />
|
<LockTag plan={Plan.PRO} />
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
NotionLogo,
|
NotionLogo,
|
||||||
WebflowLogo,
|
WebflowLogo,
|
||||||
IframeLogo,
|
IframeLogo,
|
||||||
OtherLogo,
|
|
||||||
} from './logos'
|
} from './logos'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import {
|
||||||
@@ -30,7 +29,6 @@ import {
|
|||||||
IframeModal,
|
IframeModal,
|
||||||
WixModal,
|
WixModal,
|
||||||
} from './modals'
|
} from './modals'
|
||||||
import { OtherModal } from './modals/OtherModal'
|
|
||||||
import { ScriptModal } from './modals/Script/ScriptModal'
|
import { ScriptModal } from './modals/Script/ScriptModal'
|
||||||
import { CodeIcon } from '@/components/icons'
|
import { CodeIcon } from '@/components/icons'
|
||||||
import { ApiModal } from './modals/ApiModal'
|
import { ApiModal } from './modals/ApiModal'
|
||||||
@@ -43,6 +41,11 @@ import { WhatsAppLogo } from '@/components/logos/WhatsAppLogo'
|
|||||||
import { WhatsAppModal } from './modals/WhatsAppModal/WhatsAppModal'
|
import { WhatsAppModal } from './modals/WhatsAppModal/WhatsAppModal'
|
||||||
import { ParentModalProvider } from '@/features/graph/providers/ParentModalProvider'
|
import { ParentModalProvider } from '@/features/graph/providers/ParentModalProvider'
|
||||||
import { isWhatsAppAvailable } from '@/features/telemetry/posthog'
|
import { isWhatsAppAvailable } from '@/features/telemetry/posthog'
|
||||||
|
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
||||||
|
import { hasProPerks } from '@/features/billing/helpers/hasProPerks'
|
||||||
|
import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
|
||||||
|
import { LockTag } from '@/features/billing/components/LockTag'
|
||||||
|
import { Plan } from '@typebot.io/prisma'
|
||||||
|
|
||||||
export type ModalProps = {
|
export type ModalProps = {
|
||||||
publicId: string
|
publicId: string
|
||||||
@@ -54,13 +57,15 @@ export type ModalProps = {
|
|||||||
type EmbedButtonProps = Pick<ModalProps, 'publicId' | 'isPublished'> & {
|
type EmbedButtonProps = Pick<ModalProps, 'publicId' | 'isPublished'> & {
|
||||||
logo: JSX.Element
|
logo: JSX.Element
|
||||||
label: string
|
label: string
|
||||||
Modal: (props: ModalProps) => JSX.Element
|
lockTagPlan?: Plan
|
||||||
|
modal: (modalProps: { onClose: () => void; isOpen: boolean }) => JSX.Element
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EmbedButton = ({
|
export const EmbedButton = ({
|
||||||
logo,
|
logo,
|
||||||
label,
|
label,
|
||||||
Modal,
|
modal,
|
||||||
|
lockTagPlan,
|
||||||
...modalProps
|
...modalProps
|
||||||
}: EmbedButtonProps) => {
|
}: EmbedButtonProps) => {
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||||
@@ -75,22 +80,44 @@ export const EmbedButton = ({
|
|||||||
>
|
>
|
||||||
<VStack>
|
<VStack>
|
||||||
{logo}
|
{logo}
|
||||||
<Text>{label}</Text>
|
<Text>
|
||||||
|
{label}
|
||||||
|
{lockTagPlan && (
|
||||||
|
<>
|
||||||
|
{' '}
|
||||||
|
<LockTag plan={lockTagPlan} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
</VStack>
|
</VStack>
|
||||||
<Modal isOpen={isOpen} onClose={onClose} {...modalProps} />
|
{modal({ isOpen, onClose, ...modalProps })}
|
||||||
</WrapItem>
|
</WrapItem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const integrationsList = [
|
export const integrationsList = [
|
||||||
(props: Pick<ModalProps, 'publicId' | 'isPublished'>) => {
|
(props: Pick<ModalProps, 'publicId' | 'isPublished'>) => {
|
||||||
|
const { workspace } = useWorkspace()
|
||||||
|
|
||||||
if (isWhatsAppAvailable())
|
if (isWhatsAppAvailable())
|
||||||
return (
|
return (
|
||||||
<ParentModalProvider>
|
<ParentModalProvider>
|
||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<WhatsAppLogo height={100} width="70px" />}
|
logo={<WhatsAppLogo height={100} width="70px" />}
|
||||||
label="WhatsApp"
|
label="WhatsApp"
|
||||||
Modal={WhatsAppModal}
|
lockTagPlan={hasProPerks(workspace) ? undefined : 'PRO'}
|
||||||
|
modal={({ onClose, isOpen }) =>
|
||||||
|
hasProPerks(workspace) ? (
|
||||||
|
<WhatsAppModal isOpen={isOpen} onClose={onClose} {...props} />
|
||||||
|
) : (
|
||||||
|
<ChangePlanModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
excludedPlans={['STARTER']}
|
||||||
|
type="deploy on WhatsApp"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</ParentModalProvider>
|
</ParentModalProvider>
|
||||||
@@ -100,7 +127,9 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<WordpressLogo height={100} width="70px" />}
|
logo={<WordpressLogo height={100} width="70px" />}
|
||||||
label="Wordpress"
|
label="Wordpress"
|
||||||
Modal={WordpressModal}
|
modal={({ onClose, isOpen }) => (
|
||||||
|
<WordpressModal isOpen={isOpen} onClose={onClose} {...props} />
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -108,7 +137,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<ShopifyLogo height={100} width="65px" />}
|
logo={<ShopifyLogo height={100} width="65px" />}
|
||||||
label="Shopify"
|
label="Shopify"
|
||||||
Modal={ShopifyModal}
|
modal={(modalProps) => <ShopifyModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -116,7 +145,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<WixLogo height={100} width="90px" />}
|
logo={<WixLogo height={100} width="90px" />}
|
||||||
label="Wix"
|
label="Wix"
|
||||||
Modal={WixModal}
|
modal={(modalProps) => <WixModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -124,7 +153,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<GtmLogo height={100} width="70px" />}
|
logo={<GtmLogo height={100} width="70px" />}
|
||||||
label="Google Tag Manager"
|
label="Google Tag Manager"
|
||||||
Modal={GtmModal}
|
modal={(modalProps) => <GtmModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -132,7 +161,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<JavascriptLogo height={100} width="70px" />}
|
logo={<JavascriptLogo height={100} width="70px" />}
|
||||||
label="HTML & Javascript"
|
label="HTML & Javascript"
|
||||||
Modal={JavascriptModal}
|
modal={(modalProps) => <JavascriptModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -140,7 +169,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<ReactLogo height={100} width="70px" />}
|
logo={<ReactLogo height={100} width="70px" />}
|
||||||
label="React"
|
label="React"
|
||||||
Modal={ReactModal}
|
modal={(modalProps) => <ReactModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -148,7 +177,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<NextjsLogo height={100} width="70px" />}
|
logo={<NextjsLogo height={100} width="70px" />}
|
||||||
label="Nextjs"
|
label="Nextjs"
|
||||||
Modal={NextjsModal}
|
modal={(modalProps) => <NextjsModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -156,7 +185,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<CodeIcon height={100} width="60px" />}
|
logo={<CodeIcon height={100} width="60px" />}
|
||||||
label="API"
|
label="API"
|
||||||
Modal={ApiModal}
|
modal={(modalProps) => <ApiModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -164,7 +193,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<NotionLogo height={100} width="60px" />}
|
logo={<NotionLogo height={100} width="60px" />}
|
||||||
label="Notion"
|
label="Notion"
|
||||||
Modal={NotionModal}
|
modal={(modalProps) => <NotionModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -172,7 +201,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<WebflowLogo height={100} width="70px" />}
|
logo={<WebflowLogo height={100} width="70px" />}
|
||||||
label="Webflow"
|
label="Webflow"
|
||||||
Modal={WebflowModal}
|
modal={(modalProps) => <WebflowModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -180,7 +209,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<FlutterFlowLogo height={100} width="60px" />}
|
logo={<FlutterFlowLogo height={100} width="60px" />}
|
||||||
label="FlutterFlow"
|
label="FlutterFlow"
|
||||||
Modal={FlutterFlowModal}
|
modal={(modalProps) => <FlutterFlowModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -194,7 +223,7 @@ export const integrationsList = [
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label="Script"
|
label="Script"
|
||||||
Modal={ScriptModal}
|
modal={(modalProps) => <ScriptModal {...modalProps} {...props} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -202,15 +231,7 @@ export const integrationsList = [
|
|||||||
<EmbedButton
|
<EmbedButton
|
||||||
logo={<IframeLogo height={100} width="70px" />}
|
logo={<IframeLogo height={100} width="70px" />}
|
||||||
label="Iframe"
|
label="Iframe"
|
||||||
Modal={IframeModal}
|
modal={(modalProps) => <IframeModal {...modalProps} {...props} />}
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
(props: Pick<ModalProps, 'publicId' | 'isPublished'>) => (
|
|
||||||
<EmbedButton
|
|
||||||
logo={<OtherLogo height={100} width="70px" />}
|
|
||||||
label="Other"
|
|
||||||
Modal={OtherModal}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import React, { useState } from 'react'
|
|
||||||
import { isDefined } from '@udecode/plate-common'
|
|
||||||
import { EmbedModal } from '../EmbedModal'
|
|
||||||
import { JavascriptInstructions } from './Javascript/instructions/JavascriptInstructions'
|
|
||||||
import { ModalProps } from '../EmbedButton'
|
|
||||||
|
|
||||||
export const OtherModal = ({ isOpen, onClose, isPublished }: ModalProps) => {
|
|
||||||
const [selectedEmbedType, setSelectedEmbedType] = useState<
|
|
||||||
'standard' | 'popup' | 'bubble' | undefined
|
|
||||||
>()
|
|
||||||
return (
|
|
||||||
<EmbedModal
|
|
||||||
titlePrefix="Other"
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={onClose}
|
|
||||||
isPublished={isPublished}
|
|
||||||
onSelectEmbedType={setSelectedEmbedType}
|
|
||||||
selectedEmbedType={selectedEmbedType}
|
|
||||||
>
|
|
||||||
{isDefined(selectedEmbedType) && (
|
|
||||||
<JavascriptInstructions type={selectedEmbedType} />
|
|
||||||
)}
|
|
||||||
</EmbedModal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -23,6 +23,5 @@ export const convertPublicTypebotToTypebot = (
|
|||||||
isClosed: existingTypebot.isClosed,
|
isClosed: existingTypebot.isClosed,
|
||||||
resultsTablePreferences: existingTypebot.resultsTablePreferences,
|
resultsTablePreferences: existingTypebot.resultsTablePreferences,
|
||||||
selectedThemeTemplateId: existingTypebot.selectedThemeTemplateId,
|
selectedThemeTemplateId: existingTypebot.selectedThemeTemplateId,
|
||||||
whatsAppPhoneNumberId: existingTypebot.whatsAppPhoneNumberId,
|
|
||||||
whatsAppCredentialsId: existingTypebot.whatsAppCredentialsId,
|
whatsAppCredentialsId: existingTypebot.whatsAppCredentialsId,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
import { isWriteTypebotForbidden } from '../helpers/isWriteTypebotForbidden'
|
import { isWriteTypebotForbidden } from '../helpers/isWriteTypebotForbidden'
|
||||||
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
|
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
|
||||||
import { Prisma } from '@typebot.io/prisma'
|
import { Prisma } from '@typebot.io/prisma'
|
||||||
|
import { hasProPerks } from '@/features/billing/helpers/hasProPerks'
|
||||||
|
|
||||||
export const updateTypebot = authenticatedProcedure
|
export const updateTypebot = authenticatedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -30,7 +31,6 @@ export const updateTypebot = authenticatedProcedure
|
|||||||
typebotSchema._def.schema
|
typebotSchema._def.schema
|
||||||
.pick({
|
.pick({
|
||||||
isClosed: true,
|
isClosed: true,
|
||||||
whatsAppPhoneNumberId: true,
|
|
||||||
whatsAppCredentialsId: true,
|
whatsAppCredentialsId: true,
|
||||||
})
|
})
|
||||||
.partial()
|
.partial()
|
||||||
@@ -70,7 +70,6 @@ export const updateTypebot = authenticatedProcedure
|
|||||||
plan: true,
|
plan: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
whatsAppPhoneNumberId: true,
|
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -119,6 +118,16 @@ export const updateTypebot = authenticatedProcedure
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typebot.whatsAppCredentialsId &&
|
||||||
|
!hasProPerks(existingTypebot.workspace)
|
||||||
|
) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: 'WhatsApp is only available for Pro workspaces',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const newTypebot = await prisma.typebot.update({
|
const newTypebot = await prisma.typebot.update({
|
||||||
where: {
|
where: {
|
||||||
id: existingTypebot.id,
|
id: existingTypebot.id,
|
||||||
@@ -151,7 +160,6 @@ export const updateTypebot = authenticatedProcedure
|
|||||||
customDomain:
|
customDomain:
|
||||||
typebot.customDomain === null ? null : typebot.customDomain,
|
typebot.customDomain === null ? null : typebot.customDomain,
|
||||||
isClosed: typebot.isClosed,
|
isClosed: typebot.isClosed,
|
||||||
whatsAppPhoneNumberId: typebot.whatsAppPhoneNumberId ?? undefined,
|
|
||||||
whatsAppCredentialsId: typebot.whatsAppCredentialsId ?? undefined,
|
whatsAppCredentialsId: typebot.whatsAppCredentialsId ?? undefined,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ export default {
|
|||||||
'billing.pricingCard.pro.description': 'Für Agenturen & wachsende Start-ups.',
|
'billing.pricingCard.pro.description': 'Für Agenturen & wachsende Start-ups.',
|
||||||
'billing.pricingCard.pro.everythingFromStarter': 'Alles in Starter',
|
'billing.pricingCard.pro.everythingFromStarter': 'Alles in Starter',
|
||||||
'billing.pricingCard.pro.includedSeats': '5 Plätze inklusive',
|
'billing.pricingCard.pro.includedSeats': '5 Plätze inklusive',
|
||||||
|
'billing.pricingCard.pro.whatsAppIntegration': 'WhatsApp-Integration',
|
||||||
'billing.pricingCard.pro.customDomains': 'Eigene Domains',
|
'billing.pricingCard.pro.customDomains': 'Eigene Domains',
|
||||||
'billing.pricingCard.pro.analytics': 'Detaillierte Analysen',
|
'billing.pricingCard.pro.analytics': 'Detaillierte Analysen',
|
||||||
'billing.usage.heading': 'Nutzung',
|
'billing.usage.heading': 'Nutzung',
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ export default {
|
|||||||
'billing.pricingCard.pro.description': 'For agencies & growing startups.',
|
'billing.pricingCard.pro.description': 'For agencies & growing startups.',
|
||||||
'billing.pricingCard.pro.everythingFromStarter': 'Everything in Starter',
|
'billing.pricingCard.pro.everythingFromStarter': 'Everything in Starter',
|
||||||
'billing.pricingCard.pro.includedSeats': '5 seats included',
|
'billing.pricingCard.pro.includedSeats': '5 seats included',
|
||||||
|
'billing.pricingCard.pro.whatsAppIntegration': 'WhatsApp integration',
|
||||||
'billing.pricingCard.pro.customDomains': 'Custom domains',
|
'billing.pricingCard.pro.customDomains': 'Custom domains',
|
||||||
'billing.pricingCard.pro.analytics': 'In-depth analytics',
|
'billing.pricingCard.pro.analytics': 'In-depth analytics',
|
||||||
'billing.usage.heading': 'Usage',
|
'billing.usage.heading': 'Usage',
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ export default {
|
|||||||
'billing.pricingCard.pro.everythingFromStarter':
|
'billing.pricingCard.pro.everythingFromStarter':
|
||||||
"Tout ce qu'il y a dans Starter",
|
"Tout ce qu'il y a dans Starter",
|
||||||
'billing.pricingCard.pro.includedSeats': '5 collègues inclus',
|
'billing.pricingCard.pro.includedSeats': '5 collègues inclus',
|
||||||
|
'billing.pricingCard.pro.whatsAppIntegration': 'Intégration WhatsApp',
|
||||||
'billing.pricingCard.pro.customDomains': 'Domaines personnalisés',
|
'billing.pricingCard.pro.customDomains': 'Domaines personnalisés',
|
||||||
'billing.pricingCard.pro.analytics': 'Analyses approfondies',
|
'billing.pricingCard.pro.analytics': 'Analyses approfondies',
|
||||||
'billing.usage.heading': 'Utilisation',
|
'billing.usage.heading': 'Utilisation',
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ export default {
|
|||||||
'Para agências e startups em crescimento.',
|
'Para agências e startups em crescimento.',
|
||||||
'billing.pricingCard.pro.everythingFromStarter': 'Tudo em Starter',
|
'billing.pricingCard.pro.everythingFromStarter': 'Tudo em Starter',
|
||||||
'billing.pricingCard.pro.includedSeats': '5 assentos incluídos',
|
'billing.pricingCard.pro.includedSeats': '5 assentos incluídos',
|
||||||
|
'billing.pricingCard.pro.whatsAppIntegration': 'Integração do WhatsApp',
|
||||||
'billing.pricingCard.pro.customDomains': 'Domínios personalizados',
|
'billing.pricingCard.pro.customDomains': 'Domínios personalizados',
|
||||||
'billing.pricingCard.pro.analytics': 'Análises aprofundadas',
|
'billing.pricingCard.pro.analytics': 'Análises aprofundadas',
|
||||||
'billing.usage.heading': 'Uso',
|
'billing.usage.heading': 'Uso',
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ export default {
|
|||||||
'Para agências e startups em crescimento.',
|
'Para agências e startups em crescimento.',
|
||||||
'billing.pricingCard.pro.everythingFromStarter': 'Tudo em Starter',
|
'billing.pricingCard.pro.everythingFromStarter': 'Tudo em Starter',
|
||||||
'billing.pricingCard.pro.includedSeats': '5 lugares incluídos',
|
'billing.pricingCard.pro.includedSeats': '5 lugares incluídos',
|
||||||
|
'billing.pricingCard.pro.whatsAppIntegration': 'Integração do WhatsApp',
|
||||||
'billing.pricingCard.pro.customDomains': 'Domínios personalizados',
|
'billing.pricingCard.pro.customDomains': 'Domínios personalizados',
|
||||||
'billing.pricingCard.pro.analytics': 'Análises aprofundadas',
|
'billing.pricingCard.pro.analytics': 'Análises aprofundadas',
|
||||||
'billing.usage.heading': 'Uso',
|
'billing.usage.heading': 'Uso',
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
await prisma.typebot.updateMany({
|
await prisma.typebot.updateMany({
|
||||||
where: { id: typebot.id },
|
where: { id: typebot.id },
|
||||||
data: {
|
data: {
|
||||||
|
whatsAppCredentialsId: null,
|
||||||
settings: {
|
settings: {
|
||||||
...settings,
|
...settings,
|
||||||
general: {
|
general: {
|
||||||
|
|||||||
@@ -8687,6 +8687,12 @@
|
|||||||
"comparisons"
|
"comparisons"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"sessionExpiryTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 48,
|
||||||
|
"minimum": 0.01,
|
||||||
|
"description": "Expiration delay in hours after latest interaction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -12799,6 +12805,12 @@
|
|||||||
"comparisons"
|
"comparisons"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"sessionExpiryTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 48,
|
||||||
|
"minimum": 0.01,
|
||||||
|
"description": "Expiration delay in hours after latest interaction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -12874,10 +12886,6 @@
|
|||||||
"isClosed": {
|
"isClosed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"whatsAppPhoneNumberId": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"whatsAppCredentialsId": {
|
"whatsAppCredentialsId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
@@ -12903,7 +12911,6 @@
|
|||||||
"resultsTablePreferences",
|
"resultsTablePreferences",
|
||||||
"isArchived",
|
"isArchived",
|
||||||
"isClosed",
|
"isClosed",
|
||||||
"whatsAppPhoneNumberId",
|
|
||||||
"whatsAppCredentialsId"
|
"whatsAppCredentialsId"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -16878,6 +16885,12 @@
|
|||||||
"comparisons"
|
"comparisons"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"sessionExpiryTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 48,
|
||||||
|
"minimum": 0.01,
|
||||||
|
"description": "Expiration delay in hours after latest interaction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -17020,10 +17033,6 @@
|
|||||||
"isClosed": {
|
"isClosed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"whatsAppPhoneNumberId": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"whatsAppCredentialsId": {
|
"whatsAppCredentialsId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
@@ -21014,6 +21023,12 @@
|
|||||||
"comparisons"
|
"comparisons"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"sessionExpiryTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 48,
|
||||||
|
"minimum": 0.01,
|
||||||
|
"description": "Expiration delay in hours after latest interaction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -21089,10 +21104,6 @@
|
|||||||
"isClosed": {
|
"isClosed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"whatsAppPhoneNumberId": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"whatsAppCredentialsId": {
|
"whatsAppCredentialsId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
@@ -21118,7 +21129,6 @@
|
|||||||
"resultsTablePreferences",
|
"resultsTablePreferences",
|
||||||
"isArchived",
|
"isArchived",
|
||||||
"isClosed",
|
"isClosed",
|
||||||
"whatsAppPhoneNumberId",
|
|
||||||
"whatsAppCredentialsId"
|
"whatsAppCredentialsId"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -25117,6 +25127,12 @@
|
|||||||
"comparisons"
|
"comparisons"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"sessionExpiryTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 48,
|
||||||
|
"minimum": 0.01,
|
||||||
|
"description": "Expiration delay in hours after latest interaction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -25192,10 +25208,6 @@
|
|||||||
"isClosed": {
|
"isClosed": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"whatsAppPhoneNumberId": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true
|
|
||||||
},
|
|
||||||
"whatsAppCredentialsId": {
|
"whatsAppCredentialsId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
@@ -25221,7 +25233,6 @@
|
|||||||
"resultsTablePreferences",
|
"resultsTablePreferences",
|
||||||
"isArchived",
|
"isArchived",
|
||||||
"isClosed",
|
"isClosed",
|
||||||
"whatsAppPhoneNumberId",
|
|
||||||
"whatsAppCredentialsId"
|
"whatsAppCredentialsId"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -29279,6 +29290,12 @@
|
|||||||
"comparisons"
|
"comparisons"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"sessionExpiryTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 48,
|
||||||
|
"minimum": 0.01,
|
||||||
|
"description": "Expiration delay in hours after latest interaction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|||||||
@@ -3815,6 +3815,12 @@
|
|||||||
"comparisons"
|
"comparisons"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"sessionExpiryTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 48,
|
||||||
|
"minimum": 0.01,
|
||||||
|
"description": "Expiration delay in hours after latest interaction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@@ -6226,6 +6232,12 @@
|
|||||||
"comparisons"
|
"comparisons"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"sessionExpiryTimeout": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 48,
|
||||||
|
"minimum": 0.01,
|
||||||
|
"description": "Expiration delay in hours after latest interaction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export const ProPlanCard = ({ isYearly }: Props) => {
|
|||||||
</chakra.span>
|
</chakra.span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</HStack>,
|
</HStack>,
|
||||||
|
'WhatsApp integration',
|
||||||
'Custom domains',
|
'Custom domains',
|
||||||
'In-depth analytics',
|
'In-depth analytics',
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { validateUrl } from './blocks/inputs/url/validateUrl'
|
|||||||
import { resumeChatCompletion } from './blocks/integrations/openai/resumeChatCompletion'
|
import { resumeChatCompletion } from './blocks/integrations/openai/resumeChatCompletion'
|
||||||
import { resumeWebhookExecution } from './blocks/integrations/webhook/resumeWebhookExecution'
|
import { resumeWebhookExecution } from './blocks/integrations/webhook/resumeWebhookExecution'
|
||||||
import { upsertAnswer } from './queries/upsertAnswer'
|
import { upsertAnswer } from './queries/upsertAnswer'
|
||||||
import { startBotFlow } from './startBotFlow'
|
|
||||||
import { parseButtonsReply } from './blocks/inputs/buttons/parseButtonsReply'
|
import { parseButtonsReply } from './blocks/inputs/buttons/parseButtonsReply'
|
||||||
import { ParsedReply } from './types'
|
import { ParsedReply } from './types'
|
||||||
import { validateNumber } from './blocks/inputs/number/validateNumber'
|
import { validateNumber } from './blocks/inputs/number/validateNumber'
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const resumeWhatsAppFlow = async ({
|
|||||||
? await continueBotFlow(sessionState)(messageContent)
|
? await continueBotFlow(sessionState)(messageContent)
|
||||||
: workspaceId
|
: workspaceId
|
||||||
? await startWhatsAppSession({
|
? await startWhatsAppSession({
|
||||||
message: receivedMessage,
|
incomingMessage: messageContent,
|
||||||
sessionId,
|
sessionId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
credentials: { ...credentials, id: credentialsId as string },
|
credentials: { ...credentials, id: credentialsId as string },
|
||||||
|
|||||||
@@ -13,11 +13,14 @@ import {
|
|||||||
WhatsAppIncomingMessage,
|
WhatsAppIncomingMessage,
|
||||||
defaultSessionExpiryTimeout,
|
defaultSessionExpiryTimeout,
|
||||||
} from '@typebot.io/schemas/features/whatsapp'
|
} from '@typebot.io/schemas/features/whatsapp'
|
||||||
import { isNotDefined } from '@typebot.io/lib/utils'
|
import { isInputBlock, isNotDefined } from '@typebot.io/lib/utils'
|
||||||
import { startSession } from '../startSession'
|
import { startSession } from '../startSession'
|
||||||
|
import { getNextGroup } from '../getNextGroup'
|
||||||
|
import { continueBotFlow } from '../continueBotFlow'
|
||||||
|
import { upsertResult } from '../queries/upsertResult'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
message: WhatsAppIncomingMessage
|
incomingMessage?: string
|
||||||
sessionId: string
|
sessionId: string
|
||||||
workspaceId?: string
|
workspaceId?: string
|
||||||
credentials: WhatsAppCredentials['data'] & Pick<WhatsAppCredentials, 'id'>
|
credentials: WhatsAppCredentials['data'] & Pick<WhatsAppCredentials, 'id'>
|
||||||
@@ -25,7 +28,7 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const startWhatsAppSession = async ({
|
export const startWhatsAppSession = async ({
|
||||||
message,
|
incomingMessage,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
credentials,
|
credentials,
|
||||||
contact,
|
contact,
|
||||||
@@ -63,20 +66,41 @@ export const startWhatsAppSession = async ({
|
|||||||
(publicTypebot) =>
|
(publicTypebot) =>
|
||||||
publicTypebot.settings.whatsApp?.startCondition &&
|
publicTypebot.settings.whatsApp?.startCondition &&
|
||||||
messageMatchStartCondition(
|
messageMatchStartCondition(
|
||||||
getIncomingMessageText(message),
|
incomingMessage ?? '',
|
||||||
publicTypebot.settings.whatsApp?.startCondition
|
publicTypebot.settings.whatsApp?.startCondition
|
||||||
)
|
)
|
||||||
) ?? botsWithWhatsAppEnabled[0]
|
) ?? botsWithWhatsAppEnabled[0]
|
||||||
|
|
||||||
if (isNotDefined(publicTypebot)) return
|
if (isNotDefined(publicTypebot)) return
|
||||||
|
|
||||||
const session = await startSession({
|
let session = await startSession({
|
||||||
startParams: {
|
startParams: {
|
||||||
typebot: publicTypebot.typebot.publicId as string,
|
typebot: publicTypebot.typebot.publicId as string,
|
||||||
},
|
},
|
||||||
userId: undefined,
|
userId: undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// If first block is an input block, we can directly continue the bot flow
|
||||||
|
const firstEdgeId =
|
||||||
|
session.newSessionState.typebotsQueue[0].typebot.groups[0].blocks[0]
|
||||||
|
.outgoingEdgeId
|
||||||
|
const nextGroup = await getNextGroup(session.newSessionState)(firstEdgeId)
|
||||||
|
const firstBlock = nextGroup.group?.blocks.at(0)
|
||||||
|
if (firstBlock && isInputBlock(firstBlock)) {
|
||||||
|
const resultId = session.newSessionState.typebotsQueue[0].resultId
|
||||||
|
if (resultId)
|
||||||
|
await upsertResult({
|
||||||
|
hasStarted: true,
|
||||||
|
isCompleted: false,
|
||||||
|
resultId,
|
||||||
|
typebot: session.newSessionState.typebotsQueue[0].typebot,
|
||||||
|
})
|
||||||
|
session = await continueBotFlow({
|
||||||
|
...session.newSessionState,
|
||||||
|
currentBlock: { groupId: firstBlock.groupId, blockId: firstBlock.id },
|
||||||
|
})(incomingMessage)
|
||||||
|
}
|
||||||
|
|
||||||
const sessionExpiryTimeoutHours =
|
const sessionExpiryTimeoutHours =
|
||||||
publicTypebot.settings.whatsApp?.sessionExpiryTimeout ??
|
publicTypebot.settings.whatsApp?.sessionExpiryTimeout ??
|
||||||
defaultSessionExpiryTimeout
|
defaultSessionExpiryTimeout
|
||||||
@@ -166,21 +190,3 @@ const matchComparison = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getIncomingMessageText = (message: WhatsAppIncomingMessage): string => {
|
|
||||||
switch (message.type) {
|
|
||||||
case 'text':
|
|
||||||
return message.text.body
|
|
||||||
case 'button':
|
|
||||||
return message.button.text
|
|
||||||
case 'interactive': {
|
|
||||||
return message.interactive.button_reply.title
|
|
||||||
}
|
|
||||||
case 'video':
|
|
||||||
case 'document':
|
|
||||||
case 'audio':
|
|
||||||
case 'image': {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export const parseTestTypebot = (
|
|||||||
isArchived: false,
|
isArchived: false,
|
||||||
isClosed: false,
|
isClosed: false,
|
||||||
resultsTablePreferences: null,
|
resultsTablePreferences: null,
|
||||||
whatsAppPhoneNumberId: null,
|
|
||||||
whatsAppCredentialsId: null,
|
whatsAppCredentialsId: null,
|
||||||
variables: [{ id: 'var1', name: 'var1' }],
|
variables: [{ id: 'var1', name: 'var1' }],
|
||||||
...partialTypebot,
|
...partialTypebot,
|
||||||
|
|||||||
@@ -198,7 +198,6 @@ model Typebot {
|
|||||||
webhooks Webhook[]
|
webhooks Webhook[]
|
||||||
isArchived Boolean @default(false)
|
isArchived Boolean @default(false)
|
||||||
isClosed Boolean @default(false)
|
isClosed Boolean @default(false)
|
||||||
whatsAppPhoneNumberId String?
|
|
||||||
whatsAppCredentialsId String?
|
whatsAppCredentialsId String?
|
||||||
|
|
||||||
@@index([workspaceId])
|
@@index([workspaceId])
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `whatsAppPhoneNumberId` on the `Typebot` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Typebot" DROP COLUMN "whatsAppPhoneNumberId";
|
||||||
@@ -182,7 +182,6 @@ model Typebot {
|
|||||||
webhooks Webhook[]
|
webhooks Webhook[]
|
||||||
isArchived Boolean @default(false)
|
isArchived Boolean @default(false)
|
||||||
isClosed Boolean @default(false)
|
isClosed Boolean @default(false)
|
||||||
whatsAppPhoneNumberId String?
|
|
||||||
whatsAppCredentialsId String?
|
whatsAppCredentialsId String?
|
||||||
|
|
||||||
@@index([workspaceId])
|
@@index([workspaceId])
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ export const typebotSchema = z.preprocess(
|
|||||||
resultsTablePreferences: resultsTablePreferencesSchema.nullable(),
|
resultsTablePreferences: resultsTablePreferencesSchema.nullable(),
|
||||||
isArchived: z.boolean(),
|
isArchived: z.boolean(),
|
||||||
isClosed: z.boolean(),
|
isClosed: z.boolean(),
|
||||||
whatsAppPhoneNumberId: z.string().nullable(),
|
|
||||||
whatsAppCredentialsId: z.string().nullable(),
|
whatsAppCredentialsId: z.string().nullable(),
|
||||||
}) satisfies z.ZodType<TypebotPrisma, z.ZodTypeDef, unknown>
|
}) satisfies z.ZodType<TypebotPrisma, z.ZodTypeDef, unknown>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user