✨ Add new Jump block
Also improve Select input with a clear button Closes #186
This commit is contained in:
@ -545,3 +545,10 @@ export const PackageIcon = (props: IconProps) => (
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"></line>
|
||||
</Icon>
|
||||
)
|
||||
|
||||
export const CloseIcon = (props: IconProps) => (
|
||||
<Icon viewBox="0 0 24 24" {...featherIconsBaseProps} {...props}>
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</Icon>
|
||||
)
|
||||
|
@ -12,14 +12,16 @@ import {
|
||||
InputRightElement,
|
||||
Text,
|
||||
Box,
|
||||
IconButton,
|
||||
HStack,
|
||||
} from '@chakra-ui/react'
|
||||
import { useState, useRef, ChangeEvent } from 'react'
|
||||
import { isDefined } from 'utils'
|
||||
import { useOutsideClick } from '@/hooks/useOutsideClick'
|
||||
import { useParentModal } from '@/features/graph/providers/ParentModalProvider'
|
||||
import { ChevronDownIcon } from '../icons'
|
||||
import { ChevronDownIcon, CloseIcon } from '../icons'
|
||||
|
||||
const dropdownCloseAnimationDuration = 200
|
||||
const dropdownCloseAnimationDuration = 300
|
||||
|
||||
type Item = string | { icon?: JSX.Element; label: string; value: string }
|
||||
|
||||
@ -27,7 +29,7 @@ type Props = {
|
||||
selectedItem?: string
|
||||
items: Item[]
|
||||
placeholder?: string
|
||||
onSelect?: (value: string) => void
|
||||
onSelect?: (value: string | undefined) => void
|
||||
}
|
||||
|
||||
export const Select = ({
|
||||
@ -119,6 +121,14 @@ export const Select = ({
|
||||
}
|
||||
}
|
||||
|
||||
const clearSelection = (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
setInputValue('')
|
||||
onSelect?.(undefined)
|
||||
setKeyboardFocusIndex(undefined)
|
||||
closeDropwdown()
|
||||
}
|
||||
|
||||
const resetIsTouched = () => {
|
||||
setTimeout(() => {
|
||||
setIsTouched(false)
|
||||
@ -136,7 +146,15 @@ export const Select = ({
|
||||
>
|
||||
<PopoverAnchor>
|
||||
<InputGroup>
|
||||
<Box pos="absolute" py={2} pl={4} pr={6}>
|
||||
<Box
|
||||
pos="absolute"
|
||||
pb={2}
|
||||
// We need absolute positioning the overlay match the underlying input
|
||||
pt="8.5px"
|
||||
pl="17px"
|
||||
pr={selectedItem ? 16 : 8}
|
||||
w="full"
|
||||
>
|
||||
{!isTouched && (
|
||||
<Text noOfLines={1} data-testid="selected-item-label">
|
||||
{inputValue}
|
||||
@ -156,10 +174,26 @@ export const Select = ({
|
||||
onChange={handleInputChange}
|
||||
onFocus={onOpen}
|
||||
onKeyDown={handleKeyUp}
|
||||
pr={selectedItem ? 16 : undefined}
|
||||
/>
|
||||
|
||||
<InputRightElement pointerEvents="none" cursor="pointer">
|
||||
<ChevronDownIcon />
|
||||
<InputRightElement
|
||||
width={selectedItem ? '5rem' : undefined}
|
||||
pointerEvents="none"
|
||||
>
|
||||
<HStack>
|
||||
{selectedItem && (
|
||||
<IconButton
|
||||
onClick={clearSelection}
|
||||
icon={<CloseIcon />}
|
||||
aria-label={'Clear'}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
pointerEvents="all"
|
||||
/>
|
||||
)}
|
||||
<ChevronDownIcon />
|
||||
</HStack>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</PopoverAnchor>
|
||||
|
@ -51,9 +51,9 @@ export const GoogleSheetsSettingsBody = ({
|
||||
)
|
||||
const handleCredentialsIdChange = (credentialsId?: string) =>
|
||||
onOptionsChange({ ...omit(options, 'credentialsId'), credentialsId })
|
||||
const handleSpreadsheetIdChange = (spreadsheetId: string) =>
|
||||
const handleSpreadsheetIdChange = (spreadsheetId: string | undefined) =>
|
||||
onOptionsChange({ ...options, spreadsheetId })
|
||||
const handleSheetIdChange = (sheetId: string) =>
|
||||
const handleSheetIdChange = (sheetId: string | undefined) =>
|
||||
onOptionsChange({ ...options, sheetId })
|
||||
|
||||
const handleActionChange = (action: GoogleSheetsAction) => {
|
||||
|
@ -7,7 +7,7 @@ type Props = {
|
||||
sheets: Sheet[]
|
||||
isLoading: boolean
|
||||
sheetId?: string
|
||||
onSelectSheetId: (id: string) => void
|
||||
onSelectSheetId: (id: string | undefined) => void
|
||||
}
|
||||
|
||||
export const SheetsDropdown = ({
|
||||
|
@ -5,7 +5,7 @@ import { useSpreadsheets } from '../../hooks/useSpreadsheets'
|
||||
type Props = {
|
||||
credentialsId: string
|
||||
spreadsheetId?: string
|
||||
onSelectSpreadsheetId: (id: string) => void
|
||||
onSelectSpreadsheetId: (id: string | undefined) => void
|
||||
}
|
||||
|
||||
export const SpreadsheetsDropdown = ({
|
||||
|
@ -0,0 +1,10 @@
|
||||
import { featherIconsBaseProps } from '@/components/icons'
|
||||
import { Icon, IconProps } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
|
||||
export const JumpIcon = (props: IconProps) => (
|
||||
<Icon viewBox="0 0 24 24" {...featherIconsBaseProps} {...props}>
|
||||
<polygon points="13 19 22 12 13 5 13 19"></polygon>
|
||||
<polygon points="2 19 11 12 2 5 2 19"></polygon>
|
||||
</Icon>
|
||||
)
|
@ -0,0 +1,26 @@
|
||||
import React from 'react'
|
||||
import { Tag, Text } from '@chakra-ui/react'
|
||||
import { useTypebot } from '@/features/editor'
|
||||
import { byId, isDefined } from 'utils'
|
||||
import { JumpBlock } from 'models/features/blocks/logic/jump'
|
||||
|
||||
type Props = {
|
||||
options: JumpBlock['options']
|
||||
}
|
||||
|
||||
export const JumpNodeBody = ({ options }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const selectedGroup = typebot?.groups.find(byId(options.groupId))
|
||||
const blockIndex = selectedGroup?.blocks.findIndex(byId(options.blockId))
|
||||
if (!selectedGroup) return <Text color="gray.500">Configure...</Text>
|
||||
return (
|
||||
<Text>
|
||||
Jump to <Tag colorScheme="blue">{selectedGroup.title}</Tag>{' '}
|
||||
{isDefined(blockIndex) && blockIndex >= 0 ? (
|
||||
<>
|
||||
at block <Tag colorScheme="blue">{blockIndex + 1}</Tag>
|
||||
</>
|
||||
) : null}
|
||||
</Text>
|
||||
)
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import { Select } from '@/components/inputs/Select'
|
||||
import { useTypebot } from '@/features/editor'
|
||||
import { Stack } from '@chakra-ui/react'
|
||||
import { JumpBlock } from 'models/features/blocks/logic/jump'
|
||||
import React from 'react'
|
||||
import { byId } from 'utils'
|
||||
|
||||
type Props = {
|
||||
groupId: string
|
||||
options: JumpBlock['options']
|
||||
onOptionsChange: (options: JumpBlock['options']) => void
|
||||
}
|
||||
|
||||
export const JumpSettings = ({ groupId, options, onOptionsChange }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
|
||||
const handleGroupIdChange = (groupId?: string) =>
|
||||
onOptionsChange({ ...options, groupId })
|
||||
|
||||
const handleBlockIdChange = (blockId?: string) =>
|
||||
onOptionsChange({ ...options, blockId })
|
||||
|
||||
const currentGroupId = typebot?.groups.find(byId(groupId))?.id
|
||||
|
||||
const selectedGroup = typebot?.groups.find(byId(options.groupId))
|
||||
|
||||
if (!typebot) return null
|
||||
|
||||
return (
|
||||
<Stack spacing={4}>
|
||||
<Select
|
||||
items={typebot.groups
|
||||
.filter((group) => group.id !== currentGroupId)
|
||||
.map((group) => ({
|
||||
label: group.title,
|
||||
value: group.id,
|
||||
}))}
|
||||
selectedItem={selectedGroup?.id}
|
||||
onSelect={handleGroupIdChange}
|
||||
placeholder="Select a group"
|
||||
/>
|
||||
{selectedGroup && selectedGroup.blocks.length > 1 && (
|
||||
<Select
|
||||
selectedItem={options.blockId}
|
||||
items={selectedGroup.blocks.map((block, index) => ({
|
||||
label: `Block #${(index + 1).toString()}`,
|
||||
value: block.id,
|
||||
}))}
|
||||
onSelect={handleBlockIdChange}
|
||||
placeholder="Select a block"
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
)
|
||||
}
|
28
apps/builder/src/features/blocks/logic/jump/jump.spec.ts
Normal file
28
apps/builder/src/features/blocks/logic/jump/jump.spec.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import test, { expect } from '@playwright/test'
|
||||
import { importTypebotInDatabase } from 'utils/playwright/databaseActions'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { getTestAsset } from '@/test/utils/playwright'
|
||||
|
||||
test('should work as expected', async ({ page }) => {
|
||||
const typebotId = createId()
|
||||
await importTypebotInDatabase(getTestAsset('typebots/logic/jump.json'), {
|
||||
id: typebotId,
|
||||
})
|
||||
|
||||
await page.goto(`/typebots/${typebotId}/edit`)
|
||||
await page.getByText('Configure...').click()
|
||||
await page.getByPlaceholder('Select a group').click()
|
||||
await expect(page.getByRole('menuitem', { name: 'Group #2' })).toBeHidden()
|
||||
await page.getByRole('menuitem', { name: 'Group #1' }).click()
|
||||
await page.getByPlaceholder('Select a block').click()
|
||||
await page.getByRole('menuitem', { name: 'Block #2' }).click()
|
||||
await page.getByRole('button', { name: 'Preview' }).click()
|
||||
await page.getByPlaceholder('Type your answer...').fill('Hi there!')
|
||||
await page.getByRole('button', { name: 'Send' }).click()
|
||||
await expect(
|
||||
page.locator('typebot-standard').getByText('How are you?').nth(1)
|
||||
).toBeVisible()
|
||||
await expect(
|
||||
page.locator('typebot-standard').getByText('Hello this is a test!').nth(1)
|
||||
).toBeHidden()
|
||||
})
|
@ -5,7 +5,7 @@ import { Group } from 'models'
|
||||
type Props = {
|
||||
groups: Group[]
|
||||
groupId?: string
|
||||
onGroupIdSelected: (groupId: string) => void
|
||||
onGroupIdSelected: (groupId: string | undefined) => void
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
|
@ -13,9 +13,9 @@ type Props = {
|
||||
export const TypebotLinkForm = ({ options, onOptionsChange }: Props) => {
|
||||
const { linkedTypebots, typebot } = useTypebot()
|
||||
|
||||
const handleTypebotIdChange = (typebotId: string | 'current') =>
|
||||
const handleTypebotIdChange = (typebotId: string | 'current' | undefined) =>
|
||||
onOptionsChange({ ...options, typebotId })
|
||||
const handleGroupIdChange = (groupId: string) =>
|
||||
const handleGroupIdChange = (groupId: string | undefined) =>
|
||||
onOptionsChange({ ...options, groupId })
|
||||
|
||||
return (
|
||||
|
@ -11,7 +11,7 @@ type Props = {
|
||||
idsToExclude: string[]
|
||||
typebotId?: string | 'current'
|
||||
currentWorkspaceId: string
|
||||
onSelect: (typebotId: string | 'current') => void
|
||||
onSelect: (typebotId: string | 'current' | undefined) => void
|
||||
}
|
||||
|
||||
export const TypebotsDropdown = ({
|
||||
|
@ -1,19 +1,102 @@
|
||||
import { Flex, HStack, Tooltip, useColorModeValue } from '@chakra-ui/react'
|
||||
import { DraggableBlockType } from 'models'
|
||||
import {
|
||||
BubbleBlockType,
|
||||
DraggableBlockType,
|
||||
InputBlockType,
|
||||
IntegrationBlockType,
|
||||
LogicBlockType,
|
||||
} from 'models'
|
||||
import { useBlockDnd } from '@/features/graph'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { BlockIcon } from './BlockIcon'
|
||||
import { BlockTypeLabel } from './BlockTypeLabel'
|
||||
import { isFreePlan, LockTag } from '@/features/billing'
|
||||
import { Plan } from 'db'
|
||||
import { useWorkspace } from '@/features/workspace'
|
||||
import { BlockLabel } from './BlockLabel'
|
||||
|
||||
export const BlockCard = ({
|
||||
type,
|
||||
onMouseDown,
|
||||
isDisabled = false,
|
||||
}: {
|
||||
type Props = {
|
||||
type: DraggableBlockType
|
||||
tooltip?: string
|
||||
isDisabled?: boolean
|
||||
children: React.ReactNode
|
||||
onMouseDown: (e: React.MouseEvent, type: DraggableBlockType) => void
|
||||
}) => {
|
||||
}
|
||||
|
||||
export const BlockCard = (
|
||||
props: Pick<Props, 'type' | 'onMouseDown'>
|
||||
): JSX.Element => {
|
||||
const { workspace } = useWorkspace()
|
||||
|
||||
switch (props.type) {
|
||||
case BubbleBlockType.EMBED:
|
||||
return (
|
||||
<BlockCardLayout
|
||||
{...props}
|
||||
tooltip="Embed a pdf, an iframe, a website..."
|
||||
>
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
</BlockCardLayout>
|
||||
)
|
||||
case InputBlockType.FILE:
|
||||
return (
|
||||
<BlockCardLayout {...props} tooltip="Upload Files">
|
||||
<BlockIcon type={props.type} />
|
||||
<HStack>
|
||||
<BlockLabel type={props.type} />
|
||||
{isFreePlan(workspace) && <LockTag plan={Plan.STARTER} />}
|
||||
</HStack>
|
||||
</BlockCardLayout>
|
||||
)
|
||||
case LogicBlockType.SCRIPT:
|
||||
return (
|
||||
<BlockCardLayout {...props} tooltip="Execute Javascript code">
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
</BlockCardLayout>
|
||||
)
|
||||
case LogicBlockType.TYPEBOT_LINK:
|
||||
return (
|
||||
<BlockCardLayout {...props} tooltip="Link and jump to another typebot">
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
</BlockCardLayout>
|
||||
)
|
||||
case LogicBlockType.JUMP:
|
||||
return (
|
||||
<BlockCardLayout
|
||||
{...props}
|
||||
tooltip="Fast forward the flow to another group"
|
||||
>
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
</BlockCardLayout>
|
||||
)
|
||||
case IntegrationBlockType.GOOGLE_SHEETS:
|
||||
return (
|
||||
<BlockCardLayout {...props} tooltip="Google Sheets">
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
</BlockCardLayout>
|
||||
)
|
||||
case IntegrationBlockType.GOOGLE_ANALYTICS:
|
||||
return (
|
||||
<BlockCardLayout {...props} tooltip="Google Analytics">
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
</BlockCardLayout>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<BlockCardLayout {...props}>
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
</BlockCardLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const BlockCardLayout = ({ type, onMouseDown, tooltip, children }: Props) => {
|
||||
const { draggedBlockType } = useBlockDnd()
|
||||
const [isMouseDown, setIsMouseDown] = useState(false)
|
||||
|
||||
@ -24,7 +107,7 @@ export const BlockCard = ({
|
||||
const handleMouseDown = (e: React.MouseEvent) => onMouseDown(e, type)
|
||||
|
||||
return (
|
||||
<Tooltip label="Coming soon!" isDisabled={!isDisabled}>
|
||||
<Tooltip label={tooltip}>
|
||||
<Flex pos="relative">
|
||||
<HStack
|
||||
borderWidth="1px"
|
||||
@ -32,21 +115,15 @@ export const BlockCard = ({
|
||||
rounded="lg"
|
||||
flex="1"
|
||||
cursor={'grab'}
|
||||
opacity={isMouseDown || isDisabled ? '0.4' : '1'}
|
||||
opacity={isMouseDown ? '0.4' : '1'}
|
||||
onMouseDown={handleMouseDown}
|
||||
bgColor={useColorModeValue('gray.50', 'gray.850')}
|
||||
px="4"
|
||||
py="2"
|
||||
_hover={useColorModeValue({ shadow: 'md' }, { bgColor: 'gray.800' })}
|
||||
transition="box-shadow 200ms, background-color 200ms"
|
||||
pointerEvents={isDisabled ? 'none' : 'auto'}
|
||||
>
|
||||
{!isMouseDown ? (
|
||||
<>
|
||||
<BlockIcon type={type} />
|
||||
<BlockTypeLabel type={type} />
|
||||
</>
|
||||
) : null}
|
||||
{!isMouseDown ? children : null}
|
||||
</HStack>
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { StackProps, HStack, useColorModeValue } from '@chakra-ui/react'
|
||||
import { BlockType } from 'models'
|
||||
import { BlockIcon } from './BlockIcon'
|
||||
import { BlockTypeLabel } from './BlockTypeLabel'
|
||||
import { BlockLabel } from './BlockLabel'
|
||||
|
||||
export const BlockCardOverlay = ({
|
||||
type,
|
||||
@ -24,7 +24,7 @@ export const BlockCardOverlay = ({
|
||||
{...props}
|
||||
>
|
||||
<BlockIcon type={type} />
|
||||
<BlockTypeLabel type={type} />
|
||||
<BlockLabel type={type} />
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
@ -37,10 +37,11 @@ import { GoogleAnalyticsLogo } from '@/features/blocks/integrations/googleAnalyt
|
||||
import { AudioBubbleIcon } from '@/features/blocks/bubbles/audio'
|
||||
import { WaitIcon } from '@/features/blocks/logic/wait/components/WaitIcon'
|
||||
import { ScriptIcon } from '@/features/blocks/logic/script/components/ScriptIcon'
|
||||
import { JumpIcon } from '@/features/blocks/logic/jump/components/JumpIcon'
|
||||
|
||||
type BlockIconProps = { type: BlockType } & IconProps
|
||||
|
||||
export const BlockIcon = ({ type, ...props }: BlockIconProps) => {
|
||||
export const BlockIcon = ({ type, ...props }: BlockIconProps): JSX.Element => {
|
||||
const blue = useColorModeValue('blue.500', 'blue.300')
|
||||
const orange = useColorModeValue('orange.500', 'orange.300')
|
||||
const purple = useColorModeValue('purple.500', 'purple.300')
|
||||
@ -85,6 +86,8 @@ export const BlockIcon = ({ type, ...props }: BlockIconProps) => {
|
||||
return <ScriptIcon {...props} />
|
||||
case LogicBlockType.WAIT:
|
||||
return <WaitIcon color={purple} {...props} />
|
||||
case LogicBlockType.JUMP:
|
||||
return <JumpIcon color={purple} {...props} />
|
||||
case LogicBlockType.TYPEBOT_LINK:
|
||||
return <TypebotLinkIcon color={purple} {...props} />
|
||||
case IntegrationBlockType.GOOGLE_SHEETS:
|
||||
|
@ -1,6 +1,4 @@
|
||||
import { HStack, Text, Tooltip } from '@chakra-ui/react'
|
||||
import { useWorkspace } from '@/features/workspace'
|
||||
import { Plan } from 'db'
|
||||
import { Text } from '@chakra-ui/react'
|
||||
import {
|
||||
BubbleBlockType,
|
||||
InputBlockType,
|
||||
@ -9,13 +7,10 @@ import {
|
||||
BlockType,
|
||||
} from 'models'
|
||||
import React from 'react'
|
||||
import { isFreePlan, LockTag } from '@/features/billing'
|
||||
|
||||
type Props = { type: BlockType }
|
||||
|
||||
export const BlockTypeLabel = ({ type }: Props): JSX.Element => {
|
||||
const { workspace } = useWorkspace()
|
||||
|
||||
export const BlockLabel = ({ type }: Props): JSX.Element => {
|
||||
switch (type) {
|
||||
case 'start':
|
||||
return <Text>Start</Text>
|
||||
@ -27,11 +22,7 @@ export const BlockTypeLabel = ({ type }: Props): JSX.Element => {
|
||||
case BubbleBlockType.VIDEO:
|
||||
return <Text>Video</Text>
|
||||
case BubbleBlockType.EMBED:
|
||||
return (
|
||||
<Tooltip label="Embed a pdf, an iframe, a website...">
|
||||
<Text>Embed</Text>
|
||||
</Tooltip>
|
||||
)
|
||||
return <Text>Embed</Text>
|
||||
case BubbleBlockType.AUDIO:
|
||||
return <Text>Audio</Text>
|
||||
case InputBlockType.NUMBER:
|
||||
@ -51,14 +42,7 @@ export const BlockTypeLabel = ({ type }: Props): JSX.Element => {
|
||||
case InputBlockType.RATING:
|
||||
return <Text>Rating</Text>
|
||||
case InputBlockType.FILE:
|
||||
return (
|
||||
<Tooltip label="Upload Files">
|
||||
<HStack>
|
||||
<Text>File</Text>
|
||||
{isFreePlan(workspace) && <LockTag plan={Plan.STARTER} />}
|
||||
</HStack>
|
||||
</Tooltip>
|
||||
)
|
||||
return <Text>File</Text>
|
||||
case LogicBlockType.SET_VARIABLE:
|
||||
return <Text>Set variable</Text>
|
||||
case LogicBlockType.CONDITION:
|
||||
@ -66,31 +50,17 @@ export const BlockTypeLabel = ({ type }: Props): JSX.Element => {
|
||||
case LogicBlockType.REDIRECT:
|
||||
return <Text>Redirect</Text>
|
||||
case LogicBlockType.SCRIPT:
|
||||
return (
|
||||
<Tooltip label="Run Javascript code">
|
||||
<Text>Script</Text>
|
||||
</Tooltip>
|
||||
)
|
||||
return <Text>Script</Text>
|
||||
case LogicBlockType.TYPEBOT_LINK:
|
||||
return (
|
||||
<Tooltip label="Link to another of your typebots">
|
||||
<Text>Typebot</Text>
|
||||
</Tooltip>
|
||||
)
|
||||
return <Text>Typebot</Text>
|
||||
case LogicBlockType.WAIT:
|
||||
return <Text>Wait</Text>
|
||||
case LogicBlockType.JUMP:
|
||||
return <Text>Jump</Text>
|
||||
case IntegrationBlockType.GOOGLE_SHEETS:
|
||||
return (
|
||||
<Tooltip label="Google Sheets">
|
||||
<Text>Sheets</Text>
|
||||
</Tooltip>
|
||||
)
|
||||
return <Text>Sheets</Text>
|
||||
case IntegrationBlockType.GOOGLE_ANALYTICS:
|
||||
return (
|
||||
<Tooltip label="Google Analytics">
|
||||
<Text>Analytics</Text>
|
||||
</Tooltip>
|
||||
)
|
||||
return <Text>Analytics</Text>
|
||||
case IntegrationBlockType.WEBHOOK:
|
||||
return <Text>Webhook</Text>
|
||||
case IntegrationBlockType.ZAPIER:
|
@ -224,6 +224,7 @@ export const BlockNode = ({
|
||||
align="flex-start"
|
||||
w="full"
|
||||
transition="border-color 0.2s"
|
||||
overflow="hidden"
|
||||
>
|
||||
<BlockIcon
|
||||
type={block.type}
|
||||
|
@ -39,6 +39,7 @@ import { AudioBubbleNode } from '@/features/blocks/bubbles/audio'
|
||||
import { WaitNodeContent } from '@/features/blocks/logic/wait/components/WaitNodeContent'
|
||||
import { ScriptNodeContent } from '@/features/blocks/logic/script/components/ScriptNodeContent'
|
||||
import { ButtonsBlockNode } from '@/features/blocks/inputs/buttons/components/ButtonsBlockNode'
|
||||
import { JumpNodeBody } from '@/features/blocks/logic/jump/components/JumpNodeBody'
|
||||
|
||||
type Props = {
|
||||
block: Block | StartBlock
|
||||
@ -125,6 +126,9 @@ export const BlockNodeContent = ({ block, indices }: Props): JSX.Element => {
|
||||
case LogicBlockType.WAIT: {
|
||||
return <WaitNodeContent options={block.options} />
|
||||
}
|
||||
case LogicBlockType.JUMP: {
|
||||
return <JumpNodeBody options={block.options} />
|
||||
}
|
||||
case LogicBlockType.TYPEBOT_LINK:
|
||||
return <TypebotLinkNode block={block} />
|
||||
case LogicBlockType.CONDITION:
|
||||
|
@ -42,6 +42,7 @@ import { MakeComSettings } from '@/features/blocks/integrations/makeCom'
|
||||
import { HelpDocButton } from './HelpDocButton'
|
||||
import { WaitSettings } from '@/features/blocks/logic/wait/components/WaitSettings'
|
||||
import { ScriptSettings } from '@/features/blocks/logic/script/components/ScriptSettings'
|
||||
import { JumpSettings } from '@/features/blocks/logic/jump/components/JumpSettings'
|
||||
|
||||
type Props = {
|
||||
block: BlockWithOptions
|
||||
@ -220,6 +221,15 @@ export const BlockSettings = ({
|
||||
/>
|
||||
)
|
||||
}
|
||||
case LogicBlockType.JUMP: {
|
||||
return (
|
||||
<JumpSettings
|
||||
groupId={block.groupId}
|
||||
options={block.options}
|
||||
onOptionsChange={handleOptionsChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
case IntegrationBlockType.GOOGLE_SHEETS: {
|
||||
return (
|
||||
<GoogleSheetsSettingsBody
|
||||
|
@ -227,6 +227,7 @@ const NonMemoizedDraggableGroupNode = ({
|
||||
bg: editableHoverBg,
|
||||
}}
|
||||
px="1"
|
||||
noOfLines={2}
|
||||
userSelect={'none'}
|
||||
/>
|
||||
<EditableInput minW="0" px="1" className="prevent-group-drag" />
|
||||
|
@ -409,6 +409,8 @@ const parseDefaultBlockOptions = (type: BlockWithOptionsType): BlockOptions => {
|
||||
return defaultScriptOptions
|
||||
case LogicBlockType.WAIT:
|
||||
return defaultWaitOptions
|
||||
case LogicBlockType.JUMP:
|
||||
return {}
|
||||
case LogicBlockType.TYPEBOT_LINK:
|
||||
return {}
|
||||
case IntegrationBlockType.GOOGLE_SHEETS:
|
||||
|
1
apps/builder/src/test/assets/typebots/logic/jump.json
Normal file
1
apps/builder/src/test/assets/typebots/logic/jump.json
Normal file
@ -0,0 +1 @@
|
||||
{"id":"clemrhnsp00001auiaoun42wf","version":"3","createdAt":"2023-02-27T11:53:48.217Z","updatedAt":"2023-02-27T11:53:48.217Z","icon":null,"name":"My typebot","folderId":null,"groups":[{"id":"et7lna5mxbmn9blchekkejdf","title":"Start","blocks":[{"id":"ivcklgamqz8e3uwmtshm32n9","type":"start","label":"Start","groupId":"et7lna5mxbmn9blchekkejdf","outgoingEdgeId":"ogi8zj19v4krzm9xe4c9orv2"}],"graphCoordinates":{"x":0,"y":0}},{"id":"wrd4694r5fo8czflp4t79259","graphCoordinates":{"x":41.28125,"y":169.53125},"title":"Group #1","blocks":[{"id":"pjnrvjh6un6el1tjsbu7d6w1","groupId":"wrd4694r5fo8czflp4t79259","type":"text","content":{"html":"<div>Hello this is a test!</div>","richText":[{"type":"p","children":[{"text":"Hello this is a test!"}]}],"plainText":"Hello this is a test!"}},{"id":"wvt13rpcvuy5s7mj83bm946s","groupId":"wrd4694r5fo8czflp4t79259","type":"text","content":{"html":"<div>How are you?</div>","richText":[{"type":"p","children":[{"text":"How are you?"}]}],"plainText":"How are you?"}},{"id":"he864w99l8y6c6epfd9girvv","groupId":"wrd4694r5fo8czflp4t79259","type":"text input","options":{"isLong":false,"labels":{"button":"Send","placeholder":"Type your answer..."}},"outgoingEdgeId":"a42zatxkw0q8w2kq886m9ek8"}]},{"id":"eao5ufv4wtl0yos09w6wag3j","graphCoordinates":{"x":411.078125,"y":169.94140625},"title":"Group #2","blocks":[{"id":"t33v5ijddw2e2786jiurep3p","groupId":"eao5ufv4wtl0yos09w6wag3j","type":"Jump","options":{}}]}],"variables":[],"edges":[{"from":{"groupId":"et7lna5mxbmn9blchekkejdf","blockId":"ivcklgamqz8e3uwmtshm32n9"},"to":{"groupId":"wrd4694r5fo8czflp4t79259"},"id":"ogi8zj19v4krzm9xe4c9orv2"},{"from":{"groupId":"wrd4694r5fo8czflp4t79259","blockId":"he864w99l8y6c6epfd9girvv"},"to":{"groupId":"eao5ufv4wtl0yos09w6wag3j"},"id":"a42zatxkw0q8w2kq886m9ek8"}],"theme":{"chat":{"inputs":{"color":"#303235","backgroundColor":"#FFFFFF","placeholderColor":"#9095A0"},"buttons":{"color":"#FFFFFF","backgroundColor":"#0042DA"},"hostAvatar":{"url":"https://avatars.githubusercontent.com/u/16015833?v=4","isEnabled":true},"hostBubbles":{"color":"#303235","backgroundColor":"#F7F8FF"},"guestBubbles":{"color":"#FFFFFF","backgroundColor":"#FF8E21"}},"general":{"font":"Open Sans","background":{"type":"Color","content":"#ffffff"}}},"settings":{"general":{"isBrandingEnabled":true,"isInputPrefillEnabled":true,"isResultSavingEnabled":true,"isHideQueryParamsEnabled":true,"isNewResultOnRefreshEnabled":true},"metadata":{"description":"Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form."},"typingEmulation":{"speed":300,"enabled":true,"maxDelay":1.5}},"publicId":null,"customDomain":null,"workspaceId":"freeWorkspace","resultsTablePreferences":null,"isArchived":false,"isClosed":false}
|
@ -1456,59 +1456,193 @@
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"allOf": [
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Code"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"shouldExecuteInParentContext": {
|
||||
"type": "boolean"
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
"id",
|
||||
"groupId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Code"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"shouldExecuteInParentContext": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
]
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"groupId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Condition"
|
||||
]
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"blockId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "number",
|
||||
"enum": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"content": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"logicalOperator": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"OR",
|
||||
"AND"
|
||||
]
|
||||
},
|
||||
"comparisons": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
},
|
||||
"comparisonOperator": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Equal to",
|
||||
"Not equal",
|
||||
"Contains",
|
||||
"Greater than",
|
||||
"Less than",
|
||||
"Is set"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"logicalOperator",
|
||||
"comparisons"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"content"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"items"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -1539,104 +1673,28 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Condition"
|
||||
"Redirect"
|
||||
]
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
},
|
||||
"outgoingEdgeId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"blockId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "number",
|
||||
"enum": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"content": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"logicalOperator": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"OR",
|
||||
"AND"
|
||||
]
|
||||
},
|
||||
"comparisons": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
},
|
||||
"comparisonOperator": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Equal to",
|
||||
"Not equal",
|
||||
"Contains",
|
||||
"Greater than",
|
||||
"Less than",
|
||||
"Is set"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"logicalOperator",
|
||||
"comparisons"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"content"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"isNewTab": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isNewTab"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"items"
|
||||
"options"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@ -1671,22 +1729,19 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Redirect"
|
||||
"Typebot link"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"typebotId": {
|
||||
"type": "string"
|
||||
},
|
||||
"isNewTab": {
|
||||
"type": "boolean"
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isNewTab"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
@ -1727,17 +1782,20 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Typebot link"
|
||||
"Set variable"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"typebotId": {
|
||||
"variableId": {
|
||||
"type": "string"
|
||||
},
|
||||
"groupId": {
|
||||
"expressionToEvaluate": {
|
||||
"type": "string"
|
||||
},
|
||||
"isCode": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@ -1780,20 +1838,14 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Set variable"
|
||||
"Wait"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"variableId": {
|
||||
"secondsToWaitFor": {
|
||||
"type": "string"
|
||||
},
|
||||
"expressionToEvaluate": {
|
||||
"type": "string"
|
||||
},
|
||||
"isCode": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@ -1836,13 +1888,16 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Wait"
|
||||
"Jump"
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secondsToWaitFor": {
|
||||
"groupId": {
|
||||
"type": "string"
|
||||
},
|
||||
"blockId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
|
@ -0,0 +1,33 @@
|
||||
import { ExecuteLogicResponse } from '@/features/chat'
|
||||
import {
|
||||
addEdgeToTypebot,
|
||||
createPortalEdge,
|
||||
} from '@/features/chat/api/utils/addEdgeToTypebot'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { SessionState } from 'models'
|
||||
import { JumpBlock } from 'models/features/blocks/logic/jump'
|
||||
|
||||
export const executeJumpBlock = (
|
||||
state: SessionState,
|
||||
{ groupId, blockId }: JumpBlock['options']
|
||||
): ExecuteLogicResponse => {
|
||||
const groupToJumpTo = state.typebot.groups.find(
|
||||
(group) => group.id === groupId
|
||||
)
|
||||
const blockToJumpTo =
|
||||
groupToJumpTo?.blocks.find((block) => block.id === blockId) ??
|
||||
groupToJumpTo?.blocks[0]
|
||||
|
||||
if (!blockToJumpTo?.groupId)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Block to jump to is not found',
|
||||
})
|
||||
|
||||
const portalEdge = createPortalEdge({
|
||||
to: { groupId: blockToJumpTo?.groupId, blockId: blockToJumpTo?.id },
|
||||
})
|
||||
const newSessionState = addEdgeToTypebot(state, portalEdge)
|
||||
|
||||
return { outgoingEdgeId: portalEdge.id, newSessionState }
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
import { ExecuteLogicResponse } from '@/features/chat'
|
||||
import {
|
||||
addEdgeToTypebot,
|
||||
createPortalEdge,
|
||||
} from '@/features/chat/api/utils/addEdgeToTypebot'
|
||||
import { saveErrorLog } from '@/features/logs/api'
|
||||
import prisma from '@/lib/prisma'
|
||||
import {
|
||||
TypebotLinkBlock,
|
||||
Edge,
|
||||
SessionState,
|
||||
TypebotInSession,
|
||||
Variable,
|
||||
@ -48,28 +51,17 @@ export const executeTypebotLink = async (
|
||||
})
|
||||
return { outgoingEdgeId: block.outgoingEdgeId }
|
||||
}
|
||||
const portalEdge: Edge = {
|
||||
id: (Math.random() * 1000).toString(),
|
||||
from: { blockId: '', groupId: '' },
|
||||
to: {
|
||||
groupId: nextGroupId,
|
||||
},
|
||||
}
|
||||
|
||||
const portalEdge = createPortalEdge({ to: { groupId: nextGroupId } })
|
||||
|
||||
newSessionState = addEdgeToTypebot(newSessionState, portalEdge)
|
||||
|
||||
return {
|
||||
outgoingEdgeId: portalEdge.id,
|
||||
newSessionState,
|
||||
}
|
||||
}
|
||||
|
||||
const addEdgeToTypebot = (state: SessionState, edge: Edge): SessionState => ({
|
||||
...state,
|
||||
typebot: {
|
||||
...state.typebot,
|
||||
edges: [...state.typebot.edges, edge],
|
||||
},
|
||||
})
|
||||
|
||||
const addLinkedTypebotToState = (
|
||||
state: SessionState,
|
||||
block: TypebotLinkBlock,
|
||||
|
19
apps/viewer/src/features/chat/api/utils/addEdgeToTypebot.ts
Normal file
19
apps/viewer/src/features/chat/api/utils/addEdgeToTypebot.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { SessionState, Edge } from 'models'
|
||||
|
||||
export const addEdgeToTypebot = (
|
||||
state: SessionState,
|
||||
edge: Edge
|
||||
): SessionState => ({
|
||||
...state,
|
||||
typebot: {
|
||||
...state.typebot,
|
||||
edges: [...state.typebot.edges, edge],
|
||||
},
|
||||
})
|
||||
|
||||
export const createPortalEdge = ({ to }: Pick<Edge, 'to'>) => ({
|
||||
id: createId(),
|
||||
from: { blockId: '', groupId: '' },
|
||||
to,
|
||||
})
|
@ -6,6 +6,7 @@ import { executeWait } from '@/features/blocks/logic/wait/api/utils/executeWait'
|
||||
import { LogicBlock, LogicBlockType, SessionState } from 'models'
|
||||
import { ExecuteLogicResponse } from '../../types'
|
||||
import { executeScript } from '@/features/blocks/logic/script/executeScript'
|
||||
import { executeJumpBlock } from '@/features/blocks/logic/jump/executeJumpBlock'
|
||||
|
||||
export const executeLogic =
|
||||
(state: SessionState, lastBubbleBlockId?: string) =>
|
||||
@ -23,5 +24,7 @@ export const executeLogic =
|
||||
return executeTypebotLink(state, block)
|
||||
case LogicBlockType.WAIT:
|
||||
return executeWait(state, block, lastBubbleBlockId)
|
||||
case LogicBlockType.JUMP:
|
||||
return executeJumpBlock(state, block.options)
|
||||
}
|
||||
}
|
||||
|
@ -27,5 +27,7 @@ export const executeIntegration = ({
|
||||
return executeSendEmailBlock(block, context)
|
||||
case IntegrationBlockType.CHATWOOT:
|
||||
return executeChatwootBlock(block, context)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -30,5 +30,7 @@ export const executeLogic = async (
|
||||
return executeTypebotLink(block, context)
|
||||
case LogicBlockType.WAIT:
|
||||
return { nextEdgeId: await executeWait(block, context) }
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,5 @@ export enum LogicBlockType {
|
||||
SCRIPT = 'Code',
|
||||
TYPEBOT_LINK = 'Typebot link',
|
||||
WAIT = 'Wait',
|
||||
JUMP = 'Jump',
|
||||
}
|
||||
|
17
packages/models/features/blocks/logic/jump.ts
Normal file
17
packages/models/features/blocks/logic/jump.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { z } from 'zod'
|
||||
import { blockBaseSchema } from '../baseSchemas'
|
||||
import { LogicBlockType } from './enums'
|
||||
|
||||
export const jumpOptionsSchema = z.object({
|
||||
groupId: z.string().optional(),
|
||||
blockId: z.string().optional(),
|
||||
})
|
||||
|
||||
export const jumpBlockSchema = blockBaseSchema.and(
|
||||
z.object({
|
||||
type: z.enum([LogicBlockType.JUMP]),
|
||||
options: jumpOptionsSchema,
|
||||
})
|
||||
)
|
||||
|
||||
export type JumpBlock = z.infer<typeof jumpBlockSchema>
|
@ -5,12 +5,14 @@ import { redirectOptionsSchema, redirectBlockSchema } from './redirect'
|
||||
import { setVariableOptionsSchema, setVariableBlockSchema } from './setVariable'
|
||||
import { typebotLinkOptionsSchema, typebotLinkBlockSchema } from './typebotLink'
|
||||
import { waitBlockSchema, waitOptionsSchema } from './wait'
|
||||
import { jumpBlockSchema, jumpOptionsSchema } from './jump'
|
||||
|
||||
const logicBlockOptionsSchema = scriptOptionsSchema
|
||||
.or(redirectOptionsSchema)
|
||||
.or(setVariableOptionsSchema)
|
||||
.or(typebotLinkOptionsSchema)
|
||||
.or(waitOptionsSchema)
|
||||
.or(jumpOptionsSchema)
|
||||
|
||||
export const logicBlockSchema = scriptBlockSchema
|
||||
.or(conditionBlockSchema)
|
||||
@ -18,6 +20,7 @@ export const logicBlockSchema = scriptBlockSchema
|
||||
.or(typebotLinkBlockSchema)
|
||||
.or(setVariableBlockSchema)
|
||||
.or(waitBlockSchema)
|
||||
.or(jumpBlockSchema)
|
||||
|
||||
export type LogicBlock = z.infer<typeof logicBlockSchema>
|
||||
export type LogicBlockOptions = z.infer<typeof logicBlockOptionsSchema>
|
||||
|
Reference in New Issue
Block a user