Skip to content

Commit

Permalink
feat(results): 🛂 Limit incomplete submissions
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Feb 12, 2022
1 parent 3a7b9a0 commit ec470b5
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 32 deletions.
4 changes: 3 additions & 1 deletion apps/builder/components/analytics/StatsCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export const StatsCards = ({
<Stat bgColor="white" p="4" rounded="md" boxShadow="md">
<StatLabel>Completion rate</StatLabel>
{stats ? (
<StatNumber>{stats.completionRate}%</StatNumber>
<StatNumber>
{Math.round((stats.totalCompleted / stats.totalStarts) * 100)}%
</StatNumber>
) : (
<Skeleton w="50%" h="10px" mt="2" />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,11 @@ export const CreateFolderButton = ({ isLoading, onClick }: Props) => {
<Text>Create a folder</Text>
{isFreePlan(user) && <Tag colorScheme="orange">Pro</Tag>}
</HStack>
{user && (
<UpgradeModal
isOpen={isOpen}
onClose={onClose}
user={user}
type={LimitReached.FOLDER}
/>
)}
<UpgradeModal
isOpen={isOpen}
onClose={onClose}
type={LimitReached.FOLDER}
/>
</Button>
)
}
41 changes: 40 additions & 1 deletion apps/builder/components/shared/Info.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { Alert, AlertIcon, AlertProps } from '@chakra-ui/react'
import {
Alert,
AlertIcon,
AlertProps,
Button,
HStack,
Text,
useDisclosure,
} from '@chakra-ui/react'
import React from 'react'
import { UpgradeModal } from './modals/UpgradeModal.'
import { LimitReached } from './modals/UpgradeModal./UpgradeModal'

export const Info = (props: AlertProps) => (
<Alert status="info" bgColor={'blue.50'} rounded="md" {...props}>
Expand All @@ -11,3 +21,32 @@ export const Info = (props: AlertProps) => (
export const PublishFirstInfo = (props: AlertProps) => (
<Info {...props}>You need to publish your typebot first</Info>
)

export const UnlockProPlanInfo = ({
contentLabel,
buttonLabel,
type,
}: {
contentLabel: string
buttonLabel: string
type?: LimitReached
}) => {
const { isOpen, onOpen, onClose } = useDisclosure()
return (
<Alert
status="info"
bgColor={'blue.50'}
rounded="md"
justifyContent="space-between"
>
<HStack>
<AlertIcon />
<Text>{contentLabel}</Text>
</HStack>
<Button colorScheme="blue" onClick={onOpen}>
{buttonLabel}
</Button>
<UpgradeModal isOpen={isOpen} onClose={onClose} type={type} />
</Alert>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
} from '@chakra-ui/react'
import { PricingCard } from './PricingCard'
import { ActionButton } from './ActionButton'
import { User } from 'db'
import { pay } from 'services/stripe'
import { useUser } from 'contexts/UserContext'

export enum LimitReached {
BRAND = 'Remove branding',
Expand All @@ -24,18 +24,13 @@ export enum LimitReached {
}

type UpgradeModalProps = {
user: User
type: LimitReached
type?: LimitReached
isOpen: boolean
onClose: () => void
}

export const UpgradeModal = ({
type,
user,
onClose,
isOpen,
}: UpgradeModalProps) => {
export const UpgradeModal = ({ type, onClose, isOpen }: UpgradeModalProps) => {
const { user } = useUser()
const [payLoading, setPayLoading] = useState(false)
const [userLanguage, setUserLanguage] = useState<string>('en')

Expand Down
8 changes: 8 additions & 0 deletions apps/builder/layouts/results/ResultsContent.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { Button, Flex, HStack, Tag, useToast, Text } from '@chakra-ui/react'
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { useUser } from 'contexts/UserContext'
import { useRouter } from 'next/router'
import React, { useMemo } from 'react'
import { useStats } from 'services/analytics'
import { isFreePlan } from 'services/user'
import { AnalyticsContent } from './AnalyticsContent'
import { SubmissionsContent } from './SubmissionContent'

export const ResultsContent = () => {
const router = useRouter()
const { user } = useUser()
const { typebot } = useTypebot()
const isAnalytics = useMemo(
() => router.pathname.endsWith('analytics'),
Expand Down Expand Up @@ -76,6 +79,11 @@ export const ResultsContent = () => {
typebotId={typebot.id}
onDeleteResults={handleDeletedResults}
totalResults={stats?.totalStarts ?? 0}
totalHiddenResults={
isFreePlan(user)
? (stats?.totalStarts ?? 0) - (stats?.totalCompleted ?? 0)
: undefined
}
/>
))}
</Flex>
Expand Down
20 changes: 13 additions & 7 deletions apps/builder/layouts/results/SubmissionContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ import {
useResults,
} from 'services/results'
import { unparse } from 'papaparse'
import { UnlockProPlanInfo } from 'components/shared/Info'

type Props = {
typebotId: string
totalResults: number
totalHiddenResults?: number
onDeleteResults: (total: number) => void
}
export const SubmissionsContent = ({
typebotId,
totalResults,
totalHiddenResults,
onDeleteResults,
}: Props) => {
const [selectedIndices, setSelectedIndices] = useState<number[]>([])
Expand Down Expand Up @@ -65,13 +68,10 @@ export const SubmissionsContent = ({
setIsDeleteLoading(false)
}

const totalSelected = useMemo(
() =>
selectedIndices.length === results?.length
? totalResults
: selectedIndices.length,
[results?.length, selectedIndices.length, totalResults]
)
const totalSelected =
selectedIndices.length > 0 && selectedIndices.length === results?.length
? totalResults - (totalHiddenResults ?? 0)
: selectedIndices.length

const handleScrolledToBottom = useCallback(
() => setSize((state) => state + 1),
Expand Down Expand Up @@ -111,6 +111,12 @@ export const SubmissionsContent = ({

return (
<Stack maxW="1200px" w="full" pb="28">
{totalHiddenResults && (
<UnlockProPlanInfo
buttonLabel={`Unlock ${totalHiddenResults} results`}
contentLabel="You are seeing complete submissions only."
/>
)}
<Flex w="full" justifyContent="flex-end">
<ResultsActionButtons
isDeleteLoading={isDeleteLoading}
Expand Down
2 changes: 2 additions & 0 deletions apps/builder/pages/api/typebots/[typebotId]/results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { User } from 'db'
import prisma from 'libs/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from 'next-auth/react'
import { isFreePlan } from 'services/user'
import { methodNotAllowed } from 'utils'

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
Expand All @@ -27,6 +28,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
typebotId,
typebot: { ownerId: user.id },
answers: { some: {} },
isCompleted: isFreePlan(user),
},
orderBy: {
createdAt: 'desc',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const stats: Stats = {
totalViews,
totalStarts,
completionRate: Math.round((totalCompleted / totalStarts) * 100),
totalCompleted,
}
return res.status(200).send({ stats })
}
Expand Down
12 changes: 12 additions & 0 deletions apps/builder/playwright/tests/results.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import test, { expect, Page } from '@playwright/test'
import { readFileSync } from 'fs'
import { defaultTextInputOptions, InputStepType } from 'models'
import { parse } from 'papaparse'
import path from 'path'
import { generate } from 'short-uuid'
import {
createResults,
Expand Down Expand Up @@ -86,6 +87,17 @@ test.describe('Results page', () => {
const { data: dataAll } = parse(fileAll)
validateExportAll(dataAll)
})

test.describe('Free user', () => {
test.use({
storageState: path.join(__dirname, '../freeUser.json'),
})
test("Incomplete results shouldn't be displayed", async ({ page }) => {
await page.goto(`/typebots/${typebotId}/results`)
await page.click('text=Unlock 200 results')
await expect(page.locator('text=Upgrade now')).toBeVisible()
})
})
})

const validateExportSelection = (data: unknown[]) => {
Expand Down
8 changes: 4 additions & 4 deletions packages/bot-engine/src/components/ChatBlock/ChatBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ export const ChatBlock = ({
const isSingleChoiceStep =
isChoiceInput(currentStep) && !currentStep.options.isMultipleChoice
if (isSingleChoiceStep) {
onBlockEnd(
currentStep.items.find((i) => i.content === answerContent)
?.outgoingEdgeId
)
const nextEdgeId = currentStep.items.find(
(i) => i.content === answerContent
)?.outgoingEdgeId
if (nextEdgeId) return onBlockEnd(nextEdgeId)
}

if (currentStep?.outgoingEdgeId || displayedSteps.length === steps.length)
Expand Down
2 changes: 1 addition & 1 deletion packages/models/src/answer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export type Answer = Omit<AnswerFromPrisma, 'resultId' | 'createdAt'>
export type Stats = {
totalViews: number
totalStarts: number
completionRate: number
totalCompleted: number
}

3 comments on commit ec470b5

@vercel
Copy link

@vercel vercel bot commented on ec470b5 Feb 12, 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:

landing-page-v2 – ./apps/landing-page

landing-page-v2-typebot-io.vercel.app
landing-page-v2-jade.vercel.app
landing-page-v2-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on ec470b5 Feb 12, 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

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

@vercel
Copy link

@vercel vercel bot commented on ec470b5 Feb 12, 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 – ./apps/viewer

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

Please sign in to comment.