2
0

feat(editor): Add file upload input

This commit is contained in:
Baptiste Arnaud
2022-06-12 17:34:33 +02:00
parent d4c52d47b3
commit 75365a0d82
48 changed files with 1022 additions and 587 deletions

View File

@ -20,6 +20,7 @@ import {
SendEmailIcon,
StarIcon,
TextIcon,
UploadIcon,
WebhookIcon,
} from 'assets/icons'
import {
@ -68,6 +69,8 @@ export const BlockIcon = ({ type, ...props }: BlockIconProps) => {
return <CreditCardIcon color="orange.500" {...props} />
case InputBlockType.RATING:
return <StarIcon color="orange.500" {...props} />
case InputBlockType.FILE:
return <UploadIcon color="orange.500" {...props} />
case LogicBlockType.SET_VARIABLE:
return <EditIcon color="purple.500" {...props} />
case LogicBlockType.CONDITION:

View File

@ -43,6 +43,12 @@ export const BlockTypeLabel = ({ type }: Props): JSX.Element => {
return <Text>Payment</Text>
case InputBlockType.RATING:
return <Text>Rating</Text>
case InputBlockType.FILE:
return (
<Tooltip label="Upload Files">
<Text>File</Text>
</Tooltip>
)
case LogicBlockType.SET_VARIABLE:
return <Text>Set variable</Text>
case LogicBlockType.CONDITION:

View File

@ -19,12 +19,14 @@ type Props = {
isReadOnly?: boolean
debounceTimeout?: number
withVariableButton?: boolean
height?: string
onChange?: (value: string) => void
}
export const CodeEditor = ({
value,
lang,
onChange,
height = '250px',
withVariableButton = true,
isReadOnly = false,
debounceTimeout = 1000,
@ -92,7 +94,7 @@ export const CodeEditor = ({
extensions.push(
EditorView.theme({
'&': { maxHeight: '500px' },
'.cm-gutter,.cm-content': { minHeight: isReadOnly ? '0' : '250px' },
'.cm-gutter,.cm-content': { minHeight: isReadOnly ? '0' : height },
'.cm-scroller': { overflow: 'auto' },
})
)

View File

@ -19,6 +19,7 @@ import {
WithVariableContent,
} from './contents'
import { ConfigureContent } from './contents/ConfigureContent'
import { FileInputContent } from './contents/FileInputContent'
import { ImageBubbleContent } from './contents/ImageBubbleContent'
import { PaymentInputContent } from './contents/PaymentInputContent'
import { PlaceholderContent } from './contents/PlaceholderContent'
@ -80,6 +81,9 @@ export const BlockNodeContent = ({ block, indices }: Props): JSX.Element => {
case InputBlockType.RATING: {
return <RatingInputContent block={block} />
}
case InputBlockType.FILE: {
return <FileInputContent options={block.options} />
}
case LogicBlockType.SET_VARIABLE: {
return <SetVariableContent block={block} />
}

View File

@ -0,0 +1,12 @@
import { Text } from '@chakra-ui/react'
import { FileInputOptions } from 'models'
type Props = {
options: FileInputOptions
}
export const FileInputContent = ({ options: { isMultipleAllowed } }: Props) => (
<Text noOfLines={0} pr="6">
Collect {isMultipleAllowed ? 'files' : 'file'}
</Text>
)

View File

@ -29,6 +29,7 @@ import {
import { ChoiceInputSettingsBody } from './bodies/ChoiceInputSettingsBody'
import { CodeSettings } from './bodies/CodeSettings'
import { ConditionSettingsBody } from './bodies/ConditionSettingsBody'
import { FileInputSettings } from './bodies/FileInputSettings'
import { GoogleAnalyticsSettings } from './bodies/GoogleAnalyticsSettings'
import { GoogleSheetsSettingsBody } from './bodies/GoogleSheetsSettingsBody'
import { PaymentSettings } from './bodies/PaymentSettings'
@ -173,6 +174,14 @@ export const BlockSettings = ({
/>
)
}
case InputBlockType.FILE: {
return (
<FileInputSettings
options={block.options}
onOptionsChange={handleOptionsChange}
/>
)
}
case LogicBlockType.SET_VARIABLE: {
return (
<SetVariableSettings

View File

@ -0,0 +1,64 @@
import { FormLabel, Stack } from '@chakra-ui/react'
import { CodeEditor } from 'components/shared/CodeEditor'
import { SwitchWithLabel } from 'components/shared/SwitchWithLabel'
import { Input } from 'components/shared/Textbox'
import { VariableSearchInput } from 'components/shared/VariableSearchInput'
import { FileInputOptions, Variable } from 'models'
import React from 'react'
type Props = {
options: FileInputOptions
onOptionsChange: (options: FileInputOptions) => void
}
export const FileInputSettings = ({ options, onOptionsChange }: Props) => {
const handleButtonLabelChange = (button: string) =>
onOptionsChange({ ...options, labels: { ...options.labels, button } })
const handlePlaceholderLabelChange = (placeholder: string) =>
onOptionsChange({ ...options, labels: { ...options.labels, placeholder } })
const handleLongChange = (isMultipleAllowed: boolean) =>
onOptionsChange({ ...options, isMultipleAllowed })
const handleVariableChange = (variable?: Variable) =>
onOptionsChange({ ...options, variableId: variable?.id })
return (
<Stack spacing={4}>
<SwitchWithLabel
id="switch"
label="Allow multiple files?"
initialValue={options.isMultipleAllowed}
onCheckChange={handleLongChange}
/>
<Stack>
<FormLabel mb="0">Placeholder:</FormLabel>
<CodeEditor
lang="html"
onChange={handlePlaceholderLabelChange}
value={options.labels.placeholder}
height={'100px'}
withVariableButton={false}
/>
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="button">
Button label:
</FormLabel>
<Input
id="button"
defaultValue={options.labels.button}
onChange={handleButtonLabelChange}
withVariableButton={false}
/>
</Stack>
<Stack>
<FormLabel mb="0" htmlFor="variable">
Save upload URL{options.isMultipleAllowed ? 's' : ''} in a variable:
</FormLabel>
<VariableSearchInput
initialVariableId={options.variableId}
onSelectVariable={handleVariableChange}
/>
</Stack>
</Stack>
)
}

View File

@ -171,7 +171,12 @@ export const TypebotHeader = () => {
<HStack right="40px" pos="absolute" display={['none', 'flex']}>
<CollaborationMenuButton />
{router.pathname.includes('/edit') && isNotDefined(rightPanel) && (
<Button onClick={handlePreviewClick}>Preview</Button>
<Button
onClick={handlePreviewClick}
isLoading={isNotDefined(typebot)}
>
Preview
</Button>
)}
<PublishButton />
</HStack>

View File

@ -1,6 +1,7 @@
import { Button, ButtonProps, chakra } from '@chakra-ui/react'
import React, { ChangeEvent, useState } from 'react'
import { compressFile, uploadFile } from 'services/utils'
import { compressFile } from 'services/utils'
import { uploadFiles } from 'utils'
type UploadButtonProps = {
filePath: string
@ -20,11 +21,15 @@ export const UploadButton = ({
if (!e.target?.files) return
setIsUploading(true)
const file = e.target.files[0]
const { url } = await uploadFile(
await compressFile(file),
filePath + (includeFileName ? `/${file.name}` : '')
)
if (url) onFileUploaded(url)
const urls = await uploadFiles({
files: [
{
file: await compressFile(file),
path: filePath + (includeFileName ? `/${file.name}` : ''),
},
],
})
if (urls.length) onFileUploaded(urls[0])
setIsUploading(false)
}

View File

@ -43,7 +43,6 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
const fetchTemplate = async (template: TemplateProps) => {
setSelectedTemplate(template)
const { data, error } = await sendRequest(`/templates/${template.fileName}`)
console.log(data, error)
if (error)
return showToast({ title: error.name, description: error.message })
setTypebot(data as Typebot)