Skip to content

Commit

Permalink
feat: ✨ Add new onboarding flow
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Mar 23, 2022
1 parent f9aba27 commit f4e6f63
Show file tree
Hide file tree
Showing 32 changed files with 1,115 additions and 89 deletions.
6 changes: 5 additions & 1 deletion apps/builder/components/account/PersonalInfoForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
5 changes: 5 additions & 0 deletions apps/builder/components/dashboard/FolderContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand Down Expand Up @@ -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>
Expand All @@ -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 />}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
176 changes: 176 additions & 0 deletions apps/builder/components/dashboard/OnboardingModal.tsx
Original file line number Diff line number Diff line change
@@ -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,
})
}
4 changes: 1 addition & 3 deletions apps/builder/components/editor/preview/PreviewDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/components/share/codeSnippets/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>`
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ export const WebhookSettings = ({

return () => {
setLocalWebhook((localWebhook) => {
console.log(localWebhook)
if (!localWebhook) return
updateWebhook(webhookId, localWebhook).then()
return localWebhook
Expand Down
7 changes: 6 additions & 1 deletion apps/builder/components/shared/SupportBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -21,6 +22,7 @@ export const headerHeight = 56

export const TypebotHeader = () => {
const router = useRouter()
const { rightPanel } = useEditor()
const {
typebot,
updateOnBothTypebots,
Expand Down Expand Up @@ -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 />
Expand Down
9 changes: 8 additions & 1 deletion apps/builder/components/shared/buttons/PublishButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -24,6 +26,11 @@ export const PublishButton = () => {
restorePublishedTypebot,
} = useTypebot()

const handlePublishClick = () => {
publishTypebot()
if (!publishedTypebot) push(`/typebots/${query.typebotId}/share`)
}

return (
<HStack spacing="1px">
<Tooltip
Expand All @@ -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'}
Expand Down
7 changes: 4 additions & 3 deletions apps/builder/contexts/UserContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
}>({})
Expand Down Expand Up @@ -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)
Expand Down
Loading

3 comments on commit f4e6f63

@vercel
Copy link

@vercel vercel bot commented on f4e6f63 Mar 23, 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 f4e6f63 Mar 23, 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

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

Please sign in to comment.