@ -16,6 +16,7 @@
|
||||
"@trpc/server": "10.40.0",
|
||||
"@typebot.io/bot-engine": "workspace:*",
|
||||
"@typebot.io/nextjs": "workspace:*",
|
||||
"@typebot.io/js": "workspace:*",
|
||||
"@typebot.io/prisma": "workspace:*",
|
||||
"ai": "2.2.14",
|
||||
"bot-engine": "workspace:*",
|
||||
@ -54,7 +55,7 @@
|
||||
"next-runtime-env": "1.6.2",
|
||||
"papaparse": "5.4.1",
|
||||
"superjson": "1.12.4",
|
||||
"typescript": "5.1.6",
|
||||
"zod": "3.21.4"
|
||||
"typescript": "5.2.2",
|
||||
"zod": "3.22.4"
|
||||
}
|
||||
}
|
||||
|
BIN
apps/viewer/public/site-preview.png
Normal file
BIN
apps/viewer/public/site-preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 KiB |
@ -1,10 +1,12 @@
|
||||
import { connect } from '@planetscale/database'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { IntegrationBlockType, SessionState } from '@typebot.io/schemas'
|
||||
import { SessionState } from '@typebot.io/schemas'
|
||||
import { StreamingTextResponse } from 'ai'
|
||||
import { getChatCompletionStream } from '@typebot.io/bot-engine/blocks/integrations/openai/getChatCompletionStream'
|
||||
import OpenAI from 'openai'
|
||||
import { NextResponse } from 'next/dist/server/web/spec-extension/response'
|
||||
import { IntegrationBlockType } from '@typebot.io/schemas/features/blocks/integrations/constants'
|
||||
import { getBlockById } from '@typebot.io/lib/getBlockById'
|
||||
|
||||
export const runtime = 'edge'
|
||||
export const preferredRegion = 'lhr1'
|
||||
@ -55,22 +57,16 @@ export async function POST(req: Request) {
|
||||
const state = (chatSession.rows.at(0) as { state: SessionState } | undefined)
|
||||
?.state
|
||||
|
||||
if (!state)
|
||||
if (!state || !state.currentBlockId)
|
||||
return NextResponse.json(
|
||||
{ message: 'No state found' },
|
||||
{ status: 400, headers: responseHeaders }
|
||||
)
|
||||
|
||||
const group = state.typebotsQueue[0].typebot.groups.find(
|
||||
(group) => group.id === state.currentBlock?.groupId
|
||||
const { group, block } = getBlockById(
|
||||
state.currentBlockId,
|
||||
state.typebotsQueue[0].typebot.groups
|
||||
)
|
||||
const blockIndex =
|
||||
group?.blocks.findIndex(
|
||||
(block) => block.id === state.currentBlock?.blockId
|
||||
) ?? -1
|
||||
|
||||
const block = blockIndex >= 0 ? group?.blocks[blockIndex ?? 0] : null
|
||||
|
||||
if (!block || !group)
|
||||
return NextResponse.json(
|
||||
{ message: 'Current block not found' },
|
||||
@ -79,7 +75,7 @@ export async function POST(req: Request) {
|
||||
|
||||
if (
|
||||
block.type !== IntegrationBlockType.OPEN_AI ||
|
||||
block.options.task !== 'Create chat completion'
|
||||
block.options?.task !== 'Create chat completion'
|
||||
)
|
||||
return NextResponse.json(
|
||||
{ message: 'Current block is not an OpenAI block' },
|
||||
|
@ -1,20 +1,28 @@
|
||||
import { gtmHeadSnippet } from '@/lib/google-tag-manager'
|
||||
import { Metadata } from '@typebot.io/schemas'
|
||||
import Head from 'next/head'
|
||||
import Script from 'next/script'
|
||||
import React from 'react'
|
||||
import { isNotEmpty } from '@typebot.io/lib'
|
||||
import { getViewerUrl } from '@typebot.io/lib/getViewerUrl'
|
||||
import { Settings } from '@typebot.io/schemas'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
type SEOProps = {
|
||||
url: string
|
||||
typebotName: string
|
||||
metadata: Metadata
|
||||
metadata?: Settings['metadata']
|
||||
}
|
||||
|
||||
export const SEO = ({
|
||||
url,
|
||||
typebotName,
|
||||
metadata: { title, description, favIconUrl, imageUrl, googleTagManagerId },
|
||||
metadata: {
|
||||
title,
|
||||
description,
|
||||
favIconUrl,
|
||||
imageUrl,
|
||||
googleTagManagerId,
|
||||
} = {},
|
||||
}: SEOProps) => (
|
||||
<>
|
||||
<Head key="seo">
|
||||
@ -23,7 +31,7 @@ export const SEO = ({
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href={favIconUrl ?? 'https://viewer.typebot.io/favicon.png'}
|
||||
href={favIconUrl ?? defaultSettings.metadata.favIconUrl(getViewerUrl())}
|
||||
/>
|
||||
<meta name="title" content={title ?? typebotName} />
|
||||
<meta
|
||||
@ -48,7 +56,7 @@ export const SEO = ({
|
||||
<meta
|
||||
property="og:image"
|
||||
itemProp="image"
|
||||
content={imageUrl ?? 'https://bot.typebot.io/site-preview.png'}
|
||||
content={imageUrl ?? defaultSettings.metadata.imageUrl(getViewerUrl())}
|
||||
/>
|
||||
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
@ -63,7 +71,7 @@ export const SEO = ({
|
||||
/>
|
||||
<meta
|
||||
property="twitter:image"
|
||||
content={imageUrl ?? 'https://bot.typebot.io/site-preview.png'}
|
||||
content={imageUrl ?? defaultSettings.metadata.imageUrl(getViewerUrl())}
|
||||
/>
|
||||
</Head>
|
||||
{isNotEmpty(googleTagManagerId) && (
|
||||
|
@ -1,3 +1,5 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
import { TypebotViewer } from 'bot-engine'
|
||||
import {
|
||||
AnswerInput,
|
||||
@ -23,6 +25,7 @@ import {
|
||||
import { upsertAnswerQuery } from '@/features/answers/queries/upsertAnswerQuery'
|
||||
import { createResultQuery } from '@/features/results/queries/createResultQuery'
|
||||
import { updateResultQuery } from '@/features/results/queries/updateResultQuery'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
export type TypebotPageProps = {
|
||||
publishedTypebot: Omit<PublicTypebot, 'createdAt' | 'updatedAt'> & {
|
||||
@ -64,7 +67,7 @@ export const TypebotPageV2 = ({
|
||||
setPredefinedVariables(predefinedVariables)
|
||||
initializeResult().then()
|
||||
if (isDefined(customHeadCode)) injectCustomHeadCode(customHeadCode)
|
||||
const gtmId = publishedTypebot.settings.metadata.googleTagManagerId
|
||||
const gtmId = publishedTypebot.settings.metadata?.googleTagManagerId
|
||||
if (isNotEmpty(gtmId)) document.body.prepend(gtmBodyElement(gtmId))
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
@ -73,7 +76,8 @@ export const TypebotPageV2 = ({
|
||||
const hasQueryParams = asPath.includes('?')
|
||||
if (
|
||||
hasQueryParams &&
|
||||
publishedTypebot.settings.general.isHideQueryParamsEnabled !== false
|
||||
(publishedTypebot.settings.general?.isHideQueryParamsEnabled ??
|
||||
defaultSettings.general.isHideQueryParamsEnabled) !== false
|
||||
)
|
||||
push(asPath.split('?')[0], undefined, { shallow: true })
|
||||
}
|
||||
@ -91,7 +95,8 @@ export const TypebotPageV2 = ({
|
||||
if (data?.result) {
|
||||
setResultId(data.result.id)
|
||||
if (
|
||||
publishedTypebot.settings.general.isNewResultOnRefreshEnabled !== true
|
||||
publishedTypebot.settings.general?.isNewResultOnRefreshEnabled !==
|
||||
true
|
||||
)
|
||||
setResultInSession(data.result.id)
|
||||
}
|
||||
|
@ -2,14 +2,15 @@ import { Standard } from '@typebot.io/nextjs'
|
||||
import { useRouter } from 'next/router'
|
||||
import { SEO } from './Seo'
|
||||
import { Typebot } from '@typebot.io/schemas/features/typebot/typebot'
|
||||
import { BackgroundType } from '@typebot.io/schemas/features/typebot/theme/enums'
|
||||
import { BackgroundType } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
export type TypebotV3PageProps = {
|
||||
url: string
|
||||
name: string
|
||||
publicId: string | null
|
||||
isHideQueryParamsEnabled: boolean | null
|
||||
background: Typebot['theme']['general']['background']
|
||||
background: NonNullable<Typebot['theme']['general']>['background']
|
||||
metadata: Typebot['settings']['metadata']
|
||||
}
|
||||
|
||||
@ -25,7 +26,14 @@ export const TypebotPageV3 = ({
|
||||
|
||||
const clearQueryParamsIfNecessary = () => {
|
||||
const hasQueryParams = asPath.includes('?')
|
||||
if (!hasQueryParams || !(isHideQueryParamsEnabled ?? true)) return
|
||||
if (
|
||||
!hasQueryParams ||
|
||||
!(
|
||||
isHideQueryParamsEnabled ??
|
||||
defaultSettings.general.isHideQueryParamsEnabled
|
||||
)
|
||||
)
|
||||
return
|
||||
push(asPath.split('?')[0], undefined, { shallow: true })
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,7 @@ export const sendMessageV1 = publicProcedure
|
||||
logs,
|
||||
clientSideActions,
|
||||
newSessionState,
|
||||
visitedEdges,
|
||||
} = await startSession({
|
||||
version: 1,
|
||||
startParams,
|
||||
@ -77,6 +78,7 @@ export const sendMessageV1 = publicProcedure
|
||||
input,
|
||||
logs: allLogs,
|
||||
clientSideActions,
|
||||
visitedEdges,
|
||||
})
|
||||
|
||||
return {
|
||||
@ -103,6 +105,7 @@ export const sendMessageV1 = publicProcedure
|
||||
newSessionState,
|
||||
logs,
|
||||
lastMessageNewFormat,
|
||||
visitedEdges,
|
||||
} = await continueBotFlow(message, { version: 1, state: session.state })
|
||||
|
||||
const allLogs = clientLogs ? [...(logs ?? []), ...clientLogs] : logs
|
||||
@ -116,6 +119,7 @@ export const sendMessageV1 = publicProcedure
|
||||
input,
|
||||
logs: allLogs,
|
||||
clientSideActions,
|
||||
visitedEdges,
|
||||
})
|
||||
|
||||
return {
|
||||
|
@ -57,6 +57,7 @@ export const sendMessageV2 = publicProcedure
|
||||
logs,
|
||||
clientSideActions,
|
||||
newSessionState,
|
||||
visitedEdges,
|
||||
} = await startSession({
|
||||
version: 2,
|
||||
startParams,
|
||||
@ -77,6 +78,7 @@ export const sendMessageV2 = publicProcedure
|
||||
input,
|
||||
logs: allLogs,
|
||||
clientSideActions,
|
||||
visitedEdges,
|
||||
})
|
||||
|
||||
return {
|
||||
@ -103,6 +105,7 @@ export const sendMessageV2 = publicProcedure
|
||||
newSessionState,
|
||||
logs,
|
||||
lastMessageNewFormat,
|
||||
visitedEdges,
|
||||
} = await continueBotFlow(message, { version: 2, state: session.state })
|
||||
|
||||
const allLogs = clientLogs ? [...(logs ?? []), ...clientLogs] : logs
|
||||
@ -116,6 +119,7 @@ export const sendMessageV2 = publicProcedure
|
||||
input,
|
||||
logs: allLogs,
|
||||
clientSideActions,
|
||||
visitedEdges,
|
||||
})
|
||||
|
||||
return {
|
||||
|
@ -94,7 +94,7 @@ const updateSessionState = (
|
||||
},
|
||||
}
|
||||
: typebotInQueue
|
||||
),
|
||||
) as SessionState['typebotsQueue'],
|
||||
})
|
||||
|
||||
const updateVariablesInSession = (
|
||||
|
@ -1,17 +1,19 @@
|
||||
import { publicProcedure } from '@/helpers/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import {
|
||||
Block,
|
||||
FileInputBlock,
|
||||
InputBlockType,
|
||||
LogicBlockType,
|
||||
PublicTypebot,
|
||||
TypebotLinkBlock,
|
||||
parseGroups,
|
||||
} from '@typebot.io/schemas'
|
||||
import { byId, isDefined } from '@typebot.io/lib'
|
||||
import { z } from 'zod'
|
||||
import { generatePresignedUrl } from '@typebot.io/lib/s3/deprecated/generatePresignedUrl'
|
||||
import { env } from '@typebot.io/env'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { LogicBlockType } from '@typebot.io/schemas/features/blocks/logic/constants'
|
||||
import { PublicTypebot } from '@typebot.io/prisma'
|
||||
|
||||
export const getUploadUrl = publicProcedure
|
||||
.meta({
|
||||
@ -45,13 +47,20 @@ export const getUploadUrl = publicProcedure
|
||||
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY',
|
||||
})
|
||||
|
||||
const publicTypebot = (await prisma.publicTypebot.findFirst({
|
||||
const publicTypebot = await prisma.publicTypebot.findFirst({
|
||||
where: { typebotId },
|
||||
select: {
|
||||
version: true,
|
||||
groups: true,
|
||||
typebotId: true,
|
||||
},
|
||||
})) as Pick<PublicTypebot, 'groups' | 'typebotId'>
|
||||
})
|
||||
|
||||
if (!publicTypebot)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'Typebot not found',
|
||||
})
|
||||
|
||||
const fileUploadBlock = await getFileUploadBlock(publicTypebot, blockId)
|
||||
|
||||
@ -73,27 +82,32 @@ export const getUploadUrl = publicProcedure
|
||||
})
|
||||
|
||||
const getFileUploadBlock = async (
|
||||
publicTypebot: Pick<PublicTypebot, 'groups' | 'typebotId'>,
|
||||
publicTypebot: Pick<PublicTypebot, 'groups' | 'typebotId' | 'version'>,
|
||||
blockId: string
|
||||
): Promise<FileInputBlock | null> => {
|
||||
const fileUploadBlock = publicTypebot.groups
|
||||
.flatMap((group) => group.blocks)
|
||||
const groups = parseGroups(publicTypebot.groups, {
|
||||
typebotVersion: publicTypebot.version,
|
||||
})
|
||||
const fileUploadBlock = groups
|
||||
.flatMap<Block>((group) => group.blocks)
|
||||
.find(byId(blockId))
|
||||
if (fileUploadBlock?.type === InputBlockType.FILE) return fileUploadBlock
|
||||
const linkedTypebotIds = publicTypebot.groups
|
||||
.flatMap((group) => group.blocks)
|
||||
const linkedTypebotIds = groups
|
||||
.flatMap<Block>((group) => group.blocks)
|
||||
.filter((block) => block.type === LogicBlockType.TYPEBOT_LINK)
|
||||
.flatMap((block) => (block as TypebotLinkBlock).options.typebotId)
|
||||
.flatMap((block) => (block as TypebotLinkBlock).options?.typebotId)
|
||||
.filter(isDefined)
|
||||
const linkedTypebots = (await prisma.publicTypebot.findMany({
|
||||
const linkedTypebots = await prisma.publicTypebot.findMany({
|
||||
where: { typebotId: { in: linkedTypebotIds } },
|
||||
select: {
|
||||
groups: true,
|
||||
},
|
||||
})) as Pick<PublicTypebot, 'groups'>[]
|
||||
const fileUploadBlockFromLinkedTypebots = linkedTypebots
|
||||
.flatMap((typebot) => typebot.groups)
|
||||
.flatMap((group) => group.blocks)
|
||||
})
|
||||
const fileUploadBlockFromLinkedTypebots = parseGroups(
|
||||
linkedTypebots.flatMap((typebot) => typebot.groups),
|
||||
{ typebotVersion: publicTypebot.version }
|
||||
)
|
||||
.flatMap<Block>((group) => group.blocks)
|
||||
.find(byId(blockId))
|
||||
if (fileUploadBlockFromLinkedTypebots?.type === InputBlockType.FILE)
|
||||
return fileUploadBlockFromLinkedTypebots
|
||||
|
@ -3,9 +3,11 @@ import { TRPCError } from '@trpc/server'
|
||||
import { z } from 'zod'
|
||||
import { generatePresignedPostPolicy } from '@typebot.io/lib/s3/generatePresignedPostPolicy'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { InputBlockType, publicTypebotSchema } from '@typebot.io/schemas'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { getSession } from '@typebot.io/bot-engine/queries/getSession'
|
||||
import { parseGroups } from '@typebot.io/schemas'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { getBlockById } from '@typebot.io/lib/getBlockById'
|
||||
|
||||
export const generateUploadUrl = publicProcedure
|
||||
.meta({
|
||||
@ -56,6 +58,7 @@ export const generateUploadUrl = publicProcedure
|
||||
typebotId: filePathProps.typebotId,
|
||||
},
|
||||
select: {
|
||||
version: true,
|
||||
groups: true,
|
||||
typebot: {
|
||||
select: {
|
||||
@ -75,8 +78,9 @@ export const generateUploadUrl = publicProcedure
|
||||
|
||||
const filePath = `public/workspaces/${workspaceId}/typebots/${filePathProps.typebotId}/results/${filePathProps.resultId}/${filePathProps.fileName}`
|
||||
|
||||
const fileUploadBlock = publicTypebotSchema._def.schema.shape.groups
|
||||
.parse(publicTypebot.groups)
|
||||
const fileUploadBlock = parseGroups(publicTypebot.groups, {
|
||||
typebotVersion: publicTypebot.version,
|
||||
})
|
||||
.flatMap((group) => group.blocks)
|
||||
.find((block) => block.id === filePathProps.blockId)
|
||||
|
||||
@ -90,7 +94,7 @@ export const generateUploadUrl = publicProcedure
|
||||
fileType,
|
||||
filePath,
|
||||
maxFileSize:
|
||||
fileUploadBlock.options.sizeLimit ??
|
||||
fileUploadBlock.options?.sizeLimit ??
|
||||
env.NEXT_PUBLIC_BOT_FILE_UPLOAD_MAX_SIZE,
|
||||
})
|
||||
|
||||
@ -118,6 +122,7 @@ export const generateUploadUrl = publicProcedure
|
||||
typebotId,
|
||||
},
|
||||
select: {
|
||||
version: true,
|
||||
groups: true,
|
||||
typebot: {
|
||||
select: {
|
||||
@ -139,10 +144,18 @@ export const generateUploadUrl = publicProcedure
|
||||
|
||||
const filePath = `public/workspaces/${workspaceId}/typebots/${typebotId}/results/${resultId}/${filePathProps.fileName}`
|
||||
|
||||
const fileUploadBlock = publicTypebotSchema._def.schema.shape.groups
|
||||
.parse(publicTypebot.groups)
|
||||
.flatMap((group) => group.blocks)
|
||||
.find((block) => block.id === session.state.currentBlock?.blockId)
|
||||
if (session.state.currentBlockId === undefined)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: "Can't find currentBlockId in session state",
|
||||
})
|
||||
|
||||
const { block: fileUploadBlock } = getBlockById(
|
||||
session.state.currentBlockId,
|
||||
parseGroups(publicTypebot.groups, {
|
||||
typebotVersion: publicTypebot.version,
|
||||
})
|
||||
)
|
||||
|
||||
if (fileUploadBlock?.type !== InputBlockType.FILE)
|
||||
throw new TRPCError({
|
||||
@ -154,8 +167,9 @@ export const generateUploadUrl = publicProcedure
|
||||
fileType,
|
||||
filePath,
|
||||
maxFileSize:
|
||||
fileUploadBlock.options.sizeLimit ??
|
||||
env.NEXT_PUBLIC_BOT_FILE_UPLOAD_MAX_SIZE,
|
||||
fileUploadBlock.options && 'sizeLimit' in fileUploadBlock.options
|
||||
? fileUploadBlock.options.sizeLimit
|
||||
: env.NEXT_PUBLIC_BOT_FILE_UPLOAD_MAX_SIZE,
|
||||
})
|
||||
|
||||
return {
|
||||
|
@ -7,6 +7,8 @@ import { TypebotPageProps, TypebotPageV2 } from '@/components/TypebotPageV2'
|
||||
import { TypebotPageV3, TypebotV3PageProps } from '@/components/TypebotPageV3'
|
||||
import { env } from '@typebot.io/env'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { defaultTheme } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
// Browsers that doesn't support ES modules and/or web components
|
||||
const incompatibleBrowsers = [
|
||||
@ -61,6 +63,7 @@ export const getServerSideProps: GetServerSideProps = async (
|
||||
const publishedTypebot = isMatchingViewerUrl
|
||||
? await getTypebotFromPublicId(context.query.publicId?.toString())
|
||||
: await getTypebotFromCustomDomain(customDomain)
|
||||
|
||||
return {
|
||||
props: {
|
||||
publishedTypebot,
|
||||
@ -106,11 +109,14 @@ const getTypebotFromPublicId = async (publicId?: string) => {
|
||||
? ({
|
||||
name: publishedTypebot.typebot.name,
|
||||
publicId: publishedTypebot.typebot.publicId ?? null,
|
||||
background: publishedTypebot.theme.general.background,
|
||||
background:
|
||||
publishedTypebot.theme.general?.background ??
|
||||
defaultTheme.general.background,
|
||||
isHideQueryParamsEnabled:
|
||||
publishedTypebot.settings.general.isHideQueryParamsEnabled ?? null,
|
||||
metadata: publishedTypebot.settings.metadata,
|
||||
} as Pick<
|
||||
publishedTypebot.settings.general?.isHideQueryParamsEnabled ??
|
||||
defaultSettings.general.isHideQueryParamsEnabled,
|
||||
metadata: publishedTypebot.settings.metadata ?? {},
|
||||
} satisfies Pick<
|
||||
TypebotV3PageProps,
|
||||
| 'name'
|
||||
| 'publicId'
|
||||
@ -148,11 +154,14 @@ const getTypebotFromCustomDomain = async (customDomain: string) => {
|
||||
? ({
|
||||
name: publishedTypebot.typebot.name,
|
||||
publicId: publishedTypebot.typebot.publicId ?? null,
|
||||
background: publishedTypebot.theme.general.background,
|
||||
background:
|
||||
publishedTypebot.theme.general?.background ??
|
||||
defaultTheme.general.background,
|
||||
isHideQueryParamsEnabled:
|
||||
publishedTypebot.settings.general.isHideQueryParamsEnabled ?? null,
|
||||
metadata: publishedTypebot.settings.metadata,
|
||||
} as Pick<
|
||||
publishedTypebot.settings.general?.isHideQueryParamsEnabled ??
|
||||
defaultSettings.general.isHideQueryParamsEnabled,
|
||||
metadata: publishedTypebot.settings.metadata ?? {},
|
||||
} satisfies Pick<
|
||||
TypebotV3PageProps,
|
||||
| 'name'
|
||||
| 'publicId'
|
||||
@ -214,9 +223,14 @@ const App = ({
|
||||
url={props.url}
|
||||
name={publishedTypebot.name}
|
||||
publicId={publishedTypebot.publicId}
|
||||
isHideQueryParamsEnabled={publishedTypebot.isHideQueryParamsEnabled}
|
||||
background={publishedTypebot.background}
|
||||
metadata={publishedTypebot.metadata}
|
||||
isHideQueryParamsEnabled={
|
||||
publishedTypebot.isHideQueryParamsEnabled ??
|
||||
defaultSettings.general.isHideQueryParamsEnabled
|
||||
}
|
||||
background={
|
||||
publishedTypebot.background ?? defaultTheme.general.background
|
||||
}
|
||||
metadata={publishedTypebot.metadata ?? {}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -8,17 +8,19 @@ import {
|
||||
import { hasValue, isDefined } from '@typebot.io/lib'
|
||||
import { GoogleSpreadsheet, GoogleSpreadsheetRow } from 'google-spreadsheet'
|
||||
import {
|
||||
ComparisonOperators,
|
||||
GoogleSheetsAction,
|
||||
GoogleSheetsGetOptions,
|
||||
GoogleSheetsInsertRowOptions,
|
||||
GoogleSheetsUpdateRowOptions,
|
||||
LogicalOperator,
|
||||
} from '@typebot.io/schemas'
|
||||
import Cors from 'cors'
|
||||
import { getAuthenticatedGoogleClient } from '@/lib/google-sheets'
|
||||
import { saveErrorLog } from '@typebot.io/bot-engine/logs/saveErrorLog'
|
||||
import { saveSuccessLog } from '@typebot.io/bot-engine/logs/saveSuccessLog'
|
||||
import { GoogleSheetsAction } from '@typebot.io/schemas/features/blocks/integrations/googleSheets/constants'
|
||||
import {
|
||||
ComparisonOperators,
|
||||
LogicalOperator,
|
||||
} from '@typebot.io/schemas/features/blocks/logic/condition/constants'
|
||||
|
||||
const cors = initMiddleware(Cors())
|
||||
|
||||
@ -43,11 +45,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const getRows = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const sheetId = req.query.sheetId as string
|
||||
const spreadsheetId = req.query.spreadsheetId as string
|
||||
const { resultId, credentialsId, referenceCell, filter, columns } =
|
||||
req.body as GoogleSheetsGetOptions & {
|
||||
resultId?: string
|
||||
columns: string[] | string
|
||||
}
|
||||
const body = req.body as GoogleSheetsGetOptions & {
|
||||
resultId?: string
|
||||
columns: string[] | string
|
||||
}
|
||||
const referenceCell = 'referenceCell' in body ? body.referenceCell : undefined
|
||||
const { resultId, credentialsId, filter, columns } = body
|
||||
|
||||
if (!hasValue(credentialsId)) {
|
||||
badRequest(res)
|
||||
@ -141,11 +144,13 @@ const insertRow = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const updateRow = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const sheetId = req.query.sheetId as string
|
||||
const spreadsheetId = req.query.spreadsheetId as string
|
||||
const { resultId, credentialsId, values, referenceCell } =
|
||||
req.body as GoogleSheetsUpdateRowOptions & {
|
||||
resultId?: string
|
||||
values: { [key: string]: string }
|
||||
}
|
||||
const body = req.body as GoogleSheetsUpdateRowOptions & {
|
||||
resultId?: string
|
||||
values: { [key: string]: string }
|
||||
}
|
||||
const referenceCell = 'referenceCell' in body ? body.referenceCell : undefined
|
||||
const { resultId, credentialsId, values } = body
|
||||
|
||||
if (!hasValue(credentialsId) || !referenceCell) return badRequest(res)
|
||||
const auth = await getAuthenticatedGoogleClient(credentialsId)
|
||||
if (!auth)
|
||||
@ -181,7 +186,7 @@ const matchFilter = (
|
||||
filter: NonNullable<GoogleSheetsGetOptions['filter']>
|
||||
) => {
|
||||
return filter.logicalOperator === LogicalOperator.AND
|
||||
? filter.comparisons.every(
|
||||
? filter.comparisons?.every(
|
||||
(comparison) =>
|
||||
comparison.column &&
|
||||
matchComparison(
|
||||
@ -190,7 +195,7 @@ const matchFilter = (
|
||||
comparison.value
|
||||
)
|
||||
)
|
||||
: filter.comparisons.some(
|
||||
: filter.comparisons?.some(
|
||||
(comparison) =>
|
||||
comparison.column &&
|
||||
matchComparison(
|
||||
|
@ -10,12 +10,13 @@ import Stripe from 'stripe'
|
||||
|
||||
import Cors from 'cors'
|
||||
import {
|
||||
PaymentInputOptions,
|
||||
PaymentInputBlock,
|
||||
StripeCredentials,
|
||||
Variable,
|
||||
} from '@typebot.io/schemas'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { parseVariables } from '@typebot.io/bot-engine/variables/parseVariables'
|
||||
import { defaultPaymentInputOptions } from '@typebot.io/schemas/features/blocks/inputs/payment/constants'
|
||||
|
||||
const cors = initMiddleware(Cors())
|
||||
|
||||
@ -43,11 +44,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { inputOptions, isPreview, variables } = (
|
||||
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
||||
) as {
|
||||
inputOptions: PaymentInputOptions
|
||||
inputOptions: PaymentInputBlock['options']
|
||||
isPreview: boolean
|
||||
variables: Variable[]
|
||||
}
|
||||
if (!inputOptions.credentialsId) return forbidden(res)
|
||||
if (!inputOptions?.credentialsId) return forbidden(res)
|
||||
const stripeKeys = await getStripeInfo(inputOptions.credentialsId)
|
||||
if (!stripeKeys) return forbidden(res)
|
||||
const stripe = new Stripe(
|
||||
@ -56,9 +57,13 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
: stripeKeys.live.secretKey,
|
||||
{ apiVersion: '2022-11-15' }
|
||||
)
|
||||
|
||||
const currency =
|
||||
inputOptions.currency ?? defaultPaymentInputOptions.currency
|
||||
|
||||
const amount = Math.round(
|
||||
Number(parseVariables(variables)(inputOptions.amount)) *
|
||||
(isZeroDecimalCurrency(inputOptions.currency) ? 1 : 100)
|
||||
(isZeroDecimalCurrency(currency) ? 1 : 100)
|
||||
)
|
||||
if (isNaN(amount)) return badRequest(res)
|
||||
// Create a PaymentIntent with the order amount and currency
|
||||
@ -68,7 +73,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
try {
|
||||
const paymentIntent = await stripe.paymentIntents.create({
|
||||
amount,
|
||||
currency: inputOptions.currency,
|
||||
currency,
|
||||
receipt_email: receiptEmail === '' ? undefined : receiptEmail,
|
||||
automatic_payment_methods: {
|
||||
enabled: true,
|
||||
@ -81,10 +86,8 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
isPreview && stripeKeys.test?.publicKey
|
||||
? stripeKeys.test.publicKey
|
||||
: stripeKeys.live.publicKey,
|
||||
amountLabel: `${
|
||||
amount / (isZeroDecimalCurrency(inputOptions.currency) ? 1 : 100)
|
||||
}${
|
||||
currencySymbols[inputOptions.currency] ?? ` ${inputOptions.currency}`
|
||||
amountLabel: `${amount / (isZeroDecimalCurrency(currency) ? 1 : 100)}${
|
||||
currencySymbols[currency] ?? ` ${currency}`
|
||||
}`,
|
||||
})
|
||||
} catch (err) {
|
||||
|
@ -1,30 +1,33 @@
|
||||
import {
|
||||
defaultWebhookAttributes,
|
||||
KeyValue,
|
||||
PublicTypebot,
|
||||
ResultValues,
|
||||
Typebot,
|
||||
Variable,
|
||||
Webhook,
|
||||
WebhookOptions,
|
||||
WebhookResponse,
|
||||
WebhookBlock,
|
||||
Block,
|
||||
} from '@typebot.io/schemas'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import got, { Method, Headers, HTTPError } from 'got'
|
||||
import { byId, omit } from '@typebot.io/lib'
|
||||
import { byId, isEmpty, omit } from '@typebot.io/lib'
|
||||
import { parseAnswers } from '@typebot.io/lib/results'
|
||||
import { initMiddleware, methodNotAllowed, notFound } from '@typebot.io/lib/api'
|
||||
import { stringify } from 'qs'
|
||||
import Cors from 'cors'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { HttpMethod } from '@typebot.io/schemas/features/blocks/integrations/webhook/enums'
|
||||
import { fetchLinkedTypebots } from '@typebot.io/bot-engine/blocks/logic/typebotLink/fetchLinkedTypebots'
|
||||
import { getPreviouslyLinkedTypebots } from '@typebot.io/bot-engine/blocks/logic/typebotLink/getPreviouslyLinkedTypebots'
|
||||
import { parseVariables } from '@typebot.io/bot-engine/variables/parseVariables'
|
||||
import { saveErrorLog } from '@typebot.io/bot-engine/logs/saveErrorLog'
|
||||
import { saveSuccessLog } from '@typebot.io/bot-engine/logs/saveSuccessLog'
|
||||
import { parseSampleResult } from '@typebot.io/bot-engine/blocks/integrations/webhook/parseSampleResult'
|
||||
import {
|
||||
HttpMethod,
|
||||
defaultWebhookAttributes,
|
||||
} from '@typebot.io/schemas/features/blocks/integrations/webhook/constants'
|
||||
import { getBlockById } from '@typebot.io/lib/getBlockById'
|
||||
import { IntegrationBlockType } from '@typebot.io/schemas/features/blocks/integrations/constants'
|
||||
|
||||
const cors = initMiddleware(Cors())
|
||||
|
||||
@ -47,40 +50,36 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
})) as unknown as (Typebot & { webhooks: Webhook[] }) | null
|
||||
if (!typebot) return notFound(res)
|
||||
const block = typebot.groups
|
||||
.flatMap((g) => g.blocks)
|
||||
.find(byId(blockId)) as WebhookBlock
|
||||
.flatMap<Block>((g) => g.blocks)
|
||||
.find(byId(blockId))
|
||||
if (block?.type !== IntegrationBlockType.WEBHOOK)
|
||||
return notFound(res, 'Webhook block not found')
|
||||
const webhookId = 'webhookId' in block ? block.webhookId : undefined
|
||||
const webhook =
|
||||
block.options.webhook ?? typebot.webhooks.find(byId(block.webhookId))
|
||||
block.options?.webhook ??
|
||||
typebot.webhooks.find((w) => {
|
||||
if ('id' in w) return w.id === webhookId
|
||||
return false
|
||||
})
|
||||
if (!webhook)
|
||||
return res
|
||||
.status(404)
|
||||
.send({ statusCode: 404, data: { message: `Couldn't find webhook` } })
|
||||
const preparedWebhook = prepareWebhookAttributes(webhook, block.options)
|
||||
const { group } = getBlockById(blockId, typebot.groups)
|
||||
const result = await executeWebhook(typebot)({
|
||||
webhook: preparedWebhook,
|
||||
webhook,
|
||||
variables,
|
||||
groupId: block.groupId,
|
||||
groupId: group.id,
|
||||
resultValues,
|
||||
resultId,
|
||||
parentTypebotIds,
|
||||
isCustomBody: block.options?.isCustomBody,
|
||||
})
|
||||
return res.status(200).send(result)
|
||||
}
|
||||
return methodNotAllowed(res)
|
||||
}
|
||||
|
||||
const prepareWebhookAttributes = (
|
||||
webhook: Webhook,
|
||||
options: WebhookOptions
|
||||
): Webhook => {
|
||||
if (options.isAdvancedConfig === false) {
|
||||
return { ...webhook, body: '{{state}}', ...defaultWebhookAttributes }
|
||||
} else if (options.isCustomBody === false) {
|
||||
return { ...webhook, body: '{{state}}' }
|
||||
}
|
||||
return webhook
|
||||
}
|
||||
|
||||
const checkIfBodyIsAVariable = (body: string) => /^{{.+}}$/.test(body)
|
||||
|
||||
export const executeWebhook =
|
||||
@ -92,6 +91,7 @@ export const executeWebhook =
|
||||
resultValues,
|
||||
resultId,
|
||||
parentTypebotIds = [],
|
||||
isCustomBody,
|
||||
}: {
|
||||
webhook: Webhook
|
||||
variables: Variable[]
|
||||
@ -99,27 +99,29 @@ export const executeWebhook =
|
||||
resultValues?: ResultValues
|
||||
resultId?: string
|
||||
parentTypebotIds: string[]
|
||||
isCustomBody?: boolean
|
||||
}): Promise<WebhookResponse> => {
|
||||
if (!webhook.url || !webhook.method)
|
||||
if (!webhook.url)
|
||||
return {
|
||||
statusCode: 400,
|
||||
data: { message: `Webhook doesn't have url or method` },
|
||||
}
|
||||
const basicAuth: { username?: string; password?: string } = {}
|
||||
const basicAuthHeaderIdx = webhook.headers.findIndex(
|
||||
(h) =>
|
||||
h.key?.toLowerCase() === 'authorization' &&
|
||||
h.value?.toLowerCase()?.includes('basic')
|
||||
)
|
||||
const basicAuthHeaderIdx =
|
||||
webhook.headers?.findIndex(
|
||||
(h) =>
|
||||
h.key?.toLowerCase() === 'authorization' &&
|
||||
h.value?.toLowerCase()?.includes('basic')
|
||||
) ?? -1
|
||||
const isUsernamePasswordBasicAuth =
|
||||
basicAuthHeaderIdx !== -1 &&
|
||||
webhook.headers[basicAuthHeaderIdx].value?.includes(':')
|
||||
webhook.headers?.[basicAuthHeaderIdx].value?.includes(':')
|
||||
if (isUsernamePasswordBasicAuth) {
|
||||
const [username, password] =
|
||||
webhook.headers[basicAuthHeaderIdx].value?.slice(6).split(':') ?? []
|
||||
webhook.headers?.[basicAuthHeaderIdx].value?.slice(6).split(':') ?? []
|
||||
basicAuth.username = username
|
||||
basicAuth.password = password
|
||||
webhook.headers.splice(basicAuthHeaderIdx, 1)
|
||||
webhook.headers?.splice(basicAuthHeaderIdx, 1)
|
||||
}
|
||||
const headers = convertKeyValueTableToObject(webhook.headers, variables) as
|
||||
| Headers
|
||||
@ -141,6 +143,7 @@ export const executeWebhook =
|
||||
...linkedTypebotsChildren,
|
||||
])({
|
||||
body: webhook.body,
|
||||
isCustomBody,
|
||||
resultValues,
|
||||
groupId,
|
||||
variables,
|
||||
@ -158,8 +161,8 @@ export const executeWebhook =
|
||||
url: parseVariables(variables)(
|
||||
webhook.url + (queryParams !== '' ? `?${queryParams}` : '')
|
||||
),
|
||||
method: webhook.method as Method,
|
||||
headers,
|
||||
method: (webhook.method ?? defaultWebhookAttributes.method) as Method,
|
||||
headers: headers ?? {},
|
||||
...basicAuth,
|
||||
json:
|
||||
!contentType?.includes('x-www-form-urlencoded') && body && isJson
|
||||
@ -229,14 +232,15 @@ const getBodyContent =
|
||||
resultValues,
|
||||
groupId,
|
||||
variables,
|
||||
isCustomBody,
|
||||
}: {
|
||||
body?: string | null
|
||||
resultValues?: ResultValues
|
||||
groupId: string
|
||||
variables: Variable[]
|
||||
isCustomBody?: boolean
|
||||
}): Promise<string | undefined> => {
|
||||
if (!body) return
|
||||
return body === '{{state}}'
|
||||
return isEmpty(body) && isCustomBody !== true
|
||||
? JSON.stringify(
|
||||
resultValues
|
||||
? parseAnswers({
|
||||
@ -260,7 +264,7 @@ const getBodyContent =
|
||||
variables
|
||||
)
|
||||
)
|
||||
: body
|
||||
: body ?? undefined
|
||||
}
|
||||
|
||||
const convertKeyValueTableToObject = (
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
PublicTypebot,
|
||||
ResultValues,
|
||||
SendEmailOptions,
|
||||
SendEmailBlock,
|
||||
SmtpCredentials,
|
||||
} from '@typebot.io/schemas'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
@ -56,12 +56,15 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
fileUrls,
|
||||
} = (
|
||||
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
||||
) as SendEmailOptions & {
|
||||
) as SendEmailBlock['options'] & {
|
||||
resultValues: ResultValues
|
||||
fileUrls?: string
|
||||
}
|
||||
const { name: replyToName } = parseEmailRecipient(replyTo)
|
||||
|
||||
if (!credentialsId)
|
||||
return res.status(404).send({ message: "Couldn't find credentials" })
|
||||
|
||||
const { host, port, isTlsEnabled, username, password, from } =
|
||||
(await getEmailInfo(credentialsId)) ?? {}
|
||||
if (!from)
|
||||
@ -186,9 +189,10 @@ const getEmailBody = async ({
|
||||
}: {
|
||||
typebotId: string
|
||||
resultValues: ResultValues
|
||||
} & Pick<SendEmailOptions, 'isCustomBody' | 'isBodyCode' | 'body'>): Promise<
|
||||
{ html?: string; text?: string } | undefined
|
||||
> => {
|
||||
} & Pick<
|
||||
NonNullable<SendEmailBlock['options']>,
|
||||
'isCustomBody' | 'isBodyCode' | 'body'
|
||||
>): Promise<{ html?: string; text?: string } | undefined> => {
|
||||
if (isCustomBody || (isNotDefined(isCustomBody) && !isEmpty(body)))
|
||||
return {
|
||||
html: isBodyCode ? body : undefined,
|
||||
|
@ -2,7 +2,7 @@ import { authenticateUser } from '@/helpers/authenticateUser'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { Group, WebhookBlock } from '@typebot.io/schemas'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { byId, isWebhookBlock } from '@typebot.io/lib'
|
||||
import { isWebhookBlock } from '@typebot.io/lib'
|
||||
import { methodNotAllowed } from '@typebot.io/lib/api'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
@ -28,7 +28,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
...blocks.map((b) => ({
|
||||
blockId: b.id,
|
||||
name: `${group.title} > ${b.id}`,
|
||||
url: typebot?.webhooks.find(byId(b.webhookId))?.url ?? undefined,
|
||||
url:
|
||||
typebot?.webhooks.find((w) => {
|
||||
if ('id' in w && 'webhookId' in b) return w.id === b.webhookId
|
||||
return false
|
||||
})?.url ?? undefined,
|
||||
})),
|
||||
]
|
||||
}, [])
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { authenticateUser } from '@/helpers/authenticateUser'
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { Group, WebhookBlock } from '@typebot.io/schemas'
|
||||
import { Group } from '@typebot.io/schemas'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { byId, isNotDefined, isWebhookBlock } from '@typebot.io/lib'
|
||||
import { isNotDefined, isWebhookBlock } from '@typebot.io/lib'
|
||||
import { methodNotAllowed } from '@typebot.io/lib/api'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
@ -24,15 +24,19 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
(block) =>
|
||||
isWebhookBlock(block) &&
|
||||
isNotDefined(
|
||||
typebot?.webhooks.find(byId((block as WebhookBlock).webhookId))?.url
|
||||
typebot?.webhooks.find((w) => {
|
||||
if ('id' in w && 'webhookId' in block)
|
||||
return w.id === block.webhookId
|
||||
return false
|
||||
})?.url
|
||||
)
|
||||
)
|
||||
return [
|
||||
...emptyWebhookBlocks,
|
||||
...blocks.map((s) => ({
|
||||
id: s.id,
|
||||
groupId: s.groupId,
|
||||
name: `${group.title} > ${s.id}`,
|
||||
...blocks.map((b) => ({
|
||||
id: b.id,
|
||||
groupId: group.id,
|
||||
name: `${group.title} > ${b.id}`,
|
||||
})),
|
||||
]
|
||||
}, [])
|
||||
|
@ -16,7 +16,7 @@ export const getServerSideProps: GetServerSideProps = async (
|
||||
const publishedTypebot = await getTypebotFromPublicId(
|
||||
context.query.publicId?.toString()
|
||||
)
|
||||
const headCode = publishedTypebot?.settings.metadata.customHeadCode
|
||||
const headCode = publishedTypebot?.settings.metadata?.customHeadCode
|
||||
return {
|
||||
props: {
|
||||
publishedTypebot,
|
||||
|
@ -4,6 +4,7 @@
|
||||
"updatedAt": "2022-03-08T16:07:18.899Z",
|
||||
"name": "My typebot",
|
||||
"folderId": null,
|
||||
"version": "4",
|
||||
"groups": [
|
||||
{
|
||||
"id": "1qQrnsLzRim1LqCrhbj1MW",
|
||||
|
@ -3,6 +3,7 @@
|
||||
"createdAt": "2022-03-08T15:58:49.720Z",
|
||||
"updatedAt": "2022-03-08T16:07:18.899Z",
|
||||
"name": "My typebot",
|
||||
"version": "4",
|
||||
"folderId": null,
|
||||
"groups": [
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
"icon": null,
|
||||
"name": "Another typebot copy",
|
||||
"folderId": null,
|
||||
"version": "4",
|
||||
"groups": [
|
||||
{
|
||||
"id": "clbovazhy000q3b6o716dlfq8",
|
||||
|
@ -4,6 +4,7 @@
|
||||
"updatedAt": "2022-03-23T08:41:30.106Z",
|
||||
"name": "My typebot",
|
||||
"folderId": null,
|
||||
"version": "4",
|
||||
"groups": [
|
||||
{
|
||||
"id": "cl13bgvlk0000t71a4wabccvw",
|
||||
|
@ -5,6 +5,7 @@
|
||||
"icon": null,
|
||||
"name": "My typebot copy",
|
||||
"folderId": null,
|
||||
"version": "4",
|
||||
"groups": [
|
||||
{
|
||||
"id": "clbnrow4e000c3b6oycsv9cu3",
|
||||
|
@ -5,6 +5,7 @@
|
||||
"icon": null,
|
||||
"name": "My typebot",
|
||||
"folderId": null,
|
||||
"version": "4",
|
||||
"groups": [
|
||||
{
|
||||
"id": "cl9ip9u0j0000d71a5d98gwni",
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
deleteWebhooks,
|
||||
importTypebotInDatabase,
|
||||
} from '@typebot.io/lib/playwright/databaseActions'
|
||||
import { HttpMethod } from '@typebot.io/schemas/features/blocks/integrations/webhook/enums'
|
||||
import { HttpMethod } from '@typebot.io/schemas/features/blocks/integrations/webhook/constants'
|
||||
|
||||
test.afterEach(async () => {
|
||||
await deleteWebhooks(['chat-webhook-id'])
|
||||
|
@ -2,10 +2,7 @@ import test, { expect } from '@playwright/test'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { createTypebots } from '@typebot.io/lib/playwright/databaseActions'
|
||||
import { parseDefaultGroupWithBlock } from '@typebot.io/lib/playwright/databaseHelpers'
|
||||
import {
|
||||
defaultChatwootOptions,
|
||||
IntegrationBlockType,
|
||||
} from '@typebot.io/schemas'
|
||||
import { IntegrationBlockType } from '@typebot.io/schemas/features/blocks/integrations/constants'
|
||||
|
||||
const typebotId = createId()
|
||||
|
||||
@ -19,7 +16,6 @@ test('should work as expected', async ({ page }) => {
|
||||
{
|
||||
type: IntegrationBlockType.CHATWOOT,
|
||||
options: {
|
||||
...defaultChatwootOptions,
|
||||
websiteToken: chatwootTestWebsiteToken,
|
||||
},
|
||||
},
|
||||
|
@ -1,18 +1,13 @@
|
||||
import test, { expect } from '@playwright/test'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import {
|
||||
defaultSettings,
|
||||
defaultTextInputOptions,
|
||||
InputBlockType,
|
||||
Metadata,
|
||||
} from '@typebot.io/schemas'
|
||||
import {
|
||||
createTypebots,
|
||||
updateTypebot,
|
||||
} from '@typebot.io/lib/playwright/databaseActions'
|
||||
import { parseDefaultGroupWithBlock } from '@typebot.io/lib/playwright/databaseHelpers'
|
||||
|
||||
const settings = defaultSettings({ isBrandingEnabled: true })
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { Settings } from '@typebot.io/schemas'
|
||||
import { defaultTextInputOptions } from '@typebot.io/schemas/features/blocks/inputs/text/constants'
|
||||
|
||||
test('Result should be overwritten on page refresh', async ({ page }) => {
|
||||
const typebotId = createId()
|
||||
@ -20,9 +15,7 @@ test('Result should be overwritten on page refresh', async ({ page }) => {
|
||||
{
|
||||
id: typebotId,
|
||||
settings: {
|
||||
...settings,
|
||||
general: {
|
||||
...settings.general,
|
||||
rememberUser: {
|
||||
isEnabled: true,
|
||||
storage: 'session',
|
||||
@ -31,7 +24,6 @@ test('Result should be overwritten on page refresh', async ({ page }) => {
|
||||
},
|
||||
...parseDefaultGroupWithBlock({
|
||||
type: InputBlockType.TEXT,
|
||||
options: defaultTextInputOptions,
|
||||
}),
|
||||
},
|
||||
])
|
||||
@ -60,7 +52,6 @@ test.describe('Create result on page refresh enabled', () => {
|
||||
id: typebotId,
|
||||
...parseDefaultGroupWithBlock({
|
||||
type: InputBlockType.TEXT,
|
||||
options: defaultTextInputOptions,
|
||||
}),
|
||||
},
|
||||
])
|
||||
@ -88,7 +79,6 @@ test('Hide query params', async ({ page }) => {
|
||||
id: typebotId,
|
||||
...parseDefaultGroupWithBlock({
|
||||
type: InputBlockType.TEXT,
|
||||
options: defaultTextInputOptions,
|
||||
}),
|
||||
},
|
||||
])
|
||||
@ -98,8 +88,7 @@ test('Hide query params', async ({ page }) => {
|
||||
await updateTypebot({
|
||||
id: typebotId,
|
||||
settings: {
|
||||
...settings,
|
||||
general: { ...settings.general, isHideQueryParamsEnabled: false },
|
||||
general: { isHideQueryParamsEnabled: false },
|
||||
},
|
||||
})
|
||||
await page.goto(`/${typebotId}-public?Name=John`)
|
||||
@ -116,7 +105,6 @@ test('Show close message', async ({ page }) => {
|
||||
id: typebotId,
|
||||
...parseDefaultGroupWithBlock({
|
||||
type: InputBlockType.TEXT,
|
||||
options: defaultTextInputOptions,
|
||||
}),
|
||||
isClosed: true,
|
||||
},
|
||||
@ -127,7 +115,7 @@ test('Show close message', async ({ page }) => {
|
||||
|
||||
test('Should correctly parse metadata', async ({ page }) => {
|
||||
const typebotId = createId()
|
||||
const customMetadata: Metadata = {
|
||||
const customMetadata: Settings['metadata'] = {
|
||||
description: 'My custom description',
|
||||
title: 'Custom title',
|
||||
favIconUrl: 'https://www.baptistearno.com/favicon.png',
|
||||
@ -138,12 +126,10 @@ test('Should correctly parse metadata', async ({ page }) => {
|
||||
{
|
||||
id: typebotId,
|
||||
settings: {
|
||||
...settings,
|
||||
metadata: customMetadata,
|
||||
},
|
||||
...parseDefaultGroupWithBlock({
|
||||
type: InputBlockType.TEXT,
|
||||
options: defaultTextInputOptions,
|
||||
}),
|
||||
},
|
||||
])
|
||||
|
@ -1,11 +1,11 @@
|
||||
import test, { expect } from '@playwright/test'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { HttpMethod } from '@typebot.io/schemas/features/blocks/integrations/webhook/enums'
|
||||
import {
|
||||
createWebhook,
|
||||
importTypebotInDatabase,
|
||||
} from '@typebot.io/lib/playwright/databaseActions'
|
||||
import { getTestAsset } from '@/test/utils/playwright'
|
||||
import { HttpMethod } from '@typebot.io/schemas/features/blocks/integrations/webhook/constants'
|
||||
|
||||
const typebotId = createId()
|
||||
|
||||
|
Reference in New Issue
Block a user