2
0

♻️ Replace schemas with merge and discriminated unions

Closes #374
This commit is contained in:
Baptiste Arnaud
2023-03-14 16:42:12 +01:00
parent ff09814ead
commit d154c4e2f2
52 changed files with 3217 additions and 4328 deletions

View File

@@ -14,7 +14,7 @@ import {
GoogleSheetsUpdateRowOptions, GoogleSheetsUpdateRowOptions,
} from 'models' } from 'models'
import React, { useMemo } from 'react' import React, { useMemo } from 'react'
import { isDefined, omit } from 'utils' import { isDefined } from 'utils'
import { SheetsDropdown } from './SheetsDropdown' import { SheetsDropdown } from './SheetsDropdown'
import { SpreadsheetsDropdown } from './SpreadsheetDropdown' import { SpreadsheetsDropdown } from './SpreadsheetDropdown'
import { CellWithValueStack } from './CellWithValueStack' import { CellWithValueStack } from './CellWithValueStack'
@@ -50,8 +50,11 @@ export const GoogleSheetsSettingsBody = ({
() => sheets?.find((s) => s.id === options?.sheetId), () => sheets?.find((s) => s.id === options?.sheetId),
[sheets, options?.sheetId] [sheets, options?.sheetId]
) )
const handleCredentialsIdChange = (credentialsId?: string) => const handleCredentialsIdChange = (credentialsId: string | undefined) =>
onOptionsChange({ ...omit(options, 'credentialsId'), credentialsId }) onOptionsChange({
...options,
credentialsId,
})
const handleSpreadsheetIdChange = (spreadsheetId: string | undefined) => const handleSpreadsheetIdChange = (spreadsheetId: string | undefined) =>
onOptionsChange({ ...options, spreadsheetId }) onOptionsChange({ ...options, spreadsheetId })
const handleSheetIdChange = (sheetId: string | undefined) => const handleSheetIdChange = (sheetId: string | undefined) =>
@@ -137,7 +140,7 @@ export const GoogleSheetsSettingsBody = ({
/> />
</> </>
)} )}
{'action' in options && ( {options.action && (
<ActionOptions <ActionOptions
options={options} options={options}
sheet={sheet} sheet={sheet}

View File

@@ -26,7 +26,7 @@ export const listTypebotsProcedure = authenticatedProcedure
icon: true, icon: true,
id: true, id: true,
}) })
.and(z.object({ publishedTypebotId: z.string().optional() })) .merge(z.object({ publishedTypebotId: z.string().optional() }))
), ),
}) })
) )

View File

