Skip to content

Commit

Permalink
🐛 (results) Fix bug preventing user from seeing linked typebots results
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Nov 6, 2022
1 parent 63845ef commit 6dd7bd9
Show file tree
Hide file tree
Showing 18 changed files with 234 additions and 151 deletions.
2 changes: 1 addition & 1 deletion apps/builder/components/results/ResultModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/components/results/ResultsContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -35,6 +34,7 @@ import {
useSortable,
arrayMove,
} from '@dnd-kit/sortable'
import { HeaderIcon } from 'services/typebots/results'

type Props = {
resultHeader: ResultHeaderCell[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}),
],
{
Expand Down
19 changes: 4 additions & 15 deletions apps/builder/components/results/ResultsTable/ResultsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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[]
Expand Down Expand Up @@ -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} />
Expand Down Expand Up @@ -147,8 +146,7 @@ export const ResultsTable = ({
),
},
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[resultHeader]
[onLogOpenIndex, resultHeader]
)

const instance = useReactTable({
Expand Down Expand Up @@ -258,12 +256,3 @@ const IndeterminateCheckbox = React.forwardRef(
)
}
)

export const HeaderIcon = ({ header }: { header: ResultHeaderCell }) =>
header.blockType ? (
<BlockIcon type={header.blockType} />
) : header.variableId ? (
<CodeIcon />
) : (
<CalendarIcon />
)
5 changes: 2 additions & 3 deletions apps/builder/components/share/EditableUrl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
})
}

Expand Down
25 changes: 11 additions & 14 deletions apps/builder/contexts/ResultsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<{
Expand Down Expand Up @@ -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(
() =>
Expand All @@ -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 (
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/pages/api/typebots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
}
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/playwright/tests/billing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down
10 changes: 5 additions & 5 deletions apps/builder/playwright/tests/results.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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) =>
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/services/api/dbRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 13 additions & 7 deletions apps/builder/services/typebots/results.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,18 @@ 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>
),
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 />
Expand All @@ -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]: {
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,16 +203,7 @@ const getBodyContent =
return body === '{{state}}'
? JSON.stringify(
resultValues
? parseAnswers({
groups: [
...typebot.groups,
...linkedTypebots.flatMap((t) => t.groups),
],
variables: [
...typebot.variables,
...linkedTypebots.flatMap((t) => t.variables),
],
})(resultValues)
? parseAnswers(typebot, linkedTypebots)(resultValues)
: await parseSampleResult(typebot, linkedTypebots)(groupId)
)
: body
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,7 @@ const getEmailBody = async ({
})) as unknown as PublicTypebot
if (!typebot) return
const linkedTypebots = await getLinkedTypebots(typebot)
const answers = parseAnswers({
groups: [...typebot.groups, ...linkedTypebots.flatMap((t) => t.groups)],
variables: [
...typebot.variables,
...linkedTypebots.flatMap((t) => t.variables),
],
})(resultValues)
const answers = parseAnswers(typebot, linkedTypebots)(resultValues)
return {
html: render(
<DefaultBotNotificationEmail
Expand Down
4 changes: 2 additions & 2 deletions apps/viewer/playwright/tests/api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ test('can list typebots', async ({ request }) => {
const response = await request.get(`/api/typebots`, {
headers: { Authorization: `Bearer ${apiToken}` },
})
const { typebots } = await response.json()
expect(typebots).toHaveLength(1)
const { typebots } = (await response.json()) as { typebots: unknown[] }
expect(typebots.length).toBeGreaterThanOrEqual(1)
expect(typebots[0]).toMatchObject({
id: typebotId,
publishedTypebotId: null,
Expand Down
Loading

5 comments on commit 6dd7bd9

@vercel
Copy link

@vercel vercel bot commented on 6dd7bd9 Nov 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 6dd7bd9 Nov 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

app.typebot.io
builder-v2-typebot-io.vercel.app
builder-v2-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 6dd7bd9 Nov 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2-alpha – ./apps/viewer

bot.maitempah.com
bot.phuonghub.com
bot.reviewzer.com
cares.urlabout.me
fmm.wpwakanda.com
gentleman-shop.fr
k1.kandabrand.com
lb.ticketfute.com
ov1.wpwakanda.com
ov2.wpwakanda.com
ov3.wpwakanda.com
1988.bouclidom.com
andreimayer.com.br
bot.megafox.com.br
bot.neferlopez.com
bots.robomotion.io
cadu.uninta.edu.br
dicanatural.online
goalsettingbot.com
positivobra.com.br
survey.digienge.io
this-is-a-test.com
zap.techadviser.in
bot.digitalbled.com
bot.eventhub.com.au
carsalesenquiry.com
demo.botscientis.us
forms.webisharp.com
kbsub.wpwakanda.com
live.botscientis.us
mentoria.omelhor.vc
nutrisamirbayde.com
order.maitempah.com
quest.wpwakanda.com
test.botscientis.us
typebot.stillio.com
bium.gratirabbit.com
bot.ansuraniphone.my
bot.cotemeuplano.com
chat.hayurihijab.com
chatbee.agfunnel.com
click.sevenoways.com
connect.growthguy.in
get.freebotoffer.xyz
kuiz.sistemniaga.com
offer.botscientis.us
sellmycarglasgow.com
talkbot.agfunnel.com
tenorioadvogados.com
uppity.wpwakanda.com
abutton.wpwakanda.com
aidigitalmarketing.kr
bbutton.wpwakanda.com
bot.incusservices.com
bot.meuesocial.com.br
link.cascadigital.com.br
onboarding.growthside.io
reward.onlinebotdemo.xyz
type.opaulovieira.com.br
aibot.angrybranding.co.uk
bot.aidigitalmarketing.kr
bot.arraesecenteno.com.br
bot.blackboxsports.com.br
bot.cabinrentalagency.com
boyfriend-breakup.riku.ai
brigadeirosemdrama.com.br
chat.ertcrebateportal.com
chat.thisiscrushhouse.com
sellmyharleylouisiana.com
verfica.botmachine.com.br
configurator.bouclidom.com
ted.meujalecobrasil.com.br
type.dericsoncalari.com.br
chatbot.berbelanjabiz.trade
designguide.techyscouts.com
presente.empresarias.com.mx
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
piazzatorre.barrettamario.it
requests.swamprecordsgnv.com
type.cookieacademyonline.com
bot.brigadeirosemdrama.com.br
onboarding.libertydreamcare.ie
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
yourfeedback.comebackreward.com
personal-trainer.barrettamario.it
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
viewer-v2-alpha-typebot-io.vercel.app
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
viewer-v2-alpha-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 6dd7bd9 Nov 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs-typebot-io.vercel.app
docs.typebot.io
docs-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 6dd7bd9 Nov 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.