✨ (theme) Add progress bar option (#1276)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced progress bar functionality across various components for a more interactive user experience. - Added progress tracking and display in chat sessions. - **Enhancements** - Adjusted layout height calculations in the settings and theme pages for consistency. - Improved theme customization options with progress bar styling and placement settings. - **Bug Fixes** - Fixed dynamic height calculation issues in settings and theme side menus by standardizing heights. - **Style** - Added custom styling properties for the progress bar in chat interfaces. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
130
packages/bot-engine/computeCurrentProgress.ts
Normal file
130
packages/bot-engine/computeCurrentProgress.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { blockHasItems, isDefined, isInputBlock, byId } from '@typebot.io/lib'
|
||||
import { getBlockById } from '@typebot.io/lib/getBlockById'
|
||||
import { Block, SessionState } from '@typebot.io/schemas'
|
||||
|
||||
type Props = {
|
||||
typebotsQueue: SessionState['typebotsQueue']
|
||||
progressMetadata: NonNullable<SessionState['progressMetadata']>
|
||||
currentInputBlockId: string
|
||||
}
|
||||
|
||||
export const computeCurrentProgress = ({
|
||||
typebotsQueue,
|
||||
progressMetadata,
|
||||
currentInputBlockId,
|
||||
}: Props) => {
|
||||
if (progressMetadata.totalAnswers === 0) return 0
|
||||
const paths = computePossibleNextInputBlocks({
|
||||
typebotsQueue: typebotsQueue,
|
||||
blockId: currentInputBlockId,
|
||||
visitedBlocks: {
|
||||
[typebotsQueue[0].typebot.id]: [],
|
||||
},
|
||||
currentPath: [],
|
||||
})
|
||||
|
||||
return (
|
||||
(progressMetadata.totalAnswers /
|
||||
(Math.max(...paths.map((b) => b.length)) +
|
||||
progressMetadata.totalAnswers)) *
|
||||
100
|
||||
)
|
||||
}
|
||||
|
||||
const computePossibleNextInputBlocks = ({
|
||||
currentPath,
|
||||
typebotsQueue,
|
||||
blockId,
|
||||
visitedBlocks,
|
||||
}: {
|
||||
currentPath: string[]
|
||||
typebotsQueue: SessionState['typebotsQueue']
|
||||
blockId: string
|
||||
visitedBlocks: {
|
||||
[key: string]: string[]
|
||||
}
|
||||
}): string[][] => {
|
||||
if (visitedBlocks[typebotsQueue[0].typebot.id].includes(blockId)) return []
|
||||
visitedBlocks[typebotsQueue[0].typebot.id].push(blockId)
|
||||
|
||||
const possibleNextInputBlocks: string[][] = []
|
||||
|
||||
const { block, group, blockIndex } = getBlockById(
|
||||
blockId,
|
||||
typebotsQueue[0].typebot.groups
|
||||
)
|
||||
|
||||
if (isInputBlock(block)) currentPath.push(block.id)
|
||||
|
||||
const outgoingEdgeIds = getBlockOutgoingEdgeIds(block)
|
||||
|
||||
for (const outgoingEdgeId of outgoingEdgeIds) {
|
||||
const to = typebotsQueue[0].typebot.edges.find(
|
||||
(e) => e.id === outgoingEdgeId
|
||||
)?.to
|
||||
if (!to) continue
|
||||
const blockId =
|
||||
to.blockId ??
|
||||
typebotsQueue[0].typebot.groups.find((g) => g.id === to.groupId)
|
||||
?.blocks[0].id
|
||||
if (!blockId) continue
|
||||
possibleNextInputBlocks.push(
|
||||
...computePossibleNextInputBlocks({
|
||||
typebotsQueue,
|
||||
blockId,
|
||||
visitedBlocks,
|
||||
currentPath,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
for (const block of group.blocks.slice(blockIndex + 1)) {
|
||||
possibleNextInputBlocks.push(
|
||||
...computePossibleNextInputBlocks({
|
||||
typebotsQueue,
|
||||
blockId: block.id,
|
||||
visitedBlocks,
|
||||
currentPath,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (outgoingEdgeIds.length > 0 || group.blocks.length !== blockIndex + 1)
|
||||
return possibleNextInputBlocks
|
||||
|
||||
if (typebotsQueue.length > 1) {
|
||||
const nextEdgeId = typebotsQueue[0].edgeIdToTriggerWhenDone
|
||||
const to = typebotsQueue[1].typebot.edges.find(byId(nextEdgeId))?.to
|
||||
if (!to) return possibleNextInputBlocks
|
||||
const blockId =
|
||||
to.blockId ??
|
||||
typebotsQueue[0].typebot.groups.find((g) => g.id === to.groupId)
|
||||
?.blocks[0].id
|
||||
if (blockId) {
|
||||
possibleNextInputBlocks.push(
|
||||
...computePossibleNextInputBlocks({
|
||||
typebotsQueue: typebotsQueue.slice(1),
|
||||
blockId,
|
||||
visitedBlocks: {
|
||||
...visitedBlocks,
|
||||
[typebotsQueue[1].typebot.id]: [],
|
||||
},
|
||||
currentPath,
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
possibleNextInputBlocks.push(currentPath)
|
||||
|
||||
return possibleNextInputBlocks
|
||||
}
|
||||
|
||||
const getBlockOutgoingEdgeIds = (block: Block) => {
|
||||
const edgeIds: string[] = []
|
||||
if (blockHasItems(block)) {
|
||||
edgeIds.push(...block.items.map((i) => i.outgoingEdgeId).filter(isDefined))
|
||||
}
|
||||
if (block.outgoingEdgeId) edgeIds.push(block.outgoingEdgeId)
|
||||
return edgeIds
|
||||
}
|
@ -357,6 +357,9 @@ const setNewAnswerInState =
|
||||
|
||||
return {
|
||||
...state,
|
||||
progressMetadata: state.progressMetadata
|
||||
? { totalAnswers: state.progressMetadata.totalAnswers + 1 }
|
||||
: undefined,
|
||||
typebotsQueue: state.typebotsQueue.map((typebot, index) =>
|
||||
index === 0
|
||||
? {
|
||||
|
@ -70,6 +70,13 @@ export const getNextGroup =
|
||||
...state.typebotsQueue.slice(2),
|
||||
],
|
||||
} satisfies SessionState
|
||||
if (state.progressMetadata)
|
||||
newSessionState.progressMetadata = {
|
||||
...state.progressMetadata,
|
||||
totalAnswers:
|
||||
state.progressMetadata.totalAnswers +
|
||||
state.typebotsQueue[0].answers.length,
|
||||
}
|
||||
const nextGroup = await getNextGroup(newSessionState)(nextEdgeId)
|
||||
newSessionState = nextGroup.newSessionState
|
||||
if (!nextGroup)
|
||||
|
@ -137,6 +137,11 @@ export const startSession = async ({
|
||||
startParams.type === 'preview'
|
||||
? undefined
|
||||
: typebot.settings.security?.allowedOrigins,
|
||||
progressMetadata: initialSessionState?.whatsApp
|
||||
? undefined
|
||||
: typebot.theme.general?.progressBar?.isEnabled
|
||||
? { totalAnswers: 0 }
|
||||
: undefined,
|
||||
...initialSessionState,
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user