🔥 Remove disable response saving option
Doesn't work properly when it comes to keep tracking storage usage
This commit is contained in:
@ -54,7 +54,7 @@ export const Graph = ({
|
|||||||
} = useGraph()
|
} = useGraph()
|
||||||
const { updateGroupCoordinates } = useGroupsCoordinates()
|
const { updateGroupCoordinates } = useGroupsCoordinates()
|
||||||
const [graphPosition, setGraphPosition] = useState(
|
const [graphPosition, setGraphPosition] = useState(
|
||||||
graphPositionDefaultValue(typebot.groups[0].graphCoordinates)
|
graphPositionDefaultValue(typebot.groups[0]?.graphCoordinates)
|
||||||
)
|
)
|
||||||
const [debouncedGraphPosition] = useDebounce(graphPosition, 200)
|
const [debouncedGraphPosition] = useDebounce(graphPosition, 200)
|
||||||
const transform = useMemo(
|
const transform = useMemo(
|
||||||
|
@ -40,7 +40,7 @@ export const ItemNodesList = ({
|
|||||||
|
|
||||||
const isLastBlock =
|
const isLastBlock =
|
||||||
isDefined(typebot) &&
|
isDefined(typebot) &&
|
||||||
typebot.groups[groupIndex].blocks[blockIndex + 1] === undefined
|
typebot.groups[groupIndex]?.blocks?.[blockIndex + 1] === undefined
|
||||||
|
|
||||||
const [position, setPosition] = useState({
|
const [position, setPosition] = useState({
|
||||||
x: 0,
|
x: 0,
|
||||||
|
@ -46,12 +46,6 @@ export const GeneralSettingsForm = ({
|
|||||||
isHideQueryParamsEnabled,
|
isHideQueryParamsEnabled,
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleDisableResultsSavingChange = (isResultSavingEnabled: boolean) =>
|
|
||||||
onGeneralSettingsChange({
|
|
||||||
...generalSettings,
|
|
||||||
isResultSavingEnabled: !isResultSavingEnabled,
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={6}>
|
<Stack spacing={6}>
|
||||||
<ChangePlanModal
|
<ChangePlanModal
|
||||||
@ -96,16 +90,6 @@ export const GeneralSettingsForm = ({
|
|||||||
onCheckChange={handleHideQueryParamsChange}
|
onCheckChange={handleHideQueryParamsChange}
|
||||||
moreInfoContent="If your URL contains query params, they will be automatically hidden when the bot starts."
|
moreInfoContent="If your URL contains query params, they will be automatically hidden when the bot starts."
|
||||||
/>
|
/>
|
||||||
<SwitchWithLabel
|
|
||||||
label="Disable responses saving"
|
|
||||||
initialValue={
|
|
||||||
isDefined(generalSettings.isResultSavingEnabled)
|
|
||||||
? !generalSettings.isResultSavingEnabled
|
|
||||||
: false
|
|
||||||
}
|
|
||||||
onCheckChange={handleDisableResultsSavingChange}
|
|
||||||
moreInfoContent="Prevent responses from being saved on Typebot. Chats limit usage will still be tracked."
|
|
||||||
/>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,6 @@ test.describe.parallel('Settings page', () => {
|
|||||||
page.locator('input[type="checkbox"] >> nth=-3')
|
page.locator('input[type="checkbox"] >> nth=-3')
|
||||||
).toHaveAttribute('checked', '')
|
).toHaveAttribute('checked', '')
|
||||||
|
|
||||||
await page.click('text="Disable responses saving"')
|
|
||||||
await expect(
|
|
||||||
page.locator('input[type="checkbox"] >> nth=-1')
|
|
||||||
).toHaveAttribute('checked', '')
|
|
||||||
|
|
||||||
await expect(page.getByPlaceholder('Type your answer...')).toHaveValue(
|
await expect(page.getByPlaceholder('Type your answer...')).toHaveValue(
|
||||||
'Baptiste'
|
'Baptiste'
|
||||||
)
|
)
|
||||||
|
@ -1839,6 +1839,12 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"company": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"workspaceId": {
|
"workspaceId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -1867,9 +1873,27 @@
|
|||||||
},
|
},
|
||||||
"additionalStorage": {
|
"additionalStorage": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
|
},
|
||||||
|
"vat": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
"email",
|
||||||
|
"company",
|
||||||
"workspaceId",
|
"workspaceId",
|
||||||
"currency",
|
"currency",
|
||||||
"plan",
|
"plan",
|
||||||
|
@ -3009,9 +3009,6 @@
|
|||||||
},
|
},
|
||||||
"isNewResultOnRefreshEnabled": {
|
"isNewResultOnRefreshEnabled": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
|
||||||
"isResultSavingEnabled": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@ -4993,9 +4990,6 @@
|
|||||||
},
|
},
|
||||||
"isNewResultOnRefreshEnabled": {
|
"isNewResultOnRefreshEnabled": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
|
||||||
"isResultSavingEnabled": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -107,8 +107,6 @@ export const TypebotPageV2 = ({
|
|||||||
|
|
||||||
const sendNewVariables =
|
const sendNewVariables =
|
||||||
(resultId: string) => async (variables: VariableWithValue[]) => {
|
(resultId: string) => async (variables: VariableWithValue[]) => {
|
||||||
if (publishedTypebot.settings.general.isResultSavingEnabled === false)
|
|
||||||
return
|
|
||||||
const { error } = await updateResultQuery(resultId, { variables })
|
const { error } = await updateResultQuery(resultId, { variables })
|
||||||
if (error) setError(error)
|
if (error) setError(error)
|
||||||
}
|
}
|
||||||
@ -117,10 +115,8 @@ export const TypebotPageV2 = ({
|
|||||||
answer: AnswerInput & { uploadedFiles: boolean }
|
answer: AnswerInput & { uploadedFiles: boolean }
|
||||||
) => {
|
) => {
|
||||||
if (!resultId) return setError(new Error('Error: result was not created'))
|
if (!resultId) return setError(new Error('Error: result was not created'))
|
||||||
if (publishedTypebot.settings.general.isResultSavingEnabled !== false) {
|
const { error } = await upsertAnswerQuery({ ...answer, resultId })
|
||||||
const { error } = await upsertAnswerQuery({ ...answer, resultId })
|
if (error) setError(error)
|
||||||
if (error) setError(error)
|
|
||||||
}
|
|
||||||
if (chatStarted) return
|
if (chatStarted) return
|
||||||
updateResultQuery(resultId, {
|
updateResultQuery(resultId, {
|
||||||
hasStarted: true,
|
hasStarted: true,
|
||||||
@ -128,8 +124,6 @@ export const TypebotPageV2 = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleCompleted = async () => {
|
const handleCompleted = async () => {
|
||||||
if (publishedTypebot.settings.general.isResultSavingEnabled === false)
|
|
||||||
return
|
|
||||||
if (!resultId) return setError(new Error('Error: result was not created'))
|
if (!resultId) return setError(new Error('Error: result was not created'))
|
||||||
const { error } = await updateResultQuery(resultId, { isCompleted: true })
|
const { error } = await updateResultQuery(resultId, { isCompleted: true })
|
||||||
if (error) setError(error)
|
if (error) setError(error)
|
||||||
|
@ -11,17 +11,18 @@ import Stripe from 'stripe'
|
|||||||
import { decrypt } from 'utils/api/encryption'
|
import { decrypt } from 'utils/api/encryption'
|
||||||
|
|
||||||
export const computePaymentInputRuntimeOptions =
|
export const computePaymentInputRuntimeOptions =
|
||||||
(state: Pick<SessionState, 'isPreview' | 'typebot'>) =>
|
(state: Pick<SessionState, 'result' | 'typebot'>) =>
|
||||||
(options: PaymentInputOptions) =>
|
(options: PaymentInputOptions) =>
|
||||||
createStripePaymentIntent(state)(options)
|
createStripePaymentIntent(state)(options)
|
||||||
|
|
||||||
const createStripePaymentIntent =
|
const createStripePaymentIntent =
|
||||||
(state: Pick<SessionState, 'isPreview' | 'typebot'>) =>
|
(state: Pick<SessionState, 'result' | 'typebot'>) =>
|
||||||
async (options: PaymentInputOptions): Promise<PaymentInputRuntimeOptions> => {
|
async (options: PaymentInputOptions): Promise<PaymentInputRuntimeOptions> => {
|
||||||
const {
|
const {
|
||||||
isPreview,
|
result,
|
||||||
typebot: { variables },
|
typebot: { variables },
|
||||||
} = state
|
} = state
|
||||||
|
const isPreview = !result.id
|
||||||
if (!options.credentialsId)
|
if (!options.credentialsId)
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: 'BAD_REQUEST',
|
code: 'BAD_REQUEST',
|
||||||
|
@ -50,10 +50,11 @@ if (window.$chatwoot) {
|
|||||||
}`
|
}`
|
||||||
|
|
||||||
export const executeChatwootBlock = (
|
export const executeChatwootBlock = (
|
||||||
{ typebot: { variables }, isPreview }: SessionState,
|
{ typebot: { variables }, result }: SessionState,
|
||||||
block: ChatwootBlock,
|
block: ChatwootBlock,
|
||||||
lastBubbleBlockId?: string
|
lastBubbleBlockId?: string
|
||||||
): ExecuteIntegrationResponse => {
|
): ExecuteIntegrationResponse => {
|
||||||
|
const isPreview = !result.id
|
||||||
const chatwootCode = parseChatwootOpenCode(block.options)
|
const chatwootCode = parseChatwootOpenCode(block.options)
|
||||||
return {
|
return {
|
||||||
outgoingEdgeId: block.outgoingEdgeId,
|
outgoingEdgeId: block.outgoingEdgeId,
|
||||||
|
@ -6,7 +6,7 @@ import { render } from '@faire/mjml-react/utils/render'
|
|||||||
import { DefaultBotNotificationEmail } from 'emails'
|
import { DefaultBotNotificationEmail } from 'emails'
|
||||||
import {
|
import {
|
||||||
PublicTypebot,
|
PublicTypebot,
|
||||||
ResultValues,
|
ResultInSession,
|
||||||
SendEmailBlock,
|
SendEmailBlock,
|
||||||
SendEmailOptions,
|
SendEmailOptions,
|
||||||
SessionState,
|
SessionState,
|
||||||
@ -20,11 +20,12 @@ import { decrypt } from 'utils/api'
|
|||||||
import { defaultFrom, defaultTransportOptions } from '../constants'
|
import { defaultFrom, defaultTransportOptions } from '../constants'
|
||||||
|
|
||||||
export const executeSendEmailBlock = async (
|
export const executeSendEmailBlock = async (
|
||||||
{ result, typebot, isPreview }: SessionState,
|
{ result, typebot }: SessionState,
|
||||||
block: SendEmailBlock
|
block: SendEmailBlock
|
||||||
): Promise<ExecuteIntegrationResponse> => {
|
): Promise<ExecuteIntegrationResponse> => {
|
||||||
const { options } = block
|
const { options } = block
|
||||||
const { variables } = typebot
|
const { variables } = typebot
|
||||||
|
const isPreview = !result.id
|
||||||
if (isPreview)
|
if (isPreview)
|
||||||
return {
|
return {
|
||||||
outgoingEdgeId: block.outgoingEdgeId,
|
outgoingEdgeId: block.outgoingEdgeId,
|
||||||
@ -37,7 +38,7 @@ export const executeSendEmailBlock = async (
|
|||||||
}
|
}
|
||||||
await sendEmail({
|
await sendEmail({
|
||||||
typebotId: typebot.id,
|
typebotId: typebot.id,
|
||||||
resultId: result?.id,
|
result,
|
||||||
credentialsId: options.credentialsId,
|
credentialsId: options.credentialsId,
|
||||||
recipients: options.recipients.map(parseVariables(variables)),
|
recipients: options.recipients.map(parseVariables(variables)),
|
||||||
subject: parseVariables(variables)(options.subject ?? ''),
|
subject: parseVariables(variables)(options.subject ?? ''),
|
||||||
@ -57,7 +58,7 @@ export const executeSendEmailBlock = async (
|
|||||||
|
|
||||||
const sendEmail = async ({
|
const sendEmail = async ({
|
||||||
typebotId,
|
typebotId,
|
||||||
resultId,
|
result,
|
||||||
credentialsId,
|
credentialsId,
|
||||||
recipients,
|
recipients,
|
||||||
body,
|
body,
|
||||||
@ -70,7 +71,7 @@ const sendEmail = async ({
|
|||||||
fileUrls,
|
fileUrls,
|
||||||
}: SendEmailOptions & {
|
}: SendEmailOptions & {
|
||||||
typebotId: string
|
typebotId: string
|
||||||
resultId?: string
|
result: ResultInSession
|
||||||
fileUrls?: string | string[]
|
fileUrls?: string | string[]
|
||||||
}) => {
|
}) => {
|
||||||
const { name: replyToName } = parseEmailRecipient(replyTo)
|
const { name: replyToName } = parseEmailRecipient(replyTo)
|
||||||
@ -94,12 +95,12 @@ const sendEmail = async ({
|
|||||||
isCustomBody,
|
isCustomBody,
|
||||||
isBodyCode,
|
isBodyCode,
|
||||||
typebotId,
|
typebotId,
|
||||||
resultId,
|
result,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!emailBody) {
|
if (!emailBody) {
|
||||||
await saveErrorLog({
|
await saveErrorLog({
|
||||||
resultId,
|
resultId: result.id,
|
||||||
message: 'Email not sent',
|
message: 'Email not sent',
|
||||||
details: {
|
details: {
|
||||||
error: 'No email body found',
|
error: 'No email body found',
|
||||||
@ -132,7 +133,7 @@ const sendEmail = async ({
|
|||||||
try {
|
try {
|
||||||
await transporter.sendMail(email)
|
await transporter.sendMail(email)
|
||||||
await saveSuccessLog({
|
await saveSuccessLog({
|
||||||
resultId,
|
resultId: result.id,
|
||||||
message: 'Email successfully sent',
|
message: 'Email successfully sent',
|
||||||
details: {
|
details: {
|
||||||
transportConfig: {
|
transportConfig: {
|
||||||
@ -144,7 +145,7 @@ const sendEmail = async ({
|
|||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await saveErrorLog({
|
await saveErrorLog({
|
||||||
resultId,
|
resultId: result.id,
|
||||||
message: 'Email not sent',
|
message: 'Email not sent',
|
||||||
details: {
|
details: {
|
||||||
error: err,
|
error: err,
|
||||||
@ -182,10 +183,10 @@ const getEmailBody = async ({
|
|||||||
isCustomBody,
|
isCustomBody,
|
||||||
isBodyCode,
|
isBodyCode,
|
||||||
typebotId,
|
typebotId,
|
||||||
resultId,
|
result,
|
||||||
}: {
|
}: {
|
||||||
typebotId: string
|
typebotId: string
|
||||||
resultId?: string
|
result: ResultInSession
|
||||||
} & Pick<SendEmailOptions, 'isCustomBody' | 'isBodyCode' | 'body'>): Promise<
|
} & Pick<SendEmailOptions, 'isCustomBody' | 'isBodyCode' | 'body'>): Promise<
|
||||||
{ html?: string; text?: string } | undefined
|
{ html?: string; text?: string } | undefined
|
||||||
> => {
|
> => {
|
||||||
@ -198,12 +199,7 @@ const getEmailBody = async ({
|
|||||||
where: { typebotId },
|
where: { typebotId },
|
||||||
})) as unknown as PublicTypebot
|
})) as unknown as PublicTypebot
|
||||||
if (!typebot) return
|
if (!typebot) return
|
||||||
const resultValues = (await prisma.result.findUnique({
|
const answers = parseAnswers(typebot, [])(result)
|
||||||
where: { id: resultId },
|
|
||||||
include: { answers: true },
|
|
||||||
})) as ResultValues | null
|
|
||||||
if (!resultValues) return
|
|
||||||
const answers = parseAnswers(typebot, [])(resultValues)
|
|
||||||
return {
|
return {
|
||||||
html: render(
|
html: render(
|
||||||
<DefaultBotNotificationEmail
|
<DefaultBotNotificationEmail
|
||||||
|
@ -16,16 +16,15 @@ import {
|
|||||||
WebhookOptions,
|
WebhookOptions,
|
||||||
defaultWebhookAttributes,
|
defaultWebhookAttributes,
|
||||||
HttpMethod,
|
HttpMethod,
|
||||||
ResultValues,
|
|
||||||
PublicTypebot,
|
PublicTypebot,
|
||||||
KeyValue,
|
KeyValue,
|
||||||
ReplyLog,
|
ReplyLog,
|
||||||
|
ResultInSession,
|
||||||
} from 'models'
|
} from 'models'
|
||||||
import { stringify } from 'qs'
|
import { stringify } from 'qs'
|
||||||
import { byId, omit } from 'utils'
|
import { byId, omit } from 'utils'
|
||||||
import { parseAnswers } from 'utils/results'
|
import { parseAnswers } from 'utils/results'
|
||||||
import got, { Method, Headers, HTTPError } from 'got'
|
import got, { Method, Headers, HTTPError } from 'got'
|
||||||
import { getResultValues } from '@/features/results/api'
|
|
||||||
import { parseSampleResult } from './parseSampleResult'
|
import { parseSampleResult } from './parseSampleResult'
|
||||||
|
|
||||||
export const executeWebhookBlock = async (
|
export const executeWebhookBlock = async (
|
||||||
@ -50,14 +49,11 @@ export const executeWebhookBlock = async (
|
|||||||
return { outgoingEdgeId: block.outgoingEdgeId, logs: [log] }
|
return { outgoingEdgeId: block.outgoingEdgeId, logs: [log] }
|
||||||
}
|
}
|
||||||
const preparedWebhook = prepareWebhookAttributes(webhook, block.options)
|
const preparedWebhook = prepareWebhookAttributes(webhook, block.options)
|
||||||
const resultValues =
|
|
||||||
(result && (await getResultValues(result.id))) ?? undefined
|
|
||||||
const webhookResponse = await executeWebhook({ typebot })(
|
const webhookResponse = await executeWebhook({ typebot })(
|
||||||
preparedWebhook,
|
preparedWebhook,
|
||||||
typebot.variables,
|
typebot.variables,
|
||||||
block.groupId,
|
block.groupId,
|
||||||
resultValues,
|
result
|
||||||
result?.id
|
|
||||||
)
|
)
|
||||||
const status = webhookResponse.statusCode.toString()
|
const status = webhookResponse.statusCode.toString()
|
||||||
const isError = status.startsWith('4') || status.startsWith('5')
|
const isError = status.startsWith('4') || status.startsWith('5')
|
||||||
@ -139,8 +135,7 @@ export const executeWebhook =
|
|||||||
webhook: Webhook,
|
webhook: Webhook,
|
||||||
variables: Variable[],
|
variables: Variable[],
|
||||||
groupId: string,
|
groupId: string,
|
||||||
resultValues?: ResultValues,
|
result: ResultInSession
|
||||||
resultId?: string
|
|
||||||
): Promise<WebhookResponse> => {
|
): Promise<WebhookResponse> => {
|
||||||
if (!webhook.url || !webhook.method)
|
if (!webhook.url || !webhook.method)
|
||||||
return {
|
return {
|
||||||
@ -176,7 +171,7 @@ export const executeWebhook =
|
|||||||
[]
|
[]
|
||||||
)({
|
)({
|
||||||
body: webhook.body,
|
body: webhook.body,
|
||||||
resultValues,
|
result,
|
||||||
groupId,
|
groupId,
|
||||||
variables,
|
variables,
|
||||||
})
|
})
|
||||||
@ -206,7 +201,7 @@ export const executeWebhook =
|
|||||||
try {
|
try {
|
||||||
const response = await got(request.url, omit(request, 'url'))
|
const response = await got(request.url, omit(request, 'url'))
|
||||||
await saveSuccessLog({
|
await saveSuccessLog({
|
||||||
resultId,
|
resultId: result.id,
|
||||||
message: 'Webhook successfuly executed.',
|
message: 'Webhook successfuly executed.',
|
||||||
details: {
|
details: {
|
||||||
statusCode: response.statusCode,
|
statusCode: response.statusCode,
|
||||||
@ -225,7 +220,7 @@ export const executeWebhook =
|
|||||||
data: safeJsonParse(error.response.body as string).data,
|
data: safeJsonParse(error.response.body as string).data,
|
||||||
}
|
}
|
||||||
await saveErrorLog({
|
await saveErrorLog({
|
||||||
resultId,
|
resultId: result.id,
|
||||||
message: 'Webhook returned an error',
|
message: 'Webhook returned an error',
|
||||||
details: {
|
details: {
|
||||||
request,
|
request,
|
||||||
@ -240,7 +235,7 @@ export const executeWebhook =
|
|||||||
}
|
}
|
||||||
console.error(error)
|
console.error(error)
|
||||||
await saveErrorLog({
|
await saveErrorLog({
|
||||||
resultId,
|
resultId: result.id,
|
||||||
message: 'Webhook failed to execute',
|
message: 'Webhook failed to execute',
|
||||||
details: {
|
details: {
|
||||||
request,
|
request,
|
||||||
@ -258,20 +253,20 @@ const getBodyContent =
|
|||||||
) =>
|
) =>
|
||||||
async ({
|
async ({
|
||||||
body,
|
body,
|
||||||
resultValues,
|
result,
|
||||||
groupId,
|
groupId,
|
||||||
variables,
|
variables,
|
||||||
}: {
|
}: {
|
||||||
body?: string | null
|
body?: string | null
|
||||||
resultValues?: ResultValues
|
result?: ResultInSession
|
||||||
groupId: string
|
groupId: string
|
||||||
variables: Variable[]
|
variables: Variable[]
|
||||||
}): Promise<string | undefined> => {
|
}): Promise<string | undefined> => {
|
||||||
if (!body) return
|
if (!body) return
|
||||||
return body === '{{state}}'
|
return body === '{{state}}'
|
||||||
? JSON.stringify(
|
? JSON.stringify(
|
||||||
resultValues
|
result
|
||||||
? parseAnswers(typebot, linkedTypebots)(resultValues)
|
? parseAnswers(typebot, linkedTypebots)(result)
|
||||||
: await parseSampleResult(typebot, linkedTypebots)(
|
: await parseSampleResult(typebot, linkedTypebots)(
|
||||||
groupId,
|
groupId,
|
||||||
variables
|
variables
|
||||||
|
@ -29,7 +29,7 @@ export const parseSampleResult =
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
message: 'This is a sample result, it has been generated ⬇️',
|
message: 'This is a sample result, it has been generated ⬇️',
|
||||||
'Submitted at': new Date().toISOString(),
|
submittedAt: new Date().toISOString(),
|
||||||
...parseResultSample(linkedInputBlocks, header, variables),
|
...parseResultSample(linkedInputBlocks, header, variables),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,8 @@ const getLinkedTypebot = async (
|
|||||||
state: SessionState,
|
state: SessionState,
|
||||||
typebotId: string
|
typebotId: string
|
||||||
): Promise<TypebotInSession | null> => {
|
): Promise<TypebotInSession | null> => {
|
||||||
const { typebot, isPreview } = state
|
const { typebot, result } = state
|
||||||
|
const isPreview = !result.id
|
||||||
if (typebotId === 'current') return typebot
|
if (typebotId === 'current') return typebot
|
||||||
const availableTypebots =
|
const availableTypebots =
|
||||||
'linkedTypebots' in state
|
'linkedTypebots' in state
|
||||||
@ -123,12 +124,12 @@ const getLinkedTypebot = async (
|
|||||||
: [typebot]
|
: [typebot]
|
||||||
const linkedTypebot =
|
const linkedTypebot =
|
||||||
availableTypebots.find(byId(typebotId)) ??
|
availableTypebots.find(byId(typebotId)) ??
|
||||||
(await fetchTypebot({ isPreview }, typebotId))
|
(await fetchTypebot(isPreview, typebotId))
|
||||||
return linkedTypebot
|
return linkedTypebot
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchTypebot = async (
|
const fetchTypebot = async (
|
||||||
{ isPreview }: Pick<SessionState, 'isPreview'>,
|
isPreview: boolean,
|
||||||
typebotId: string
|
typebotId: string
|
||||||
): Promise<TypebotInSession | null> => {
|
): Promise<TypebotInSession | null> => {
|
||||||
if (isPreview) {
|
if (isPreview) {
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
ChatReply,
|
ChatReply,
|
||||||
chatReplySchema,
|
chatReplySchema,
|
||||||
ChatSession,
|
ChatSession,
|
||||||
Result,
|
ResultInSession,
|
||||||
sendMessageInputSchema,
|
sendMessageInputSchema,
|
||||||
SessionState,
|
SessionState,
|
||||||
StartParams,
|
StartParams,
|
||||||
@ -85,7 +85,11 @@ export const sendMessageProcedure = publicProcedure
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!input && session.state.result?.hasStarted)
|
if (
|
||||||
|
!input &&
|
||||||
|
session.state.result.answers.length > 0 &&
|
||||||
|
session.state.result.id
|
||||||
|
)
|
||||||
await setResultAsCompleted(session.state.result.id)
|
await setResultAsCompleted(session.state.result.id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -106,9 +110,6 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
|
|||||||
message: 'No typebot provided in startParams',
|
message: 'No typebot provided in startParams',
|
||||||
})
|
})
|
||||||
|
|
||||||
const isPreview =
|
|
||||||
startParams?.isPreview || typeof startParams?.typebot !== 'string'
|
|
||||||
|
|
||||||
const typebot = await getTypebot(startParams, userId)
|
const typebot = await getTypebot(startParams, userId)
|
||||||
|
|
||||||
const prefilledVariables = startParams.prefilledVariables
|
const prefilledVariables = startParams.prefilledVariables
|
||||||
@ -117,8 +118,8 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
|
|||||||
|
|
||||||
const result = await getResult({
|
const result = await getResult({
|
||||||
...startParams,
|
...startParams,
|
||||||
isPreview,
|
isPreview: startParams.isPreview || typeof startParams.typebot !== 'string',
|
||||||
typebot: typebot.id,
|
typebotId: typebot.id,
|
||||||
prefilledVariables,
|
prefilledVariables,
|
||||||
isNewResultOnRefreshEnabled:
|
isNewResultOnRefreshEnabled:
|
||||||
typebot.settings.general.isNewResultOnRefreshEnabled ?? false,
|
typebot.settings.general.isNewResultOnRefreshEnabled ?? false,
|
||||||
@ -140,10 +141,11 @@ const startSession = async (startParams?: StartParams, userId?: string) => {
|
|||||||
typebots: [],
|
typebots: [],
|
||||||
queue: [],
|
queue: [],
|
||||||
},
|
},
|
||||||
result: result
|
result: {
|
||||||
? { id: result.id, variables: result.variables, hasStarted: false }
|
id: result?.id,
|
||||||
: undefined,
|
variables: result?.variables ?? [],
|
||||||
isPreview,
|
answers: result?.answers ?? [],
|
||||||
|
},
|
||||||
currentTypebotId: typebot.id,
|
currentTypebotId: typebot.id,
|
||||||
dynamicTheme: parseDynamicThemeInState(typebot.theme),
|
dynamicTheme: parseDynamicThemeInState(typebot.theme),
|
||||||
}
|
}
|
||||||
@ -297,20 +299,21 @@ const getTypebot = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getResult = async ({
|
const getResult = async ({
|
||||||
typebot,
|
typebotId,
|
||||||
isPreview,
|
isPreview,
|
||||||
resultId,
|
resultId,
|
||||||
prefilledVariables,
|
prefilledVariables,
|
||||||
isNewResultOnRefreshEnabled,
|
isNewResultOnRefreshEnabled,
|
||||||
}: Pick<StartParams, 'isPreview' | 'resultId' | 'typebot'> & {
|
}: Pick<StartParams, 'isPreview' | 'resultId'> & {
|
||||||
|
typebotId: string
|
||||||
prefilledVariables: Variable[]
|
prefilledVariables: Variable[]
|
||||||
isNewResultOnRefreshEnabled: boolean
|
isNewResultOnRefreshEnabled: boolean
|
||||||
}) => {
|
}) => {
|
||||||
if (isPreview || typeof typebot !== 'string') return
|
if (isPreview) return
|
||||||
const select = {
|
const select = {
|
||||||
id: true,
|
id: true,
|
||||||
variables: true,
|
variables: true,
|
||||||
hasStarted: true,
|
answers: { select: { blockId: true, variableId: true, content: true } },
|
||||||
} satisfies Prisma.ResultSelect
|
} satisfies Prisma.ResultSelect
|
||||||
|
|
||||||
const existingResult =
|
const existingResult =
|
||||||
@ -318,7 +321,7 @@ const getResult = async ({
|
|||||||
? ((await prisma.result.findFirst({
|
? ((await prisma.result.findFirst({
|
||||||
where: { id: resultId },
|
where: { id: resultId },
|
||||||
select,
|
select,
|
||||||
})) as Pick<Result, 'id' | 'variables' | 'hasStarted'>)
|
})) as ResultInSession)
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
if (existingResult) {
|
if (existingResult) {
|
||||||
@ -344,19 +347,19 @@ const getResult = async ({
|
|||||||
return {
|
return {
|
||||||
id: existingResult.id,
|
id: existingResult.id,
|
||||||
variables: updatedResult.variables,
|
variables: updatedResult.variables,
|
||||||
hasStarted: existingResult.hasStarted,
|
answers: existingResult.answers,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return (await prisma.result.create({
|
return (await prisma.result.create({
|
||||||
data: {
|
data: {
|
||||||
isCompleted: false,
|
isCompleted: false,
|
||||||
typebotId: typebot,
|
typebotId,
|
||||||
variables: prefilledVariables.filter((variable) =>
|
variables: prefilledVariables.filter((variable) =>
|
||||||
isDefined(variable.value)
|
isDefined(variable.value)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
select,
|
select,
|
||||||
})) as Pick<Result, 'id' | 'variables' | 'hasStarted'>
|
})) as ResultInSession
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import { validateUrl } from '@/features/blocks/inputs/url/api'
|
|||||||
import { parseVariables, updateVariables } from '@/features/variables'
|
import { parseVariables, updateVariables } from '@/features/variables'
|
||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { TRPCError } from '@trpc/server'
|
import { TRPCError } from '@trpc/server'
|
||||||
|
import { Prisma } from 'db'
|
||||||
import got from 'got'
|
import got from 'got'
|
||||||
import {
|
import {
|
||||||
Block,
|
Block,
|
||||||
@ -15,6 +16,7 @@ import {
|
|||||||
ChatReply,
|
ChatReply,
|
||||||
InputBlock,
|
InputBlock,
|
||||||
InputBlockType,
|
InputBlockType,
|
||||||
|
ResultInSession,
|
||||||
SessionState,
|
SessionState,
|
||||||
} from 'models'
|
} from 'models'
|
||||||
import { isInputBlock, isNotDefined } from 'utils'
|
import { isInputBlock, isNotDefined } from 'utils'
|
||||||
@ -86,17 +88,9 @@ const processAndSaveAnswer =
|
|||||||
(state: SessionState, block: InputBlock) =>
|
(state: SessionState, block: InputBlock) =>
|
||||||
async (reply: string | null): Promise<SessionState> => {
|
async (reply: string | null): Promise<SessionState> => {
|
||||||
if (!reply) return state
|
if (!reply) return state
|
||||||
if (!state.isPreview && state.result) {
|
let newState = await saveAnswer(state, block)(reply)
|
||||||
await saveAnswer(state.result.id, block)(reply)
|
newState = await saveVariableValueIfAny(newState, block)(reply)
|
||||||
if (!state.result.hasStarted) await setResultAsStarted(state.result.id)
|
return newState
|
||||||
}
|
|
||||||
const newState = await saveVariableValueIfAny(state, block)(reply)
|
|
||||||
return {
|
|
||||||
...newState,
|
|
||||||
result: newState.result
|
|
||||||
? { ...newState.result, hasStarted: true }
|
|
||||||
: undefined,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveVariableValueIfAny =
|
const saveVariableValueIfAny =
|
||||||
@ -115,13 +109,6 @@ const saveVariableValueIfAny =
|
|||||||
return newSessionState
|
return newSessionState
|
||||||
}
|
}
|
||||||
|
|
||||||
const setResultAsStarted = async (resultId: string) => {
|
|
||||||
await prisma.result.update({
|
|
||||||
where: { id: resultId },
|
|
||||||
data: { hasStarted: true },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const setResultAsCompleted = async (resultId: string) => {
|
export const setResultAsCompleted = async (resultId: string) => {
|
||||||
await prisma.result.update({
|
await prisma.result.update({
|
||||||
where: { id: resultId },
|
where: { id: resultId },
|
||||||
@ -152,31 +139,65 @@ const parseRetryMessage = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const saveAnswer =
|
const saveAnswer =
|
||||||
(resultId: string, block: InputBlock) => async (reply: string) => {
|
(state: SessionState, block: InputBlock) =>
|
||||||
|
async (reply: string): Promise<SessionState> => {
|
||||||
|
const resultId = state.result?.id
|
||||||
const answer = {
|
const answer = {
|
||||||
resultId: resultId,
|
resultId,
|
||||||
blockId: block.id,
|
blockId: block.id,
|
||||||
groupId: block.groupId,
|
groupId: block.groupId,
|
||||||
content: reply,
|
content: reply,
|
||||||
variableId: block.options.variableId,
|
variableId: block.options.variableId,
|
||||||
storageUsed: 0,
|
storageUsed: 0,
|
||||||
}
|
}
|
||||||
|
if (state.result.answers.length === 0 && state.result.id)
|
||||||
|
await setResultAsStarted(state.result.id)
|
||||||
|
|
||||||
|
const newSessionState = setNewAnswerInState(state)({
|
||||||
|
blockId: block.id,
|
||||||
|
variableId: block.options.variableId ?? null,
|
||||||
|
content: reply,
|
||||||
|
})
|
||||||
|
|
||||||
if (reply.includes('http') && block.type === InputBlockType.FILE) {
|
if (reply.includes('http') && block.type === InputBlockType.FILE) {
|
||||||
answer.storageUsed = await computeStorageUsed(reply)
|
answer.storageUsed = await computeStorageUsed(reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.answer.upsert({
|
if (resultId)
|
||||||
where: {
|
await prisma.answer.upsert({
|
||||||
resultId_blockId_groupId: {
|
where: {
|
||||||
resultId,
|
resultId_blockId_groupId: {
|
||||||
groupId: block.groupId,
|
resultId,
|
||||||
blockId: block.id,
|
groupId: block.groupId,
|
||||||
|
blockId: block.id,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
create: answer as Prisma.AnswerUncheckedCreateInput,
|
||||||
|
update: answer,
|
||||||
|
})
|
||||||
|
return newSessionState
|
||||||
|
}
|
||||||
|
|
||||||
|
const setResultAsStarted = async (resultId: string) => {
|
||||||
|
await prisma.result.update({
|
||||||
|
where: { id: resultId },
|
||||||
|
data: { hasStarted: true },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setNewAnswerInState =
|
||||||
|
(state: SessionState) => (newAnswer: ResultInSession['answers'][number]) => {
|
||||||
|
const newAnswers = state.result.answers
|
||||||
|
.filter((answer) => answer.blockId !== newAnswer.blockId)
|
||||||
|
.concat(newAnswer)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
result: {
|
||||||
|
...state.result,
|
||||||
|
answers: newAnswers,
|
||||||
},
|
},
|
||||||
create: answer,
|
} satisfies SessionState
|
||||||
update: answer,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const computeStorageUsed = async (reply: string) => {
|
const computeStorageUsed = async (reply: string) => {
|
||||||
|
@ -113,7 +113,7 @@ export const executeGroup =
|
|||||||
}
|
}
|
||||||
|
|
||||||
const computeRuntimeOptions =
|
const computeRuntimeOptions =
|
||||||
(state: Pick<SessionState, 'isPreview' | 'typebot'>) =>
|
(state: Pick<SessionState, 'result' | 'typebot'>) =>
|
||||||
(block: InputBlock): Promise<RuntimeOptions> | undefined => {
|
(block: InputBlock): Promise<RuntimeOptions> | undefined => {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case InputBlockType.PAYMENT: {
|
case InputBlockType.PAYMENT: {
|
||||||
@ -158,7 +158,7 @@ const parseBubbleBlock =
|
|||||||
}
|
}
|
||||||
|
|
||||||
const injectVariablesValueInBlock =
|
const injectVariablesValueInBlock =
|
||||||
(state: Pick<SessionState, 'isPreview' | 'typebot'>) =>
|
(state: Pick<SessionState, 'result' | 'typebot'>) =>
|
||||||
async (block: InputBlock): Promise<ChatReply['input']> => {
|
async (block: InputBlock): Promise<ChatReply['input']> => {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case InputBlockType.CHOICE: {
|
case InputBlockType.CHOICE: {
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export * from './utils'
|
|
@ -1,8 +0,0 @@
|
|||||||
import prisma from '@/lib/prisma'
|
|
||||||
import { ResultValues } from 'models'
|
|
||||||
|
|
||||||
export const getResultValues = async (resultId: string) =>
|
|
||||||
(await prisma.result.findUnique({
|
|
||||||
where: { id: resultId },
|
|
||||||
include: { answers: true },
|
|
||||||
})) as ResultValues | null
|
|
@ -1 +0,0 @@
|
|||||||
export * from './getResultValues'
|
|
@ -157,12 +157,10 @@ export const updateVariables =
|
|||||||
...state.typebot,
|
...state.typebot,
|
||||||
variables: updateTypebotVariables(state)(newVariables),
|
variables: updateTypebotVariables(state)(newVariables),
|
||||||
},
|
},
|
||||||
result: state.result
|
result: {
|
||||||
? {
|
...state.result,
|
||||||
...state.result,
|
variables: await updateResultVariables(state)(newVariables),
|
||||||
variables: await updateResultVariables(state)(newVariables),
|
},
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateResultVariables =
|
const updateResultVariables =
|
||||||
@ -170,7 +168,6 @@ const updateResultVariables =
|
|||||||
async (
|
async (
|
||||||
newVariables: VariableWithUnknowValue[]
|
newVariables: VariableWithUnknowValue[]
|
||||||
): Promise<VariableWithValue[]> => {
|
): Promise<VariableWithValue[]> => {
|
||||||
if (!result) return []
|
|
||||||
const serializedNewVariables = newVariables.map((variable) => ({
|
const serializedNewVariables = newVariables.map((variable) => ({
|
||||||
...variable,
|
...variable,
|
||||||
value: Array.isArray(variable.value)
|
value: Array.isArray(variable.value)
|
||||||
@ -187,14 +184,15 @@ const updateResultVariables =
|
|||||||
...serializedNewVariables,
|
...serializedNewVariables,
|
||||||
].filter((variable) => isDefined(variable.value)) as VariableWithValue[]
|
].filter((variable) => isDefined(variable.value)) as VariableWithValue[]
|
||||||
|
|
||||||
await prisma.result.update({
|
if (result.id)
|
||||||
where: {
|
await prisma.result.update({
|
||||||
id: result.id,
|
where: {
|
||||||
},
|
id: result.id,
|
||||||
data: {
|
},
|
||||||
variables: updatedVariables,
|
data: {
|
||||||
},
|
variables: updatedVariables,
|
||||||
})
|
},
|
||||||
|
})
|
||||||
|
|
||||||
return updatedVariables
|
return updatedVariables
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { safeStringify } from '@/features/variables'
|
import { safeStringify } from '@/features/variables'
|
||||||
import {
|
import {
|
||||||
AnswerInput,
|
AnswerInput,
|
||||||
ResultValues,
|
ResultValuesInput,
|
||||||
Variable,
|
Variable,
|
||||||
VariableWithUnknowValue,
|
VariableWithUnknowValue,
|
||||||
VariableWithValue,
|
VariableWithValue,
|
||||||
@ -11,7 +11,7 @@ import { isDefined } from 'utils'
|
|||||||
|
|
||||||
const answersContext = createContext<{
|
const answersContext = createContext<{
|
||||||
resultId?: string
|
resultId?: string
|
||||||
resultValues: ResultValues
|
resultValues: ResultValuesInput
|
||||||
addAnswer: (
|
addAnswer: (
|
||||||
existingVariables: Variable[]
|
existingVariables: Variable[]
|
||||||
) => (
|
) => (
|
||||||
@ -35,7 +35,7 @@ export const AnswersProvider = ({
|
|||||||
onVariablesUpdated?: (variables: VariableWithValue[]) => void
|
onVariablesUpdated?: (variables: VariableWithValue[]) => void
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}) => {
|
}) => {
|
||||||
const [resultValues, setResultValues] = useState<ResultValues>({
|
const [resultValues, setResultValues] = useState<ResultValuesInput>({
|
||||||
answers: [],
|
answers: [],
|
||||||
variables: [],
|
variables: [],
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
|
@ -3,7 +3,7 @@ import {
|
|||||||
Edge,
|
Edge,
|
||||||
Group,
|
Group,
|
||||||
PublicTypebot,
|
PublicTypebot,
|
||||||
ResultValues,
|
ResultValuesInput,
|
||||||
Typebot,
|
Typebot,
|
||||||
Variable,
|
Variable,
|
||||||
VariableWithUnknowValue,
|
VariableWithUnknowValue,
|
||||||
@ -45,7 +45,7 @@ export type IntegrationState = {
|
|||||||
blockId: string
|
blockId: string
|
||||||
isPreview: boolean
|
isPreview: boolean
|
||||||
variables: Variable[]
|
variables: Variable[]
|
||||||
resultValues: ResultValues
|
resultValues: ResultValuesInput
|
||||||
groups: Group[]
|
groups: Group[]
|
||||||
resultId?: string
|
resultId?: string
|
||||||
parentTypebotIds: string[]
|
parentTypebotIds: string[]
|
||||||
|
@ -6,8 +6,6 @@ import {
|
|||||||
redirectOptionsSchema,
|
redirectOptionsSchema,
|
||||||
} from './blocks'
|
} from './blocks'
|
||||||
import { publicTypebotSchema } from './publicTypebot'
|
import { publicTypebotSchema } from './publicTypebot'
|
||||||
import { ChatSession as ChatSessionPrisma } from 'db'
|
|
||||||
import { schemaForType } from './utils'
|
|
||||||
import { logSchema, resultSchema } from './result'
|
import { logSchema, resultSchema } from './result'
|
||||||
import { typebotSchema } from './typebot'
|
import { typebotSchema } from './typebot'
|
||||||
import {
|
import {
|
||||||
@ -18,6 +16,7 @@ import {
|
|||||||
audioBubbleContentSchema,
|
audioBubbleContentSchema,
|
||||||
embedBubbleContentSchema,
|
embedBubbleContentSchema,
|
||||||
} from './blocks/bubbles'
|
} from './blocks/bubbles'
|
||||||
|
import { answerSchema } from './answer'
|
||||||
|
|
||||||
const typebotInSessionStateSchema = publicTypebotSchema.pick({
|
const typebotInSessionStateSchema = publicTypebotSchema.pick({
|
||||||
id: true,
|
id: true,
|
||||||
@ -31,6 +30,23 @@ const dynamicThemeSchema = z.object({
|
|||||||
guestAvatarUrl: z.string().optional(),
|
guestAvatarUrl: z.string().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const answerInSessionStateSchema = answerSchema.pick({
|
||||||
|
content: true,
|
||||||
|
blockId: true,
|
||||||
|
variableId: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const resultInSessionStateSchema = resultSchema
|
||||||
|
.pick({
|
||||||
|
variables: true,
|
||||||
|
})
|
||||||
|
.and(
|
||||||
|
z.object({
|
||||||
|
answers: z.array(answerInSessionStateSchema),
|
||||||
|
id: z.string().optional(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
export const sessionStateSchema = z.object({
|
export const sessionStateSchema = z.object({
|
||||||
typebot: typebotInSessionStateSchema,
|
typebot: typebotInSessionStateSchema,
|
||||||
dynamicTheme: dynamicThemeSchema.optional(),
|
dynamicTheme: dynamicThemeSchema.optional(),
|
||||||
@ -39,10 +55,7 @@ export const sessionStateSchema = z.object({
|
|||||||
queue: z.array(z.object({ edgeId: z.string(), typebotId: z.string() })),
|
queue: z.array(z.object({ edgeId: z.string(), typebotId: z.string() })),
|
||||||
}),
|
}),
|
||||||
currentTypebotId: z.string(),
|
currentTypebotId: z.string(),
|
||||||
result: resultSchema
|
result: resultInSessionStateSchema,
|
||||||
.pick({ id: true, variables: true, hasStarted: true })
|
|
||||||
.optional(),
|
|
||||||
isPreview: z.boolean(),
|
|
||||||
currentBlock: z
|
currentBlock: z
|
||||||
.object({
|
.object({
|
||||||
blockId: z.string(),
|
blockId: z.string(),
|
||||||
@ -51,14 +64,12 @@ export const sessionStateSchema = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const chatSessionSchema = schemaForType<ChatSessionPrisma>()(
|
const chatSessionSchema = z.object({
|
||||||
z.object({
|
id: z.string(),
|
||||||
id: z.string(),
|
createdAt: z.date(),
|
||||||
createdAt: z.date(),
|
updatedAt: z.date(),
|
||||||
updatedAt: z.date(),
|
state: sessionStateSchema,
|
||||||
state: sessionStateSchema,
|
})
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const textMessageSchema = z.object({
|
const textMessageSchema = z.object({
|
||||||
type: z.enum([BubbleBlockType.TEXT]),
|
type: z.enum([BubbleBlockType.TEXT]),
|
||||||
@ -234,6 +245,7 @@ export const chatReplySchema = z.object({
|
|||||||
export type ChatSession = z.infer<typeof chatSessionSchema>
|
export type ChatSession = z.infer<typeof chatSessionSchema>
|
||||||
export type SessionState = z.infer<typeof sessionStateSchema>
|
export type SessionState = z.infer<typeof sessionStateSchema>
|
||||||
export type TypebotInSession = z.infer<typeof typebotInSessionStateSchema>
|
export type TypebotInSession = z.infer<typeof typebotInSessionStateSchema>
|
||||||
|
export type ResultInSession = z.infer<typeof resultInSessionStateSchema>
|
||||||
export type ChatReply = z.infer<typeof chatReplySchema>
|
export type ChatReply = z.infer<typeof chatReplySchema>
|
||||||
export type ChatMessage = z.infer<typeof chatMessageSchema>
|
export type ChatMessage = z.infer<typeof chatMessageSchema>
|
||||||
export type SendMessageInput = z.infer<typeof sendMessageInputSchema>
|
export type SendMessageInput = z.infer<typeof sendMessageInputSchema>
|
||||||
|
@ -48,11 +48,16 @@ export type ResultWithAnswersInput = z.infer<
|
|||||||
>
|
>
|
||||||
export type Log = z.infer<typeof logSchema>
|
export type Log = z.infer<typeof logSchema>
|
||||||
|
|
||||||
export type ResultValues = Pick<
|
export type ResultValuesInput = Pick<
|
||||||
ResultWithAnswersInput,
|
ResultWithAnswersInput,
|
||||||
'answers' | 'createdAt' | 'variables'
|
'answers' | 'createdAt' | 'variables'
|
||||||
>
|
>
|
||||||
|
|
||||||
|
export type ResultValues = Pick<
|
||||||
|
ResultWithAnswers,
|
||||||
|
'answers' | 'createdAt' | 'variables'
|
||||||
|
>
|
||||||
|
|
||||||
export type ResultHeaderCell = {
|
export type ResultHeaderCell = {
|
||||||
id: string
|
id: string
|
||||||
label: string
|
label: string
|
||||||
|
@ -6,7 +6,6 @@ const generalSettings = z.object({
|
|||||||
isInputPrefillEnabled: z.boolean().optional(),
|
isInputPrefillEnabled: z.boolean().optional(),
|
||||||
isHideQueryParamsEnabled: z.boolean().optional(),
|
isHideQueryParamsEnabled: z.boolean().optional(),
|
||||||
isNewResultOnRefreshEnabled: z.boolean().optional(),
|
isNewResultOnRefreshEnabled: z.boolean().optional(),
|
||||||
isResultSavingEnabled: z.boolean().optional(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const typingEmulation = z.object({
|
const typingEmulation = z.object({
|
||||||
@ -36,7 +35,6 @@ export const defaultSettings: Settings = {
|
|||||||
isNewResultOnRefreshEnabled: true,
|
isNewResultOnRefreshEnabled: true,
|
||||||
isInputPrefillEnabled: true,
|
isInputPrefillEnabled: true,
|
||||||
isHideQueryParamsEnabled: true,
|
isHideQueryParamsEnabled: true,
|
||||||
isResultSavingEnabled: true,
|
|
||||||
},
|
},
|
||||||
typingEmulation: { enabled: true, speed: 300, maxDelay: 1.5 },
|
typingEmulation: { enabled: true, speed: 300, maxDelay: 1.5 },
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -6,9 +6,9 @@ import {
|
|||||||
Answer,
|
Answer,
|
||||||
VariableWithValue,
|
VariableWithValue,
|
||||||
Typebot,
|
Typebot,
|
||||||
ResultWithAnswersInput,
|
|
||||||
ResultWithAnswers,
|
ResultWithAnswers,
|
||||||
InputBlockType,
|
InputBlockType,
|
||||||
|
ResultInSession,
|
||||||
} from 'models'
|
} from 'models'
|
||||||
import { isInputBlock, isDefined, byId, isNotEmpty } from './utils'
|
import { isInputBlock, isDefined, byId, isNotEmpty } from './utils'
|
||||||
|
|
||||||
@ -218,16 +218,16 @@ export const parseAnswers =
|
|||||||
createdAt,
|
createdAt,
|
||||||
answers,
|
answers,
|
||||||
variables: resultVariables,
|
variables: resultVariables,
|
||||||
}: Pick<ResultWithAnswersInput, 'answers' | 'variables'> & {
|
}: Omit<ResultInSession, 'hasStarted'> & { createdAt?: Date | string }): {
|
||||||
// TODO: remove once we are using 100% tRPC
|
|
||||||
createdAt: Date | string
|
|
||||||
}): {
|
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
} => {
|
} => {
|
||||||
const header = parseResultHeader(typebot, linkedTypebots)
|
const header = parseResultHeader(typebot, linkedTypebots)
|
||||||
return {
|
return {
|
||||||
submittedAt:
|
submittedAt: !createdAt
|
||||||
typeof createdAt === 'string' ? createdAt : createdAt.toISOString(),
|
? new Date().toISOString()
|
||||||
|
: typeof createdAt === 'string'
|
||||||
|
? createdAt
|
||||||
|
: createdAt.toISOString(),
|
||||||
...[...answers, ...resultVariables].reduce<{
|
...[...answers, ...resultVariables].reduce<{
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
}>((o, answerOrVariable) => {
|
}>((o, answerOrVariable) => {
|
||||||
|
Reference in New Issue
Block a user