Skip to content

Commit

Permalink
feat(engine): ✨ Add {{state}} to body to get form state
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Feb 22, 2022
1 parent 1b900b3 commit d0994e6
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 19 deletions.
3 changes: 3 additions & 0 deletions apps/builder/components/editor/preview/PreviewDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export const PreviewDrawer = () => {
if (event.data.typebotInfo) {
toast({ description: event.data.typebotInfo })
}
if (event.data.typebotError) {
toast({ description: event.data.typebotError, status: 'error' })
}
}
window.addEventListener('message', onMessageFromBot)
return () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { withSentry } from '@sentry/nextjs'
import { Prisma } from 'db'
import prisma from 'libs/prisma'
import { IntegrationStepType, Typebot } from 'models'
import { HttpMethod, IntegrationStepType, Typebot } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import { authenticateUser } from 'services/api/utils'
import { methodNotAllowed } from 'utils'
Expand Down Expand Up @@ -47,7 +47,15 @@ const addUrlToWebhookStep = (
steps: b.steps.map((s) => {
if (s.id === stepId) {
if (s.type !== IntegrationStepType.WEBHOOK) throw new Error()
return { ...s, webhook: { ...s.webhook, url } }
return {
...s,
webhook: {
...s.webhook,
url,
method: HttpMethod.POST,
body: '{{state}}',
},
}
}
return s
}),
Expand Down
4 changes: 4 additions & 0 deletions packages/bot-engine/src/components/ChatBlock/ChatBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { executeLogic } from 'services/logic'
import { executeIntegration } from 'services/integration'
import { parseRetryStep, stepCanBeRetried } from 'services/inputs'
import { parseVariables } from 'index'
import { useAnswers } from 'contexts/AnswersContext'

type ChatBlockProps = {
steps: PublicStep[]
Expand All @@ -32,6 +33,7 @@ export const ChatBlock = ({
}: ChatBlockProps) => {
const { typebot, updateVariableValue, createEdge, apiHost, isPreview } =
useTypebot()
const { resultValues } = useAnswers()
const [displayedSteps, setDisplayedSteps] = useState<PublicStep[]>([])

useEffect(() => {
Expand Down Expand Up @@ -68,6 +70,8 @@ export const ChatBlock = ({
variables: typebot.variables,
isPreview,
updateVariableValue,
resultValues,
blocks: typebot.blocks,
},
})
nextEdgeId ? onBlockEnd(nextEdgeId) : displayNextStep()
Expand Down
10 changes: 8 additions & 2 deletions packages/bot-engine/src/components/ConversationContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ export const ConversationContainer = ({
{ block: PublicBlock; startStepIndex: number }[]
>([])
const [localAnswer, setLocalAnswer] = useState<Answer | undefined>()
const { answers } = useAnswers()
const {
resultValues: { answers },
setPrefilledVariables,
} = useAnswers()
const bottomAnchor = useRef<HTMLDivElement | null>(null)
const scrollableContainer = useRef<HTMLDivElement | null>(null)

Expand All @@ -51,7 +54,10 @@ export const ConversationContainer = ({

useEffect(() => {
const prefilledVariables = injectUrlParamsIntoVariables()
if (onVariablesPrefilled) onVariablesPrefilled(prefilledVariables)
if (onVariablesPrefilled) {
onVariablesPrefilled(prefilledVariables)
setPrefilledVariables(prefilledVariables)
}
displayNextBlock(typebot.blocks[0].steps[0].outgoingEdgeId)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
Expand Down
29 changes: 24 additions & 5 deletions packages/bot-engine/src/contexts/AnswersContext.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
import { Answer } from 'models'
import { Answer, ResultWithAnswers, VariableWithValue } from 'models'
import React, { createContext, ReactNode, useContext, useState } from 'react'

export type ResultValues = Pick<
ResultWithAnswers,
'answers' | 'createdAt' | 'prefilledVariables'
>
const answersContext = createContext<{
answers: Answer[]
resultValues: ResultValues
addAnswer: (answer: Answer) => void
setPrefilledVariables: (variables: VariableWithValue[]) => void
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
}>({})

export const AnswersContext = ({ children }: { children: ReactNode }) => {
const [answers, setAnswers] = useState<Answer[]>([])
const [resultValues, setResultValues] = useState<ResultValues>({
answers: [],
prefilledVariables: [],
createdAt: new Date().toISOString(),
})

const addAnswer = (answer: Answer) =>
setAnswers((answers) => [...answers, answer])
setResultValues((resultValues) => ({
...resultValues,
answers: [...resultValues.answers, answer],
}))

const setPrefilledVariables = (variables: VariableWithValue[]) =>
setResultValues((resultValues) => ({
...resultValues,
prefilledVariables: variables,
}))

return (
<answersContext.Provider
value={{
answers,
resultValues,
addAnswer,
setPrefilledVariables,
}}
>
{children}
Expand Down
17 changes: 13 additions & 4 deletions packages/bot-engine/src/services/integration.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ResultValues } from 'contexts/AnswersContext'
import {
IntegrationStep,
IntegrationStepType,
Expand All @@ -11,11 +12,12 @@ import {
GoogleAnalyticsStep,
WebhookStep,
SendEmailStep,
PublicBlock,
} from 'models'
import { stringify } from 'qs'
import { sendRequest } from 'utils'
import { parseAnswers, sendRequest } from 'utils'
import { sendGaEvent } from '../../lib/gtag'
import { sendInfoMessage } from './postMessage'
import { sendErrorMessage, sendInfoMessage } from './postMessage'
import { parseVariables, parseVariablesInObject } from './variable'

const safeEval = eval
Expand All @@ -27,8 +29,11 @@ type IntegrationContext = {
stepId: string
isPreview: boolean
variables: Variable[]
resultValues: ResultValues
blocks: PublicBlock[]
updateVariableValue: (variableId: string, value: string) => void
}

export const executeIntegration = ({
step,
context,
Expand Down Expand Up @@ -179,7 +184,7 @@ const executeWebhook = async (

const sendEmail = async (
step: SendEmailStep,
{ variables, apiHost, isPreview }: IntegrationContext
{ variables, apiHost, isPreview, resultValues, blocks }: IntegrationContext
) => {
if (isPreview) sendInfoMessage('Emails are not sent in preview mode')
if (isPreview) return step.outgoingEdgeId
Expand All @@ -191,11 +196,15 @@ const sendEmail = async (
credentialsId: options.credentialsId,
recipients: options.recipients.map(parseVariables(variables)),
subject: parseVariables(variables)(options.subject ?? ''),
body: parseVariables(variables)(options.body ?? ''),
body:
options.body === '{{state}}'
? parseAnswers({ variables, blocks })(resultValues)
: parseVariables(variables)(options.body ?? ''),
cc: (options.cc ?? []).map(parseVariables(variables)),
bcc: (options.bcc ?? []).map(parseVariables(variables)),
},
})
console.error(error)
if (isPreview && error) sendErrorMessage(`Webhook failed: ${error.message}`)
return step.outgoingEdgeId
}
4 changes: 4 additions & 0 deletions packages/bot-engine/src/services/postMessage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export const sendInfoMessage = (typebotInfo: string) => {
parent.postMessage({ typebotInfo })
}

export const sendErrorMessage = (typebotError: string) => {
parent.postMessage({ typebotError })
}
29 changes: 23 additions & 6 deletions packages/utils/src/apiUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Typebot, Answer, VariableWithValue, ResultWithAnswers } from 'models'
import {
Typebot,
Answer,
VariableWithValue,
ResultWithAnswers,
PublicTypebot,
Block,
} from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import { byId, isDefined } from '.'

Expand All @@ -24,17 +31,27 @@ export const initMiddleware =
})

export const parseAnswers =
({ blocks, variables }: Pick<Typebot, 'blocks' | 'variables'>) =>
(result: ResultWithAnswers) => ({
submittedAt: result.createdAt,
...[...result.answers, ...result.prefilledVariables].reduce<{
({
blocks,
variables,
}: Pick<Typebot | PublicTypebot, 'blocks' | 'variables'>) =>
({
createdAt,
answers,
prefilledVariables,
}: Pick<
ResultWithAnswers,
'createdAt' | 'answers' | 'prefilledVariables'
>) => ({
submittedAt: createdAt,
...[...answers, ...prefilledVariables].reduce<{
[key: string]: string
}>((o, answerOrVariable) => {
if ('blockId' in answerOrVariable) {
const answer = answerOrVariable as Answer
const key = answer.variableId
? variables.find(byId(answer.variableId))?.name
: blocks.find(byId(answer.blockId))?.title
: (blocks as Block[]).find(byId(answer.blockId))?.title
if (!key) return o
return {
...o,
Expand Down

2 comments on commit d0994e6

@vercel
Copy link

@vercel vercel bot commented on d0994e6 Feb 22, 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-git-main-typebot-io.vercel.app
builder-v2-typebot-io.vercel.app
app.typebot.io

@vercel
Copy link

@vercel vercel bot commented on d0994e6 Feb 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.