2
0

refactor(♻️ Add defaults everywhere (+ settings page)):

This commit is contained in:
Baptiste Arnaud
2022-01-25 18:19:37 +01:00
parent 21448bcc8a
commit c5aaa323d1
115 changed files with 1436 additions and 720 deletions

View File

@ -0,0 +1,33 @@
import { Flex, FormLabel, Stack, Switch, Text } from '@chakra-ui/react'
import { GeneralSettings } from 'models'
import React from 'react'
type Props = {
generalSettings: GeneralSettings
onGeneralSettingsChange: (generalSettings: GeneralSettings) => void
}
export const GeneralSettingsForm = ({
generalSettings,
onGeneralSettingsChange,
}: Props) => {
const handleSwitchChange = () =>
onGeneralSettingsChange({
isBrandingEnabled: !generalSettings?.isBrandingEnabled,
})
return (
<Stack spacing={6}>
<Flex justifyContent="space-between" align="center">
<FormLabel htmlFor="branding" mb="0">
Typebot.io branding
</FormLabel>
<Switch
id="branding"
isChecked={generalSettings.isBrandingEnabled}
onChange={handleSwitchChange}
/>
</Flex>
</Stack>
)
}

View File

@ -0,0 +1,112 @@
import React from 'react'
import { Metadata } from 'models'
import {
FormLabel,
Popover,
PopoverTrigger,
Stack,
Image,
PopoverContent,
} from '@chakra-ui/react'
import { ImageUploadContent } from 'components/shared/ImageUploadContent'
import {
InputWithVariableButton,
TextareaWithVariableButton,
} from 'components/shared/TextboxWithVariableButton'
type Props = {
typebotName: string
metadata: Metadata
onMetadataChange: (metadata: Metadata) => void
}
export const MetadataForm = ({
typebotName,
metadata,
onMetadataChange,
}: Props) => {
const handleTitleChange = (title: string) =>
onMetadataChange({ ...metadata, title })
const handleDescriptionChange = (description: string) =>
onMetadataChange({ ...metadata, description })
const handleFavIconSubmit = (favIconUrl: string) =>
onMetadataChange({ ...metadata, favIconUrl })
const handleImageSubmit = (imageUrl: string) =>
onMetadataChange({ ...metadata, imageUrl })
return (
<Stack spacing="6">
<Stack>
<FormLabel mb="0" htmlFor="icon">
Icon:
</FormLabel>
<Popover isLazy>
<PopoverTrigger>
<Image
src={metadata.favIconUrl ?? '/favicon.png'}
w="20px"
alt="Fav icon"
cursor="pointer"
_hover={{ filter: 'brightness(.9)' }}
transition="filter 200ms"
rounded="md"
/>
</PopoverTrigger>
<PopoverContent p="4">
<ImageUploadContent
url={metadata.favIconUrl ?? ''}
onSubmit={handleFavIconSubmit}
isGiphyEnabled={false}
/>
</PopoverContent>
</Popover>
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="image">
Image:
</FormLabel>
<Popover isLazy>
<PopoverTrigger>
<Image
src={metadata.imageUrl ?? '/viewer-preview.png'}
alt="Website image"
cursor="pointer"
_hover={{ filter: 'brightness(.9)' }}
transition="filter 200ms"
rounded="md"
/>
</PopoverTrigger>
<PopoverContent p="4">
<ImageUploadContent
url={metadata.imageUrl}
onSubmit={handleImageSubmit}
isGiphyEnabled={false}
/>
</PopoverContent>
</Popover>
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="title">
Title:
</FormLabel>
<InputWithVariableButton
id="title"
initialValue={metadata.title ?? typebotName}
delay={100}
onChange={handleTitleChange}
/>
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="description">
Description:
</FormLabel>
<TextareaWithVariableButton
id="description"
initialValue={metadata.description}
delay={100}
onChange={handleDescriptionChange}
/>
</Stack>
</Stack>
)
}

View File

