2
0

♻️ (builder) Change to features-centric folder structure

This commit is contained in:
Baptiste Arnaud
2022-11-15 09:35:48 +01:00
committed by Baptiste Arnaud
parent 3686465a85
commit 643571fe7d
683 changed files with 3907 additions and 3643 deletions

View File

@ -0,0 +1,32 @@
import { getTestAsset } from '@/test/utils/playwright'
import test, { expect } from '@playwright/test'
import cuid from 'cuid'
import {
importTypebotInDatabase,
injectFakeResults,
} from 'utils/playwright/databaseActions'
import { starterWorkspaceId } from 'utils/playwright/databaseSetup'
test('analytics are not available for non-pro workspaces', async ({ page }) => {
const typebotId = cuid()
await importTypebotInDatabase(
getTestAsset('typebots/results/submissionHeader.json'),
{
id: typebotId,
workspaceId: starterWorkspaceId,
}
)
await injectFakeResults({ typebotId, count: 10 })
await page.goto(`/typebots/${typebotId}/results/analytics`)
const firstDropoffBox = page.locator('text="%" >> nth=0')
await firstDropoffBox.hover()
await expect(
page.locator('text="Unlock Drop-off rate by upgrading to Pro plan"')
).toBeVisible()
await firstDropoffBox.click()
await expect(
page.locator(
'text="You need to upgrade your plan in order to unlock in-depth analytics"'
)
).toBeVisible()
})

View File

@ -0,0 +1,63 @@
import { Flex, Spinner, useDisclosure } from '@chakra-ui/react'
import { useToast } from '@/hooks/useToast'
import { useTypebot } from '@/features/editor'
import { Stats } from 'models'
import React from 'react'
import { useAnswersCount } from '../hooks/useAnswersCount'
import {
Graph,
GraphProvider,
GroupsCoordinatesProvider,
} from '@/features/graph'
import { ChangePlanModal, LimitReached } from '@/features/billing'
import { StatsCards } from './StatsCards'
export const AnalyticsGraphContainer = ({ stats }: { stats?: Stats }) => {
const { isOpen, onOpen, onClose } = useDisclosure()
const { typebot, publishedTypebot } = useTypebot()
const { showToast } = useToast()
const { answersCounts } = useAnswersCount({
typebotId: publishedTypebot && typebot?.id,
onError: (err) => showToast({ title: err.name, description: err.message }),
})
return (
<Flex
w="full"
pos="relative"
bgColor="gray.50"
h="full"
justifyContent="center"
>
{publishedTypebot && answersCounts && stats ? (
<GraphProvider isReadOnly>
<GroupsCoordinatesProvider groups={publishedTypebot?.groups}>
<Graph
flex="1"
typebot={publishedTypebot}
onUnlockProPlanClick={onOpen}
answersCounts={[
{ ...answersCounts[0], totalAnswers: stats?.totalStarts },
...answersCounts?.slice(1),
]}
/>
</GroupsCoordinatesProvider>
</GraphProvider>
) : (
<Flex
justify="center"
align="center"
boxSize="full"
bgColor="rgba(255, 255, 255, 0.5)"
>
<Spinner color="gray" />
</Flex>
)}
<ChangePlanModal
onClose={onClose}
isOpen={isOpen}
type={LimitReached.ANALYTICS}
/>
<StatsCards stats={stats} pos="absolute" top={10} />
</Flex>
)
}

View File

@ -0,0 +1,46 @@
import {
GridProps,
SimpleGrid,
Skeleton,
Stat,
StatLabel,
StatNumber,
} from '@chakra-ui/react'
import { Stats } from 'models'
import React from 'react'
export const StatsCards = ({
stats,
...props
}: { stats?: Stats } & GridProps) => {
return (
<SimpleGrid columns={{ base: 1, md: 3 }} spacing="6" {...props}>
<Stat bgColor="white" p="4" rounded="md" boxShadow="md">
<StatLabel>Views</StatLabel>
{stats ? (
<StatNumber>{stats.totalViews}</StatNumber>
) : (
<Skeleton w="50%" h="10px" mt="2" />
)}
</Stat>
<Stat bgColor="white" p="4" rounded="md" boxShadow="md">
<StatLabel>Starts</StatLabel>
{stats ? (
<StatNumber>{stats.totalStarts}</StatNumber>
) : (
<Skeleton w="50%" h="10px" mt="2" />
)}
</Stat>
<Stat bgColor="white" p="4" rounded="md" boxShadow="md">
<StatLabel>Completion rate</StatLabel>
{stats ? (
<StatNumber>
{Math.round((stats.totalCompleted / stats.totalStarts) * 100)}%
</StatNumber>
) : (
<Skeleton w="50%" h="10px" mt="2" />
)}
</Stat>
</SimpleGrid>
)
}

View File

@ -0,0 +1,25 @@
import { fetcher } from '@/utils/helpers'
import useSWR from 'swr'
import { AnswersCount } from '../types'
export const useAnswersCount = ({
typebotId,
onError,
}: {
typebotId?: string
onError: (error: Error) => void
}) => {
const { data, error, mutate } = useSWR<
{ answersCounts: AnswersCount[] },
Error
>(
typebotId ? `/api/typebots/${typebotId}/results/answers/count` : null,
fetcher
)
if (error) onError(error)
return {
answersCounts: data?.answersCounts,
isLoading: !error && !data,
mutate,
}
}

View File

@ -0,0 +1,2 @@
export { AnalyticsGraphContainer } from './components/AnalyticsGraphContainer'
export type { AnswersCount } from './types'

View File

@ -0,0 +1 @@
export type AnswersCount = { groupId: string; totalAnswers: number }