@@ -1,11 +1,11 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useTypebot } from '@/providers/TypebotProvider'
|
||||
import { AudioBubbleContent } from '@typebot.io/schemas'
|
||||
import { TypingBubble } from '@/components/TypingBubble'
|
||||
import { parseVariables } from '@/features/variables'
|
||||
import { AudioBubbleBlock } from '@typebot.io/schemas'
|
||||
|
||||
type Props = {
|
||||
url: AudioBubbleContent['url']
|
||||
url: NonNullable<AudioBubbleBlock['content']>['url']
|
||||
onTransitionEnd: () => void
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ export const EmbedBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
}
|
||||
}, [isLoading, isTyping, onTypingEnd])
|
||||
|
||||
const height = block.content.height
|
||||
const height = block.content?.height
|
||||
? typeof block.content.height === 'string'
|
||||
? parseVariables(typebot.variables)(block.content.height) + 'px'
|
||||
: block.content.height
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTypebot } from '@/providers/TypebotProvider'
|
||||
import { BubbleBlockType, TextBubbleBlock } from '@typebot.io/schemas'
|
||||
import { TextBubbleBlock } from '@typebot.io/schemas'
|
||||
import { computeTypingDuration } from '../utils/computeTypingDuration'
|
||||
import { parseVariables } from '@/features/variables'
|
||||
import { TypingBubble } from '@/components/TypingBubble'
|
||||
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
|
||||
|
||||
type Props = {
|
||||
block: TextBubbleBlock
|
||||
@@ -12,19 +13,13 @@ type Props = {
|
||||
|
||||
export const showAnimationDuration = 400
|
||||
|
||||
const defaultTypingEmulation = {
|
||||
enabled: true,
|
||||
speed: 300,
|
||||
maxDelay: 1.5,
|
||||
}
|
||||
|
||||
export const TextBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
const { typebot, isLoading } = useTypebot()
|
||||
const messageContainer = useRef<HTMLDivElement | null>(null)
|
||||
const [isTyping, setIsTyping] = useState(true)
|
||||
|
||||
const [content] = useState(
|
||||
parseVariables(typebot.variables)(block.content.html)
|
||||
parseVariables(typebot.variables)(block.content?.html)
|
||||
)
|
||||
|
||||
const onTypingEnd = useCallback(() => {
|
||||
@@ -37,10 +32,10 @@ export const TextBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
useEffect(() => {
|
||||
if (!isTyping || isLoading) return
|
||||
|
||||
const typingTimeout = computeTypingDuration(
|
||||
block.content.plainText ?? '',
|
||||
typebot.settings?.typingEmulation ?? defaultTypingEmulation
|
||||
)
|
||||
const typingTimeout = computeTypingDuration({
|
||||
bubbleContent: block.content?.plainText ?? '',
|
||||
typingSettings: typebot.settings?.typingEmulation,
|
||||
})
|
||||
const timeout = setTimeout(() => {
|
||||
onTypingEnd()
|
||||
}, typingTimeout)
|
||||
@@ -49,7 +44,7 @@ export const TextBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}, [
|
||||
block.content.plainText,
|
||||
block.content?.plainText,
|
||||
isLoading,
|
||||
isTyping,
|
||||
onTypingEnd,
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
import { TypingEmulation } from '@typebot.io/schemas'
|
||||
import { Settings } from '@typebot.io/schemas'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
|
||||
export const computeTypingDuration = (
|
||||
bubbleContent: string,
|
||||
typingSettings: TypingEmulation
|
||||
) => {
|
||||
type Props = {
|
||||
bubbleContent: string
|
||||
typingSettings?: Settings['typingEmulation']
|
||||
}
|
||||
|
||||
export const computeTypingDuration = ({
|
||||
bubbleContent,
|
||||
typingSettings,
|
||||
}: Props) => {
|
||||
let wordCount = bubbleContent.match(/(\w+)/g)?.length ?? 0
|
||||
if (wordCount === 0) wordCount = bubbleContent.length
|
||||
const typedWordsPerMinute = typingSettings.speed
|
||||
let typingTimeout = typingSettings.enabled
|
||||
? (wordCount / typedWordsPerMinute) * 60000
|
||||
: 0
|
||||
if (typingTimeout > typingSettings.maxDelay * 1000)
|
||||
typingTimeout = typingSettings.maxDelay * 1000
|
||||
const { enabled, speed, maxDelay } = {
|
||||
enabled: typingSettings?.enabled ?? defaultSettings.typingEmulation.enabled,
|
||||
speed: typingSettings?.speed ?? defaultSettings.typingEmulation.speed,
|
||||
maxDelay:
|
||||
typingSettings?.maxDelay ?? defaultSettings.typingEmulation.maxDelay,
|
||||
}
|
||||
const typedWordsPerMinute = speed
|
||||
let typingTimeout = enabled ? (wordCount / typedWordsPerMinute) * 60000 : 0
|
||||
if (typingTimeout > maxDelay * 1000) typingTimeout = maxDelay * 1000
|
||||
return typingTimeout
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTypebot } from '@/providers/TypebotProvider'
|
||||
import {
|
||||
Variable,
|
||||
VideoBubbleContent,
|
||||
VideoBubbleContentType,
|
||||
VideoBubbleBlock,
|
||||
} from '@typebot.io/schemas'
|
||||
import { Variable, VideoBubbleBlock } from '@typebot.io/schemas'
|
||||
import { TypingBubble } from '@/components/TypingBubble'
|
||||
import { parseVariables } from '@/features/variables'
|
||||
import { VideoBubbleContentType } from '@typebot.io/schemas/features/blocks/bubbles/video/constants'
|
||||
|
||||
type Props = {
|
||||
block: VideoBubbleBlock
|
||||
@@ -71,7 +67,7 @@ const VideoContent = ({
|
||||
isTyping,
|
||||
variables,
|
||||
}: {
|
||||
content?: VideoBubbleContent
|
||||
content?: VideoBubbleBlock['content']
|
||||
isTyping: boolean
|
||||
variables: Variable[]
|
||||
}) => {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { SendButton } from '@/components/SendButton'
|
||||
import { InputSubmitContent } from '@/types'
|
||||
import { DateInputOptions } from '@typebot.io/schemas'
|
||||
import { useState } from 'react'
|
||||
import { parseReadableDate } from '../utils/parseReadableDate'
|
||||
import { DateInputBlock } from '@typebot.io/schemas'
|
||||
|
||||
type DateInputProps = {
|
||||
onSubmit: (inputValue: InputSubmitContent) => void
|
||||
options?: DateInputOptions
|
||||
options: DateInputBlock['options']
|
||||
}
|
||||
|
||||
export const DateForm = ({
|
||||
|
||||
@@ -2,9 +2,10 @@ import { Spinner, SendButton } from '@/components/SendButton'
|
||||
import { useAnswers } from '@/providers/AnswersProvider'
|
||||
import { useTypebot } from '@/providers/TypebotProvider'
|
||||
import { InputSubmitContent } from '@/types'
|
||||
import { defaultFileInputOptions, FileInputBlock } from '@typebot.io/schemas'
|
||||
import { FileInputBlock } from '@typebot.io/schemas'
|
||||
import React, { ChangeEvent, FormEvent, useState, DragEvent } from 'react'
|
||||
import { uploadFiles } from '../helpers/uploadFiles'
|
||||
import { defaultFileInputOptions } from '@typebot.io/schemas/features/blocks/inputs/file/constants'
|
||||
|
||||
type Props = {
|
||||
block: FileInputBlock
|
||||
@@ -13,13 +14,13 @@ type Props = {
|
||||
}
|
||||
|
||||
export const FileUploadForm = ({
|
||||
block: {
|
||||
id,
|
||||
options: { isMultipleAllowed, labels, sizeLimit, isRequired },
|
||||
},
|
||||
block: { id, options },
|
||||
onSubmit,
|
||||
onSkip,
|
||||
}: Props) => {
|
||||
const { isMultipleAllowed, labels, isRequired } = options ?? {}
|
||||
const sizeLimit =
|
||||
options && 'sizeLimit' in options ? options?.sizeLimit : undefined
|
||||
const { isPreview, currentTypebotId } = useTypebot()
|
||||
const { resultId } = useAnswers()
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([])
|
||||
@@ -160,7 +161,7 @@ export const FileUploadForm = ({
|
||||
)}
|
||||
<p
|
||||
className="text-sm text-gray-500 text-center"
|
||||
dangerouslySetInnerHTML={{ __html: labels.placeholder }}
|
||||
dangerouslySetInnerHTML={{ __html: labels?.placeholder ?? '' }}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
@@ -181,7 +182,7 @@ export const FileUploadForm = ({
|
||||
}
|
||||
onClick={onSkip}
|
||||
>
|
||||
{labels.skip ?? defaultFileInputOptions.labels.skip}
|
||||
{labels?.skip ?? defaultFileInputOptions.labels.skip}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -195,17 +196,17 @@ export const FileUploadForm = ({
|
||||
}
|
||||
onClick={clearFiles}
|
||||
>
|
||||
{labels.clear ?? defaultFileInputOptions.labels.clear}
|
||||
{labels?.clear ?? defaultFileInputOptions.labels.clear}
|
||||
</button>
|
||||
)}
|
||||
<SendButton
|
||||
type="submit"
|
||||
label={
|
||||
labels.button === defaultFileInputOptions.labels.button
|
||||
labels?.button === defaultFileInputOptions.labels.button
|
||||
? `${labels.button} ${selectedFiles.length} file${
|
||||
selectedFiles.length > 1 ? 's' : ''
|
||||
}`
|
||||
: labels.button
|
||||
: labels?.button ?? ''
|
||||
}
|
||||
disableIcon
|
||||
/>
|
||||
|
||||
@@ -23,7 +23,6 @@ export const uploadFiles = async ({
|
||||
i += 1
|
||||
const { data } = await sendRequest<{
|
||||
presignedUrl: string
|
||||
formData: Record<string, string>
|
||||
hasReachedStorageLimit: boolean
|
||||
}>(
|
||||
`${basePath}/storage/upload-url?filePath=${encodeURIComponent(
|
||||
@@ -36,14 +35,9 @@ export const uploadFiles = async ({
|
||||
const url = data.presignedUrl
|
||||
if (data.hasReachedStorageLimit) urls.push(null)
|
||||
else {
|
||||
const formData = new FormData()
|
||||
Object.entries(data.formData).forEach(([key, value]) => {
|
||||
formData.append(key, value)
|
||||
})
|
||||
formData.append('file', file)
|
||||
const upload = await fetch(data.presignedUrl, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
const upload = await fetch(url, {
|
||||
method: 'PUT',
|
||||
body: file,
|
||||
})
|
||||
|
||||
if (!upload.ok) continue
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { PaymentInputOptions, PaymentProvider } from '@typebot.io/schemas'
|
||||
import React from 'react'
|
||||
import { PaymentInputBlock } from '@typebot.io/schemas'
|
||||
import { StripePaymentForm } from './StripePaymentForm'
|
||||
import { PaymentProvider } from '@typebot.io/schemas/features/blocks/inputs/payment/constants'
|
||||
|
||||
type Props = {
|
||||
onSuccess: () => void
|
||||
options: PaymentInputOptions
|
||||
options: PaymentInputBlock['options']
|
||||
}
|
||||
|
||||
export const PaymentForm = ({ onSuccess, options }: Props): JSX.Element => {
|
||||
switch (options.provider) {
|
||||
switch (options?.provider) {
|
||||
case undefined:
|
||||
case PaymentProvider.STRIPE:
|
||||
return <StripePaymentForm onSuccess={onSuccess} options={options} />
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { FormEvent, useEffect, useState } from 'react'
|
||||
import { useStripe, useElements, PaymentElement } from '@stripe/react-stripe-js'
|
||||
import { Elements } from '@stripe/react-stripe-js'
|
||||
import { PaymentInputOptions, Variable } from '@typebot.io/schemas'
|
||||
import { PaymentInputBlock, Variable } from '@typebot.io/schemas'
|
||||
import { SendButton, Spinner } from '@/components/SendButton'
|
||||
import { initStripe } from '@/lib/stripe'
|
||||
import { parseVariables } from '@/features/variables'
|
||||
@@ -11,7 +11,7 @@ import { createPaymentIntentQuery } from '../../queries/createPaymentIntentQuery
|
||||
import { Stripe } from '@stripe/stripe-js'
|
||||
|
||||
type Props = {
|
||||
options: PaymentInputOptions
|
||||
options: PaymentInputBlock['options']
|
||||
onSuccess: () => void
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ const CheckoutForm = ({
|
||||
onSuccess: () => void
|
||||
clientSecret: string
|
||||
amountLabel: string
|
||||
options: PaymentInputOptions
|
||||
options: PaymentInputBlock['options']
|
||||
variables: Variable[]
|
||||
viewerHost: string
|
||||
}) => {
|
||||
@@ -131,13 +131,13 @@ const CheckoutForm = ({
|
||||
return_url: viewerHost,
|
||||
payment_method_data: {
|
||||
billing_details: {
|
||||
name: options.additionalInformation?.name
|
||||
? parseVariables(variables)(options.additionalInformation?.name)
|
||||
name: options?.additionalInformation?.name
|
||||
? parseVariables(variables)(options.additionalInformation.name)
|
||||
: undefined,
|
||||
email: options.additionalInformation?.email
|
||||
email: options?.additionalInformation?.email
|
||||
? parseVariables(variables)(options.additionalInformation?.email)
|
||||
: undefined,
|
||||
phone: options.additionalInformation?.phoneNumber
|
||||
phone: options?.additionalInformation?.phoneNumber
|
||||
? parseVariables(variables)(
|
||||
options.additionalInformation?.phoneNumber
|
||||
)
|
||||
@@ -172,7 +172,7 @@ const CheckoutForm = ({
|
||||
/>
|
||||
{isPayButtonVisible && (
|
||||
<SendButton
|
||||
label={`${options.labels.button} ${amountLabel}`}
|
||||
label={`${options?.labels?.button} ${amountLabel}`}
|
||||
isDisabled={isLoading || !stripe || !elements}
|
||||
isLoading={isLoading}
|
||||
className="mt-4 w-full max-w-lg"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PaymentInputOptions, Variable } from '@typebot.io/schemas'
|
||||
import { PaymentInputBlock, Variable } from '@typebot.io/schemas'
|
||||
import { sendRequest } from '@typebot.io/lib'
|
||||
|
||||
export const createPaymentIntentQuery = ({
|
||||
@@ -7,7 +7,7 @@ export const createPaymentIntentQuery = ({
|
||||
inputOptions,
|
||||
variables,
|
||||
}: {
|
||||
inputOptions: PaymentInputOptions
|
||||
inputOptions: PaymentInputBlock['options']
|
||||
apiHost: string
|
||||
variables: Variable[]
|
||||
isPreview: boolean
|
||||
|
||||
@@ -52,8 +52,10 @@ export const PhoneInput = ({
|
||||
ref={inputRef}
|
||||
value={inputValue}
|
||||
onChange={handleChange}
|
||||
placeholder={block.options.labels.placeholder ?? 'Your phone number...'}
|
||||
defaultCountry={block.options.defaultCountryCode as Country}
|
||||
placeholder={
|
||||
block.options?.labels?.placeholder ?? 'Your phone number...'
|
||||
}
|
||||
defaultCountry={block.options?.defaultCountryCode as Country}
|
||||
autoFocus={!isMobile}
|
||||
/>
|
||||
<SendButton
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { InputSubmitContent } from '@/types'
|
||||
import { RatingInputOptions, RatingInputBlock } from '@typebot.io/schemas'
|
||||
import { RatingInputBlock } from '@typebot.io/schemas'
|
||||
import React, { FormEvent, useState } from 'react'
|
||||
import { isDefined, isEmpty, isNotDefined } from '@typebot.io/lib'
|
||||
import { SendButton } from '../../../../../components/SendButton'
|
||||
import { defaultRatingInputOptions } from '@typebot.io/schemas/features/blocks/inputs/rating/constants'
|
||||
|
||||
type Props = {
|
||||
block: RatingInputBlock
|
||||
@@ -19,14 +20,14 @@ export const RatingForm = ({ block, onSubmit }: Props) => {
|
||||
}
|
||||
|
||||
const handleClick = (rating: number) => {
|
||||
if (block.options.isOneClickSubmitEnabled)
|
||||
if (block.options?.isOneClickSubmitEnabled)
|
||||
onSubmit({ value: rating.toString() })
|
||||
setRating(rating)
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="flex flex-col" onSubmit={handleSubmit}>
|
||||
{block.options.labels.left && (
|
||||
{block.options?.labels?.left && (
|
||||
<span className="text-sm w-full mb-2 rating-label">
|
||||
{block.options.labels.left}
|
||||
</span>
|
||||
@@ -34,20 +35,20 @@ export const RatingForm = ({ block, onSubmit }: Props) => {
|
||||
<div className="flex flex-wrap justify-center">
|
||||
{Array.from(
|
||||
Array(
|
||||
block.options.length +
|
||||
(block.options.buttonType === 'Numbers' ? 1 : 0)
|
||||
(block.options?.length ?? defaultRatingInputOptions.length) +
|
||||
(block.options?.buttonType === 'Numbers' ? 1 : 0)
|
||||
)
|
||||
).map((_, idx) => (
|
||||
<RatingButton
|
||||
{...block.options}
|
||||
key={idx}
|
||||
rating={rating}
|
||||
idx={idx + (block.options.buttonType === 'Numbers' ? 0 : 1)}
|
||||
idx={idx + (block.options?.buttonType === 'Numbers' ? 0 : 1)}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{block.options.labels.right && (
|
||||
{block.options?.labels?.right && (
|
||||
<span className="text-sm w-full text-right mb-2 pr-2 rating-label">
|
||||
{block.options.labels.right}
|
||||
</span>
|
||||
@@ -56,7 +57,7 @@ export const RatingForm = ({ block, onSubmit }: Props) => {
|
||||
<div className="flex justify-end mr-2">
|
||||
{isDefined(rating) && (
|
||||
<SendButton
|
||||
label={block.options?.labels.button ?? 'Send'}
|
||||
label={block.options?.labels?.button ?? 'Send'}
|
||||
disableIcon
|
||||
/>
|
||||
)}
|
||||
@@ -71,7 +72,10 @@ const RatingButton = ({
|
||||
buttonType,
|
||||
customIcon,
|
||||
onClick,
|
||||
}: Pick<RatingInputOptions, 'buttonType' | 'customIcon'> & {
|
||||
}: Pick<
|
||||
NonNullable<RatingInputBlock['options']>,
|
||||
'buttonType' | 'customIcon'
|
||||
> & {
|
||||
rating: number | undefined
|
||||
idx: number
|
||||
onClick: (idx: number) => void
|
||||
@@ -100,7 +104,7 @@ const RatingButton = ({
|
||||
onClick={() => onClick(idx)}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
customIcon.isEnabled && !isEmpty(customIcon.svg)
|
||||
customIcon?.isEnabled && !isEmpty(customIcon.svg)
|
||||
? customIcon.svg
|
||||
: defaultIcon,
|
||||
}}
|
||||
|
||||
@@ -2,9 +2,11 @@ import { parseVariables } from '@/features/variables'
|
||||
import { IntegrationState } from '@/types'
|
||||
import { sendEventToParent } from '@/utils/chat'
|
||||
import { isEmbedded } from '@/utils/helpers'
|
||||
import { ChatwootBlock, ChatwootOptions } from '@typebot.io/schemas'
|
||||
import { ChatwootBlock } from '@typebot.io/schemas'
|
||||
|
||||
const parseSetUserCode = (user: ChatwootOptions['user']) => `
|
||||
const parseSetUserCode = (
|
||||
user: NonNullable<ChatwootBlock['options']>['user']
|
||||
) => `
|
||||
window.$chatwoot.setUser("${user?.id ?? ''}", {
|
||||
email: ${user?.email ? `"${user.email}"` : 'undefined'},
|
||||
name: ${user?.name ? `"${user.name}"` : 'undefined'},
|
||||
@@ -17,7 +19,7 @@ const parseChatwootOpenCode = ({
|
||||
baseUrl,
|
||||
websiteToken,
|
||||
user,
|
||||
}: ChatwootOptions) => `
|
||||
}: ChatwootBlock['options'] = {}) => `
|
||||
if (window.$chatwoot) {
|
||||
if(${Boolean(user)}) {
|
||||
${parseSetUserCode(user)}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { IntegrationState } from '@/types'
|
||||
import { parseLog } from '@/utils/helpers'
|
||||
import {
|
||||
GoogleSheetsBlock,
|
||||
GoogleSheetsAction,
|
||||
GoogleSheetsInsertRowOptions,
|
||||
GoogleSheetsUpdateRowOptions,
|
||||
GoogleSheetsGetOptions,
|
||||
@@ -12,12 +11,14 @@ import {
|
||||
Variable,
|
||||
} from '@typebot.io/schemas'
|
||||
import { sendRequest, byId } from '@typebot.io/lib'
|
||||
import { GoogleSheetsAction } from '@typebot.io/schemas/features/blocks/integrations/googleSheets/constants'
|
||||
|
||||
export const executeGoogleSheetBlock = async (
|
||||
block: GoogleSheetsBlock,
|
||||
context: IntegrationState
|
||||
) => {
|
||||
if (!('action' in block.options)) return block.outgoingEdgeId
|
||||
if (!block.options || !('action' in block.options))
|
||||
return block.outgoingEdgeId
|
||||
switch (block.options.action) {
|
||||
case GoogleSheetsAction.INSERT_ROW:
|
||||
insertRowInGoogleSheets(block.options, context)
|
||||
@@ -68,7 +69,7 @@ const updateRowInGoogleSheets = (
|
||||
options: GoogleSheetsUpdateRowOptions,
|
||||
{ variables, apiHost, onNewLog, resultId }: IntegrationState
|
||||
) => {
|
||||
if (!options.cellsToUpsert || !options.referenceCell) return
|
||||
if (!options.cellsToUpsert || !('referenceCell' in options)) return
|
||||
sendRequest({
|
||||
url: `${apiHost}/api/integrations/google-sheets/spreadsheets/${options.spreadsheetId}/sheets/${options.sheetId}`,
|
||||
method: 'POST',
|
||||
@@ -78,8 +79,8 @@ const updateRowInGoogleSheets = (
|
||||
values: parseCellValues(options.cellsToUpsert, variables),
|
||||
resultId,
|
||||
referenceCell: {
|
||||
column: options.referenceCell.column,
|
||||
value: parseVariables(variables)(options.referenceCell.value ?? ''),
|
||||
column: options.referenceCell?.column,
|
||||
value: parseVariables(variables)(options.referenceCell?.value ?? ''),
|
||||
},
|
||||
},
|
||||
}).then(({ error }) => {
|
||||
@@ -113,15 +114,18 @@ const getRowFromGoogleSheets = async (
|
||||
body: {
|
||||
action: GoogleSheetsAction.GET,
|
||||
credentialsId: options.credentialsId,
|
||||
referenceCell: options.referenceCell
|
||||
? {
|
||||
column: options.referenceCell.column,
|
||||
value: parseVariables(variables)(options.referenceCell.value ?? ''),
|
||||
}
|
||||
: undefined,
|
||||
referenceCell:
|
||||
'referenceCell' in options
|
||||
? {
|
||||
column: options.referenceCell?.column,
|
||||
value: parseVariables(variables)(
|
||||
options.referenceCell?.value ?? ''
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
filter: options.filter
|
||||
? {
|
||||
comparisons: options.filter.comparisons.map((comparison) => ({
|
||||
comparisons: options.filter.comparisons?.map((comparison) => ({
|
||||
...comparison,
|
||||
value: parseVariables(variables)(comparison.value),
|
||||
})),
|
||||
|
||||
@@ -29,18 +29,18 @@ export const executeSendEmailBlock = (
|
||||
url: `${apiHost}/api/typebots/${typebotId}/integrations/email?resultId=${resultId}`,
|
||||
method: 'POST',
|
||||
body: {
|
||||
credentialsId: options.credentialsId,
|
||||
recipients: options.recipients.map(parseVariables(variables)),
|
||||
subject: parseVariables(variables)(options.subject ?? ''),
|
||||
body: parseVariables(variables)(options.body ?? ''),
|
||||
cc: (options.cc ?? []).map(parseVariables(variables)),
|
||||
bcc: (options.bcc ?? []).map(parseVariables(variables)),
|
||||
replyTo: options.replyTo
|
||||
credentialsId: options?.credentialsId,
|
||||
recipients: options?.recipients?.map(parseVariables(variables)),
|
||||
subject: parseVariables(variables)(options?.subject ?? ''),
|
||||
body: parseVariables(variables)(options?.body ?? ''),
|
||||
cc: (options?.cc ?? []).map(parseVariables(variables)),
|
||||
bcc: (options?.bcc ?? []).map(parseVariables(variables)),
|
||||
replyTo: options?.replyTo
|
||||
? parseVariables(variables)(options.replyTo)
|
||||
: undefined,
|
||||
fileUrls: variables.find(byId(options.attachmentsVariableId))?.value,
|
||||
isCustomBody: options.isCustomBody,
|
||||
isBodyCode: options.isBodyCode,
|
||||
fileUrls: variables.find(byId(options?.attachmentsVariableId))?.value,
|
||||
isCustomBody: options?.isCustomBody,
|
||||
isBodyCode: options?.isBodyCode,
|
||||
resultValues,
|
||||
},
|
||||
}).then(({ error }) => {
|
||||
|
||||
@@ -48,7 +48,7 @@ export const executeWebhook = async (
|
||||
: 'Webhook successfuly executed',
|
||||
details: JSON.stringify(error ?? data, null, 2).substring(0, 1000),
|
||||
})
|
||||
const newVariables = block.options.responseVariableMapping.reduce<
|
||||
const newVariables = block.options?.responseVariableMapping?.reduce<
|
||||
VariableWithUnknowValue[]
|
||||
>((newVariables, varMapping) => {
|
||||
if (!varMapping?.bodyPath || !varMapping.variableId) return newVariables
|
||||
@@ -66,6 +66,6 @@ export const executeWebhook = async (
|
||||
return newVariables
|
||||
}
|
||||
}, [])
|
||||
updateVariables(newVariables)
|
||||
if (newVariables) updateVariables(newVariables)
|
||||
return block.outgoingEdgeId
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { parseVariables } from '@/features/variables'
|
||||
import { EdgeId, LogicState } from '@/types'
|
||||
import {
|
||||
Comparison,
|
||||
ComparisonOperators,
|
||||
ConditionBlock,
|
||||
LogicalOperator,
|
||||
Variable,
|
||||
} from '@typebot.io/schemas'
|
||||
import { Comparison, ConditionBlock, Variable } from '@typebot.io/schemas'
|
||||
import { isNotDefined, isDefined } from '@typebot.io/lib'
|
||||
import {
|
||||
LogicalOperator,
|
||||
ComparisonOperators,
|
||||
} from '@typebot.io/schemas/features/blocks/logic/condition/constants'
|
||||
|
||||
export const executeCondition = (
|
||||
block: ConditionBlock,
|
||||
@@ -16,9 +14,9 @@ export const executeCondition = (
|
||||
const passedCondition = block.items.find((item) => {
|
||||
const { content } = item
|
||||
const isConditionPassed =
|
||||
content.logicalOperator === LogicalOperator.AND
|
||||
? content.comparisons.every(executeComparison(variables))
|
||||
: content.comparisons.some(executeComparison(variables))
|
||||
content?.logicalOperator === LogicalOperator.AND
|
||||
? content.comparisons?.every(executeComparison(variables))
|
||||
: content?.comparisons?.some(executeComparison(variables))
|
||||
return isConditionPassed
|
||||
})
|
||||
return passedCondition ? passedCondition.outgoingEdgeId : block.outgoingEdgeId
|
||||
|
||||
@@ -8,7 +8,7 @@ export const executeScript = async (
|
||||
block: ScriptBlock,
|
||||
{ typebot: { variables } }: LogicState
|
||||
) => {
|
||||
if (!block.options.content) return
|
||||
if (!block.options?.content) return
|
||||
if (block.options.shouldExecuteInParentContext && isEmbedded) {
|
||||
sendEventToParent({
|
||||
codeToExecute: parseVariables(variables)(block.options.content),
|
||||
|
||||
@@ -8,6 +8,8 @@ export const executeSetVariable = (
|
||||
{ typebot: { variables }, updateVariableValue, updateVariables }: LogicState
|
||||
): EdgeId | undefined => {
|
||||
if (!block.options?.variableId) return block.outgoingEdgeId
|
||||
if (block.options.type !== undefined && block.options.type !== 'Custom')
|
||||
return block.outgoingEdgeId
|
||||
const evaluatedExpression = block.options.expressionToEvaluate
|
||||
? evaluateSetVariableExpression(variables)(
|
||||
block.options.expressionToEvaluate
|
||||
|
||||
@@ -9,10 +9,10 @@ export const fetchAndInjectTypebot = async (
|
||||
): Promise<LinkedTypebot | undefined> => {
|
||||
const { data, error } = isPreview
|
||||
? await sendRequest<{ typebot: Typebot }>(
|
||||
`/api/typebots/${block.options.typebotId}`
|
||||
`/api/typebots/${block.options?.typebotId}`
|
||||
)
|
||||
: await sendRequest<{ typebot: PublicTypebot }>(
|
||||
`${apiHost}/api/publicTypebots/${block.options.typebotId}`
|
||||
`${apiHost}/api/publicTypebots/${block.options?.typebotId}`
|
||||
)
|
||||
if (!data || error) return
|
||||
return injectLinkedTypebot(data.typebot)
|
||||
|
||||
@@ -21,12 +21,12 @@ export const executeTypebotLink = async (
|
||||
currentTypebotId,
|
||||
} = context
|
||||
const linkedTypebot = (
|
||||
block.options.typebotId === 'current'
|
||||
block.options?.typebotId === 'current'
|
||||
? typebot
|
||||
: [typebot, ...linkedTypebots].find((typebot) =>
|
||||
'typebotId' in typebot
|
||||
? typebot.typebotId === block.options.typebotId
|
||||
: typebot.id === block.options.typebotId
|
||||
? typebot.typebotId === block.options?.typebotId
|
||||
: typebot.id === block.options?.typebotId
|
||||
) ?? (await fetchAndInjectTypebot(block, context))
|
||||
) as PublicTypebot | LinkedTypebot | undefined
|
||||
if (!linkedTypebot) {
|
||||
@@ -47,13 +47,13 @@ export const executeTypebotLink = async (
|
||||
'typebotId' in linkedTypebot ? linkedTypebot.typebotId : linkedTypebot.id
|
||||
)
|
||||
const nextGroupId =
|
||||
block.options.groupId ??
|
||||
block.options?.groupId ??
|
||||
linkedTypebot.groups.find((b) => b.blocks.some((s) => s.type === 'start'))
|
||||
?.id
|
||||
if (!nextGroupId) return { nextEdgeId: block.outgoingEdgeId }
|
||||
const newEdge: Edge = {
|
||||
id: (Math.random() * 1000).toString(),
|
||||
from: { blockId: '', groupId: '' },
|
||||
from: { blockId: '' },
|
||||
to: {
|
||||
groupId: nextGroupId,
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ export const executeWait = async (
|
||||
block: WaitBlock,
|
||||
{ typebot: { variables } }: LogicState
|
||||
) => {
|
||||
if (!block.options.secondsToWaitFor) return block.outgoingEdgeId
|
||||
if (!block.options?.secondsToWaitFor) return block.outgoingEdgeId
|
||||
const parsedSecondsToWaitFor = parseVariables(variables)(
|
||||
block.options.secondsToWaitFor
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {
|
||||
Background,
|
||||
BackgroundType,
|
||||
ChatTheme,
|
||||
ContainerColors,
|
||||
GeneralTheme,
|
||||
InputColors,
|
||||
Theme,
|
||||
} from '@typebot.io/schemas'
|
||||
import { BackgroundType } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
|
||||
const cssVariableNames = {
|
||||
general: {
|
||||
|
||||
Reference in New Issue
Block a user