@ -1,26 +0,0 @@
import { Flex, Stack } from '@chakra-ui/react'
import { TypingEmulationSettings } from 'models'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import React from 'react'
import { TypingEmulation } from './TypingEmulation'
export const SettingsContent = () => {
const { typebot, updateTypebot } = useTypebot()
const handleTypingEmulationUpdate = (
typingEmulation: TypingEmulationSettings
) => {
if (!typebot) return
updateTypebot({ settings: { ...typebot.settings, typingEmulation } })
}
return (
<Flex h="full" w="full" justifyContent="center" align="flex-start">
<Stack p="6" rounded="md" borderWidth={1} w="600px" minH="500px" mt={10}>
<TypingEmulation
typingEmulation={typebot?.settings?.typingEmulation}
onUpdate={handleTypingEmulationUpdate}
/>
</Stack>
</Flex>
)
}

View File

@ -0,0 +1,103 @@
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Heading,
HStack,
Stack,
} from '@chakra-ui/react'
import { ChatIcon, CodeIcon, MoreVerticalIcon } from 'assets/icons'
import { headerHeight } from 'components/shared/TypebotHeader'
import { useTypebot } from 'contexts/TypebotContext'
import { GeneralSettings, Metadata, TypingEmulation } from 'models'
import React from 'react'
import { GeneralSettingsForm } from './GeneralSettingsForm'
import { MetadataForm } from './MetadataForm'
import { TypingEmulationForm } from './TypingEmulationForm'
export const SettingsSideMenu = () => {
const { typebot, updateTypebot } = useTypebot()
const handleTypingEmulationChange = (typingEmulation: TypingEmulation) =>
typebot &&
updateTypebot({ settings: { ...typebot.settings, typingEmulation } })
const handleGeneralSettingsChange = (general: GeneralSettings) =>
typebot && updateTypebot({ settings: { ...typebot.settings, general } })
const handleMetadataChange = (metadata: Metadata) =>
typebot && updateTypebot({ settings: { ...typebot.settings, metadata } })
return (
<Stack
flex="1"
maxW="400px"
height={`calc(100vh - ${headerHeight}px)`}
borderRightWidth={1}
pt={10}
spacing={10}
overflowY="scroll"
pb="20"
>
<Heading fontSize="xl" textAlign="center">
Settings
</Heading>
<Accordion allowMultiple allowToggle>
<AccordionItem>
<AccordionButton py={6}>
<HStack flex="1" pl={2}>
<MoreVerticalIcon transform={'rotate(90deg)'} />
<Heading fontSize="lg">General</Heading>
</HStack>
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} px="6">
{typebot && (
<GeneralSettingsForm
generalSettings={typebot.settings.general}
onGeneralSettingsChange={handleGeneralSettingsChange}
/>
)}
</AccordionPanel>
</AccordionItem>
<AccordionItem>
<AccordionButton py={6}>
<HStack flex="1" pl={2}>
<ChatIcon />
<Heading fontSize="lg">Typing emulation</Heading>
</HStack>
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} px="6">
{typebot && (
<TypingEmulationForm
typingEmulation={typebot.settings.typingEmulation}
onUpdate={handleTypingEmulationChange}
/>
)}
</AccordionPanel>
</AccordionItem>
<AccordionItem>
<AccordionButton py={6}>
<HStack flex="1" pl={2}>
<CodeIcon />
<Heading fontSize="lg">Metadata</Heading>
</HStack>
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} px="6">
{typebot && (
<MetadataForm
typebotName={typebot.name}
metadata={typebot.settings.metadata}
onMetadataChange={handleMetadataChange}
/>
)}
</AccordionPanel>
</AccordionItem>
</Accordion>
</Stack>
)
}

View File

