⚡ (webhook) Enable advanced config for Zapier and Make.com
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import { Text } from '@chakra-ui/react'
|
import { Text } from '@chakra-ui/react'
|
||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import { defaultWebhookAttributes, MakeComBlock, Webhook } from 'models'
|
import { MakeComBlock } from 'models'
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { byId, isNotDefined } from 'utils'
|
import { byId, isNotDefined } from 'utils'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -9,22 +8,9 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const MakeComContent = ({ block }: Props) => {
|
export const MakeComContent = ({ block }: Props) => {
|
||||||
const { webhooks, typebot, updateWebhook } = useTypebot()
|
const { webhooks } = useTypebot()
|
||||||
const webhook = webhooks.find(byId(block.webhookId))
|
const webhook = webhooks.find(byId(block.webhookId))
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!typebot) return
|
|
||||||
if (!webhook) {
|
|
||||||
const { webhookId } = block
|
|
||||||
const newWebhook = {
|
|
||||||
id: webhookId,
|
|
||||||
...defaultWebhookAttributes,
|
|
||||||
typebotId: typebot.id,
|
|
||||||
} as Webhook
|
|
||||||
updateWebhook(webhookId, newWebhook)
|
|
||||||
}
|
|
||||||
}, [block, typebot, updateWebhook, webhook])
|
|
||||||
|
|
||||||
if (isNotDefined(webhook?.body))
|
if (isNotDefined(webhook?.body))
|
||||||
return <Text color="gray.500">Configure...</Text>
|
return <Text color="gray.500">Configure...</Text>
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,31 +1,68 @@
|
|||||||
import {
|
import { Alert, AlertIcon, Button, Link, Stack, Text } from '@chakra-ui/react'
|
||||||
Alert,
|
|
||||||
AlertIcon,
|
|
||||||
Button,
|
|
||||||
Input,
|
|
||||||
Link,
|
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
} from '@chakra-ui/react'
|
|
||||||
import { ExternalLinkIcon } from '@/components/icons'
|
import { ExternalLinkIcon } from '@/components/icons'
|
||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import { MakeComBlock } from 'models'
|
import { MakeComBlock, Webhook, WebhookOptions } from 'models'
|
||||||
import React from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import { byId } from 'utils'
|
import { byId, env } from 'utils'
|
||||||
|
import { WebhookAdvancedConfigForm } from '../../webhook/components/WebhookAdvancedConfigForm'
|
||||||
|
import { useDebouncedCallback } from 'use-debounce'
|
||||||
|
|
||||||
|
const debounceWebhookTimeout = 2000
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
block: MakeComBlock
|
block: MakeComBlock
|
||||||
|
onOptionsChange: (options: WebhookOptions) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MakeComSettings = ({ block }: Props) => {
|
export const MakeComSettings = ({
|
||||||
const { webhooks } = useTypebot()
|
block: { webhookId, id: blockId, options },
|
||||||
const webhook = webhooks.find(byId(block.webhookId))
|
onOptionsChange,
|
||||||
|
}: Props) => {
|
||||||
|
const { webhooks, updateWebhook } = useTypebot()
|
||||||
|
const webhook = webhooks.find(byId(webhookId))
|
||||||
|
|
||||||
|
const [localWebhook, _setLocalWebhook] = useState(webhook)
|
||||||
|
const updateWebhookDebounced = useDebouncedCallback(
|
||||||
|
async (newLocalWebhook) => {
|
||||||
|
await updateWebhook(newLocalWebhook.id, newLocalWebhook)
|
||||||
|
},
|
||||||
|
env('E2E_TEST') === 'true' ? 0 : debounceWebhookTimeout
|
||||||
|
)
|
||||||
|
|
||||||
|
const setLocalWebhook = useCallback(
|
||||||
|
(newLocalWebhook: Webhook) => {
|
||||||
|
_setLocalWebhook(newLocalWebhook)
|
||||||
|
updateWebhookDebounced(newLocalWebhook)
|
||||||
|
},
|
||||||
|
[updateWebhookDebounced]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!localWebhook ||
|
||||||
|
localWebhook.url ||
|
||||||
|
!webhook?.url ||
|
||||||
|
webhook.url === localWebhook.url
|
||||||
|
)
|
||||||
|
return
|
||||||
|
setLocalWebhook({
|
||||||
|
...localWebhook,
|
||||||
|
url: webhook?.url,
|
||||||
|
})
|
||||||
|
}, [webhook, localWebhook, setLocalWebhook])
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
updateWebhookDebounced.flush()
|
||||||
|
},
|
||||||
|
[updateWebhookDebounced]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={4}>
|
<Stack spacing={4}>
|
||||||
<Alert status={webhook?.url ? 'success' : 'info'} rounded="md">
|
<Alert status={localWebhook?.url ? 'success' : 'info'} rounded="md">
|
||||||
<AlertIcon />
|
<AlertIcon />
|
||||||
{webhook?.url ? (
|
{localWebhook?.url ? (
|
||||||
<>Your scenario is correctly configured 🚀</>
|
<>Your scenario is correctly configured 🚀</>
|
||||||
) : (
|
) : (
|
||||||
<Stack>
|
<Stack>
|
||||||
@@ -41,7 +78,15 @@ export const MakeComSettings = ({ block }: Props) => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
{webhook?.url && <Input value={webhook?.url} isDisabled />}
|
{localWebhook && (
|
||||||
|
<WebhookAdvancedConfigForm
|
||||||
|
blockId={blockId}
|
||||||
|
webhook={localWebhook}
|
||||||
|
options={options}
|
||||||
|
onWebhookChange={setLocalWebhook}
|
||||||
|
onOptionsChange={onOptionsChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Text } from '@chakra-ui/react'
|
import { Text } from '@chakra-ui/react'
|
||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import { defaultWebhookAttributes, PabblyConnectBlock, Webhook } from 'models'
|
import { PabblyConnectBlock } from 'models'
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { byId, isNotDefined } from 'utils'
|
import { byId, isNotDefined } from 'utils'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -9,22 +8,9 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const PabblyConnectContent = ({ block }: Props) => {
|
export const PabblyConnectContent = ({ block }: Props) => {
|
||||||
const { webhooks, typebot, updateWebhook } = useTypebot()
|
const { webhooks } = useTypebot()
|
||||||
const webhook = webhooks.find(byId(block.webhookId))
|
const webhook = webhooks.find(byId(block.webhookId))
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!typebot) return
|
|
||||||
if (!webhook) {
|
|
||||||
const { webhookId } = block
|
|
||||||
const newWebhook = {
|
|
||||||
id: webhookId,
|
|
||||||
...defaultWebhookAttributes,
|
|
||||||
typebotId: typebot.id,
|
|
||||||
} as Webhook
|
|
||||||
updateWebhook(webhookId, newWebhook)
|
|
||||||
}
|
|
||||||
}, [block, typebot, updateWebhook, webhook])
|
|
||||||
|
|
||||||
if (isNotDefined(webhook?.body))
|
if (isNotDefined(webhook?.body))
|
||||||
return <Text color="gray.500">Configure...</Text>
|
return <Text color="gray.500">Configure...</Text>
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { Alert, AlertIcon, Button, Link, Stack, Text } from '@chakra-ui/react'
|
||||||
|
import { ExternalLinkIcon } from '@/components/icons'
|
||||||
|
import { useTypebot } from '@/features/editor'
|
||||||
|
import { PabblyConnectBlock, Webhook, WebhookOptions } from 'models'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { byId, env } from 'utils'
|
||||||
|
import { WebhookAdvancedConfigForm } from '../../webhook/components/WebhookAdvancedConfigForm'
|
||||||
|
import { useDebouncedCallback } from 'use-debounce'
|
||||||
|
import { TextInput } from '@/components/inputs'
|
||||||
|
|
||||||
|
const debounceWebhookTimeout = 2000
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
block: PabblyConnectBlock
|
||||||
|
onOptionsChange: (options: WebhookOptions) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PabblyConnectSettings = ({
|
||||||
|
block: { webhookId, id: blockId, options },
|
||||||
|
onOptionsChange,
|
||||||
|
}: Props) => {
|
||||||
|
const { webhooks, updateWebhook } = useTypebot()
|
||||||
|
|
||||||
|
const [localWebhook, _setLocalWebhook] = useState(
|
||||||
|
webhooks.find(byId(webhookId))
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateWebhookDebounced = useDebouncedCallback(
|
||||||
|
async (newLocalWebhook) => {
|
||||||
|
await updateWebhook(newLocalWebhook.id, newLocalWebhook)
|
||||||
|
},
|
||||||
|
env('E2E_TEST') === 'true' ? 0 : debounceWebhookTimeout
|
||||||
|
)
|
||||||
|
|
||||||
|
const setLocalWebhook = (newLocalWebhook: Webhook) => {
|
||||||
|
_setLocalWebhook(newLocalWebhook)
|
||||||
|
updateWebhookDebounced(newLocalWebhook)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
updateWebhookDebounced.flush()
|
||||||
|
},
|
||||||
|
[updateWebhookDebounced]
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleUrlChange = (url: string) =>
|
||||||
|
localWebhook &&
|
||||||
|
setLocalWebhook({
|
||||||
|
...localWebhook,
|
||||||
|
url,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack spacing={4}>
|
||||||
|
<Alert status={localWebhook?.url ? 'success' : 'info'} rounded="md">
|
||||||
|
<AlertIcon />
|
||||||
|
{localWebhook?.url ? (
|
||||||
|
<>Your scenario is correctly configured 🚀</>
|
||||||
|
) : (
|
||||||
|
<Stack>
|
||||||
|
<Text>Head up to Pabbly Connect to get the webhook URL:</Text>
|
||||||
|
<Button
|
||||||
|
as={Link}
|
||||||
|
href="https://www.pabbly.com/connect/integrations/typebot/"
|
||||||
|
isExternal
|
||||||
|
colorScheme="blue"
|
||||||
|
>
|
||||||
|
<Text mr="2">Pabbly.com</Text> <ExternalLinkIcon />
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Alert>
|
||||||
|
<TextInput
|
||||||
|
placeholder="Paste webhook URL..."
|
||||||
|
defaultValue={localWebhook?.url ?? ''}
|
||||||
|
onChange={handleUrlChange}
|
||||||
|
withVariableButton={false}
|
||||||
|
debounceTimeout={0}
|
||||||
|
/>
|
||||||
|
{localWebhook && (
|
||||||
|
<WebhookAdvancedConfigForm
|
||||||
|
blockId={blockId}
|
||||||
|
webhook={localWebhook}
|
||||||
|
options={options}
|
||||||
|
onWebhookChange={setLocalWebhook}
|
||||||
|
onOptionsChange={onOptionsChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
import { DropdownList } from '@/components/DropdownList'
|
||||||
|
import { CodeEditor } from '@/components/inputs/CodeEditor'
|
||||||
|
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
|
||||||
|
import { TableList, TableListItemProps } from '@/components/TableList'
|
||||||
|
import { useTypebot } from '@/features/editor'
|
||||||
|
import { useToast } from '@/hooks/useToast'
|
||||||
|
import {
|
||||||
|
Stack,
|
||||||
|
HStack,
|
||||||
|
Accordion,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionButton,
|
||||||
|
AccordionIcon,
|
||||||
|
AccordionPanel,
|
||||||
|
Button,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
|
import {
|
||||||
|
HttpMethod,
|
||||||
|
KeyValue,
|
||||||
|
VariableForTest,
|
||||||
|
ResponseVariableMapping,
|
||||||
|
WebhookOptions,
|
||||||
|
Webhook,
|
||||||
|
} from 'models'
|
||||||
|
import { useState, useMemo } from 'react'
|
||||||
|
import { executeWebhook } from '../queries/executeWebhookQuery'
|
||||||
|
import { convertVariablesForTestToVariables } from '../utils/convertVariablesForTestToVariables'
|
||||||
|
import { getDeepKeys } from '../utils/getDeepKeys'
|
||||||
|
import {
|
||||||
|
QueryParamsInputs,
|
||||||
|
HeadersInputs,
|
||||||
|
} from './WebhookSettings/KeyValueInputs'
|
||||||
|
import { DataVariableInputs } from './WebhookSettings/ResponseMappingInputs'
|
||||||
|
import { VariableForTestInputs } from './WebhookSettings/VariableForTestInputs'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
blockId: string
|
||||||
|
webhook: Webhook
|
||||||
|
options: WebhookOptions
|
||||||
|
onWebhookChange: (webhook: Webhook) => void
|
||||||
|
onOptionsChange: (options: WebhookOptions) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WebhookAdvancedConfigForm = ({
|
||||||
|
blockId,
|
||||||
|
webhook,
|
||||||
|
options,
|
||||||
|
onWebhookChange,
|
||||||
|
onOptionsChange,
|
||||||
|
}: Props) => {
|
||||||
|
const { typebot, save, updateWebhook } = useTypebot()
|
||||||
|
const [isTestResponseLoading, setIsTestResponseLoading] = useState(false)
|
||||||
|
const [testResponse, setTestResponse] = useState<string>()
|
||||||
|
const [responseKeys, setResponseKeys] = useState<string[]>([])
|
||||||
|
const { showToast } = useToast()
|
||||||
|
|
||||||
|
const handleMethodChange = (method: HttpMethod) =>
|
||||||
|
onWebhookChange({ ...webhook, method })
|
||||||
|
|
||||||
|
const handleQueryParamsChange = (queryParams: KeyValue[]) =>
|
||||||
|
onWebhookChange({ ...webhook, queryParams })
|
||||||
|
|
||||||
|
const handleHeadersChange = (headers: KeyValue[]) =>
|
||||||
|
onWebhookChange({ ...webhook, headers })
|
||||||
|
|
||||||
|
const handleBodyChange = (body: string) =>
|
||||||
|
onWebhookChange({ ...webhook, body })
|
||||||
|
|
||||||
|
const handleVariablesChange = (variablesForTest: VariableForTest[]) =>
|
||||||
|
onOptionsChange({ ...options, variablesForTest })
|
||||||
|
|
||||||
|
const handleResponseMappingChange = (
|
||||||
|
responseVariableMapping: ResponseVariableMapping[]
|
||||||
|
) => onOptionsChange({ ...options, responseVariableMapping })
|
||||||
|
|
||||||
|
const handleAdvancedConfigChange = (isAdvancedConfig: boolean) =>
|
||||||
|
onOptionsChange({ ...options, isAdvancedConfig })
|
||||||
|
|
||||||
|
const handleBodyFormStateChange = (isCustomBody: boolean) =>
|
||||||
|
onOptionsChange({ ...options, isCustomBody })
|
||||||
|
|
||||||
|
const handleTestRequestClick = async () => {
|
||||||
|
if (!typebot || !webhook) return
|
||||||
|
setIsTestResponseLoading(true)
|
||||||
|
await Promise.all([updateWebhook(webhook.id, webhook), save()])
|
||||||
|
const { data, error } = await executeWebhook(
|
||||||
|
typebot.id,
|
||||||
|
convertVariablesForTestToVariables(
|
||||||
|
options.variablesForTest,
|
||||||
|
typebot.variables
|
||||||
|
),
|
||||||
|
{ blockId }
|
||||||
|
)
|
||||||
|
if (error)
|
||||||
|
return showToast({ title: error.name, description: error.message })
|
||||||
|
setTestResponse(JSON.stringify(data, undefined, 2))
|
||||||
|
setResponseKeys(getDeepKeys(data))
|
||||||
|
setIsTestResponseLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResponseMappingInputs = useMemo(
|
||||||
|
() =>
|
||||||
|
function Component(props: TableListItemProps<ResponseVariableMapping>) {
|
||||||
|
return <DataVariableInputs {...props} dataItems={responseKeys} />
|
||||||
|
},
|
||||||
|
[responseKeys]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SwitchWithLabel
|
||||||
|
label="Advanced configuration"
|
||||||
|
initialValue={options.isAdvancedConfig ?? true}
|
||||||
|
onCheckChange={handleAdvancedConfigChange}
|
||||||
|
/>
|
||||||
|
{(options.isAdvancedConfig ?? true) && (
|
||||||
|
<>
|
||||||
|
<HStack justify="space-between">
|
||||||
|
<Text>Method:</Text>
|
||||||
|
<DropdownList<HttpMethod>
|
||||||
|
currentItem={webhook.method as HttpMethod}
|
||||||
|
onItemSelect={handleMethodChange}
|
||||||
|
items={Object.values(HttpMethod)}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
<Accordion allowMultiple>
|
||||||
|
<AccordionItem>
|
||||||
|
<AccordionButton justifyContent="space-between">
|
||||||
|
Query params
|
||||||
|
<AccordionIcon />
|
||||||
|
</AccordionButton>
|
||||||
|
<AccordionPanel py={4} as={Stack} spacing="6">
|
||||||
|
<TableList<KeyValue>
|
||||||
|
initialItems={webhook.queryParams}
|
||||||
|
onItemsChange={handleQueryParamsChange}
|
||||||
|
Item={QueryParamsInputs}
|
||||||
|
addLabel="Add a param"
|
||||||
|
debounceTimeout={0}
|
||||||
|
/>
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem>
|
||||||
|
<AccordionButton justifyContent="space-between">
|
||||||
|
Headers
|
||||||
|
<AccordionIcon />
|
||||||
|
</AccordionButton>
|
||||||
|
<AccordionPanel py={4} as={Stack} spacing="6">
|
||||||
|
<TableList<KeyValue>
|
||||||
|
initialItems={webhook.headers}
|
||||||
|
onItemsChange={handleHeadersChange}
|
||||||
|
Item={HeadersInputs}
|
||||||
|
addLabel="Add a value"
|
||||||
|
debounceTimeout={0}
|
||||||
|
/>
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem>
|
||||||
|
<AccordionButton justifyContent="space-between">
|
||||||
|
Body
|
||||||
|
<AccordionIcon />
|
||||||
|
</AccordionButton>
|
||||||
|
<AccordionPanel py={4} as={Stack} spacing="6">
|
||||||
|
<SwitchWithLabel
|
||||||
|
label="Custom body"
|
||||||
|
initialValue={options.isCustomBody ?? true}
|
||||||
|
onCheckChange={handleBodyFormStateChange}
|
||||||
|
/>
|
||||||
|
{(options.isCustomBody ?? true) && (
|
||||||
|
<CodeEditor
|
||||||
|
defaultValue={webhook.body ?? ''}
|
||||||
|
lang="json"
|
||||||
|
onChange={handleBodyChange}
|
||||||
|
debounceTimeout={0}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem>
|
||||||
|
<AccordionButton justifyContent="space-between">
|
||||||
|
Variable values for test
|
||||||
|
<AccordionIcon />
|
||||||
|
</AccordionButton>
|
||||||
|
<AccordionPanel py={4} as={Stack} spacing="6">
|
||||||
|
<TableList<VariableForTest>
|
||||||
|
initialItems={
|
||||||
|
options?.variablesForTest ?? { byId: {}, allIds: [] }
|
||||||
|
}
|
||||||
|
onItemsChange={handleVariablesChange}
|
||||||
|
Item={VariableForTestInputs}
|
||||||
|
addLabel="Add an entry"
|
||||||
|
debounceTimeout={0}
|
||||||
|
/>
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{webhook.url && (
|
||||||
|
<Button
|
||||||
|
onClick={handleTestRequestClick}
|
||||||
|
colorScheme="blue"
|
||||||
|
isLoading={isTestResponseLoading}
|
||||||
|
>
|
||||||
|
Test the request
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{testResponse && (
|
||||||
|
<CodeEditor isReadOnly lang="json" value={testResponse} />
|
||||||
|
)}
|
||||||
|
{(testResponse || options?.responseVariableMapping.length > 0) && (
|
||||||
|
<Accordion allowMultiple>
|
||||||
|
<AccordionItem>
|
||||||
|
<AccordionButton justifyContent="space-between">
|
||||||
|
Save in variables
|
||||||
|
<AccordionIcon />
|
||||||
|
</AccordionButton>
|
||||||
|
<AccordionPanel py={4} as={Stack} spacing="6">
|
||||||
|
<TableList<ResponseVariableMapping>
|
||||||
|
initialItems={options.responseVariableMapping}
|
||||||
|
onItemsChange={handleResponseMappingChange}
|
||||||
|
Item={ResponseMappingInputs}
|
||||||
|
addLabel="Add an entry"
|
||||||
|
debounceTimeout={0}
|
||||||
|
/>
|
||||||
|
</AccordionPanel>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,70 +1,24 @@
|
|||||||
import React, { useEffect, useMemo, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import {
|
import { Spinner, Stack } from '@chakra-ui/react'
|
||||||
Accordion,
|
|
||||||
AccordionButton,
|
|
||||||
AccordionIcon,
|
|
||||||
AccordionItem,
|
|
||||||
AccordionPanel,
|
|
||||||
Button,
|
|
||||||
HStack,
|
|
||||||
Spinner,
|
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
Alert,
|
|
||||||
AlertIcon,
|
|
||||||
Link,
|
|
||||||
} from '@chakra-ui/react'
|
|
||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import {
|
import { WebhookOptions, Webhook, WebhookBlock } from 'models'
|
||||||
HttpMethod,
|
|
||||||
KeyValue,
|
|
||||||
WebhookOptions,
|
|
||||||
VariableForTest,
|
|
||||||
ResponseVariableMapping,
|
|
||||||
WebhookBlock,
|
|
||||||
MakeComBlock,
|
|
||||||
PabblyConnectBlock,
|
|
||||||
Webhook,
|
|
||||||
} from 'models'
|
|
||||||
import { DropdownList } from '@/components/DropdownList'
|
|
||||||
import { CodeEditor } from '@/components/inputs/CodeEditor'
|
|
||||||
import { HeadersInputs, QueryParamsInputs } from './KeyValueInputs'
|
|
||||||
import { VariableForTestInputs } from './VariableForTestInputs'
|
|
||||||
import { DataVariableInputs } from './ResponseMappingInputs'
|
|
||||||
import { byId, env } from 'utils'
|
import { byId, env } from 'utils'
|
||||||
import { ExternalLinkIcon } from '@/components/icons'
|
|
||||||
import { useToast } from '@/hooks/useToast'
|
|
||||||
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
|
|
||||||
import { TableListItemProps, TableList } from '@/components/TableList'
|
|
||||||
import { executeWebhook } from '../../queries/executeWebhookQuery'
|
|
||||||
import { getDeepKeys } from '../../utils/getDeepKeys'
|
|
||||||
import { TextInput } from '@/components/inputs'
|
import { TextInput } from '@/components/inputs'
|
||||||
import { convertVariablesForTestToVariables } from '../../utils/convertVariablesForTestToVariables'
|
|
||||||
import { useDebouncedCallback } from 'use-debounce'
|
import { useDebouncedCallback } from 'use-debounce'
|
||||||
|
import { WebhookAdvancedConfigForm } from '../WebhookAdvancedConfigForm'
|
||||||
|
|
||||||
const debounceWebhookTimeout = 2000
|
const debounceWebhookTimeout = 2000
|
||||||
|
|
||||||
type Provider = {
|
|
||||||
name: 'Pabbly Connect'
|
|
||||||
url: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
block: WebhookBlock | MakeComBlock | PabblyConnectBlock
|
block: WebhookBlock
|
||||||
onOptionsChange: (options: WebhookOptions) => void
|
onOptionsChange: (options: WebhookOptions) => void
|
||||||
provider?: Provider
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WebhookSettings = ({
|
export const WebhookSettings = ({
|
||||||
block: { options, id: blockId, webhookId },
|
block: { webhookId, id: blockId, options },
|
||||||
onOptionsChange,
|
onOptionsChange,
|
||||||
provider,
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { typebot, save, webhooks, updateWebhook } = useTypebot()
|
const { webhooks, updateWebhook } = useTypebot()
|
||||||
const [isTestResponseLoading, setIsTestResponseLoading] = useState(false)
|
|
||||||
const [testResponse, setTestResponse] = useState<string>()
|
|
||||||
const [responseKeys, setResponseKeys] = useState<string[]>([])
|
|
||||||
const { showToast } = useToast()
|
|
||||||
const [localWebhook, _setLocalWebhook] = useState(
|
const [localWebhook, _setLocalWebhook] = useState(
|
||||||
webhooks.find(byId(webhookId))
|
webhooks.find(byId(webhookId))
|
||||||
)
|
)
|
||||||
@@ -90,200 +44,23 @@ export const WebhookSettings = ({
|
|||||||
const handleUrlChange = (url?: string) =>
|
const handleUrlChange = (url?: string) =>
|
||||||
localWebhook && setLocalWebhook({ ...localWebhook, url: url ?? null })
|
localWebhook && setLocalWebhook({ ...localWebhook, url: url ?? null })
|
||||||
|
|
||||||
const handleMethodChange = (method: HttpMethod) =>
|
|
||||||
localWebhook && setLocalWebhook({ ...localWebhook, method })
|
|
||||||
|
|
||||||
const handleQueryParamsChange = (queryParams: KeyValue[]) =>
|
|
||||||
localWebhook && setLocalWebhook({ ...localWebhook, queryParams })
|
|
||||||
|
|
||||||
const handleHeadersChange = (headers: KeyValue[]) =>
|
|
||||||
localWebhook && setLocalWebhook({ ...localWebhook, headers })
|
|
||||||
|
|
||||||
const handleBodyChange = (body: string) =>
|
|
||||||
localWebhook && setLocalWebhook({ ...localWebhook, body })
|
|
||||||
|
|
||||||
const handleVariablesChange = (variablesForTest: VariableForTest[]) =>
|
|
||||||
onOptionsChange({ ...options, variablesForTest })
|
|
||||||
|
|
||||||
const handleResponseMappingChange = (
|
|
||||||
responseVariableMapping: ResponseVariableMapping[]
|
|
||||||
) => onOptionsChange({ ...options, responseVariableMapping })
|
|
||||||
|
|
||||||
const handleAdvancedConfigChange = (isAdvancedConfig: boolean) =>
|
|
||||||
onOptionsChange({ ...options, isAdvancedConfig })
|
|
||||||
|
|
||||||
const handleBodyFormStateChange = (isCustomBody: boolean) =>
|
|
||||||
onOptionsChange({ ...options, isCustomBody })
|
|
||||||
|
|
||||||
const handleTestRequestClick = async () => {
|
|
||||||
if (!typebot || !localWebhook) return
|
|
||||||
setIsTestResponseLoading(true)
|
|
||||||
await Promise.all([updateWebhook(localWebhook.id, localWebhook), save()])
|
|
||||||
const { data, error } = await executeWebhook(
|
|
||||||
typebot.id,
|
|
||||||
convertVariablesForTestToVariables(
|
|
||||||
options.variablesForTest,
|
|
||||||
typebot.variables
|
|
||||||
),
|
|
||||||
{ blockId }
|
|
||||||
)
|
|
||||||
if (error)
|
|
||||||
return showToast({ title: error.name, description: error.message })
|
|
||||||
setTestResponse(JSON.stringify(data, undefined, 2))
|
|
||||||
setResponseKeys(getDeepKeys(data))
|
|
||||||
setIsTestResponseLoading(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const ResponseMappingInputs = useMemo(
|
|
||||||
() =>
|
|
||||||
function Component(props: TableListItemProps<ResponseVariableMapping>) {
|
|
||||||
return <DataVariableInputs {...props} dataItems={responseKeys} />
|
|
||||||
},
|
|
||||||
[responseKeys]
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!localWebhook) return <Spinner />
|
if (!localWebhook) return <Spinner />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={4}>
|
<Stack spacing={4}>
|
||||||
{provider && (
|
|
||||||
<Alert status={'info'} rounded="md">
|
|
||||||
<AlertIcon />
|
|
||||||
<Stack>
|
|
||||||
<Text>Head up to {provider.name} to configure this block:</Text>
|
|
||||||
<Button as={Link} href={provider.url} isExternal colorScheme="blue">
|
|
||||||
<Text mr="2">{provider.name}</Text> <ExternalLinkIcon />
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Paste webhook URL..."
|
placeholder="Paste webhook URL..."
|
||||||
defaultValue={localWebhook.url ?? ''}
|
defaultValue={localWebhook.url ?? ''}
|
||||||
onChange={handleUrlChange}
|
onChange={handleUrlChange}
|
||||||
debounceTimeout={0}
|
debounceTimeout={0}
|
||||||
withVariableButton={!provider}
|
|
||||||
/>
|
/>
|
||||||
<SwitchWithLabel
|
<WebhookAdvancedConfigForm
|
||||||
label="Advanced configuration"
|
blockId={blockId}
|
||||||
initialValue={options.isAdvancedConfig ?? true}
|
webhook={localWebhook}
|
||||||
onCheckChange={handleAdvancedConfigChange}
|
options={options}
|
||||||
|
onWebhookChange={setLocalWebhook}
|
||||||
|
onOptionsChange={onOptionsChange}
|
||||||
/>
|
/>
|
||||||
{(options.isAdvancedConfig ?? true) && (
|
|
||||||
<Stack>
|
|
||||||
<HStack justify="space-between">
|
|
||||||
<Text>Method:</Text>
|
|
||||||
<DropdownList<HttpMethod>
|
|
||||||
currentItem={localWebhook.method as HttpMethod}
|
|
||||||
onItemSelect={handleMethodChange}
|
|
||||||
items={Object.values(HttpMethod)}
|
|
||||||
/>
|
|
||||||
</HStack>
|
|
||||||
<Accordion allowMultiple>
|
|
||||||
<AccordionItem>
|
|
||||||
<AccordionButton justifyContent="space-between">
|
|
||||||
Query params
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
<AccordionPanel py={4} as={Stack} spacing="6">
|
|
||||||
<TableList<KeyValue>
|
|
||||||
initialItems={localWebhook.queryParams}
|
|
||||||
onItemsChange={handleQueryParamsChange}
|
|
||||||
Item={QueryParamsInputs}
|
|
||||||
addLabel="Add a param"
|
|
||||||
debounceTimeout={0}
|
|
||||||
/>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<AccordionButton justifyContent="space-between">
|
|
||||||
Headers
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
<AccordionPanel py={4} as={Stack} spacing="6">
|
|
||||||
<TableList<KeyValue>
|
|
||||||
initialItems={localWebhook.headers}
|
|
||||||
onItemsChange={handleHeadersChange}
|
|
||||||
Item={HeadersInputs}
|
|
||||||
addLabel="Add a value"
|
|
||||||
debounceTimeout={0}
|
|
||||||
/>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<AccordionButton justifyContent="space-between">
|
|
||||||
Body
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
<AccordionPanel py={4} as={Stack} spacing="6">
|
|
||||||
<SwitchWithLabel
|
|
||||||
label="Custom body"
|
|
||||||
initialValue={options.isCustomBody ?? true}
|
|
||||||
onCheckChange={handleBodyFormStateChange}
|
|
||||||
/>
|
|
||||||
{(options.isCustomBody ?? true) && (
|
|
||||||
<CodeEditor
|
|
||||||
defaultValue={localWebhook.body ?? ''}
|
|
||||||
lang="json"
|
|
||||||
onChange={handleBodyChange}
|
|
||||||
debounceTimeout={0}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<AccordionButton justifyContent="space-between">
|
|
||||||
Variable values for test
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
<AccordionPanel py={4} as={Stack} spacing="6">
|
|
||||||
<TableList<VariableForTest>
|
|
||||||
initialItems={
|
|
||||||
options?.variablesForTest ?? { byId: {}, allIds: [] }
|
|
||||||
}
|
|
||||||
onItemsChange={handleVariablesChange}
|
|
||||||
Item={VariableForTestInputs}
|
|
||||||
addLabel="Add an entry"
|
|
||||||
debounceTimeout={0}
|
|
||||||
/>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
<Stack>
|
|
||||||
{localWebhook.url && (
|
|
||||||
<Button
|
|
||||||
onClick={handleTestRequestClick}
|
|
||||||
colorScheme="blue"
|
|
||||||
isLoading={isTestResponseLoading}
|
|
||||||
>
|
|
||||||
Test the request
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{testResponse && (
|
|
||||||
<CodeEditor isReadOnly lang="json" value={testResponse} />
|
|
||||||
)}
|
|
||||||
{(testResponse || options?.responseVariableMapping.length > 0) && (
|
|
||||||
<Accordion allowMultiple>
|
|
||||||
<AccordionItem>
|
|
||||||
<AccordionButton justifyContent="space-between">
|
|
||||||
Save in variables
|
|
||||||
<AccordionIcon />
|
|
||||||
</AccordionButton>
|
|
||||||
<AccordionPanel py={4} as={Stack} spacing="6">
|
|
||||||
<TableList<ResponseVariableMapping>
|
|
||||||
initialItems={options.responseVariableMapping}
|
|
||||||
onItemsChange={handleResponseMappingChange}
|
|
||||||
Item={ResponseMappingInputs}
|
|
||||||
addLabel="Add an entry"
|
|
||||||
debounceTimeout={0}
|
|
||||||
/>
|
|
||||||
</AccordionPanel>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Text } from '@chakra-ui/react'
|
import { Text } from '@chakra-ui/react'
|
||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import { defaultWebhookAttributes, Webhook, ZapierBlock } from 'models'
|
import { ZapierBlock } from 'models'
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { byId, isNotDefined } from 'utils'
|
import { byId, isNotDefined } from 'utils'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -9,22 +8,9 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ZapierContent = ({ block }: Props) => {
|
export const ZapierContent = ({ block }: Props) => {
|
||||||
const { webhooks, typebot, updateWebhook } = useTypebot()
|
const { webhooks } = useTypebot()
|
||||||
const webhook = webhooks.find(byId(block.webhookId))
|
const webhook = webhooks.find(byId(block.webhookId))
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!typebot) return
|
|
||||||
if (!webhook) {
|
|
||||||
const { webhookId } = block
|
|
||||||
const newWebhook = {
|
|
||||||
id: webhookId,
|
|
||||||
...defaultWebhookAttributes,
|
|
||||||
typebotId: typebot.id,
|
|
||||||
} as Webhook
|
|
||||||
updateWebhook(webhookId, newWebhook)
|
|
||||||
}
|
|
||||||
}, [block, typebot, updateWebhook, webhook])
|
|
||||||
|
|
||||||
if (isNotDefined(webhook?.body))
|
if (isNotDefined(webhook?.body))
|
||||||
return <Text color="gray.500">Configure...</Text>
|
return <Text color="gray.500">Configure...</Text>
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,31 +1,69 @@
|
|||||||
import {
|
import { Alert, AlertIcon, Button, Link, Stack, Text } from '@chakra-ui/react'
|
||||||
Alert,
|
|
||||||
AlertIcon,
|
|
||||||
Button,
|
|
||||||
Input,
|
|
||||||
Link,
|
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
} from '@chakra-ui/react'
|
|
||||||
import { ExternalLinkIcon } from '@/components/icons'
|
import { ExternalLinkIcon } from '@/components/icons'
|
||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import { ZapierBlock } from 'models'
|
import { Webhook, WebhookOptions, ZapierBlock } from 'models'
|
||||||
import React from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import { byId } from 'utils'
|
import { byId, env } from 'utils'
|
||||||
|
import { WebhookAdvancedConfigForm } from '../../webhook/components/WebhookAdvancedConfigForm'
|
||||||
|
import { useDebouncedCallback } from 'use-debounce'
|
||||||
|
|
||||||
|
const debounceWebhookTimeout = 2000
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
block: ZapierBlock
|
block: ZapierBlock
|
||||||
|
onOptionsChange: (options: WebhookOptions) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ZapierSettings = ({ block }: Props) => {
|
export const ZapierSettings = ({
|
||||||
const { webhooks } = useTypebot()
|
block: { webhookId, id: blockId, options },
|
||||||
const webhook = webhooks.find(byId(block.webhookId))
|
onOptionsChange,
|
||||||
|
}: Props) => {
|
||||||
|
const { webhooks, updateWebhook } = useTypebot()
|
||||||
|
const webhook = webhooks.find(byId(webhookId))
|
||||||
|
|
||||||
|
const [localWebhook, _setLocalWebhook] = useState(webhook)
|
||||||
|
|
||||||
|
const updateWebhookDebounced = useDebouncedCallback(
|
||||||
|
async (newLocalWebhook) => {
|
||||||
|
await updateWebhook(newLocalWebhook.id, newLocalWebhook)
|
||||||
|
},
|
||||||
|
env('E2E_TEST') === 'true' ? 0 : debounceWebhookTimeout
|
||||||
|
)
|
||||||
|
|
||||||
|
const setLocalWebhook = useCallback(
|
||||||
|
(newLocalWebhook: Webhook) => {
|
||||||
|
_setLocalWebhook(newLocalWebhook)
|
||||||
|
updateWebhookDebounced(newLocalWebhook)
|
||||||
|
},
|
||||||
|
[updateWebhookDebounced]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
!localWebhook ||
|
||||||
|
localWebhook.url ||
|
||||||
|
!webhook?.url ||
|
||||||
|
webhook.url === localWebhook.url
|
||||||
|
)
|
||||||
|
return
|
||||||
|
setLocalWebhook({
|
||||||
|
...localWebhook,
|
||||||
|
url: webhook?.url,
|
||||||
|
})
|
||||||
|
}, [webhook, localWebhook, setLocalWebhook])
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
updateWebhookDebounced.flush()
|
||||||
|
},
|
||||||
|
[updateWebhookDebounced]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={4}>
|
<Stack spacing={4}>
|
||||||
<Alert status={webhook?.url ? 'success' : 'info'} rounded="md">
|
<Alert status={localWebhook?.url ? 'success' : 'info'} rounded="md">
|
||||||
<AlertIcon />
|
<AlertIcon />
|
||||||
{webhook?.url ? (
|
{localWebhook?.url ? (
|
||||||
<>Your zap is correctly configured 🚀</>
|
<>Your zap is correctly configured 🚀</>
|
||||||
) : (
|
) : (
|
||||||
<Stack>
|
<Stack>
|
||||||
@@ -41,7 +79,15 @@ export const ZapierSettings = ({ block }: Props) => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Alert>
|
</Alert>
|
||||||
{webhook?.url && <Input value={webhook?.url} isDisabled />}
|
{localWebhook && (
|
||||||
|
<WebhookAdvancedConfigForm
|
||||||
|
blockId={blockId}
|
||||||
|
webhook={localWebhook}
|
||||||
|
options={options}
|
||||||
|
onWebhookChange={setLocalWebhook}
|
||||||
|
onOptionsChange={onOptionsChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,12 @@ import { SetVariableSettings } from '@/features/blocks/logic/setVariable'
|
|||||||
import { TypebotLinkForm } from '@/features/blocks/logic/typebotLink'
|
import { TypebotLinkForm } from '@/features/blocks/logic/typebotLink'
|
||||||
import { ButtonsBlockSettings } from '@/features/blocks/inputs/buttons'
|
import { ButtonsBlockSettings } from '@/features/blocks/inputs/buttons'
|
||||||
import { ChatwootSettingsForm } from '@/features/blocks/integrations/chatwoot'
|
import { ChatwootSettingsForm } from '@/features/blocks/integrations/chatwoot'
|
||||||
import { MakeComSettings } from '@/features/blocks/integrations/makeCom'
|
|
||||||
import { HelpDocButton } from './HelpDocButton'
|
import { HelpDocButton } from './HelpDocButton'
|
||||||
import { WaitSettings } from '@/features/blocks/logic/wait/components/WaitSettings'
|
import { WaitSettings } from '@/features/blocks/logic/wait/components/WaitSettings'
|
||||||
import { ScriptSettings } from '@/features/blocks/logic/script/components/ScriptSettings'
|
import { ScriptSettings } from '@/features/blocks/logic/script/components/ScriptSettings'
|
||||||
import { JumpSettings } from '@/features/blocks/logic/jump/components/JumpSettings'
|
import { JumpSettings } from '@/features/blocks/logic/jump/components/JumpSettings'
|
||||||
|
import { MakeComSettings } from '@/features/blocks/integrations/makeCom/components/MakeComSettings'
|
||||||
|
import { PabblyConnectSettings } from '@/features/blocks/integrations/pabbly/components/PabblyConnectSettings'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
block: BlockWithOptions
|
block: BlockWithOptions
|
||||||
@@ -248,20 +249,20 @@ export const BlockSettings = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
case IntegrationBlockType.ZAPIER: {
|
case IntegrationBlockType.ZAPIER: {
|
||||||
return <ZapierSettings block={block} />
|
return (
|
||||||
|
<ZapierSettings block={block} onOptionsChange={handleOptionsChange} />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
case IntegrationBlockType.MAKE_COM: {
|
case IntegrationBlockType.MAKE_COM: {
|
||||||
return <MakeComSettings block={block} />
|
return (
|
||||||
|
<MakeComSettings block={block} onOptionsChange={handleOptionsChange} />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
case IntegrationBlockType.PABBLY_CONNECT: {
|
case IntegrationBlockType.PABBLY_CONNECT: {
|
||||||
return (
|
return (
|
||||||
<WebhookSettings
|
<PabblyConnectSettings
|
||||||
block={block}
|
block={block}
|
||||||
onOptionsChange={handleOptionsChange}
|
onOptionsChange={handleOptionsChange}
|
||||||
provider={{
|
|
||||||
name: 'Pabbly Connect',
|
|
||||||
url: 'https://www.pabbly.com/connect/integrations/typebot/',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user