Skip to content

Commit

Permalink
fix(results): 🐛 Collect prefilled variables in db
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Feb 17, 2022
1 parent 0336bc2 commit aaf78e8
Show file tree
Hide file tree
Showing 19 changed files with 454 additions and 507 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const SetVariableContent = ({ step }: { step: SetVariableStep }) => {
<Text color={'gray.500'}>
{variableName === '' && expression === ''
? 'Click to edit...'
: `${variableName} = ${expression}`}
: `${variableName} ${expression ? `= ${expression}` : ``}`}
</Text>
)
}
1 change: 0 additions & 1 deletion apps/builder/layouts/results/ResultsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { useRouter } from 'next/router'
import React, { useMemo } from 'react'
import { useStats } from 'services/analytics'
import { isFreePlan } from 'services/user'
import { isDefined } from 'utils'
import { AnalyticsContent } from './AnalyticsContent'
import { SubmissionsContent } from './SubmissionContent'

Expand Down
1 change: 0 additions & 1 deletion apps/builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
"qs": "^6.10.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-frame-component": "^5.2.1",
"react-table": "^7.7.0",
"short-uuid": "^4.2.0",
"slate": "^0.72.8",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
return res
.status(400)
.send({ message: "User didn't accepted required scopes" })
// console.log(tokens)
const { encryptedData, iv } = encrypt(tokens)
const credentials = {
name: email,
Expand Down
4 changes: 2 additions & 2 deletions apps/builder/playwright/services/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ const createCredentials = () => {
expiry_date: 1642441058842,
access_token:
'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:
'1//03NRE9V8T-aayCgYIARAAGAMSNwF-L9Ir6zVzF-wm30psz0lbDJj5Y9OgqTO0cvBISODMW4QTR0VK40BLnOQgcHCHkb9c769TAhQ',
'1//039xWRt8YaYa3CgYIARAAGAMSNwF-L9Iru9FyuTrDSa7lkSceggPho83kJt2J29G69iEhT1C6XV1vmo6bQS9puL_R2t8FIwR3gek',
})
return prisma.credentials.createMany({
data: [
Expand Down
99 changes: 54 additions & 45 deletions apps/builder/services/publicTypebot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import shortId from 'short-uuid'
import { HStack, Text } from '@chakra-ui/react'
import { CalendarIcon, CodeIcon } from 'assets/icons'
import { StepIcon } from 'components/editor/StepsSideBar/StepIcon'
import { isInputStep, sendRequest } from 'utils'
import { byId, isInputStep, sendRequest } from 'utils'
import { isDefined } from '@udecode/plate-common'

export const parseTypebotToPublicTypebot = (
Expand Down Expand Up @@ -48,12 +48,14 @@ export const updatePublishedTypebot = async (
body: typebot,
})

export const parseSubmissionsColumns = (
typebot: PublicTypebot
): {
type HeaderCell = {
Header: JSX.Element
accessor: string
}[] => {
}
export const parseSubmissionsColumns = (
typebot: PublicTypebot
): HeaderCell[] => {
const parsedBlocks = parseBlocksHeaders(typebot)
return [
{
Header: (
Expand All @@ -64,58 +66,65 @@ export const parseSubmissionsColumns = (
),
accessor: 'createdAt',
},
...parseBlocksHeaders(typebot),
...parseVariablesHeaders(typebot),
...parsedBlocks,
...parseVariablesHeaders(typebot, parsedBlocks),
]
}

const parseBlocksHeaders = (typebot: PublicTypebot) =>
typebot.blocks
.filter((block) => typebot && block.steps.some((step) => isInputStep(step)))
.map((block) => {
.reduce<HeaderCell[]>((headers, block) => {
const inputStep = block.steps.find((step) => isInputStep(step))
if (!inputStep || !isInputStep(inputStep)) return
return {
Header: (
<HStack
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 (
!inputStep ||
!isInputStep(inputStep) ||
headers.find((h) => h.accessor === inputStep.options.variableId)
)
if (isVariableInInputStep) return
return {
return headers
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: (
<HStack minW={'150px'} maxW="500px">
<CodeIcon />
<Text>{v.name}</Text>
</HStack>
),
accessor: v.id,
}
})
.filter(isDefined)
},
]
}, [])
22 changes: 16 additions & 6 deletions apps/builder/services/results.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Result } from 'models'
import { Result, VariableWithValue } from 'models'
import useSWRInfinite from 'swr/infinite'
import { fetcher } from './utils'
import { stringify } from 'qs'
import { Answer } from 'db'
import { sendRequest } from 'utils'
import { isDefined, sendRequest } from 'utils'

const paginationLimit = 50

Expand Down Expand Up @@ -98,8 +98,18 @@ export const parseDateToReadable = (dateStr: string): string => {
export const convertResultsToTableData = (results?: ResultWithAnswers[]) =>
(results ?? []).map((result) => ({
createdAt: parseDateToReadable(result.createdAt),
...result.answers.reduce(
(o, answer) => ({ ...o, [answer.blockId]: answer.content }),
{}
),
...[...result.answers, ...result.prefilledVariables].reduce<{
[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 }
}, {}),
}))
17 changes: 11 additions & 6 deletions apps/viewer/layouts/TypebotPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TypebotViewer } from 'bot-engine'
import { Answer, PublicTypebot } from 'models'
import { Answer, PublicTypebot, VariableWithValue } from 'models'
import React, { useEffect, useState } from 'react'
import { upsertAnswer } from 'services/answer'
import { SEO } from '../components/Seo'
Expand All @@ -19,21 +19,25 @@ export const TypebotPage = ({
isIE,
url,
}: TypebotPageProps & { typebot: PublicTypebot }) => {
const [showTypebot, setShowTypebot] = useState(false)
const [error, setError] = useState<Error | undefined>(
isIE ? new Error('Internet explorer is not supported') : undefined
)
const [resultId, setResultId] = useState<string | undefined>()

// Workaround for react-frame-component bug (https://github.com/ryanseddon/react-frame-component/pull/207)
useEffect(() => {
initializeResult()
// eslint-disable-next-line react-hooks/exhaustive-deps
setShowTypebot(true)
}, [])

const initializeResult = async () => {
const initializeResult = async (variables: VariableWithValue[]) => {
const resultIdFromSession = sessionStorage.getItem(sessionStorageKey)
if (resultIdFromSession) setResultId(resultIdFromSession)
else {
const { error, data: result } = await createResult(typebot.typebotId)
const { error, data: result } = await createResult(
typebot.typebotId,
variables
)
if (error) setError(error)
if (result) {
setResultId(result.id)
Expand All @@ -60,11 +64,12 @@ export const TypebotPage = ({
return (
<div style={{ height: '100vh' }}>
<SEO url={url} chatbotName={typebot.name} />
{resultId && (
{showTypebot && (
<TypebotViewer
typebot={typebot}
onNewAnswer={handleNewAnswer}
onCompleted={handleCompleted}
onVariablesPrefilled={initializeResult}
/>
)}
</div>
Expand Down
File renamed without changes.
8 changes: 6 additions & 2 deletions apps/viewer/pages/api/results.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { withSentry } from '@sentry/nextjs'
import prisma from 'libs/prisma'
import { VariableWithValue } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import { methodNotAllowed } from 'utils'

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
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({
data: { typebotId, isCompleted: false },
data: { ...resultData, isCompleted: false },
})
return res.send(result)
}
Expand Down
3 changes: 1 addition & 2 deletions apps/viewer/pages/api/results/[id].ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { withSentry } from '@sentry/nextjs'
import prisma from 'libs/prisma'
import { Result } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import { methodNotAllowed } from 'utils'

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
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 result = await prisma.result.update({
where: { id },
Expand Down
8 changes: 6 additions & 2 deletions apps/viewer/services/result.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Result } from 'db'
import { VariableWithValue } from 'models'
import { sendRequest } from 'utils'

export const createResult = async (typebotId: string) => {
export const createResult = async (
typebotId: string,
prefilledVariables: VariableWithValue[]
) => {
return sendRequest<Result>({
url: `/api/results`,
method: 'POST',
body: { typebotId },
body: { typebotId, prefilledVariables },
})
}

Expand Down
18 changes: 14 additions & 4 deletions packages/bot-engine/src/components/ChatBlock/ChatStep/ChatStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,18 @@ export const ChatStep = ({
}) => {
const { addAnswer } = useAnswers()

const handleInputSubmit = (content: string, isRetry: boolean) => {
if (!isRetry) addAnswer({ stepId: step.id, blockId: step.blockId, content })
const handleInputSubmit = (
content: string,
isRetry: boolean,
variableId?: string
) => {
if (!isRetry)
addAnswer({
stepId: step.id,
blockId: step.blockId,
content,
variableId: variableId ?? null,
})
onTransitionEnd(content, isRetry)
}

Expand All @@ -38,7 +48,7 @@ const InputChatStep = ({
onSubmit,
}: {
step: InputStep
onSubmit: (value: string, isRetry: boolean) => void
onSubmit: (value: string, isRetry: boolean, variableId?: string) => void
}) => {
const { typebot } = useTypebot()
const { addNewAvatarOffset } = useHostAvatars()
Expand All @@ -54,7 +64,7 @@ const InputChatStep = ({

const handleSubmit = (value: string) => {
setAnswer(value)
onSubmit(value, !isInputValid(value, step.type))
onSubmit(value, !isInputValid(value, step.type), step.options.variableId)
}

if (answer) {
Expand Down
Loading

3 comments on commit aaf78e8

@vercel
Copy link

@vercel vercel bot commented on aaf78e8 Feb 17, 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:

docs – ./apps/docs

docs-git-main-typebot-io.vercel.app
docs.typebot.io
docs-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on aaf78e8 Feb 17, 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 aaf78e8 Feb 17, 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

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

Please sign in to comment.