refactor(graph): ♻️ Add Edges table in Typebot
This commit is contained in:
@ -5,26 +5,31 @@ import {
|
||||
getAnchorsPosition,
|
||||
computeEdgePath,
|
||||
getEndpointTopOffset,
|
||||
getSourceEndpointId,
|
||||
} from 'services/graph'
|
||||
|
||||
type Props = { stepId: string }
|
||||
type Props = { edgeId: string }
|
||||
|
||||
export const Edge = ({ stepId }: Props) => {
|
||||
export const Edge = ({ edgeId }: Props) => {
|
||||
const { typebot } = useAnalyticsGraph()
|
||||
const step = typebot?.steps.byId[stepId]
|
||||
const edge = typebot?.edges.byId[edgeId]
|
||||
const { sourceEndpoints, targetEndpoints, graphPosition } = useGraph()
|
||||
const [sourceTop, setSourceTop] = useState(
|
||||
getEndpointTopOffset(graphPosition, sourceEndpoints, stepId)
|
||||
getEndpointTopOffset(
|
||||
graphPosition,
|
||||
sourceEndpoints,
|
||||
getSourceEndpointId(edge)
|
||||
)
|
||||
)
|
||||
const [targetTop, setTargetTop] = useState(
|
||||
getEndpointTopOffset(graphPosition, sourceEndpoints, step?.target?.stepId)
|
||||
getEndpointTopOffset(graphPosition, sourceEndpoints, edge?.to.stepId)
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const newSourceTop = getEndpointTopOffset(
|
||||
graphPosition,
|
||||
sourceEndpoints,
|
||||
stepId
|
||||
getSourceEndpointId(edge)
|
||||
)
|
||||
const sensibilityThreshold = 10
|
||||
const newSourceTopIsTooClose =
|
||||
@ -39,7 +44,7 @@ export const Edge = ({ stepId }: Props) => {
|
||||
const newTargetTop = getEndpointTopOffset(
|
||||
graphPosition,
|
||||
targetEndpoints,
|
||||
step?.target?.stepId
|
||||
edge?.to.stepId
|
||||
)
|
||||
const sensibilityThreshold = 10
|
||||
const newSourceTopIsTooClose =
|
||||
@ -51,15 +56,14 @@ export const Edge = ({ stepId }: Props) => {
|
||||
}, [graphPosition])
|
||||
|
||||
const { sourceBlock, targetBlock } = useMemo(() => {
|
||||
if (!typebot) return {}
|
||||
if (!step?.target) return {}
|
||||
const targetBlock = typebot.blocks.byId[step.target.blockId]
|
||||
const sourceBlock = typebot.blocks.byId[step.blockId]
|
||||
if (!typebot || !edge) return {}
|
||||
const targetBlock = typebot.blocks.byId[edge.to.blockId]
|
||||
const sourceBlock = typebot.blocks.byId[edge.from.blockId]
|
||||
return {
|
||||
sourceBlock,
|
||||
targetBlock,
|
||||
}
|
||||
}, [step?.blockId, step?.target, typebot])
|
||||
}, [edge, typebot])
|
||||
|
||||
const path = useMemo(() => {
|
||||
if (!sourceBlock || !targetBlock) return ``
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { chakra } from '@chakra-ui/system'
|
||||
import { useAnalyticsGraph } from 'contexts/AnalyticsGraphProvider'
|
||||
import React, { useMemo } from 'react'
|
||||
import React from 'react'
|
||||
import { AnswersCount } from 'services/analytics'
|
||||
import { isDefined } from 'utils'
|
||||
import { DropOffBlock } from '../blocks/DropOffBlock'
|
||||
import { DropOffEdge } from './DropOffEdge'
|
||||
import { Edge } from './Edge'
|
||||
@ -12,13 +11,6 @@ type Props = { answersCounts?: AnswersCount[] }
|
||||
export const Edges = ({ answersCounts }: Props) => {
|
||||
const { typebot } = useAnalyticsGraph()
|
||||
|
||||
const stepIdsWithTarget: string[] = useMemo(() => {
|
||||
if (!typebot) return []
|
||||
return typebot.steps.allIds.filter((stepId) =>
|
||||
isDefined(typebot.steps.byId[stepId].target)
|
||||
)
|
||||
}, [typebot])
|
||||
|
||||
return (
|
||||
<>
|
||||
<chakra.svg
|
||||
@ -29,8 +21,8 @@ export const Edges = ({ answersCounts }: Props) => {
|
||||
left="0"
|
||||
top="0"
|
||||
>
|
||||
{stepIdsWithTarget.map((stepId) => (
|
||||
<Edge key={stepId} stepId={stepId} />
|
||||
{typebot?.edges.allIds.map((edgeId) => (
|
||||
<Edge key={edgeId} edgeId={edgeId} />
|
||||
))}
|
||||
<marker
|
||||
id={'arrow'}
|
||||
|
@ -3,6 +3,7 @@ import { useAnalyticsGraph } from 'contexts/AnalyticsGraphProvider'
|
||||
import React, { useMemo } from 'react'
|
||||
import { AnswersCount } from 'services/analytics'
|
||||
import { computeSourceCoordinates } from 'services/graph'
|
||||
import { isDefined } from 'utils'
|
||||
|
||||
type Props = {
|
||||
answersCounts: AnswersCount[]
|
||||
@ -20,11 +21,12 @@ export const DropOffBlock = ({ answersCounts, blockId }: Props) => {
|
||||
const { totalDroppedUser, dropOffRate } = useMemo(() => {
|
||||
if (!typebot || totalAnswers === undefined)
|
||||
return { previousTotal: undefined, dropOffRate: undefined }
|
||||
const previousBlockIds = typebot.blocks.allIds.filter(() =>
|
||||
typebot.steps.allIds.find(
|
||||
(sId) => typebot.steps.byId[sId].target?.blockId === blockId
|
||||
)
|
||||
)
|
||||
const previousBlockIds = typebot.edges.allIds
|
||||
.map((edgeId) => {
|
||||
const edge = typebot.edges.byId[edgeId]
|
||||
return edge.to.blockId === blockId ? edge.from.blockId : undefined
|
||||
})
|
||||
.filter((blockId) => isDefined(blockId))
|
||||
const previousTotal = answersCounts
|
||||
.filter((a) => previousBlockIds.includes(a.blockId))
|
||||
.reduce((prev, acc) => acc.totalAnswers + prev, 0)
|
||||
|
@ -20,18 +20,17 @@ type Props = {
|
||||
}
|
||||
|
||||
export const BlockNode = ({ block }: Props) => {
|
||||
const { connectingIds, setConnectingIds, previewingIds } = useGraph()
|
||||
const { connectingIds, setConnectingIds, previewingEdgeId } = useGraph()
|
||||
const { typebot, updateBlock } = useTypebot()
|
||||
const { setMouseOverBlockId } = useDnd()
|
||||
const { draggedStep, draggedStepType } = useDnd()
|
||||
const [isMouseDown, setIsMouseDown] = useState(false)
|
||||
const [isConnecting, setIsConnecting] = useState(false)
|
||||
const isPreviewing = useMemo(
|
||||
() =>
|
||||
previewingIds.sourceId === block.id ||
|
||||
previewingIds.targetId === block.id,
|
||||
[block.id, previewingIds.sourceId, previewingIds.targetId]
|
||||
)
|
||||
const isPreviewing = useMemo(() => {
|
||||
if (!previewingEdgeId) return
|
||||
const edge = typebot?.edges.byId[previewingEdgeId]
|
||||
return edge?.to.blockId === block.id || edge?.from.blockId === block.id
|
||||
}, [block.id, previewingEdgeId, typebot?.edges.byId])
|
||||
|
||||
useEffect(() => {
|
||||
setIsConnecting(
|
||||
|
@ -128,7 +128,7 @@ export const ChoiceItemNode = ({
|
||||
source={{
|
||||
blockId: typebot.steps.byId[item.stepId].blockId,
|
||||
stepId: item.stepId,
|
||||
choiceItemId: item.id,
|
||||
nodeId: item.id,
|
||||
}}
|
||||
pos="absolute"
|
||||
right="15px"
|
||||
|
@ -49,9 +49,7 @@ export const SettingsPopoverContent = ({ step, onExpandClick }: Props) => {
|
||||
<PopoverContent onMouseDown={handleMouseDown} pos="relative">
|
||||
<PopoverArrow />
|
||||
<PopoverBody
|
||||
px="6"
|
||||
pb="6"
|
||||
pt="4"
|
||||
p="6"
|
||||
overflowY="scroll"
|
||||
maxH="400px"
|
||||
ref={ref}
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { Box, BoxProps, Flex } from '@chakra-ui/react'
|
||||
import { ConnectingSourceIds, useGraph } from 'contexts/GraphContext'
|
||||
import { useGraph } from 'contexts/GraphContext'
|
||||
import { Source } from 'models'
|
||||
import React, { MouseEvent, useEffect, useRef } from 'react'
|
||||
|
||||
export const SourceEndpoint = ({
|
||||
source,
|
||||
...props
|
||||
}: BoxProps & {
|
||||
source: ConnectingSourceIds
|
||||
source: Source
|
||||
}) => {
|
||||
const { setConnectingIds, addSourceEndpoint: addEndpoint } = useGraph()
|
||||
const ref = useRef<HTMLDivElement | null>(null)
|
||||
@ -18,8 +19,7 @@ export const SourceEndpoint = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return
|
||||
const id =
|
||||
source.choiceItemId ?? source.stepId + (source.conditionType ?? '')
|
||||
const id = source.nodeId ?? source.stepId + (source.conditionType ?? '')
|
||||
addEndpoint({
|
||||
id,
|
||||
ref,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
HStack,
|
||||
Popover,
|
||||
@ -7,12 +6,11 @@ import {
|
||||
useDisclosure,
|
||||
useEventListener,
|
||||
} from '@chakra-ui/react'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { Block, DraggableStep, Step } from 'models'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { DraggableStep, Step } from 'models'
|
||||
import { useGraph } from 'contexts/GraphContext'
|
||||
import { StepIcon } from 'components/board/StepTypesList/StepIcon'
|
||||
import {
|
||||
isDefined,
|
||||
isInputStep,
|
||||
isLogicStep,
|
||||
isTextBubbleStep,
|
||||
@ -50,7 +48,7 @@ export const StepNode = ({
|
||||
}) => {
|
||||
const { query } = useRouter()
|
||||
const { setConnectingIds, connectingIds } = useGraph()
|
||||
const { moveStep, typebot } = useTypebot()
|
||||
const { moveStep } = useTypebot()
|
||||
const [isConnecting, setIsConnecting] = useState(false)
|
||||
const [mouseDownEvent, setMouseDownEvent] =
|
||||
useState<{ absolute: Coordinates; relative: Coordinates }>()
|
||||
@ -132,30 +130,6 @@ export const StepNode = ({
|
||||
setIsEditing(false)
|
||||
}
|
||||
|
||||
const connectedStubPosition: 'right' | 'left' | undefined = useMemo(() => {
|
||||
if (!typebot) return
|
||||
const currentBlock = typebot.blocks?.byId[step.blockId]
|
||||
const isDragginConnectorFromCurrentBlock =
|
||||
connectingIds?.source.blockId === currentBlock?.id &&
|
||||
connectingIds?.target?.blockId
|
||||
const targetBlockId = isDragginConnectorFromCurrentBlock
|
||||
? connectingIds.target?.blockId
|
||||
: step.target?.blockId
|
||||
const targetedBlock = targetBlockId && typebot.blocks.byId[targetBlockId]
|
||||
return targetedBlock
|
||||
? targetedBlock.graphCoordinates.x <
|
||||
(currentBlock as Block).graphCoordinates.x
|
||||
? 'left'
|
||||
: 'right'
|
||||
: undefined
|
||||
}, [
|
||||
typebot,
|
||||
step.blockId,
|
||||
step.target?.blockId,
|
||||
connectingIds?.source.blockId,
|
||||
connectingIds?.target?.blockId,
|
||||
])
|
||||
|
||||
return isEditing && isTextBubbleStep(step) ? (
|
||||
<TextEditor
|
||||
stepId={step.id}
|
||||
@ -184,16 +158,6 @@ export const StepNode = ({
|
||||
data-testid={`step-${step.id}`}
|
||||
w="full"
|
||||
>
|
||||
{connectedStubPosition === 'left' && (
|
||||
<Box
|
||||
h="2px"
|
||||
pos="absolute"
|
||||
left="-18px"
|
||||
top="25px"
|
||||
w="18px"
|
||||
bgColor="blue.500"
|
||||
/>
|
||||
)}
|
||||
<HStack
|
||||
flex="1"
|
||||
userSelect="none"
|
||||
@ -225,24 +189,6 @@ export const StepNode = ({
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{isDefined(connectedStubPosition) &&
|
||||
hasDefaultConnector(step) &&
|
||||
isConnectable && (
|
||||
<Box
|
||||
h="2px"
|
||||
pos="absolute"
|
||||
right={
|
||||
connectedStubPosition === 'left' ? undefined : '-18px'
|
||||
}
|
||||
left={
|
||||
connectedStubPosition === 'left' ? '-18px' : undefined
|
||||
}
|
||||
top="25px"
|
||||
w="18px"
|
||||
bgColor="gray.500"
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</PopoverTrigger>
|
||||
{hasPopover(step) && (
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { useEventListener } from '@chakra-ui/hooks'
|
||||
import assert from 'assert'
|
||||
import { headerHeight } from 'components/shared/TypebotHeader/TypebotHeader'
|
||||
import { useGraph, ConnectingIds } from 'contexts/GraphContext'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import { Step, Target } from 'models'
|
||||
import { Target } from 'models'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import {
|
||||
computeConnectingEdgePath,
|
||||
@ -18,7 +19,7 @@ export const DrawingEdge = () => {
|
||||
sourceEndpoints,
|
||||
targetEndpoints,
|
||||
} = useGraph()
|
||||
const { typebot, updateStep, updateChoiceItem } = useTypebot()
|
||||
const { typebot, createEdge } = useTypebot()
|
||||
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 })
|
||||
|
||||
const sourceBlock = useMemo(
|
||||
@ -32,7 +33,7 @@ export const DrawingEdge = () => {
|
||||
return getEndpointTopOffset(
|
||||
graphPosition,
|
||||
sourceEndpoints,
|
||||
connectingIds.source.choiceItemId ??
|
||||
connectingIds.source.nodeId ??
|
||||
connectingIds.source.stepId + (connectingIds.source.conditionType ?? '')
|
||||
)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@ -79,23 +80,8 @@ export const DrawingEdge = () => {
|
||||
})
|
||||
|
||||
const createNewEdge = (connectingIds: ConnectingIds) => {
|
||||
if (connectingIds.source.choiceItemId) {
|
||||
updateChoiceItem(connectingIds.source.choiceItemId, {
|
||||
target: connectingIds.target,
|
||||
})
|
||||
} else if (connectingIds.source.conditionType === 'true') {
|
||||
updateStep(connectingIds.source.stepId, {
|
||||
trueTarget: connectingIds.target,
|
||||
} as Step)
|
||||
} else if (connectingIds.source.conditionType === 'false') {
|
||||
updateStep(connectingIds.source.stepId, {
|
||||
falseTarget: connectingIds.target,
|
||||
} as Step)
|
||||
} else {
|
||||
updateStep(connectingIds.source.stepId, {
|
||||
target: connectingIds.target,
|
||||
})
|
||||
}
|
||||
assert(connectingIds.target)
|
||||
createEdge({ from: connectingIds.source, to: connectingIds.target })
|
||||
}
|
||||
|
||||
if ((mousePosition.x === 0 && mousePosition.y === 0) || !connectingIds)
|
||||
|
@ -1,14 +1,11 @@
|
||||
import { isDefined } from '@udecode/plate-core'
|
||||
import assert from 'assert'
|
||||
import { Coordinates, useGraph } from 'contexts/GraphContext'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import { ChoiceItem } from 'models'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import {
|
||||
getAnchorsPosition,
|
||||
computeEdgePath,
|
||||
getEndpointTopOffset,
|
||||
getTarget,
|
||||
getSourceEndpointId,
|
||||
} from 'services/graph'
|
||||
|
||||
export type AnchorsPositionProps = {
|
||||
@ -18,44 +15,31 @@ export type AnchorsPositionProps = {
|
||||
totalSegments: number
|
||||
}
|
||||
|
||||
export enum EdgeType {
|
||||
STEP,
|
||||
CHOICE_ITEM,
|
||||
CONDITION_TRUE,
|
||||
CONDITION_FALSE,
|
||||
}
|
||||
|
||||
export const Edge = ({
|
||||
type,
|
||||
stepId,
|
||||
item,
|
||||
}: {
|
||||
type: EdgeType
|
||||
stepId: string
|
||||
item?: ChoiceItem
|
||||
}) => {
|
||||
export const Edge = ({ edgeId }: { edgeId: string }) => {
|
||||
const { typebot } = useTypebot()
|
||||
const { previewingIds, sourceEndpoints, targetEndpoints, graphPosition } =
|
||||
const { previewingEdgeId, sourceEndpoints, targetEndpoints, graphPosition } =
|
||||
useGraph()
|
||||
const step = typebot?.steps.byId[stepId]
|
||||
const isPreviewing = useMemo(
|
||||
() =>
|
||||
previewingIds.sourceId === step?.blockId &&
|
||||
previewingIds.targetId === step?.target?.blockId,
|
||||
[previewingIds.sourceId, previewingIds.targetId, step]
|
||||
const edge = useMemo(
|
||||
() => typebot?.edges.byId[edgeId],
|
||||
[edgeId, typebot?.edges.byId]
|
||||
)
|
||||
const isPreviewing = previewingEdgeId === edgeId
|
||||
const [sourceTop, setSourceTop] = useState(
|
||||
getEndpointTopOffset(graphPosition, sourceEndpoints, item?.id ?? step?.id)
|
||||
getEndpointTopOffset(
|
||||
graphPosition,
|
||||
sourceEndpoints,
|
||||
getSourceEndpointId(edge)
|
||||
)
|
||||
)
|
||||
const [targetTop, setTargetTop] = useState(
|
||||
getEndpointTopOffset(graphPosition, targetEndpoints, step?.id)
|
||||
getEndpointTopOffset(graphPosition, targetEndpoints, edge?.to.stepId)
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const newSourceTop = getEndpointTopOffset(
|
||||
graphPosition,
|
||||
sourceEndpoints,
|
||||
getSourceEndpointId()
|
||||
getSourceEndpointId(edge)
|
||||
)
|
||||
const sensibilityThreshold = 10
|
||||
const newSourceTopIsTooClose =
|
||||
@ -66,26 +50,12 @@ export const Edge = ({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [typebot?.blocks, typebot?.steps, graphPosition, sourceEndpoints])
|
||||
|
||||
const getSourceEndpointId = () => {
|
||||
switch (type) {
|
||||
case EdgeType.STEP:
|
||||
return step?.id
|
||||
case EdgeType.CHOICE_ITEM:
|
||||
return item?.id
|
||||
case EdgeType.CONDITION_TRUE:
|
||||
return step?.id + 'true'
|
||||
case EdgeType.CONDITION_FALSE:
|
||||
return step?.id + 'false'
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!step) return
|
||||
const target = getTarget(step, type)
|
||||
if (!edge) return
|
||||
const newTargetTop = getEndpointTopOffset(
|
||||
graphPosition,
|
||||
targetEndpoints,
|
||||
target?.blockId ?? target?.stepId
|
||||
edge?.to.stepId
|
||||
)
|
||||
const sensibilityThreshold = 10
|
||||
const newSourceTopIsTooClose =
|
||||
@ -97,20 +67,17 @@ export const Edge = ({
|
||||
}, [typebot?.blocks, typebot?.steps, graphPosition, targetEndpoints])
|
||||
|
||||
const { sourceBlock, targetBlock } = useMemo(() => {
|
||||
if (!typebot) return {}
|
||||
const step = typebot.steps.byId[stepId]
|
||||
const sourceBlock = typebot.blocks.byId[step.blockId]
|
||||
const targetBlockId = getTarget(step, type)?.blockId
|
||||
assert(isDefined(targetBlockId))
|
||||
const targetBlock = typebot.blocks.byId[targetBlockId]
|
||||
if (!typebot || !edge?.from.stepId) return {}
|
||||
const sourceBlock = typebot.blocks.byId[edge.from.blockId]
|
||||
const targetBlock = typebot.blocks.byId[edge.to.blockId]
|
||||
return {
|
||||
sourceBlock,
|
||||
targetBlock,
|
||||
}
|
||||
}, [stepId, type, typebot])
|
||||
}, [edge?.from.blockId, edge?.from.stepId, edge?.to.blockId, typebot])
|
||||
|
||||
const path = useMemo(() => {
|
||||
if (!sourceBlock || !targetBlock || !step) return ``
|
||||
if (!sourceBlock || !targetBlock) return ``
|
||||
const anchorsPosition = getAnchorsPosition({
|
||||
sourceBlock,
|
||||
targetBlock,
|
||||
@ -118,7 +85,7 @@ export const Edge = ({
|
||||
targetTop,
|
||||
})
|
||||
return computeEdgePath(anchorsPosition)
|
||||
}, [sourceBlock, sourceTop, step, targetBlock, targetTop])
|
||||
}, [sourceBlock, sourceTop, targetBlock, targetTop])
|
||||
|
||||
if (sourceTop === 0) return <></>
|
||||
return (
|
||||
|
@ -1,39 +1,11 @@
|
||||
import { chakra } from '@chakra-ui/system'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import { ChoiceItem, ConditionStep } from 'models'
|
||||
import React, { useMemo } from 'react'
|
||||
import { isConditionStep, isDefined, isSingleChoiceInput } from 'utils'
|
||||
import React from 'react'
|
||||
import { DrawingEdge } from './DrawingEdge'
|
||||
import { Edge, EdgeType } from './Edge'
|
||||
import { Edge } from './Edge'
|
||||
|
||||
export const Edges = () => {
|
||||
const { typebot } = useTypebot()
|
||||
const stepIdsWithTarget: string[] = useMemo(() => {
|
||||
if (!typebot) return []
|
||||
return typebot.steps.allIds.filter((stepId) =>
|
||||
isDefined(typebot.steps.byId[stepId].target)
|
||||
)
|
||||
}, [typebot])
|
||||
const singleChoiceItemsWithTarget: ChoiceItem[] = useMemo(() => {
|
||||
if (!typebot) return []
|
||||
return typebot.choiceItems.allIds
|
||||
.filter(
|
||||
(itemId) =>
|
||||
isDefined(typebot.choiceItems.byId[itemId].target) &&
|
||||
isSingleChoiceInput(
|
||||
typebot.steps.byId[typebot.choiceItems.byId[itemId].stepId]
|
||||
)
|
||||
)
|
||||
.map((itemId) => typebot.choiceItems.byId[itemId])
|
||||
}, [typebot])
|
||||
const conditionStepIdsWithTarget = useMemo(
|
||||
() =>
|
||||
typebot?.steps.allIds.filter((stepId) => {
|
||||
const step = typebot.steps.byId[stepId]
|
||||
return isConditionStep(step) && (step.trueTarget || step.falseTarget)
|
||||
}),
|
||||
[typebot?.steps.allIds, typebot?.steps.byId]
|
||||
)
|
||||
|
||||
return (
|
||||
<chakra.svg
|
||||
@ -45,19 +17,8 @@ export const Edges = () => {
|
||||
top="0"
|
||||
>
|
||||
<DrawingEdge />
|
||||
{stepIdsWithTarget.map((stepId) => (
|
||||
<Edge key={stepId} stepId={stepId} type={EdgeType.STEP} />
|
||||
))}
|
||||
{singleChoiceItemsWithTarget.map((item) => (
|
||||
<Edge
|
||||
key={item.id}
|
||||
stepId={item.stepId}
|
||||
item={item}
|
||||
type={EdgeType.CHOICE_ITEM}
|
||||
/>
|
||||
))}
|
||||
{conditionStepIdsWithTarget?.map((stepId) => (
|
||||
<ConditionStepEdges key={stepId} stepId={stepId} />
|
||||
{typebot?.edges.allIds.map((edgeId) => (
|
||||
<Edge key={edgeId} edgeId={edgeId} />
|
||||
))}
|
||||
<marker
|
||||
id={'arrow'}
|
||||
@ -92,18 +53,3 @@ export const Edges = () => {
|
||||
</chakra.svg>
|
||||
)
|
||||
}
|
||||
|
||||
const ConditionStepEdges = ({ stepId }: { stepId: string }) => {
|
||||
const { typebot } = useTypebot()
|
||||
const step = typebot?.steps.byId[stepId] as ConditionStep
|
||||
return (
|
||||
<>
|
||||
{step.trueTarget && (
|
||||
<Edge type={EdgeType.CONDITION_TRUE} stepId={stepId} />
|
||||
)}
|
||||
{step.falseTarget && (
|
||||
<Edge type={EdgeType.CONDITION_FALSE} stepId={stepId} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
|
||||
export const PreviewDrawer = () => {
|
||||
const { typebot } = useTypebot()
|
||||
const { setRightPanel } = useEditor()
|
||||
const { previewingIds, setPreviewingIds } = useGraph()
|
||||
const { setPreviewingEdgeId } = useGraph()
|
||||
const [isResizing, setIsResizing] = useState(false)
|
||||
const [width, setWidth] = useState(500)
|
||||
const [isResizeHandleVisible, setIsResizeHandleVisible] = useState(false)
|
||||
@ -45,13 +45,7 @@ export const PreviewDrawer = () => {
|
||||
}
|
||||
useEventListener('mouseup', handleMouseUp)
|
||||
|
||||
const handleNewBlockVisible = (targetId: string) =>
|
||||
setPreviewingIds({
|
||||
sourceId: !previewingIds.sourceId
|
||||
? typebot?.blocks.allIds[0]
|
||||
: previewingIds.targetId,
|
||||
targetId: targetId,
|
||||
})
|
||||
const handleNewBlockVisible = (edgeId: string) => setPreviewingEdgeId(edgeId)
|
||||
|
||||
const handleRestartClick = () => setRestartKey((key) => key + 1)
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
import {
|
||||
HStack,
|
||||
IconButton,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputProps,
|
||||
InputRightElement,
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
@ -55,6 +54,7 @@ export const InputWithVariableButton = ({
|
||||
if (!inputRef.current) return
|
||||
inputRef.current.selectionStart = inputRef.current.selectionEnd =
|
||||
carretPosition + `{{${variable.name}}}`.length
|
||||
setCarretPosition(inputRef.current.selectionStart)
|
||||
}, 100)
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ export const InputWithVariableButton = ({
|
||||
setValue(e.target.value)
|
||||
|
||||
return (
|
||||
<InputGroup>
|
||||
<HStack>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
onKeyUp={handleKeyUp}
|
||||
@ -77,26 +77,24 @@ export const InputWithVariableButton = ({
|
||||
{...props}
|
||||
bgColor={'white'}
|
||||
/>
|
||||
<InputRightElement>
|
||||
<Popover matchWidth isLazy closeOnBlur={false}>
|
||||
<PopoverTrigger>
|
||||
<IconButton
|
||||
aria-label="Insert a variable"
|
||||
icon={<UserIcon />}
|
||||
size="sm"
|
||||
pos="relative"
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent w="full">
|
||||
<VariableSearchInput
|
||||
onSelectVariable={handleVariableSelected}
|
||||
placeholder="Search for a variable"
|
||||
shadow="lg"
|
||||
isDefaultOpen
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
<Popover matchWidth isLazy>
|
||||
<PopoverTrigger>
|
||||
<IconButton
|
||||
aria-label="Insert a variable"
|
||||
icon={<UserIcon />}
|
||||
pos="relative"
|
||||
ml="2"
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent w="full">
|
||||
<VariableSearchInput
|
||||
onSelectVariable={handleVariableSelected}
|
||||
placeholder="Search for a variable"
|
||||
shadow="lg"
|
||||
isDefaultOpen
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import {
|
||||
Button,
|
||||
InputProps,
|
||||
IconButton,
|
||||
Portal,
|
||||
} from '@chakra-ui/react'
|
||||
import { PlusIcon, TrashIcon } from 'assets/icons'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
@ -98,8 +97,8 @@ export const VariableSearchInput = ({
|
||||
isOpen={isOpen}
|
||||
initialFocusRef={inputRef}
|
||||
matchWidth
|
||||
offset={[0, 0]}
|
||||
isLazy
|
||||
offset={[0, 2]}
|
||||
>
|
||||
<PopoverTrigger>
|
||||
<Input
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Block, Step, Table, Target } from 'models'
|
||||
import { Block, Source, Step, Table, Target } from 'models'
|
||||
import {
|
||||
createContext,
|
||||
Dispatch,
|
||||
@ -43,19 +43,10 @@ export type Node = Omit<Block, 'steps'> & {
|
||||
const graphPositionDefaultValue = { x: 400, y: 100, scale: 1 }
|
||||
|
||||
export type ConnectingIds = {
|
||||
source: ConnectingSourceIds
|
||||
source: Source
|
||||
target?: Target
|
||||
}
|
||||
|
||||
export type ConnectingSourceIds = {
|
||||
blockId: string
|
||||
stepId: string
|
||||
choiceItemId?: string
|
||||
conditionType?: 'true' | 'false'
|
||||
}
|
||||
|
||||
type PreviewingIdsProps = { sourceId?: string; targetId?: string }
|
||||
|
||||
type StepId = string
|
||||
type NodeId = string
|
||||
export type Endpoint = {
|
||||
@ -68,8 +59,8 @@ const graphContext = createContext<{
|
||||
setGraphPosition: Dispatch<SetStateAction<Position>>
|
||||
connectingIds: ConnectingIds | null
|
||||
setConnectingIds: Dispatch<SetStateAction<ConnectingIds | null>>
|
||||
previewingIds: PreviewingIdsProps
|
||||
setPreviewingIds: Dispatch<SetStateAction<PreviewingIdsProps>>
|
||||
previewingEdgeId?: string
|
||||
setPreviewingEdgeId: Dispatch<SetStateAction<string | undefined>>
|
||||
sourceEndpoints: Table<Endpoint>
|
||||
addSourceEndpoint: (endpoint: Endpoint) => void
|
||||
targetEndpoints: Table<Endpoint>
|
||||
@ -84,7 +75,7 @@ const graphContext = createContext<{
|
||||
export const GraphProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [graphPosition, setGraphPosition] = useState(graphPositionDefaultValue)
|
||||
const [connectingIds, setConnectingIds] = useState<ConnectingIds | null>(null)
|
||||
const [previewingIds, setPreviewingIds] = useState<PreviewingIdsProps>({})
|
||||
const [previewingEdgeId, setPreviewingEdgeId] = useState<string>()
|
||||
const [sourceEndpoints, setSourceEndpoints] = useState<Table<Endpoint>>({
|
||||
byId: {},
|
||||
allIds: [],
|
||||
@ -115,8 +106,8 @@ export const GraphProvider = ({ children }: { children: ReactNode }) => {
|
||||
setGraphPosition,
|
||||
connectingIds,
|
||||
setConnectingIds,
|
||||
previewingIds,
|
||||
setPreviewingIds,
|
||||
previewingEdgeId,
|
||||
setPreviewingEdgeId,
|
||||
sourceEndpoints,
|
||||
targetEndpoints,
|
||||
addSourceEndpoint,
|
||||
|
@ -28,6 +28,7 @@ import { useImmer, Updater } from 'use-immer'
|
||||
import { stepsAction, StepsActions } from './actions/steps'
|
||||
import { choiceItemsAction, ChoiceItemsActions } from './actions/choiceItems'
|
||||
import { variablesAction, VariablesActions } from './actions/variables'
|
||||
import { edgesAction, EdgesActions } from './actions/edges'
|
||||
|
||||
type UpdateTypebotPayload = Partial<{
|
||||
theme: Theme
|
||||
@ -50,7 +51,8 @@ const typebotContext = createContext<
|
||||
} & BlocksActions &
|
||||
StepsActions &
|
||||
ChoiceItemsActions &
|
||||
VariablesActions
|
||||
VariablesActions &
|
||||
EdgesActions
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
>({})
|
||||
@ -211,6 +213,7 @@ export const TypebotContext = ({
|
||||
...stepsAction(setLocalTypebot as Updater<Typebot>),
|
||||
...choiceItemsAction(setLocalTypebot as Updater<Typebot>),
|
||||
...variablesAction(setLocalTypebot as Updater<Typebot>),
|
||||
...edgesAction(setLocalTypebot as Updater<Typebot>),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -3,6 +3,7 @@ import { WritableDraft } from 'immer/dist/internal'
|
||||
import { Block, DraggableStep, DraggableStepType, Typebot } from 'models'
|
||||
import { parseNewBlock } from 'services/typebots'
|
||||
import { Updater } from 'use-immer'
|
||||
import { deleteEdgeDraft } from './edges'
|
||||
import { createStepDraft, deleteStepDraft } from './steps'
|
||||
|
||||
export type BlocksActions = {
|
||||
@ -44,6 +45,7 @@ export const blocksActions = (setTypebot: Updater<Typebot>): BlocksActions => ({
|
||||
deleteBlock: (blockId: string) =>
|
||||
setTypebot((typebot) => {
|
||||
deleteStepsInsideBlock(typebot, blockId)
|
||||
deleteAssociatedEdges(typebot, blockId)
|
||||
deleteBlockDraft(typebot)(blockId)
|
||||
}),
|
||||
})
|
||||
@ -55,6 +57,16 @@ export const removeEmptyBlocks = (typebot: WritableDraft<Typebot>) => {
|
||||
emptyBlockIds.forEach(deleteBlockDraft(typebot))
|
||||
}
|
||||
|
||||
const deleteAssociatedEdges = (
|
||||
typebot: WritableDraft<Typebot>,
|
||||
blockId: string
|
||||
) => {
|
||||
typebot.edges.allIds.forEach((edgeId) => {
|
||||
if (typebot.edges.byId[edgeId].to.blockId === blockId)
|
||||
deleteEdgeDraft(typebot, edgeId)
|
||||
})
|
||||
}
|
||||
|
||||
const deleteStepsInsideBlock = (
|
||||
typebot: WritableDraft<Typebot>,
|
||||
blockId: string
|
||||
|
69
apps/builder/contexts/TypebotContext/actions/edges.ts
Normal file
69
apps/builder/contexts/TypebotContext/actions/edges.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { Typebot, Edge, ConditionStep } from 'models'
|
||||
import { Updater } from 'use-immer'
|
||||
import { WritableDraft } from 'immer/dist/types/types-external'
|
||||
import { generate } from 'short-uuid'
|
||||
|
||||
export type EdgesActions = {
|
||||
createEdge: (edge: Omit<Edge, 'id'>) => void
|
||||
updateEdge: (edgeId: string, updates: Partial<Omit<Edge, 'id'>>) => void
|
||||
deleteEdge: (edgeId: string) => void
|
||||
}
|
||||
|
||||
export const edgesAction = (setTypebot: Updater<Typebot>): EdgesActions => ({
|
||||
createEdge: (edge: Omit<Edge, 'id'>) => {
|
||||
setTypebot((typebot) => {
|
||||
const newEdge = {
|
||||
...edge,
|
||||
id: generate(),
|
||||
}
|
||||
if (edge.from.nodeId) {
|
||||
deleteEdgeDraft(
|
||||
typebot,
|
||||
typebot.choiceItems.byId[edge.from.nodeId].edgeId
|
||||
)
|
||||
typebot.choiceItems.byId[edge.from.nodeId].edgeId = newEdge.id
|
||||
} else if (edge.from.conditionType === 'true') {
|
||||
deleteEdgeDraft(
|
||||
typebot,
|
||||
(typebot.steps.byId[edge.from.stepId] as ConditionStep).trueEdgeId
|
||||
)
|
||||
;(typebot.steps.byId[edge.from.stepId] as ConditionStep).trueEdgeId =
|
||||
newEdge.id
|
||||
} else if (edge.from.conditionType === 'false') {
|
||||
deleteEdgeDraft(
|
||||
typebot,
|
||||
(typebot.steps.byId[edge.from.stepId] as ConditionStep).falseEdgeId
|
||||
)
|
||||
;(typebot.steps.byId[edge.from.stepId] as ConditionStep).falseEdgeId =
|
||||
newEdge.id
|
||||
} else {
|
||||
deleteEdgeDraft(typebot, typebot.steps.byId[edge.from.stepId].edgeId)
|
||||
typebot.steps.byId[edge.from.stepId].edgeId = newEdge.id
|
||||
}
|
||||
typebot.edges.byId[newEdge.id] = newEdge
|
||||
typebot.edges.allIds.push(newEdge.id)
|
||||
})
|
||||
},
|
||||
updateEdge: (edgeId: string, updates: Partial<Omit<Edge, 'id'>>) =>
|
||||
setTypebot((typebot) => {
|
||||
typebot.edges.byId[edgeId] = {
|
||||
...typebot.edges.byId[edgeId],
|
||||
...updates,
|
||||
}
|
||||
}),
|
||||
deleteEdge: (edgeId: string) => {
|
||||
setTypebot((typebot) => {
|
||||
deleteEdgeDraft(typebot, edgeId)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
export const deleteEdgeDraft = (
|
||||
typebot: WritableDraft<Typebot>,
|
||||
edgeId?: string
|
||||
) => {
|
||||
if (!edgeId) return
|
||||
delete typebot.edges.byId[edgeId]
|
||||
const index = typebot.edges.allIds.indexOf(edgeId)
|
||||
if (index !== -1) typebot.edges.allIds.splice(index, 1)
|
||||
}
|
@ -11,6 +11,7 @@ import { removeEmptyBlocks } from './blocks'
|
||||
import { WritableDraft } from 'immer/dist/types/types-external'
|
||||
import { createChoiceItemDraft, deleteChoiceItemDraft } from './choiceItems'
|
||||
import { isChoiceInput } from 'utils'
|
||||
import { deleteEdgeDraft } from './edges'
|
||||
|
||||
export type StepsActions = {
|
||||
createStep: (
|
||||
@ -50,12 +51,23 @@ export const stepsAction = (setTypebot: Updater<Typebot>): StepsActions => ({
|
||||
setTypebot((typebot) => {
|
||||
const step = typebot.steps.byId[stepId]
|
||||
if (isChoiceInput(step)) deleteChoiceItemsInsideStep(typebot, step)
|
||||
deleteAssociatedEdges(typebot, stepId)
|
||||
removeStepIdFromBlock(typebot, stepId)
|
||||
deleteStepDraft(typebot, stepId)
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const deleteAssociatedEdges = (
|
||||
typebot: WritableDraft<Typebot>,
|
||||
stepId: string
|
||||
) => {
|
||||
typebot.edges.allIds.forEach((edgeId) => {
|
||||
if (typebot.edges.byId[edgeId].from.stepId === stepId)
|
||||
deleteEdgeDraft(typebot, edgeId)
|
||||
})
|
||||
}
|
||||
|
||||
const removeStepIdFromBlock = (
|
||||
typebot: WritableDraft<Typebot>,
|
||||
stepId: string
|
||||
|
@ -1,71 +1,96 @@
|
||||
{
|
||||
"id": "typebot4",
|
||||
"createdAt": "2022-01-17T14:37:01.826Z",
|
||||
"updatedAt": "2022-01-17T14:37:01.826Z",
|
||||
"id": "ckylszb9z0354z31a623dg7ji",
|
||||
"createdAt": "2022-01-19T17:12:27.863Z",
|
||||
"updatedAt": "2022-01-19T17:12:27.863Z",
|
||||
"name": "My typebot",
|
||||
"ownerId": "user2",
|
||||
"ownerId": "ckylsz8yy0335z31amvq0jwtt",
|
||||
"publishedTypebotId": null,
|
||||
"folderId": null,
|
||||
"blocks": {
|
||||
"byId": {
|
||||
"bec5A5bLwenmZpCJc8FRaM": {
|
||||
"id": "bec5A5bLwenmZpCJc8FRaM",
|
||||
"j24wz82YG3rjXMgrmCiTLy": {
|
||||
"id": "j24wz82YG3rjXMgrmCiTLy",
|
||||
"title": "Start",
|
||||
"stepIds": ["uDMB7a2ucg17WGvbQJjeRn"],
|
||||
"stepIds": ["1NdXPCiRicqDA8k4JfnXfi"],
|
||||
"graphCoordinates": { "x": 0, "y": 0 }
|
||||
},
|
||||
"bcyiT7P6E99YnHKnpxs4Yux": {
|
||||
"id": "bcyiT7P6E99YnHKnpxs4Yux",
|
||||
"bmaKTUXkT2cc3wtKfK7ra71": {
|
||||
"id": "bmaKTUXkT2cc3wtKfK7ra71",
|
||||
"title": "Block #2",
|
||||
"stepIds": ["step1"],
|
||||
"graphCoordinates": { "x": 411, "y": 108 }
|
||||
"graphCoordinates": { "x": 175, "y": 197 },
|
||||
"stepIds": ["spHxPWbSqkVZW9gqH86ovC5"]
|
||||
},
|
||||
"bgpNxHtBBXWrP1QMe2A8hZ9": {
|
||||
"id": "bgpNxHtBBXWrP1QMe2A8hZ9",
|
||||
"bnt8fM5Wgc8gBg2iSmUcfJu": {
|
||||
"id": "bnt8fM5Wgc8gBg2iSmUcfJu",
|
||||
"title": "Block #3",
|
||||
"graphCoordinates": { "x": 1, "y": 236 },
|
||||
"stepIds": ["sj72oDhJEe4N92KTt64GKWs"]
|
||||
"graphCoordinates": { "x": 504, "y": 347 },
|
||||
"stepIds": ["siPoEE9H27hVHqykth3a7Kj"]
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"bec5A5bLwenmZpCJc8FRaM",
|
||||
"bcyiT7P6E99YnHKnpxs4Yux",
|
||||
"bgpNxHtBBXWrP1QMe2A8hZ9"
|
||||
"j24wz82YG3rjXMgrmCiTLy",
|
||||
"bmaKTUXkT2cc3wtKfK7ra71",
|
||||
"bnt8fM5Wgc8gBg2iSmUcfJu"
|
||||
]
|
||||
},
|
||||
"steps": {
|
||||
"byId": {
|
||||
"step1": {
|
||||
"id": "step1",
|
||||
"type": "Google Sheets",
|
||||
"blockId": "bcyiT7P6E99YnHKnpxs4Yux"
|
||||
},
|
||||
"uDMB7a2ucg17WGvbQJjeRn": {
|
||||
"id": "uDMB7a2ucg17WGvbQJjeRn",
|
||||
"1NdXPCiRicqDA8k4JfnXfi": {
|
||||
"id": "1NdXPCiRicqDA8k4JfnXfi",
|
||||
"type": "start",
|
||||
"label": "Start",
|
||||
"target": { "blockId": "bgpNxHtBBXWrP1QMe2A8hZ9" },
|
||||
"blockId": "bec5A5bLwenmZpCJc8FRaM"
|
||||
"blockId": "j24wz82YG3rjXMgrmCiTLy",
|
||||
"edgeId": "benDCcLMUWNvi6Fg6CXE9H"
|
||||
},
|
||||
"sj72oDhJEe4N92KTt64GKWs": {
|
||||
"id": "sj72oDhJEe4N92KTt64GKWs",
|
||||
"blockId": "bgpNxHtBBXWrP1QMe2A8hZ9",
|
||||
"spHxPWbSqkVZW9gqH86ovC5": {
|
||||
"id": "spHxPWbSqkVZW9gqH86ovC5",
|
||||
"blockId": "bmaKTUXkT2cc3wtKfK7ra71",
|
||||
"type": "email input",
|
||||
"target": { "blockId": "bcyiT7P6E99YnHKnpxs4Yux" },
|
||||
"options": { "variableId": "8H3aQsNji2Gyfpp3RPozNN" }
|
||||
"options": { "variableId": "oexLr4sJQNVdSnYCGgGRB3" },
|
||||
"edgeId": "6Tax9rw7L8kmRn9JRD2Mrg"
|
||||
},
|
||||
"siPoEE9H27hVHqykth3a7Kj": {
|
||||
"id": "siPoEE9H27hVHqykth3a7Kj",
|
||||
"blockId": "bnt8fM5Wgc8gBg2iSmUcfJu",
|
||||
"type": "Google Sheets"
|
||||
}
|
||||
},
|
||||
"allIds": ["uDMB7a2ucg17WGvbQJjeRn", "step1", "sj72oDhJEe4N92KTt64GKWs"]
|
||||
"allIds": [
|
||||
"1NdXPCiRicqDA8k4JfnXfi",
|
||||
"spHxPWbSqkVZW9gqH86ovC5",
|
||||
"siPoEE9H27hVHqykth3a7Kj"
|
||||
]
|
||||
},
|
||||
"choiceItems": { "byId": {}, "allIds": [] },
|
||||
"variables": {
|
||||
"byId": {
|
||||
"8H3aQsNji2Gyfpp3RPozNN": {
|
||||
"id": "8H3aQsNji2Gyfpp3RPozNN",
|
||||
"oexLr4sJQNVdSnYCGgGRB3": {
|
||||
"id": "oexLr4sJQNVdSnYCGgGRB3",
|
||||
"name": "Email"
|
||||
}
|
||||
},
|
||||
"allIds": ["8H3aQsNji2Gyfpp3RPozNN"]
|
||||
"allIds": ["oexLr4sJQNVdSnYCGgGRB3"]
|
||||
},
|
||||
"edges": {
|
||||
"byId": {
|
||||
"benDCcLMUWNvi6Fg6CXE9H": {
|
||||
"from": {
|
||||
"blockId": "j24wz82YG3rjXMgrmCiTLy",
|
||||
"stepId": "1NdXPCiRicqDA8k4JfnXfi"
|
||||
},
|
||||
"to": { "blockId": "bmaKTUXkT2cc3wtKfK7ra71" },
|
||||
"id": "benDCcLMUWNvi6Fg6CXE9H"
|
||||
},
|
||||
"6Tax9rw7L8kmRn9JRD2Mrg": {
|
||||
"from": {
|
||||
"blockId": "bmaKTUXkT2cc3wtKfK7ra71",
|
||||
"stepId": "spHxPWbSqkVZW9gqH86ovC5"
|
||||
},
|
||||
"to": { "blockId": "bnt8fM5Wgc8gBg2iSmUcfJu" },
|
||||
"id": "6Tax9rw7L8kmRn9JRD2Mrg"
|
||||
}
|
||||
},
|
||||
"allIds": ["benDCcLMUWNvi6Fg6CXE9H", "6Tax9rw7L8kmRn9JRD2Mrg"]
|
||||
},
|
||||
"theme": {
|
||||
"general": {
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
@ -1,70 +1,70 @@
|
||||
{
|
||||
"id": "typebot4",
|
||||
"createdAt": "2022-01-17T14:37:01.826Z",
|
||||
"updatedAt": "2022-01-17T14:37:01.826Z",
|
||||
"id": "ckyltevlb0559z31an8cmkyrp",
|
||||
"createdAt": "2022-01-19T17:24:34.031Z",
|
||||
"updatedAt": "2022-01-19T17:24:34.031Z",
|
||||
"name": "My typebot",
|
||||
"ownerId": "user2",
|
||||
"ownerId": "ckyltekzq0533z31ad8opmacz",
|
||||
"publishedTypebotId": null,
|
||||
"folderId": null,
|
||||
"blocks": {
|
||||
"byId": {
|
||||
"bec5A5bLwenmZpCJc8FRaM": {
|
||||
"id": "bec5A5bLwenmZpCJc8FRaM",
|
||||
"kPupUcEn7TcBGKHUpgK2Q5": {
|
||||
"id": "kPupUcEn7TcBGKHUpgK2Q5",
|
||||
"title": "Start",
|
||||
"stepIds": ["uDMB7a2ucg17WGvbQJjeRn"],
|
||||
"stepIds": ["nP5oWm7PxigMupyWpPLq24"],
|
||||
"graphCoordinates": { "x": 0, "y": 0 }
|
||||
},
|
||||
"bcyiT7P6E99YnHKnpxs4Yux": {
|
||||
"id": "bcyiT7P6E99YnHKnpxs4Yux",
|
||||
"bi4J5fv9DFn1zPSqGf8eRht": {
|
||||
"id": "bi4J5fv9DFn1zPSqGf8eRht",
|
||||
"title": "Block #2",
|
||||
"stepIds": ["step1"],
|
||||
"graphCoordinates": { "x": 411, "y": 108 }
|
||||
"graphCoordinates": { "x": 104, "y": 201 },
|
||||
"stepIds": ["scH9qdXwFfAScoavj6UzQNT"]
|
||||
},
|
||||
"bgpNxHtBBXWrP1QMe2A8hZ9": {
|
||||
"id": "bgpNxHtBBXWrP1QMe2A8hZ9",
|
||||
"bwqGhUsa2SKaxXSrKtapVc8": {
|
||||
"id": "bwqGhUsa2SKaxXSrKtapVc8",
|
||||
"title": "Block #3",
|
||||
"stepIds": ["sj72oDhJEe4N92KTt64GKWs"],
|
||||
"graphCoordinates": { "x": 1, "y": 236 }
|
||||
"graphCoordinates": { "x": 458, "y": 292 },
|
||||
"stepIds": ["shZdc8Qw48domEbS7vLW5eN"]
|
||||
},
|
||||
"buwMV9tx2EFcMbRkNTELt3J": {
|
||||
"id": "buwMV9tx2EFcMbRkNTELt3J",
|
||||
"bqmgS9hLUu2RA2oxVv7hMka": {
|
||||
"id": "bqmgS9hLUu2RA2oxVv7hMka",
|
||||
"title": "Block #4",
|
||||
"graphCoordinates": { "x": 441, "y": 330 },
|
||||
"stepIds": ["s2R2bk7qfSRVgTyRmPhVw7p"]
|
||||
"graphCoordinates": { "x": 102, "y": 432 },
|
||||
"stepIds": ["s4z6G3MGAyhXChU9jakQWer"]
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"bec5A5bLwenmZpCJc8FRaM",
|
||||
"bcyiT7P6E99YnHKnpxs4Yux",
|
||||
"bgpNxHtBBXWrP1QMe2A8hZ9",
|
||||
"buwMV9tx2EFcMbRkNTELt3J"
|
||||
"kPupUcEn7TcBGKHUpgK2Q5",
|
||||
"bi4J5fv9DFn1zPSqGf8eRht",
|
||||
"bwqGhUsa2SKaxXSrKtapVc8",
|
||||
"bqmgS9hLUu2RA2oxVv7hMka"
|
||||
]
|
||||
},
|
||||
"steps": {
|
||||
"byId": {
|
||||
"step1": {
|
||||
"id": "step1",
|
||||
"type": "Google Sheets",
|
||||
"blockId": "bcyiT7P6E99YnHKnpxs4Yux",
|
||||
"target": { "blockId": "buwMV9tx2EFcMbRkNTELt3J" }
|
||||
},
|
||||
"uDMB7a2ucg17WGvbQJjeRn": {
|
||||
"id": "uDMB7a2ucg17WGvbQJjeRn",
|
||||
"nP5oWm7PxigMupyWpPLq24": {
|
||||
"id": "nP5oWm7PxigMupyWpPLq24",
|
||||
"type": "start",
|
||||
"label": "Start",
|
||||
"target": { "blockId": "bgpNxHtBBXWrP1QMe2A8hZ9" },
|
||||
"blockId": "bec5A5bLwenmZpCJc8FRaM"
|
||||
"blockId": "kPupUcEn7TcBGKHUpgK2Q5",
|
||||
"edgeId": "kCLXGLpiM2F6pn4wFYc1f5"
|
||||
},
|
||||
"sj72oDhJEe4N92KTt64GKWs": {
|
||||
"id": "sj72oDhJEe4N92KTt64GKWs",
|
||||
"scH9qdXwFfAScoavj6UzQNT": {
|
||||
"id": "scH9qdXwFfAScoavj6UzQNT",
|
||||
"blockId": "bi4J5fv9DFn1zPSqGf8eRht",
|
||||
"type": "email input",
|
||||
"target": { "blockId": "bcyiT7P6E99YnHKnpxs4Yux" },
|
||||
"blockId": "bgpNxHtBBXWrP1QMe2A8hZ9",
|
||||
"options": { "variableId": "8H3aQsNji2Gyfpp3RPozNN" }
|
||||
"options": { "variableId": "ifXp66N1meAtoUDcbqWxuD" },
|
||||
"edgeId": "7Czn5hJFUfpkRGtwGnKxtt"
|
||||
},
|
||||
"s2R2bk7qfSRVgTyRmPhVw7p": {
|
||||
"id": "s2R2bk7qfSRVgTyRmPhVw7p",
|
||||
"blockId": "buwMV9tx2EFcMbRkNTELt3J",
|
||||
"shZdc8Qw48domEbS7vLW5eN": {
|
||||
"id": "shZdc8Qw48domEbS7vLW5eN",
|
||||
"blockId": "bwqGhUsa2SKaxXSrKtapVc8",
|
||||
"type": "Google Sheets",
|
||||
"edgeId": "eMhGokwHMDRDrynSvpiRje"
|
||||
},
|
||||
"s4z6G3MGAyhXChU9jakQWer": {
|
||||
"id": "s4z6G3MGAyhXChU9jakQWer",
|
||||
"blockId": "bqmgS9hLUu2RA2oxVv7hMka",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>Your name is: {{First name}} {{Last name}}</div>",
|
||||
@ -81,21 +81,54 @@
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"uDMB7a2ucg17WGvbQJjeRn",
|
||||
"step1",
|
||||
"sj72oDhJEe4N92KTt64GKWs",
|
||||
"s2R2bk7qfSRVgTyRmPhVw7p"
|
||||
"nP5oWm7PxigMupyWpPLq24",
|
||||
"scH9qdXwFfAScoavj6UzQNT",
|
||||
"shZdc8Qw48domEbS7vLW5eN",
|
||||
"s4z6G3MGAyhXChU9jakQWer"
|
||||
]
|
||||
},
|
||||
"choiceItems": { "byId": {}, "allIds": [] },
|
||||
"variables": {
|
||||
"byId": {
|
||||
"8H3aQsNji2Gyfpp3RPozNN": {
|
||||
"id": "8H3aQsNji2Gyfpp3RPozNN",
|
||||
"ifXp66N1meAtoUDcbqWxuD": {
|
||||
"id": "ifXp66N1meAtoUDcbqWxuD",
|
||||
"name": "Email"
|
||||
}
|
||||
},
|
||||
"allIds": ["8H3aQsNji2Gyfpp3RPozNN"]
|
||||
"allIds": ["ifXp66N1meAtoUDcbqWxuD"]
|
||||
},
|
||||
"edges": {
|
||||
"byId": {
|
||||
"kCLXGLpiM2F6pn4wFYc1f5": {
|
||||
"from": {
|
||||
"blockId": "kPupUcEn7TcBGKHUpgK2Q5",
|
||||
"stepId": "nP5oWm7PxigMupyWpPLq24"
|
||||
},
|
||||
"to": { "blockId": "bi4J5fv9DFn1zPSqGf8eRht" },
|
||||
"id": "kCLXGLpiM2F6pn4wFYc1f5"
|
||||
},
|
||||
"7Czn5hJFUfpkRGtwGnKxtt": {
|
||||
"from": {
|
||||
"blockId": "bi4J5fv9DFn1zPSqGf8eRht",
|
||||
"stepId": "scH9qdXwFfAScoavj6UzQNT"
|
||||
},
|
||||
"to": { "blockId": "bwqGhUsa2SKaxXSrKtapVc8" },
|
||||
"id": "7Czn5hJFUfpkRGtwGnKxtt"
|
||||
},
|
||||
"eMhGokwHMDRDrynSvpiRje": {
|
||||
"from": {
|
||||
"blockId": "bwqGhUsa2SKaxXSrKtapVc8",
|
||||
"stepId": "shZdc8Qw48domEbS7vLW5eN"
|
||||
},
|
||||
"to": { "blockId": "bqmgS9hLUu2RA2oxVv7hMka" },
|
||||
"id": "eMhGokwHMDRDrynSvpiRje"
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"kCLXGLpiM2F6pn4wFYc1f5",
|
||||
"7Czn5hJFUfpkRGtwGnKxtt",
|
||||
"eMhGokwHMDRDrynSvpiRje"
|
||||
]
|
||||
},
|
||||
"theme": {
|
||||
"general": {
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
@ -1,110 +1,71 @@
|
||||
{
|
||||
"id": "typebot4",
|
||||
"createdAt": "2022-01-14T14:23:36.576Z",
|
||||
"updatedAt": "2022-01-14T14:23:36.576Z",
|
||||
"id": "ckylsd52p0114z31aobllswmu",
|
||||
"createdAt": "2022-01-19T16:55:13.393Z",
|
||||
"updatedAt": "2022-01-19T16:55:13.393Z",
|
||||
"name": "My typebot",
|
||||
"ownerId": "ckye5hs3e1801em1a0eodjj3f",
|
||||
"ownerId": "ckylsbdf60088z31ayqytest6",
|
||||
"publishedTypebotId": null,
|
||||
"folderId": null,
|
||||
"blocks": {
|
||||
"byId": {
|
||||
"1YV9MuAa6dd6eNxC5BipDZ": {
|
||||
"id": "1YV9MuAa6dd6eNxC5BipDZ",
|
||||
"2x83WHtEBkiv7pk7KgqJwZ": {
|
||||
"id": "2x83WHtEBkiv7pk7KgqJwZ",
|
||||
"title": "Start",
|
||||
"stepIds": ["3EzaqYRLFqFQFbCj2gQP3q"],
|
||||
"stepIds": ["1A76iZBgXG7hvkG2koCxe4"],
|
||||
"graphCoordinates": { "x": 0, "y": 0 }
|
||||
},
|
||||
"bnmeD9SVeGPhF4qvwKfxE8R": {
|
||||
"id": "bnmeD9SVeGPhF4qvwKfxE8R",
|
||||
"bwga7RwqQWbowdHph27DM1N": {
|
||||
"id": "bwga7RwqQWbowdHph27DM1N",
|
||||
"title": "Block #2",
|
||||
"stepIds": ["condition1", "condition2"],
|
||||
"graphCoordinates": { "x": 194, "y": 228 }
|
||||
"graphCoordinates": { "x": 78, "y": 224 },
|
||||
"stepIds": ["srwUKaUFFmehppJ2ZDqp4xG", "sxvzuo48GHi3AcAfmiFyYC1"]
|
||||
},
|
||||
"b3EaF53FGbQH5MhbBPUjDjb": {
|
||||
"id": "b3EaF53FGbQH5MhbBPUjDjb",
|
||||
"bu8whx817bJBG37FQrtD5dD": {
|
||||
"id": "bu8whx817bJBG37FQrtD5dD",
|
||||
"title": "Block #3",
|
||||
"graphCoordinates": { "x": 430, "y": 287 },
|
||||
"stepIds": ["ituVWW1AvQeVdFHTwsiVao", "5SLc4whZooZVUfr1bmTNSC"]
|
||||
},
|
||||
"b59jwmEdwZUvJszV394x44u": {
|
||||
"id": "b59jwmEdwZUvJszV394x44u",
|
||||
"title": "Block #4",
|
||||
"graphCoordinates": { "x": 375, "y": -39 },
|
||||
"stepIds": ["siBqadjM6AJXf25Ct4413dM", "sqTSo2heZ5vdfzJjNZfYUK5"]
|
||||
"graphCoordinates": { "x": 844, "y": 185 },
|
||||
"stepIds": ["sm1YcKTL9cQMCGywzo1wyBB"]
|
||||
},
|
||||
"b2Wjyg4MsqB5xhQYQPbPYkc": {
|
||||
"id": "b2Wjyg4MsqB5xhQYQPbPYkc",
|
||||
"title": "Block #4",
|
||||
"graphCoordinates": { "x": 712, "y": 186 },
|
||||
"stepIds": ["srFH8bxpJZuShgr2hmz4uhx"]
|
||||
},
|
||||
"b4KB24EywCVt3zX3opqMvYS": {
|
||||
"id": "b4KB24EywCVt3zX3opqMvYS",
|
||||
"baVF9HqhuSnLDZqY9eRPpcp": {
|
||||
"id": "baVF9HqhuSnLDZqY9eRPpcp",
|
||||
"title": "Block #5",
|
||||
"graphCoordinates": { "x": 700, "y": 340 },
|
||||
"stepIds": ["ssBZF1FgMDYZJTbmTzb8Uks"]
|
||||
"graphCoordinates": { "x": 841, "y": 356 },
|
||||
"stepIds": ["sb3o6J8Fybg6u8KuayKviJq"]
|
||||
},
|
||||
"bppe1zzyayc8ub14ozqJEXb": {
|
||||
"id": "bppe1zzyayc8ub14ozqJEXb",
|
||||
"b9aEH46RHuZWTdQwZJ6KBWR": {
|
||||
"id": "b9aEH46RHuZWTdQwZJ6KBWR",
|
||||
"title": "Block #6",
|
||||
"graphCoordinates": { "x": 713, "y": 491 },
|
||||
"stepIds": ["scQmWL2qGp1oXnEYdqjLcDv"]
|
||||
"graphCoordinates": { "x": 839, "y": 523 },
|
||||
"stepIds": ["scKogEJSTq4kPeHRhwTTjit"]
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"1YV9MuAa6dd6eNxC5BipDZ",
|
||||
"bnmeD9SVeGPhF4qvwKfxE8R",
|
||||
"b3EaF53FGbQH5MhbBPUjDjb",
|
||||
"b2Wjyg4MsqB5xhQYQPbPYkc",
|
||||
"b4KB24EywCVt3zX3opqMvYS",
|
||||
"bppe1zzyayc8ub14ozqJEXb"
|
||||
"2x83WHtEBkiv7pk7KgqJwZ",
|
||||
"bwga7RwqQWbowdHph27DM1N",
|
||||
"bu8whx817bJBG37FQrtD5dD",
|
||||
"b59jwmEdwZUvJszV394x44u",
|
||||
"baVF9HqhuSnLDZqY9eRPpcp",
|
||||
"b9aEH46RHuZWTdQwZJ6KBWR"
|
||||
]
|
||||
},
|
||||
"steps": {
|
||||
"byId": {
|
||||
"3EzaqYRLFqFQFbCj2gQP3q": {
|
||||
"id": "3EzaqYRLFqFQFbCj2gQP3q",
|
||||
"1A76iZBgXG7hvkG2koCxe4": {
|
||||
"id": "1A76iZBgXG7hvkG2koCxe4",
|
||||
"type": "start",
|
||||
"label": "Start",
|
||||
"target": { "blockId": "b3EaF53FGbQH5MhbBPUjDjb" },
|
||||
"blockId": "1YV9MuAa6dd6eNxC5BipDZ"
|
||||
"blockId": "2x83WHtEBkiv7pk7KgqJwZ",
|
||||
"edgeId": "jjNy2hYgrQgPS9EBMKA7MH"
|
||||
},
|
||||
"condition1": {
|
||||
"id": "condition1",
|
||||
"type": "Condition",
|
||||
"blockId": "bnmeD9SVeGPhF4qvwKfxE8R",
|
||||
"trueTarget": { "blockId": "b2Wjyg4MsqB5xhQYQPbPYkc" },
|
||||
"options": {
|
||||
"comparisons": {
|
||||
"byId": { "comparison1": { "comparisonOperator": "Equal to" } },
|
||||
"allIds": ["comparison1"]
|
||||
},
|
||||
"logicalOperator": "AND"
|
||||
}
|
||||
},
|
||||
"condition2": {
|
||||
"id": "condition2",
|
||||
"blockId": "bnmeD9SVeGPhF4qvwKfxE8R",
|
||||
"type": "Condition",
|
||||
"trueTarget": { "blockId": "b4KB24EywCVt3zX3opqMvYS" },
|
||||
"falseTarget": { "blockId": "bppe1zzyayc8ub14ozqJEXb" },
|
||||
"options": {
|
||||
"comparisons": {
|
||||
"byId": { "comparison1": { "comparisonOperator": "Equal to" } },
|
||||
"allIds": ["comparison1"]
|
||||
},
|
||||
"logicalOperator": "AND"
|
||||
}
|
||||
},
|
||||
"sqTSo2heZ5vdfzJjNZfYUK5": {
|
||||
"id": "sqTSo2heZ5vdfzJjNZfYUK5",
|
||||
"blockId": "b3EaF53FGbQH5MhbBPUjDjb",
|
||||
"type": "number input",
|
||||
"options": {
|
||||
"variableId": "icVxLRv1sQnPyNwRX9cjK9",
|
||||
"min": 0,
|
||||
"max": 150,
|
||||
"labels": { "placeholder": "Type your age..." }
|
||||
},
|
||||
"target": { "blockId": "bnmeD9SVeGPhF4qvwKfxE8R" }
|
||||
},
|
||||
"siBqadjM6AJXf25Ct4413dM": {
|
||||
"id": "siBqadjM6AJXf25Ct4413dM",
|
||||
"blockId": "b3EaF53FGbQH5MhbBPUjDjb",
|
||||
"srwUKaUFFmehppJ2ZDqp4xG": {
|
||||
"id": "srwUKaUFFmehppJ2ZDqp4xG",
|
||||
"blockId": "bwga7RwqQWbowdHph27DM1N",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>How old are you?</div>",
|
||||
@ -114,39 +75,78 @@
|
||||
"plainText": "How old are you?"
|
||||
}
|
||||
},
|
||||
"srFH8bxpJZuShgr2hmz4uhx": {
|
||||
"id": "srFH8bxpJZuShgr2hmz4uhx",
|
||||
"blockId": "b2Wjyg4MsqB5xhQYQPbPYkc",
|
||||
"sxvzuo48GHi3AcAfmiFyYC1": {
|
||||
"id": "sxvzuo48GHi3AcAfmiFyYC1",
|
||||
"blockId": "bwga7RwqQWbowdHph27DM1N",
|
||||
"type": "number input",
|
||||
"options": { "variableId": "dEz689uVm8AxUM8TrbQd2t" },
|
||||
"edgeId": "7mcWaWohM9zGtLX8ZSnqFy"
|
||||
},
|
||||
"ituVWW1AvQeVdFHTwsiVao": {
|
||||
"id": "ituVWW1AvQeVdFHTwsiVao",
|
||||
"blockId": "bu8whx817bJBG37FQrtD5dD",
|
||||
"type": "Condition",
|
||||
"options": {
|
||||
"comparisons": {
|
||||
"byId": {
|
||||
"ituVWW1AvQeVdFHTwsiVao": {
|
||||
"id": "ituVWW1AvQeVdFHTwsiVao",
|
||||
"comparisonOperator": "Equal to",
|
||||
"variableId": "ixdebNyG6gYgmowpkioQtG"
|
||||
}
|
||||
},
|
||||
"allIds": ["ituVWW1AvQeVdFHTwsiVao"]
|
||||
},
|
||||
"logicalOperator": "AND"
|
||||
},
|
||||
"trueEdgeId": "iBPsFyBsPv6Rbdfo2QdJyi"
|
||||
},
|
||||
"5SLc4whZooZVUfr1bmTNSC": {
|
||||
"id": "5SLc4whZooZVUfr1bmTNSC",
|
||||
"blockId": "bu8whx817bJBG37FQrtD5dD",
|
||||
"type": "Condition",
|
||||
"options": {
|
||||
"comparisons": {
|
||||
"byId": {
|
||||
"5SLc4whZooZVUfr1bmTNSC": {
|
||||
"id": "5SLc4whZooZVUfr1bmTNSC",
|
||||
"comparisonOperator": "Equal to"
|
||||
}
|
||||
},
|
||||
"allIds": ["5SLc4whZooZVUfr1bmTNSC"]
|
||||
},
|
||||
"logicalOperator": "AND"
|
||||
},
|
||||
"trueEdgeId": "354PJ2jD5U3J2APqLsPJrp",
|
||||
"falseEdgeId": "94bmeCLigEUUpWYw2xsAVB"
|
||||
},
|
||||
"sm1YcKTL9cQMCGywzo1wyBB": {
|
||||
"id": "sm1YcKTL9cQMCGywzo1wyBB",
|
||||
"blockId": "b59jwmEdwZUvJszV394x44u",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>Wow you are older than 80</div>",
|
||||
"html": "<div>You are older than 80</div>",
|
||||
"richText": [
|
||||
{
|
||||
"type": "p",
|
||||
"children": [{ "text": "Wow you are older than 80" }]
|
||||
}
|
||||
{ "type": "p", "children": [{ "text": "You are older than 80" }] }
|
||||
],
|
||||
"plainText": "Wow you are older than 80"
|
||||
"plainText": "You are older than 80"
|
||||
}
|
||||
},
|
||||
"ssBZF1FgMDYZJTbmTzb8Uks": {
|
||||
"id": "ssBZF1FgMDYZJTbmTzb8Uks",
|
||||
"blockId": "b4KB24EywCVt3zX3opqMvYS",
|
||||
"sb3o6J8Fybg6u8KuayKviJq": {
|
||||
"id": "sb3o6J8Fybg6u8KuayKviJq",
|
||||
"blockId": "baVF9HqhuSnLDZqY9eRPpcp",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>Wow you are older than 20</div>",
|
||||
"html": "<div>You are older than 20</div>",
|
||||
"richText": [
|
||||
{
|
||||
"type": "p",
|
||||
"children": [{ "text": "Wow you are older than 20" }]
|
||||
}
|
||||
{ "type": "p", "children": [{ "text": "You are older than 20" }] }
|
||||
],
|
||||
"plainText": "Wow you are older than 20"
|
||||
"plainText": "You are older than 20"
|
||||
}
|
||||
},
|
||||
"scQmWL2qGp1oXnEYdqjLcDv": {
|
||||
"id": "scQmWL2qGp1oXnEYdqjLcDv",
|
||||
"blockId": "bppe1zzyayc8ub14ozqJEXb",
|
||||
"scKogEJSTq4kPeHRhwTTjit": {
|
||||
"id": "scKogEJSTq4kPeHRhwTTjit",
|
||||
"blockId": "b9aEH46RHuZWTdQwZJ6KBWR",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>You are younger than 20</div>",
|
||||
@ -158,25 +158,80 @@
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"3EzaqYRLFqFQFbCj2gQP3q",
|
||||
"condition1",
|
||||
"condition2",
|
||||
"sqTSo2heZ5vdfzJjNZfYUK5",
|
||||
"siBqadjM6AJXf25Ct4413dM",
|
||||
"srFH8bxpJZuShgr2hmz4uhx",
|
||||
"ssBZF1FgMDYZJTbmTzb8Uks",
|
||||
"scQmWL2qGp1oXnEYdqjLcDv"
|
||||
"1A76iZBgXG7hvkG2koCxe4",
|
||||
"srwUKaUFFmehppJ2ZDqp4xG",
|
||||
"sxvzuo48GHi3AcAfmiFyYC1",
|
||||
"ituVWW1AvQeVdFHTwsiVao",
|
||||
"5SLc4whZooZVUfr1bmTNSC",
|
||||
"sxvzuo48GHi3AcAfmiFyYC1",
|
||||
"sm1YcKTL9cQMCGywzo1wyBB",
|
||||
"sb3o6J8Fybg6u8KuayKviJq",
|
||||
"scKogEJSTq4kPeHRhwTTjit"
|
||||
]
|
||||
},
|
||||
"choiceItems": { "byId": {}, "allIds": [] },
|
||||
"variables": {
|
||||
"byId": {
|
||||
"icVxLRv1sQnPyNwRX9cjK9": {
|
||||
"id": "icVxLRv1sQnPyNwRX9cjK9",
|
||||
"dEz689uVm8AxUM8TrbQd2t": {
|
||||
"id": "dEz689uVm8AxUM8TrbQd2t",
|
||||
"name": "Age"
|
||||
}
|
||||
},
|
||||
"allIds": ["icVxLRv1sQnPyNwRX9cjK9"]
|
||||
"allIds": ["dEz689uVm8AxUM8TrbQd2t"]
|
||||
},
|
||||
"edges": {
|
||||
"byId": {
|
||||
"jjNy2hYgrQgPS9EBMKA7MH": {
|
||||
"from": {
|
||||
"blockId": "2x83WHtEBkiv7pk7KgqJwZ",
|
||||
"stepId": "1A76iZBgXG7hvkG2koCxe4"
|
||||
},
|
||||
"to": { "blockId": "bwga7RwqQWbowdHph27DM1N" },
|
||||
"id": "jjNy2hYgrQgPS9EBMKA7MH"
|
||||
},
|
||||
"iBPsFyBsPv6Rbdfo2QdJyi": {
|
||||
"from": {
|
||||
"blockId": "bu8whx817bJBG37FQrtD5dD",
|
||||
"stepId": "ituVWW1AvQeVdFHTwsiVao",
|
||||
"conditionType": "true"
|
||||
},
|
||||
"to": { "blockId": "b59jwmEdwZUvJszV394x44u" },
|
||||
"id": "iBPsFyBsPv6Rbdfo2QdJyi"
|
||||
},
|
||||
"354PJ2jD5U3J2APqLsPJrp": {
|
||||
"from": {
|
||||
"blockId": "bu8whx817bJBG37FQrtD5dD",
|
||||
"stepId": "5SLc4whZooZVUfr1bmTNSC",
|
||||
"conditionType": "true"
|
||||
},
|
||||
"to": { "blockId": "baVF9HqhuSnLDZqY9eRPpcp" },
|
||||
"id": "354PJ2jD5U3J2APqLsPJrp"
|
||||
},
|
||||
"94bmeCLigEUUpWYw2xsAVB": {
|
||||
"from": {
|
||||
"blockId": "bu8whx817bJBG37FQrtD5dD",
|
||||
"stepId": "5SLc4whZooZVUfr1bmTNSC",
|
||||
"conditionType": "false"
|
||||
},
|
||||
"to": { "blockId": "b9aEH46RHuZWTdQwZJ6KBWR" },
|
||||
"id": "94bmeCLigEUUpWYw2xsAVB"
|
||||
},
|
||||
"7mcWaWohM9zGtLX8ZSnqFy": {
|
||||
"from": {
|
||||
"blockId": "bwga7RwqQWbowdHph27DM1N",
|
||||
"stepId": "sxvzuo48GHi3AcAfmiFyYC1"
|
||||
},
|
||||
"to": { "blockId": "bu8whx817bJBG37FQrtD5dD" },
|
||||
"id": "7mcWaWohM9zGtLX8ZSnqFy"
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"jjNy2hYgrQgPS9EBMKA7MH",
|
||||
"iBPsFyBsPv6Rbdfo2QdJyi",
|
||||
"354PJ2jD5U3J2APqLsPJrp",
|
||||
"94bmeCLigEUUpWYw2xsAVB",
|
||||
"7mcWaWohM9zGtLX8ZSnqFy"
|
||||
]
|
||||
},
|
||||
"theme": {
|
||||
"general": {
|
||||
|
BIN
apps/builder/cypress/fixtures/typebots/logic/condition.png
Normal file
BIN
apps/builder/cypress/fixtures/typebots/logic/condition.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
@ -1,121 +1,144 @@
|
||||
{
|
||||
"id": "typebot4",
|
||||
"createdAt": "2022-01-13T08:10:06.705Z",
|
||||
"updatedAt": "2022-01-13T08:10:06.705Z",
|
||||
"id": "ckylrr3qh0030fn1a3nszzxiu",
|
||||
"createdAt": "2022-01-19T16:38:05.225Z",
|
||||
"updatedAt": "2022-01-19T16:38:05.225Z",
|
||||
"name": "My typebot",
|
||||
"ownerId": "ckybcurfh1612li1a62gqojvj",
|
||||
"ownerId": "ckylrpsmt0006fn1ah956d0z1",
|
||||
"publishedTypebotId": null,
|
||||
"folderId": null,
|
||||
"blocks": {
|
||||
"byId": {
|
||||
"eXpkU5dMsjRPeY3WverF45": {
|
||||
"id": "eXpkU5dMsjRPeY3WverF45",
|
||||
"kmUzhRFzSKjkaipYNcku9S": {
|
||||
"id": "kmUzhRFzSKjkaipYNcku9S",
|
||||
"title": "Start",
|
||||
"stepIds": ["vWwzs6EmChn2PJcJQMr2gT"],
|
||||
"stepIds": ["6XgP3JoCh7Y4M8GCX9DKym"],
|
||||
"graphCoordinates": { "x": 0, "y": 0 }
|
||||
},
|
||||
"b87iNgPqGvKrybzHcTD4Sze": {
|
||||
"id": "b87iNgPqGvKrybzHcTD4Sze",
|
||||
"title": "Block #4",
|
||||
"stepIds": ["s8a2ASVaM3PoaD3y9amHrtT", "sc7FNu4BUCmmhmP14hk2Hij"],
|
||||
"graphCoordinates": { "x": 584, "y": 389 }
|
||||
},
|
||||
"bg22czQixaBBY8M8LJ9wEm1": {
|
||||
"id": "bg22czQixaBBY8M8LJ9wEm1",
|
||||
"title": "Block #3",
|
||||
"stepIds": ["set-var-1", "set-var-2"],
|
||||
"graphCoordinates": { "x": 581, "y": 97 }
|
||||
},
|
||||
"bvGtcdRjZtbbaimKyUD3NAW": {
|
||||
"id": "bvGtcdRjZtbbaimKyUD3NAW",
|
||||
"bwWRAaX5m6NZyZ9jjpXmWSb": {
|
||||
"id": "bwWRAaX5m6NZyZ9jjpXmWSb",
|
||||
"title": "Block #2",
|
||||
"stepIds": ["sgFoKA5Y4MXBkqFzCGCWGeg", "number-step"],
|
||||
"graphCoordinates": { "x": 190, "y": 222 }
|
||||
"graphCoordinates": { "x": -21, "y": 221 },
|
||||
"stepIds": ["sqMVMXeRYp4inLcRqej2Wac", "s8n3nJajsBaYqrFeRYVvcf6"]
|
||||
},
|
||||
"baUyUnNBxZzPe1z5PqE4NkD": {
|
||||
"id": "baUyUnNBxZzPe1z5PqE4NkD",
|
||||
"title": "Block #3",
|
||||
"graphCoordinates": { "x": 375, "y": 280 },
|
||||
"stepIds": ["shfL5ueQDuj2RPcJPWZGArT", "sugJ6xN3jFys1CjWfsxGhiJ"]
|
||||
},
|
||||
"bwkKNpJmAFCCLbZSnPnnLnR": {
|
||||
"id": "bwkKNpJmAFCCLbZSnPnnLnR",
|
||||
"title": "Block #4",
|
||||
"graphCoordinates": { "x": 421, "y": 42 },
|
||||
"stepIds": ["shR7ae3iNEvB6arCSu7wVFF"]
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"eXpkU5dMsjRPeY3WverF45",
|
||||
"bvGtcdRjZtbbaimKyUD3NAW",
|
||||
"bg22czQixaBBY8M8LJ9wEm1",
|
||||
"b87iNgPqGvKrybzHcTD4Sze"
|
||||
"kmUzhRFzSKjkaipYNcku9S",
|
||||
"bwWRAaX5m6NZyZ9jjpXmWSb",
|
||||
"baUyUnNBxZzPe1z5PqE4NkD",
|
||||
"bwkKNpJmAFCCLbZSnPnnLnR"
|
||||
]
|
||||
},
|
||||
"steps": {
|
||||
"byId": {
|
||||
"vWwzs6EmChn2PJcJQMr2gT": {
|
||||
"id": "vWwzs6EmChn2PJcJQMr2gT",
|
||||
"6XgP3JoCh7Y4M8GCX9DKym": {
|
||||
"id": "6XgP3JoCh7Y4M8GCX9DKym",
|
||||
"type": "start",
|
||||
"label": "Start",
|
||||
"target": { "blockId": "bvGtcdRjZtbbaimKyUD3NAW" },
|
||||
"blockId": "eXpkU5dMsjRPeY3WverF45"
|
||||
"blockId": "kmUzhRFzSKjkaipYNcku9S",
|
||||
"edgeId": "ahfJ4fUuvxX2dcBMk876tf"
|
||||
},
|
||||
"set-var-2": {
|
||||
"id": "set-var-2",
|
||||
"type": "Set variable",
|
||||
"target": { "blockId": "b87iNgPqGvKrybzHcTD4Sze" },
|
||||
"blockId": "bg22czQixaBBY8M8LJ9wEm1"
|
||||
},
|
||||
"s8a2ASVaM3PoaD3y9amHrtT": {
|
||||
"id": "s8a2ASVaM3PoaD3y9amHrtT",
|
||||
"type": "text",
|
||||
"blockId": "b87iNgPqGvKrybzHcTD4Sze",
|
||||
"content": {
|
||||
"html": "<div>Total: {{Total}}</div>",
|
||||
"richText": [
|
||||
{ "type": "p", "children": [{ "text": "Total: {{Total}}" }] }
|
||||
],
|
||||
"plainText": "Total: {{Total}}"
|
||||
}
|
||||
},
|
||||
"sgFoKA5Y4MXBkqFzCGCWGeg": {
|
||||
"id": "sgFoKA5Y4MXBkqFzCGCWGeg",
|
||||
"type": "text",
|
||||
"blockId": "bvGtcdRjZtbbaimKyUD3NAW",
|
||||
"content": {
|
||||
"html": "<div>1000 * ?</div>",
|
||||
"richText": [{ "type": "p", "children": [{ "text": "1000 * ?" }] }],
|
||||
"plainText": "1000 * ?"
|
||||
}
|
||||
},
|
||||
"number-step": {
|
||||
"id": "number-step",
|
||||
"s8n3nJajsBaYqrFeRYVvcf6": {
|
||||
"id": "s8n3nJajsBaYqrFeRYVvcf6",
|
||||
"blockId": "bwWRAaX5m6NZyZ9jjpXmWSb",
|
||||
"type": "number input",
|
||||
"target": { "blockId": "bg22czQixaBBY8M8LJ9wEm1" },
|
||||
"blockId": "bvGtcdRjZtbbaimKyUD3NAW"
|
||||
"edgeId": "dcJedLC7qsLtsmm1wbiFFc"
|
||||
},
|
||||
"set-var-1": {
|
||||
"id": "set-var-1",
|
||||
"blockId": "bg22czQixaBBY8M8LJ9wEm1",
|
||||
"sqMVMXeRYp4inLcRqej2Wac": {
|
||||
"id": "sqMVMXeRYp4inLcRqej2Wac",
|
||||
"blockId": "bwWRAaX5m6NZyZ9jjpXmWSb",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>How old are you?</div>",
|
||||
"richText": [
|
||||
{ "type": "p", "children": [{ "text": "How old are you?" }] }
|
||||
],
|
||||
"plainText": "How old are you?"
|
||||
}
|
||||
},
|
||||
"shfL5ueQDuj2RPcJPWZGArT": {
|
||||
"id": "shfL5ueQDuj2RPcJPWZGArT",
|
||||
"blockId": "baUyUnNBxZzPe1z5PqE4NkD",
|
||||
"type": "Set variable"
|
||||
},
|
||||
"sc7FNu4BUCmmhmP14hk2Hij": {
|
||||
"id": "sc7FNu4BUCmmhmP14hk2Hij",
|
||||
"blockId": "b87iNgPqGvKrybzHcTD4Sze",
|
||||
"sugJ6xN3jFys1CjWfsxGhiJ": {
|
||||
"id": "sugJ6xN3jFys1CjWfsxGhiJ",
|
||||
"blockId": "baUyUnNBxZzPe1z5PqE4NkD",
|
||||
"type": "Set variable",
|
||||
"edgeId": "sA5gvCVVBVYdGsdeSGF5ei"
|
||||
},
|
||||
"shR7ae3iNEvB6arCSu7wVFF": {
|
||||
"id": "shR7ae3iNEvB6arCSu7wVFF",
|
||||
"blockId": "bwkKNpJmAFCCLbZSnPnnLnR",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>Custom var: {{Custom var}}</div>",
|
||||
"html": "<div>Total: {{Total}}</div><div>Custom var: {{Custom var}}</div>",
|
||||
"richText": [
|
||||
{ "type": "p", "children": [{ "text": "Total: {{Total}}" }] },
|
||||
{
|
||||
"type": "p",
|
||||
"children": [{ "text": "Custom var: {{Custom var}}" }]
|
||||
}
|
||||
],
|
||||
"plainText": "Custom var: {{Custom var}}"
|
||||
"plainText": "Total: {{Total}}Custom var: {{Custom var}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"vWwzs6EmChn2PJcJQMr2gT",
|
||||
"s8a2ASVaM3PoaD3y9amHrtT",
|
||||
"set-var-2",
|
||||
"sgFoKA5Y4MXBkqFzCGCWGeg",
|
||||
"number-step",
|
||||
"set-var-1",
|
||||
"sc7FNu4BUCmmhmP14hk2Hij"
|
||||
"6XgP3JoCh7Y4M8GCX9DKym",
|
||||
"s8n3nJajsBaYqrFeRYVvcf6",
|
||||
"sqMVMXeRYp4inLcRqej2Wac",
|
||||
"shfL5ueQDuj2RPcJPWZGArT",
|
||||
"sugJ6xN3jFys1CjWfsxGhiJ",
|
||||
"shR7ae3iNEvB6arCSu7wVFF"
|
||||
]
|
||||
},
|
||||
"choiceItems": { "byId": {}, "allIds": [] },
|
||||
"variables": { "byId": {}, "allIds": [] },
|
||||
"edges": {
|
||||
"byId": {
|
||||
"ahfJ4fUuvxX2dcBMk876tf": {
|
||||
"from": {
|
||||
"blockId": "kmUzhRFzSKjkaipYNcku9S",
|
||||
"stepId": "6XgP3JoCh7Y4M8GCX9DKym"
|
||||
},
|
||||
"to": { "blockId": "bwWRAaX5m6NZyZ9jjpXmWSb" },
|
||||
"id": "ahfJ4fUuvxX2dcBMk876tf"
|
||||
},
|
||||
"dcJedLC7qsLtsmm1wbiFFc": {
|
||||
"from": {
|
||||
"blockId": "bwWRAaX5m6NZyZ9jjpXmWSb",
|
||||
"stepId": "s8n3nJajsBaYqrFeRYVvcf6"
|
||||
},
|
||||
"to": { "blockId": "baUyUnNBxZzPe1z5PqE4NkD" },
|
||||
"id": "dcJedLC7qsLtsmm1wbiFFc"
|
||||
},
|
||||
"sA5gvCVVBVYdGsdeSGF5ei": {
|
||||
"from": {
|
||||
"blockId": "baUyUnNBxZzPe1z5PqE4NkD",
|
||||
"stepId": "sugJ6xN3jFys1CjWfsxGhiJ"
|
||||
},
|
||||
"to": { "blockId": "bwkKNpJmAFCCLbZSnPnnLnR" },
|
||||
"id": "sA5gvCVVBVYdGsdeSGF5ei"
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"ahfJ4fUuvxX2dcBMk876tf",
|
||||
"dcJedLC7qsLtsmm1wbiFFc",
|
||||
"sA5gvCVVBVYdGsdeSGF5ei"
|
||||
]
|
||||
},
|
||||
"theme": {
|
||||
"general": {
|
||||
"font": "Open Sans",
|
||||
|
BIN
apps/builder/cypress/fixtures/typebots/logic/setVariable.png
Normal file
BIN
apps/builder/cypress/fixtures/typebots/logic/setVariable.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
@ -1,70 +1,70 @@
|
||||
{
|
||||
"id": "typebot4",
|
||||
"createdAt": "2022-01-12T08:20:11.572Z",
|
||||
"updatedAt": "2022-01-12T08:20:11.572Z",
|
||||
"id": "ckylsr69q0240z31afjhedyxo",
|
||||
"createdAt": "2022-01-19T17:06:08.126Z",
|
||||
"updatedAt": "2022-01-19T17:06:08.126Z",
|
||||
"name": "My typebot",
|
||||
"ownerId": "ckyb9vs110792li1az8bve32o",
|
||||
"ownerId": "ckylsr4fi0220z31apbinpy9d",
|
||||
"publishedTypebotId": null,
|
||||
"folderId": null,
|
||||
"blocks": {
|
||||
"byId": {
|
||||
"iE81k3ViAne3mfPEWeZJcq": {
|
||||
"id": "iE81k3ViAne3mfPEWeZJcq",
|
||||
"weeBMMXxNKwEonMfDX8Z5k": {
|
||||
"id": "weeBMMXxNKwEonMfDX8Z5k",
|
||||
"title": "Start",
|
||||
"stepIds": ["fLNNAAtRkrw7GG5iqCCdYx"],
|
||||
"stepIds": ["nEXiHesKXRQJhQbaWfbDVH"],
|
||||
"graphCoordinates": { "x": 0, "y": 0 }
|
||||
},
|
||||
"bcs6TWbHd9inLRDyGZaX7SD": {
|
||||
"id": "bcs6TWbHd9inLRDyGZaX7SD",
|
||||
"bg2MBdkf6y7g6WsbqAP3eAT": {
|
||||
"id": "bg2MBdkf6y7g6WsbqAP3eAT",
|
||||
"title": "Block #2",
|
||||
"graphCoordinates": { "x": 362, "y": 202 },
|
||||
"stepIds": ["sdExhY2LZ6VZ69s2WFmBcZg"]
|
||||
"graphCoordinates": { "x": 120, "y": 221 },
|
||||
"stepIds": ["sqzMjp1Ba4jTL3A6iJehC6C"]
|
||||
},
|
||||
"bgjJd4KTrHSy2GD7AfCPECR": {
|
||||
"id": "bgjJd4KTrHSy2GD7AfCPECR",
|
||||
"bj5BE1yKPzFFhvRk6cMnmsQ": {
|
||||
"id": "bj5BE1yKPzFFhvRk6cMnmsQ",
|
||||
"title": "Block #3",
|
||||
"graphCoordinates": { "x": 846, "y": 186 },
|
||||
"stepIds": ["sgwvBXuNbZ16vvJK7rAA8Gw"]
|
||||
"graphCoordinates": { "x": 529, "y": 130 },
|
||||
"stepIds": ["s8zPdEj96z8EoJG2zBqgoE8"]
|
||||
},
|
||||
"bkLYB73fk4GePgqDLnUHy1t": {
|
||||
"id": "bkLYB73fk4GePgqDLnUHy1t",
|
||||
"bdET8zLFQbwpTaAmi4wmezE": {
|
||||
"id": "bdET8zLFQbwpTaAmi4wmezE",
|
||||
"title": "Block #4",
|
||||
"graphCoordinates": { "x": 851, "y": 498 },
|
||||
"stepIds": ["sbE2QKjYNXwrBexFgYrkMcn"]
|
||||
"graphCoordinates": { "x": 538, "y": 386 },
|
||||
"stepIds": ["sjZ28izS5e3VjNynFKT2F7E"]
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"iE81k3ViAne3mfPEWeZJcq",
|
||||
"bcs6TWbHd9inLRDyGZaX7SD",
|
||||
"bgjJd4KTrHSy2GD7AfCPECR",
|
||||
"bkLYB73fk4GePgqDLnUHy1t"
|
||||
"weeBMMXxNKwEonMfDX8Z5k",
|
||||
"bg2MBdkf6y7g6WsbqAP3eAT",
|
||||
"bj5BE1yKPzFFhvRk6cMnmsQ",
|
||||
"bdET8zLFQbwpTaAmi4wmezE"
|
||||
]
|
||||
},
|
||||
"steps": {
|
||||
"byId": {
|
||||
"fLNNAAtRkrw7GG5iqCCdYx": {
|
||||
"id": "fLNNAAtRkrw7GG5iqCCdYx",
|
||||
"nEXiHesKXRQJhQbaWfbDVH": {
|
||||
"id": "nEXiHesKXRQJhQbaWfbDVH",
|
||||
"type": "start",
|
||||
"label": "Start",
|
||||
"blockId": "iE81k3ViAne3mfPEWeZJcq",
|
||||
"target": { "blockId": "bcs6TWbHd9inLRDyGZaX7SD" }
|
||||
"blockId": "weeBMMXxNKwEonMfDX8Z5k",
|
||||
"edgeId": "uh95dDpiiZdYxpPFsUqZEg"
|
||||
},
|
||||
"sdExhY2LZ6VZ69s2WFmBcZg": {
|
||||
"id": "sdExhY2LZ6VZ69s2WFmBcZg",
|
||||
"blockId": "bcs6TWbHd9inLRDyGZaX7SD",
|
||||
"sqzMjp1Ba4jTL3A6iJehC6C": {
|
||||
"id": "sqzMjp1Ba4jTL3A6iJehC6C",
|
||||
"blockId": "bg2MBdkf6y7g6WsbqAP3eAT",
|
||||
"type": "choice input",
|
||||
"options": {
|
||||
"itemIds": [
|
||||
"rFcixXR7vVXDeUuWajc7gR",
|
||||
"1Ksnvcvtpn358jX5rtEfKa",
|
||||
"8PLMwgYdaEzG6FAvCo46L8"
|
||||
"bWrsg18ucP9cdtFKhzgHbF",
|
||||
"p7Z57shv7p79KiwAtdi8Y3",
|
||||
"wjMRa2GBBnME9bEiNi6XgP"
|
||||
]
|
||||
},
|
||||
"target": { "blockId": "bkLYB73fk4GePgqDLnUHy1t" }
|
||||
"edgeId": "asT5shwJqDQ67qPuydR4gy"
|
||||
},
|
||||
"sgwvBXuNbZ16vvJK7rAA8Gw": {
|
||||
"id": "sgwvBXuNbZ16vvJK7rAA8Gw",
|
||||
"blockId": "bgjJd4KTrHSy2GD7AfCPECR",
|
||||
"s8zPdEj96z8EoJG2zBqgoE8": {
|
||||
"id": "s8zPdEj96z8EoJG2zBqgoE8",
|
||||
"blockId": "bj5BE1yKPzFFhvRk6cMnmsQ",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>I love burgers!</div>",
|
||||
@ -74,9 +74,9 @@
|
||||
"plainText": "I love burgers!"
|
||||
}
|
||||
},
|
||||
"sbE2QKjYNXwrBexFgYrkMcn": {
|
||||
"id": "sbE2QKjYNXwrBexFgYrkMcn",
|
||||
"blockId": "bkLYB73fk4GePgqDLnUHy1t",
|
||||
"sjZ28izS5e3VjNynFKT2F7E": {
|
||||
"id": "sjZ28izS5e3VjNynFKT2F7E",
|
||||
"blockId": "bdET8zLFQbwpTaAmi4wmezE",
|
||||
"type": "text",
|
||||
"content": {
|
||||
"html": "<div>Cool!</div>",
|
||||
@ -86,35 +86,70 @@
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"fLNNAAtRkrw7GG5iqCCdYx",
|
||||
"sdExhY2LZ6VZ69s2WFmBcZg",
|
||||
"sgwvBXuNbZ16vvJK7rAA8Gw",
|
||||
"sbE2QKjYNXwrBexFgYrkMcn"
|
||||
"nEXiHesKXRQJhQbaWfbDVH",
|
||||
"sqzMjp1Ba4jTL3A6iJehC6C",
|
||||
"s8zPdEj96z8EoJG2zBqgoE8",
|
||||
"sjZ28izS5e3VjNynFKT2F7E"
|
||||
]
|
||||
},
|
||||
"choiceItems": {
|
||||
"byId": {
|
||||
"rFcixXR7vVXDeUuWajc7gR": {
|
||||
"id": "rFcixXR7vVXDeUuWajc7gR",
|
||||
"stepId": "sdExhY2LZ6VZ69s2WFmBcZg",
|
||||
"bWrsg18ucP9cdtFKhzgHbF": {
|
||||
"id": "bWrsg18ucP9cdtFKhzgHbF",
|
||||
"stepId": "sqzMjp1Ba4jTL3A6iJehC6C",
|
||||
"content": "Burgers",
|
||||
"target": { "blockId": "bgjJd4KTrHSy2GD7AfCPECR" }
|
||||
"edgeId": "jfR6AUWt9b4dhjnUHXB179"
|
||||
},
|
||||
"1Ksnvcvtpn358jX5rtEfKa": {
|
||||
"id": "1Ksnvcvtpn358jX5rtEfKa",
|
||||
"stepId": "sdExhY2LZ6VZ69s2WFmBcZg",
|
||||
"p7Z57shv7p79KiwAtdi8Y3": {
|
||||
"id": "p7Z57shv7p79KiwAtdi8Y3",
|
||||
"stepId": "sqzMjp1Ba4jTL3A6iJehC6C",
|
||||
"content": "Hot dogs"
|
||||
},
|
||||
"8PLMwgYdaEzG6FAvCo46L8": {
|
||||
"id": "8PLMwgYdaEzG6FAvCo46L8",
|
||||
"stepId": "sdExhY2LZ6VZ69s2WFmBcZg",
|
||||
"wjMRa2GBBnME9bEiNi6XgP": {
|
||||
"id": "wjMRa2GBBnME9bEiNi6XgP",
|
||||
"stepId": "sqzMjp1Ba4jTL3A6iJehC6C",
|
||||
"content": "Carpaccio"
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"rFcixXR7vVXDeUuWajc7gR",
|
||||
"1Ksnvcvtpn358jX5rtEfKa",
|
||||
"8PLMwgYdaEzG6FAvCo46L8"
|
||||
"bWrsg18ucP9cdtFKhzgHbF",
|
||||
"p7Z57shv7p79KiwAtdi8Y3",
|
||||
"wjMRa2GBBnME9bEiNi6XgP"
|
||||
]
|
||||
},
|
||||
"variables": { "byId": {}, "allIds": [] },
|
||||
"edges": {
|
||||
"byId": {
|
||||
"uh95dDpiiZdYxpPFsUqZEg": {
|
||||
"from": {
|
||||
"blockId": "weeBMMXxNKwEonMfDX8Z5k",
|
||||
"stepId": "nEXiHesKXRQJhQbaWfbDVH"
|
||||
},
|
||||
"to": { "blockId": "bg2MBdkf6y7g6WsbqAP3eAT" },
|
||||
"id": "uh95dDpiiZdYxpPFsUqZEg"
|
||||
},
|
||||
"jfR6AUWt9b4dhjnUHXB179": {
|
||||
"from": {
|
||||
"blockId": "bg2MBdkf6y7g6WsbqAP3eAT",
|
||||
"stepId": "sqzMjp1Ba4jTL3A6iJehC6C",
|
||||
"nodeId": "bWrsg18ucP9cdtFKhzgHbF"
|
||||
},
|
||||
"to": { "blockId": "bj5BE1yKPzFFhvRk6cMnmsQ" },
|
||||
"id": "jfR6AUWt9b4dhjnUHXB179"
|
||||
},
|
||||
"asT5shwJqDQ67qPuydR4gy": {
|
||||
"from": {
|
||||
"blockId": "bg2MBdkf6y7g6WsbqAP3eAT",
|
||||
"stepId": "sqzMjp1Ba4jTL3A6iJehC6C"
|
||||
},
|
||||
"to": { "blockId": "bdET8zLFQbwpTaAmi4wmezE" },
|
||||
"id": "asT5shwJqDQ67qPuydR4gy"
|
||||
}
|
||||
},
|
||||
"allIds": [
|
||||
"uh95dDpiiZdYxpPFsUqZEg",
|
||||
"jfR6AUWt9b4dhjnUHXB179",
|
||||
"asT5shwJqDQ67qPuydR4gy"
|
||||
]
|
||||
},
|
||||
"theme": {
|
||||
@ -126,6 +161,5 @@
|
||||
"settings": {
|
||||
"typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 }
|
||||
},
|
||||
"variables": { "byId": {}, "allIds": [] },
|
||||
"publicId": null
|
||||
}
|
||||
|
BIN
apps/builder/cypress/fixtures/typebots/singleChoiceTarget.png
Normal file
BIN
apps/builder/cypress/fixtures/typebots/singleChoiceTarget.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
@ -148,7 +148,10 @@ const parseTypebotToPublicTypebot = (
|
||||
publicId: typebot.publicId,
|
||||
choiceItems: typebot.choiceItems,
|
||||
variables: typebot.variables,
|
||||
edges: typebot.edges,
|
||||
})
|
||||
|
||||
export const loadRawTypebotInDatabase = (typebot: Typebot) =>
|
||||
prisma.typebot.create({ data: { ...typebot, ownerId: userIds[1] } as any })
|
||||
prisma.typebot.create({
|
||||
data: { ...typebot, id: 'typebot4', ownerId: userIds[1] } as any,
|
||||
})
|
||||
|
@ -64,7 +64,7 @@ export const parseTestTypebot = ({
|
||||
type: 'start',
|
||||
blockId: 'block0',
|
||||
label: 'Start',
|
||||
target: { blockId: 'block1' },
|
||||
edgeId: 'edge1',
|
||||
},
|
||||
...steps.byId,
|
||||
},
|
||||
@ -75,6 +75,16 @@ export const parseTestTypebot = ({
|
||||
publishedTypebotId: null,
|
||||
updatedAt: new Date(),
|
||||
variables: { byId: {}, allIds: [] },
|
||||
edges: {
|
||||
byId: {
|
||||
edge1: {
|
||||
id: 'edge1',
|
||||
from: { blockId: 'block0', stepId: 'step0' },
|
||||
to: { blockId: 'block1' },
|
||||
},
|
||||
},
|
||||
allIds: ['edge1'],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,11 @@ Cypress.Commands.add(
|
||||
}
|
||||
)
|
||||
|
||||
Cypress.Commands.add('createVariable', (name: string) => {
|
||||
cy.findByTestId('variables-input').type(name)
|
||||
cy.findByRole('menuitem', { name: `Create "${name}"` }).click()
|
||||
})
|
||||
|
||||
const getDocumentScroll = () => {
|
||||
if (document.scrollingElement) {
|
||||
const { scrollTop, scrollLeft } = document.scrollingElement
|
||||
|
@ -21,6 +21,7 @@ declare global {
|
||||
signOut(): Chainable<any>
|
||||
signIn(email: string): Chainable<any>
|
||||
loadTypebotFixtureInDatabase(path: string): Chainable<any>
|
||||
createVariable(name: string): Chainable<any>
|
||||
mouseMoveBy(
|
||||
x: number,
|
||||
y: number,
|
||||
|
@ -1,261 +0,0 @@
|
||||
import { createTypebotWithStep } from 'cypress/plugins/data'
|
||||
import { preventUserFromRefreshing } from 'cypress/plugins/utils'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
import { InputStepType } from 'models'
|
||||
|
||||
describe('Text input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.TEXT })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cy.window().then((win) => {
|
||||
win.removeEventListener('beforeunload', preventUserFromRefreshing)
|
||||
})
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your answer...')
|
||||
.should('have.attr', 'type')
|
||||
.should('equal', 'text')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('Your name...')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
cy.findByTestId('step-step1').should('contain.text', 'Your name...')
|
||||
getIframeBody().findByPlaceholderText('Your name...').should('exist')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('checkbox', { name: 'Long text?' }).check({ force: true })
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByTestId('textarea').should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Number input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.NUMBER })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your answer...')
|
||||
.should('have.attr', 'type')
|
||||
.should('equal', 'number')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('Your name...')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByRole('spinbutton', { name: 'Min:' }).type('0')
|
||||
cy.findByRole('spinbutton', { name: 'Max:' }).type('100')
|
||||
cy.findByRole('spinbutton', { name: 'Step:' }).type('10')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
cy.findByTestId('step-step1').should('contain.text', 'Your name...')
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Your name...')
|
||||
.should('exist')
|
||||
.type('-1{enter}')
|
||||
.clear()
|
||||
.type('150{enter}')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
})
|
||||
})
|
||||
|
||||
describe('Email input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.EMAIL })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your email...')
|
||||
.should('have.attr', 'type')
|
||||
.should('equal', 'email')
|
||||
getIframeBody().findByRole('button', { name: 'Send' })
|
||||
getIframeBody().findByPlaceholderText('Type your email...').should('exist')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('Your email...')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByTestId('step-step1').should('contain.text', 'Your email...')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByPlaceholderText('Your email...').should('exist')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('URL input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.URL })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your URL...')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'url')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('Your URL...')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByTestId('step-step1').should('contain.text', 'Your URL...')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByPlaceholderText('Your URL...').should('exist')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('Date input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.DATE })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByTestId('from-date')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'date')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('checkbox', { name: 'Is range?' }).check({ force: true })
|
||||
cy.findByRole('textbox', { name: 'From label:' }).clear().type('Previous:')
|
||||
cy.findByRole('textbox', { name: 'To label:' }).clear().type('After:')
|
||||
cy.findByRole('checkbox', { name: 'With time?' }).check({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody()
|
||||
.findByTestId('from-date')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'datetime-local')
|
||||
getIframeBody()
|
||||
.findByTestId('to-date')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'datetime-local')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('Phone number input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.PHONE })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Your phone number...')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'tel')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('+33 XX XX XX XX')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('+33 XX XX XX XX')
|
||||
.type('+33 6 73 18 45 36')
|
||||
getIframeBody()
|
||||
.findByRole('img')
|
||||
.should('have.attr', 'alt')
|
||||
.should('eq', 'France')
|
||||
getIframeBody().findByRole('button', { name: 'Go' }).click()
|
||||
getIframeBody().findByText('+33673184536').should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Button input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.CHOICE })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('Can edit choice items', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByDisplayValue('Click to edit').type('Item 1{enter}')
|
||||
cy.findByText('Item 1').trigger('mouseover')
|
||||
cy.findByRole('button', { name: 'Add item' }).click()
|
||||
cy.findByDisplayValue('Click to edit').type('Item 2{enter}')
|
||||
cy.findByRole('button', { name: 'Add item' }).click()
|
||||
cy.findByDisplayValue('Click to edit').type('Item 3{enter}')
|
||||
cy.findByText('Item 2').rightclick()
|
||||
cy.findByRole('menuitem', { name: 'Delete' }).click()
|
||||
cy.findByText('Item 2').should('not.exist')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Item 3' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Item 3' }).should('not.exist')
|
||||
getIframeBody().findByText('Item 3')
|
||||
cy.findByRole('button', { name: 'Close' }).click()
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('checkbox', { name: 'Multiple choice?' }).check({
|
||||
force: true,
|
||||
})
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.wait(200)
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByText('Item 1').trigger('mouseover')
|
||||
cy.findByRole('button', { name: 'Add item' }).click()
|
||||
cy.findByDisplayValue('Click to edit').type('Item 2{enter}')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody().findByRole('checkbox', { name: 'Item 3' }).click()
|
||||
getIframeBody().findByRole('checkbox', { name: 'Item 1' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Go' }).click()
|
||||
getIframeBody().findByText('Item 3, Item 1').should('exist')
|
||||
})
|
||||
|
||||
it('Single choice targets should work', () => {
|
||||
cy.loadTypebotFixtureInDatabase('typebots/singleChoiceTarget.json')
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot4/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Burgers' }).click()
|
||||
getIframeBody().findByText('I love burgers!').should('exist')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Carpaccio' }).click()
|
||||
getIframeBody().findByText('Cool!').should('exist')
|
||||
})
|
||||
})
|
58
apps/builder/cypress/tests/inputs/button.ts
Normal file
58
apps/builder/cypress/tests/inputs/button.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { createTypebotWithStep } from 'cypress/plugins/data'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
import { InputStepType } from 'models'
|
||||
|
||||
describe('Button input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.CHOICE })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('Can edit choice items', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByDisplayValue('Click to edit').type('Item 1{enter}')
|
||||
cy.findByText('Item 1').trigger('mouseover')
|
||||
cy.findByRole('button', { name: 'Add item' }).click()
|
||||
cy.findByDisplayValue('Click to edit').type('Item 2{enter}')
|
||||
cy.findByRole('button', { name: 'Add item' }).click()
|
||||
cy.findByDisplayValue('Click to edit').type('Item 3{enter}')
|
||||
cy.findByText('Item 2').rightclick()
|
||||
cy.findByRole('menuitem', { name: 'Delete' }).click()
|
||||
cy.findByText('Item 2').should('not.exist')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Item 3' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Item 3' }).should('not.exist')
|
||||
getIframeBody().findByText('Item 3')
|
||||
cy.findByRole('button', { name: 'Close' }).click()
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('checkbox', { name: 'Multiple choice?' }).check({
|
||||
force: true,
|
||||
})
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.wait(200)
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByText('Item 1').trigger('mouseover')
|
||||
cy.findByRole('button', { name: 'Add item' }).click()
|
||||
cy.findByDisplayValue('Click to edit').type('Item 2{enter}')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody().findByRole('checkbox', { name: 'Item 3' }).click()
|
||||
getIframeBody().findByRole('checkbox', { name: 'Item 1' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Go' }).click()
|
||||
getIframeBody().findByText('Item 3, Item 1').should('exist')
|
||||
})
|
||||
|
||||
it('Single choice targets should work', () => {
|
||||
cy.loadTypebotFixtureInDatabase('typebots/singleChoiceTarget.json')
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot4/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Burgers' }).click()
|
||||
getIframeBody().findByText('I love burgers!').should('exist')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByRole('button', { name: 'Carpaccio' }).click()
|
||||
getIframeBody().findByText('Cool!').should('exist')
|
||||
})
|
||||
})
|
38
apps/builder/cypress/tests/inputs/date.ts
Normal file
38
apps/builder/cypress/tests/inputs/date.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { createTypebotWithStep } from 'cypress/plugins/data'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
import { InputStepType } from 'models'
|
||||
|
||||
describe('Date input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.DATE })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByTestId('from-date')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'date')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('checkbox', { name: 'Is range?' }).check({ force: true })
|
||||
cy.findByRole('textbox', { name: 'From label:' }).clear().type('Previous:')
|
||||
cy.findByRole('textbox', { name: 'To label:' }).clear().type('After:')
|
||||
cy.findByRole('checkbox', { name: 'With time?' }).check({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody()
|
||||
.findByTestId('from-date')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'datetime-local')
|
||||
getIframeBody()
|
||||
.findByTestId('to-date')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'datetime-local')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
})
|
||||
})
|
33
apps/builder/cypress/tests/inputs/email.ts
Normal file
33
apps/builder/cypress/tests/inputs/email.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { createTypebotWithStep } from 'cypress/plugins/data'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
import { InputStepType } from 'models'
|
||||
|
||||
describe('Email input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.EMAIL })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your email...')
|
||||
.should('have.attr', 'type')
|
||||
.should('equal', 'email')
|
||||
getIframeBody().findByRole('button', { name: 'Send' })
|
||||
getIframeBody().findByPlaceholderText('Type your email...').should('exist')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('Your email...')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByTestId('step-step1').should('contain.text', 'Your email...')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByPlaceholderText('Your email...').should('exist')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
})
|
||||
})
|
40
apps/builder/cypress/tests/inputs/number.ts
Normal file
40
apps/builder/cypress/tests/inputs/number.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { createTypebotWithStep } from 'cypress/plugins/data'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
import { InputStepType } from 'models'
|
||||
|
||||
describe('Number input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.NUMBER })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your answer...')
|
||||
.should('have.attr', 'type')
|
||||
.should('equal', 'number')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('Your name...')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByRole('spinbutton', { name: 'Min:' }).type('0')
|
||||
cy.findByRole('spinbutton', { name: 'Max:' }).type('100')
|
||||
cy.findByRole('spinbutton', { name: 'Step:' }).type('10')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
cy.findByTestId('step-step1').should('contain.text', 'Your name...')
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Your name...')
|
||||
.should('exist')
|
||||
.type('-1{enter}')
|
||||
.clear()
|
||||
.type('150{enter}')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
})
|
||||
})
|
37
apps/builder/cypress/tests/inputs/phone.ts
Normal file
37
apps/builder/cypress/tests/inputs/phone.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { createTypebotWithStep } from 'cypress/plugins/data'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
import { InputStepType } from 'models'
|
||||
|
||||
describe('Phone number input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.PHONE })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Your phone number...')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'tel')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('+33 XX XX XX XX')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('+33 XX XX XX XX')
|
||||
.type('+33 6 73 18 45 36')
|
||||
getIframeBody()
|
||||
.findByRole('img')
|
||||
.should('have.attr', 'alt')
|
||||
.should('eq', 'France')
|
||||
getIframeBody().findByRole('button', { name: 'Go' }).click()
|
||||
getIframeBody().findByText('+33673184536').should('exist')
|
||||
})
|
||||
})
|
42
apps/builder/cypress/tests/inputs/text.ts
Normal file
42
apps/builder/cypress/tests/inputs/text.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { createTypebotWithStep } from 'cypress/plugins/data'
|
||||
import { preventUserFromRefreshing } from 'cypress/plugins/utils'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
import { InputStepType } from 'models'
|
||||
|
||||
describe('Text input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.TEXT })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cy.window().then((win) => {
|
||||
win.removeEventListener('beforeunload', preventUserFromRefreshing)
|
||||
})
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your answer...')
|
||||
.should('have.attr', 'type')
|
||||
.should('equal', 'text')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('Your name...')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
cy.findByTestId('step-step1').should('contain.text', 'Your name...')
|
||||
getIframeBody().findByPlaceholderText('Your name...').should('exist')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('checkbox', { name: 'Long text?' }).check({ force: true })
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByTestId('textarea').should('exist')
|
||||
})
|
||||
})
|
31
apps/builder/cypress/tests/inputs/url.ts
Normal file
31
apps/builder/cypress/tests/inputs/url.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { createTypebotWithStep } from 'cypress/plugins/data'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
import { InputStepType } from 'models'
|
||||
|
||||
describe('URL input', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
createTypebotWithStep({ type: InputStepType.URL })
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot3/edit')
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your URL...')
|
||||
.should('have.attr', 'type')
|
||||
.should('eq', 'url')
|
||||
getIframeBody().findByRole('button', { name: 'Send' }).should('be.disabled')
|
||||
cy.findByTestId('step-step1').click({ force: true })
|
||||
cy.findByRole('textbox', { name: 'Placeholder:' })
|
||||
.clear()
|
||||
.type('Your URL...')
|
||||
cy.findByRole('textbox', { name: 'Button label:' }).clear().type('Go')
|
||||
cy.findByTestId('step-step1').should('contain.text', 'Your URL...')
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByPlaceholderText('Your URL...').should('exist')
|
||||
getIframeBody().findByRole('button', { name: 'Go' })
|
||||
})
|
||||
})
|
@ -121,7 +121,7 @@ describe('Google sheets', () => {
|
||||
})
|
||||
|
||||
const fillInSpreadsheetInfo = () => {
|
||||
cy.findByTestId('step-step1').click()
|
||||
cy.findByText('Configure...').click()
|
||||
|
||||
cy.findByRole('button', { name: 'Select an account' }).click()
|
||||
cy.findByRole('menuitem', { name: 'test2@gmail.com' }).click()
|
||||
|
@ -1,44 +1,6 @@
|
||||
import { preventUserFromRefreshing } from 'cypress/plugins/utils'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
|
||||
describe('Set variables', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cy.window().then((win) => {
|
||||
win.removeEventListener('beforeunload', preventUserFromRefreshing)
|
||||
})
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.loadTypebotFixtureInDatabase('typebots/logic/setVariable.json')
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot4/edit')
|
||||
cy.findByTestId('step-number-step').click()
|
||||
createNewVar('Num')
|
||||
cy.findByTestId('step-set-var-1').click()
|
||||
createNewVar('Total')
|
||||
cy.findByRole('textbox', { name: 'Value / Expression:' }).type(
|
||||
'1000 * {{Num}}',
|
||||
{ parseSpecialCharSequences: false }
|
||||
)
|
||||
cy.findByTestId('step-set-var-2').click()
|
||||
createNewVar('Custom var')
|
||||
cy.findByRole('textbox', { name: 'Value / Expression:' }).type(
|
||||
'Custom value'
|
||||
)
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your answer...')
|
||||
.type('365{enter}')
|
||||
getIframeBody().findByText('Total: 365000').should('exist')
|
||||
getIframeBody().findByText('Custom var: Custom value')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Condition step', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
@ -56,7 +18,7 @@ describe('Condition step', () => {
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot4/edit')
|
||||
|
||||
cy.findByTestId('step-condition1').click()
|
||||
cy.findAllByText('Equal to').first().click()
|
||||
|
||||
cy.findByTestId('variables-input').click()
|
||||
cy.findByRole('menuitem', { name: 'Age' }).click()
|
||||
@ -71,7 +33,7 @@ describe('Condition step', () => {
|
||||
cy.findByRole('menuitem', { name: 'Less than' }).click()
|
||||
cy.findAllByPlaceholderText('Type a value...').last().type('100')
|
||||
|
||||
cy.findByTestId('step-condition2').click()
|
||||
cy.findAllByText('Equal to').last().click()
|
||||
|
||||
cy.findByTestId('variables-input').click()
|
||||
cy.findByRole('menuitem', { name: 'Age' }).click()
|
||||
@ -80,20 +42,21 @@ describe('Condition step', () => {
|
||||
cy.findByPlaceholderText('Type a value...').type('20')
|
||||
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody().findByPlaceholderText('Type your age...').type('15{enter}')
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your answer...')
|
||||
.type('15{enter}')
|
||||
getIframeBody().findByText('You are younger than 20').should('exist')
|
||||
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByPlaceholderText('Type your age...').type('45{enter}')
|
||||
getIframeBody().findByText('Wow you are older than 20').should('exist')
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your answer...')
|
||||
.type('45{enter}')
|
||||
getIframeBody().findByText('You are older than 20').should('exist')
|
||||
|
||||
cy.findByRole('button', { name: 'Restart' }).click()
|
||||
getIframeBody().findByPlaceholderText('Type your age...').type('90{enter}')
|
||||
getIframeBody().findByText('Wow you are older than 80').should('exist')
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your answer...')
|
||||
.type('90{enter}')
|
||||
getIframeBody().findByText('You are older than 80').should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
const createNewVar = (name: string) => {
|
||||
cy.findByTestId('variables-input').type(name)
|
||||
cy.findByRole('menuitem', { name: `Create "${name}"` }).click()
|
||||
}
|
40
apps/builder/cypress/tests/logic/setVariable.ts
Normal file
40
apps/builder/cypress/tests/logic/setVariable.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { preventUserFromRefreshing } from 'cypress/plugins/utils'
|
||||
import { getIframeBody } from 'cypress/support'
|
||||
|
||||
describe('Set variables', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('seed')
|
||||
cy.signOut()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cy.window().then((win) => {
|
||||
win.removeEventListener('beforeunload', preventUserFromRefreshing)
|
||||
})
|
||||
})
|
||||
|
||||
it('options should work', () => {
|
||||
cy.loadTypebotFixtureInDatabase('typebots/logic/setVariable.json')
|
||||
cy.signIn('test2@gmail.com')
|
||||
cy.visit('/typebots/typebot4/edit')
|
||||
cy.findByText('Type your answer...').click()
|
||||
cy.createVariable('Num')
|
||||
cy.findAllByText('Click to edit...').first().click()
|
||||
cy.createVariable('Total')
|
||||
cy.findByRole('textbox', { name: 'Value / Expression:' }).type(
|
||||
'1000 * {{Num}}',
|
||||
{ parseSpecialCharSequences: false }
|
||||
)
|
||||
cy.findAllByText('Click to edit...').last().click()
|
||||
cy.createVariable('Custom var')
|
||||
cy.findByRole('textbox', { name: 'Value / Expression:' }).type(
|
||||
'Custom value'
|
||||
)
|
||||
cy.findByRole('button', { name: 'Preview' }).click()
|
||||
getIframeBody()
|
||||
.findByPlaceholderText('Type your answer...')
|
||||
.type('365{enter}')
|
||||
getIframeBody().findByText('Total: 365000').should('exist')
|
||||
getIframeBody().findByText('Custom var: Custom value')
|
||||
})
|
||||
})
|
@ -1,9 +1,6 @@
|
||||
import { Coordinates } from '@dnd-kit/core/dist/types'
|
||||
import { Block, Step, Table, Target, Typebot } from 'models'
|
||||
import {
|
||||
AnchorsPositionProps,
|
||||
EdgeType,
|
||||
} from 'components/board/graph/Edges/Edge'
|
||||
import { Block, Edge, Table, Target, Typebot } from 'models'
|
||||
import { AnchorsPositionProps } from 'components/board/graph/Edges/Edge'
|
||||
import {
|
||||
stubLength,
|
||||
blockWidth,
|
||||
@ -13,7 +10,6 @@ import {
|
||||
} from 'contexts/GraphContext'
|
||||
import { roundCorners } from 'svg-round-corners'
|
||||
import { headerHeight } from 'components/shared/TypebotHeader'
|
||||
import { isConditionStep } from 'utils'
|
||||
|
||||
export const computeDropOffPath = (
|
||||
sourcePosition: Coordinates,
|
||||
@ -285,10 +281,10 @@ export const computeEdgePathToMouse = ({
|
||||
export const getEndpointTopOffset = (
|
||||
graphPosition: Coordinates,
|
||||
endpoints: Table<Endpoint>,
|
||||
id?: string
|
||||
endpointId?: string
|
||||
): number => {
|
||||
if (!id) return 0
|
||||
const endpointRef = endpoints.byId[id]?.ref
|
||||
if (!endpointId) return 0
|
||||
const endpointRef = endpoints.byId[endpointId]?.ref
|
||||
if (!endpointRef) return 0
|
||||
return (
|
||||
8 +
|
||||
@ -298,17 +294,5 @@ export const getEndpointTopOffset = (
|
||||
)
|
||||
}
|
||||
|
||||
export const getTarget = (step: Step, edgeType: EdgeType) => {
|
||||
if (!step) return
|
||||
switch (edgeType) {
|
||||
case EdgeType.STEP:
|
||||
case EdgeType.CHOICE_ITEM:
|
||||
return step.target
|
||||
case EdgeType.CONDITION_TRUE:
|
||||
if (!isConditionStep(step)) return
|
||||
return step.trueTarget
|
||||
case EdgeType.CONDITION_FALSE:
|
||||
if (!isConditionStep(step)) return
|
||||
return step.falseTarget
|
||||
}
|
||||
}
|
||||
export const getSourceEndpointId = (edge?: Edge) =>
|
||||
edge?.from.nodeId ?? edge?.from.stepId + `${edge?.from.conditionType ?? ''}`
|
||||
|
@ -18,6 +18,7 @@ export const parseTypebotToPublicTypebot = (
|
||||
publicId: typebot.publicId,
|
||||
choiceItems: typebot.choiceItems,
|
||||
variables: typebot.variables,
|
||||
edges: typebot.edges,
|
||||
})
|
||||
|
||||
export const createPublishedTypebot = async (
|
||||
|
@ -234,6 +234,7 @@ export const parseNewTypebot = ({
|
||||
steps: { byId: { [startStepId]: startStep }, allIds: [startStepId] },
|
||||
choiceItems: { byId: {}, allIds: [] },
|
||||
variables: { byId: {}, allIds: [] },
|
||||
edges: { byId: {}, allIds: [] },
|
||||
theme,
|
||||
settings,
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { TransitionGroup, CSSTransition } from 'react-transition-group'
|
||||
import { ChatStep } from './ChatStep'
|
||||
import { AvatarSideContainer } from './AvatarSideContainer'
|
||||
import { HostAvatarsContext } from '../../contexts/HostAvatarsContext'
|
||||
import { Step, Target } from 'models'
|
||||
import { Edge, Step, Target } from 'models'
|
||||
import { useTypebot } from '../../contexts/TypebotContext'
|
||||
import {
|
||||
isChoiceInput,
|
||||
@ -20,7 +20,7 @@ import { executeIntegration } from 'services/integration'
|
||||
type ChatBlockProps = {
|
||||
stepIds: string[]
|
||||
startStepId?: string
|
||||
onBlockEnd: (target?: Target) => void
|
||||
onBlockEnd: (edgeId?: string) => void
|
||||
}
|
||||
|
||||
export const ChatBlock = ({
|
||||
@ -46,20 +46,20 @@ export const ChatBlock = ({
|
||||
const currentStep = [...displayedSteps].pop()
|
||||
if (!currentStep) return
|
||||
if (isLogicStep(currentStep)) {
|
||||
const target = executeLogic(
|
||||
const nextEdgeId = executeLogic(
|
||||
currentStep,
|
||||
typebot.variables,
|
||||
updateVariableValue
|
||||
)
|
||||
target ? onBlockEnd(target) : displayNextStep()
|
||||
nextEdgeId ? onBlockEnd(nextEdgeId) : displayNextStep()
|
||||
}
|
||||
if (isIntegrationStep(currentStep)) {
|
||||
const target = await executeIntegration(
|
||||
const nextEdgeId = await executeIntegration(
|
||||
currentStep,
|
||||
typebot.variables,
|
||||
updateVariableValue
|
||||
)
|
||||
target ? onBlockEnd(target) : displayNextStep()
|
||||
nextEdgeId ? onBlockEnd(nextEdgeId) : displayNextStep()
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,11 +90,8 @@ export const ChatBlock = ({
|
||||
answerContent
|
||||
)
|
||||
)
|
||||
if (
|
||||
currentStep?.target?.blockId ||
|
||||
displayedSteps.length === stepIds.length
|
||||
)
|
||||
return onBlockEnd(currentStep?.target)
|
||||
if (currentStep?.edgeId || displayedSteps.length === stepIds.length)
|
||||
return onBlockEnd(currentStep.edgeId)
|
||||
}
|
||||
const nextStep = typebot.steps.byId[stepIds[displayedSteps.length]]
|
||||
if (nextStep) setDisplayedSteps([...displayedSteps, nextStep])
|
||||
|
@ -5,11 +5,11 @@ import { useFrame } from 'react-frame-component'
|
||||
import { setCssVariablesValue } from '../services/theme'
|
||||
import { useAnswers } from '../contexts/AnswersContext'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { Answer, Block, PublicTypebot, Target } from 'models'
|
||||
import { Answer, Block, Edge, PublicTypebot } from 'models'
|
||||
|
||||
type Props = {
|
||||
typebot: PublicTypebot
|
||||
onNewBlockVisible: (blockId: string) => void
|
||||
onNewBlockVisible: (edgeId: string) => void
|
||||
onNewAnswer: (answer: Answer) => void
|
||||
onCompleted: () => void
|
||||
}
|
||||
@ -27,22 +27,24 @@ export const ConversationContainer = ({
|
||||
const { answers } = useAnswers()
|
||||
const bottomAnchor = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const displayNextBlock = (target?: Target) => {
|
||||
if (!target) return onCompleted()
|
||||
const displayNextBlock = (edgeId?: string) => {
|
||||
const edge = typebot.edges.byId[edgeId ?? '']
|
||||
if (!edge) return onCompleted()
|
||||
const nextBlock = {
|
||||
block: typebot.blocks.byId[target.blockId],
|
||||
startStepId: target.stepId,
|
||||
block: typebot.blocks.byId[edge.to.blockId],
|
||||
startStepId: edge.to.stepId,
|
||||
}
|
||||
if (!nextBlock) return onCompleted()
|
||||
onNewBlockVisible(target.blockId)
|
||||
onNewBlockVisible(edge.id)
|
||||
setDisplayedBlocks([...displayedBlocks, nextBlock])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const blocks = typebot.blocks
|
||||
const firstTarget =
|
||||
typebot.steps.byId[blocks.byId[blocks.allIds[0]].stepIds[0]].target
|
||||
if (firstTarget) displayNextBlock(firstTarget)
|
||||
const firstEdgeId =
|
||||
typebot.steps.byId[blocks.byId[blocks.allIds[0]].stepIds[0]].edgeId
|
||||
if (!firstEdgeId) return
|
||||
displayNextBlock(firstEdgeId)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -11,7 +11,7 @@ import { Answer, BackgroundType, PublicTypebot } from 'models'
|
||||
|
||||
export type TypebotViewerProps = {
|
||||
typebot: PublicTypebot
|
||||
onNewBlockVisible?: (blockId: string) => void
|
||||
onNewBlockVisible?: (edgeId: string) => void
|
||||
onNewAnswer?: (answer: Answer) => void
|
||||
onCompleted?: () => void
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ export const getSingleChoiceTargetId = (
|
||||
currentStep: ChoiceInputStep,
|
||||
choiceItems: Table<ChoiceItem>,
|
||||
answerContent?: string
|
||||
): Target | undefined => {
|
||||
): string | undefined => {
|
||||
const itemId = currentStep.options.itemIds.find(
|
||||
(itemId) => choiceItems.byId[itemId].content === answerContent
|
||||
)
|
||||
if (!itemId) throw new Error('itemId should exist')
|
||||
return choiceItems.byId[itemId].target ?? currentStep.target
|
||||
return choiceItems.byId[itemId].edgeId ?? currentStep.edgeId
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ const executeGoogleSheetIntegration = async (
|
||||
variables: Table<Variable>,
|
||||
updateVariableValue: (variableId: string, value: string) => void
|
||||
) => {
|
||||
if (!step.options) return step.target
|
||||
if (!step.options) return step.edgeId
|
||||
switch (step.options?.action) {
|
||||
case GoogleSheetsAction.INSERT_ROW:
|
||||
await insertRowInGoogleSheets(step.options, variables)
|
||||
@ -56,7 +56,7 @@ const executeGoogleSheetIntegration = async (
|
||||
await getRowFromGoogleSheets(step.options, variables, updateVariableValue)
|
||||
break
|
||||
}
|
||||
return step.target
|
||||
return step.edgeId
|
||||
}
|
||||
|
||||
const insertRowInGoogleSheets = async (
|
||||
|
@ -15,7 +15,7 @@ export const executeLogic = (
|
||||
step: LogicStep,
|
||||
variables: Table<Variable>,
|
||||
updateVariableValue: (variableId: string, expression: string) => void
|
||||
): Target | undefined => {
|
||||
): string | undefined => {
|
||||
switch (step.type) {
|
||||
case LogicStepType.SET_VARIABLE: {
|
||||
if (!step.options?.variableId || !step.options.expressionToEvaluate)
|
||||
@ -36,7 +36,7 @@ export const executeLogic = (
|
||||
: step.options?.comparisons.allIds.some(
|
||||
executeComparison(step, variables)
|
||||
)
|
||||
return isConditionPassed ? step.trueTarget : step.falseTarget
|
||||
return isConditionPassed ? step.trueEdgeId : step.falseEdgeId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +110,7 @@ model Typebot {
|
||||
steps Json
|
||||
choiceItems Json
|
||||
variables Json
|
||||
edges Json
|
||||
theme Json
|
||||
settings Json
|
||||
publicId String? @unique
|
||||
@ -124,6 +125,7 @@ model PublicTypebot {
|
||||
steps Json
|
||||
choiceItems Json
|
||||
variables Json
|
||||
edges Json
|
||||
theme Json
|
||||
settings Json
|
||||
publicId String? @unique
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { PublicTypebot as PublicTypebotFromPrisma } from 'db'
|
||||
import { Block, ChoiceItem, Settings, Step, Theme } from './typebot'
|
||||
import { Block, ChoiceItem, Edge, Settings, Step, Theme } from './typebot'
|
||||
import { Variable } from './typebot/variable'
|
||||
import { Table } from './utils'
|
||||
|
||||
@ -17,6 +17,7 @@ export type PublicTypebot = Omit<
|
||||
steps: Table<Step>
|
||||
choiceItems: Table<ChoiceItem>
|
||||
variables: Table<Variable>
|
||||
edges: Table<Edge>
|
||||
theme: Theme
|
||||
settings: Settings
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { Target } from '.'
|
||||
import { StepBase } from './steps'
|
||||
|
||||
export type InputStep =
|
||||
@ -66,7 +65,7 @@ export type ChoiceItem = {
|
||||
id: string
|
||||
stepId: string
|
||||
content?: string
|
||||
target?: Target
|
||||
edgeId?: string
|
||||
}
|
||||
|
||||
type OptionBase = { variableId?: string }
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { StepBase, Target } from '.'
|
||||
import { StepBase } from '.'
|
||||
import { Table } from '../..'
|
||||
|
||||
export type LogicStep = SetVariableStep | ConditionStep
|
||||
@ -32,8 +32,8 @@ export enum ComparisonOperators {
|
||||
export type ConditionStep = StepBase & {
|
||||
type: LogicStepType.CONDITION
|
||||
options: ConditionOptions
|
||||
trueTarget?: Target
|
||||
falseTarget?: Target
|
||||
trueEdgeId?: string
|
||||
falseEdgeId?: string
|
||||
}
|
||||
|
||||
export type ConditionOptions = {
|
||||
|
@ -36,11 +36,9 @@ export type StepOptions =
|
||||
| LogicStepOptions
|
||||
| IntegrationStepOptions
|
||||
|
||||
export type StepBase = { id: string; blockId: string; target?: Target }
|
||||
export type StepBase = { id: string; blockId: string; edgeId?: string }
|
||||
|
||||
export type StartStep = StepBase & {
|
||||
type: 'start'
|
||||
label: string
|
||||
}
|
||||
|
||||
export type Target = { blockId: string; stepId?: string }
|
||||
|
@ -14,6 +14,7 @@ export type Typebot = Omit<
|
||||
steps: Table<Step>
|
||||
choiceItems: Table<ChoiceItem>
|
||||
variables: Table<Variable>
|
||||
edges: Table<Edge>
|
||||
theme: Theme
|
||||
settings: Settings
|
||||
}
|
||||
@ -27,3 +28,16 @@ export type Block = {
|
||||
}
|
||||
stepIds: string[]
|
||||
}
|
||||
|
||||
export type Source = {
|
||||
blockId: string
|
||||
stepId: string
|
||||
nodeId?: string
|
||||
conditionType?: 'true' | 'false'
|
||||
}
|
||||
export type Target = { blockId: string; stepId?: string }
|
||||
export type Edge = {
|
||||
id: string
|
||||
from: Source
|
||||
to: Target
|
||||
}
|
||||
|
Reference in New Issue
Block a user