refactor(editor): ♻️ Undo / Redo buttons + structure refacto
Yet another huge refacto... While implementing undo and redo features I understood that I updated the stored typebot too many times (i.e. on each key input) so I had to rethink it entirely. I also moved around some files.
This commit is contained in:
@ -0,0 +1,104 @@
|
||||
import { Text } from '@chakra-ui/react'
|
||||
import {
|
||||
Step,
|
||||
StartStep,
|
||||
BubbleStepType,
|
||||
InputStepType,
|
||||
LogicStepType,
|
||||
IntegrationStepType,
|
||||
} from 'models'
|
||||
import { isInputStep } from 'utils'
|
||||
import { ButtonNodesList } from '../../ButtonNode'
|
||||
import {
|
||||
ConditionContent,
|
||||
SetVariableContent,
|
||||
TextBubbleContent,
|
||||
VideoBubbleContent,
|
||||
WebhookContent,
|
||||
WithVariableContent,
|
||||
} from './contents'
|
||||
import { ConfigureContent } from './contents/ConfigureContent'
|
||||
import { ImageBubbleContent } from './contents/ImageBubbleContent'
|
||||
import { PlaceholderContent } from './contents/PlaceholderContent'
|
||||
|
||||
type Props = {
|
||||
step: Step | StartStep
|
||||
isConnectable?: boolean
|
||||
}
|
||||
export const StepNodeContent = ({ step }: Props) => {
|
||||
if (isInputStep(step) && step.options.variableId) {
|
||||
return <WithVariableContent step={step} />
|
||||
}
|
||||
switch (step.type) {
|
||||
case BubbleStepType.TEXT: {
|
||||
return <TextBubbleContent step={step} />
|
||||
}
|
||||
case BubbleStepType.IMAGE: {
|
||||
return <ImageBubbleContent step={step} />
|
||||
}
|
||||
case BubbleStepType.VIDEO: {
|
||||
return <VideoBubbleContent step={step} />
|
||||
}
|
||||
case InputStepType.TEXT:
|
||||
case InputStepType.NUMBER:
|
||||
case InputStepType.EMAIL:
|
||||
case InputStepType.URL:
|
||||
case InputStepType.PHONE: {
|
||||
return (
|
||||
<PlaceholderContent placeholder={step.options.labels.placeholder} />
|
||||
)
|
||||
}
|
||||
case InputStepType.DATE: {
|
||||
return <Text color={'gray.500'}>Pick a date...</Text>
|
||||
}
|
||||
case InputStepType.CHOICE: {
|
||||
return <ButtonNodesList step={step} />
|
||||
}
|
||||
case LogicStepType.SET_VARIABLE: {
|
||||
return <SetVariableContent step={step} />
|
||||
}
|
||||
case LogicStepType.CONDITION: {
|
||||
return <ConditionContent step={step} />
|
||||
}
|
||||
case LogicStepType.REDIRECT: {
|
||||
return (
|
||||
<ConfigureContent
|
||||
label={
|
||||
step.options?.url ? `Redirect to ${step.options?.url}` : undefined
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
case IntegrationStepType.GOOGLE_SHEETS: {
|
||||
return (
|
||||
<ConfigureContent
|
||||
label={
|
||||
step.options && 'action' in step.options
|
||||
? step.options.action
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
case IntegrationStepType.GOOGLE_ANALYTICS: {
|
||||
return (
|
||||
<ConfigureContent
|
||||
label={
|
||||
step.options?.action
|
||||
? `Track "${step.options?.action}" `
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
case IntegrationStepType.WEBHOOK: {
|
||||
return <WebhookContent step={step} />
|
||||
}
|
||||
case 'start': {
|
||||
return <Text>Start</Text>
|
||||
}
|
||||
default: {
|
||||
return <Text>No input</Text>
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
import { Flex, Stack, HStack, Tag, Text } from '@chakra-ui/react'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { ConditionStep } from 'models'
|
||||
import { SourceEndpoint } from '../../../../Endpoints/SourceEndpoint'
|
||||
|
||||
export const ConditionContent = ({ step }: { step: ConditionStep }) => {
|
||||
const { typebot } = useTypebot()
|
||||
return (
|
||||
<Flex>
|
||||
{step.options?.comparisons.allIds.length === 0 ? (
|
||||
<Text color={'gray.500'}>Configure...</Text>
|
||||
) : (
|
||||
<Stack>
|
||||
{step.options?.comparisons.allIds.map((comparisonId, idx) => {
|
||||
const comparison = step.options?.comparisons.byId[comparisonId]
|
||||
const variable =
|
||||
typebot?.variables.byId[comparison?.variableId ?? '']
|
||||
return (
|
||||
<HStack key={comparisonId} spacing={1}>
|
||||
{idx > 0 && <Text>{step.options?.logicalOperator ?? ''}</Text>}
|
||||
{variable?.name && (
|
||||
<Tag bgColor="orange.400">{variable.name}</Tag>
|
||||
)}
|
||||
{comparison.comparisonOperator && (
|
||||
<Text>{comparison?.comparisonOperator}</Text>
|
||||
)}
|
||||
{comparison?.value && (
|
||||
<Tag bgColor={'green.400'}>{comparison.value}</Tag>
|
||||
)}
|
||||
</HStack>
|
||||
)
|
||||
})}
|
||||
</Stack>
|
||||
)}
|
||||
<SourceEndpoint
|
||||
source={{
|
||||
blockId: step.blockId,
|
||||
stepId: step.id,
|
||||
conditionType: 'true',
|
||||
}}
|
||||
pos="absolute"
|
||||
top="7px"
|
||||
right="15px"
|
||||
/>
|
||||
<SourceEndpoint
|
||||
source={{
|
||||
blockId: step.blockId,
|
||||
stepId: step.id,
|
||||
conditionType: 'false',
|
||||
}}
|
||||
pos="absolute"
|
||||
bottom="7px"
|
||||
right="15px"
|
||||
/>
|
||||
</Flex>
|
||||
)
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import { Text } from '@chakra-ui/react'
|
||||
|
||||
type Props = { label?: string }
|
||||
|
||||
export const ConfigureContent = ({ label }: Props) => (
|
||||
<Text color={label ? 'currentcolor' : 'gray.500'} isTruncated>
|
||||
{label ?? 'Configure...'}
|
||||
</Text>
|
||||
)
|
@ -0,0 +1,16 @@
|
||||
import { Box, Text, Image } from '@chakra-ui/react'
|
||||
import { ImageBubbleStep } from 'models'
|
||||
|
||||
export const ImageBubbleContent = ({ step }: { step: ImageBubbleStep }) =>
|
||||
!step.content?.url ? (
|
||||
<Text color={'gray.500'}>Click to edit...</Text>
|
||||
) : (
|
||||
<Box w="full">
|
||||
<Image
|
||||
src={step.content?.url}
|
||||
alt="Step image"
|
||||
rounded="md"
|
||||
objectFit="cover"
|
||||
/>
|
||||
</Box>
|
||||
)
|
@ -0,0 +1,8 @@
|
||||
import React from 'react'
|
||||
import { Text } from '@chakra-ui/react'
|
||||
|
||||
type Props = { placeholder: string }
|
||||
|
||||
export const PlaceholderContent = ({ placeholder }: Props) => (
|
||||
<Text color={'gray.500'}>{placeholder}</Text>
|
||||
)
|
@ -0,0 +1,17 @@
|
||||
import { Text } from '@chakra-ui/react'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { SetVariableStep } from 'models'
|
||||
|
||||
export const SetVariableContent = ({ step }: { step: SetVariableStep }) => {
|
||||
const { typebot } = useTypebot()
|
||||
const variableName =
|
||||
typebot?.variables.byId[step.options?.variableId ?? '']?.name ?? ''
|
||||
const expression = step.options?.expressionToEvaluate ?? ''
|
||||
return (
|
||||
<Text color={'gray.500'}>
|
||||
{variableName === '' && expression === ''
|
||||
? 'Click to edit...'
|
||||
: `${variableName} = ${expression}`}
|
||||
</Text>
|
||||
)
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { Flex } from '@chakra-ui/react'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { TextBubbleStep } from 'models'
|
||||
import React from 'react'
|
||||
import { parseVariableHighlight } from 'services/utils'
|
||||
|
||||
type Props = {
|
||||
step: TextBubbleStep
|
||||
}
|
||||
|
||||
export const TextBubbleContent = ({ step }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
return (
|
||||
<Flex
|
||||
flexDir={'column'}
|
||||
opacity={step.content.html === '' ? '0.5' : '1'}
|
||||
className="slate-html-container"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
step.content.html === ''
|
||||
? `<p>Click to edit...</p>`
|
||||
: parseVariableHighlight(step.content.html, typebot),
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import { Box, Text } from '@chakra-ui/react'
|
||||
import { VideoBubbleStep, VideoBubbleContentType } from 'models'
|
||||
|
||||
export const VideoBubbleContent = ({ step }: { step: VideoBubbleStep }) => {
|
||||
if (!step.content?.url || !step.content.type)
|
||||
return <Text color="gray.500">Click to edit...</Text>
|
||||
switch (step.content.type) {
|
||||
case VideoBubbleContentType.URL:
|
||||
return (
|
||||
<Box w="full" h="120px" pos="relative">
|
||||
<video
|
||||
key={step.content.url}
|
||||
controls
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
left: '0',
|
||||
top: '0',
|
||||
borderRadius: '10px',
|
||||
}}
|
||||
>
|
||||
<source src={step.content.url} />
|
||||
</video>
|
||||
</Box>
|
||||
)
|
||||
case VideoBubbleContentType.VIMEO:
|
||||
case VideoBubbleContentType.YOUTUBE: {
|
||||
const baseUrl =
|
||||
step.content.type === VideoBubbleContentType.VIMEO
|
||||
? 'https://player.vimeo.com/video'
|
||||
: 'https://www.youtube.com/embed'
|
||||
return (
|
||||
<Box w="full" h="120px" pos="relative">
|
||||
<iframe
|
||||
src={`${baseUrl}/${step.content.id}`}
|
||||
allowFullScreen
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
left: '0',
|
||||
top: '0',
|
||||
borderRadius: '10px',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { Text } from '@chakra-ui/react'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { WebhookStep } from 'models'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
type Props = {
|
||||
step: WebhookStep
|
||||
}
|
||||
|
||||
export const WebhookContent = ({ step }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const webhook = useMemo(
|
||||
() => typebot?.webhooks.byId[step.options?.webhookId ?? ''],
|
||||
[step.options?.webhookId, typebot?.webhooks.byId]
|
||||
)
|
||||
if (!webhook?.url) return <Text color="gray.500">Configure...</Text>
|
||||
return (
|
||||
<Text isTruncated pr="6">
|
||||
{webhook.method} {webhook.url}
|
||||
</Text>
|
||||
)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import { InputStep } from 'models'
|
||||
import { chakra, Text } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
|
||||
type Props = {
|
||||
step: InputStep
|
||||
}
|
||||
|
||||
export const WithVariableContent = ({ step }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const variableName =
|
||||
typebot?.variables.byId[step.options.variableId as string].name
|
||||
return (
|
||||
<Text>
|
||||
Collect{' '}
|
||||
<chakra.span
|
||||
bgColor="orange.400"
|
||||
color="white"
|
||||
rounded="md"
|
||||
py="0.5"
|
||||
px="1"
|
||||
>
|
||||
{variableName}
|
||||
</chakra.span>
|
||||
</Text>
|
||||
)
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
export * from './ConditionContent'
|
||||
export * from './SetVariableContent'
|
||||
export * from './WithVariableContent'
|
||||
export * from './VideoBubbleContent'
|
||||
export * from './WebhookContent'
|
||||
export * from './TextBubbleContent'
|
@ -0,0 +1 @@
|
||||
export { StepNodeContent } from './StepNodeContent'
|
Reference in New Issue
Block a user