2
0

♻️ (builder) Change to features-centric folder structure

This commit is contained in:
Baptiste Arnaud
2022-11-15 09:35:48 +01:00
committed by Baptiste Arnaud
parent 3686465a85
commit 643571fe7d
683 changed files with 3907 additions and 3643 deletions

View File

@ -0,0 +1,7 @@
import { FilterIcon } from '@/components/icons'
import { IconProps } from '@chakra-ui/react'
import React from 'react'
export const ConditionIcon = (props: IconProps) => (
<FilterIcon color="purple.500" {...props} />
)

View File

@ -0,0 +1,73 @@
import { Stack, Tag, Text, Flex, Wrap } from '@chakra-ui/react'
import { useTypebot } from '@/features/editor'
import { Comparison, ConditionItem, ComparisonOperators } from 'models'
import React from 'react'
import { byId, isNotDefined } from 'utils'
type Props = {
item: ConditionItem
}
export const ConditionNodeContent = ({ item }: Props) => {
const { typebot } = useTypebot()
return (
<Flex px={2} py={2}>
{item.content.comparisons.length === 0 ||
comparisonIsEmpty(item.content.comparisons[0]) ? (
<Text color={'gray.500'}>Configure...</Text>
) : (
<Stack maxW="170px">
{item.content.comparisons.map((comparison, idx) => {
const variable = typebot?.variables.find(
byId(comparison.variableId)
)
return (
<Wrap key={comparison.id} spacing={1} noOfLines={1}>
{idx > 0 && <Text>{item.content.logicalOperator ?? ''}</Text>}
{variable?.name && (
<Tag bgColor="orange.400" color="white">
{variable.name}
</Tag>
)}
{comparison.comparisonOperator && (
<Text>
{parseComparisonOperatorSymbol(
comparison.comparisonOperator
)}
</Text>
)}
{comparison?.value && (
<Tag bgColor={'gray.200'}>
<Text noOfLines={1}>{comparison.value}</Text>
</Tag>
)}
</Wrap>
)
})}
</Stack>
)}
</Flex>
)
}
const comparisonIsEmpty = (comparison: Comparison) =>
isNotDefined(comparison.comparisonOperator) &&
isNotDefined(comparison.value) &&
isNotDefined(comparison.variableId)
const parseComparisonOperatorSymbol = (operator: ComparisonOperators) => {
switch (operator) {
case ComparisonOperators.CONTAINS:
return 'contains'
case ComparisonOperators.EQUAL:
return '='
case ComparisonOperators.GREATER:
return '>'
case ComparisonOperators.IS_SET:
return 'is set'
case ComparisonOperators.LESS:
return '<'
case ComparisonOperators.NOT_EQUAL:
return '!='
}
}

View File

@ -0,0 +1,50 @@
import { Stack } from '@chakra-ui/react'
import { DropdownList } from '@/components/DropdownList'
import { Comparison, Variable, ComparisonOperators } from 'models'
import { TableListItemProps } from '@/components/TableList'
import { VariableSearchInput } from '@/components/VariableSearchInput'
import { Input } from '@/components/inputs'
export const ComparisonItem = ({
item,
onItemChange,
}: TableListItemProps<Comparison>) => {
const handleSelectVariable = (variable?: Variable) => {
if (variable?.id === item.variableId) return
onItemChange({ ...item, variableId: variable?.id })
}
const handleSelectComparisonOperator = (
comparisonOperator: ComparisonOperators
) => {
if (comparisonOperator === item.comparisonOperator) return
onItemChange({ ...item, comparisonOperator })
}
const handleChangeValue = (value: string) => {
if (value === item.value) return
onItemChange({ ...item, value })
}
return (
<Stack p="4" rounded="md" flex="1" borderWidth="1px">
<VariableSearchInput
initialVariableId={item.variableId}
onSelectVariable={handleSelectVariable}
placeholder="Search for a variable"
/>
<DropdownList<ComparisonOperators>
currentItem={item.comparisonOperator}
onItemSelect={handleSelectComparisonOperator}
items={Object.values(ComparisonOperators)}
placeholder="Select an operator"
/>
{item.comparisonOperator !== ComparisonOperators.IS_SET && (
<Input
defaultValue={item.value ?? ''}
onChange={handleChangeValue}
placeholder="Type a value..."
/>
)}
</Stack>
)
}

