2
0

feat(dashboard): 🎨 Improve typebot import

This commit is contained in:
Baptiste Arnaud
2022-04-09 16:48:12 -05:00
parent 82446c41af
commit b38b114f12
3 changed files with 84 additions and 82 deletions

View File

@ -16,11 +16,13 @@ import { isMobile } from 'services/utils'
import { MoreButton } from 'components/dashboard/FolderContent/MoreButton' import { MoreButton } from 'components/dashboard/FolderContent/MoreButton'
import { ConfirmModal } from 'components/modals/ConfirmModal' import { ConfirmModal } from 'components/modals/ConfirmModal'
import { GripIcon } from 'assets/icons' import { GripIcon } from 'assets/icons'
import { deleteTypebot, duplicateTypebot } from 'services/typebots' import { deleteTypebot, importTypebot, getTypebot } from 'services/typebots'
import { Typebot } from 'models' import { Typebot } from 'models'
import { useTypebotDnd } from 'contexts/TypebotDndContext' import { useTypebotDnd } from 'contexts/TypebotDndContext'
import { useDebounce } from 'use-debounce' import { useDebounce } from 'use-debounce'
import { TypebotIcon } from 'components/shared/TypebotHeader/TypebotIcon' import { TypebotIcon } from 'components/shared/TypebotHeader/TypebotIcon'
import { useUser } from 'contexts/UserContext'
import { Plan } from 'db'
type ChatbotCardProps = { type ChatbotCardProps = {
typebot: Pick<Typebot, 'id' | 'publishedTypebotId' | 'name' | 'icon'> typebot: Pick<Typebot, 'id' | 'publishedTypebotId' | 'name' | 'icon'>
@ -36,6 +38,7 @@ export const TypebotButton = ({
onMouseDown, onMouseDown,
}: ChatbotCardProps) => { }: ChatbotCardProps) => {
const router = useRouter() const router = useRouter()
const { user } = useUser()
const { draggedTypebot } = useTypebotDnd() const { draggedTypebot } = useTypebotDnd()
const [draggedTypebotDebounced] = useDebounce(draggedTypebot, 200) const [draggedTypebotDebounced] = useDebounce(draggedTypebot, 200)
const { const {
@ -71,7 +74,13 @@ export const TypebotButton = ({
const handleDuplicateClick = async (e: React.MouseEvent) => { const handleDuplicateClick = async (e: React.MouseEvent) => {
e.stopPropagation() e.stopPropagation()
const { data: createdTypebot, error } = await duplicateTypebot(typebot.id) const { data } = await getTypebot(typebot.id)
const typebotToDuplicate = data?.typebot
if (!typebotToDuplicate) return { error: new Error('Typebot not found') }
const { data: createdTypebot, error } = await importTypebot(
data.typebot,
user?.plan ?? Plan.FREE
)
if (error) if (error)
return toast({ return toast({
title: "Couldn't duplicate typebot", title: "Couldn't duplicate typebot",

View File

@ -11,7 +11,7 @@ import { useUser } from 'contexts/UserContext'
import { Typebot } from 'models' import { Typebot } from 'models'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import React, { useState } from 'react' import React, { useState } from 'react'
import { importTypebot, createTypebot } from 'services/typebots' import { createTypebot, importTypebot } from 'services/typebots'
import { ImportTypebotFromFileButton } from './ImportTypebotFromFileButton' import { ImportTypebotFromFileButton } from './ImportTypebotFromFileButton'
import { TemplatesModal } from './TemplatesModal' import { TemplatesModal } from './TemplatesModal'
@ -46,7 +46,7 @@ export const CreateNewTypebotButtons = () => {
}, },
}, },
}, },
user user.plan
) )
: await createTypebot({ : await createTypebot({
folderId, folderId,

View File

@ -48,11 +48,11 @@ import {
} from 'utils' } from 'utils'
import { dequal } from 'dequal' import { dequal } from 'dequal'
import { stringify } from 'qs' import { stringify } from 'qs'
import { isChoiceInput, isConditionStep, sendRequest, omit } from 'utils' import { isChoiceInput, isConditionStep, sendRequest } from 'utils'
import cuid from 'cuid' import cuid from 'cuid'
import { diff } from 'deep-object-diff' import { diff } from 'deep-object-diff'
import { duplicateWebhook } from 'services/webhook' import { duplicateWebhook } from 'services/webhook'
import { Plan, User } from 'db' import { Plan } from 'db'
export type TypebotInDashboard = Pick< export type TypebotInDashboard = Pick<
Typebot, Typebot,
@ -96,91 +96,84 @@ export const createTypebot = async ({
}) })
} }
export const importTypebot = async (typebot: Typebot, user: User) => { export const importTypebot = async (typebot: Typebot, userPlan: Plan) => {
const typebotToImport: Omit<Typebot, 'id' | 'updatedAt' | 'createdAt'> = omit(
{
...typebot,
publishedTypebotId: null,
publicId: null,
customDomain: null,
},
'id',
'updatedAt',
'createdAt'
)
return sendRequest<Typebot>({ return sendRequest<Typebot>({
url: `/api/typebots`, url: `/api/typebots`,
method: 'POST', method: 'POST',
body: cleanUpTypebot(typebotToImport, user), body: await duplicateTypebot(typebot, userPlan),
}) })
} }
export const duplicateTypebot = async (typebotId: string) => { const duplicateTypebot = async (
const { data } = await getTypebot(typebotId) typebot: Typebot,
const typebotToDuplicate = data?.typebot userPlan: Plan
if (!typebotToDuplicate) return { error: new Error('Typebot not found') } ): Promise<Typebot> => {
const duplicatedTypebot: Omit<Typebot, 'id' | 'updatedAt' | 'createdAt'> = const blockIdsMapping = generateOldNewIdsMapping(typebot.blocks)
omit( const edgeIdsMapping = generateOldNewIdsMapping(typebot.edges)
{ return {
...typebotToDuplicate, ...typebot,
name: `${typebotToDuplicate.name} copy`, id: cuid(),
publishedTypebotId: null, name: `${typebot.name} copy`,
publicId: null, publishedTypebotId: null,
customDomain: null, publicId: null,
}, customDomain: null,
'id', blocks: await Promise.all(
'updatedAt', typebot.blocks.map(async (b) => ({
'createdAt' ...b,
) id: blockIdsMapping.get(b.id) as string,
return sendRequest<Typebot>({ steps: await Promise.all(
url: `/api/typebots`, b.steps.map(async (s) => {
method: 'POST', const newIds = {
body: await cleanAndDuplicateWebhooks(duplicatedTypebot), blockId: blockIdsMapping.get(s.blockId) as string,
}) outgoingEdgeId: s.outgoingEdgeId
} ? edgeIdsMapping.get(s.outgoingEdgeId)
: undefined,
const cleanUpTypebot = ( }
typebot: Omit<Typebot, 'id' | 'updatedAt' | 'createdAt'>, if (isWebhookStep(s)) {
user: User const newWebhook = await duplicateWebhook(s.webhookId)
): Omit<Typebot, 'id' | 'updatedAt' | 'createdAt'> => ({ return {
...typebot, ...s,
blocks: typebot.blocks.map((b) => ({ webhookId: newWebhook ? newWebhook.id : cuid(),
...b, ...newIds,
steps: b.steps.map((s) => }
isWebhookStep(s) ? { ...s, webhookId: cuid() } : s }
return {
...s,
...newIds,
}
})
),
}))
), ),
})), edges: typebot.edges.map((e) => ({
settings: ...e,
typebot.settings.general.isBrandingEnabled === false && id: edgeIdsMapping.get(e.id) as string,
user.plan === Plan.FREE from: {
? { ...e.from,
...typebot.settings, blockId: blockIdsMapping.get(e.from.blockId) as string,
general: { ...typebot.settings.general, isBrandingEnabled: true }, },
} to: { ...e.to, blockId: blockIdsMapping.get(e.to.blockId) as string },
: typebot.settings, })),
}) settings:
typebot.settings.general.isBrandingEnabled === false &&
const cleanAndDuplicateWebhooks = async ( userPlan === Plan.FREE
typebot: Omit<Typebot, 'id' | 'updatedAt' | 'createdAt'> ? {
) => ({ ...typebot.settings,
...typebot, general: { ...typebot.settings.general, isBrandingEnabled: true },
blocks: await Promise.all(
typebot.blocks.map(async (b) => ({
...b,
steps: await Promise.all(
b.steps.map(async (s) => {
if (isWebhookStep(s)) {
const newWebhook = await duplicateWebhook(s.webhookId)
return { ...s, webhookId: newWebhook ? newWebhook.id : cuid() }
} }
return s : typebot.settings,
}) createdAt: new Date().toISOString(),
), updatedAt: new Date().toISOString(),
})) }
), }
})
const getTypebot = (typebotId: string) => const generateOldNewIdsMapping = (itemWithId: { id: string }[]) => {
const idsMapping: Map<string, string> = new Map()
itemWithId.forEach((item) => idsMapping.set(item.id, cuid()))
return idsMapping
}
export const getTypebot = (typebotId: string) =>
sendRequest<{ typebot: Typebot }>({ sendRequest<{ typebot: Typebot }>({
url: `/api/typebots/${typebotId}`, url: `/api/typebots/${typebotId}`,
method: 'GET', method: 'GET',