@@ -6,17 +6,21 @@ import {
|
||||
Tag,
|
||||
Text,
|
||||
} from '@chakra-ui/react'
|
||||
import { GeneralSettings, rememberUserStorages } from '@typebot.io/schemas'
|
||||
import { Settings } from '@typebot.io/schemas'
|
||||
import React from 'react'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
|
||||
import { SwitchWithRelatedSettings } from '@/components/SwitchWithRelatedSettings'
|
||||
import { DropdownList } from '@/components/DropdownList'
|
||||
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'
|
||||
import {
|
||||
defaultSettings,
|
||||
rememberUserStorages,
|
||||
} from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
type Props = {
|
||||
generalSettings: GeneralSettings
|
||||
onGeneralSettingsChange: (generalSettings: GeneralSettings) => void
|
||||
generalSettings: Settings['general'] | undefined
|
||||
onGeneralSettingsChange: (generalSettings: Settings['general']) => void
|
||||
}
|
||||
|
||||
export const GeneralSettingsForm = ({
|
||||
@@ -27,7 +31,7 @@ export const GeneralSettingsForm = ({
|
||||
onGeneralSettingsChange({
|
||||
...generalSettings,
|
||||
rememberUser: {
|
||||
...generalSettings.rememberUser,
|
||||
...generalSettings?.rememberUser,
|
||||
isEnabled,
|
||||
},
|
||||
})
|
||||
@@ -45,12 +49,14 @@ export const GeneralSettingsForm = ({
|
||||
})
|
||||
|
||||
const updateRememberUserStorage = (
|
||||
storage: NonNullable<GeneralSettings['rememberUser']>['storage']
|
||||
storage: NonNullable<
|
||||
NonNullable<Settings['general']>['rememberUser']
|
||||
>['storage']
|
||||
) =>
|
||||
onGeneralSettingsChange({
|
||||
...generalSettings,
|
||||
rememberUser: {
|
||||
...generalSettings.rememberUser,
|
||||
...generalSettings?.rememberUser,
|
||||
storage,
|
||||
},
|
||||
})
|
||||
@@ -59,13 +65,19 @@ export const GeneralSettingsForm = ({
|
||||
<Stack spacing={6}>
|
||||
<SwitchWithLabel
|
||||
label="Prefill input"
|
||||
initialValue={generalSettings.isInputPrefillEnabled ?? true}
|
||||
initialValue={
|
||||
generalSettings?.isInputPrefillEnabled ??
|
||||
defaultSettings.general.isInputPrefillEnabled
|
||||
}
|
||||
onCheckChange={handleInputPrefillChange}
|
||||
moreInfoContent="Inputs are automatically pre-filled whenever their associated variable has a value"
|
||||
/>
|
||||
<SwitchWithLabel
|
||||
label="Hide query params on bot start"
|
||||
initialValue={generalSettings.isHideQueryParamsEnabled ?? true}
|
||||
initialValue={
|
||||
generalSettings?.isHideQueryParamsEnabled ??
|
||||
defaultSettings.general.isHideQueryParamsEnabled
|
||||
}
|
||||
onCheckChange={handleHideQueryParamsChange}
|
||||
moreInfoContent="If your URL contains query params, they will be automatically hidden when the bot starts."
|
||||
/>
|
||||
@@ -73,9 +85,9 @@ export const GeneralSettingsForm = ({
|
||||
label={'Remember user'}
|
||||
moreInfoContent="If enabled, user previous variables will be prefilled and his new answers will override the previous ones."
|
||||
initialValue={
|
||||
generalSettings.rememberUser?.isEnabled ??
|
||||
(isDefined(generalSettings.isNewResultOnRefreshEnabled)
|
||||
? !generalSettings.isNewResultOnRefreshEnabled
|
||||
generalSettings?.rememberUser?.isEnabled ??
|
||||
(isDefined(generalSettings?.isNewResultOnRefreshEnabled)
|
||||
? !generalSettings?.isNewResultOnRefreshEnabled
|
||||
: false)
|
||||
}
|
||||
onCheckChange={toggleRememberUser}
|
||||
@@ -97,7 +109,7 @@ export const GeneralSettingsForm = ({
|
||||
</MoreInfoTooltip>
|
||||
</FormLabel>
|
||||
<DropdownList
|
||||
currentItem={generalSettings.rememberUser?.storage ?? 'session'}
|
||||
currentItem={generalSettings?.rememberUser?.storage ?? 'session'}
|
||||
onItemSelect={updateRememberUserStorage}
|
||||
items={rememberUserStorages}
|
||||
></DropdownList>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import { Metadata } from '@typebot.io/schemas'
|
||||
import { Settings } from '@typebot.io/schemas'
|
||||
import {
|
||||
FormLabel,
|
||||
Popover,
|
||||
@@ -14,13 +14,15 @@ import { CodeEditor } from '@/components/inputs/CodeEditor'
|
||||
import { ImageUploadContent } from '@/components/ImageUploadContent'
|
||||
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'
|
||||
import { TextInput, Textarea } from '@/components/inputs'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
type Props = {
|
||||
workspaceId: string
|
||||
typebotId: string
|
||||
typebotName: string
|
||||
metadata: Metadata
|
||||
onMetadataChange: (metadata: Metadata) => void
|
||||
metadata: Settings['metadata']
|
||||
onMetadataChange: (metadata: Settings['metadata']) => void
|
||||
}
|
||||
|
||||
export const MetadataForm = ({
|
||||
@@ -43,6 +45,14 @@ export const MetadataForm = ({
|
||||
const handleHeadCodeChange = (customHeadCode: string) =>
|
||||
onMetadataChange({ ...metadata, customHeadCode })
|
||||
|
||||
const favIconUrl =
|
||||
metadata?.favIconUrl ??
|
||||
defaultSettings.metadata.favIconUrl(env.NEXT_PUBLIC_VIEWER_URL[0])
|
||||
|
||||
const imageUrl =
|
||||
metadata?.imageUrl ??
|
||||
defaultSettings.metadata.imageUrl(env.NEXT_PUBLIC_VIEWER_URL[0])
|
||||
|
||||
return (
|
||||
<Stack spacing="6">
|
||||
<Stack>
|
||||
@@ -52,7 +62,7 @@ export const MetadataForm = ({
|
||||
<Popover isLazy placement="top">
|
||||
<PopoverTrigger>
|
||||
<Image
|
||||
src={metadata.favIconUrl ?? '/favicon.png'}
|
||||
src={favIconUrl}
|
||||
w="20px"
|
||||
alt="Fav icon"
|
||||
cursor="pointer"
|
||||
@@ -68,7 +78,7 @@ export const MetadataForm = ({
|
||||
typebotId,
|
||||
fileName: 'favIcon',
|
||||
}}
|
||||
defaultUrl={metadata.favIconUrl ?? ''}
|
||||
defaultUrl={favIconUrl}
|
||||
onSubmit={handleFavIconSubmit}
|
||||
excludedTabs={['giphy', 'unsplash', 'emoji']}
|
||||
imageSize="thumb"
|
||||
@@ -83,7 +93,7 @@ export const MetadataForm = ({
|
||||
<Popover isLazy placement="top">
|
||||
<PopoverTrigger>
|
||||
<Image
|
||||
src={metadata.imageUrl ?? '/viewer-preview.png'}
|
||||
src={imageUrl}
|
||||
alt="Website image"
|
||||
cursor="pointer"
|
||||
_hover={{ filter: 'brightness(.9)' }}
|
||||
@@ -98,7 +108,7 @@ export const MetadataForm = ({
|
||||
typebotId,
|
||||
fileName: 'ogImage',
|
||||
}}
|
||||
defaultUrl={metadata.imageUrl}
|
||||
defaultUrl={imageUrl}
|
||||
onSubmit={handleImageSubmit}
|
||||
excludedTabs={['giphy', 'icon', 'emoji']}
|
||||
/>
|
||||
@@ -107,16 +117,18 @@ export const MetadataForm = ({
|
||||
</Stack>
|
||||
<TextInput
|
||||
label="Title:"
|
||||
defaultValue={metadata.title ?? typebotName}
|
||||
defaultValue={metadata?.title ?? typebotName}
|
||||
onChange={handleTitleChange}
|
||||
/>
|
||||
<Textarea
|
||||
defaultValue={metadata.description}
|
||||
defaultValue={
|
||||
metadata?.description ?? defaultSettings.metadata.description
|
||||
}
|
||||
onChange={handleDescriptionChange}
|
||||
label="Description:"
|
||||
/>
|
||||
<TextInput
|
||||
defaultValue={metadata.googleTagManagerId}
|
||||
defaultValue={metadata?.googleTagManagerId}
|
||||
placeholder="GTM-XXXXXX"
|
||||
onChange={handleGoogleTagManagerIdChange}
|
||||
label="Google Tag Manager ID:"
|
||||
@@ -132,7 +144,7 @@ export const MetadataForm = ({
|
||||
</HStack>
|
||||
<CodeEditor
|
||||
id="head"
|
||||
defaultValue={metadata.customHeadCode ?? ''}
|
||||
defaultValue={metadata?.customHeadCode}
|
||||
onChange={handleHeadCodeChange}
|
||||
lang="html"
|
||||
withVariableButton={false}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Stack,
|
||||
} from '@chakra-ui/react'
|
||||
import { ChatIcon, CodeIcon, MoreVerticalIcon } from '@/components/icons'
|
||||
import { GeneralSettings, Metadata, TypingEmulation } from '@typebot.io/schemas'
|
||||
import { Settings } from '@typebot.io/schemas'
|
||||
import React from 'react'
|
||||
import { GeneralSettingsForm } from './GeneralSettingsForm'
|
||||
import { MetadataForm } from './MetadataForm'
|
||||
@@ -20,17 +20,19 @@ import { headerHeight } from '@/features/editor/constants'
|
||||
export const SettingsSideMenu = () => {
|
||||
const { typebot, updateTypebot } = useTypebot()
|
||||
|
||||
const handleTypingEmulationChange = (typingEmulation: TypingEmulation) =>
|
||||
const updateTypingEmulation = (
|
||||
typingEmulation: Settings['typingEmulation']
|
||||
) =>
|
||||
typebot &&
|
||||
updateTypebot({
|
||||
updates: { settings: { ...typebot.settings, typingEmulation } },
|
||||
})
|
||||
|
||||
const handleGeneralSettingsChange = (general: GeneralSettings) =>
|
||||
const handleGeneralSettingsChange = (general: Settings['general']) =>
|
||||
typebot &&
|
||||
updateTypebot({ updates: { settings: { ...typebot.settings, general } } })
|
||||
|
||||
const handleMetadataChange = (metadata: Metadata) =>
|
||||
const handleMetadataChange = (metadata: Settings['metadata']) =>
|
||||
typebot &&
|
||||
updateTypebot({ updates: { settings: { ...typebot.settings, metadata } } })
|
||||
|
||||
@@ -78,7 +80,7 @@ export const SettingsSideMenu = () => {
|
||||
{typebot && (
|
||||
<TypingEmulationForm
|
||||
typingEmulation={typebot.settings.typingEmulation}
|
||||
onUpdate={handleTypingEmulationChange}
|
||||
onUpdate={updateTypingEmulation}
|
||||
/>
|
||||
)}
|
||||
</AccordionPanel>
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { Flex, FormLabel, Stack, Switch } from '@chakra-ui/react'
|
||||
import { TypingEmulation } from '@typebot.io/schemas'
|
||||
import { Stack } from '@chakra-ui/react'
|
||||
import { Settings } from '@typebot.io/schemas'
|
||||
import React from 'react'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { NumberInput } from '@/components/inputs'
|
||||
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
type Props = {
|
||||
typingEmulation: TypingEmulation
|
||||
onUpdate: (typingEmulation: TypingEmulation) => void
|
||||
typingEmulation: Settings['typingEmulation']
|
||||
onUpdate: (typingEmulation: Settings['typingEmulation']) => void
|
||||
}
|
||||
|
||||
export const TypingEmulationForm = ({ typingEmulation, onUpdate }: Props) => {
|
||||
const handleSwitchChange = () =>
|
||||
const updateIsEnabled = (enabled: boolean) =>
|
||||
onUpdate({
|
||||
...typingEmulation,
|
||||
enabled: !typingEmulation.enabled,
|
||||
enabled,
|
||||
})
|
||||
|
||||
const handleSpeedChange = (speed?: number) =>
|
||||
@@ -22,24 +24,24 @@ export const TypingEmulationForm = ({ typingEmulation, onUpdate }: Props) => {
|
||||
const handleMaxDelayChange = (maxDelay?: number) =>
|
||||
isDefined(maxDelay) && onUpdate({ ...typingEmulation, maxDelay })
|
||||
|
||||
const isEnabled =
|
||||
typingEmulation?.enabled ?? defaultSettings.typingEmulation.enabled
|
||||
|
||||
return (
|
||||
<Stack spacing={6}>
|
||||
<Flex justifyContent="space-between" align="center">
|
||||
<FormLabel htmlFor="typing-emulation" mb="0">
|
||||
Typing emulation
|
||||
</FormLabel>
|
||||
<Switch
|
||||
id="typing-emulation"
|
||||
isChecked={typingEmulation.enabled}
|
||||
onChange={handleSwitchChange}
|
||||
/>
|
||||
</Flex>
|
||||
{typingEmulation.enabled && (
|
||||
<SwitchWithLabel
|
||||
label={'Typing emulation'}
|
||||
initialValue={isEnabled}
|
||||
onCheckChange={updateIsEnabled}
|
||||
/>
|
||||
{isEnabled && (
|
||||
<Stack pl={10}>
|
||||
<NumberInput
|
||||
label="Words per minutes:"
|
||||
data-testid="speed"
|
||||
defaultValue={typingEmulation.speed}
|
||||
defaultValue={
|
||||
typingEmulation?.speed ?? defaultSettings.typingEmulation.speed
|
||||
}
|
||||
onValueChange={handleSpeedChange}
|
||||
withVariableButton={false}
|
||||
maxW="100px"
|
||||
@@ -48,7 +50,10 @@ export const TypingEmulationForm = ({ typingEmulation, onUpdate }: Props) => {
|
||||
<NumberInput
|
||||
label="Max delay (in seconds):"
|
||||
data-testid="max-delay"
|
||||
defaultValue={typingEmulation.maxDelay}
|
||||
defaultValue={
|
||||
typingEmulation?.maxDelay ??
|
||||
defaultSettings.typingEmulation.maxDelay
|
||||
}
|
||||
onValueChange={handleMaxDelayChange}
|
||||
withVariableButton={false}
|
||||
maxW="100px"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getTestAsset } from '@/test/utils/playwright'
|
||||
import test, { expect } from '@playwright/test'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { defaultTextInputOptions } from '@typebot.io/schemas'
|
||||
import { importTypebotInDatabase } from '@typebot.io/lib/playwright/databaseActions'
|
||||
import { defaultTextInputOptions } from '@typebot.io/schemas/features/blocks/inputs/text/constants'
|
||||
|
||||
test.describe.parallel('Settings page', () => {
|
||||
test.describe('General', () => {
|
||||
@@ -65,7 +65,10 @@ test.describe.parallel('Settings page', () => {
|
||||
|
||||
// Fav icon
|
||||
const favIconImg = page.locator('img >> nth=0')
|
||||
await expect(favIconImg).toHaveAttribute('src', '/favicon.png')
|
||||
await expect(favIconImg).toHaveAttribute(
|
||||
'src',
|
||||
'http://localhost:3001/favicon.png'
|
||||
)
|
||||
await favIconImg.click()
|
||||
await expect(page.locator('text=Giphy')).toBeHidden()
|
||||
await page.click('button:has-text("Link")')
|
||||
@@ -80,7 +83,10 @@ test.describe.parallel('Settings page', () => {
|
||||
|
||||
// Website image
|
||||
const websiteImg = page.locator('img >> nth=1')
|
||||
await expect(websiteImg).toHaveAttribute('src', '/viewer-preview.png')
|
||||
await expect(websiteImg).toHaveAttribute(
|
||||
'src',
|
||||
'http://localhost:3001/site-preview.png'
|
||||
)
|
||||
await websiteImg.click()
|
||||
await expect(page.locator('text=Giphy')).toBeHidden()
|
||||
await page.click('button >> text="Link"')
|
||||
|
||||
Reference in New Issue
Block a user