import { InputBlock, PublicBot, ResultHeaderCell, Block, Bot, BotLinkBlock, Variable, } from "indite-js/schemas"; import { byId, isNotDefined } from "indite-js/lib"; import { isInputBlock } from "indite-js/schemas/helpers"; import { InputBlockType } from "indite-js/schemas/features/blocks/inputs/constants"; import { LogicBlockType } from "indite-js/schemas/features/blocks/logic/constants"; import { parseResultHeader } from "indite-js/results/parseResultHeader"; export const parseSampleResult = ( bot: Pick, linkedBots: (Bot | PublicBot)[], userEmail?: string ) => async ( currentGroupId: string, variables: Variable[] ): Promise> => { const header = parseResultHeader(bot, linkedBots); const linkedInputBlocks = await extractLinkedInputBlocks( bot, linkedBots )(currentGroupId); return { message: "This is a sample result, it has been generated ⬇️", submittedAt: new Date().toISOString(), ...parseResultSample(linkedInputBlocks, header, variables, userEmail), }; }; const extractLinkedInputBlocks = ( bot: Pick, linkedBots: (Bot | PublicBot)[] ) => async ( currentGroupId?: string, direction: "backward" | "forward" = "backward" ): Promise => { const previousLinkedBotBlocks = walkEdgesAndExtract( "linkedBot", direction, bot )({ groupId: currentGroupId, }) as BotLinkBlock[]; const linkedBotInputs = previousLinkedBotBlocks.length > 0 ? await Promise.all( previousLinkedBotBlocks.map((linkedBot) => { const linkedChatbot = linkedBots.find((t) => "botId" in t ? t.botId === linkedBot.options?.botId : t.id === linkedBot.options?.botId ); if (!linkedChatbot) return []; return extractLinkedInputBlocks(linkedChatbot, linkedBots)( linkedBot.options?.groupId, "forward" ); }) ) : []; return ( walkEdgesAndExtract( "input", direction, bot )({ groupId: currentGroupId, }) as InputBlock[] ).concat(linkedBotInputs.flatMap((l) => l)); }; const parseResultSample = ( inputBlocks: InputBlock[], headerCells: ResultHeaderCell[], variables: Variable[], userEmail?: string ) => headerCells.reduce>( (resultSample, cell) => { const inputBlock = inputBlocks.find((inputBlock) => cell.blocks?.some((block) => block.id === inputBlock.id) ); if (isNotDefined(inputBlock)) { if (cell.variableIds) { const variableValue = variables.find( (variable) => cell.variableIds?.includes(variable.id) && variable.value )?.value; return { ...resultSample, [cell.label]: variableValue ?? "content", }; } return resultSample; } const variableValue = variables.find( (variable) => cell.variableIds?.includes(variable.id) && variable.value )?.value; const value = variableValue ?? getSampleValue(inputBlock, userEmail); return { ...resultSample, [cell.label]: value, }; }, {} ); const getSampleValue = (block: InputBlock, userEmail?: string): string => { switch (block.type) { case InputBlockType.CHOICE: return block.options?.isMultipleChoice ? block.items.map((item) => item.content).join(", ") : block.items[0]?.content ?? "Item"; case InputBlockType.DATE: return new Date().toUTCString(); case InputBlockType.EMAIL: return userEmail ?? "test@email.com"; case InputBlockType.NUMBER: return "20"; case InputBlockType.PHONE: return "+33665566773"; case InputBlockType.TEXT: return "answer value"; case InputBlockType.URL: return "https://test.com"; case InputBlockType.FILE: return "https://domain.com/fake-file.png"; case InputBlockType.RATING: return "8"; case InputBlockType.PAYMENT: return "Success"; case InputBlockType.PICTURE_CHOICE: return block.options?.isMultipleChoice ? block.items.map((item) => item.title ?? item.pictureSrc).join(", ") : block.items[0]?.title ?? block.items[0]?.pictureSrc ?? "Item"; } }; const walkEdgesAndExtract = ( type: "input" | "linkedBot", direction: "backward" | "forward", bot: Pick ) => ({ groupId }: { groupId?: string }): Block[] => { const currentGroupId = groupId ?? (bot.groups.find((b) => b.blocks[0].type === "start")?.id as string); const blocksInGroup = extractBlocksInGroup( type, bot )({ groupId: currentGroupId, }); const otherGroupIds = getGroupIdsLinkedToGroup( bot, direction )(currentGroupId); return blocksInGroup.concat( otherGroupIds.flatMap((groupId) => extractBlocksInGroup(type, bot)({ groupId }) ) ); }; const getGroupIdsLinkedToGroup = ( bot: Pick, direction: "backward" | "forward", visitedGroupIds: string[] = [] ) => (groupId: string): string[] => { const linkedGroupIds = bot.edges.reduce((groupIds, edge) => { const fromGroupId = bot.groups.find((g) => g.blocks.some( (b) => "blockId" in edge.from && b.id === edge.from.blockId ) )?.id; if (!fromGroupId) return groupIds; if (direction === "forward") { if ( (!visitedGroupIds || !visitedGroupIds?.includes(edge.to.groupId)) && fromGroupId === groupId ) { visitedGroupIds.push(edge.to.groupId); return groupIds.concat(edge.to.groupId); } return groupIds; } if ( !visitedGroupIds.includes(fromGroupId) && edge.to.groupId === groupId ) { visitedGroupIds.push(fromGroupId); return groupIds.concat(fromGroupId); } return groupIds; }, []); return linkedGroupIds.concat( linkedGroupIds.flatMap( getGroupIdsLinkedToGroup(bot, direction, visitedGroupIds) ) ); }; const extractBlocksInGroup = ( type: "input" | "linkedBot", bot: Pick ) => ({ groupId, blockId }: { groupId: string; blockId?: string }) => { const currentGroup = bot.groups.find(byId(groupId)); if (!currentGroup) return []; const blocks: Block[] = []; for (const block of currentGroup.blocks) { if (block.id === blockId) break; if (type === "input" && isInputBlock(block)) blocks.push(block); if (type === "linkedBot" && block.type === LogicBlockType.BOT_LINK) blocks.push(block); } return blocks; };