diff --git a/apps/builder/assets/logos/ZapierLogo.tsx b/apps/builder/assets/logos/ZapierLogo.tsx new file mode 100644 index 000000000..c7b32512f --- /dev/null +++ b/apps/builder/assets/logos/ZapierLogo.tsx @@ -0,0 +1,15 @@ +import { Icon, IconProps } from '@chakra-ui/react' + +export const ZapierLogo = (props: IconProps) => ( + + + +) diff --git a/apps/builder/assets/logos/index.tsx b/apps/builder/assets/logos/index.tsx index 2ec27f1ed..c72f19a62 100644 --- a/apps/builder/assets/logos/index.tsx +++ b/apps/builder/assets/logos/index.tsx @@ -14,3 +14,4 @@ export * from './WordpressLogo' export * from './WixLogo' export * from './GoogleLogo' export * from './FacebookLogo' +export * from './ZapierLogo' diff --git a/apps/builder/components/editor/StepsSideBar/StepIcon.tsx b/apps/builder/components/editor/StepsSideBar/StepIcon.tsx index a2f6a5b11..1da130f81 100644 --- a/apps/builder/components/editor/StepsSideBar/StepIcon.tsx +++ b/apps/builder/components/editor/StepsSideBar/StepIcon.tsx @@ -17,7 +17,7 @@ import { TextIcon, WebhookIcon, } from 'assets/icons' -import { GoogleAnalyticsLogo, GoogleSheetsLogo } from 'assets/logos' +import { GoogleAnalyticsLogo, GoogleSheetsLogo, ZapierLogo } from 'assets/logos' import { BubbleStepType, InputStepType, @@ -63,6 +63,8 @@ export const StepIcon = ({ type, ...props }: StepIconProps) => { return case IntegrationStepType.WEBHOOK: return + case IntegrationStepType.ZAPIER: + return case IntegrationStepType.EMAIL: return case 'start': diff --git a/apps/builder/components/editor/StepsSideBar/StepTypeLabel.tsx b/apps/builder/components/editor/StepsSideBar/StepTypeLabel.tsx index 1236a6de7..2f6c66934 100644 --- a/apps/builder/components/editor/StepsSideBar/StepTypeLabel.tsx +++ b/apps/builder/components/editor/StepsSideBar/StepTypeLabel.tsx @@ -51,6 +51,8 @@ export const StepTypeLabel = ({ type }: Props) => { ) case IntegrationStepType.WEBHOOK: return Webhook + case IntegrationStepType.ZAPIER: + return Zapier case IntegrationStepType.EMAIL: return Email default: diff --git a/apps/builder/components/shared/Graph/Nodes/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx b/apps/builder/components/shared/Graph/Nodes/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx index 7e9ad3549..ede5e0553 100644 --- a/apps/builder/components/shared/Graph/Nodes/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx +++ b/apps/builder/components/shared/Graph/Nodes/StepNode/SettingsPopoverContent/SettingsPopoverContent.tsx @@ -36,6 +36,7 @@ import { RedirectSettings } from './bodies/RedirectSettings' import { SendEmailSettings } from './bodies/SendEmailSettings/SendEmailSettings' import { SetVariableSettings } from './bodies/SetVariableSettings' import { WebhookSettings } from './bodies/WebhookSettings' +import { ZapierSettings } from './bodies/ZapierSettings' type Props = { step: Exclude @@ -199,6 +200,9 @@ export const StepSettings = ({ /> ) } + case IntegrationStepType.ZAPIER: { + return + } case IntegrationStepType.WEBHOOK: { return ( { + return ( + + + + {step.webhook.url ? ( + <>Your zap is correctly configured 🚀 + ) : ( + + Head up to Zapier to configure this step: + + + )} + + {step.webhook.url && } + + ) +} diff --git a/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodeContent/StepNodeContent.tsx b/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodeContent/StepNodeContent.tsx index 14b760dc5..d676b95b8 100644 --- a/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodeContent/StepNodeContent.tsx +++ b/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodeContent/StepNodeContent.tsx @@ -21,6 +21,7 @@ import { ConfigureContent } from './contents/ConfigureContent' import { ImageBubbleContent } from './contents/ImageBubbleContent' import { PlaceholderContent } from './contents/PlaceholderContent' import { SendEmailContent } from './contents/SendEmailContent' +import { ZapierContent } from './contents/ZapierContent' type Props = { step: Step | StartStep @@ -102,6 +103,9 @@ export const StepNodeContent = ({ step, indices }: Props) => { case IntegrationStepType.WEBHOOK: { return } + case IntegrationStepType.ZAPIER: { + return + } case IntegrationStepType.EMAIL: { return } diff --git a/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodeContent/contents/ZapierContent.tsx b/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodeContent/contents/ZapierContent.tsx new file mode 100644 index 000000000..aaed7b2a1 --- /dev/null +++ b/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodeContent/contents/ZapierContent.tsx @@ -0,0 +1,17 @@ +import { Text } from '@chakra-ui/react' +import { ZapierStep } from 'models' +import { isNotDefined } from 'utils' + +type Props = { + step: ZapierStep +} + +export const ZapierContent = ({ step }: Props) => { + if (isNotDefined(step.webhook.body)) + return Configure... + return ( + + {step.webhook.url ? 'Enabled' : 'Disabled'} + + ) +} diff --git a/apps/builder/services/typebots.ts b/apps/builder/services/typebots.ts index 565dd1fa5..a06aed0f0 100644 --- a/apps/builder/services/typebots.ts +++ b/apps/builder/services/typebots.ts @@ -218,6 +218,7 @@ const parseDefaultStepOptions = (type: StepWithOptionsType): StepOptions => { return defaultGoogleSheetsOptions case IntegrationStepType.GOOGLE_ANALYTICS: return defaultGoogleAnalyticsOptions + case IntegrationStepType.ZAPIER: case IntegrationStepType.WEBHOOK: return defaultWebhookOptions case IntegrationStepType.EMAIL: diff --git a/apps/viewer/pages/api/typebots/[typebotId]/blocks/[blockId]/steps/[stepId]/subscribeWebhook.ts b/apps/viewer/pages/api/typebots/[typebotId]/blocks/[blockId]/steps/[stepId]/subscribeWebhook.ts index d271d949b..21a8eda81 100644 --- a/apps/viewer/pages/api/typebots/[typebotId]/blocks/[blockId]/steps/[stepId]/subscribeWebhook.ts +++ b/apps/viewer/pages/api/typebots/[typebotId]/blocks/[blockId]/steps/[stepId]/subscribeWebhook.ts @@ -1,10 +1,10 @@ import { withSentry } from '@sentry/nextjs' import { Prisma } from 'db' import prisma from 'libs/prisma' -import { HttpMethod, IntegrationStepType, Typebot } from 'models' +import { HttpMethod, Typebot } from 'models' import { NextApiRequest, NextApiResponse } from 'next' import { authenticateUser } from 'services/api/utils' -import { methodNotAllowed } from 'utils' +import { isWebhookStep, methodNotAllowed } from 'utils' const handler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method === 'PATCH') { @@ -46,7 +46,7 @@ const addUrlToWebhookStep = ( ...b, steps: b.steps.map((s) => { if (s.id === stepId) { - if (s.type !== IntegrationStepType.WEBHOOK) throw new Error() + if (!isWebhookStep(s)) throw new Error() return { ...s, webhook: { diff --git a/apps/viewer/pages/api/typebots/[typebotId]/blocks/[blockId]/steps/[stepId]/unsubscribeWebhook.ts b/apps/viewer/pages/api/typebots/[typebotId]/blocks/[blockId]/steps/[stepId]/unsubscribeWebhook.ts index a1265a02e..f35310dcb 100644 --- a/apps/viewer/pages/api/typebots/[typebotId]/blocks/[blockId]/steps/[stepId]/unsubscribeWebhook.ts +++ b/apps/viewer/pages/api/typebots/[typebotId]/blocks/[blockId]/steps/[stepId]/unsubscribeWebhook.ts @@ -1,11 +1,11 @@ import { withSentry } from '@sentry/nextjs' import { Prisma } from 'db' import prisma from 'libs/prisma' -import { IntegrationStepType, Typebot } from 'models' +import { Typebot } from 'models' import { NextApiRequest, NextApiResponse } from 'next' import { authenticateUser } from 'services/api/utils' import { omit } from 'services/utils' -import { methodNotAllowed } from 'utils' +import { isWebhookStep, methodNotAllowed } from 'utils' const handler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method === 'DELETE') { @@ -44,7 +44,7 @@ const removeUrlFromWebhookStep = ( ...b, steps: b.steps.map((s) => { if (s.id === stepId) { - if (s.type !== IntegrationStepType.WEBHOOK) throw new Error() + if (!isWebhookStep(s)) throw new Error() return { ...s, webhook: omit(s.webhook, 'url') } } return s diff --git a/apps/viewer/pages/api/typebots/[typebotId]/webhookSteps.ts b/apps/viewer/pages/api/typebots/[typebotId]/webhookSteps.ts index 0bc94926e..a88672bb7 100644 --- a/apps/viewer/pages/api/typebots/[typebotId]/webhookSteps.ts +++ b/apps/viewer/pages/api/typebots/[typebotId]/webhookSteps.ts @@ -1,9 +1,9 @@ import { withSentry } from '@sentry/nextjs' import prisma from 'libs/prisma' -import { Block, IntegrationStepType } from 'models' +import { Block } from 'models' import { NextApiRequest, NextApiResponse } from 'next' import { authenticateUser } from 'services/api/utils' -import { methodNotAllowed } from 'utils' +import { isWebhookStep, methodNotAllowed } from 'utils' const handler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method === 'GET') { @@ -18,7 +18,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { { blockId: string; id: string; name: string }[] >((emptyWebhookSteps, block) => { const steps = block.steps.filter( - (step) => step.type === IntegrationStepType.WEBHOOK && !step.webhook.url + (step) => isWebhookStep(step) && !step.webhook.url ) return [ ...emptyWebhookSteps, diff --git a/packages/bot-engine/src/services/integration.ts b/packages/bot-engine/src/services/integration.ts index bee087e60..579598976 100644 --- a/packages/bot-engine/src/services/integration.ts +++ b/packages/bot-engine/src/services/integration.ts @@ -13,6 +13,7 @@ import { WebhookStep, SendEmailStep, PublicBlock, + ZapierStep, } from 'models' import { stringify } from 'qs' import { parseAnswers, sendRequest } from 'utils' @@ -46,6 +47,7 @@ export const executeIntegration = ({ return executeGoogleSheetIntegration(step, context) case IntegrationStepType.GOOGLE_ANALYTICS: return executeGoogleAnalyticsIntegration(step, context) + case IntegrationStepType.ZAPIER: case IntegrationStepType.WEBHOOK: return executeWebhook(step, context) case IntegrationStepType.EMAIL: @@ -156,7 +158,7 @@ const parseCellValues = ( }, {}) const executeWebhook = async ( - step: WebhookStep, + step: WebhookStep | ZapierStep, { blockId, stepId, diff --git a/packages/models/src/typebot/steps/integration.ts b/packages/models/src/typebot/steps/integration.ts index 82221c41f..1d02278e4 100644 --- a/packages/models/src/typebot/steps/integration.ts +++ b/packages/models/src/typebot/steps/integration.ts @@ -5,6 +5,7 @@ export type IntegrationStep = | GoogleAnalyticsStep | WebhookStep | SendEmailStep + | ZapierStep export type IntegrationStepOptions = | GoogleSheetsOptions @@ -17,6 +18,7 @@ export enum IntegrationStepType { GOOGLE_ANALYTICS = 'Google Analytics', WEBHOOK = 'Webhook', EMAIL = 'Email', + ZAPIER = 'Zapier', } export type GoogleSheetsStep = StepBase & { @@ -35,6 +37,10 @@ export type WebhookStep = StepBase & { webhook: Webhook } +export type ZapierStep = Omit & { + type: IntegrationStepType.ZAPIER +} + export type SendEmailStep = StepBase & { type: IntegrationStepType.EMAIL options: SendEmailOptions diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts index e3e188f22..dc5f348a6 100644 --- a/packages/utils/src/utils.ts +++ b/packages/utils/src/utils.ts @@ -99,7 +99,7 @@ export const isIntegrationStep = ( (Object.values(IntegrationStepType) as string[]).includes(step.type) export const isWebhookStep = (step: Step | PublicStep): step is WebhookStep => - step.type === IntegrationStepType.WEBHOOK + 'webhook' in step export const isBubbleStepType = (type: StepType): type is BubbleStepType => (Object.values(BubbleStepType) as string[]).includes(type) @@ -114,7 +114,11 @@ export const stepTypeHasOption = ( export const stepTypeHasWebhook = ( type: StepType -): type is IntegrationStepType.WEBHOOK => type === IntegrationStepType.WEBHOOK +): type is IntegrationStepType.WEBHOOK => + Object.values([ + IntegrationStepType.WEBHOOK, + IntegrationStepType.ZAPIER, + ] as string[]).includes(type) export const stepTypeHasItems = ( type: StepType