fix(engine): 🐛 Save variables from webhooks in results
This commit is contained in:
@ -131,7 +131,7 @@ export const convertResultsToTableData = (
|
||||
): { [key: string]: string }[] =>
|
||||
(results ?? []).map((result) => ({
|
||||
'Submitted at': parseDateToReadable(result.createdAt),
|
||||
...[...result.answers, ...result.prefilledVariables].reduce<{
|
||||
...[...result.answers, ...result.variables].reduce<{
|
||||
[key: string]: string
|
||||
}>((o, answerOrVariable) => {
|
||||
if ('blockId' in answerOrVariable) {
|
||||
|
@ -54,11 +54,9 @@ export const TypebotPage = ({
|
||||
setShowTypebot(true)
|
||||
}
|
||||
|
||||
const handleVariablesPrefilled = async (
|
||||
prefilledVariables: VariableWithValue[]
|
||||
) => {
|
||||
const handleNewVariables = async (variables: VariableWithValue[]) => {
|
||||
if (!resultId) return setError(new Error('Result was not created'))
|
||||
const { error } = await updateResult(resultId, { prefilledVariables })
|
||||
const { error } = await updateResult(resultId, { variables })
|
||||
if (error) setError(error)
|
||||
}
|
||||
|
||||
@ -98,7 +96,7 @@ export const TypebotPage = ({
|
||||
predefinedVariables={predefinedVariables}
|
||||
onNewAnswer={handleNewAnswer}
|
||||
onCompleted={handleCompleted}
|
||||
onVariablesPrefilled={handleVariablesPrefilled}
|
||||
onVariablesUpdated={handleNewVariables}
|
||||
onNewLog={handleNewLog}
|
||||
/>
|
||||
)}
|
||||
|
@ -49,7 +49,7 @@ export const ChatBlock = ({
|
||||
injectLinkedTypebot,
|
||||
linkedTypebots,
|
||||
} = useTypebot()
|
||||
const { resultValues } = useAnswers()
|
||||
const { resultValues, updateVariables } = useAnswers()
|
||||
const [processedSteps, setProcessedSteps] = useState<Step[]>([])
|
||||
const [displayedChunks, setDisplayedChunks] = useState<ChatDisplayChunk[]>([])
|
||||
|
||||
@ -104,6 +104,7 @@ export const ChatBlock = ({
|
||||
typebot,
|
||||
linkedTypebots,
|
||||
updateVariableValue,
|
||||
updateVariables,
|
||||
injectLinkedTypebot,
|
||||
onNewLog,
|
||||
createEdge,
|
||||
@ -121,6 +122,7 @@ export const ChatBlock = ({
|
||||
variables: typebot.variables,
|
||||
isPreview,
|
||||
updateVariableValue,
|
||||
updateVariables,
|
||||
resultValues,
|
||||
blocks: typebot.blocks,
|
||||
onNewLog,
|
||||
|
@ -14,21 +14,19 @@ type Props = {
|
||||
predefinedVariables?: { [key: string]: string | undefined }
|
||||
onNewBlockVisible: (edge: Edge) => void
|
||||
onCompleted: () => void
|
||||
onVariablesPrefilled?: (prefilledVariables: VariableWithValue[]) => void
|
||||
}
|
||||
export const ConversationContainer = ({
|
||||
theme,
|
||||
predefinedVariables,
|
||||
onNewBlockVisible,
|
||||
onCompleted,
|
||||
onVariablesPrefilled,
|
||||
}: Props) => {
|
||||
const { typebot, updateVariableValue } = useTypebot()
|
||||
const { document: frameDocument } = useFrame()
|
||||
const [displayedBlocks, setDisplayedBlocks] = useState<
|
||||
{ block: Block; startStepIndex: number }[]
|
||||
>([])
|
||||
const { setPrefilledVariables } = useAnswers()
|
||||
const { updateVariables } = useAnswers()
|
||||
const bottomAnchor = useRef<HTMLDivElement | null>(null)
|
||||
const scrollableContainer = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
@ -53,8 +51,7 @@ export const ConversationContainer = ({
|
||||
|
||||
useEffect(() => {
|
||||
const prefilledVariables = injectPredefinedVariables(predefinedVariables)
|
||||
if (onVariablesPrefilled) onVariablesPrefilled(prefilledVariables)
|
||||
setPrefilledVariables(prefilledVariables)
|
||||
updateVariables(prefilledVariables)
|
||||
displayNextBlock(typebot.blocks[0].steps[0].outgoingEdgeId)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
@ -31,7 +31,7 @@ export type TypebotViewerProps = {
|
||||
onNewAnswer?: (answer: Answer) => void
|
||||
onNewLog?: (log: Omit<Log, 'id' | 'createdAt' | 'resultId'>) => void
|
||||
onCompleted?: () => void
|
||||
onVariablesPrefilled?: (prefilledVariables: VariableWithValue[]) => void
|
||||
onVariablesUpdated?: (variables: VariableWithValue[]) => void
|
||||
}
|
||||
|
||||
export const TypebotViewer = ({
|
||||
@ -44,7 +44,7 @@ export const TypebotViewer = ({
|
||||
onNewBlockVisible,
|
||||
onNewAnswer,
|
||||
onCompleted,
|
||||
onVariablesPrefilled,
|
||||
onVariablesUpdated,
|
||||
}: TypebotViewerProps) => {
|
||||
const containerBgColor = useMemo(
|
||||
() =>
|
||||
@ -93,7 +93,10 @@ export const TypebotViewer = ({
|
||||
isPreview={isPreview}
|
||||
onNewLog={handleNewLog}
|
||||
>
|
||||
<AnswersContext onNewAnswer={handleNewAnswer}>
|
||||
<AnswersContext
|
||||
onNewAnswer={handleNewAnswer}
|
||||
onVariablesUpdated={onVariablesUpdated}
|
||||
>
|
||||
<div
|
||||
className="flex text-base overflow-hidden bg-cover h-screen w-screen flex-col items-center typebot-container"
|
||||
style={{
|
||||
@ -108,7 +111,6 @@ export const TypebotViewer = ({
|
||||
onNewBlockVisible={handleNewBlockVisible}
|
||||
onCompleted={handleCompleted}
|
||||
predefinedVariables={predefinedVariables}
|
||||
onVariablesPrefilled={onVariablesPrefilled}
|
||||
/>
|
||||
</div>
|
||||
{typebot.settings.general.isBrandingEnabled && (
|
||||
|
@ -4,7 +4,7 @@ import React, { createContext, ReactNode, useContext, useState } from 'react'
|
||||
const answersContext = createContext<{
|
||||
resultValues: ResultValues
|
||||
addAnswer: (answer: Answer) => void
|
||||
setPrefilledVariables: (variables: VariableWithValue[]) => void
|
||||
updateVariables: (variables: VariableWithValue[]) => void
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
@ -12,13 +12,15 @@ const answersContext = createContext<{
|
||||
export const AnswersContext = ({
|
||||
children,
|
||||
onNewAnswer,
|
||||
onVariablesUpdated,
|
||||
}: {
|
||||
onNewAnswer: (answer: Answer) => void
|
||||
onVariablesUpdated?: (variables: VariableWithValue[]) => void
|
||||
children: ReactNode
|
||||
}) => {
|
||||
const [resultValues, setResultValues] = useState<ResultValues>({
|
||||
answers: [],
|
||||
prefilledVariables: [],
|
||||
variables: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
})
|
||||
|
||||
@ -30,18 +32,22 @@ export const AnswersContext = ({
|
||||
onNewAnswer(answer)
|
||||
}
|
||||
|
||||
const setPrefilledVariables = (variables: VariableWithValue[]) =>
|
||||
setResultValues((resultValues) => ({
|
||||
const updateVariables = (variables: VariableWithValue[]) =>
|
||||
setResultValues((resultValues) => {
|
||||
const updatedVariables = [...resultValues.variables, ...variables]
|
||||
if (onVariablesUpdated) onVariablesUpdated(updatedVariables)
|
||||
return {
|
||||
...resultValues,
|
||||
prefilledVariables: variables,
|
||||
}))
|
||||
variables: updatedVariables,
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<answersContext.Provider
|
||||
value={{
|
||||
resultValues,
|
||||
addAnswer,
|
||||
setPrefilledVariables,
|
||||
updateVariables,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -15,9 +15,10 @@ import {
|
||||
ZapierStep,
|
||||
ResultValues,
|
||||
Block,
|
||||
VariableWithValue,
|
||||
} from 'models'
|
||||
import { stringify } from 'qs'
|
||||
import { sendRequest } from 'utils'
|
||||
import { byId, sendRequest } from 'utils'
|
||||
import { sendGaEvent } from '../../lib/gtag'
|
||||
import { parseVariables, parseVariablesInObject } from './variable'
|
||||
|
||||
@ -30,6 +31,7 @@ type IntegrationContext = {
|
||||
variables: Variable[]
|
||||
resultValues: ResultValues
|
||||
blocks: Block[]
|
||||
updateVariables: (variables: VariableWithValue[]) => void
|
||||
updateVariableValue: (variableId: string, value: string) => void
|
||||
onNewLog: (log: Omit<Log, 'id' | 'createdAt' | 'resultId'>) => void
|
||||
}
|
||||
@ -141,7 +143,13 @@ const updateRowInGoogleSheets = async (
|
||||
|
||||
const getRowFromGoogleSheets = async (
|
||||
options: GoogleSheetsGetOptions,
|
||||
{ variables, updateVariableValue, apiHost, onNewLog }: IntegrationContext
|
||||
{
|
||||
variables,
|
||||
updateVariableValue,
|
||||
updateVariables,
|
||||
apiHost,
|
||||
onNewLog,
|
||||
}: IntegrationContext
|
||||
) => {
|
||||
if (!options.referenceCell || !options.cellsToExtract) return
|
||||
const queryParams = stringify(
|
||||
@ -167,9 +175,23 @@ const getRowFromGoogleSheets = async (
|
||||
)
|
||||
)
|
||||
if (!data) return
|
||||
options.cellsToExtract.forEach((cell) =>
|
||||
updateVariableValue(cell.variableId ?? '', data[cell.column ?? ''])
|
||||
const newVariables = options.cellsToExtract.reduce<VariableWithValue[]>(
|
||||
(newVariables, cell) => {
|
||||
const existingVariable = variables.find(byId(cell.variableId))
|
||||
const value = data[cell.column ?? '']
|
||||
if (!existingVariable || !value) return newVariables
|
||||
updateVariableValue(existingVariable.id, value)
|
||||
return [
|
||||
...newVariables,
|
||||
{
|
||||
...existingVariable,
|
||||
value,
|
||||
},
|
||||
]
|
||||
},
|
||||
[]
|
||||
)
|
||||
updateVariables(newVariables)
|
||||
}
|
||||
const parseCellValues = (
|
||||
cells: Cell[],
|
||||
@ -191,6 +213,7 @@ const executeWebhook = async (
|
||||
stepId,
|
||||
variables,
|
||||
updateVariableValue,
|
||||
updateVariables,
|
||||
typebotId,
|
||||
apiHost,
|
||||
resultValues,
|
||||
@ -218,13 +241,19 @@ const executeWebhook = async (
|
||||
: 'Webhook successfuly executed',
|
||||
details: JSON.stringify(error ?? data, null, 2).substring(0, 1000),
|
||||
})
|
||||
step.options.responseVariableMapping.forEach((varMapping) => {
|
||||
if (!varMapping?.bodyPath || !varMapping.variableId) return
|
||||
const newVariables = step.options.responseVariableMapping.reduce<
|
||||
VariableWithValue[]
|
||||
>((newVariables, varMapping) => {
|
||||
if (!varMapping?.bodyPath || !varMapping.variableId) return newVariables
|
||||
const existingVariable = variables.find(byId(varMapping.variableId))
|
||||
if (!existingVariable) return newVariables
|
||||
const value = Function(
|
||||
`return (${JSON.stringify(data)}).${varMapping?.bodyPath}`
|
||||
)()
|
||||
updateVariableValue(varMapping.variableId, value)
|
||||
})
|
||||
updateVariableValue(existingVariable?.id, value)
|
||||
return [...newVariables, { ...existingVariable, value }]
|
||||
}, [])
|
||||
updateVariables(newVariables)
|
||||
return step.outgoingEdgeId
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
PublicTypebot,
|
||||
Typebot,
|
||||
Edge,
|
||||
VariableWithValue,
|
||||
} from 'models'
|
||||
import { byId, isDefined, isNotDefined, sendRequest } from 'utils'
|
||||
import { sanitizeUrl } from './utils'
|
||||
@ -28,6 +29,7 @@ type LogicContext = {
|
||||
typebot: PublicTypebot
|
||||
linkedTypebots: LinkedTypebot[]
|
||||
updateVariableValue: (variableId: string, value: string) => void
|
||||
updateVariables: (variables: VariableWithValue[]) => void
|
||||
injectLinkedTypebot: (typebot: Typebot | PublicTypebot) => LinkedTypebot
|
||||
onNewLog: (log: Omit<Log, 'id' | 'createdAt' | 'resultId'>) => void
|
||||
createEdge: (edge: Edge) => void
|
||||
@ -56,7 +58,7 @@ export const executeLogic = async (
|
||||
|
||||
const executeSetVariable = (
|
||||
step: SetVariableStep,
|
||||
{ typebot: { variables }, updateVariableValue }: LogicContext
|
||||
{ typebot: { variables }, updateVariableValue, updateVariables }: LogicContext
|
||||
): EdgeId | undefined => {
|
||||
if (!step.options?.variableId || !step.options.expressionToEvaluate)
|
||||
return step.outgoingEdgeId
|
||||
@ -64,7 +66,10 @@ const executeSetVariable = (
|
||||
const evaluatedExpression = evaluateExpression(
|
||||
parseVariables(variables)(expression)
|
||||
)
|
||||
updateVariableValue(step.options.variableId, evaluatedExpression)
|
||||
const existingVariable = variables.find(byId(step.options.variableId))
|
||||
if (!existingVariable) return step.outgoingEdgeId
|
||||
updateVariableValue(existingVariable.id, evaluatedExpression)
|
||||
updateVariables([{ ...existingVariable, value: evaluatedExpression }])
|
||||
return step.outgoingEdgeId
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Result"
|
||||
RENAME COLUMN "prefilledVariables" "variables"
|
@ -184,7 +184,7 @@ model Result {
|
||||
typebotId String
|
||||
typebot Typebot @relation(fields: [typebotId], references: [id], onDelete: Cascade)
|
||||
answers Answer[]
|
||||
prefilledVariables Json[]
|
||||
variables Json[]
|
||||
isCompleted Boolean
|
||||
logs Log[]
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Result as ResultFromPrisma } from 'db'
|
||||
import { Answer, InputStepType, VariableWithValue } from '.'
|
||||
|
||||
export type Result = Omit<
|
||||
ResultFromPrisma,
|
||||
'createdAt' | 'prefilledVariables'
|
||||
> & { createdAt: string; prefilledVariables: VariableWithValue[] }
|
||||
export type Result = Omit<ResultFromPrisma, 'createdAt' | 'variables'> & {
|
||||
createdAt: string
|
||||
variables: VariableWithValue[]
|
||||
}
|
||||
|
||||
export type ResultWithAnswers = Result & { answers: Answer[] }
|
||||
|
||||
export type ResultValues = Pick<
|
||||
ResultWithAnswers,
|
||||
'answers' | 'createdAt' | 'prefilledVariables'
|
||||
'answers' | 'createdAt' | 'variables'
|
||||
>
|
||||
|
||||
export type ResultHeaderCell = {
|
||||
|
@ -91,14 +91,14 @@ export const parseAnswers =
|
||||
({
|
||||
createdAt,
|
||||
answers,
|
||||
prefilledVariables,
|
||||
}: Pick<ResultWithAnswers, 'createdAt' | 'answers' | 'prefilledVariables'>): {
|
||||
variables: resultVariables,
|
||||
}: Pick<ResultWithAnswers, 'createdAt' | 'answers' | 'variables'>): {
|
||||
[key: string]: string
|
||||
} => {
|
||||
const header = parseResultHeader({ blocks, variables })
|
||||
return {
|
||||
submittedAt: createdAt,
|
||||
...[...answers, ...prefilledVariables].reduce<{
|
||||
...[...answers, ...resultVariables].reduce<{
|
||||
[key: string]: string
|
||||
}>((o, answerOrVariable) => {
|
||||
if ('blockId' in answerOrVariable) {
|
||||
|
Reference in New Issue
Block a user