2
0

(variables) Add session option in variables (#1490)

Closes #1488
This commit is contained in:
Baptiste Arnaud
2024-05-02 15:11:16 +02:00
committed by GitHub
parent 87653f8e10
commit b4ae098440
20 changed files with 156 additions and 82 deletions

View File

@ -99,7 +99,7 @@
},
"devDependencies": {
"@chakra-ui/styled-system": "2.9.1",
"@playwright/test": "1.41.2",
"@playwright/test": "1.43.1",
"@typebot.io/billing": "workspace:*",
"@typebot.io/forge": "workspace:*",
"@typebot.io/forge-repository": "workspace:*",
@ -123,12 +123,13 @@
"@types/qs": "6.9.7",
"@types/react": "18.2.15",
"@types/tinycolor2": "1.4.3",
"dotenv-cli": "7.2.1",
"dotenv-cli": "7.4.1",
"eslint": "8.44.0",
"eslint-config-custom": "workspace:*",
"next-runtime-env": "1.6.2",
"superjson": "1.12.4",
"typescript": "5.4.5",
"zod": "3.22.4"
"zod": "3.22.4",
"dotenv": "16.4.5"
}
}

View File

@ -1,4 +1,8 @@
import { defineConfig, devices } from '@playwright/test'
import { resolve } from 'path'
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('dotenv').config({ path: resolve(__dirname, '../../.env') })
export default defineConfig({
timeout: process.env.CI ? 50 * 1000 : 40 * 1000,

View File

@ -6,8 +6,9 @@ import {
Switch,
SwitchProps,
} from '@chakra-ui/react'
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { MoreInfoTooltip } from '../MoreInfoTooltip'
import { isDefined } from '@typebot.io/lib'
export type SwitchWithLabelProps = {
label: string
@ -32,6 +33,11 @@ export const SwitchWithLabel = ({
if (onCheckChange) onCheckChange(!isChecked)
}
useEffect(() => {
if (isChecked === undefined && isDefined(initialValue))
setIsChecked(initialValue)
}, [initialValue, isChecked])
return (
<FormControl as={HStack} justifyContent={justifyContent}>
<FormLabel mb="0">

View File

@ -123,7 +123,7 @@ export const VariableSearchInput = ({
if (!inputValue || inputValue === '') return
const id = 'v' + createId()
onSelectVariable({ id, name: inputValue })
createVariable({ id, name: inputValue })
createVariable({ id, name: inputValue, isSessionVariable: true })
inputRef.current?.blur()
onClose()
}

View File

@ -24,7 +24,7 @@ test.describe('Date input block', () => {
'date'
)
await page.locator('[data-testid="from-date"]').fill('2021-01-01')
await page.getByRole('button', { name: 'Send' }).click()
await page.locator('form').getByRole('button').click()
await expect(page.locator('text="01/01/2021"')).toBeVisible()
await page.click(`text=Pick a date`)

View File

@ -32,7 +32,7 @@ test('options should work', async ({ page }) => {
await page.click('text=Test')
await expect(page.locator(`text=Send`)).toBeHidden()
await page.getByRole('button', { name: '8' }).click()
await page.getByRole('checkbox', { name: '8' }).click()
await page.locator(`text=Send`).click()
await expect(page.getByTestId('guest-bubble')).toHaveText('8')
await page.click('text=Rate from 0 to 10')

View File

@ -1,7 +1,6 @@
import { Alert, AlertIcon, FormLabel, Stack, Tag, Text } from '@chakra-ui/react'
import { CodeEditor } from '@/components/inputs/CodeEditor'
import { SetVariableBlock, Variable } from '@typebot.io/schemas'
import React from 'react'
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
import { Select } from '@/components/inputs/Select'
@ -13,6 +12,7 @@ import {
} from '@typebot.io/schemas/features/blocks/logic/setVariable/constants'
import { TextInput } from '@/components/inputs'
import { isDefined } from '@typebot.io/lib'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
type Props = {
options: SetVariableBlock['options']
@ -24,7 +24,12 @@ const setVarTypes = valueTypes.filter(
)
export const SetVariableSettings = ({ options, onOptionsChange }: Props) => {
const updateVariableId = (variable?: Variable) =>
const { typebot, updateVariable } = useTypebot()
const selectedVariable = typebot?.variables.find(
(variable) => variable.id === options?.variableId
)
const updateVariableId = (variable?: Pick<Variable, 'id'>) =>
onOptionsChange({
...options,
variableId: variable?.id,
@ -36,6 +41,13 @@ export const SetVariableSettings = ({ options, onOptionsChange }: Props) => {
type: type as NonNullable<SetVariableBlock['options']>['type'],
})
const updateIsSessionVariable = (isSavingInResults: boolean) => {
if (!selectedVariable?.id) return
updateVariable(selectedVariable.id, {
isSessionVariable: !isSavingInResults,
})
}
return (
<Stack spacing={4}>
<Stack>
@ -49,22 +61,34 @@ export const SetVariableSettings = ({ options, onOptionsChange }: Props) => {
/>
</Stack>
<Stack>
<Text mb="0" fontWeight="medium">
Value:
</Text>
<Select
selectedItem={options?.type ?? defaultSetVariableOptions.type}
items={setVarTypes.map((type) => ({
label: type,
value: type,
icon:
type === 'Contact name' || type === 'Phone number' ? (
<WhatsAppLogo />
) : undefined,
}))}
onSelect={updateValueType}
/>
<Stack spacing="4">
<Stack>
<Text mb="0" fontWeight="medium">
Value:
</Text>
<Select
selectedItem={options?.type ?? defaultSetVariableOptions.type}
items={setVarTypes.map((type) => ({
label: type,
value: type,
icon:
type === 'Contact name' || type === 'Phone number' ? (
<WhatsAppLogo />
) : undefined,
}))}
onSelect={updateValueType}
/>
</Stack>
{selectedVariable && (
<SwitchWithLabel
key={selectedVariable.id}
label="Save in results?"
moreInfoContent="By default, the variable is saved only for the user chat session. Check this option if you want to also store the variable in the typebot Results table."
initialValue={!selectedVariable.isSessionVariable}
onCheckChange={updateIsSessionVariable}
/>
)}
<SetVariableValue options={options} onOptionsChange={onOptionsChange} />
</Stack>
</Stack>
@ -91,7 +115,7 @@ const SetVariableValue = ({
isExecutedOnClient,
})
const updateItemVariableId = (variable?: Variable) => {
const updateItemVariableId = (variable?: Pick<Variable, 'id'>) => {
if (!options || options.type !== 'Map item with same index') return
onOptionsChange({
...options,
@ -102,7 +126,7 @@ const SetVariableValue = ({
})
}
const updateBaseListVariableId = (variable?: Variable) => {
const updateBaseListVariableId = (variable?: Pick<Variable, 'id'>) => {
if (!options || options.type !== 'Map item with same index') return
onOptionsChange({
...options,
@ -113,7 +137,7 @@ const SetVariableValue = ({
})
}
const updateTargetListVariableId = (variable?: Variable) => {
const updateTargetListVariableId = (variable?: Pick<Variable, 'id'>) => {
if (!options || options.type !== 'Map item with same index') return
onOptionsChange({
...options,

View File

@ -28,11 +28,16 @@ test.describe('Set variable block', () => {
.fill('1000 * {{Num}}')
await page.click('text=Click to edit...', { force: true })
await expect(page.getByText('Save in results?')).toBeHidden()
await page.fill(
'input[placeholder="Select a variable"] >> nth=-1',
'Custom var'
)
await page.getByRole('menuitem', { name: 'Create Custom var' }).click()
await expect(page.getByText('Save in results?')).toBeVisible()
await expect(
page.getByRole('group').nth(1).locator('.chakra-switch')
).not.toHaveAttribute('data-checked')
await page
.getByTestId('code-editor')
.getByRole('textbox')

View File

@ -33,6 +33,7 @@ test('table features should work', async ({ page }) => {
await expect(page.locator('text=Additional information')).toBeVisible()
await expect(page.locator('text=utm_source')).toBeVisible()
await expect(page.locator('text=utm_userid')).toBeVisible()
await expect(page.locator('text=utm_session')).toBeHidden()
})
await test.step('Resize columns', async () => {

View File

@ -177,7 +177,7 @@ test("can't add new members when limit is reached", async ({ page }) => {
await page.click('button >> text="Invite"')
await expect(
page.locator(
'text="Upgrade your plan to work with more team members, and unlock awesome power features 🚀"'
'text="Upgrade your plan to work with more team members, and unlock awesome power features"'
)
).toBeVisible()
await expect(page.locator('button >> text="Invite"')).toBeDisabled()

View File

@ -258,8 +258,21 @@
}
],
"variables": [
{ "id": "giiLFGw5xXBCHzvp1qAbdX", "name": "Name" },
{ "id": "3VFChNVSCXQ2rXv4DrJ8Ah", "name": "Email" },
{
"id": "giiLFGw5xXBCHzvp1qAbdX",
"name": "Name",
"isSessionVariable": true
},
{
"id": "3VFChNVSCXQ2rXv4DrJ8Ah",
"name": "Email",
"isSessionVariable": true
},
{
"id": "8Q8t9YCc3ieAEXSYkmnCxH",
"name": "utm_session",
"isSessionVariable": true
},
{ "id": "8Q8t9YCc3ieAEXSYkmnCxH", "name": "utm_source" },
{ "id": "ds54chWAyWC4zjkdWAm3Vc", "name": "utm_userid" }
],