Skip to content

Commit

Permalink
✨ Add OpenAI block
Browse files Browse the repository at this point in the history
Also migrate credentials to tRPC

Closes #253
  • Loading branch information
baptisteArno committed Mar 9, 2023
1 parent 97cfdfe commit ff04edf
Show file tree
Hide file tree
Showing 86 changed files with 2,563 additions and 1,035 deletions.
14 changes: 8 additions & 6 deletions apps/builder/src/components/DropdownList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,23 @@ import {
import { ChevronLeftIcon } from '@/components/icons'
import React, { ReactNode } from 'react'

type Props<T> = {
currentItem?: T
onItemSelect: (item: T) => void
items: T[]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Props<T extends readonly any[]> = {
currentItem: T[number] | undefined
onItemSelect: (item: T[number]) => void
items: T
placeholder?: string
}

export const DropdownList = <T,>({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const DropdownList = <T extends readonly any[]>({
currentItem,
onItemSelect,
items,
placeholder = '',
...props
}: Props<T> & MenuButtonProps) => {
const handleMenuItemClick = (operator: T) => () => {
const handleMenuItemClick = (operator: T[number]) => () => {
onItemSelect(operator)
}
return (
Expand Down
27 changes: 27 additions & 0 deletions apps/builder/src/components/SetVariableLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useColorModeValue, HStack, Tag, Text } from '@chakra-ui/react'
import { Variable } from 'models'

export const SetVariableLabel = ({
variableId,
variables,
}: {
variableId: string
variables?: Variable[]
}) => {
const textColor = useColorModeValue('gray.600', 'gray.400')
const variableName = variables?.find(
(variable) => variable.id === variableId
)?.name

if (!variableName) return null
return (
<HStack fontStyle="italic" spacing={1}>
<Text fontSize="sm" color={textColor}>
Set
</Text>
<Tag bg="orange.400" color="white" size="sm">
{variableName}
</Tag>
</HStack>
)
}
111 changes: 87 additions & 24 deletions apps/builder/src/components/TableList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Box, Button, Fade, Flex, IconButton, Stack } from '@chakra-ui/react'
import {
Box,
Button,
Fade,
Flex,
IconButton,
SlideFade,
Stack,
} from '@chakra-ui/react'
import { TrashIcon, PlusIcon } from '@/components/icons'
import { createId } from '@paralleldrive/cuid2'
import React, { useState } from 'react'
Expand All @@ -7,26 +15,25 @@ type ItemWithId<T> = T & { id: string }

export type TableListItemProps<T> = {
item: T
debounceTimeout?: number
onItemChange: (item: T) => void
}

type Props<T> = {
initialItems: ItemWithId<T>[]
isOrdered?: boolean
addLabel?: string
debounceTimeout?: number
onItemsChange: (items: ItemWithId<T>[]) => void
Item: (props: TableListItemProps<T>) => JSX.Element
ComponentBetweenItems?: (props: unknown) => JSX.Element
onItemsChange: (items: ItemWithId<T>[]) => void
}

export const TableList = <T,>({
initialItems,
onItemsChange,
isOrdered,
addLabel = 'Add',
debounceTimeout,
Item,
ComponentBetweenItems,
onItemsChange,
}: Props<T>) => {
const [items, setItems] = useState(initialItems)
const [showDeleteIndex, setShowDeleteIndex] = useState<number | null>(null)
Expand All @@ -38,6 +45,15 @@ export const TableList = <T,>({
onItemsChange([...items, newItem])
}

const insertItem = (itemIndex: number) => () => {
const id = createId()
const newItem = { id } as ItemWithId<T>
const newItems = [...items]
newItems.splice(itemIndex + 1, 0, newItem)
setItems(newItems)
onItemsChange(newItems)
}

const updateItem = (itemIndex: number, updates: Partial<T>) => {
const newItems = items.map((item, idx) =>
idx === itemIndex ? { ...item, ...updates } : item
Expand All @@ -62,7 +78,7 @@ export const TableList = <T,>({
const handleMouseLeave = () => setShowDeleteIndex(null)

return (
<Stack spacing="4">
<Stack spacing={0} pt="2">
{items.map((item, itemIndex) => (
<Box key={item.id}>
{itemIndex !== 0 && ComponentBetweenItems && (
Expand All @@ -73,35 +89,82 @@ export const TableList = <T,>({
onMouseEnter={handleMouseEnter(itemIndex)}
onMouseLeave={handleMouseLeave}
mt={itemIndex !== 0 && ComponentBetweenItems ? 4 : 0}
justifyContent="center"
pb="4"
>
<Item
item={item}
onItemChange={handleCellChange(itemIndex)}
debounceTimeout={debounceTimeout}
/>
<Fade in={showDeleteIndex === itemIndex}>
<Item item={item} onItemChange={handleCellChange(itemIndex)} />
<Fade
in={showDeleteIndex === itemIndex}
style={{
position: 'absolute',
left: '-15px',
top: '-15px',
}}
unmountOnExit
>
<IconButton
icon={<TrashIcon />}
aria-label="Remove cell"
onClick={deleteItem(itemIndex)}
pos="absolute"
left="-15px"
top="-15px"
size="sm"
shadow="md"
/>
</Fade>
{isOrdered && (
<>
{itemIndex === 0 && (
<SlideFade
offsetY="-5px"
in={showDeleteIndex === itemIndex}
style={{
position: 'absolute',
top: '-15px',
}}
unmountOnExit
>
<IconButton
aria-label={addLabel}
icon={<PlusIcon />}
size="xs"
shadow="md"
colorScheme="blue"
onClick={insertItem(itemIndex - 1)}
/>
</SlideFade>
)}
<SlideFade
offsetY="5px"
in={showDeleteIndex === itemIndex}
style={{
position: 'absolute',
bottom: '5px',
}}
unmountOnExit
>
<IconButton
aria-label={addLabel}
icon={<PlusIcon />}
size="xs"
shadow="md"
colorScheme="blue"
onClick={insertItem(itemIndex)}
/>
</SlideFade>
</>
)}
</Flex>
</Box>
))}
<Button
leftIcon={<PlusIcon />}
onClick={createItem}
flexShrink={0}
colorScheme="blue"
>
{addLabel}
</Button>
{!isOrdered && (
<Button
leftIcon={<PlusIcon />}
onClick={createItem}
flexShrink={0}
colorScheme="blue"
>
{addLabel}
</Button>
)}
</Stack>
)
}
4 changes: 2 additions & 2 deletions apps/builder/src/components/inputs/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import { useDebouncedCallback } from 'use-debounce'
import { env } from 'utils'
import { MoreInfoTooltip } from '../MoreInfoTooltip'

type Value<HasVariable> = HasVariable extends undefined | true
type Value<HasVariable> = HasVariable extends true | undefined
? number | VariableString
: number

type Props<HasVariable extends boolean> = {
defaultValue?: Value<HasVariable>
defaultValue: Value<HasVariable> | undefined
debounceTimeout?: number
withVariableButton?: HasVariable
label?: string
Expand Down
4 changes: 4 additions & 0 deletions apps/builder/src/components/inputs/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { injectVariableInText } from '@/features/variables/utils/injectVariableI
import { focusInput } from '@/utils/focusInput'
import {
FormControl,
FormHelperText,
FormLabel,
HStack,
Input as ChakraInput,
Expand All @@ -26,6 +27,7 @@ export type TextInputProps = {
onChange: (value: string) => void
debounceTimeout?: number
label?: ReactNode
helperText?: ReactNode
moreInfoTooltip?: string
withVariableButton?: boolean
isRequired?: boolean
Expand All @@ -42,6 +44,7 @@ export const TextInput = forwardRef(function TextInput(
defaultValue,
debounceTimeout = 1000,
label,
helperText,
moreInfoTooltip,
withVariableButton = true,
isRequired,
Expand Down Expand Up @@ -137,6 +140,7 @@ export const TextInput = forwardRef(function TextInput(
) : (
Input
)}
{helperText && <FormHelperText>{helperText}</FormHelperText>}
</FormControl>
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ test.describe.parallel('Buttons input block', () => {
await page.getByLabel('Button label:').fill('Go')
await page.getByPlaceholder('Select a variable').nth(1).click()
await page.getByText('var1').click()
await expect(page.getByText('Collectsvar1')).toBeVisible()
await expect(page.getByText('Setvar1')).toBeVisible()
await page.click('[data-testid="block2-icon"]')

await page.locator('text=Item 1').hover()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { BlockIndices, ChoiceInputBlock, Variable } from 'models'
import { BlockIndices, ChoiceInputBlock } from 'models'
import React from 'react'
import { ItemNodesList } from '@/features/graph/components/Nodes/ItemNode'
import {
HStack,
Stack,
Tag,
Text,
useColorModeValue,
Wrap,
} from '@chakra-ui/react'
import { Stack, Tag, Text, Wrap } from '@chakra-ui/react'
import { useTypebot } from '@/features/editor'
import { SetVariableLabel } from '@/components/SetVariableLabel'

type Props = {
block: ChoiceInputBlock
Expand All @@ -25,7 +19,7 @@ export const ButtonsBlockNode = ({ block, indices }: Props) => {
return (
<Stack w="full">
{block.options.variableId ? (
<CollectVariableLabel
<SetVariableLabel
variableId={block.options.variableId}
variables={typebot?.variables}
/>
Expand All @@ -44,28 +38,3 @@ export const ButtonsBlockNode = ({ block, indices }: Props) => {
</Stack>
)
}

const CollectVariableLabel = ({
variableId,
variables,
}: {
variableId: string
variables?: Variable[]
}) => {
const textColor = useColorModeValue('gray.600', 'gray.400')
const variableName = variables?.find(
(variable) => variable.id === variableId
)?.name

if (!variableName) return null
return (
<HStack fontStyle="italic" spacing={1}>
<Text fontSize="sm" color={textColor}>
Collects
</Text>
<Tag bg="orange.400" color="white" size="sm">
{variableName}
</Tag>
</HStack>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,22 @@ import {
AccordionPanel,
} from '@chakra-ui/react'
import { DropdownList } from '@/components/DropdownList'
import { CredentialsType, PaymentInputOptions, PaymentProvider } from 'models'
import React, { ChangeEvent, useState } from 'react'
import { PaymentInputOptions, PaymentProvider } from 'models'
import React, { ChangeEvent } from 'react'
import { currencies } from './currencies'
import { StripeConfigModal } from './StripeConfigModal'
import { CredentialsDropdown } from '@/features/credentials'
import { TextInput } from '@/components/inputs'
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'

type Props = {
options: PaymentInputOptions
onOptionsChange: (options: PaymentInputOptions) => void
}

export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
const { workspace } = useWorkspace()
const { isOpen, onOpen, onClose } = useDisclosure()
const [refreshCredentialsKey, setRefreshCredentialsKey] = useState(0)

const handleProviderChange = (provider: PaymentProvider) => {
onOptionsChange({
Expand All @@ -35,7 +36,6 @@ export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
}

const handleCredentialsSelect = (credentialsId?: string) => {
setRefreshCredentialsKey(refreshCredentialsKey + 1)
onOptionsChange({
...options,
credentialsId,
Expand Down Expand Up @@ -96,13 +96,15 @@ export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
</Stack>
<Stack>
<Text>Account:</Text>
<CredentialsDropdown
type={CredentialsType.STRIPE}
currentCredentialsId={options.credentialsId}
onCredentialsSelect={handleCredentialsSelect}
onCreateNewClick={onOpen}
refreshDropdownKey={refreshCredentialsKey}
/>
{workspace && (
<CredentialsDropdown
type="stripe"
workspaceId={workspace.id}
currentCredentialsId={options.credentialsId}
onCredentialsSelect={handleCredentialsSelect}
onCreateNewClick={onOpen}
/>
)}
</Stack>
<HStack>
<TextInput
Expand Down
Loading

4 comments on commit ff04edf

@vercel
Copy link

@vercel vercel bot commented on ff04edf Mar 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on ff04edf Mar 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-git-main-typebot-io.vercel.app
builder-v2-typebot-io.vercel.app
app.typebot.io

@vercel
Copy link

@vercel vercel bot commented on ff04edf Mar 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

ns8.vn
1stop.au
yobot.me
klujo.com
me.cr8.ai
8jours.top
bot.mycompany.reviews
bot.outstandbrand.com
bot.ramonmatos.com.br
bot.robertohairlab.it
bot.sharemyreview.net
bot.truongnguyen.live
botz.cloudsiteapp.com
cdd.searchcube.com.sg
chat.missarkansas.org
chatbot.ownacademy.co
criar.somaperuzzo.com
homerun.wpwakanda.com
sbutton.wpwakanda.com
815639944.21000000.one
83720273.bouclidom.com
aplicacao.bmind.com.br
apply.ansuraniphone.my
bbutton.wpwwakanda.com
bot.ilmuseoaiborghi.it
bot.louismarcondes.com
bot.pratikmandalia.com
bot.t20worldcup.com.au
bot2.mycompany.reviews
bot3.mycompany.reviews
c23111azqw.nigerias.io
dieta.barrettamario.it
felipewelington.com.br
form.bridesquadapp.com
form.searchcube.com.sg
gcase.barrettamario.it
help.giversforgood.com
info.clickasuransi.com
kodawariab736.skeep.it
michaeljackson.riku.ai
premium.kandabrand.com
report.gratirabbit.com
resume.gratirabbit.com
83242573.actualizar.xyz
87656003.actualizar.xyz
88152257.actualizar.xyz
91375310.actualizar.xyz
arrivalx2.wpwakanda.com
bot.blackboxtips.com.br
bot.hotelplayarimini.it
bot.upgradesolutions.eu
bots.baptiste-arnaud.fr
help.comebackreward.com
link.venturasuceder.com
mainmenu.diddancing.com
manualhandlingcourse.ie
type.dericsoncalari.com.br
bot.pinpointinteractive.com
bot.polychromes-project.com
bot.seidinembroseanchetu.it
chat.semanalimpanome.com.br
chatbot.berbelanjabiz.trade
designguide.techyscouts.com
jcapp.virtuesocialmedia.com
liveconvert2.kandalearn.com
presente.empresarias.com.mx
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
bot.seidibergamoseanchetu.it
desabafe.sergiolimajr.com.br
download.venturemarketing.in
jc-app.virtuesocialmedia.com
piazzatorre.barrettamario.it
type.cookieacademyonline.com
upload.atlasoutfittersk9.com
bot.brigadeirosemdrama.com.br
forms.escoladeautomacao.com.br
onboarding.libertydreamcare.ie
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
elevateyourmind.groovepages.com
viewer-v2-typebot-io.vercel.app
yourfeedback.comebackreward.com
gerador.verificadordehospedes.com
personal-trainer.barrettamario.it
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
bot.studiotecnicoimmobiliaremerelli.it
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
chrome-os-inquiry-system.itschromeos.com
viewer-v2-git-main-typebot-io.vercel.app
main-menu-for-itschromeos.itschromeos.com

@vercel
Copy link

@vercel vercel bot commented on ff04edf Mar 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs-typebot-io.vercel.app
docs-git-main-typebot-io.vercel.app
docs.typebot.io

Please sign in to comment.