2
0
Files
bot/packages/lib/results/parseResultHeader.ts
2024-01-12 10:16:01 +01:00

213 lines
6.0 KiB
TypeScript

import {
ResultWithAnswers,
ResultHeaderCell,
Group,
Variable,
InputBlock,
Typebot,
} from '@typebot.io/schemas'
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
import { isInputBlock, byId, isNotEmpty } from '../utils'
export const parseResultHeader = (
typebot: Pick<Typebot, 'groups' | 'variables'>,
linkedTypebots: Pick<Typebot, 'groups' | 'variables'>[] | undefined,
results?: ResultWithAnswers[]
): ResultHeaderCell[] => {
const parsedGroups = [
...typebot.groups,
...(linkedTypebots ?? []).flatMap(
(linkedTypebot) => linkedTypebot.groups as Group[]
),
]
const parsedVariables = [
...typebot.variables,
...(linkedTypebots ?? []).flatMap(
(linkedTypebot) => linkedTypebot.variables
),
]
const inputsResultHeader = parseInputsResultHeader({
groups: parsedGroups,
variables: parsedVariables,
})
return [
{ label: 'Submitted at', id: 'date' },
...inputsResultHeader,
...parseVariablesHeaders(parsedVariables, inputsResultHeader),
...parseResultsFromPreviousBotVersions(results ?? [], inputsResultHeader),
]
}
type ResultHeaderCellWithBlock = Omit<ResultHeaderCell, 'blocks'> & {
blocks: NonNullable<ResultHeaderCell['blocks']>
}
const parseInputsResultHeader = ({
groups,
variables,
}: {
groups: Group[]
variables: Variable[]
}): ResultHeaderCellWithBlock[] =>
(
groups
.flatMap((group) =>
group.blocks.map((block) => ({
...block,
groupTitle: group.title,
groupId: group.id,
}))
)
.filter((block) => isInputBlock(block)) as (InputBlock & {
groupId: string
groupTitle: string
})[]
).reduce<ResultHeaderCellWithBlock[]>((existingHeaders, inputBlock) => {
if (
existingHeaders.some(
(existingHeader) =>
inputBlock.options?.variableId &&
existingHeader.variableIds?.includes(inputBlock.options.variableId)
)
)
return existingHeaders
const matchedVariableName =
inputBlock.options?.variableId &&
variables.find(byId(inputBlock.options.variableId))?.name
let label = matchedVariableName ?? inputBlock.groupTitle
const headerWithSameLabel = existingHeaders.find((h) => h.label === label)
if (headerWithSameLabel) {
const shouldMerge = headerWithSameLabel.blocks?.some(
(block) => block.id === inputBlock.id
)
if (shouldMerge) {
const updatedHeaderCell: ResultHeaderCellWithBlock = {
...headerWithSameLabel,
variableIds:
headerWithSameLabel.variableIds && inputBlock.options?.variableId
? headerWithSameLabel.variableIds.concat([
inputBlock.options.variableId,
])
: undefined,
blocks: headerWithSameLabel.blocks.concat({
id: inputBlock.id,
groupId: inputBlock.groupId,
}),
}
return [
...existingHeaders.filter(
(existingHeader) => existingHeader.label !== label
),
updatedHeaderCell,
]
}
const totalPrevious = existingHeaders.filter((h) =>
h.label.includes(label)
).length
const newHeaderCell: ResultHeaderCellWithBlock = {
id: inputBlock.id,
label: label + ` (${totalPrevious})`,
blocks: [
{
id: inputBlock.id,
groupId: inputBlock.groupId,
},
],
blockType: inputBlock.type,
variableIds: inputBlock.options?.variableId
? [inputBlock.options.variableId]
: undefined,
}
return [...existingHeaders, newHeaderCell]
}
const newHeaderCell: ResultHeaderCellWithBlock = {
id: inputBlock.id,
label,
blocks: [
{
id: inputBlock.id,
groupId: inputBlock.groupId,
},
],
blockType: inputBlock.type,
variableIds: inputBlock.options?.variableId
? [inputBlock.options.variableId]
: undefined,
}
return [...existingHeaders, newHeaderCell]
}, [])
const parseVariablesHeaders = (
variables: Variable[],
existingInputResultHeaders: ResultHeaderCell[]
) =>
variables.reduce<ResultHeaderCell[]>((existingHeaders, variable) => {
if (
existingInputResultHeaders.some((existingInputResultHeader) =>
existingInputResultHeader.variableIds?.includes(variable.id)
)
)
return existingHeaders
const headerCellWithSameLabel = existingHeaders.find(
(existingHeader) => existingHeader.label === variable.name
)
if (headerCellWithSameLabel) {
const updatedHeaderCell: ResultHeaderCell = {
...headerCellWithSameLabel,
variableIds: headerCellWithSameLabel.variableIds?.concat([variable.id]),
}
return [
...existingHeaders.filter((h) => h.label !== variable.name),
updatedHeaderCell,
]
}
const newHeaderCell: ResultHeaderCell = {
id: variable.id,
label: variable.name,
variableIds: [variable.id],
}
return [...existingHeaders, newHeaderCell]
}, [])
const parseResultsFromPreviousBotVersions = (
results: ResultWithAnswers[],
existingInputResultHeaders: ResultHeaderCell[]
): ResultHeaderCell[] =>
results
.flatMap((result) => result.answers)
.filter(
(answer) =>
!answer.variableId &&
existingInputResultHeaders.every(
(header) => header.id !== answer.blockId
) &&
isNotEmpty(answer.content)
)
.reduce<ResultHeaderCell[]>((existingHeaders, answer) => {
if (
existingHeaders.some(
(existingHeader) => existingHeader.id === answer.blockId
)
)
return existingHeaders
return [
...existingHeaders,
{
id: answer.blockId,
label: `${answer.blockId} (deleted block)`,
blocks: [
{
id: answer.blockId,
groupId: answer.groupId,
},
],
blockType: InputBlockType.TEXT,
},
]
}, [])