@@ -1033,38 +1033,28 @@
"typebots": { "typebots": {
"type": "array", "type": "array",
"items": { "items": {
"allOf": [ "type": "object",
{ "properties": {
"type": "object", "name": {
"properties": { "type": "string"
"name": {
"type": "string"
},
"icon": {
"type": "string",
"nullable": true
},
"id": {
"type": "string"
}
},
"required": [
"name",
"icon",
"id"
],
"additionalProperties": false
}, },
{ "icon": {
"type": "object", "type": "string",
"properties": { "nullable": true
"publishedTypebotId": { },
"type": "string" "id": {
} "type": "string"
}, },
"additionalProperties": false "publishedTypebotId": {
"type": "string"
} }
] },
"required": [
"name",
"icon",
"id"
],
"additionalProperties": false
} }
} }
}, },
@@ -1444,128 +1434,116 @@
"results": { "results": {
"type": "array", "type": "array",
"items": { "items": {
"allOf": [ "type": "object",
{ "properties": {
"type": "object", "id": {
"properties": { "type": "string"
"id": {
"type": "string"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"typebotId": {
"type": "string"
},
"variables": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"required": [
"id",
"name",
"value"
],
"additionalProperties": false
}
},
"isCompleted": {
"type": "boolean"
},
"hasStarted": {
"type": "boolean",
"nullable": true
},
"isArchived": {
"type": "boolean",
"nullable": true
}
},
"required": [
"id",
"createdAt",
"typebotId",
"variables",
"isCompleted",
"hasStarted",
"isArchived"
],
"additionalProperties": false
}, },
{ "createdAt": {
"type": "object", "type": "string",
"properties": { "format": "date-time"
"answers": { },
"type": "array", "typebotId": {
"items": { "type": "string"
"type": "object", },
"properties": { "variables": {
"createdAt": { "type": "array",
"type": "string", "items": {
"format": "date-time" "type": "object",
}, "properties": {
"resultId": { "id": {
"type": "string"
},
"name": {
"type": "string"
},
"value": {
"anyOf": [
{
"type": "string" "type": "string"
}, },
"blockId": { {
"type": "string" "type": "array",
}, "items": {
"groupId": { "type": "string"
"type": "string" }
},
"variableId": {
"type": "string",
"nullable": true
},
"content": {
"type": "string"
},
"storageUsed": {
"type": "number",
"nullable": true
} }
}, ]
"required": [
"createdAt",
"resultId",
"blockId",
"groupId",
"variableId",
"content",
"storageUsed"
],
"additionalProperties": false
} }
} },
}, "required": [
"required": [ "id",
"answers" "name",
], "value"
"additionalProperties": false ],
"additionalProperties": false
}
},
"isCompleted": {
"type": "boolean"
},
"hasStarted": {
"type": "boolean",
"nullable": true
},
"isArchived": {
"type": "boolean",
"nullable": true
},
"answers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"createdAt": {
"type": "string",
"format": "date-time"
},
"resultId": {
"type": "string"
},
"blockId": {
"type": "string"
},
"groupId": {
"type": "string"
},
"variableId": {
"type": "string",
"nullable": true
},
"content": {
"type": "string"
},
"storageUsed": {
"type": "number",
"nullable": true
}
},
"required": [
"createdAt",
"resultId",
"blockId",
"groupId",
"variableId",
"content",
"storageUsed"
],
"additionalProperties": false
}
} }
] },
"required": [
"id",
"createdAt",
"typebotId",
"variables",
"isCompleted",
"hasStarted",
"isArchived",
"answers"
],
"additionalProperties": false
} }
}, },
"nextCursor": { "nextCursor": {

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,9 @@ export const executeGoogleSheetBlock = async (
state: SessionState, state: SessionState,
block: GoogleSheetsBlock block: GoogleSheetsBlock
): Promise<ExecuteIntegrationResponse> => { ): Promise<ExecuteIntegrationResponse> => {
if (!('action' in block.options)) const action = block.options.action
return { outgoingEdgeId: block.outgoingEdgeId } if (!action) return { outgoingEdgeId: block.outgoingEdgeId }
switch (block.options.action) { switch (action) {
case GoogleSheetsAction.INSERT_ROW: case GoogleSheetsAction.INSERT_ROW:
return insertRow(state, { return insertRow(state, {
options: block.options, options: block.options,

View File

@@ -18,7 +18,7 @@ export const answerInputSchema = answerSchema
variableId: true, variableId: true,
storageUsed: true, storageUsed: true,
}) })
.and( .merge(
z.object({ z.object({
variableId: z.string().nullish(), variableId: z.string().nullish(),
storageUsed: z.number().nullish(), storageUsed: z.number().nullish(),

View File

@@ -6,7 +6,7 @@ export const audioBubbleContentSchema = z.object({
url: z.string().optional(), url: z.string().optional(),
}) })
export const audioBubbleBlockSchema = blockBaseSchema.and( export const audioBubbleBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([BubbleBlockType.AUDIO]), type: z.enum([BubbleBlockType.AUDIO]),
content: audioBubbleContentSchema, content: audioBubbleContentSchema,

View File

@@ -8,7 +8,7 @@ export const embedBubbleContentSchema = z.object({
height: z.number().or(variableStringSchema), height: z.number().or(variableStringSchema),
}) })
export const embedBubbleBlockSchema = blockBaseSchema.and( export const embedBubbleBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([BubbleBlockType.EMBED]), type: z.enum([BubbleBlockType.EMBED]),
content: embedBubbleContentSchema, content: embedBubbleContentSchema,

View File

@@ -6,7 +6,7 @@ export const imageBubbleContentSchema = z.object({
url: z.string().optional(), url: z.string().optional(),
}) })
export const imageBubbleBlockSchema = blockBaseSchema.and( export const imageBubbleBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([BubbleBlockType.IMAGE]), type: z.enum([BubbleBlockType.IMAGE]),
content: imageBubbleContentSchema, content: imageBubbleContentSchema,

View File

@@ -1,7 +1,6 @@
export * from './audio' export * from './audio'
export * from './embed' export * from './embed'
export * from './enums'
export * from './image' export * from './image'
export * from './schemas'
export * from './text' export * from './text'
export * from './video' export * from './video'
export * from './enums'

View File

@@ -1,21 +0,0 @@
import { z } from 'zod'
import { audioBubbleBlockSchema, audioBubbleContentSchema } from './audio'
import { embedBubbleContentSchema, embedBubbleBlockSchema } from './embed'
import { imageBubbleContentSchema, imageBubbleBlockSchema } from './image'
import { textBubbleContentSchema, textBubbleBlockSchema } from './text'
import { videoBubbleContentSchema, videoBubbleBlockSchema } from './video'
export const bubbleBlockContentSchema = textBubbleContentSchema
.or(imageBubbleContentSchema)
.or(videoBubbleContentSchema)
.or(embedBubbleContentSchema)
.or(audioBubbleContentSchema)
export const bubbleBlockSchema = textBubbleBlockSchema
.or(imageBubbleBlockSchema)
.or(videoBubbleBlockSchema)
.or(embedBubbleBlockSchema)
.or(audioBubbleBlockSchema)
export type BubbleBlock = z.infer<typeof bubbleBlockSchema>
export type BubbleBlockContent = z.infer<typeof bubbleBlockContentSchema>

View File

@@ -16,7 +16,7 @@ export const textBubbleContentSchema = z.object({
export type TextBubbleContent = z.infer<typeof textBubbleContentSchema> export type TextBubbleContent = z.infer<typeof textBubbleContentSchema>
export const textBubbleBlockSchema = blockBaseSchema.and( export const textBubbleBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([BubbleBlockType.TEXT]), type: z.enum([BubbleBlockType.TEXT]),
content: textBubbleContentSchema, content: textBubbleContentSchema,

View File

@@ -9,7 +9,7 @@ export const videoBubbleContentSchema = z.object({
type: z.nativeEnum(VideoBubbleContentType).optional(), type: z.nativeEnum(VideoBubbleContentType).optional(),
}) })
export const videoBubbleBlockSchema = blockBaseSchema.and( export const videoBubbleBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([BubbleBlockType.VIDEO]), type: z.enum([BubbleBlockType.VIDEO]),
content: videoBubbleContentSchema, content: videoBubbleContentSchema,

View File

@@ -3,5 +3,5 @@ export * from './bubbles'
export * from './inputs' export * from './inputs'
export * from './integrations' export * from './integrations'
export * from './logic' export * from './logic'
export * from './schemas'
export * from './start' export * from './start'
export * from './schemas'

View File

@@ -5,7 +5,7 @@ import { optionBaseSchema, blockBaseSchema } from '../baseSchemas'
import { defaultButtonLabel } from './constants' import { defaultButtonLabel } from './constants'
import { InputBlockType } from './enums' import { InputBlockType } from './enums'
export const choiceInputOptionsSchema = optionBaseSchema.and( export const choiceInputOptionsSchema = optionBaseSchema.merge(
z.object({ z.object({
isMultipleChoice: z.boolean(), isMultipleChoice: z.boolean(),
buttonLabel: z.string(), buttonLabel: z.string(),
@@ -18,14 +18,14 @@ export const defaultChoiceInputOptions: ChoiceInputOptions = {
isMultipleChoice: false, isMultipleChoice: false,
} }
export const buttonItemSchema = itemBaseSchema.and( export const buttonItemSchema = itemBaseSchema.merge(
z.object({ z.object({
type: z.literal(ItemType.BUTTON), type: z.literal(ItemType.BUTTON),
content: z.string().optional(), content: z.string().optional(),
}) })
) )
export const choiceInputSchema = blockBaseSchema.and( export const choiceInputSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([InputBlockType.CHOICE]), type: z.enum([InputBlockType.CHOICE]),
items: z.array(buttonItemSchema), items: z.array(buttonItemSchema),

View File

@@ -3,7 +3,7 @@ import { optionBaseSchema, blockBaseSchema } from '../baseSchemas'
import { defaultButtonLabel } from './constants' import { defaultButtonLabel } from './constants'
import { InputBlockType } from './enums' import { InputBlockType } from './enums'
export const dateInputOptionsSchema = optionBaseSchema.and( export const dateInputOptionsSchema = optionBaseSchema.merge(
z.object({ z.object({
labels: z.object({ labels: z.object({
button: z.string(), button: z.string(),
@@ -15,7 +15,7 @@ export const dateInputOptionsSchema = optionBaseSchema.and(
}) })
) )
export const dateInputSchema = blockBaseSchema.and( export const dateInputSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([InputBlockType.DATE]), type: z.enum([InputBlockType.DATE]),
options: dateInputOptionsSchema, options: dateInputOptionsSchema,

View File

@@ -5,14 +5,14 @@ import { InputBlockType } from './enums'
import { textInputOptionsBaseSchema } from './text' import { textInputOptionsBaseSchema } from './text'
export const emailInputOptionsSchema = optionBaseSchema export const emailInputOptionsSchema = optionBaseSchema
.and(textInputOptionsBaseSchema) .merge(textInputOptionsBaseSchema)
.and( .merge(
z.object({ z.object({
retryMessageContent: z.string(), retryMessageContent: z.string(),
}) })
) )
export const emailInputSchema = blockBaseSchema.and( export const emailInputSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([InputBlockType.EMAIL]), type: z.enum([InputBlockType.EMAIL]),
options: emailInputOptionsSchema, options: emailInputOptionsSchema,

View File

@@ -2,7 +2,7 @@ import { z } from 'zod'
import { optionBaseSchema, blockBaseSchema } from '../baseSchemas' import { optionBaseSchema, blockBaseSchema } from '../baseSchemas'
import { InputBlockType } from './enums' import { InputBlockType } from './enums'
export const fileInputOptionsSchema = optionBaseSchema.and( export const fileInputOptionsSchema = optionBaseSchema.merge(
z.object({ z.object({
isRequired: z.boolean().optional(), isRequired: z.boolean().optional(),
isMultipleAllowed: z.boolean(), isMultipleAllowed: z.boolean(),
@@ -16,7 +16,7 @@ export const fileInputOptionsSchema = optionBaseSchema.and(
}) })
) )
export const fileInputStepSchema = blockBaseSchema.and( export const fileInputStepSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.literal(InputBlockType.FILE), type: z.literal(InputBlockType.FILE),
options: fileInputOptionsSchema, options: fileInputOptionsSchema,

View File

@@ -8,6 +8,5 @@ export * from './number'
export * from './payment' export * from './payment'
export * from './phone' export * from './phone'
export * from './rating' export * from './rating'
export * from './schemas'
export * from './text' export * from './text'
export * from './url' export * from './url'

View File

@@ -5,8 +5,8 @@ import { InputBlockType } from './enums'
import { textInputOptionsBaseSchema } from './text' import { textInputOptionsBaseSchema } from './text'
export const numberInputOptionsSchema = optionBaseSchema export const numberInputOptionsSchema = optionBaseSchema
.and(textInputOptionsBaseSchema) .merge(textInputOptionsBaseSchema)
.and( .merge(
z.object({ z.object({
min: z.number().optional(), min: z.number().optional(),
max: z.number().optional(), max: z.number().optional(),
@@ -14,7 +14,7 @@ export const numberInputOptionsSchema = optionBaseSchema
}) })
) )
export const numberInputSchema = blockBaseSchema.and( export const numberInputSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([InputBlockType.NUMBER]), type: z.enum([InputBlockType.NUMBER]),
options: numberInputOptionsSchema, options: numberInputOptionsSchema,

View File

@@ -14,7 +14,7 @@ export type CreditCardDetails = {
cvc: string cvc: string
} }
export const paymentInputOptionsSchema = optionBaseSchema.and( export const paymentInputOptionsSchema = optionBaseSchema.merge(
z.object({ z.object({
provider: z.nativeEnum(PaymentProvider), provider: z.nativeEnum(PaymentProvider),
labels: z.object({ labels: z.object({
@@ -40,7 +40,7 @@ export const paymentInputRuntimeOptionsSchema = z.object({
publicKey: z.string(), publicKey: z.string(),
}) })
export const paymentInputSchema = blockBaseSchema.and( export const paymentInputSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([InputBlockType.PAYMENT]), type: z.enum([InputBlockType.PAYMENT]),
options: paymentInputOptionsSchema, options: paymentInputOptionsSchema,

View File

@@ -5,15 +5,15 @@ import { InputBlockType } from './enums'
import { textInputOptionsBaseSchema } from './text' import { textInputOptionsBaseSchema } from './text'
export const phoneNumberInputOptionsSchema = optionBaseSchema export const phoneNumberInputOptionsSchema = optionBaseSchema
.and(textInputOptionsBaseSchema) .merge(textInputOptionsBaseSchema)
.and( .merge(
z.object({ z.object({
retryMessageContent: z.string(), retryMessageContent: z.string(),
defaultCountryCode: z.string().optional(), defaultCountryCode: z.string().optional(),
}) })
) )
export const phoneNumberInputBlockSchema = blockBaseSchema.and( export const phoneNumberInputBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([InputBlockType.PHONE]), type: z.enum([InputBlockType.PHONE]),
options: phoneNumberInputOptionsSchema, options: phoneNumberInputOptionsSchema,

View File

@@ -10,7 +10,7 @@ export const defaultRatingInputOptions: RatingInputOptions = {
customIcon: { isEnabled: false }, customIcon: { isEnabled: false },
} }
export const ratingInputOptionsSchema = optionBaseSchema.and( export const ratingInputOptionsSchema = optionBaseSchema.merge(
z.object({ z.object({
buttonType: z.literal('Icons').or(z.literal('Numbers')), buttonType: z.literal('Icons').or(z.literal('Numbers')),
length: z.number(), length: z.number(),
@@ -27,7 +27,7 @@ export const ratingInputOptionsSchema = optionBaseSchema.and(
}) })
) )
export const ratingInputBlockSchema = blockBaseSchema.and( export const ratingInputBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.literal(InputBlockType.RATING), type: z.literal(InputBlockType.RATING),
options: ratingInputOptionsSchema, options: ratingInputOptionsSchema,

View File

@@ -1,42 +0,0 @@
import { z } from 'zod'
import { choiceInputOptionsSchema, choiceInputSchema } from './choice'
import { dateInputOptionsSchema, dateInputSchema } from './date'
import { emailInputOptionsSchema, emailInputSchema } from './email'
import { numberInputOptionsSchema, numberInputSchema } from './number'
import { paymentInputOptionsSchema, paymentInputSchema } from './payment'
import {
phoneNumberInputOptionsSchema,
phoneNumberInputBlockSchema,
} from './phone'
import { ratingInputOptionsSchema, ratingInputBlockSchema } from './rating'
import { textInputOptionsSchema, textInputSchema } from './text'
import { fileInputOptionsSchema, fileInputStepSchema } from './file'
import { urlInputOptionsSchema, urlInputSchema } from './url'
import { optionBaseSchema } from '../baseSchemas'
export type OptionBase = z.infer<typeof optionBaseSchema>
export const inputBlockOptionsSchema = textInputOptionsSchema
.or(choiceInputOptionsSchema)
.or(emailInputOptionsSchema)
.or(numberInputOptionsSchema)
.or(urlInputOptionsSchema)
.or(phoneNumberInputOptionsSchema)
.or(dateInputOptionsSchema)
.or(paymentInputOptionsSchema)
.or(ratingInputOptionsSchema)
.or(fileInputOptionsSchema)
export const inputBlockSchema = textInputSchema
.or(numberInputSchema)
.or(emailInputSchema)
.or(urlInputSchema)
.or(dateInputSchema)
.or(phoneNumberInputBlockSchema)
.or(choiceInputSchema)
.or(paymentInputSchema)
.or(ratingInputBlockSchema)
.or(fileInputStepSchema)
export type InputBlock = z.infer<typeof inputBlockSchema>
export type InputBlockOptions = z.infer<typeof inputBlockOptionsSchema>

View File

@@ -11,8 +11,8 @@ export const textInputOptionsBaseSchema = z.object({
}) })
export const textInputOptionsSchema = textInputOptionsBaseSchema export const textInputOptionsSchema = textInputOptionsBaseSchema
.and(optionBaseSchema) .merge(optionBaseSchema)
.and( .merge(
z.object({ z.object({
isLong: z.boolean(), isLong: z.boolean(),
}) })
@@ -23,7 +23,7 @@ export const defaultTextInputOptions: TextInputOptions = {
labels: { button: defaultButtonLabel, placeholder: 'Type your answer...' }, labels: { button: defaultButtonLabel, placeholder: 'Type your answer...' },
} }
export const textInputSchema = blockBaseSchema.and( export const textInputSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([InputBlockType.TEXT]), type: z.enum([InputBlockType.TEXT]),
options: textInputOptionsSchema, options: textInputOptionsSchema,

View File

@@ -5,14 +5,14 @@ import { InputBlockType } from './enums'
import { textInputOptionsBaseSchema } from './text' import { textInputOptionsBaseSchema } from './text'
export const urlInputOptionsSchema = optionBaseSchema export const urlInputOptionsSchema = optionBaseSchema
.and(textInputOptionsBaseSchema) .merge(textInputOptionsBaseSchema)
.and( .merge(
z.object({ z.object({
retryMessageContent: z.string(), retryMessageContent: z.string(),
}) })
) )
export const urlInputSchema = blockBaseSchema.and( export const urlInputSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([InputBlockType.URL]), type: z.enum([InputBlockType.URL]),
options: urlInputOptionsSchema, options: urlInputOptionsSchema,

View File

@@ -19,7 +19,7 @@ export const chatwootOptionsSchema = z.object({
.optional(), .optional(),
}) })
export const chatwootBlockSchema = blockBaseSchema.and( export const chatwootBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([IntegrationBlockType.CHATWOOT]), type: z.enum([IntegrationBlockType.CHATWOOT]),
options: chatwootOptionsSchema, options: chatwootOptionsSchema,

View File

@@ -10,7 +10,7 @@ export const googleAnalyticsOptionsSchema = z.object({
value: z.number().optional(), value: z.number().optional(),
}) })
export const googleAnalyticsBlockSchema = blockBaseSchema.and( export const googleAnalyticsBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([IntegrationBlockType.GOOGLE_ANALYTICS]), type: z.enum([IntegrationBlockType.GOOGLE_ANALYTICS]),
options: googleAnalyticsOptionsSchema, options: googleAnalyticsOptionsSchema,

View File

@@ -29,7 +29,13 @@ const rowsFilterComparisonSchema = z.object({
value: z.string().optional(), value: z.string().optional(),
}) })
const googleSheetsGetOptionsSchema = googleSheetsOptionsBaseSchema.and( const initialGoogleSheetsOptionsSchema = googleSheetsOptionsBaseSchema.merge(
z.object({
action: z.undefined(),
})
)
const googleSheetsGetOptionsSchema = googleSheetsOptionsBaseSchema.merge(
z.object({ z.object({
action: z.enum([GoogleSheetsAction.GET]), action: z.enum([GoogleSheetsAction.GET]),
// TODO: remove referenceCell once migrated to filtering // TODO: remove referenceCell once migrated to filtering
@@ -42,14 +48,14 @@ const googleSheetsGetOptionsSchema = googleSheetsOptionsBaseSchema.and(
}) })
) )
const googleSheetsInsertRowOptionsSchema = googleSheetsOptionsBaseSchema.and( const googleSheetsInsertRowOptionsSchema = googleSheetsOptionsBaseSchema.merge(
z.object({ z.object({
action: z.enum([GoogleSheetsAction.INSERT_ROW]), action: z.enum([GoogleSheetsAction.INSERT_ROW]),
cellsToInsert: z.array(cellSchema), cellsToInsert: z.array(cellSchema),
}) })
) )
const googleSheetsUpdateRowOptionsSchema = googleSheetsOptionsBaseSchema.and( const googleSheetsUpdateRowOptionsSchema = googleSheetsOptionsBaseSchema.merge(
z.object({ z.object({
action: z.enum([GoogleSheetsAction.UPDATE_ROW]), action: z.enum([GoogleSheetsAction.UPDATE_ROW]),
cellsToUpsert: z.array(cellSchema), cellsToUpsert: z.array(cellSchema),
@@ -57,12 +63,14 @@ const googleSheetsUpdateRowOptionsSchema = googleSheetsOptionsBaseSchema.and(
}) })
) )
export const googleSheetsOptionsSchema = googleSheetsGetOptionsSchema export const googleSheetsOptionsSchema = z.discriminatedUnion('action', [
.or(googleSheetsInsertRowOptionsSchema) googleSheetsGetOptionsSchema,
.or(googleSheetsUpdateRowOptionsSchema) googleSheetsInsertRowOptionsSchema,
.or(googleSheetsOptionsBaseSchema) googleSheetsUpdateRowOptionsSchema,
initialGoogleSheetsOptionsSchema,
])
export const googleSheetsBlockSchema = blockBaseSchema.and( export const googleSheetsBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([IntegrationBlockType.GOOGLE_SHEETS]), type: z.enum([IntegrationBlockType.GOOGLE_SHEETS]),
options: googleSheetsOptionsSchema, options: googleSheetsOptionsSchema,

View File

@@ -4,7 +4,6 @@ export * from './googleAnalytics'
export * from './googleSheets' export * from './googleSheets'
export * from './makeCom' export * from './makeCom'
export * from './pabblyConnect' export * from './pabblyConnect'
export * from './schemas'
export * from './sendEmail' export * from './sendEmail'
export * from './webhook' export * from './webhook'
export * from './zapier' export * from './zapier'

View File

@@ -3,7 +3,7 @@ import { blockBaseSchema } from '../baseSchemas'
import { IntegrationBlockType } from './enums' import { IntegrationBlockType } from './enums'
import { webhookOptionsSchema } from './webhook' import { webhookOptionsSchema } from './webhook'
export const makeComBlockSchema = blockBaseSchema.and( export const makeComBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([IntegrationBlockType.MAKE_COM]), type: z.enum([IntegrationBlockType.MAKE_COM]),
options: webhookOptionsSchema, options: webhookOptionsSchema,

View File

@@ -3,7 +3,7 @@ import { blockBaseSchema } from '../baseSchemas'
import { IntegrationBlockType } from './enums' import { IntegrationBlockType } from './enums'
import { webhookOptionsSchema } from './webhook' import { webhookOptionsSchema } from './webhook'
export const pabblyConnectBlockSchema = blockBaseSchema.and( export const pabblyConnectBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([IntegrationBlockType.PABBLY_CONNECT]), type: z.enum([IntegrationBlockType.PABBLY_CONNECT]),
options: webhookOptionsSchema, options: webhookOptionsSchema,

View File

@@ -1,23 +0,0 @@
import { z } from 'zod'
import { chatwootBlockSchema } from './chatwoot'
import { googleAnalyticsBlockSchema } from './googleAnalytics'
import { googleSheetsBlockSchema } from './googleSheets/schemas'
import { makeComBlockSchema } from './makeCom'
import { openAIBlockSchema } from './openai'
import { pabblyConnectBlockSchema } from './pabblyConnect'
import { sendEmailBlockSchema } from './sendEmail'
import { webhookBlockSchema } from './webhook'
import { zapierBlockSchema } from './zapier'
export const integrationBlockSchema = googleSheetsBlockSchema
.or(googleAnalyticsBlockSchema)
.or(webhookBlockSchema)
.or(sendEmailBlockSchema)
.or(zapierBlockSchema)
.or(makeComBlockSchema)
.or(pabblyConnectBlockSchema)
.or(chatwootBlockSchema)
.or(openAIBlockSchema)
export type IntegrationBlock = z.infer<typeof integrationBlockSchema>
export type IntegrationBlockOptions = IntegrationBlock['options']

View File

@@ -15,7 +15,7 @@ export const sendEmailOptionsSchema = z.object({
attachmentsVariableId: z.string().optional(), attachmentsVariableId: z.string().optional(),
}) })
export const sendEmailBlockSchema = blockBaseSchema.and( export const sendEmailBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([IntegrationBlockType.EMAIL]), type: z.enum([IntegrationBlockType.EMAIL]),
options: sendEmailOptionsSchema, options: sendEmailOptionsSchema,

View File

@@ -21,7 +21,7 @@ export const webhookOptionsSchema = z.object({
isCustomBody: z.boolean().optional(), isCustomBody: z.boolean().optional(),
}) })
export const webhookBlockSchema = blockBaseSchema.and( export const webhookBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([IntegrationBlockType.WEBHOOK]), type: z.enum([IntegrationBlockType.WEBHOOK]),
options: webhookOptionsSchema, options: webhookOptionsSchema,

View File

@@ -3,7 +3,7 @@ import { blockBaseSchema } from '../baseSchemas'
import { IntegrationBlockType } from './enums' import { IntegrationBlockType } from './enums'
import { webhookOptionsSchema } from './webhook' import { webhookOptionsSchema } from './webhook'
export const zapierBlockSchema = blockBaseSchema.and( export const zapierBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([IntegrationBlockType.ZAPIER]), type: z.enum([IntegrationBlockType.ZAPIER]),
options: webhookOptionsSchema, options: webhookOptionsSchema,

View File

@@ -30,14 +30,14 @@ const conditionContentSchema = z.object({
comparisons: z.array(comparisonSchema), comparisons: z.array(comparisonSchema),
}) })
export const conditionItemSchema = itemBaseSchema.and( export const conditionItemSchema = itemBaseSchema.merge(
z.object({ z.object({
type: z.literal(ItemType.CONDITION), type: z.literal(ItemType.CONDITION),
content: conditionContentSchema, content: conditionContentSchema,
}) })
) )
export const conditionBlockSchema = blockBaseSchema.and( export const conditionBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([LogicBlockType.CONDITION]), type: z.enum([LogicBlockType.CONDITION]),
items: z.array(conditionItemSchema), items: z.array(conditionItemSchema),

View File

@@ -1,7 +1,6 @@
export * from './script' export * from './script'
export * from './condition' export * from './condition'
export * from './enums' export * from './enums'
export * from './logicBlock'
export * from './redirect' export * from './redirect'
export * from './setVariable' export * from './setVariable'
export * from './typebotLink' export * from './typebotLink'

View File

@@ -7,7 +7,7 @@ export const jumpOptionsSchema = z.object({
blockId: z.string().optional(), blockId: z.string().optional(),
}) })
export const jumpBlockSchema = blockBaseSchema.and( export const jumpBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([LogicBlockType.JUMP]), type: z.enum([LogicBlockType.JUMP]),
options: jumpOptionsSchema, options: jumpOptionsSchema,

View File

@@ -1,26 +0,0 @@
import { z } from 'zod'
import { scriptOptionsSchema, scriptBlockSchema } from './script'
import { conditionBlockSchema } from './condition'
import { redirectOptionsSchema, redirectBlockSchema } from './redirect'
import { setVariableOptionsSchema, setVariableBlockSchema } from './setVariable'
import { typebotLinkOptionsSchema, typebotLinkBlockSchema } from './typebotLink'
import { waitBlockSchema, waitOptionsSchema } from './wait'
import { jumpBlockSchema, jumpOptionsSchema } from './jump'
const logicBlockOptionsSchema = scriptOptionsSchema
.or(redirectOptionsSchema)
.or(setVariableOptionsSchema)
.or(typebotLinkOptionsSchema)
.or(waitOptionsSchema)
.or(jumpOptionsSchema)
export const logicBlockSchema = scriptBlockSchema
.or(conditionBlockSchema)
.or(redirectBlockSchema)
.or(typebotLinkBlockSchema)
.or(setVariableBlockSchema)
.or(waitBlockSchema)
.or(jumpBlockSchema)
export type LogicBlock = z.infer<typeof logicBlockSchema>
export type LogicBlockOptions = z.infer<typeof logicBlockOptionsSchema>

View File

@@ -7,7 +7,7 @@ export const redirectOptionsSchema = z.object({
isNewTab: z.boolean(), isNewTab: z.boolean(),
}) })
export const redirectBlockSchema = blockBaseSchema.and( export const redirectBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([LogicBlockType.REDIRECT]), type: z.enum([LogicBlockType.REDIRECT]),
options: redirectOptionsSchema, options: redirectOptionsSchema,

View File

@@ -8,7 +8,7 @@ export const scriptOptionsSchema = z.object({
shouldExecuteInParentContext: z.boolean().optional(), shouldExecuteInParentContext: z.boolean().optional(),
}) })
export const scriptBlockSchema = blockBaseSchema.and( export const scriptBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([LogicBlockType.SCRIPT]), type: z.enum([LogicBlockType.SCRIPT]),
options: scriptOptionsSchema, options: scriptOptionsSchema,

View File

@@ -8,7 +8,7 @@ export const setVariableOptionsSchema = z.object({
isCode: z.boolean().optional(), isCode: z.boolean().optional(),
}) })
export const setVariableBlockSchema = blockBaseSchema.and( export const setVariableBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([LogicBlockType.SET_VARIABLE]), type: z.enum([LogicBlockType.SET_VARIABLE]),
options: setVariableOptionsSchema, options: setVariableOptionsSchema,

View File

@@ -7,7 +7,7 @@ export const typebotLinkOptionsSchema = z.object({
groupId: z.string().optional(), groupId: z.string().optional(),
}) })
export const typebotLinkBlockSchema = blockBaseSchema.and( export const typebotLinkBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([LogicBlockType.TYPEBOT_LINK]), type: z.enum([LogicBlockType.TYPEBOT_LINK]),
options: typebotLinkOptionsSchema, options: typebotLinkOptionsSchema,

View File

@@ -6,7 +6,7 @@ export const waitOptionsSchema = z.object({
secondsToWaitFor: z.string().optional(), secondsToWaitFor: z.string().optional(),
}) })
export const waitBlockSchema = blockBaseSchema.and( export const waitBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.enum([LogicBlockType.WAIT]), type: z.enum([LogicBlockType.WAIT]),
options: waitOptionsSchema, options: waitOptionsSchema,

View File

@@ -1,28 +1,49 @@
import { z } from 'zod' import { z } from 'zod'
import { BubbleBlockType } from './bubbles/enums' import { BubbleBlockType } from './bubbles/enums'
import { BubbleBlock, bubbleBlockSchema } from './bubbles/schemas' import { ChoiceInputBlock, choiceInputSchema } from './inputs/choice'
import { ChoiceInputBlock } from './inputs/choice'
import { InputBlockType } from './inputs/enums' import { InputBlockType } from './inputs/enums'
import {
InputBlock,
InputBlockOptions,
inputBlockSchema,
} from './inputs/schemas'
import { IntegrationBlockType } from './integrations/enums' import { IntegrationBlockType } from './integrations/enums'
import { import { ConditionBlock, conditionBlockSchema } from './logic/condition'
IntegrationBlock,
IntegrationBlockOptions,
integrationBlockSchema,
} from './integrations/schemas'
import { ConditionBlock } from './logic/condition'
import { LogicBlockType } from './logic/enums' import { LogicBlockType } from './logic/enums'
import {
LogicBlock,
LogicBlockOptions,
logicBlockSchema,
} from './logic/logicBlock'
import { blockBaseSchema } from './baseSchemas' import { blockBaseSchema } from './baseSchemas'
import { startBlockSchema } from './start/schemas' import { startBlockSchema } from './start/schemas'
import {
textBubbleBlockSchema,
imageBubbleBlockSchema,
videoBubbleBlockSchema,
embedBubbleBlockSchema,
audioBubbleBlockSchema,
} from './bubbles'
import {
textInputSchema,
emailInputSchema,
numberInputSchema,
urlInputSchema,
phoneNumberInputBlockSchema,
dateInputSchema,
paymentInputSchema,
ratingInputBlockSchema,
fileInputStepSchema,
} from './inputs'
import {
chatwootBlockSchema,
googleAnalyticsBlockSchema,
googleSheetsBlockSchema,
makeComBlockSchema,
pabblyConnectBlockSchema,
sendEmailBlockSchema,
webhookBlockSchema,
zapierBlockSchema,
} from './integrations'
import { openAIBlockSchema } from './integrations/openai'
import {
scriptBlockSchema,
redirectBlockSchema,
setVariableBlockSchema,
typebotLinkBlockSchema,
waitBlockSchema,
} from './logic'
import { jumpBlockSchema } from './logic/jump'
export type DraggableBlock = export type DraggableBlock =
| BubbleBlock | BubbleBlock
@@ -67,10 +88,74 @@ export type BlockIndices = {
blockIndex: number blockIndex: number
} }
export const blockSchema = startBlockSchema const bubbleBlockSchema = z.discriminatedUnion('type', [
.or(bubbleBlockSchema) textBubbleBlockSchema,
.or(inputBlockSchema) imageBubbleBlockSchema,
.or(logicBlockSchema) videoBubbleBlockSchema,
.or(integrationBlockSchema) embedBubbleBlockSchema,
audioBubbleBlockSchema,
])
export type BubbleBlock = z.infer<typeof bubbleBlockSchema>
export type BubbleBlockContent = BubbleBlock['content']
export const inputBlockSchema = z.discriminatedUnion('type', [
textInputSchema,
choiceInputSchema,
emailInputSchema,
numberInputSchema,
urlInputSchema,
phoneNumberInputBlockSchema,
dateInputSchema,
paymentInputSchema,
ratingInputBlockSchema,
fileInputStepSchema,
])
export type InputBlock = z.infer<typeof inputBlockSchema>
export type InputBlockOptions = InputBlock['options']
export const logicBlockSchema = z.discriminatedUnion('type', [
scriptBlockSchema,
conditionBlockSchema,
redirectBlockSchema,
setVariableBlockSchema,
typebotLinkBlockSchema,
waitBlockSchema,
jumpBlockSchema,
])
export type LogicBlock = z.infer<typeof logicBlockSchema>
export type LogicBlockOptions = LogicBlock extends
| {
options?: infer Options
}
| {}
? Options
: never
export const integrationBlockSchema = z.discriminatedUnion('type', [
chatwootBlockSchema,
googleAnalyticsBlockSchema,
googleSheetsBlockSchema,
makeComBlockSchema,
openAIBlockSchema,
pabblyConnectBlockSchema,
sendEmailBlockSchema,
webhookBlockSchema,
zapierBlockSchema,
])
export type IntegrationBlock = z.infer<typeof integrationBlockSchema>
export type IntegrationBlockOptions = IntegrationBlock['options']
export const blockSchema = z.union([
startBlockSchema,
bubbleBlockSchema,
inputBlockSchema,
logicBlockSchema,
integrationBlockSchema,
])
export type Block = z.infer<typeof blockSchema> export type Block = z.infer<typeof blockSchema>

View File

@@ -1,7 +1,7 @@
import { z } from 'zod' import { z } from 'zod'
import { blockBaseSchema } from '../baseSchemas' import { blockBaseSchema } from '../baseSchemas'
export const startBlockSchema = blockBaseSchema.and( export const startBlockSchema = blockBaseSchema.merge(
z.object({ z.object({
type: z.literal('start'), type: z.literal('start'),
label: z.string(), label: z.string(),

View File

@@ -1,7 +1,6 @@
import { z } from 'zod' import { z } from 'zod'
import { import {
googleAnalyticsOptionsSchema, googleAnalyticsOptionsSchema,
inputBlockSchema,
paymentInputRuntimeOptionsSchema, paymentInputRuntimeOptionsSchema,
redirectOptionsSchema, redirectOptionsSchema,
} from './blocks' } from './blocks'
@@ -9,7 +8,6 @@ import { publicTypebotSchema } from './publicTypebot'
import { logSchema, resultSchema } from './result' import { logSchema, resultSchema } from './result'
import { typebotSchema } from './typebot' import { typebotSchema } from './typebot'
import { import {
BubbleBlockType,
textBubbleContentSchema, textBubbleContentSchema,
imageBubbleContentSchema, imageBubbleContentSchema,
videoBubbleContentSchema, videoBubbleContentSchema,
@@ -17,6 +15,8 @@ import {
embedBubbleContentSchema, embedBubbleContentSchema,
} from './blocks/bubbles' } from './blocks/bubbles'
import { answerSchema } from './answer' import { answerSchema } from './answer'
import { BubbleBlockType } from './blocks/bubbles/enums'
import { inputBlockSchema } from './blocks/schemas'
const typebotInSessionStateSchema = publicTypebotSchema.pick({ const typebotInSessionStateSchema = publicTypebotSchema.pick({
id: true, id: true,
@@ -40,7 +40,7 @@ const resultInSessionStateSchema = resultSchema
.pick({ .pick({
variables: true, variables: true,
}) })
.and( .merge(
z.object({ z.object({
answers: z.array(answerInSessionStateSchema), answers: z.array(answerInSessionStateSchema),
id: z.string().optional(), id: z.string().optional(),
@@ -72,7 +72,7 @@ const chatSessionSchema = z.object({
}) })
const textMessageSchema = z.object({ const textMessageSchema = z.object({
type: z.enum([BubbleBlockType.TEXT]), type: z.literal(BubbleBlockType.TEXT),
content: textBubbleContentSchema.omit({ content: textBubbleContentSchema.omit({
richText: true, richText: true,
}), }),
@@ -99,17 +99,19 @@ const embedMessageSchema = z.object({
.omit({ .omit({
height: true, height: true,
}) })
.and(z.object({ height: z.number().optional() })), .merge(z.object({ height: z.number().optional() })),
}) })
const chatMessageSchema = z const chatMessageSchema = z
.object({ id: z.string() }) .object({ id: z.string() })
.and( .and(
textMessageSchema z.discriminatedUnion('type', [
.or(imageMessageSchema) textMessageSchema,
.or(videoMessageSchema) imageMessageSchema,
.or(audioMessageSchema) videoMessageSchema,
.or(embedMessageSchema) audioMessageSchema,
embedMessageSchema,
])
) )
const scriptToExecuteSchema = z.object({ const scriptToExecuteSchema = z.object({
@@ -187,7 +189,7 @@ const replyLogSchema = logSchema
status: true, status: true,
description: true, description: true,
}) })
.and(z.object({ details: z.unknown().optional() })) .merge(z.object({ details: z.unknown().optional() }))
const clientSideActionSchema = z const clientSideActionSchema = z
.object({ .object({

View File

@@ -22,7 +22,7 @@ export const publicTypebotSchema = z.object({
settings: settingsSchema, settings: settingsSchema,
}) satisfies z.ZodType<PrismaPublicTypebot> }) satisfies z.ZodType<PrismaPublicTypebot>
const publicTypebotWithName = publicTypebotSchema.and( const publicTypebotWithName = publicTypebotSchema.merge(
typebotSchema.pick({ name: true, isArchived: true, isClosed: true }) typebotSchema.pick({ name: true, isArchived: true, isClosed: true })
) )

View File

@@ -14,13 +14,13 @@ export const resultSchema = z.object({
isArchived: z.boolean().nullable(), isArchived: z.boolean().nullable(),
}) satisfies z.ZodType<ResultPrisma> }) satisfies z.ZodType<ResultPrisma>
export const resultWithAnswersSchema = resultSchema.and( export const resultWithAnswersSchema = resultSchema.merge(
z.object({ z.object({
answers: z.array(answerSchema), answers: z.array(answerSchema),
}) })
) )
export const resultWithAnswersInputSchema = resultSchema.and( export const resultWithAnswersInputSchema = resultSchema.merge(
z.object({ z.object({
answers: z.array(answerInputSchema), answers: z.array(answerInputSchema),
}) })

View File

@@ -11,7 +11,7 @@ const containerColorsSchema = z.object({
color: z.string(), color: z.string(),
}) })
const inputColorsSchema = containerColorsSchema.and( const inputColorsSchema = containerColorsSchema.merge(
z.object({ z.object({
placeholderColor: z.string(), placeholderColor: z.string(),
}) })

View File

@@ -1,9 +1,9 @@
import { z } from 'zod' import { z } from 'zod'
import { settingsSchema } from './settings' import { settingsSchema } from './settings'
import { blockSchema } from '../blocks'
import { themeSchema } from './theme' import { themeSchema } from './theme'
import { variableSchema } from './variable' import { variableSchema } from './variable'
import { Typebot as TypebotPrisma } from 'db' import { Typebot as TypebotPrisma } from 'db'
import { blockSchema } from '../blocks/schemas'
export const groupSchema = z.object({ export const groupSchema = z.object({
id: z.string(), id: z.string(),