🚸 (results) Add time filter to results table as…

This commit is contained in:
Baptiste Arnaud
2024-02-06 17:48:31 +01:00
parent 3e2533b934
commit 066fabce06
20 changed files with 376 additions and 67 deletions

View File

@@ -14,6 +14,7 @@ import { LogicBlockType } from '@typebot.io/schemas/features/blocks/logic/consta
import { parseResultHeader } from '@typebot.io/lib/results/parseResultHeader'
import { convertResultsToTableData } from '@typebot.io/lib/results/convertResultsToTableData'
import { parseCellContent } from './helpers/parseCellContent'
import { timeFilterValues } from '../analytics/constants'
const resultsContext = createContext<{
resultsList: { results: ResultWithAnswers[] }[] | undefined
@@ -30,11 +31,13 @@ const resultsContext = createContext<{
}>({})
export const ResultsProvider = ({
timeFilter,
children,
typebotId,
totalResults,
onDeleteResults,
}: {
timeFilter: (typeof timeFilterValues)[number]
children: ReactNode
typebotId: string
totalResults: number
@@ -43,6 +46,7 @@ export const ResultsProvider = ({
const { publishedTypebot } = useTypebot()
const { showToast } = useToast()
const { data, fetchNextPage, hasNextPage, refetch } = useResultsQuery({
timeFilter,
typebotId,
onError: (error) => {
showToast({ description: error })

View File

@@ -4,6 +4,11 @@ import { TRPCError } from '@trpc/server'
import { resultWithAnswersSchema } from '@typebot.io/schemas'
import { z } from 'zod'
import { isReadTypebotForbidden } from '@/features/typebot/helpers/isReadTypebotForbidden'
import {
timeFilterValues,
defaultTimeFilter,
} from '@/features/analytics/constants'
import { parseDateFromTimeFilter } from '@/features/analytics/helpers/parseDateFromTimeFilter'
const maxLimit = 100
@@ -26,6 +31,7 @@ export const getResults = authenticatedProcedure
),
limit: z.coerce.number().min(1).max(maxLimit).default(50),
cursor: z.string().optional(),
timeFilter: z.enum(timeFilterValues).default(defaultTimeFilter),
})
)
.output(
@@ -70,6 +76,9 @@ export const getResults = authenticatedProcedure
})
if (!typebot || (await isReadTypebotForbidden(typebot, user)))
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })
const date = parseDateFromTimeFilter(input.timeFilter)
const results = await prisma.result.findMany({
take: limit + 1,
cursor: cursor ? { id: cursor } : undefined,
@@ -77,6 +86,11 @@ export const getResults = authenticatedProcedure
typebotId: typebot.id,
hasStarted: true,
isArchived: false,
createdAt: date
? {
gte: date,
}
: undefined,
},
orderBy: {
createdAt: 'desc',

View File

@@ -14,11 +14,15 @@ import {
} from '@chakra-ui/react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useMemo } from 'react'
import { useStats } from '../hooks/useStats'
import { useMemo, useState } from 'react'
import { ResultsProvider } from '../ResultsProvider'
import { ResultsTableContainer } from './ResultsTableContainer'
import { TypebotNotFoundPage } from '@/features/editor/components/TypebotNotFoundPage'
import { trpc } from '@/lib/trpc'
import {
defaultTimeFilter,
timeFilterValues,
} from '@/features/analytics/constants'
export const ResultsPage = () => {
const router = useRouter()
@@ -32,18 +36,25 @@ export const ResultsPage = () => {
router.pathname.endsWith('analytics') ? '#f4f5f8' : 'white',
router.pathname.endsWith('analytics') ? 'gray.850' : 'gray.900'
)
const [timeFilter, setTimeFilter] =
useState<(typeof timeFilterValues)[number]>(defaultTimeFilter)
const { showToast } = useToast()
const { stats, mutate } = useStats({
typebotId: publishedTypebot?.typebotId,
onError: (err) => showToast({ title: err.name, description: err.message }),
})
const { data: { stats } = {}, refetch } = trpc.analytics.getStats.useQuery(
{
typebotId: publishedTypebot?.typebotId as string,
timeFilter,
},
{
enabled: !!publishedTypebot,
onError: (err) => showToast({ description: err.message }),
}
)
const handleDeletedResults = (total: number) => {
const handleDeletedResults = () => {
if (!stats) return
mutate({
stats: { ...stats, totalStarts: stats.totalStarts - total },
})
refetch()
}
if (is404) return <TypebotNotFoundPage />
@@ -100,14 +111,22 @@ export const ResultsPage = () => {
{workspace &&
publishedTypebot &&
(isAnalytics ? (
<AnalyticsGraphContainer stats={stats} />
<AnalyticsGraphContainer
stats={stats}
timeFilter={timeFilter}
onTimeFilterChange={setTimeFilter}
/>
) : (
<ResultsProvider
timeFilter={timeFilter}
typebotId={publishedTypebot.typebotId}
totalResults={stats?.totalStarts ?? 0}
onDeleteResults={handleDeletedResults}
>
<ResultsTableContainer />
<ResultsTableContainer
timeFilter={timeFilter}
onTimeFilterChange={setTimeFilter}
/>
</ResultsProvider>
))}
</Flex>

View File

@@ -6,8 +6,16 @@ import { useResults } from '../ResultsProvider'
import { ResultModal } from './ResultModal'
import { ResultsTable } from './table/ResultsTable'
import { useRouter } from 'next/router'
import { timeFilterValues } from '@/features/analytics/constants'
export const ResultsTableContainer = () => {
type Props = {
timeFilter: (typeof timeFilterValues)[number]
onTimeFilterChange: (timeFilter: (typeof timeFilterValues)[number]) => void
}
export const ResultsTableContainer = ({
timeFilter,
onTimeFilterChange,
}: Props) => {
const { query } = useRouter()
const {
flatResults: results,
@@ -61,8 +69,10 @@ export const ResultsTableContainer = () => {
data={tableData}
onScrollToBottom={fetchNextPage}
hasMore={hasNextPage}
timeFilter={timeFilter}
onLogOpenIndex={handleLogOpenIndex}
onResultExpandIndex={handleResultExpandIndex}
onTimeFilterChange={onTimeFilterChange}
/>
)}
</Stack>

View File

@@ -31,12 +31,16 @@ import { IndeterminateCheckbox } from './IndeterminateCheckbox'
import { colors } from '@/lib/theme'
import { HeaderIcon } from '../HeaderIcon'
import { parseColumnsOrder } from '@typebot.io/lib/results/parseColumnsOrder'
import { TimeFilterDropdown } from '@/features/analytics/components/TimeFilterDropdown'
import { timeFilterValues } from '@/features/analytics/constants'
type ResultsTableProps = {
resultHeader: ResultHeaderCell[]
data: TableData[]
hasMore?: boolean
preferences?: ResultsTablePreferences
timeFilter: (typeof timeFilterValues)[number]
onTimeFilterChange: (timeFilter: (typeof timeFilterValues)[number]) => void
onScrollToBottom: () => void
onLogOpenIndex: (index: number) => () => void
onResultExpandIndex: (index: number) => () => void
@@ -47,6 +51,8 @@ export const ResultsTable = ({
data,
hasMore,
preferences,
timeFilter,
onTimeFilterChange,
onScrollToBottom,
onLogOpenIndex,
onResultExpandIndex,
@@ -222,6 +228,11 @@ export const ResultsTable = ({
onClearSelection={() => setRowSelection({})}
/>
)}
<TimeFilterDropdown
timeFilter={timeFilter}
onTimeFilterChange={onTimeFilterChange}
size="sm"
/>
<TableSettingsButton
resultHeader={resultHeader}
columnVisibility={columnsVisibility}

View File

@@ -1,15 +1,17 @@
import { timeFilterValues } from '@/features/analytics/constants'
import { trpc } from '@/lib/trpc'
export const useResultsQuery = ({
typebotId,
onError,
}: {
type Params = {
timeFilter: (typeof timeFilterValues)[number]
typebotId: string
onError?: (error: string) => void
}) => {
}
export const useResultsQuery = ({ timeFilter, typebotId, onError }: Params) => {
const { data, error, fetchNextPage, hasNextPage, refetch } =
trpc.results.getResults.useInfiniteQuery(
{
timeFilter,
typebotId,
},
{

View File

@@ -1,22 +0,0 @@
import { Stats } from '@typebot.io/schemas'
import { fetcher } from '@/helpers/fetcher'
import useSWR from 'swr'
export const useStats = ({
typebotId,
onError,
}: {
typebotId?: string
onError: (error: Error) => void
}) => {
const { data, error, mutate } = useSWR<{ stats: Stats }, Error>(
typebotId ? `/api/typebots/${typebotId}/analytics/stats` : null,
fetcher
)
if (error) onError(error)
return {
stats: data?.stats,
isLoading: !error && !data,
mutate,
}
}