@ -1,39 +0,0 @@
import {
NumberInputProps,
NumberInput,
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper,
} from '@chakra-ui/react'
import { useState, useEffect } from 'react'
export const SmartNumberInput = ({
initialValue,
onValueChange,
...props
}: {
initialValue?: number
onValueChange: (value?: number) => void
} & NumberInputProps) => {
const [value, setValue] = useState(initialValue?.toString() ?? '')
useEffect(() => {
if (value.endsWith('.') || value.endsWith(',')) return
if (value === '') onValueChange(undefined)
const newValue = parseFloat(value)
if (isNaN(newValue)) return
onValueChange(newValue)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value])
return (
<NumberInput onChange={setValue} value={value} {...props}>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
)
}

View File

@ -1,63 +0,0 @@
import { Flex, Stack, Switch, Text } from '@chakra-ui/react'
import { TypingEmulationSettings } from 'models'
import React from 'react'
import { SmartNumberInput } from './SmartNumberInput'
type TypingEmulationProps = {
typingEmulation?: TypingEmulationSettings
onUpdate: (typingEmulation: TypingEmulationSettings) => void
}
export const TypingEmulation = ({
typingEmulation,
onUpdate,
}: TypingEmulationProps) => {
const handleSwitchChange = () => {
if (!typingEmulation) return
onUpdate({ ...typingEmulation, enabled: !typingEmulation.enabled })
}
const handleSpeedChange = (speed?: number) => {
if (!typingEmulation) return
onUpdate({ ...typingEmulation, speed: speed ?? 0 })
}
const handleMaxDelayChange = (maxDelay?: number) => {
if (!typingEmulation) return
onUpdate({ ...typingEmulation, maxDelay: maxDelay ?? 0 })
}
return (
<Stack spacing={4}>
<Flex justifyContent="space-between" align="center">
<Text>Typing emulation</Text>
<Switch
isChecked={typingEmulation?.enabled}
onChange={handleSwitchChange}
/>
</Flex>
{typingEmulation?.enabled && (
<Stack pl={10}>
<Flex justify="space-between" align="center">
<Text>Words per minutes:</Text>
<SmartNumberInput
initialValue={typingEmulation.speed}
onValueChange={handleSpeedChange}
maxW="100px"
step={30}
/>
</Flex>
<Flex justify="space-between" align="center">
<Text>Max delay (in seconds):</Text>
<SmartNumberInput
initialValue={typingEmulation.maxDelay}
onValueChange={handleMaxDelayChange}
maxW="100px"
step={0.1}
/>
</Flex>
</Stack>
)}
</Stack>
)
}

View File

@ -0,0 +1,69 @@
import { Flex, FormLabel, Stack, Switch, Text } from '@chakra-ui/react'
import { TypingEmulation } from 'models'
import React from 'react'
import { isDefined } from 'utils'
import { SmartNumberInput } from '../shared/SmartNumberInput'
type Props = {
typingEmulation: TypingEmulation
onUpdate: (typingEmulation: TypingEmulation) => void
}
export const TypingEmulationForm = ({ typingEmulation, onUpdate }: Props) => {
const handleSwitchChange = () =>
onUpdate({
...typingEmulation,
enabled: !typingEmulation.enabled,
})
const handleSpeedChange = (speed?: number) =>
isDefined(speed) && onUpdate({ ...typingEmulation, speed })
const handleMaxDelayChange = (maxDelay?: number) =>
isDefined(maxDelay) && onUpdate({ ...typingEmulation, maxDelay })
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 && (
<Stack pl={10}>
<Flex justify="space-between" align="center">
<FormLabel htmlFor="speed" mb="0">
Words per minutes:
</FormLabel>
<SmartNumberInput
id="speed"
data-testid="speed"
value={typingEmulation.speed}
onValueChange={handleSpeedChange}
maxW="100px"
step={30}
/>
</Flex>
<Flex justify="space-between" align="center">
<FormLabel htmlFor="max-delay" mb="0">
Max delay (in seconds):
</FormLabel>
<SmartNumberInput
id="max-delay"
data-testid="max-delay"
value={typingEmulation.maxDelay}
onValueChange={handleMaxDelayChange}
maxW="100px"
step={0.1}
/>
</Flex>
</Stack>
)}
</Stack>
)
}