🚸 (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

@@ -0,0 +1,89 @@
import prisma from '@typebot.io/lib/prisma'
import { authenticatedProcedure } from '@/helpers/server/trpc'
import { TRPCError } from '@trpc/server'
import { z } from 'zod'
import { canReadTypebots } from '@/helpers/databaseRules'
import { Stats, statsSchema } from '@typebot.io/schemas'
import { defaultTimeFilter, timeFilterValues } from '../constants'
import { parseDateFromTimeFilter } from '../helpers/parseDateFromTimeFilter'
export const getStats = authenticatedProcedure
.meta({
openapi: {
method: 'GET',
path: '/v1/typebots/{typebotId}/analytics/stats',
protect: true,
summary: 'Get results stats',
tags: ['Analytics'],
},
})
.input(
z.object({
typebotId: z.string(),
timeFilter: z.enum(timeFilterValues).default(defaultTimeFilter),
})
)
.output(z.object({ stats: statsSchema }))
.query(async ({ input: { typebotId, timeFilter }, ctx: { user } }) => {
const typebot = await prisma.typebot.findFirst({
where: canReadTypebots(typebotId, user),
select: { publishedTypebot: true, id: true },
})
if (!typebot?.publishedTypebot)
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Published typebot not found',
})
const date = parseDateFromTimeFilter(timeFilter)
const [totalViews, totalStarts, totalCompleted] = await prisma.$transaction(
[
prisma.result.count({
where: {
typebotId: typebot.id,
isArchived: false,
createdAt: date
? {
gte: date,
}
: undefined,
},
}),
prisma.result.count({
where: {
typebotId: typebot.id,
isArchived: false,
hasStarted: true,
createdAt: date
? {
gte: date,
}
: undefined,
},
}),
prisma.result.count({
where: {
typebotId: typebot.id,
isArchived: false,
isCompleted: true,
createdAt: date
? {
gte: date,
}
: undefined,
},
}),
]
)
const stats: Stats = {
totalViews,
totalStarts,
totalCompleted,
}
return {
stats,
}
})

View File

@@ -1,8 +1,10 @@
import { router } from '@/helpers/server/trpc'
import { getTotalAnswers } from './getTotalAnswers'
import { getTotalVisitedEdges } from './getTotalVisitedEdges'
import { getStats } from './getStats'
export const analyticsRouter = router({
getTotalAnswers,
getTotalVisitedEdges,
getStats,
})

View File

@@ -6,7 +6,7 @@ import {
} from '@chakra-ui/react'
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
import { Stats } from '@typebot.io/schemas'
import React, { useState } from 'react'
import React from 'react'
import { StatsCards } from './StatsCards'
import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
import { Graph } from '@/features/graph/components/Graph'
@@ -15,14 +15,22 @@ import { useTranslate } from '@tolgee/react'
import { trpc } from '@/lib/trpc'
import { isDefined } from '@typebot.io/lib'
import { EventsCoordinatesProvider } from '@/features/graph/providers/EventsCoordinateProvider'
import { defaultTimeFilter, timeFilterValues } from '../constants'
import { timeFilterValues } from '../constants'
export const AnalyticsGraphContainer = ({ stats }: { stats?: Stats }) => {
type Props = {
timeFilter: (typeof timeFilterValues)[number]
onTimeFilterChange: (timeFilter: (typeof timeFilterValues)[number]) => void
stats?: Stats
}
export const AnalyticsGraphContainer = ({
timeFilter,
onTimeFilterChange,
stats,
}: Props) => {
const { t } = useTranslate()
const { isOpen, onOpen, onClose } = useDisclosure()
const { typebot, publishedTypebot } = useTypebot()
const [timeFilter, setTimeFilter] =
useState<(typeof timeFilterValues)[number]>(defaultTimeFilter)
const { data } = trpc.analytics.getTotalAnswers.useQuery(
{
typebotId: typebot?.id as string,
@@ -85,7 +93,7 @@ export const AnalyticsGraphContainer = ({ stats }: { stats?: Stats }) => {
stats={stats}
pos="absolute"
timeFilter={timeFilter}
setTimeFilter={setTimeFilter}
onTimeFilterChange={onTimeFilterChange}
/>
</Flex>
)

View File

@@ -10,8 +10,8 @@ import {
} from '@chakra-ui/react'
import { Stats } from '@typebot.io/schemas'
import React from 'react'
import { DropdownList } from '@/components/DropdownList'
import { timeFilterLabels, timeFilterValues } from '../constants'
import { timeFilterValues } from '../constants'
import { TimeFilterDropdown } from './TimeFilterDropdown'
const computeCompletionRate =
(notAvailableLabel: string) =>
@@ -23,12 +23,12 @@ const computeCompletionRate =
export const StatsCards = ({
stats,
timeFilter,
setTimeFilter,
onTimeFilterChange,
...props
}: {
stats?: Stats
timeFilter: (typeof timeFilterValues)[number]
setTimeFilter: (timeFilter: (typeof timeFilterValues)[number]) => void
onTimeFilterChange: (timeFilter: (typeof timeFilterValues)[number]) => void
} & GridProps) => {
const { t } = useTranslate()
const bg = useColorModeValue('white', 'gray.900')
@@ -69,15 +69,9 @@ export const StatsCards = ({
<Skeleton w="50%" h="10px" mt="2" />
)}
</Stat>
<DropdownList
items={Object.entries(timeFilterLabels).map(([value, label]) => ({
label,
value,
}))}
currentItem={timeFilter}
onItemSelect={(val) =>
setTimeFilter(val as (typeof timeFilterValues)[number])
}
<TimeFilterDropdown
timeFilter={timeFilter}
onTimeFilterChange={onTimeFilterChange}
backgroundColor="white"
boxShadow="md"
/>

View File

@@ -0,0 +1,26 @@
import { DropdownList } from '@/components/DropdownList'
import { timeFilterLabels, timeFilterValues } from '../constants'
import { ButtonProps } from '@chakra-ui/react'
type Props = {
timeFilter: (typeof timeFilterValues)[number]
onTimeFilterChange: (timeFilter: (typeof timeFilterValues)[number]) => void
} & ButtonProps
export const TimeFilterDropdown = ({
timeFilter,
onTimeFilterChange,
...props
}: Props) => (
<DropdownList
items={Object.entries(timeFilterLabels).map(([value, label]) => ({
label,
value,
}))}
currentItem={timeFilter}
onItemSelect={(val) =>
onTimeFilterChange(val as (typeof timeFilterValues)[number])
}
{...props}
/>
)