🐛 (results) Fix bug preventing user from seeing linked typebots results
This commit is contained in:
@ -11,8 +11,8 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { useResults } from 'contexts/ResultsProvider'
|
||||
import React from 'react'
|
||||
import { HeaderIcon } from 'services/typebots/results'
|
||||
import { isDefined } from 'utils'
|
||||
import { HeaderIcon } from './ResultsTable/ResultsTable'
|
||||
|
||||
type Props = {
|
||||
resultIdx: number | null
|
||||
|
@ -27,7 +27,7 @@ export const ResultsContent = () => {
|
||||
const handleResultModalClose = () => setExpandedResultIndex(null)
|
||||
|
||||
const handleLogOpenIndex = (index: number) => () => {
|
||||
if (!results) return
|
||||
if (!results[index]) return
|
||||
setInspectingLogsResultId(results[index].id)
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@ import { ToolIcon, EyeIcon, EyeOffIcon, GripIcon } from 'assets/icons'
|
||||
import { ResultHeaderCell } from 'models'
|
||||
import React, { forwardRef, useState } from 'react'
|
||||
import { isNotDefined } from 'utils'
|
||||
import { HeaderIcon } from './ResultsTable'
|
||||
import {
|
||||
DndContext,
|
||||
closestCenter,
|
||||
@ -35,6 +34,7 @@ import {
|
||||
useSortable,
|
||||
arrayMove,
|
||||
} from '@dnd-kit/sortable'
|
||||
import { HeaderIcon } from 'services/typebots/results'
|
||||
|
||||
type Props = {
|
||||
resultHeader: ResultHeaderCell[]
|
||||
|
@ -93,18 +93,39 @@ export const ResultsActionButtons = ({
|
||||
: tableData.filter((data) =>
|
||||
selectedResultsId.includes(data.id.plainText)
|
||||
)
|
||||
|
||||
const fields = typebot?.resultsTablePreferences?.columnsOrder
|
||||
? typebot.resultsTablePreferences.columnsOrder.reduce<string[]>(
|
||||
(currentHeaderLabels, columnId) => {
|
||||
if (
|
||||
typebot.resultsTablePreferences?.columnsVisibility[columnId] ===
|
||||
false
|
||||
)
|
||||
return currentHeaderLabels
|
||||
const columnLabel = resultHeader.find(
|
||||
(headerCell) => headerCell.id === columnId
|
||||
)?.label
|
||||
if (!columnLabel) return currentHeaderLabels
|
||||
return [...currentHeaderLabels, columnLabel]
|
||||
},
|
||||
[]
|
||||
)
|
||||
: resultHeader.map((headerCell) => headerCell.label)
|
||||
|
||||
const data = dataToUnparse.map<{ [key: string]: string }>((data) => {
|
||||
const newObject: { [key: string]: string } = {}
|
||||
fields?.forEach((field) => {
|
||||
console.log(data[field])
|
||||
newObject[field] = data[field]?.plainText
|
||||
})
|
||||
return newObject
|
||||
})
|
||||
|
||||
const csvData = new Blob(
|
||||
[
|
||||
unparse({
|
||||
data: dataToUnparse.map<{ [key: string]: string }>((data) => {
|
||||
const newObject: { [key: string]: string } = {}
|
||||
Object.keys(data).forEach((key) => {
|
||||
newObject[key] = (data[key] as { plainText: string })
|
||||
.plainText as string
|
||||
})
|
||||
return newObject
|
||||
}),
|
||||
fields: resultHeader.map((h) => h.label),
|
||||
data,
|
||||
fields,
|
||||
}),
|
||||
],
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
Stack,
|
||||
Text,
|
||||
} from '@chakra-ui/react'
|
||||
import { AlignLeftTextIcon, CalendarIcon, CodeIcon } from 'assets/icons'
|
||||
import { AlignLeftTextIcon } from 'assets/icons'
|
||||
import { ResultHeaderCell, ResultsTablePreferences } from 'models'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { LoadingRows } from './LoadingRows'
|
||||
@ -18,14 +18,13 @@ import {
|
||||
ColumnOrderState,
|
||||
ColumnDef,
|
||||
} from '@tanstack/react-table'
|
||||
import { BlockIcon } from 'components/editor/BlocksSideBar/BlockIcon'
|
||||
import { ColumnSettingsButton } from './ColumnsSettingsButton'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { useDebounce } from 'use-debounce'
|
||||
import { ResultsActionButtons } from './ResultsActionButtons'
|
||||
import { Row } from './Row'
|
||||
import { HeaderRow } from './HeaderRow'
|
||||
import { CellValueType, TableData } from 'services/typebots/results'
|
||||
import { CellValueType, HeaderIcon, TableData } from 'services/typebots/results'
|
||||
|
||||
type ResultsTableProps = {
|
||||
resultHeader: ResultHeaderCell[]
|
||||
@ -112,7 +111,7 @@ export const ResultsTable = ({
|
||||
...resultHeader.map<ColumnDef<TableData>>((header) => ({
|
||||
id: header.id,
|
||||
accessorKey: header.label,
|
||||
size: header.isLong ? 400 : 200,
|
||||
size: 200,
|
||||
header: () => (
|
||||
<HStack overflow="hidden" data-testid={`${header.label} header`}>
|
||||
<HeaderIcon header={header} />
|
||||
@ -147,8 +146,7 @@ export const ResultsTable = ({
|
||||
),
|
||||
},
|
||||
],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[resultHeader]
|
||||
[onLogOpenIndex, resultHeader]
|
||||
)
|
||||
|
||||
const instance = useReactTable({
|
||||
@ -258,12 +256,3 @@ const IndeterminateCheckbox = React.forwardRef(
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export const HeaderIcon = ({ header }: { header: ResultHeaderCell }) =>
|
||||
header.blockType ? (
|
||||
<BlockIcon type={header.blockType} />
|
||||
) : header.variableId ? (
|
||||
<CodeIcon />
|
||||
) : (
|
||||
<CalendarIcon />
|
||||
)
|
||||
|
@ -29,12 +29,11 @@ export const EditableUrl = ({
|
||||
const [value, setValue] = useState(pathname)
|
||||
|
||||
const handleSubmit = (newPathname: string) => {
|
||||
if (/^[a-z]+(-[a-z]+)*$/.test(newPathname))
|
||||
return onPathnameChange(newPathname)
|
||||
if (/^[a-z0-9-]*$/.test(newPathname)) return onPathnameChange(newPathname)
|
||||
setValue(pathname)
|
||||
showToast({
|
||||
title: 'Invalid ID',
|
||||
description: 'Should contain only contain letters and dashes.',
|
||||
description: 'Should contain only contain letters, numbers and dashes.',
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
useResults as useFetchResults,
|
||||
} from 'services/typebots/results'
|
||||
import { KeyedMutator } from 'swr'
|
||||
import { isDefined, parseResultHeader } from 'utils'
|
||||
import { parseResultHeader } from 'utils'
|
||||
import { useTypebot } from './TypebotContext'
|
||||
|
||||
const resultsContext = createContext<{
|
||||
@ -46,19 +46,17 @@ export const ResultsProvider = ({
|
||||
typebotId,
|
||||
})
|
||||
|
||||
console.log(data?.flatMap((d) => d.results) ?? [])
|
||||
|
||||
const fetchMore = () => setSize((state) => state + 1)
|
||||
|
||||
const groupsAndVariables = {
|
||||
groups: [
|
||||
...(publishedTypebot?.groups ?? []),
|
||||
...(linkedTypebots?.flatMap((t) => t.groups) ?? []),
|
||||
].filter(isDefined),
|
||||
variables: [
|
||||
...(publishedTypebot?.variables ?? []),
|
||||
...(linkedTypebots?.flatMap((t) => t.variables) ?? []),
|
||||
].filter(isDefined),
|
||||
}
|
||||
const resultHeader = parseResultHeader(groupsAndVariables)
|
||||
const resultHeader = useMemo(
|
||||
() =>
|
||||
publishedTypebot
|
||||
? parseResultHeader(publishedTypebot, linkedTypebots)
|
||||
: [],
|
||||
[linkedTypebots, publishedTypebot]
|
||||
)
|
||||
|
||||
const tableData = useMemo(
|
||||
() =>
|
||||
@ -68,8 +66,7 @@ export const ResultsProvider = ({
|
||||
resultHeader
|
||||
)
|
||||
: [],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[publishedTypebot?.id, resultHeader.length, data]
|
||||
[publishedTypebot, data, resultHeader]
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -45,7 +45,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
],
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
select: { name: true, id: true, groups: true },
|
||||
select: { name: true, id: true, groups: true, variables: true },
|
||||
})
|
||||
return res.send({ typebots })
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ test('should display invoices', async ({ page }) => {
|
||||
await page.click('text=Billing & Usage')
|
||||
await expect(page.locator('text="Invoices"')).toBeVisible()
|
||||
await expect(page.locator('tr')).toHaveCount(3)
|
||||
await expect(page.locator('text="€39.00"')).toBeVisible()
|
||||
await expect(page.locator('text="$39.00"')).toBeVisible()
|
||||
})
|
||||
|
||||
test('custom plans should work', async ({ page }) => {
|
||||
|
@ -21,7 +21,7 @@ test.beforeEach(async () => {
|
||||
await injectFakeResults({ typebotId, count: 200, isChronological: true })
|
||||
})
|
||||
|
||||
test('Results', async ({ page }) => {
|
||||
test('table features should work', async ({ page }) => {
|
||||
await page.goto(`/typebots/${typebotId}/results`)
|
||||
|
||||
await test.step('Check header format', async () => {
|
||||
@ -147,14 +147,14 @@ test('Results', async ({ page }) => {
|
||||
|
||||
const validateExportSelection = (data: unknown[]) => {
|
||||
expect(data).toHaveLength(3)
|
||||
expect((data[1] as unknown[])[3]).toBe('content199')
|
||||
expect((data[2] as unknown[])[3]).toBe('content198')
|
||||
expect((data[1] as unknown[])[0]).toBe('content199')
|
||||
expect((data[2] as unknown[])[0]).toBe('content198')
|
||||
}
|
||||
|
||||
const validateExportAll = (data: unknown[]) => {
|
||||
expect(data).toHaveLength(201)
|
||||
expect((data[1] as unknown[])[3]).toBe('content199')
|
||||
expect((data[200] as unknown[])[3]).toBe('content0')
|
||||
expect((data[1] as unknown[])[0]).toBe('content199')
|
||||
expect((data[200] as unknown[])[0]).toBe('content0')
|
||||
}
|
||||
|
||||
const scrollToBottom = (page: Page) =>
|
||||
|
@ -73,7 +73,7 @@ export const canPublishFileInput = async ({
|
||||
return false
|
||||
}
|
||||
if (workspace?.plan === Plan.FREE) {
|
||||
forbidden(res, 'You need to upgrade your plan to use this feature')
|
||||
forbidden(res, 'You need to upgrade your plan to use file input blocks')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -136,7 +136,7 @@ export const parseSubmissionsColumns = (
|
||||
): HeaderCell[] =>
|
||||
resultHeader.map((header) => ({
|
||||
Header: (
|
||||
<HStack minW={header.isLong ? '400px' : '150px'} maxW="500px">
|
||||
<HStack minW="150px" maxW="500px">
|
||||
<HeaderIcon header={header} />
|
||||
<Text>{header.label}</Text>
|
||||
</HStack>
|
||||
@ -144,10 +144,10 @@ export const parseSubmissionsColumns = (
|
||||
accessor: header.label,
|
||||
}))
|
||||
|
||||
const HeaderIcon = ({ header }: { header: ResultHeaderCell }) =>
|
||||
export const HeaderIcon = ({ header }: { header: ResultHeaderCell }) =>
|
||||
header.blockType ? (
|
||||
<BlockIcon type={header.blockType} />
|
||||
) : header.variableId ? (
|
||||
) : header.variableIds ? (
|
||||
<CodeIcon />
|
||||
) : (
|
||||
<CalendarIcon />
|
||||
@ -174,9 +174,13 @@ export const convertResultsToTableData = (
|
||||
if ('groupId' in answerOrVariable) {
|
||||
const answer = answerOrVariable as Answer
|
||||
const header = answer.variableId
|
||||
? headerCells.find((h) => h.variableId === answer.variableId)
|
||||
: headerCells.find((h) => h.blockId === answer.blockId)
|
||||
if (!header || !header.blockId || !header.blockType) return o
|
||||
? headerCells.find((headerCell) =>
|
||||
headerCell.variableIds?.includes(answer.variableId as string)
|
||||
)
|
||||
: headerCells.find((headerCell) =>
|
||||
headerCell.blocks?.some((block) => block.id === answer.blockId)
|
||||
)
|
||||
if (!header || !header.blocks || !header.blockType) return o
|
||||
return {
|
||||
...o,
|
||||
[header.label]: {
|
||||
@ -186,7 +190,9 @@ export const convertResultsToTableData = (
|
||||
}
|
||||
}
|
||||
const variable = answerOrVariable as VariableWithValue
|
||||
const key = headerCells.find((h) => h.variableId === variable.id)?.label
|
||||
const key = headerCells.find((headerCell) =>
|
||||
headerCell.variableIds?.includes(variable.id)
|
||||
)?.label
|
||||
if (!key) return o
|
||||
if (isDefined(o[key])) return o
|
||||
return {
|
||||
|
Reference in New Issue
Block a user