@ -5,7 +5,6 @@
|
||||
"version": "0.1.0",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "pnpm tsc --noEmit && tsup",
|
||||
"dev": "tsup --watch"
|
||||
@ -41,7 +40,7 @@
|
||||
"@typebot.io/tsconfig": "workspace:*",
|
||||
"tsup": "6.5.0",
|
||||
"typebot-js": "workspace:*",
|
||||
"typescript": "4.9.4",
|
||||
"typescript": "5.2.2",
|
||||
"@typebot.io/lib": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useAnswers } from '../../../providers/AnswersProvider'
|
||||
import { InputBlock, InputBlockType } from '@typebot.io/schemas'
|
||||
import { InputBlock } from '@typebot.io/schemas'
|
||||
import { GuestBubble } from './bubbles/GuestBubble'
|
||||
import { byId } from '@typebot.io/lib'
|
||||
import { InputSubmitContent } from '@/types'
|
||||
@ -17,6 +17,9 @@ import { ChoiceForm } from '@/features/blocks/inputs/buttons'
|
||||
import { PaymentForm } from '@/features/blocks/inputs/payment'
|
||||
import { RatingForm } from '@/features/blocks/inputs/rating'
|
||||
import { FileUploadForm } from '@/features/blocks/inputs/fileUpload'
|
||||
import { defaultSettings } from '@typebot.io/schemas/features/typebot/settings/constants'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { getBlockById } from '@typebot.io/lib/getBlockById'
|
||||
|
||||
export const InputChatBlock = ({
|
||||
block,
|
||||
@ -39,9 +42,11 @@ export const InputChatBlock = ({
|
||||
const [answer, setAnswer] = useState<string>()
|
||||
const [isEditting, setIsEditting] = useState(false)
|
||||
|
||||
const { variableId } = block.options
|
||||
const { variableId } = block.options ?? {}
|
||||
const defaultValue =
|
||||
(typebot.settings.general.isInputPrefillEnabled ?? true) && variableId
|
||||
(typebot.settings.general?.isInputPrefillEnabled ??
|
||||
defaultSettings.general.isInputPrefillEnabled) &&
|
||||
variableId
|
||||
? typebot.variables.find(
|
||||
(variable) =>
|
||||
variable.name === typebot.variables.find(byId(variableId))?.name
|
||||
@ -51,14 +56,17 @@ export const InputChatBlock = ({
|
||||
const handleSubmit = async ({ label, value, itemId }: InputSubmitContent) => {
|
||||
setAnswer(label ?? value)
|
||||
const isRetry = !isInputValid(value, block.type)
|
||||
if (!isRetry && addAnswer)
|
||||
if (!isRetry && addAnswer) {
|
||||
const { group } = getBlockById(block.id, typebot.groups)
|
||||
await addAnswer(typebot.variables)({
|
||||
blockId: block.id,
|
||||
groupId: block.groupId,
|
||||
groupId: group.id,
|
||||
content: value,
|
||||
variableId,
|
||||
uploadedFiles: block.type === InputBlockType.FILE,
|
||||
})
|
||||
}
|
||||
|
||||
if (!isEditting) onTransitionEnd({ label, value, itemId }, isRetry)
|
||||
setIsEditting(false)
|
||||
}
|
||||
@ -66,11 +74,11 @@ export const InputChatBlock = ({
|
||||
if (isLoading) return null
|
||||
|
||||
if (answer) {
|
||||
const avatarUrl = typebot.theme.chat.guestAvatar?.url
|
||||
const avatarUrl = typebot.theme.chat?.guestAvatar?.url
|
||||
return (
|
||||
<GuestBubble
|
||||
message={answer}
|
||||
showAvatar={typebot.theme.chat.guestAvatar?.isEnabled ?? false}
|
||||
showAvatar={typebot.theme.chat?.guestAvatar?.isEnabled ?? false}
|
||||
avatarSrc={avatarUrl && parseVariables(typebot.variables)(avatarUrl)}
|
||||
/>
|
||||
)
|
||||
@ -160,7 +168,7 @@ const Input = ({
|
||||
<PaymentForm
|
||||
options={block.options}
|
||||
onSuccess={() =>
|
||||
onSubmit({ value: block.options.labels.success ?? 'Success' })
|
||||
onSubmit({ value: block.options?.labels?.success ?? 'Success' })
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
@ -3,7 +3,8 @@ import { EmbedBubble } from '@/features/blocks/bubbles/embed'
|
||||
import { ImageBubble } from '@/features/blocks/bubbles/image'
|
||||
import { TextBubble } from '@/features/blocks/bubbles/textBubble'
|
||||
import { VideoBubble } from '@/features/blocks/bubbles/video'
|
||||
import { BubbleBlock, BubbleBlockType } from '@typebot.io/schemas'
|
||||
import { BubbleBlock } from '@typebot.io/schemas'
|
||||
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
|
||||
|
||||
type Props = {
|
||||
block: BubbleBlock
|
||||
@ -23,7 +24,7 @@ export const HostBubble = ({ block, onTransitionEnd }: Props) => {
|
||||
case BubbleBlockType.AUDIO:
|
||||
return (
|
||||
<AudioBubble
|
||||
url={block.content.url}
|
||||
url={block.content?.url}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
/>
|
||||
)
|
||||
|
@ -15,7 +15,6 @@ import {
|
||||
import {
|
||||
BubbleBlock,
|
||||
InputBlock,
|
||||
LogicBlockType,
|
||||
PublicTypebot,
|
||||
Block,
|
||||
} from '@typebot.io/schemas'
|
||||
@ -30,6 +29,8 @@ import { executeIntegration } from '@/utils/executeIntegration'
|
||||
import { executeLogic } from '@/utils/executeLogic'
|
||||
import { blockCanBeRetried, parseRetryBlock } from '@/utils/inputs'
|
||||
import { PopupBlockedToast } from '../PopupBlockedToast'
|
||||
import { LogicBlockType } from '@typebot.io/schemas/features/blocks/logic/constants'
|
||||
import { getBlockById } from '@typebot.io/lib/getBlockById'
|
||||
|
||||
type ChatGroupProps = {
|
||||
blocks: Block[]
|
||||
@ -143,19 +144,20 @@ export const ChatGroup = ({
|
||||
if (blockedPopupUrl) setBlockedPopupUrl(blockedPopupUrl)
|
||||
const isRedirecting =
|
||||
currentBlock.type === LogicBlockType.REDIRECT &&
|
||||
currentBlock.options.isNewTab === false
|
||||
currentBlock.options?.isNewTab === false
|
||||
if (isRedirecting) return
|
||||
nextEdgeId
|
||||
? onGroupEnd({ edgeId: nextEdgeId, updatedTypebot: linkedTypebot })
|
||||
: displayNextBlock()
|
||||
}
|
||||
if (isIntegrationBlock(currentBlock)) {
|
||||
const { group } = getBlockById(currentBlock.id, typebot.groups)
|
||||
const nextEdgeId = await executeIntegration({
|
||||
block: currentBlock,
|
||||
context: {
|
||||
apiHost,
|
||||
typebotId: currentTypebotId,
|
||||
groupId: currentBlock.groupId,
|
||||
groupId: group.id,
|
||||
blockId: currentBlock.id,
|
||||
variables: typebot.variables,
|
||||
isPreview,
|
||||
@ -181,10 +183,12 @@ export const ChatGroup = ({
|
||||
scroll()
|
||||
const currentBlock = [...processedBlocks].pop()
|
||||
if (currentBlock) {
|
||||
if (isRetry && blockCanBeRetried(currentBlock))
|
||||
if (isRetry && blockCanBeRetried(currentBlock)) {
|
||||
const { group } = getBlockById(currentBlock.id, typebot.groups)
|
||||
return insertBlockInStack(
|
||||
parseRetryBlock(currentBlock, typebot.variables, createEdge)
|
||||
parseRetryBlock(currentBlock, group.id, typebot.variables, createEdge)
|
||||
)
|
||||
}
|
||||
if (
|
||||
isInputBlock(currentBlock) &&
|
||||
currentBlock.options?.variableId &&
|
||||
@ -196,7 +200,7 @@ export const ChatGroup = ({
|
||||
)
|
||||
}
|
||||
const isSingleChoiceBlock =
|
||||
isChoiceInput(currentBlock) && !currentBlock.options.isMultipleChoice
|
||||
isChoiceInput(currentBlock) && !currentBlock.options?.isMultipleChoice
|
||||
if (isSingleChoiceBlock) {
|
||||
const nextEdgeId = currentBlock.items.find(
|
||||
byId(answerContent?.itemId)
|
||||
@ -214,7 +218,7 @@ export const ChatGroup = ({
|
||||
nextBlock ? insertBlockInStack(nextBlock) : onGroupEnd({})
|
||||
}
|
||||
|
||||
const avatarSrc = typebot.theme.chat.hostAvatar?.url
|
||||
const avatarSrc = typebot.theme.chat?.hostAvatar?.url
|
||||
|
||||
return (
|
||||
<div className="flex w-full" data-group-name={groupTitle}>
|
||||
@ -224,10 +228,10 @@ export const ChatGroup = ({
|
||||
key={idx}
|
||||
displayChunk={chunk}
|
||||
hostAvatar={{
|
||||
isEnabled: typebot.theme.chat.hostAvatar?.isEnabled ?? true,
|
||||
isEnabled: typebot.theme.chat?.hostAvatar?.isEnabled ?? true,
|
||||
src: avatarSrc && parseVariables(typebot.variables)(avatarSrc),
|
||||
}}
|
||||
hasGuestAvatar={typebot.theme.chat.guestAvatar?.isEnabled ?? false}
|
||||
hasGuestAvatar={typebot.theme.chat?.guestAvatar?.isEnabled ?? false}
|
||||
onDisplayNextBlock={displayNextBlock}
|
||||
keepShowingHostAvatar={keepShowingHostAvatar}
|
||||
blockedPopupUrl={blockedPopupUrl}
|
||||
|
@ -57,7 +57,7 @@ export const ConversationContainer = ({
|
||||
if (!nextGroup) return
|
||||
onNewGroupVisible({
|
||||
id: 'edgeId',
|
||||
from: { groupId: 'block', blockId: 'block' },
|
||||
from: { blockId: 'block' },
|
||||
to: { groupId },
|
||||
})
|
||||
return setDisplayedGroups([
|
||||
|
@ -7,7 +7,6 @@ import { ConversationContainer } from './ConversationContainer'
|
||||
import { AnswersProvider } from '../providers/AnswersProvider'
|
||||
import {
|
||||
AnswerInput,
|
||||
BackgroundType,
|
||||
Edge,
|
||||
PublicTypebot,
|
||||
VariableWithValue,
|
||||
@ -16,6 +15,7 @@ import { Log } from '@typebot.io/prisma'
|
||||
import { LiteBadge } from './LiteBadge'
|
||||
import { isNotEmpty } from '@typebot.io/lib'
|
||||
import { getViewerUrl } from '@typebot.io/lib/getViewerUrl'
|
||||
import { BackgroundType } from '@typebot.io/schemas/features/typebot/theme/constants'
|
||||
|
||||
export type TypebotViewerProps = {
|
||||
typebot: Omit<PublicTypebot, 'updatedAt' | 'createdAt'>
|
||||
@ -78,7 +78,7 @@ export const TypebotViewer = ({
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `@import url('https://fonts.googleapis.com/css2?family=${
|
||||
typebot.theme.general.font ?? 'Open Sans'
|
||||
typebot.theme.general?.font ?? 'Open Sans'
|
||||
}:ital,wght@0,300;0,400;0,600;1,300;1,400;1,600&display=swap');`,
|
||||
}}
|
||||
/>
|
||||
@ -112,7 +112,7 @@ export const TypebotViewer = ({
|
||||
startGroupId={startGroupId}
|
||||
/>
|
||||
</div>
|
||||
{typebot.settings.general.isBrandingEnabled && <LiteBadge />}
|
||||
{typebot.settings.general?.isBrandingEnabled && <LiteBadge />}
|
||||
</div>
|
||||
</AnswersProvider>
|
||||
</TypebotProvider>
|
||||
|
@ -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: {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { GoogleAnalyticsOptions } from '@typebot.io/schemas'
|
||||
import { GoogleAnalyticsBlock } from '@typebot.io/schemas'
|
||||
|
||||
declare const gtag: (
|
||||
type: string,
|
||||
@ -33,7 +33,7 @@ const initGoogleAnalytics = (id: string): Promise<void> =>
|
||||
if (existingScript) resolve()
|
||||
})
|
||||
|
||||
export const sendGaEvent = (options: GoogleAnalyticsOptions) => {
|
||||
export const sendGaEvent = (options: GoogleAnalyticsBlock['options']) => {
|
||||
if (!options) return
|
||||
gtag('event', options.action, {
|
||||
event_category: options.category,
|
||||
|
@ -133,7 +133,7 @@ export const TypebotProvider = ({
|
||||
groups: [...localTypebot.groups, ...typebotToInject.groups],
|
||||
variables: [...localTypebot.variables, ...typebotToInject.variables],
|
||||
edges: [...localTypebot.edges, ...typebotToInject.edges],
|
||||
}
|
||||
} as TypebotViewerProps['typebot']
|
||||
setLocalTypebot(updatedTypebot)
|
||||
return typebotToInject
|
||||
}
|
||||
|
@ -1,12 +1,8 @@
|
||||
import {
|
||||
BubbleBlock,
|
||||
BubbleBlockType,
|
||||
InputBlock,
|
||||
InputBlockType,
|
||||
Block,
|
||||
} from '@typebot.io/schemas'
|
||||
import { BubbleBlock, InputBlock, Block } from '@typebot.io/schemas'
|
||||
import { isBubbleBlock, isInputBlock } from '@typebot.io/lib'
|
||||
import type { TypebotPostMessageData } from 'typebot-js'
|
||||
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
|
||||
export const getLastChatBlockType = (
|
||||
blocks: Block[]
|
||||
|
@ -4,7 +4,8 @@ import { executeGoogleSheetBlock } from '@/features/blocks/integrations/googleSh
|
||||
import { executeSendEmailBlock } from '@/features/blocks/integrations/sendEmail'
|
||||
import { executeWebhook } from '@/features/blocks/integrations/webhook'
|
||||
import { IntegrationState } from '@/types'
|
||||
import { IntegrationBlock, IntegrationBlockType } from '@typebot.io/schemas'
|
||||
import { IntegrationBlock } from '@typebot.io/schemas'
|
||||
import { IntegrationBlockType } from '@typebot.io/schemas/features/blocks/integrations/constants'
|
||||
|
||||
export const executeIntegration = ({
|
||||
block,
|
||||
|
@ -6,8 +6,9 @@ import { executeTypebotLink } from '@/features/blocks/logic/typebotLink'
|
||||
import { executeWait } from '@/features/blocks/logic/wait'
|
||||
import { LinkedTypebot } from '@/providers/TypebotProvider'
|
||||
import { EdgeId, LogicState } from '@/types'
|
||||
import { LogicBlock, LogicBlockType } from '@typebot.io/schemas'
|
||||
import { LogicBlock } from '@typebot.io/schemas'
|
||||
import { executeScript } from '@/features/blocks/logic/script/executeScript'
|
||||
import { LogicBlockType } from '@typebot.io/schemas/features/blocks/logic/constants'
|
||||
|
||||
export const executeLogic = async (
|
||||
block: LogicBlock,
|
||||
|
@ -4,16 +4,16 @@ import { validateUrl } from '@/features/blocks/inputs/url'
|
||||
import { parseVariables } from '@/features/variables'
|
||||
import {
|
||||
BubbleBlock,
|
||||
BubbleBlockType,
|
||||
Edge,
|
||||
EmailInputBlock,
|
||||
InputBlockType,
|
||||
PhoneNumberInputBlock,
|
||||
Block,
|
||||
UrlInputBlock,
|
||||
Variable,
|
||||
} from '@typebot.io/schemas'
|
||||
import { isInputBlock } from '@typebot.io/lib'
|
||||
import { isDefined, isInputBlock } from '@typebot.io/lib'
|
||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
|
||||
|
||||
export const isInputValid = (
|
||||
inputValue: string,
|
||||
@ -33,23 +33,25 @@ export const isInputValid = (
|
||||
export const blockCanBeRetried = (
|
||||
block: Block
|
||||
): block is EmailInputBlock | UrlInputBlock | PhoneNumberInputBlock =>
|
||||
isInputBlock(block) && 'retryMessageContent' in block.options
|
||||
isInputBlock(block) &&
|
||||
isDefined(block.options) &&
|
||||
'retryMessageContent' in block.options
|
||||
|
||||
export const parseRetryBlock = (
|
||||
block: EmailInputBlock | UrlInputBlock | PhoneNumberInputBlock,
|
||||
groupId: string,
|
||||
variables: Variable[],
|
||||
createEdge: (edge: Edge) => void
|
||||
): BubbleBlock => {
|
||||
const content = parseVariables(variables)(block.options.retryMessageContent)
|
||||
const content = parseVariables(variables)(block.options?.retryMessageContent)
|
||||
const newBlockId = block.id + Math.random() * 1000
|
||||
const newEdge: Edge = {
|
||||
id: (Math.random() * 1000).toString(),
|
||||
from: { blockId: newBlockId, groupId: block.groupId },
|
||||
to: { groupId: block.groupId, blockId: block.id },
|
||||
from: { blockId: newBlockId },
|
||||
to: { groupId, blockId: block.id },
|
||||
}
|
||||
createEdge(newEdge)
|
||||
return {
|
||||
groupId: block.groupId,
|
||||
id: newBlockId,
|
||||
type: BubbleBlockType.TEXT,
|
||||
content: {
|
||||
|
@ -4,7 +4,6 @@ export default defineConfig((options) => ({
|
||||
entry: ['src/index.ts'],
|
||||
sourcemap: true,
|
||||
minify: !options.watch,
|
||||
dts: true,
|
||||
format: ['esm', 'cjs'],
|
||||
loader: {
|
||||
'.css': 'text',
|
||||
|
@ -18,7 +18,7 @@
|
||||
"jest-environment-jsdom": "29.4.1",
|
||||
"prettier": "2.8.3",
|
||||
"ts-jest": "29.0.5",
|
||||
"typescript": "4.9.4",
|
||||
"typescript": "5.2.2",
|
||||
"@typebot.io/tsconfig": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user