2
0

feat: Add new onboarding flow

This commit is contained in:
Baptiste Arnaud
2022-03-23 09:56:39 +01:00
parent f9aba27aae
commit f4e6f63b26
32 changed files with 1115 additions and 89 deletions

View File

@ -122,7 +122,11 @@ export const PersonalInfoForm = () => {
{hasUnsavedChanges && (
<Flex justifyContent="flex-end">
<Button colorScheme="blue" onClick={saveUser} isLoading={isSaving}>
<Button
colorScheme="blue"
onClick={() => saveUser()}
isLoading={isSaving}
>
Save
</Button>
</Flex>

View File

@ -27,6 +27,7 @@ import { ButtonSkeleton, FolderButton } from './FolderContent/FolderButton'
import { SharedTypebotsButton } from './FolderContent/SharedTypebotsButton'
import { TypebotButton } from './FolderContent/TypebotButton'
import { TypebotCardOverlay } from './FolderContent/TypebotButtonOverlay'
import { OnboardingModal } from './OnboardingModal'
type Props = { folder: DashboardFolder | null }
@ -163,6 +164,9 @@ export const FolderContent = ({ folder }: Props) => {
return (
<Flex w="full" flex="1" justify="center">
{typebots && user && folder === null && (
<OnboardingModal totalTypebots={typebots.length} />
)}
<Stack w="1000px" spacing={6}>
<Skeleton isLoaded={folder?.name !== undefined}>
<Heading as="h1">{folder?.name}</Heading>
@ -179,6 +183,7 @@ export const FolderContent = ({ folder }: Props) => {
<CreateBotButton
folderId={folder?.id}
isLoading={isTypebotLoading}
isFirstBot={typebots?.length === 0 && folder === null}
/>
{totalSharedTypebots > 0 && <SharedTypebotsButton />}
{isFolderLoading && <ButtonSkeleton />}

View File

@ -1,18 +1,23 @@
import { Button, ButtonProps, Text, VStack } from '@chakra-ui/react'
import { PlusIcon } from 'assets/icons'
import { useRouter } from 'next/router'
import { stringify } from 'qs'
import React from 'react'
export const CreateBotButton = ({
folderId,
isFirstBot,
...props
}: { folderId?: string } & ButtonProps) => {
}: { folderId?: string; isFirstBot: boolean } & ButtonProps) => {
const router = useRouter()
const handleClick = () =>
folderId
? router.push(`/typebots/create?folderId=${folderId}`)
: router.push('/typebots/create')
router.push(
`/typebots/create?${stringify({
isFirstBot: !isFirstBot ? undefined : isFirstBot,
folderId,
})}`
)
return (
<Button

View File

@ -0,0 +1,176 @@
import {
chakra,
Modal,
ModalBody,
ModalContent,
ModalOverlay,
useDisclosure,
useToast,
} from '@chakra-ui/react'
import { TypebotViewer } from 'bot-engine'
import { useUser } from 'contexts/UserContext'
import { Answer, Typebot } from 'models'
import React, { useEffect, useRef, useState } from 'react'
import { parseTypebotToPublicTypebot } from 'services/publicTypebot'
import { sendRequest } from 'utils'
import confetti from 'canvas-confetti'
type Props = { totalTypebots: number }
export const OnboardingModal = ({ totalTypebots }: Props) => {
const { user, saveUser } = useUser()
const { isOpen, onOpen, onClose } = useDisclosure()
const [typebot, setTypebot] = useState<Typebot>()
const confettiCanvaContainer = useRef<HTMLCanvasElement | null>(null)
const confettiCanon = useRef<confetti.CreateTypes>()
const [chosenCategories, setChosenCategories] = useState<string[]>([])
const toast = useToast({
position: 'top-right',
status: 'error',
})
useEffect(() => {
fetchTemplate()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect(() => {
const isNewUser =
user &&
new Date(user?.createdAt as unknown as string).toDateString() ===
new Date().toDateString() &&
totalTypebots === 0
if (isNewUser) onOpen()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [user])
useEffect(() => {
initConfettis()
return () => {
window.removeEventListener('message', handleIncomingMessage)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [confettiCanvaContainer.current])
const initConfettis = () => {
if (!confettiCanvaContainer.current || confettiCanon.current) return
confettiCanon.current = confetti.create(confettiCanvaContainer.current, {
resize: true,
useWorker: true,
})
window.addEventListener('message', handleIncomingMessage)
}
const handleIncomingMessage = (message: MessageEvent) => {
if (message.data.from === 'typebot') {
if (message.data.action === 'shootConfettis' && confettiCanon.current)
shootConfettis(confettiCanon.current)
}
}
const fetchTemplate = async () => {
const { data, error } = await sendRequest(`/bots/onboarding.json`)
if (error) return toast({ title: error.name, description: error.message })
setTypebot(data as Typebot)
}
const handleNewAnswer = (answer: Answer) => {
const isName = answer.variableId === 'cl126f4hf000i2e6d8zvzc3t1'
const isCompany = answer.variableId === 'cl126jqww000w2e6dq9yv4ifq'
const isCategories = answer.variableId === 'cl126mo3t001b2e6dvyi16bkd'
const isOtherCategories = answer.variableId === 'cl126q38p001q2e6d0hj23f6b'
if (isName) saveUser({ name: answer.content })
if (isCompany) saveUser({ company: answer.content })
if (isCategories) {
const onboardingCategories = answer.content.split(', ')
saveUser({ onboardingCategories })
setChosenCategories(onboardingCategories)
}
if (isOtherCategories)
saveUser({
onboardingCategories: [...chosenCategories, answer.content],
})
}
return (
<Modal
size="3xl"
isOpen={isOpen}
onClose={onClose}
blockScrollOnMount={false}
>
<chakra.canvas
ref={confettiCanvaContainer}
pos="fixed"
top="0"
left="0"
w="full"
h="full"
zIndex={9999}
pointerEvents="none"
/>
<ModalOverlay />
<ModalContent h="85vh">
<ModalBody>
{typebot && (
<TypebotViewer
typebot={parseTypebotToPublicTypebot(typebot)}
predefinedVariables={{
Name: user?.name?.split(' ')[0] ?? undefined,
}}
onNewAnswer={handleNewAnswer}
/>
)}
</ModalBody>
</ModalContent>
</Modal>
)
}
const shootConfettis = (confettiCanon: confetti.CreateTypes) => {
const count = 200
const defaults = {
origin: { y: 0.7 },
}
const fire = (
particleRatio: number,
opts: {
spread: number
startVelocity?: number
decay?: number
scalar?: number
}
) => {
confettiCanon(
Object.assign({}, defaults, opts, {
particleCount: Math.floor(count * particleRatio),
})
)
}
fire(0.25, {
spread: 26,
startVelocity: 55,
})
fire(0.2, {
spread: 60,
})
fire(0.35, {
spread: 100,
decay: 0.91,
scalar: 0.8,
})
fire(0.1, {
spread: 120,
startVelocity: 25,
decay: 0.92,
scalar: 1.2,
})
fire(0.1, {
spread: 120,
startVelocity: 45,
})
}

View File

@ -59,10 +59,8 @@ export const PreviewDrawer = () => {
setRightPanel(undefined)
}
const handleNewLog = (log: Omit<Log, 'id' | 'createdAt' | 'resultId'>) => {
const handleNewLog = (log: Omit<Log, 'id' | 'createdAt' | 'resultId'>) =>
toast(log as UseToastOptions)
console.log(log.details)
}
return (
<Flex

View File

@ -139,4 +139,4 @@ export const parseInitBubbleCode = ({
)
}
export const typebotJsHtml = `<script src="https://unpkg.com/typebot-js@2.2.0"></script>`
export const typebotJsHtml = `<script src="https://unpkg.com/typebot-js@2.2.1"></script>`

View File

@ -80,7 +80,6 @@ export const WebhookSettings = ({
return () => {
setLocalWebhook((localWebhook) => {
console.log(localWebhook)
if (!localWebhook) return
updateWebhook(webhookId, localWebhook).then()
return localWebhook

View File

@ -17,7 +17,12 @@ export const SupportBubble = () => {
process.env.NEXT_PUBLIC_VIEWER_URL
}/typebot-support`,
backgroundColor: '#ffffff',
button: { color: '#0042DA' },
button: {
color: '#0042DA',
iconUrl:
'https://user-images.githubusercontent.com/16015833/159536717-35bb78f8-f659-49f2-ad7f-00172be69cfb.svg',
iconStyle: 'border-radius: 0; width: 50%',
},
hiddenVariables: {
'User ID': user?.id,
Name: user?.name ?? undefined,

View File

@ -13,6 +13,7 @@ import { RightPanel, useEditor } from 'contexts/EditorContext'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { useRouter } from 'next/router'
import React from 'react'
import { isNotDefined } from 'utils'
import { PublishButton } from '../buttons/PublishButton'
import { CollaborationMenuButton } from './CollaborationMenuButton'
import { EditableTypebotName } from './EditableTypebotName'
@ -21,6 +22,7 @@ export const headerHeight = 56
export const TypebotHeader = () => {
const router = useRouter()
const { rightPanel } = useEditor()
const {
typebot,
updateOnBothTypebots,
@ -154,7 +156,7 @@ export const TypebotHeader = () => {
<HStack right="40px" pos="absolute">
<CollaborationMenuButton />
{router.pathname.includes('/edit') && (
{router.pathname.includes('/edit') && isNotDefined(rightPanel) && (
<Button onClick={handlePreviewClick}>Preview</Button>
)}
<PublishButton />

View File

@ -12,10 +12,12 @@ import {
} from '@chakra-ui/react'
import { ChevronLeftIcon } from 'assets/icons'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { useRouter } from 'next/router'
import { timeSince } from 'services/utils'
import { isNotDefined } from 'utils'
export const PublishButton = () => {
const { push, query } = useRouter()
const {
isPublishing,
isPublished,
@ -24,6 +26,11 @@ export const PublishButton = () => {
restorePublishedTypebot,
} = useTypebot()
const handlePublishClick = () => {
publishTypebot()
if (!publishedTypebot) push(`/typebots/${query.typebotId}/share`)
}
return (
<HStack spacing="1px">
<Tooltip
@ -47,7 +54,7 @@ export const PublishButton = () => {
colorScheme="blue"
isLoading={isPublishing}
isDisabled={isPublished}
onClick={publishTypebot}
onClick={handlePublishClick}
borderRightRadius={publishedTypebot && !isPublished ? 0 : undefined}
>
{isPublished ? 'Published' : 'Publish'}

View File

@ -21,7 +21,7 @@ const userContext = createContext<{
hasUnsavedChanges: boolean
isOAuthProvider: boolean
updateUser: (newUser: Partial<User>) => void
saveUser: () => void
saveUser: (newUser?: Partial<User>) => void
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
}>({})
@ -75,10 +75,11 @@ export const UserContext = ({ children }: { children: ReactNode }) => {
setUser({ ...user, ...newUser })
}
const saveUser = async () => {
const saveUser = async (newUser?: Partial<User>) => {
if (isNotDefined(user)) return
setIsSaving(true)
const { error } = await updateUserInDb(user.id, user)
if (newUser) updateUser(newUser)
const { error } = await updateUserInDb(user.id, { ...user, ...newUser })
if (error) toast({ title: error.name, description: error.message })
await refreshUser()
setIsSaving(false)

View File

@ -6,13 +6,14 @@ import {
Text,
Stack,
useToast,
Tooltip,
} from '@chakra-ui/react'
import { CreateTypebotMoreButton } from 'components/templates/ImportFileMenuItem'
import { TemplateButton } from 'components/templates/TemplateButton'
import { useUser } from 'contexts/UserContext'
import { defaultTheme, Typebot } from 'models'
import { useRouter } from 'next/router'
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { createTypebot, importTypebot } from 'services/typebots/typebots'
export type TemplateProps = { name: string; emoji: string; fileName: string }
@ -22,6 +23,8 @@ const templates: TemplateProps[] = [
export const TemplatesContent = () => {
const { user } = useUser()
const router = useRouter()
const [isFromScratchTooltipOpened, setIsFromScratchTooltipOpened] =
useState(false)
const [isLoading, setIsLoading] = useState(false)
@ -31,6 +34,13 @@ export const TemplatesContent = () => {
title: 'An error occured',
})
useEffect(() => {
if (!router.isReady) return
const isFirstBot = router.query.isFirstBot as string | undefined
if (isFirstBot) setIsFromScratchTooltipOpened(true)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [router.isReady])
const handleCreateSubmit = async (typebot?: Typebot) => {
if (!user) return
setIsLoading(true)
@ -60,13 +70,23 @@ export const TemplatesContent = () => {
<Flex w="full" justifyContent="center">
<Stack maxW="1000px" flex="1" pt="6" spacing={4}>
<Flex justifyContent="space-between">
<Button
onClick={() => handleCreateSubmit()}
isLoading={isLoading}
colorScheme="blue"
<Tooltip
isOpen={isFromScratchTooltipOpened}
label="Strongly suggested if you're new to Typebot."
maxW="200px"
hasArrow
placement="right"
rounded="md"
>
Start from scratch
</Button>
<Button
onClick={() => handleCreateSubmit()}
isLoading={isLoading}
colorScheme="blue"
>
Start from scratch
</Button>
</Tooltip>
<CreateTypebotMoreButton onNewTypebot={handleCreateSubmit} />
</Flex>
<Divider />

View File

@ -40,6 +40,7 @@
"@udecode/plate-ui-toolbar": "^10.2.2",
"bot-engine": "*",
"browser-image-compression": "^1.0.17",
"canvas-confetti": "^1.5.1",
"cuid": "^2.1.8",
"db": "*",
"deep-object-diff": "^1.1.7",
@ -85,6 +86,7 @@
},
"devDependencies": {
"@playwright/test": "^1.19.2",
"@types/canvas-confetti": "^1.4.2",
"@types/google-spreadsheet": "^3.1.5",
"@types/jsonwebtoken": "8.5.8",
"@types/micro-cors": "^0.1.2",

View File

@ -80,7 +80,10 @@ test.describe.parallel('Image bubble step', () => {
await page.click('text=Click to edit...')
await page.click('text=Giphy')
await page.click('img >> nth=3', { force: true })
await page.click('img >> nth=3', {
force: true,
position: { x: 0, y: 0 },
})
await expect(page.locator('img[alt="Step image"]')).toHaveAttribute(
'src',
new RegExp('giphy.com/media', 'gm')

View File

@ -54,7 +54,7 @@ test.describe('Dashboard page', () => {
test.use({
storageState: path.join(__dirname, '../freeUser.json'),
})
test("create folder shouldn't be available", async ({ page }) => {
test("Add my domain shouldn't be available", async ({ page }) => {
await page.goto(`/typebots/${typebotId}/share`)
await page.click('text=Add my domain')
await expect(page.locator('text=Upgrade now')).toBeVisible()

View File

@ -41,26 +41,4 @@ test.describe.parallel('Text input step', () => {
).toBeVisible()
await expect(typebotViewer(page).locator(`text=Go`)).toBeVisible()
})
test('variable in URL should prefill the input', async ({ page }) => {
const typebotId = cuid()
await createTypebots([
{
id: typebotId,
...parseDefaultBlockWithStep({
type: InputStepType.TEXT,
options: { ...defaultTextInputOptions, variableId: 'var1' },
}),
},
])
await page.goto(`/typebots/${typebotId}/edit?var1=My prefilled answer`)
await page.click('text=Preview')
await expect(
typebotViewer(page).locator(
`input[placeholder="${defaultTextInputOptions.labels.placeholder}"]`
)
).toHaveAttribute('value', 'My prefilled answer')
await expect(typebotViewer(page).locator(`button`)).toBeEnabled()
})
})

View File

@ -0,0 +1,635 @@
{
"id": "cl128l5vx007509il86n74oer",
"createdAt": "2022-03-22T14:33:05.037Z",
"updatedAt": "2022-03-22T16:33:37.928Z",
"name": "Onboarding",
"ownerId": "ckzmhmiey001009mnzt5nkxu8",
"publishedTypebotId": "cl128n64i00092e69wenv1dlx",
"folderId": null,
"blocks": [
{
"id": "cl1265zct0000mb1a6bir36w7",
"steps": [
{
"id": "cl1265zct0001mb1afel460do",
"type": "start",
"label": "Start",
"blockId": "cl1265zct0000mb1a6bir36w7",
"outgoingEdgeId": "cl1266kt100082e6d1wks5dtp"
}
],
"title": "Start",
"graphCoordinates": { "x": 0, "y": 0 }
},
{
"id": "cl1266bah00032e6dgdnj4vgz",
"steps": [
{
"id": "cl1266bam00042e6dm0gn22vy",
"type": "Condition",
"items": [
{
"id": "cl1266bam00052e6dn1sdjnax",
"type": 1,
"stepId": "cl1266bam00042e6dm0gn22vy",
"content": {
"comparisons": [
{
"id": "cl1266cg600062e6d76qwk74v",
"variableId": "cl126f4hf000i2e6d8zvzc3t1",
"comparisonOperator": "Is set"
}
],
"logicalOperator": "AND"
},
"outgoingEdgeId": "cl12bk3j6000c2e69bak89ja9"
}
],
"blockId": "cl1266bah00032e6dgdnj4vgz",
"outgoingEdgeId": "cl12bnfyd000g2e69g7lr3czq"
}
],
"title": "Block #1",
"graphCoordinates": { "x": 266, "y": 162 }
},
{
"id": "cl1267q1z000d2e6d949f2ge4",
"steps": [
{
"id": "cl1267q2c000e2e6dynjeg83n",
"type": "text",
"blockId": "cl1267q1z000d2e6d949f2ge4",
"content": {
"html": "<div>Welcome 👋</div>",
"richText": [
{ "type": "p", "children": [{ "text": "Welcome 👋" }] }
],
"plainText": "Welcome 👋"
}
},
{
"id": "cl1267y1u000f2e6d4rlglv6g",
"type": "text",
"blockId": "cl1267q1z000d2e6d949f2ge4",
"content": {
"html": "<div>What&#x27;s your name?</div>",
"richText": [
{ "type": "p", "children": [{ "text": "What's your name?" }] }
],
"plainText": "What's your name?"
}
},
{
"id": "cl126820m000g2e6dfleq78bt",
"type": "text input",
"blockId": "cl1267q1z000d2e6d949f2ge4",
"options": {
"isLong": false,
"labels": {
"button": "Send",
"placeholder": "Type your answer..."
},
"variableId": "cl126f4hf000i2e6d8zvzc3t1"
}
},
{
"id": "cl1289y1s00142e6dvbkpvbje",
"type": "Code",
"blockId": "cl1267q1z000d2e6d949f2ge4",
"options": {
"name": "Store Name in DB",
"content": "postMessage({from: \"typebot\", action: \"storeName\", content: \"{{Name}}\"}, \"*\")"
},
"outgoingEdgeId": "cl12bk56s000d2e69oll3nqxm"
}
],
"title": "Block #3",
"graphCoordinates": { "x": 269, "y": 381 }
},
{
"id": "cl126ixoq000p2e6dfbz9sype",
"steps": [
{
"id": "cl1266v6f000a2e6db7wj3ux7",
"type": "text",
"blockId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>Welcome {{Name}} 👋</div>",
"richText": [
{ "type": "p", "children": [{ "text": "Welcome {{Name}} 👋" }] }
],
"plainText": "Welcome {{Name}} 👋"
}
},
{
"id": "cl126hb9m000l2e6d5qk3mohn",
"type": "text",
"blockId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>I&#x27;m super pumped that you&#x27;ve decided to try out Typebot 😍</div>",
"richText": [
{
"type": "p",
"children": [
{
"text": "I'm super pumped that you've decided to try out Typebot 😍"
}
]
}
],
"plainText": "I'm super pumped that you've decided to try out Typebot 😍"
}
},
{
"id": "cl126hpw1000m2e6dneousygl",
"type": "text",
"blockId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>You are small steps away from meaningful, hyper-personalized experience for your users</div>",
"richText": [
{
"type": "p",
"children": [
{
"text": "You are small steps away from meaningful, hyper-personalized experience for your users"
}
]
}
],
"plainText": "You are small steps away from meaningful, hyper-personalized experience for your users"
}
},
{
"id": "cl126guhd000k2e6d6ypkex9z",
"type": "text",
"blockId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>Let&#x27;s get you set up for your Typebot journey.</div>",
"richText": [
{
"type": "p",
"children": [
{ "text": "Let's get you set up for your Typebot journey." }
]
}
],
"plainText": "Let's get you set up for your Typebot journey."
}
},
{
"id": "cl126ixp9000q2e6dslh0zypi",
"type": "text",
"blockId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>Do you work for a specific company?</div>",
"richText": [
{
"type": "p",
"children": [{ "text": "Do you work for a specific company?" }]
}
],
"plainText": "Do you work for a specific company?"
}
},
{
"id": "cl126jb2q000r2e6dgqlnxnt8",
"type": "choice input",
"items": [
{
"id": "cl126jb2q000s2e6dm60yq5p2",
"type": 0,
"stepId": "cl126jb2q000r2e6dgqlnxnt8",
"content": "Yes",
"outgoingEdgeId": "cl126jsoo000x2e6ditu7dgf8"
},
{
"id": "cl126jc5a000t2e6dqv91w7j6",
"type": 0,
"stepId": "cl126jb2q000r2e6dgqlnxnt8",
"content": "No",
"outgoingEdgeId": "cl126l5tx00122e6dmisci6h5"
}
],
"blockId": "cl126ixoq000p2e6dfbz9sype",
"options": { "buttonLabel": "Send", "isMultipleChoice": false }
}
],
"title": "Block #5",
"graphCoordinates": { "x": 614, "y": 244 }
},
{
"id": "cl126jioj000u2e6dqssno3hv",
"steps": [
{
"id": "cl126jioz000v2e6dwrk1f2cb",
"type": "text input",
"blockId": "cl126jioj000u2e6dqssno3hv",
"options": {
"isLong": false,
"labels": {
"button": "Send",
"placeholder": "Type the company name..."
},
"variableId": "cl126jqww000w2e6dq9yv4ifq"
}
},
{
"id": "cl12890kw00132e6dp9v5dexm",
"type": "Code",
"blockId": "cl126jioj000u2e6dqssno3hv",
"options": {
"name": "Store company in DB",
"content": "postMessage({from: \"typebot\", action: \"storeCompany\", content: \"{{Company}}\"}, \"*\")"
},
"outgoingEdgeId": "cl128ag8i00162e6dufv3tgo0"
}
],
"title": "Block #6",
"graphCoordinates": { "x": 969, "y": 308 }
},
{
"id": "cl126krbp00102e6dnjelmfa1",
"steps": [
{
"id": "cl126krck00112e6d1m6ctxpn",
"type": "text",
"blockId": "cl126krbp00102e6dnjelmfa1",
"content": {
"html": "<div>What type of forms are you planning to build with Typebot?</div>",
"richText": [
{
"type": "p",
"children": [
{
"text": "What type of forms are you planning to build with Typebot?"
}
]
}
],
"plainText": "What type of forms are you planning to build with Typebot?"
}
},
{
"id": "cl126lb8v00142e6duv5qe08l",
"type": "choice input",
"items": [
{
"id": "cl126onz9001g2e6dk0nbjeu6",
"type": 0,
"stepId": "cl126lb8v00142e6duv5qe08l",
"content": "Lead qualification"
},
{
"id": "cl126lm6c00172e6d1pfvdiju",
"type": 0,
"stepId": "cl126lb8v00142e6duv5qe08l",
"content": "Customer support"
},
{
"id": "cl126orr2001h2e6d0fqs7737",
"type": 0,
"stepId": "cl126lb8v00142e6duv5qe08l",
"content": "Customer research"
},
{
"id": "cl126oudu001i2e6dktwi7qwv",
"type": 0,
"stepId": "cl126lb8v00142e6duv5qe08l",
"content": "User onboarding"
},
{
"id": "cl126luv500192e6dl317ssyr",
"type": 0,
"stepId": "cl126lb8v00142e6duv5qe08l",
"content": "Quizzes"
},
{
"id": "cl126lz8q001a2e6d8b9lb3b5",
"type": 0,
"stepId": "cl126lb8v00142e6duv5qe08l",
"content": "Content distribution"
},
{
"id": "cl126nf7k001d2e6dg2zczjgz",
"type": 0,
"stepId": "cl126lb8v00142e6duv5qe08l",
"content": "FAQ"
},
{
"id": "cl126ngy8001e2e6ddfo5s9fm",
"type": 0,
"stepId": "cl126lb8v00142e6duv5qe08l",
"content": "Other"
}
],
"blockId": "cl126krbp00102e6dnjelmfa1",
"options": {
"variableId": "cl126mo3t001b2e6dvyi16bkd",
"buttonLabel": "Send",
"isMultipleChoice": true
}
},
{
"id": "cl128ain900172e6d1osj4u90",
"type": "Code",
"blockId": "cl126krbp00102e6dnjelmfa1",
"options": {
"name": "Store categories in DB",
"content": "postMessage({from: \"typebot\", action: \"storeCategories\", content: \"{{Categories}}\"}, \"*\")"
},
"outgoingEdgeId": "cl128azam00182e6dct61k7v5"
}
],
"title": "Block #6",
"graphCoordinates": { "x": 1218, "y": 510 }
},
{
"id": "cl126p75m001j2e6d73qmes0m",
"steps": [
{
"id": "cl126p76d001k2e6dbhnf2ysq",
"type": "text",
"blockId": "cl126p75m001j2e6d73qmes0m",
"content": {
"html": "<div>Thank you for answering those questions!</div>",
"richText": [
{
"type": "p",
"children": [
{ "text": "Thank you for answering those questions!" }
]
}
],
"plainText": "Thank you for answering those questions!"
}
},
{
"id": "cl128375600112e6d4l0jtuyf",
"type": "Code",
"blockId": "cl126p75m001j2e6d73qmes0m",
"options": {
"name": "Shoot confettis",
"content": "postMessage({from: \"typebot\", action: \"shootConfettis\"}, \"*\")"
}
},
{
"id": "cl126rfy6001t2e6d21gcb6b0",
"type": "image",
"blockId": "cl126p75m001j2e6d73qmes0m",
"content": {
"url": "https://media4.giphy.com/media/l0amJzVHIAfl7jMDos/giphy.gif?cid=fe3852a3i4c33635xdtj3nesr9uq4zteujaab6b0jr42gpxx&rid=giphy.gif&ct=g"
}
},
{
"id": "cl126txta001y2e6dtxrbsnek",
"type": "text",
"blockId": "cl126p75m001j2e6d73qmes0m",
"content": {
"html": "<div>You can reach out to me using the contact bubble on the bottom right corner 🤓</div>",
"richText": [
{
"type": "p",
"children": [
{
"text": "You can reach out to me using the contact bubble on the bottom right corner 🤓"
}
]
}
],
"plainText": "You can reach out to me using the contact bubble on the bottom right corner 🤓"
}
},
{
"id": "cl12buyly00172e6991bz38ch",
"blockId": "cl126p75m001j2e6d73qmes0m",
"type": "text",
"content": {
"html": "<div>Let&#x27;s create your first typebot...</div>",
"richText": [
{
"type": "p",
"children": [{ "text": "Let's create your first typebot..." }]
}
],
"plainText": "Let's create your first typebot..."
}
},
{
"id": "cl12bwpi800182e69kcivnp1s",
"blockId": "cl126p75m001j2e6d73qmes0m",
"type": "Code",
"options": {
"name": "Go to typebot creation",
"content": "setTimeout(() => {window.location.href = \"https://app.typebot.io/typebots/create?isFirstBot=true\"}, 4000)"
}
}
],
"title": "Block #7",
"graphCoordinates": { "x": 1612, "y": 1103 }
},
{
"id": "cl126pv6w001n2e6dp0qkvthu",
"steps": [
{
"id": "cl127yxym000b2e6d9hksxo6h",
"type": "text",
"blockId": "cl126pv6w001n2e6dp0qkvthu",
"content": {
"html": "<div>What else?</div>",
"richText": [
{ "type": "p", "children": [{ "text": "What else?" }] }
],
"plainText": "What else?"
}
},
{
"id": "cl126pv7n001o2e6dajltc4qz",
"type": "text input",
"blockId": "cl126pv6w001n2e6dp0qkvthu",
"options": {
"isLong": false,
"labels": { "button": "Send", "placeholder": "Type your answer" },
"variableId": "cl126q38p001q2e6d0hj23f6b"
}
},
{
"id": "cl128b34o00192e6dqjxs3cxf",
"type": "Code",
"blockId": "cl126pv6w001n2e6dp0qkvthu",
"options": {
"name": "Store Other categories in DB",
"content": "postMessage({from: \"typebot\", action: \"storeOtherCategories\", content: \"{{Other categories}}\"}, \"*\")"
},
"outgoingEdgeId": "cl128c0fu001a2e6droq69g6z"
}
],
"title": "Block #8",
"graphCoordinates": { "x": 1943, "y": 895 }
},
{
"id": "cl1278gx9002v2e6d4kf3v89s",
"steps": [
{
"id": "cl1278gyk002w2e6d744eb87n",
"type": "Condition",
"items": [
{
"id": "cl1278gyk002x2e6dwmpzs3nf",
"type": 1,
"stepId": "cl1278gyk002w2e6d744eb87n",
"content": {
"comparisons": [
{
"id": "cl1278irq002y2e6dv4965diw",
"value": "Other",
"variableId": "cl126mo3t001b2e6dvyi16bkd",
"comparisonOperator": "Contains"
}
],
"logicalOperator": "AND"
},
"outgoingEdgeId": "cl1278r3b002z2e6d6d6rk9dh"
}
],
"blockId": "cl1278gx9002v2e6d4kf3v89s",
"outgoingEdgeId": "cl1278trd00312e6dxmzhcmmn"
}
],
"title": "Block #13",
"graphCoordinates": { "x": 1585, "y": 792 }
}
],
"variables": [
{ "id": "cl126f4hf000i2e6d8zvzc3t1", "name": "Name" },
{ "id": "cl126jqww000w2e6dq9yv4ifq", "name": "Company" },
{ "id": "cl126mo3t001b2e6dvyi16bkd", "name": "Categories" },
{ "id": "cl126q38p001q2e6d0hj23f6b", "name": "Other categories" }
],
"edges": [
{
"id": "cl1266kt100082e6d1wks5dtp",
"to": { "blockId": "cl1266bah00032e6dgdnj4vgz" },
"from": {
"stepId": "cl1265zct0001mb1afel460do",
"blockId": "cl1265zct0000mb1a6bir36w7"
}
},
{
"id": "cl126jsoo000x2e6ditu7dgf8",
"to": { "blockId": "cl126jioj000u2e6dqssno3hv" },
"from": {
"itemId": "cl126jb2q000s2e6dm60yq5p2",
"stepId": "cl126jb2q000r2e6dgqlnxnt8",
"blockId": "cl126ixoq000p2e6dfbz9sype"
}
},
{
"id": "cl126l5tx00122e6dmisci6h5",
"to": { "blockId": "cl126krbp00102e6dnjelmfa1" },
"from": {
"itemId": "cl126jc5a000t2e6dqv91w7j6",
"stepId": "cl126jb2q000r2e6dgqlnxnt8",
"blockId": "cl126ixoq000p2e6dfbz9sype"
}
},
{
"id": "cl1278r3b002z2e6d6d6rk9dh",
"to": { "blockId": "cl126pv6w001n2e6dp0qkvthu" },
"from": {
"itemId": "cl1278gyk002x2e6dwmpzs3nf",
"stepId": "cl1278gyk002w2e6d744eb87n",
"blockId": "cl1278gx9002v2e6d4kf3v89s"
}
},
{
"id": "cl1278trd00312e6dxmzhcmmn",
"to": { "blockId": "cl126p75m001j2e6d73qmes0m" },
"from": {
"stepId": "cl1278gyk002w2e6d744eb87n",
"blockId": "cl1278gx9002v2e6d4kf3v89s"
}
},
{
"id": "cl128ag8i00162e6dufv3tgo0",
"to": { "blockId": "cl126krbp00102e6dnjelmfa1" },
"from": {
"stepId": "cl12890kw00132e6dp9v5dexm",
"blockId": "cl126jioj000u2e6dqssno3hv"
}
},
{
"id": "cl128azam00182e6dct61k7v5",
"to": { "blockId": "cl1278gx9002v2e6d4kf3v89s" },
"from": {
"stepId": "cl128ain900172e6d1osj4u90",
"blockId": "cl126krbp00102e6dnjelmfa1"
}
},
{
"id": "cl128c0fu001a2e6droq69g6z",
"to": { "blockId": "cl126p75m001j2e6d73qmes0m" },
"from": {
"stepId": "cl128b34o00192e6dqjxs3cxf",
"blockId": "cl126pv6w001n2e6dp0qkvthu"
}
},
{
"from": {
"blockId": "cl1266bah00032e6dgdnj4vgz",
"stepId": "cl1266bam00042e6dm0gn22vy",
"itemId": "cl1266bam00052e6dn1sdjnax"
},
"to": { "blockId": "cl126ixoq000p2e6dfbz9sype" },
"id": "cl12bk3j6000c2e69bak89ja9"
},
{
"from": {
"blockId": "cl1267q1z000d2e6d949f2ge4",
"stepId": "cl1289y1s00142e6dvbkpvbje"
},
"to": {
"blockId": "cl126ixoq000p2e6dfbz9sype",
"stepId": "cl126hb9m000l2e6d5qk3mohn"
},
"id": "cl12bk56s000d2e69oll3nqxm"
},
{
"from": {
"blockId": "cl1266bah00032e6dgdnj4vgz",
"stepId": "cl1266bam00042e6dm0gn22vy"
},
"to": { "blockId": "cl1267q1z000d2e6d949f2ge4" },
"id": "cl12bnfyd000g2e69g7lr3czq"
}
],
"theme": {
"chat": {
"inputs": {
"color": "#303235",
"backgroundColor": "#FFFFFF",
"placeholderColor": "#9095A0"
},
"buttons": { "color": "#FFFFFF", "backgroundColor": "#0042DA" },
"hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" },
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" },
"hostAvatar": {
"isEnabled": true,
"url": "https://s3.eu-west-3.amazonaws.com/typebot/typebots/ckzp7a2za005809lczf2knzix/273013187_1315820332248257_6244778509534754615_n.jpg"
}
},
"general": { "font": "Open Sans", "background": { "type": "None" } }
},
"settings": {
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isNewResultOnRefreshEnabled": false
},
"metadata": {
"description": "Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form."
},
"typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 }
},
"publicId": "typebot-onboarding",
"customDomain": null
}

View File

@ -32,7 +32,6 @@ export const RealTimeResults = () => {
}, [])
const processMessage = (event: MessageEvent) => {
console.log(event.data)
if (event.data.from === 'typebot') refreshIframeContent()
}

View File

@ -21,11 +21,22 @@ export const TypebotPage = ({
url,
}: TypebotPageProps & { typebot: PublicTypebot }) => {
const [showTypebot, setShowTypebot] = useState(false)
const [predefinedVariables, setPredefinedVariables] =
useState<{ [key: string]: string }>()
const [error, setError] = useState<Error | undefined>(
isIE ? new Error('Internet explorer is not supported') : undefined
)
const [resultId, setResultId] = useState<string | undefined>()
useEffect(() => {
const urlParams = new URLSearchParams(location.search)
const predefinedVariables: { [key: string]: string } = {}
urlParams.forEach((value, key) => {
predefinedVariables[key] = value
})
setPredefinedVariables(predefinedVariables)
}, [])
// Workaround for react-frame-component bug (https://github.com/ryanseddon/react-frame-component/pull/207)
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
@ -83,6 +94,7 @@ export const TypebotPage = ({
{showTypebot && (
<TypebotViewer
typebot={typebot}
predefinedVariables={predefinedVariables}
onNewAnswer={handleNewAnswer}
onCompleted={handleCompleted}
onVariablesPrefilled={initializeResult}

View File

@ -46,7 +46,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
.status(404)
.send({ statusCode: 404, data: { message: `Couldn't find webhook` } })
const preparedWebhook = prepareWebhookAttributes(webhook, step.options)
console.log(preparedWebhook)
const result = await executeWebhook(typebot)(
preparedWebhook,
variables,

View File

@ -0,0 +1,122 @@
{
"id": "cl13bgvlm0050t71aq6a3w777",
"createdAt": "2022-03-23T08:41:30.106Z",
"updatedAt": "2022-03-23T08:41:30.106Z",
"name": "My typebot",
"ownerId": "cl139ni700000481a01gxhw4z",
"publishedTypebotId": null,
"folderId": null,
"blocks": [
{
"id": "cl13bgvlk0000t71a4wabccvw",
"steps": [
{
"id": "cl13bgvlk0001t71a3pilbj53",
"type": "start",
"label": "Start",
"blockId": "cl13bgvlk0000t71a4wabccvw",
"outgoingEdgeId": "cl13bgz4800062e6dv7ejcchb"
}
],
"title": "Start",
"graphCoordinates": { "x": 0, "y": 0 }
},
{
"id": "cl13bgy1s00042e6dao1wyobm",
"graphCoordinates": { "x": 329, "y": 65 },
"title": "Block #1",
"steps": [
{
"id": "cl13bgy1w00052e6d5x57wt7o",
"blockId": "cl13bgy1s00042e6dao1wyobm",
"type": "text",
"content": {
"html": "<div>Hey I know you!</div>",
"richText": [
{ "type": "p", "children": [{ "text": "Hey I know you!" }] }
],
"plainText": "Hey I know you!"
}
},
{
"id": "cl13bh6jd00072e6dftdirwy4",
"blockId": "cl13bgy1s00042e6dao1wyobm",
"type": "text",
"content": {
"html": "<div>Your name is {{Name}}</div>",
"richText": [
{ "type": "p", "children": [{ "text": "Your name is {{Name}}" }] }
],
"plainText": "Your name is {{Name}}"
}
},
{
"id": "cl13bhfxd00092e6dydvcqlhm",
"blockId": "cl13bgy1s00042e6dao1wyobm",
"type": "text",
"content": {
"html": "<div>What&#x27;s your email?</div>",
"richText": [
{ "type": "p", "children": [{ "text": "What's your email?" }] }
],
"plainText": "What's your email?"
}
},
{
"id": "cl13bhnay000a2e6dxa630dh3",
"blockId": "cl13bgy1s00042e6dao1wyobm",
"type": "email input",
"options": {
"labels": { "button": "Send", "placeholder": "Type your email..." },
"retryMessageContent": "This email doesn't seem to be valid. Can you type it again?",
"variableId": "cl13bhr3w000b2e6d3c9kid0p"
}
}
]
}
],
"variables": [
{ "id": "cl13bha3l00082e6duaz0xm6f", "name": "Name" },
{ "id": "cl13bhr3w000b2e6d3c9kid0p", "name": "Email" }
],
"edges": [
{
"from": {
"blockId": "cl13bgvlk0000t71a4wabccvw",
"stepId": "cl13bgvlk0001t71a3pilbj53"
},
"to": { "blockId": "cl13bgy1s00042e6dao1wyobm" },
"id": "cl13bgz4800062e6dv7ejcchb"
}
],
"theme": {
"chat": {
"inputs": {
"color": "#303235",
"backgroundColor": "#FFFFFF",
"placeholderColor": "#9095A0"
},
"buttons": { "color": "#FFFFFF", "backgroundColor": "#0042DA" },
"hostAvatar": {
"url": "https://avatars.githubusercontent.com/u/16015833?v=4",
"isEnabled": true
},
"hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" },
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" }
},
"general": { "font": "Open Sans", "background": { "type": "None" } }
},
"settings": {
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isNewResultOnRefreshEnabled": false
},
"metadata": {
"description": "Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form."
},
"typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 }
},
"publicId": null,
"customDomain": null
}

View File

@ -0,0 +1,22 @@
import test, { expect } from '@playwright/test'
import { importTypebotInDatabase } from '../services/database'
import cuid from 'cuid'
import path from 'path'
import { typebotViewer } from '../services/selectorUtils'
test('should correctly be injected', async ({ page }) => {
const typebotId = cuid()
await importTypebotInDatabase(
path.join(__dirname, '../fixtures/typebots/predefinedVariables.json'),
{ id: typebotId, publicId: `${typebotId}-public` }
)
await page.goto(`/${typebotId}-public`)
await expect(typebotViewer(page).locator('text="Your name is"')).toBeVisible()
await page.goto(`/${typebotId}-public?Name=Baptiste&Email=email@test.com`)
await expect(
typebotViewer(page).locator('text="Your name is Baptiste"')
).toBeVisible()
await expect(
typebotViewer(page).locator('input[value="email@test.com"]')
).toBeVisible()
})

View File

@ -158,7 +158,7 @@ export const ChatBlock = ({
if (currentStep?.outgoingEdgeId || processedSteps.length === steps.length)
return onBlockEnd(currentStep.outgoingEdgeId)
}
const nextStep = steps[processedSteps.length]
const nextStep = steps[processedSteps.length + startStepIndex]
if (nextStep) insertStepInStack(nextStep)
}

View File

@ -11,12 +11,14 @@ import { LinkedTypebot, useTypebot } from 'contexts/TypebotContext'
type Props = {
theme: Theme
predefinedVariables?: { [key: string]: string | undefined }
onNewBlockVisible: (edge: Edge) => void
onCompleted: () => void
onVariablesPrefilled?: (prefilledVariables: VariableWithValue[]) => void
}
export const ConversationContainer = ({
theme,
predefinedVariables,
onNewBlockVisible,
onCompleted,
onVariablesPrefilled,
@ -50,23 +52,28 @@ export const ConversationContainer = ({
}
useEffect(() => {
const prefilledVariables = injectUrlParamsIntoVariables()
if (onVariablesPrefilled) {
onVariablesPrefilled(prefilledVariables)
setPrefilledVariables(prefilledVariables)
if (predefinedVariables) {
const prefilledVariables = injectPredefinedVariables(predefinedVariables)
if (onVariablesPrefilled) {
onVariablesPrefilled(prefilledVariables)
setPrefilledVariables(prefilledVariables)
}
}
displayNextBlock(typebot.blocks[0].steps[0].outgoingEdgeId)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const injectUrlParamsIntoVariables = () => {
const urlParams = new URLSearchParams(location.search)
const injectPredefinedVariables = (predefinedVariables: {
[key: string]: string | undefined
}) => {
const prefilledVariables: VariableWithValue[] = []
urlParams.forEach((value, key) => {
Object.keys(predefinedVariables).forEach((key) => {
const matchingVariable = typebot.variables.find(
(v) => v.name.toLowerCase() === key.toLowerCase()
)
if (isNotDefined(matchingVariable)) return
const value = predefinedVariables[key]
if (!value) return
updateVariableValue(matchingVariable?.id, value)
prefilledVariables.push({ ...matchingVariable, value })
})

View File

@ -26,17 +26,20 @@ export type TypebotViewerProps = {
isPreview?: boolean
apiHost?: string
style?: CSSProperties
predefinedVariables?: { [key: string]: string | undefined }
onNewBlockVisible?: (edge: Edge) => void
onNewAnswer?: (answer: Answer) => void
onNewLog?: (log: Omit<Log, 'id' | 'createdAt' | 'resultId'>) => void
onCompleted?: () => void
onVariablesPrefilled?: (prefilledVariables: VariableWithValue[]) => void
}
export const TypebotViewer = ({
typebot,
apiHost = process.env.NEXT_PUBLIC_VIEWER_URL,
isPreview = false,
style,
predefinedVariables,
onNewLog,
onNewBlockVisible,
onNewAnswer,
@ -104,6 +107,7 @@ export const TypebotViewer = ({
theme={typebot.theme}
onNewBlockVisible={handleNewBlockVisible}
onCompleted={handleCompleted}
predefinedVariables={predefinedVariables}
onVariablesPrefilled={onVariablesPrefilled}
/>
</div>

View File

@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "company" TEXT,
ADD COLUMN "onboardingCategories" TEXT[];

View File

@ -56,6 +56,8 @@ model User {
customDomains CustomDomain[]
apiToken String?
CollaboratorsOnTypebots CollaboratorsOnTypebots[]
company String?
onboardingCategories String[]
}
model CustomDomain {

View File

@ -1,6 +1,6 @@
{
"name": "typebot-js",
"version": "2.2.0",
"version": "2.2.1",
"main": "dist/index.js",
"unpkg": "dist/index.umd.min.js",
"license": "AGPL-3.0-or-later",

View File

@ -1,40 +1,44 @@
import { ButtonParams } from "../../types";
import { ButtonParams } from '../../types'
export const createButton = (params?: ButtonParams): HTMLButtonElement => {
const button = document.createElement("button");
button.id = "typebot-bubble-button";
button.style.backgroundColor = params?.color ?? "#0042DA";
button.appendChild(createButtonIcon(params?.iconUrl));
button.appendChild(createCloseIcon());
return button;
};
const button = document.createElement('button')
button.id = 'typebot-bubble-button'
button.style.backgroundColor = params?.color ?? '#0042DA'
button.appendChild(createButtonIcon(params?.iconUrl, params?.iconStyle))
button.appendChild(createCloseIcon())
return button
}
const createButtonIcon = (src?: string): SVGElement | HTMLImageElement => {
if (!src) return createDefaultIcon();
const icon = document.createElement("img");
icon.classList.add("icon");
icon.src = src;
return icon;
};
const createButtonIcon = (
src?: string,
style?: string
): SVGElement | HTMLImageElement => {
if (!src) return createDefaultIcon()
const icon = document.createElement('img')
icon.classList.add('icon')
icon.src = src
if (style) icon.setAttribute('style', style)
return icon
}
const createDefaultIcon = (): SVGElement => {
const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
icon.setAttribute("viewBox", "0 0 41 19");
icon.style.width = "63%";
icon.innerHTML = typebotLogoSvgTextContent();
icon.classList.add("icon");
return icon;
};
const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
icon.setAttribute('viewBox', '0 0 41 19')
icon.style.width = '63%'
icon.innerHTML = typebotLogoSvgTextContent()
icon.classList.add('icon')
return icon
}
const createCloseIcon = (): SVGElement => {
const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
icon.setAttribute("viewBox", "0 0 512 512");
icon.innerHTML = closeSvgPath;
icon.classList.add("close-icon");
return icon;
};
const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
icon.setAttribute('viewBox', '0 0 512 512')
icon.innerHTML = closeSvgPath
icon.classList.add('close-icon')
return icon
}
const typebotLogoSvgTextContent = () =>
`<rect x="40.29" y="0.967773" width="6.83761" height="30.7692" rx="3.4188" transform="rotate(90 40.29 0.967773)"></rect> <path fill-rule="evenodd" clip-rule="evenodd" d="M3.70884 7.80538C5.597 7.80538 7.12765 6.27473 7.12765 4.38658C7.12765 2.49842 5.597 0.967773 3.70884 0.967773C1.82069 0.967773 0.290039 2.49842 0.290039 4.38658C0.290039 6.27473 1.82069 7.80538 3.70884 7.80538Z" fill="white"></path> <rect x="0.290039" y="18.0615" width="6.83761" height="30.7692" rx="3.4188" transform="rotate(-90 0.290039 18.0615)" fill="white"></rect> <path fill-rule="evenodd" clip-rule="evenodd" d="M36.8712 11.2239C34.9831 11.2239 33.4524 12.7546 33.4524 14.6427C33.4524 16.5309 34.9831 18.0615 36.8712 18.0615C38.7594 18.0615 40.29 16.5309 40.29 14.6427C40.29 12.7546 38.7594 11.2239 36.8712 11.2239Z" fill="white"></path>`;
`<rect x="40.29" y="0.967773" width="6.83761" height="30.7692" rx="3.4188" transform="rotate(90 40.29 0.967773)"></rect> <path fill-rule="evenodd" clip-rule="evenodd" d="M3.70884 7.80538C5.597 7.80538 7.12765 6.27473 7.12765 4.38658C7.12765 2.49842 5.597 0.967773 3.70884 0.967773C1.82069 0.967773 0.290039 2.49842 0.290039 4.38658C0.290039 6.27473 1.82069 7.80538 3.70884 7.80538Z" fill="white"></path> <rect x="0.290039" y="18.0615" width="6.83761" height="30.7692" rx="3.4188" transform="rotate(-90 0.290039 18.0615)" fill="white"></rect> <path fill-rule="evenodd" clip-rule="evenodd" d="M36.8712 11.2239C34.9831 11.2239 33.4524 12.7546 33.4524 14.6427C33.4524 16.5309 34.9831 18.0615 36.8712 18.0615C38.7594 18.0615 40.29 16.5309 40.29 14.6427C40.29 12.7546 38.7594 11.2239 36.8712 11.2239Z" fill="white"></path>`
export const closeSvgPath = `<path d="M278.6 256l68.2-68.2c6.2-6.2 6.2-16.4 0-22.6-6.2-6.2-16.4-6.2-22.6 0L256 233.4l-68.2-68.2c-6.2-6.2-16.4-6.2-22.6 0-3.1 3.1-4.7 7.2-4.7 11.3 0 4.1 1.6 8.2 4.7 11.3l68.2 68.2-68.2 68.2c-3.1 3.1-4.7 7.2-4.7 11.3 0 4.1 1.6 8.2 4.7 11.3 6.2 6.2 16.4 6.2 22.6 0l68.2-68.2 68.2 68.2c6.2 6.2 16.4 6.2 22.6 0 6.2-6.2 6.2-16.4 0-22.6L278.6 256z"></path>`;
export const closeSvgPath = `<path d="M278.6 256l68.2-68.2c6.2-6.2 6.2-16.4 0-22.6-6.2-6.2-16.4-6.2-22.6 0L256 233.4l-68.2-68.2c-6.2-6.2-16.4-6.2-22.6 0-3.1 3.1-4.7 7.2-4.7 11.3 0 4.1 1.6 8.2 4.7 11.3l68.2 68.2-68.2 68.2c-3.1 3.1-4.7 7.2-4.7 11.3 0 4.1 1.6 8.2 4.7 11.3 6.2 6.2 16.4 6.2 22.6 0l68.2-68.2 68.2 68.2c6.2 6.2 16.4 6.2 22.6 0 6.2-6.2 6.2-16.4 0-22.6L278.6 256z"></path>`

View File

@ -29,6 +29,7 @@ export type BubbleParams = {
export type ButtonParams = {
color?: string
iconUrl?: string
iconStyle?: string
}
export type ProactiveMessageParams = {

View File

@ -18,5 +18,6 @@
"dx": {
"cache": false
}
}
},
"globalDependencies": ["$NEXT_PUBLIC_E2E_TEST"]
}

View File

@ -4067,6 +4067,11 @@
"@types/node" "*"
"@types/responselike" "*"
"@types/canvas-confetti@^1.4.2":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@types/canvas-confetti/-/canvas-confetti-1.4.2.tgz#35c99fc904492fdcc6515c742509e04f3527211c"
integrity sha512-t45KUDHlwrD9PJVRHc5z1SlXhO82BQEgMKUXGEV1KnWLFMPA6Y5LfUsLTHHzH9KcKDHZLEiYYH5nIDcjRKWNTg==
"@types/connect-history-api-fallback@^1.3.5":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae"
@ -5896,6 +5901,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001283, caniuse-lite@^1.0.30001297, can
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001314.tgz#65c7f9fb7e4594fca0a333bec1d8939662377596"
integrity sha512-0zaSO+TnCHtHJIbpLroX7nsD+vYuOVjl3uzFbJO1wMVbuveJA0RK2WcQA9ZUIOiO0/ArMiMgHJLxfEZhQiC0kw==
canvas-confetti@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/canvas-confetti/-/canvas-confetti-1.5.1.tgz#bf5b8622ef3bcd347378a972fc4194a89cfe0c9b"
integrity sha512-Ncz+oZJP6OvY7ti4E1slxVlyAV/3g7H7oQtcCDXgwGgARxPnwYY9PW5Oe+I8uvspYNtuHviAdgA0LfcKFWJfpg==
ccount@^1.0.0, ccount@^1.0.3:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043"