fix(results): 🐛 Collect prefilled variables in db
This commit is contained in:
@@ -12,7 +12,7 @@ export const SetVariableContent = ({ step }: { step: SetVariableStep }) => {
|
|||||||
<Text color={'gray.500'}>
|
<Text color={'gray.500'}>
|
||||||
{variableName === '' && expression === ''
|
{variableName === '' && expression === ''
|
||||||
? 'Click to edit...'
|
? 'Click to edit...'
|
||||||
: `${variableName} = ${expression}`}
|
: `${variableName} ${expression ? `= ${expression}` : ``}`}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { useRouter } from 'next/router'
|
|||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { useStats } from 'services/analytics'
|
import { useStats } from 'services/analytics'
|
||||||
import { isFreePlan } from 'services/user'
|
import { isFreePlan } from 'services/user'
|
||||||
import { isDefined } from 'utils'
|
|
||||||
import { AnalyticsContent } from './AnalyticsContent'
|
import { AnalyticsContent } from './AnalyticsContent'
|
||||||
import { SubmissionsContent } from './SubmissionContent'
|
import { SubmissionsContent } from './SubmissionContent'
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,6 @@
|
|||||||
"qs": "^6.10.3",
|
"qs": "^6.10.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-frame-component": "^5.2.1",
|
|
||||||
"react-table": "^7.7.0",
|
"react-table": "^7.7.0",
|
||||||
"short-uuid": "^4.2.0",
|
"short-uuid": "^4.2.0",
|
||||||
"slate": "^0.72.8",
|
"slate": "^0.72.8",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
return res
|
return res
|
||||||
.status(400)
|
.status(400)
|
||||||
.send({ message: "User didn't accepted required scopes" })
|
.send({ message: "User didn't accepted required scopes" })
|
||||||
|
// console.log(tokens)
|
||||||
const { encryptedData, iv } = encrypt(tokens)
|
const { encryptedData, iv } = encrypt(tokens)
|
||||||
const credentials = {
|
const credentials = {
|
||||||
name: email,
|
name: email,
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ const createCredentials = () => {
|
|||||||
expiry_date: 1642441058842,
|
expiry_date: 1642441058842,
|
||||||
access_token:
|
access_token:
|
||||||
'ya29.A0ARrdaM--PV_87ebjywDJpXKb77NBFJl16meVUapYdfNv6W6ZzqqC47fNaPaRjbDbOIIcp6f49cMaX5ndK9TAFnKwlVqz3nrK9nLKqgyDIhYsIq47smcAIZkK56SWPx3X3DwAFqRu2UPojpd2upWwo-3uJrod',
|
'ya29.A0ARrdaM--PV_87ebjywDJpXKb77NBFJl16meVUapYdfNv6W6ZzqqC47fNaPaRjbDbOIIcp6f49cMaX5ndK9TAFnKwlVqz3nrK9nLKqgyDIhYsIq47smcAIZkK56SWPx3X3DwAFqRu2UPojpd2upWwo-3uJrod',
|
||||||
// This token is linked to a mock Google account (typebot.test.user@gmail.com)
|
// This token is linked to a test Google account (typebot.test.user@gmail.com)
|
||||||
refresh_token:
|
refresh_token:
|
||||||
'1//03NRE9V8T-aayCgYIARAAGAMSNwF-L9Ir6zVzF-wm30psz0lbDJj5Y9OgqTO0cvBISODMW4QTR0VK40BLnOQgcHCHkb9c769TAhQ',
|
'1//039xWRt8YaYa3CgYIARAAGAMSNwF-L9Iru9FyuTrDSa7lkSceggPho83kJt2J29G69iEhT1C6XV1vmo6bQS9puL_R2t8FIwR3gek',
|
||||||
})
|
})
|
||||||
return prisma.credentials.createMany({
|
return prisma.credentials.createMany({
|
||||||
data: [
|
data: [
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import shortId from 'short-uuid'
|
|||||||
import { HStack, Text } from '@chakra-ui/react'
|
import { HStack, Text } from '@chakra-ui/react'
|
||||||
import { CalendarIcon, CodeIcon } from 'assets/icons'
|
import { CalendarIcon, CodeIcon } from 'assets/icons'
|
||||||
import { StepIcon } from 'components/editor/StepsSideBar/StepIcon'
|
import { StepIcon } from 'components/editor/StepsSideBar/StepIcon'
|
||||||
import { isInputStep, sendRequest } from 'utils'
|
import { byId, isInputStep, sendRequest } from 'utils'
|
||||||
import { isDefined } from '@udecode/plate-common'
|
import { isDefined } from '@udecode/plate-common'
|
||||||
|
|
||||||
export const parseTypebotToPublicTypebot = (
|
export const parseTypebotToPublicTypebot = (
|
||||||
@@ -48,12 +48,14 @@ export const updatePublishedTypebot = async (
|
|||||||
body: typebot,
|
body: typebot,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const parseSubmissionsColumns = (
|
type HeaderCell = {
|
||||||
typebot: PublicTypebot
|
|
||||||
): {
|
|
||||||
Header: JSX.Element
|
Header: JSX.Element
|
||||||
accessor: string
|
accessor: string
|
||||||
}[] => {
|
}
|
||||||
|
export const parseSubmissionsColumns = (
|
||||||
|
typebot: PublicTypebot
|
||||||
|
): HeaderCell[] => {
|
||||||
|
const parsedBlocks = parseBlocksHeaders(typebot)
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
Header: (
|
Header: (
|
||||||
@@ -64,51 +66,58 @@ export const parseSubmissionsColumns = (
|
|||||||
),
|
),
|
||||||
accessor: 'createdAt',
|
accessor: 'createdAt',
|
||||||
},
|
},
|
||||||
...parseBlocksHeaders(typebot),
|
...parsedBlocks,
|
||||||
...parseVariablesHeaders(typebot),
|
...parseVariablesHeaders(typebot, parsedBlocks),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseBlocksHeaders = (typebot: PublicTypebot) =>
|
const parseBlocksHeaders = (typebot: PublicTypebot) =>
|
||||||
typebot.blocks
|
typebot.blocks
|
||||||
.filter((block) => typebot && block.steps.some((step) => isInputStep(step)))
|
.filter((block) => typebot && block.steps.some((step) => isInputStep(step)))
|
||||||
.map((block) => {
|
.reduce<HeaderCell[]>((headers, block) => {
|
||||||
const inputStep = block.steps.find((step) => isInputStep(step))
|
const inputStep = block.steps.find((step) => isInputStep(step))
|
||||||
if (!inputStep || !isInputStep(inputStep)) return
|
if (
|
||||||
return {
|
!inputStep ||
|
||||||
Header: (
|
!isInputStep(inputStep) ||
|
||||||
<HStack
|
headers.find((h) => h.accessor === inputStep.options.variableId)
|
||||||
minW={
|
|
||||||
'isLong' in inputStep.options && inputStep.options.isLong
|
|
||||||
? '400px'
|
|
||||||
: '150px'
|
|
||||||
}
|
|
||||||
maxW="500px"
|
|
||||||
>
|
|
||||||
<StepIcon type={inputStep.type} />
|
|
||||||
<Text>{block.title}</Text>
|
|
||||||
</HStack>
|
|
||||||
),
|
|
||||||
accessor: block.id,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(isDefined)
|
|
||||||
|
|
||||||
const parseVariablesHeaders = (typebot: PublicTypebot) =>
|
|
||||||
typebot.variables
|
|
||||||
.map((v) => {
|
|
||||||
const isVariableInInputStep = isDefined(
|
|
||||||
typebot.blocks.find((b) => {
|
|
||||||
const inputStep = b.steps.find((step) => isInputStep(step))
|
|
||||||
return (
|
|
||||||
inputStep &&
|
|
||||||
isInputStep(inputStep) &&
|
|
||||||
inputStep.options.variableId === v.id
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
if (isVariableInInputStep) return
|
return headers
|
||||||
return {
|
const matchedVariableName =
|
||||||
|
inputStep.options.variableId &&
|
||||||
|
typebot.variables.find(byId(inputStep.options.variableId))?.name
|
||||||
|
return [
|
||||||
|
...headers,
|
||||||
|
{
|
||||||
|
Header: (
|
||||||
|
<HStack
|
||||||
|
minW={
|
||||||
|
'isLong' in inputStep.options && inputStep.options.isLong
|
||||||
|
? '400px'
|
||||||
|
: '150px'
|
||||||
|
}
|
||||||
|
maxW="500px"
|
||||||
|
>
|
||||||
|
<StepIcon type={inputStep.type} />
|
||||||
|
<Text>{matchedVariableName ?? block.title}</Text>
|
||||||
|
</HStack>
|
||||||
|
),
|
||||||
|
accessor: inputStep.options.variableId ?? block.id,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const parseVariablesHeaders = (
|
||||||
|
typebot: PublicTypebot,
|
||||||
|
parsedBlocks: {
|
||||||
|
Header: JSX.Element
|
||||||
|
accessor: string
|
||||||
|
}[]
|
||||||
|
) =>
|
||||||
|
typebot.variables.reduce<HeaderCell[]>((headers, v) => {
|
||||||
|
if (parsedBlocks.find((b) => b.accessor === v.id)) return headers
|
||||||
|
return [
|
||||||
|
...headers,
|
||||||
|
{
|
||||||
Header: (
|
Header: (
|
||||||
<HStack minW={'150px'} maxW="500px">
|
<HStack minW={'150px'} maxW="500px">
|
||||||
<CodeIcon />
|
<CodeIcon />
|
||||||
@@ -116,6 +125,6 @@ const parseVariablesHeaders = (typebot: PublicTypebot) =>
|
|||||||
</HStack>
|
</HStack>
|
||||||
),
|
),
|
||||||
accessor: v.id,
|
accessor: v.id,
|
||||||
}
|
},
|
||||||
})
|
]
|
||||||
.filter(isDefined)
|
}, [])
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Result } from 'models'
|
import { Result, VariableWithValue } from 'models'
|
||||||
import useSWRInfinite from 'swr/infinite'
|
import useSWRInfinite from 'swr/infinite'
|
||||||
import { fetcher } from './utils'
|
import { fetcher } from './utils'
|
||||||
import { stringify } from 'qs'
|
import { stringify } from 'qs'
|
||||||
import { Answer } from 'db'
|
import { Answer } from 'db'
|
||||||
import { sendRequest } from 'utils'
|
import { isDefined, sendRequest } from 'utils'
|
||||||
|
|
||||||
const paginationLimit = 50
|
const paginationLimit = 50
|
||||||
|
|
||||||
@@ -98,8 +98,18 @@ export const parseDateToReadable = (dateStr: string): string => {
|
|||||||
export const convertResultsToTableData = (results?: ResultWithAnswers[]) =>
|
export const convertResultsToTableData = (results?: ResultWithAnswers[]) =>
|
||||||
(results ?? []).map((result) => ({
|
(results ?? []).map((result) => ({
|
||||||
createdAt: parseDateToReadable(result.createdAt),
|
createdAt: parseDateToReadable(result.createdAt),
|
||||||
...result.answers.reduce(
|
...[...result.answers, ...result.prefilledVariables].reduce<{
|
||||||
(o, answer) => ({ ...o, [answer.blockId]: answer.content }),
|
[key: string]: string
|
||||||
{}
|
}>((o, answerOrVariable) => {
|
||||||
),
|
if ('blockId' in answerOrVariable) {
|
||||||
|
const answer = answerOrVariable as Answer
|
||||||
|
return {
|
||||||
|
...o,
|
||||||
|
[answer.variableId ?? answer.blockId]: answer.content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const variable = answerOrVariable as VariableWithValue
|
||||||
|
if (isDefined(o[variable.id])) return o
|
||||||
|
return { ...o, [variable.id]: variable.value }
|
||||||
|
}, {}),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { TypebotViewer } from 'bot-engine'
|
import { TypebotViewer } from 'bot-engine'
|
||||||
import { Answer, PublicTypebot } from 'models'
|
import { Answer, PublicTypebot, VariableWithValue } from 'models'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { upsertAnswer } from 'services/answer'
|
import { upsertAnswer } from 'services/answer'
|
||||||
import { SEO } from '../components/Seo'
|
import { SEO } from '../components/Seo'
|
||||||
@@ -19,21 +19,25 @@ export const TypebotPage = ({
|
|||||||
isIE,
|
isIE,
|
||||||
url,
|
url,
|
||||||
}: TypebotPageProps & { typebot: PublicTypebot }) => {
|
}: TypebotPageProps & { typebot: PublicTypebot }) => {
|
||||||
|
const [showTypebot, setShowTypebot] = useState(false)
|
||||||
const [error, setError] = useState<Error | undefined>(
|
const [error, setError] = useState<Error | undefined>(
|
||||||
isIE ? new Error('Internet explorer is not supported') : undefined
|
isIE ? new Error('Internet explorer is not supported') : undefined
|
||||||
)
|
)
|
||||||
const [resultId, setResultId] = useState<string | undefined>()
|
const [resultId, setResultId] = useState<string | undefined>()
|
||||||
|
|
||||||
|
// Workaround for react-frame-component bug (https://github.com/ryanseddon/react-frame-component/pull/207)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initializeResult()
|
setShowTypebot(true)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const initializeResult = async () => {
|
const initializeResult = async (variables: VariableWithValue[]) => {
|
||||||
const resultIdFromSession = sessionStorage.getItem(sessionStorageKey)
|
const resultIdFromSession = sessionStorage.getItem(sessionStorageKey)
|
||||||
if (resultIdFromSession) setResultId(resultIdFromSession)
|
if (resultIdFromSession) setResultId(resultIdFromSession)
|
||||||
else {
|
else {
|
||||||
const { error, data: result } = await createResult(typebot.typebotId)
|
const { error, data: result } = await createResult(
|
||||||
|
typebot.typebotId,
|
||||||
|
variables
|
||||||
|
)
|
||||||
if (error) setError(error)
|
if (error) setError(error)
|
||||||
if (result) {
|
if (result) {
|
||||||
setResultId(result.id)
|
setResultId(result.id)
|
||||||
@@ -60,11 +64,12 @@ export const TypebotPage = ({
|
|||||||
return (
|
return (
|
||||||
<div style={{ height: '100vh' }}>
|
<div style={{ height: '100vh' }}>
|
||||||
<SEO url={url} chatbotName={typebot.name} />
|
<SEO url={url} chatbotName={typebot.name} />
|
||||||
{resultId && (
|
{showTypebot && (
|
||||||
<TypebotViewer
|
<TypebotViewer
|
||||||
typebot={typebot}
|
typebot={typebot}
|
||||||
onNewAnswer={handleNewAnswer}
|
onNewAnswer={handleNewAnswer}
|
||||||
onCompleted={handleCompleted}
|
onCompleted={handleCompleted}
|
||||||
|
onVariablesPrefilled={initializeResult}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import { withSentry } from '@sentry/nextjs'
|
import { withSentry } from '@sentry/nextjs'
|
||||||
import prisma from 'libs/prisma'
|
import prisma from 'libs/prisma'
|
||||||
|
import { VariableWithValue } from 'models'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { methodNotAllowed } from 'utils'
|
import { methodNotAllowed } from 'utils'
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const { typebotId } = JSON.parse(req.body) as { typebotId: string }
|
const resultData = JSON.parse(req.body) as {
|
||||||
|
typebotId: string
|
||||||
|
prefilledVariables: VariableWithValue[]
|
||||||
|
}
|
||||||
const result = await prisma.result.create({
|
const result = await prisma.result.create({
|
||||||
data: { typebotId, isCompleted: false },
|
data: { ...resultData, isCompleted: false },
|
||||||
})
|
})
|
||||||
return res.send(result)
|
return res.send(result)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { withSentry } from '@sentry/nextjs'
|
import { withSentry } from '@sentry/nextjs'
|
||||||
import prisma from 'libs/prisma'
|
import prisma from 'libs/prisma'
|
||||||
import { Result } from 'models'
|
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { methodNotAllowed } from 'utils'
|
import { methodNotAllowed } from 'utils'
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
if (req.method === 'PATCH') {
|
if (req.method === 'PATCH') {
|
||||||
const data = JSON.parse(req.body) as Result
|
const data = JSON.parse(req.body) as { isCompleted: true }
|
||||||
const id = req.query.id.toString()
|
const id = req.query.id.toString()
|
||||||
const result = await prisma.result.update({
|
const result = await prisma.result.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import { Result } from 'db'
|
import { Result } from 'db'
|
||||||
|
import { VariableWithValue } from 'models'
|
||||||
import { sendRequest } from 'utils'
|
import { sendRequest } from 'utils'
|
||||||
|
|
||||||
export const createResult = async (typebotId: string) => {
|
export const createResult = async (
|
||||||
|
typebotId: string,
|
||||||
|
prefilledVariables: VariableWithValue[]
|
||||||
|
) => {
|
||||||
return sendRequest<Result>({
|
return sendRequest<Result>({
|
||||||
url: `/api/results`,
|
url: `/api/results`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: { typebotId },
|
body: { typebotId, prefilledVariables },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,18 @@ export const ChatStep = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { addAnswer } = useAnswers()
|
const { addAnswer } = useAnswers()
|
||||||
|
|
||||||
const handleInputSubmit = (content: string, isRetry: boolean) => {
|
const handleInputSubmit = (
|
||||||
if (!isRetry) addAnswer({ stepId: step.id, blockId: step.blockId, content })
|
content: string,
|
||||||
|
isRetry: boolean,
|
||||||
|
variableId?: string
|
||||||
|
) => {
|
||||||
|
if (!isRetry)
|
||||||
|
addAnswer({
|
||||||
|
stepId: step.id,
|
||||||
|
blockId: step.blockId,
|
||||||
|
content,
|
||||||
|
variableId: variableId ?? null,
|
||||||
|
})
|
||||||
onTransitionEnd(content, isRetry)
|
onTransitionEnd(content, isRetry)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +48,7 @@ const InputChatStep = ({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
}: {
|
}: {
|
||||||
step: InputStep
|
step: InputStep
|
||||||
onSubmit: (value: string, isRetry: boolean) => void
|
onSubmit: (value: string, isRetry: boolean, variableId?: string) => void
|
||||||
}) => {
|
}) => {
|
||||||
const { typebot } = useTypebot()
|
const { typebot } = useTypebot()
|
||||||
const { addNewAvatarOffset } = useHostAvatars()
|
const { addNewAvatarOffset } = useHostAvatars()
|
||||||
@@ -54,7 +64,7 @@ const InputChatStep = ({
|
|||||||
|
|
||||||
const handleSubmit = (value: string) => {
|
const handleSubmit = (value: string) => {
|
||||||
setAnswer(value)
|
setAnswer(value)
|
||||||
onSubmit(value, !isInputValid(value, step.type))
|
onSubmit(value, !isInputValid(value, step.type), step.options.variableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (answer) {
|
if (answer) {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { useFrame } from 'react-frame-component'
|
|||||||
import { setCssVariablesValue } from '../services/theme'
|
import { setCssVariablesValue } from '../services/theme'
|
||||||
import { useAnswers } from '../contexts/AnswersContext'
|
import { useAnswers } from '../contexts/AnswersContext'
|
||||||
import { deepEqual } from 'fast-equals'
|
import { deepEqual } from 'fast-equals'
|
||||||
import { Answer, Edge, PublicBlock, Theme } from 'models'
|
import { Answer, Edge, PublicBlock, Theme, VariableWithValue } from 'models'
|
||||||
import { byId } from 'utils'
|
import { byId, isNotDefined } from 'utils'
|
||||||
import { animateScroll as scroll } from 'react-scroll'
|
import { animateScroll as scroll } from 'react-scroll'
|
||||||
import { useTypebot } from 'contexts/TypebotContext'
|
import { useTypebot } from 'contexts/TypebotContext'
|
||||||
|
|
||||||
@@ -15,12 +15,14 @@ type Props = {
|
|||||||
onNewBlockVisible: (edge: Edge) => void
|
onNewBlockVisible: (edge: Edge) => void
|
||||||
onNewAnswer: (answer: Answer) => void
|
onNewAnswer: (answer: Answer) => void
|
||||||
onCompleted: () => void
|
onCompleted: () => void
|
||||||
|
onVariablesPrefilled?: (prefilledVariables: VariableWithValue[]) => void
|
||||||
}
|
}
|
||||||
export const ConversationContainer = ({
|
export const ConversationContainer = ({
|
||||||
theme,
|
theme,
|
||||||
onNewBlockVisible,
|
onNewBlockVisible,
|
||||||
onNewAnswer,
|
onNewAnswer,
|
||||||
onCompleted,
|
onCompleted,
|
||||||
|
onVariablesPrefilled,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { typebot, updateVariableValue } = useTypebot()
|
const { typebot, updateVariableValue } = useTypebot()
|
||||||
const { document: frameDocument } = useFrame()
|
const { document: frameDocument } = useFrame()
|
||||||
@@ -48,19 +50,24 @@ export const ConversationContainer = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
injectUrlParamsIntoVariables()
|
const prefilledVariables = injectUrlParamsIntoVariables()
|
||||||
|
if (onVariablesPrefilled) onVariablesPrefilled(prefilledVariables)
|
||||||
displayNextBlock(typebot.blocks[0].steps[0].outgoingEdgeId)
|
displayNextBlock(typebot.blocks[0].steps[0].outgoingEdgeId)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const injectUrlParamsIntoVariables = () => {
|
const injectUrlParamsIntoVariables = () => {
|
||||||
const urlParams = new URLSearchParams(location.search)
|
const urlParams = new URLSearchParams(location.search)
|
||||||
|
const prefilledVariables: VariableWithValue[] = []
|
||||||
urlParams.forEach((value, key) => {
|
urlParams.forEach((value, key) => {
|
||||||
const matchingVariable = typebot.variables.find(
|
const matchingVariable = typebot.variables.find(
|
||||||
(v) => v.name.toLowerCase() === key.toLowerCase()
|
(v) => v.name.toLowerCase() === key.toLowerCase()
|
||||||
)
|
)
|
||||||
if (matchingVariable) updateVariableValue(matchingVariable?.id, value)
|
if (isNotDefined(matchingVariable)) return
|
||||||
|
updateVariableValue(matchingVariable?.id, value)
|
||||||
|
prefilledVariables.push({ ...matchingVariable, value })
|
||||||
})
|
})
|
||||||
|
return prefilledVariables
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -10,7 +10,13 @@ import phoneNumberInputStyle from 'react-phone-number-input/style.css'
|
|||||||
import phoneSyle from '../assets/phone.css'
|
import phoneSyle from '../assets/phone.css'
|
||||||
import { ConversationContainer } from './ConversationContainer'
|
import { ConversationContainer } from './ConversationContainer'
|
||||||
import { AnswersContext } from '../contexts/AnswersContext'
|
import { AnswersContext } from '../contexts/AnswersContext'
|
||||||
import { Answer, BackgroundType, Edge, PublicTypebot } from 'models'
|
import {
|
||||||
|
Answer,
|
||||||
|
BackgroundType,
|
||||||
|
Edge,
|
||||||
|
PublicTypebot,
|
||||||
|
VariableWithValue,
|
||||||
|
} from 'models'
|
||||||
|
|
||||||
export type TypebotViewerProps = {
|
export type TypebotViewerProps = {
|
||||||
typebot: PublicTypebot
|
typebot: PublicTypebot
|
||||||
@@ -19,6 +25,7 @@ export type TypebotViewerProps = {
|
|||||||
onNewBlockVisible?: (edge: Edge) => void
|
onNewBlockVisible?: (edge: Edge) => void
|
||||||
onNewAnswer?: (answer: Answer) => void
|
onNewAnswer?: (answer: Answer) => void
|
||||||
onCompleted?: () => void
|
onCompleted?: () => void
|
||||||
|
onVariablesPrefilled?: (prefilledVariables: VariableWithValue[]) => void
|
||||||
}
|
}
|
||||||
export const TypebotViewer = ({
|
export const TypebotViewer = ({
|
||||||
typebot,
|
typebot,
|
||||||
@@ -27,6 +34,7 @@ export const TypebotViewer = ({
|
|||||||
onNewBlockVisible,
|
onNewBlockVisible,
|
||||||
onNewAnswer,
|
onNewAnswer,
|
||||||
onCompleted,
|
onCompleted,
|
||||||
|
onVariablesPrefilled,
|
||||||
}: TypebotViewerProps) => {
|
}: TypebotViewerProps) => {
|
||||||
const containerBgColor = useMemo(
|
const containerBgColor = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -84,6 +92,7 @@ export const TypebotViewer = ({
|
|||||||
onNewBlockVisible={handleNewBlockVisible}
|
onNewBlockVisible={handleNewBlockVisible}
|
||||||
onNewAnswer={handleNewAnswer}
|
onNewAnswer={handleNewAnswer}
|
||||||
onCompleted={handleCompleted}
|
onCompleted={handleCompleted}
|
||||||
|
onVariablesPrefilled={onVariablesPrefilled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{typebot.settings.general.isBrandingEnabled && (
|
{typebot.settings.general.isBrandingEnabled && (
|
||||||
|
|||||||
@@ -125,21 +125,23 @@ model PublicTypebot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Result {
|
model Result {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
typebotId String
|
typebotId String
|
||||||
typebot Typebot @relation(fields: [typebotId], references: [id], onDelete: Cascade)
|
typebot Typebot @relation(fields: [typebotId], references: [id], onDelete: Cascade)
|
||||||
answers Answer[]
|
answers Answer[]
|
||||||
isCompleted Boolean
|
prefilledVariables Json[]
|
||||||
|
isCompleted Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
model Answer {
|
model Answer {
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
resultId String
|
resultId String
|
||||||
result Result @relation(fields: [resultId], references: [id], onDelete: Cascade)
|
result Result @relation(fields: [resultId], references: [id], onDelete: Cascade)
|
||||||
stepId String
|
stepId String
|
||||||
blockId String
|
blockId String
|
||||||
content String
|
variableId String?
|
||||||
|
content String
|
||||||
|
|
||||||
@@unique([resultId, blockId, stepId])
|
@@unique([resultId, blockId, stepId])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
import { Result as ResultFromPrisma } from 'db'
|
import { Result as ResultFromPrisma } from 'db'
|
||||||
|
import { VariableWithValue } from '.'
|
||||||
|
|
||||||
export type Result = Omit<ResultFromPrisma, 'createdAt'> & { createdAt: string }
|
export type Result = Omit<
|
||||||
|
ResultFromPrisma,
|
||||||
|
'createdAt' | 'prefilledVariables'
|
||||||
|
> & { createdAt: string; prefilledVariables: VariableWithValue[] }
|
||||||
|
|||||||
@@ -3,3 +3,7 @@ export type Variable = {
|
|||||||
name: string
|
name: string
|
||||||
value?: string
|
value?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VariableWithValue = Omit<Variable, 'value'> & {
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user