fix(results): 🐛 Loading rows
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
DATABASE_URL=postgresql://postgres:@localhost:5432/typebot
|
||||
|
||||
SECRET=q3t6v9y$B&E)H@McQfTjWnZr4u7x!z%C #256-bits secret (can be generated here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx)
|
||||
ENCRYPTION_SECRET=q3t6v9y$B&E)H@McQfTjWnZr4u7x!z%C #256-bits secret (can be generated here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx)
|
||||
NEXTAUTH_URL=http://localhost:3000
|
||||
|
||||
# Used for email auth and email notifications
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Checkbox, Flex, Skeleton } from '@chakra-ui/react'
|
||||
import { chakra, Checkbox, Flex, Skeleton } from '@chakra-ui/react'
|
||||
import React from 'react'
|
||||
|
||||
type LoadingRowsProps = {
|
||||
@@ -9,35 +9,32 @@ export const LoadingRows = ({ totalColumns }: LoadingRowsProps) => {
|
||||
return (
|
||||
<>
|
||||
{Array.from(Array(3)).map((row, idx) => (
|
||||
<Flex as="tr" key={idx}>
|
||||
<Flex
|
||||
key={idx}
|
||||
py={2}
|
||||
px={4}
|
||||
<tr key={idx}>
|
||||
<chakra.td
|
||||
px="4"
|
||||
py="2"
|
||||
border="1px"
|
||||
as="td"
|
||||
borderColor="gray.200"
|
||||
width="50px"
|
||||
>
|
||||
<Flex>
|
||||
<Checkbox isDisabled />
|
||||
</Flex>
|
||||
</chakra.td>
|
||||
{Array.from(Array(totalColumns)).map((cell, idx) => {
|
||||
return (
|
||||
<Flex
|
||||
<chakra.td
|
||||
key={idx}
|
||||
py={2}
|
||||
px={4}
|
||||
px="4"
|
||||
py="2"
|
||||
border="1px"
|
||||
as="td"
|
||||
borderColor="gray.200"
|
||||
width="180px"
|
||||
align="center"
|
||||
>
|
||||
<Skeleton height="5px" w="full" />
|
||||
</Flex>
|
||||
</chakra.td>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</tr>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react/jsx-key */
|
||||
import { Box, Checkbox, Flex } from '@chakra-ui/react'
|
||||
import { chakra, Checkbox, Flex } from '@chakra-ui/react'
|
||||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
|
||||
import React, { useEffect, useMemo, useRef } from 'react'
|
||||
import { Hooks, useFlexLayout, useRowSelect, useTable } from 'react-table'
|
||||
import { Hooks, useRowSelect, useTable } from 'react-table'
|
||||
import { parseSubmissionsColumns } from 'services/publicTypebot'
|
||||
import { LoadingRows } from './LoadingRows'
|
||||
|
||||
const defaultCellWidth = 180
|
||||
|
||||
type SubmissionsTableProps = {
|
||||
data?: any
|
||||
hasMore?: boolean
|
||||
@@ -38,12 +36,7 @@ export const SubmissionsTable = ({
|
||||
prepareRow,
|
||||
getTableBodyProps,
|
||||
selectedFlatRows,
|
||||
} = useTable(
|
||||
{ columns, data, defaultColumn: { width: defaultCellWidth } },
|
||||
useRowSelect,
|
||||
checkboxColumnHook,
|
||||
useFlexLayout
|
||||
) as any
|
||||
} = useTable({ columns, data }, useRowSelect, checkboxColumnHook) as any
|
||||
|
||||
useEffect(() => {
|
||||
onNewSelection(selectedFlatRows.map((row: any) => row.index))
|
||||
@@ -68,81 +61,69 @@ export const SubmissionsTable = ({
|
||||
|
||||
return (
|
||||
<Flex
|
||||
overflow="scroll"
|
||||
maxW="full"
|
||||
maxH="full"
|
||||
overflow="scroll"
|
||||
ref={tableWrapper}
|
||||
className="table-wrapper"
|
||||
rounded="md"
|
||||
data-testid="table-wrapper"
|
||||
pb="20"
|
||||
ref={tableWrapper}
|
||||
>
|
||||
<Box as="table" rounded="md" {...getTableProps()} w="full" h="full">
|
||||
<Box as="thead" pos="sticky" top="0" zIndex={2}>
|
||||
<chakra.table rounded="md" {...getTableProps()}>
|
||||
<thead>
|
||||
{headerGroups.map((headerGroup: any) => {
|
||||
return (
|
||||
<Flex as="tr" {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map((column: any, idx: number) => {
|
||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map((column: any) => {
|
||||
return (
|
||||
<Flex
|
||||
py={2}
|
||||
px={4}
|
||||
<chakra.th
|
||||
px="4"
|
||||
py="2"
|
||||
border="1px"
|
||||
borderColor="gray.200"
|
||||
as="th"
|
||||
color="gray.500"
|
||||
fontWeight="normal"
|
||||
textAlign="left"
|
||||
bgColor={'white'}
|
||||
whiteSpace="nowrap"
|
||||
{...column.getHeaderProps()}
|
||||
style={{
|
||||
width: idx === 0 ? '50px' : `${defaultCellWidth}px`,
|
||||
}}
|
||||
>
|
||||
{column.render('Header')}
|
||||
</Flex>
|
||||
</chakra.th>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</thead>
|
||||
|
||||
<Box as="tbody" {...getTableBodyProps()}>
|
||||
<tbody {...getTableBodyProps()}>
|
||||
{rows.map((row: any, idx: number) => {
|
||||
prepareRow(row)
|
||||
return (
|
||||
<Flex
|
||||
as="tr"
|
||||
<tr
|
||||
{...row.getRowProps()}
|
||||
ref={(ref) => {
|
||||
if (idx === data.length - 10) bottomElement.current = ref
|
||||
}}
|
||||
>
|
||||
{row.cells.map((cell: any, idx: number) => {
|
||||
{row.cells.map((cell: any) => {
|
||||
return (
|
||||
<Flex
|
||||
py={2}
|
||||
px={4}
|
||||
<chakra.td
|
||||
px="4"
|
||||
py="2"
|
||||
border="1px"
|
||||
as="td"
|
||||
borderColor="gray.200"
|
||||
bgColor={'white'}
|
||||
whiteSpace={
|
||||
cell?.value?.length > 100 ? 'normal' : 'nowrap'
|
||||
}
|
||||
{...cell.getCellProps()}
|
||||
style={{
|
||||
width: idx === 0 ? '50px' : `${defaultCellWidth}px`,
|
||||
}}
|
||||
>
|
||||
{cell.render('Cell')}
|
||||
</Flex>
|
||||
</chakra.td>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
{hasMore === true && <LoadingRows totalColumns={columns.length} />}
|
||||
</Box>
|
||||
</Box>
|
||||
</tbody>
|
||||
</chakra.table>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
@@ -168,12 +149,14 @@ const IndeterminateCheckbox = React.forwardRef(
|
||||
const resolvedRef: any = ref || defaultRef
|
||||
|
||||
return (
|
||||
<Flex justify="center">
|
||||
<Checkbox
|
||||
ref={resolvedRef}
|
||||
{...rest}
|
||||
isIndeterminate={indeterminate}
|
||||
isChecked={checked}
|
||||
/>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -106,11 +106,11 @@ export const SubmissionsContent = ({
|
||||
const tableData: { [key: string]: string }[] = useMemo(
|
||||
() => convertResultsToTableData(results),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[results?.length]
|
||||
[results]
|
||||
)
|
||||
|
||||
return (
|
||||
<Stack maxW="1200px" w="full">
|
||||
<Stack maxW="1200px" w="full" pb="28">
|
||||
<Flex w="full" justifyContent="flex-end">
|
||||
<ResultsActionButtons
|
||||
isDeleteLoading={isDeleteLoading}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"test": "dotenv -e ./playwright/.env -- yarn playwright test",
|
||||
"test:open": "dotenv -e ./playwright/.env -v PWDEBUG=1 -- yarn playwright test"
|
||||
"test": "dotenv -e ./playwright/.env -e .env.local -- yarn playwright test",
|
||||
"test:open": "dotenv -e ./playwright/.env -e .env.local -v PWDEBUG=1 -- yarn playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/css-reset": "^1.1.1",
|
||||
|
||||
@@ -49,13 +49,13 @@ if (process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET)
|
||||
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||
NextAuth(req, res, {
|
||||
adapter: PrismaAdapter(prisma),
|
||||
secret: process.env.SECRET,
|
||||
secret: process.env.ENCRYPTION_SECRET,
|
||||
providers,
|
||||
session: {
|
||||
strategy: 'database',
|
||||
},
|
||||
callbacks: {
|
||||
session: async ({ session, user }) => ({ ...session, user }),
|
||||
session: ({ session, user }) => ({ ...session, user }),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import path from 'path'
|
||||
const config: PlaywrightTestConfig = {
|
||||
globalSetup: require.resolve(path.join(__dirname, 'playwright/global-setup')),
|
||||
testDir: path.join(__dirname, 'playwright/tests'),
|
||||
timeout: 10 * 1000,
|
||||
timeout: 10 * 2000,
|
||||
expect: {
|
||||
timeout: 5000,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import test, { expect, Page } from '@playwright/test'
|
||||
import { readFileSync } from 'fs'
|
||||
import { InputStepType } from 'models'
|
||||
import { defaultTextInputOptions, InputStepType } from 'models'
|
||||
import { parse } from 'papaparse'
|
||||
import { generate } from 'short-uuid'
|
||||
import {
|
||||
@@ -17,7 +17,10 @@ test.describe('Results page', () => {
|
||||
await createTypebots([
|
||||
{
|
||||
id: typebotId,
|
||||
...parseDefaultBlockWithStep({ type: InputStepType.TEXT }),
|
||||
...parseDefaultBlockWithStep({
|
||||
type: InputStepType.TEXT,
|
||||
options: defaultTextInputOptions,
|
||||
}),
|
||||
},
|
||||
])
|
||||
await createResults({ typebotId })
|
||||
|
||||
@@ -78,18 +78,25 @@ export const parseSubmissionsColumns = (
|
||||
(block) => typebot && block.steps.some((step) => isInputStep(step))
|
||||
)
|
||||
.map((block) => {
|
||||
const inputStep = block.steps.find((step) =>
|
||||
isInputStep(step)
|
||||
) as InputStep
|
||||
const inputStep = block.steps.find((step) => isInputStep(step))
|
||||
if (!inputStep || !isInputStep(inputStep)) return
|
||||
return {
|
||||
Header: (
|
||||
<HStack>
|
||||
<HStack
|
||||
minW={
|
||||
'isLong' in inputStep.options && inputStep.options.isLong
|
||||
? '400px'
|
||||
: '150px'
|
||||
}
|
||||
maxW="500px"
|
||||
>
|
||||
<StepIcon type={inputStep.type} />
|
||||
<Text>{block.title}</Text>
|
||||
</HStack>
|
||||
),
|
||||
accessor: block.id,
|
||||
}
|
||||
}),
|
||||
})
|
||||
.filter(isDefined),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { stringify } from 'qs'
|
||||
import { Answer } from 'db'
|
||||
import { sendRequest } from 'utils'
|
||||
|
||||
const paginationLimit = 50
|
||||
|
||||
const getKey = (
|
||||
typebotId: string,
|
||||
pageIndex: number,
|
||||
@@ -16,7 +18,7 @@ const getKey = (
|
||||
if (pageIndex === 0) return `/api/typebots/${typebotId}/results?limit=50`
|
||||
return `/api/typebots/${typebotId}/results?lastResultId=${
|
||||
previousPageData.results[previousPageData.results.length - 1].id
|
||||
}&limit=50`
|
||||
}&limit=${paginationLimit}`
|
||||
}
|
||||
|
||||
export type ResultWithAnswers = Result & { answers: Answer[] }
|
||||
@@ -49,7 +51,10 @@ export const useResults = ({
|
||||
setSize,
|
||||
size,
|
||||
hasMore:
|
||||
data && data.length > 0 && data[data.length - 1].results.length > 0,
|
||||
data &&
|
||||
data.length > 0 &&
|
||||
data[data.length - 1].results.length > 0 &&
|
||||
data.length === paginationLimit,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
ENCRYPTION_SECRET=
|
||||
NEXT_PUBLIC_VIEWER_HOST=http://localhost:3001
|
||||
DATABASE_URL=postgresql://postgres:@localhost:5432/typebot
|
||||
|
||||
|
||||
@@ -76,7 +76,6 @@ export const ChatBlock = ({
|
||||
|
||||
const displayNextStep = (answerContent?: string, isRetry?: boolean) => {
|
||||
const currentStep = [...displayedSteps].pop()
|
||||
console.log(currentStep)
|
||||
if (currentStep) {
|
||||
if (isRetry && stepCanBeRetried(currentStep))
|
||||
return setDisplayedSteps([
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { randomBytes, createCipheriv, createDecipheriv } from 'crypto'
|
||||
|
||||
const algorithm = 'aes-256-gcm'
|
||||
const secretKey = process.env.SECRET
|
||||
const secretKey = process.env.ENCRYPTION_SECRET
|
||||
|
||||
export const encrypt = (
|
||||
data: object
|
||||
|
||||
Reference in New Issue
Block a user