View File

@ -0,0 +1,46 @@
import { Flex } from '@chakra-ui/react'
import { DropdownList } from '@/components/DropdownList'
import {
Comparison,
ConditionItem,
ConditionBlock,
LogicalOperator,
} from 'models'
import React from 'react'
import { ComparisonItem } from './ComparisonsItem'
import { TableList } from '@/components/TableList'
type ConditionSettingsBodyProps = {
block: ConditionBlock
onItemChange: (updates: Partial<ConditionItem>) => void
}
export const ConditionSettingsBody = ({
block,
onItemChange,
}: ConditionSettingsBodyProps) => {
const itemContent = block.items[0].content
const handleComparisonsChange = (comparisons: Comparison[]) =>
onItemChange({ content: { ...itemContent, comparisons } })
const handleLogicalOperatorChange = (logicalOperator: LogicalOperator) =>
onItemChange({ content: { ...itemContent, logicalOperator } })
return (
<TableList<Comparison>
initialItems={itemContent.comparisons}
onItemsChange={handleComparisonsChange}
Item={ComparisonItem}
ComponentBetweenItems={() => (
<Flex justify="center">
<DropdownList<LogicalOperator>
currentItem={itemContent.logicalOperator}
onItemSelect={handleLogicalOperatorChange}
items={Object.values(LogicalOperator)}
/>
</Flex>
)}
addLabel="Add a comparison"
/>
)
}

View File

@ -0,0 +1 @@
export { ConditionSettingsBody } from './ConditonSettingsBody'

View File

@ -0,0 +1,80 @@
import test, { expect } from '@playwright/test'
import { typebotViewer } from 'utils/playwright/testHelpers'
import { importTypebotInDatabase } from 'utils/playwright/databaseActions'
import cuid from 'cuid'
import { getTestAsset } from '@/test/utils/playwright'
const typebotId = cuid()
test.describe('Condition block', () => {
test('its configuration should work', async ({ page }) => {
await importTypebotInDatabase(
getTestAsset('typebots/logic/condition.json'),
{
id: typebotId,
}
)
await page.goto(`/typebots/${typebotId}/edit`)
await page.click('text=Configure... >> nth=0', { force: true })
await page.fill(
'input[placeholder="Search for a variable"] >> nth=-1',
'Age'
)
await page.click('button:has-text("Age")')
await page.click('button:has-text("Select an operator")')
await page.click('button:has-text("Greater than")', { force: true })
await page.fill('input[placeholder="Type a value..."]', '80')
await page.click('button:has-text("Add a comparison")')
await page.fill(
':nth-match(input[placeholder="Search for a variable"], 2)',
'Age'
)
await page.click('button:has-text("Age")')
await page.click('button:has-text("Select an operator")')
await page.click('button:has-text("Less than")', { force: true })
await page.fill(
':nth-match(input[placeholder="Type a value..."], 2)',
'100'
)
await page.click('text=Configure...', { force: true })
await page.fill(
'input[placeholder="Search for a variable"] >> nth=-1',
'Age'
)
await page.click('button:has-text("Age")')
await page.click('button:has-text("Select an operator")')
await page.click('button:has-text("Greater than")', { force: true })
await page.fill('input[placeholder="Type a value..."]', '20')
await page.click('text=Preview')
await typebotViewer(page)
.locator('input[placeholder="Type a number..."]')
.fill('15')
await typebotViewer(page).locator('text=Send').click()
await expect(
typebotViewer(page).locator('text=You are younger than 20')
).toBeVisible()
await page.click('text=Restart')
await typebotViewer(page)
.locator('input[placeholder="Type a number..."]')
.fill('45')
await typebotViewer(page).locator('text=Send').click()
await expect(
typebotViewer(page).locator('text=You are older than 20')
).toBeVisible()
await page.click('text=Restart')
await typebotViewer(page)
.locator('input[placeholder="Type a number..."]')
.fill('90')
await typebotViewer(page).locator('text=Send').click()
await expect(
typebotViewer(page).locator('text=You are older than 80')
).toBeVisible()
})
})

View File

@ -0,0 +1,3 @@
export { ConditionSettingsBody } from './components/ConditionSettingsBody'
export { ConditionNodeContent } from './components/ConditionNodeContent'
export { ConditionIcon } from './components/ConditionIcon'