@@ -7,16 +7,16 @@ import {
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react'
|
||||
import { ToolIcon, TemplateIcon, DownloadIcon } from '@/components/icons'
|
||||
import { Typebot } from '@typebot.io/schemas'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useState } from 'react'
|
||||
import { ImportTypebotFromFileButton } from './ImportTypebotFromFileButton'
|
||||
import { TemplatesModal } from './TemplatesModal'
|
||||
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
||||
import { useUser } from '@/features/account/hooks/useUser'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
import { Typebot } from '@typebot.io/schemas'
|
||||
import { TemplatesModal } from './TemplatesModal'
|
||||
|
||||
export const CreateNewTypebotButtons = () => {
|
||||
const { t } = useTranslate()
|
||||
@@ -29,12 +29,41 @@ export const CreateNewTypebotButtons = () => {
|
||||
|
||||
const { showToast } = useToast()
|
||||
|
||||
const { mutate } = trpc.typebot.createTypebot.useMutation({
|
||||
const { mutate: createTypebot } = trpc.typebot.createTypebot.useMutation({
|
||||
onMutate: () => {
|
||||
setIsLoading(true)
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast({ description: error.message })
|
||||
showToast({
|
||||
title: 'Failed to create bot',
|
||||
description: error.message,
|
||||
})
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
router.push({
|
||||
pathname: `/typebots/${data.typebot.id}/edit`,
|
||||
query:
|
||||
router.query.isFirstBot === 'true'
|
||||
? {
|
||||
isFirstBot: 'true',
|
||||
}
|
||||
: {},
|
||||
})
|
||||
},
|
||||
onSettled: () => {
|
||||
setIsLoading(false)
|
||||
},
|
||||
})
|
||||
|
||||
const { mutate: importTypebot } = trpc.typebot.importTypebot.useMutation({
|
||||
onMutate: () => {
|
||||
setIsLoading(true)
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast({
|
||||
title: 'Failed to import bot',
|
||||
description: error.message,
|
||||
})
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
router.push({
|
||||
@@ -55,19 +84,21 @@ export const CreateNewTypebotButtons = () => {
|
||||
const handleCreateSubmit = async (typebot?: Typebot) => {
|
||||
if (!user || !workspace) return
|
||||
const folderId = router.query.folderId?.toString() ?? null
|
||||
mutate({
|
||||
workspaceId: workspace.id,
|
||||
typebot: {
|
||||
...(typebot
|
||||
? {
|
||||
...typebot,
|
||||
publicId: undefined,
|
||||
customDomain: undefined,
|
||||
}
|
||||
: {}),
|
||||
folderId,
|
||||
},
|
||||
})
|
||||
if (typebot)
|
||||
importTypebot({
|
||||
workspaceId: workspace.id,
|
||||
typebot: {
|
||||
...typebot,
|
||||
folderId,
|
||||
},
|
||||
})
|
||||
else
|
||||
createTypebot({
|
||||
workspaceId: workspace.id,
|
||||
typebot: {
|
||||
folderId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -130,6 +161,7 @@ export const CreateNewTypebotButtons = () => {
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onTypebotChoose={handleCreateSubmit}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</VStack>
|
||||
)
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { Button, ButtonProps, chakra } from '@chakra-ui/react'
|
||||
import { Typebot, typebotCreateSchema } from '@typebot.io/schemas'
|
||||
import { preprocessTypebot } from '@typebot.io/schemas/features/typebot/helpers/preprocessTypebot'
|
||||
import { Typebot } from '@typebot.io/schemas'
|
||||
import React, { ChangeEvent } from 'react'
|
||||
import { z } from 'zod'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
|
||||
type Props = {
|
||||
@@ -22,10 +20,12 @@ export const ImportTypebotFromFileButton = ({
|
||||
const file = e.target.files[0]
|
||||
const fileContent = await readFile(file)
|
||||
try {
|
||||
const typebot = z
|
||||
.preprocess(preprocessTypebot, typebotCreateSchema)
|
||||
.parse(JSON.parse(fileContent))
|
||||
onNewTypebot(typebot as Typebot)
|
||||
const typebot = JSON.parse(fileContent)
|
||||
onNewTypebot({
|
||||
...typebot,
|
||||
events: typebot.events ?? null,
|
||||
icon: typebot.icon ?? null,
|
||||
} as Typebot)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
showToast({
|
||||
|
||||
@@ -25,17 +25,21 @@ type Props = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onTypebotChoose: (typebot: Typebot) => void
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
|
||||
export const TemplatesModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onTypebotChoose,
|
||||
isLoading,
|
||||
}: Props) => {
|
||||
const { t } = useTranslate()
|
||||
const templateCardBackgroundColor = useColorModeValue('white', 'gray.800')
|
||||
const [typebot, setTypebot] = useState<Typebot>()
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<TemplateProps>(
|
||||
templates[0]
|
||||
)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const { showToast } = useToast()
|
||||
|
||||
const fetchTemplate = useCallback(
|
||||
@@ -55,10 +59,9 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
|
||||
fetchTemplate(templates[0])
|
||||
}, [fetchTemplate])
|
||||
|
||||
const onUseThisTemplateClick = () => {
|
||||
const onUseThisTemplateClick = async () => {
|
||||
if (!typebot) return
|
||||
onTypebotChoose(typebot)
|
||||
setIsLoading(true)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -106,7 +106,6 @@ export const templates: TemplateProps[] = [
|
||||
emoji: '🦾',
|
||||
fileName: 'lead-gen-ai.json',
|
||||
category: 'marketing',
|
||||
isNew: true,
|
||||
description:
|
||||
'You are a marketing agency and this bot allows you generate new leads interested in your services. An AI block is used to dig deeper into the user needs.',
|
||||
},
|
||||
@@ -115,7 +114,6 @@ export const templates: TemplateProps[] = [
|
||||
emoji: '🐶',
|
||||
fileName: 'dog-insurance-offer.json',
|
||||
category: 'marketing',
|
||||
isNew: true,
|
||||
description:
|
||||
'You are a dog insurance company. This bot allows you to collect information about the dog and provide a quote.',
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@ test.describe.parallel('Templates page', () => {
|
||||
page.locator('button >> text="Settings & Members"')
|
||||
).toBeEnabled()
|
||||
await page.click('text=Start from scratch')
|
||||
await expect(page).toHaveURL(new RegExp(`/edit`))
|
||||
await expect(page).toHaveURL(new RegExp(`/edit`), { timeout: 20000 })
|
||||
})
|
||||
|
||||
test('From file should import correctly', async ({ page }) => {
|
||||
@@ -18,7 +18,7 @@ test.describe.parallel('Templates page', () => {
|
||||
'input[type="file"]',
|
||||
getTestAsset('typebots/singleChoiceTarget.json')
|
||||
)
|
||||
await expect(page).toHaveURL(new RegExp(`/edit`))
|
||||
await expect(page).toHaveURL(new RegExp(`/edit`), { timeout: 20000 })
|
||||
})
|
||||
|
||||
test('Templates should be previewable and usable', async ({ page }) => {
|
||||
|
||||
Reference in New Issue
Block a user