@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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">
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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`)
|
||||
|
@ -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')
|
||||
|
@ -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,
|
||||
|
@ -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')
|
||||
|
@ -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 () => {
|
||||
|
@ -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()
|
||||
|
@ -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" }
|
||||
],
|
||||
|
@ -126,6 +126,14 @@ This value block allows you to find the `Id` from `Ids` with the same index as `
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## Save in results
|
||||
|
||||
By default, new variables are not persisted in the [Results](../../../results) table. They are only stored for the current user chat session. Enabling this option will save the variable in the `Results` table.
|
||||
|
||||
## Execute on client
|
||||
|
||||
This option is useful when you want to execute the custom code on the client side. This is only necessary when you need access the user's browser information. So, if you need access to `window`, `document`, `navigator`, etc., you should enable this option.
|
||||
|
||||
## Get user's geo location
|
||||
|
||||
For this you can provide the following custom code:
|
||||
|
@ -40,7 +40,7 @@
|
||||
"dotenv": "16.4.5",
|
||||
"@faire/mjml-react": "3.3.0",
|
||||
"@paralleldrive/cuid2": "2.2.1",
|
||||
"@playwright/test": "1.36.0",
|
||||
"@playwright/test": "1.43.1",
|
||||
"@typebot.io/emails": "workspace:*",
|
||||
"@typebot.io/env": "workspace:*",
|
||||
"@typebot.io/forge": "workspace:*",
|
||||
@ -55,7 +55,8 @@
|
||||
"@types/papaparse": "5.3.7",
|
||||
"@types/qs": "6.9.7",
|
||||
"@types/react": "18.2.15",
|
||||
"dotenv-cli": "7.2.1",
|
||||
"dotenv-cli": "7.4.1",
|
||||
"dotenv": "16.4.5",
|
||||
"eslint": "8.44.0",
|
||||
"eslint-config-custom": "workspace:*",
|
||||
"google-auth-library": "8.9.0",
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { defineConfig, devices } from '@playwright/test'
|
||||
import { resolve } from 'path'
|
||||
|
||||
process.env.SKIP_ENV_CHECK = 'true'
|
||||
// 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,
|
||||
|
Reference in New Issue
Block a user