Skip to content

Commit

Permalink
✨ Add Chatwoot livechat integration
Browse files Browse the repository at this point in the history
Closes #159
  • Loading branch information
baptisteArno committed Nov 10, 2022
1 parent 92147c3 commit ea84039
Show file tree
Hide file tree
Showing 32 changed files with 464 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export const WorkspaceSettingsForm = ({ onClose }: { onClose: () => void }) => {

const handleChangeIcon = (icon: string) => {
if (!workspace?.id) return
console.log(icon)
updateWorkspace(workspace?.id, { icon })
}

Expand Down
3 changes: 3 additions & 0 deletions apps/builder/components/editor/BlocksSideBar/BlockIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
PabblyConnectLogo,
ZapierLogo,
} from 'assets/logos'
import { ChatwootLogo } from 'features/chatwoot'
import {
BubbleBlockType,
InputBlockType,
Expand Down Expand Up @@ -95,6 +96,8 @@ export const BlockIcon = ({ type, ...props }: BlockIconProps) => {
return <PabblyConnectLogo {...props} />
case IntegrationBlockType.EMAIL:
return <SendEmailIcon {...props} />
case IntegrationBlockType.CHATWOOT:
return <ChatwootLogo {...props} />
case 'start':
return <FlagIcon {...props} />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,7 @@ export const BlockTypeLabel = ({ type }: Props): JSX.Element => {
return <Text>Pabbly</Text>
case IntegrationBlockType.EMAIL:
return <Text>Email</Text>
case IntegrationBlockType.CHATWOOT:
return <Text>Chatwoot</Text>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ export const ResultsActionButtons = ({
const data = dataToUnparse.map<{ [key: string]: string }>((data) => {
const newObject: { [key: string]: string } = {}
fields?.forEach((field) => {
console.log(data[field])
newObject[field] = data[field]?.plainText
})
return newObject
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Text } from '@chakra-ui/react'
import { ChatwootBlockNodeLabel } from 'features/chatwoot'
import {
Block,
StartBlock,
Expand Down Expand Up @@ -153,6 +154,9 @@ export const BlockNodeContent = ({ block, indices }: Props): JSX.Element => {
case IntegrationBlockType.EMAIL: {
return <SendEmailContent block={block} />
}
case IntegrationBlockType.CHATWOOT: {
return <ChatwootBlockNodeLabel block={block} />
}
case 'start': {
return <Text>Start</Text>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
IconButton,
} from '@chakra-ui/react'
import { ExpandIcon } from 'assets/icons'
import { ChatwootSettingsForm } from 'features/chatwoot/components'
import {
ConditionItem,
ConditionBlock,
Expand Down Expand Up @@ -276,5 +277,13 @@ export const BlockSettings = ({
/>
)
}
case IntegrationBlockType.CHATWOOT: {
return (
<ChatwootSettingsForm
options={block.options}
onOptionsChange={handleOptionsChange}
/>
)
}
}
}
2 changes: 1 addition & 1 deletion apps/builder/components/shared/MoreInfoTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type Props = {

export const MoreInfoTooltip = ({ children }: Props) => {
return (
<Tooltip label={children} hasArrow rounded="md" p="3">
<Tooltip label={children} hasArrow rounded="md" p="3" placement="top">
<chakra.span cursor="pointer">
<HelpCircleIcon />
</chakra.span>
Expand Down
60 changes: 37 additions & 23 deletions apps/builder/components/shared/Textbox/TextBox.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {
ComponentWithAs,
FormControl,
FormLabel,
HStack,
InputProps,
TextareaProps,
Expand All @@ -9,6 +11,7 @@ import React, { ChangeEvent, useEffect, useRef, useState } from 'react'
import { useDebouncedCallback } from 'use-debounce'
import { env } from 'utils'
import { VariablesButton } from '../buttons/VariablesButton'
import { MoreInfoTooltip } from '../MoreInfoTooltip'

export type TextBoxProps = {
onChange: (value: string) => void
Expand All @@ -17,13 +20,17 @@ export type TextBoxProps = {
| ComponentWithAs<'input', InputProps>
withVariableButton?: boolean
debounceTimeout?: number
label?: string
moreInfoTooltip?: string
} & Omit<InputProps & TextareaProps, 'onChange'>

export const TextBox = ({
onChange,
TextBox,
withVariableButton = true,
debounceTimeout = 1000,
label,
moreInfoTooltip,
...props
}: TextBoxProps) => {
const textBoxRef = useRef<(HTMLInputElement & HTMLTextAreaElement) | null>(
Expand Down Expand Up @@ -92,29 +99,36 @@ export const TextBox = ({
setCarretPosition(textBoxRef.current.selectionStart)
}

if (!withVariableButton) {
return (
<TextBox
ref={textBoxRef}
onChange={handleChange}
bgColor={'white'}
value={value}
{...props}
/>
)
}
const Input = (
<TextBox
ref={textBoxRef}
value={value}
onKeyUp={handleKeyUp}
onClick={handleKeyUp}
onChange={handleChange}
bgColor={'white'}
{...props}
/>
)

return (
<HStack spacing={0} align={'flex-end'}>
<TextBox
ref={textBoxRef}
value={value}
onKeyUp={handleKeyUp}
onClick={handleKeyUp}
onChange={handleChange}
bgColor={'white'}
{...props}
/>
<VariablesButton onSelectVariable={handleVariableSelected} />
</HStack>
<FormControl isRequired={props.isRequired}>
{label && (
<FormLabel>
{label}{' '}
{moreInfoTooltip && (
<MoreInfoTooltip>{moreInfoTooltip}</MoreInfoTooltip>
)}
</FormLabel>
)}
{withVariableButton ? (
<HStack spacing={0} align={'flex-end'}>
{Input}
<VariablesButton onSelectVariable={handleVariableSelected} />
</HStack>
) : (
Input
)}
</FormControl>
)
}
2 changes: 0 additions & 2 deletions apps/builder/contexts/ResultsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ export const ResultsProvider = ({
typebotId,
})

console.log(data?.flatMap((d) => d.results) ?? [])

const fetchMore = () => setSize((state) => state + 1)

const resultHeader = useMemo(
Expand Down
43 changes: 43 additions & 0 deletions apps/builder/features/chatwoot/chatwoot.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import test, { expect } from '@playwright/test'
import { createTypebots } from 'utils/playwright/databaseActions'
import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers'
import cuid from 'cuid'
import { defaultChatwootOptions, IntegrationBlockType } from 'models'
import { typebotViewer } from 'utils/playwright/testHelpers'

const typebotId = cuid()

const chatwootTestWebsiteToken = 'tueXiiqEmrWUCZ4NUyoR7nhE'

test.describe('Chatwoot block', () => {
test('should be configurable', async ({ page }) => {
await createTypebots([
{
id: typebotId,
...parseDefaultGroupWithBlock({
type: IntegrationBlockType.CHATWOOT,
options: defaultChatwootOptions,
}),
},
])

await page.goto(`/typebots/${typebotId}/edit`)
await page.getByText('Configure...').click()
await expect(page.getByLabel('Base URL')).toHaveAttribute(
'value',
defaultChatwootOptions.baseUrl
)
await page.getByLabel('Website token').fill(chatwootTestWebsiteToken)
await expect(page.getByText('Open Chatwoot')).toBeVisible()
await page.getByRole('button', { name: 'Set user details' }).click()
await page.getByLabel('ID').fill('123')
await page.getByLabel('Name').fill('John Doe')
await page.getByLabel('Email').fill('[email protected]')
await page.getByLabel('Avatar URL').fill('https://domain.com/avatar.png')
await page.getByLabel('Phone number').fill('+33654347543')
await page.getByRole('button', { name: 'Preview' }).click()
await expect(
page.getByText("Chatwoot won't open in preview mode").nth(0)
).toBeVisible()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Text } from '@chakra-ui/react'
import { ChatwootBlock } from 'models'

type Props = {
block: ChatwootBlock
}

export const ChatwootBlockNodeLabel = ({ block }: Props) =>
block.options.websiteToken.length === 0 ? (
<Text color="gray.500">Configure...</Text>
) : (
<Text>Open Chatwoot</Text>
)
29 changes: 29 additions & 0 deletions apps/builder/features/chatwoot/components/ChatwootLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Icon, IconProps } from '@chakra-ui/react'

export const ChatwootLogo = (props: IconProps) => (
<Icon
viewBox="0 0 512 512"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g
id="Square-logo"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd"
>
<g id="chatwoot_logo" fill-rule="nonzero">
<circle id="Oval" fill="#47A7F6" cx="256" cy="256" r="256"></circle>
<path
d="M362.807947,368.807947 L244.122956,368.807947 C178.699407,368.807947 125.456954,315.561812 125.456954,250.12177 C125.456954,184.703089 178.699407,131.456954 244.124143,131.456954 C309.565494,131.456954 362.807947,184.703089 362.807947,250.12177 L362.807947,368.807947 Z"
id="Fill-1"
stroke="#FFFFFF"
stroke-width="6"
fill="#FFFFFF"
></path>
</g>
</g>
</Icon>
)
98 changes: 98 additions & 0 deletions apps/builder/features/chatwoot/components/ChatwootSettingsForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Stack,
} from '@chakra-ui/react'
import { Input } from 'components/shared/Textbox'
import { ChatwootOptions } from 'models'
import React from 'react'

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

export const ChatwootSettingsForm = ({ options, onOptionsChange }: Props) => {
return (
<Stack spacing={4}>
<Input
isRequired
label="Base URL"
defaultValue={options.baseUrl}
onChange={(baseUrl: string) => {
onOptionsChange({ ...options, baseUrl })
}}
withVariableButton={false}
/>
<Input
isRequired
label="Website token"
defaultValue={options.websiteToken}
onChange={(websiteToken) =>
onOptionsChange({ ...options, websiteToken })
}
moreInfoTooltip="Can be found in Chatwoot under Settings > Inboxes > Settings > Configuration, in the code snippet."
/>
<Accordion allowMultiple>
<AccordionItem>
<AccordionButton justifyContent="space-between">
Set user details
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="4">
<Input
label="ID"
defaultValue={options.user?.id}
onChange={(id: string) => {
onOptionsChange({ ...options, user: { ...options.user, id } })
}}
/>
<Input
label="Name"
defaultValue={options.user?.name}
onChange={(name: string) => {
onOptionsChange({
...options,
user: { ...options.user, name },
})
}}
/>
<Input
label="Email"
defaultValue={options.user?.email}
onChange={(email: string) => {
onOptionsChange({
...options,
user: { ...options.user, email },
})
}}
/>
<Input
label="Avatar URL"
defaultValue={options.user?.avatarUrl}
onChange={(avatarUrl: string) => {
onOptionsChange({
...options,
user: { ...options.user, avatarUrl },
})
}}
/>
<Input
label="Phone number"
defaultValue={options.user?.phoneNumber}
onChange={(phoneNumber: string) => {
onOptionsChange({
...options,
user: { ...options.user, phoneNumber },
})
}}
/>
</AccordionPanel>
</AccordionItem>
</Accordion>
</Stack>
)
}
3 changes: 3 additions & 0 deletions apps/builder/features/chatwoot/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { ChatwootLogo } from './ChatwootLogo'
export { ChatwootBlockNodeLabel } from './ChatwootBlockNodeLabel'
export { ChatwootSettingsForm } from './ChatwootSettingsForm'
1 change: 1 addition & 0 deletions apps/builder/features/chatwoot/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './components'
1 change: 0 additions & 1 deletion apps/builder/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { playwrightBaseConfig } from 'configs/playwright'

const config: PlaywrightTestConfig = {
...playwrightBaseConfig,
testDir: path.join(__dirname, 'playwright/tests'),
webServer: process.env.CI
? {
...(playwrightBaseConfig.webServer as { command: string }),
Expand Down
Loading

5 comments on commit ea84039

@vercel
Copy link

@vercel vercel bot commented on ea84039 Nov 10, 2022

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 ea84039 Nov 10, 2022

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 ea84039 Nov 10, 2022

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-typebot-io.vercel.app
builder-v2-git-main-typebot-io.vercel.app
app.typebot.io

@vercel
Copy link

@vercel vercel bot commented on ea84039 Nov 10, 2022

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.typebot.io
docs-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on ea84039 Nov 10, 2022

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-alpha – ./apps/viewer

ns8.vn
yobot.me
247987.com
8jours.top
bot.aws.bj
bot.bbc.bj
finplex.be
sat.cr8.ai
bot.aipr.kr
minipost.uk
bt.id8rs.com
bot.krdfy.com
vhpage.cr8.ai
am.nigerias.io
an.nigerias.io
ar.nigerias.io
bot.enreso.org
bot.lalmon.com
ticketfute.com
apo.nigerias.io
apr.nigerias.io
aso.nigerias.io
bot.ageenda.com
bot.artiweb.app
bot.tc-mail.com
chat.sureb4.com
eventhub.com.au
games.klujo.com
sakuranembro.it
typebot.aloe.do
bot.piccinato.co
bot.upfunnel.art
botc.ceox.com.br
clo.closeer.work
faqs.nigerias.io
feedback.ofx.one
form.syncwin.com
kw.wpwakanda.com
myrentalhost.com
stan.vselise.com
start.taxtree.io
typebot.aloe.bot
voicehelp.cr8.ai
app.chatforms.net
bot.agfunnel.tech
bot.hostnation.de
bot.maitempah.com
bot.phuonghub.com
bot.reviewzer.com
cares.urlabout.me
fmm.wpwakanda.com
gentleman-shop.fr
k1.kandabrand.com
lb.ticketfute.com
ov2.wpwakanda.com
ov3.wpwakanda.com
reward.onlinebotdemo.xyz
type.opaulovieira.com.br
aibot.angrybranding.co.uk
bot.aidigitalmarketing.kr
bot.arraesecenteno.com.br
bot.blackboxsports.com.br
bot.cabinrentalagency.com
boyfriend-breakup.riku.ai
brigadeirosemdrama.com.br
chat.ertcrebateportal.com
chat.thisiscrushhouse.com
sellmyharleylouisiana.com
verfica.botmachine.com.br
configurator.bouclidom.com
help.atlasoutfittersk9.com
ted.meujalecobrasil.com.br
type.dericsoncalari.com.br
chatbot.berbelanjabiz.trade
designguide.techyscouts.com
presente.empresarias.com.mx
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
piazzatorre.barrettamario.it
requests.swamprecordsgnv.com
type.cookieacademyonline.com
bot.brigadeirosemdrama.com.br
onboarding.libertydreamcare.ie
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
yourfeedback.comebackreward.com
personal-trainer.barrettamario.it
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
viewer-v2-alpha-typebot-io.vercel.app
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
viewer-v2-alpha-git-main-typebot-io.vercel.app

Please sign in to comment.