2
0

🧰 Aggregate utils & set up results collection in viewer

This commit is contained in:
Baptiste Arnaud
2021-12-29 10:22:26 +01:00
parent 447172d0cb
commit f088f694b9
42 changed files with 404 additions and 141 deletions

View File

@ -1,15 +1,6 @@
import React from 'react'
export const ErrorPage = ({ error }: { error: 'offline' | '500' | 'IE' }) => {
let errorLabel =
'An error occured. Please try to refresh or contact the owner of this bot.'
if (error === 'offline') {
errorLabel =
'Looks like your device is offline. Please, try to refresh the page.'
}
if (error === 'IE') {
errorLabel = "This bot isn't compatible with Internet Explorer."
}
export const ErrorPage = ({ error }: { error: Error }) => {
return (
<div
style={{
@ -20,10 +11,8 @@ export const ErrorPage = ({ error }: { error: 'offline' | '500' | 'IE' }) => {
flexDirection: 'column',
}}
>
{error === '500' && (
<h1 style={{ fontWeight: 'bold', fontSize: '30px' }}>500</h1>
)}
<h2>{errorLabel}</h2>
<h1 style={{ fontWeight: 'bold', fontSize: '30px' }}>{error.name}</h1>
<h2>{error.message}</h2>
</div>
)
}

View File

@ -1,6 +1,7 @@
import { PublicTypebot, TypebotViewer } from 'bot-engine'
import React from 'react'
import { Answer, PublicTypebot, TypebotViewer } from 'bot-engine'
import React, { useEffect, useState } from 'react'
import { SEO } from '../components/Seo'
import { createResult, updateResult } from '../services/result'
import { ErrorPage } from './ErrorPage'
import { NotFoundPage } from './NotFoundPage'
@ -10,17 +11,61 @@ export type TypebotPageProps = {
isIE: boolean
}
const sessionStorageKey = 'resultId'
export const TypebotPage = ({ typebot, isIE, url }: TypebotPageProps) => {
const [error, setError] = useState<Error | undefined>(
isIE ? new Error('Internet explorer is not supported') : undefined
)
const [resultId, setResultId] = useState<string | undefined>()
useEffect(() => {
initializeResult()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const initializeResult = async () => {
if (!typebot) return
const resultIdFromSession = sessionStorage.getItem(sessionStorageKey)
if (resultIdFromSession) setResultId(resultIdFromSession)
else {
const { error, data: result } = await createResult(typebot.typebotId)
if (error) setError(error)
if (result) {
setResultId(result.id)
sessionStorage.setItem(sessionStorageKey, result.id)
}
}
}
const handleAnswersUpdate = async (answers: Answer[]) => {
if (!resultId) return setError(new Error('Result was not created'))
const { error } = await updateResult(resultId, { answers })
if (error) setError(error)
}
const handleCompleted = async () => {
if (!resultId) return setError(new Error('Result was not created'))
const { error } = await updateResult(resultId, { isCompleted: true })
if (error) setError(error)
}
if (!typebot) {
return <NotFoundPage />
}
if (isIE) {
return <ErrorPage error={'IE'} />
if (error) {
return <ErrorPage error={error} />
}
return (
<div style={{ height: '100vh' }}>
<SEO url={url} chatbotName={typebot.name} />
<TypebotViewer typebot={typebot} />
{resultId && (
<TypebotViewer
typebot={typebot}
onAnswersUpdate={handleAnswersUpdate}
onCompleted={handleCompleted}
/>
)}
</div>
)
}

View File

@ -0,0 +1,6 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const withTM = require('next-transpile-modules')(['utils'])
module.exports = withTM({
reactStrictMode: true,
})

View File

@ -13,7 +13,8 @@
"db": "*",
"next": "^12.0.7",
"react": "^17.0.2",
"react-dom": "^17.0.2"
"react-dom": "^17.0.2",
"utils": "*"
},
"devDependencies": {
"@types/node": "^17.0.4",
@ -24,6 +25,7 @@
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-prettier": "^4.0.0",
"next-transpile-modules": "^9.0.0",
"prettier": "^2.5.1",
"typescript": "^4.5.4"
}

View File

@ -11,7 +11,7 @@ export const getServerSideProps: GetServerSideProps = async (
const pathname = context.resolvedUrl.split('?')[0]
try {
if (!context.req.headers.host) return { props: {} }
typebot = await getTypebotFromPublicId(context.query.publicId.toString())
typebot = await getTypebotFromPublicId(context.query.publicId?.toString())
if (!typebot) return { props: {} }
return {
props: {
@ -32,8 +32,9 @@ export const getServerSideProps: GetServerSideProps = async (
}
const getTypebotFromPublicId = async (
publicId: string
publicId?: string
): Promise<PublicTypebot | undefined> => {
if (!publicId) return
const typebot = await prisma.publicTypebot.findUnique({
where: { publicId },
})

View File

@ -0,0 +1,16 @@
import prisma from 'libs/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { methodNotAllowed } from 'utils'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'POST') {
const { typebotId } = JSON.parse(req.body)
const result = await prisma.result.create({
data: { typebotId },
})
return res.send(result)
}
return methodNotAllowed(res)
}
export default handler

View File

@ -0,0 +1,18 @@
import prisma from 'libs/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { methodNotAllowed } from 'utils'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'PATCH') {
const data = JSON.parse(req.body)
const id = req.query.id.toString()
const result = await prisma.result.update({
where: { id },
data: { ...data, updatedAt: new Date() },
})
return res.send(result)
}
return methodNotAllowed(res)
}
export default handler

View File

@ -0,0 +1,21 @@
import { Result } from 'db'
import { sendRequest } from 'utils'
export const createResult = async (typebotId: string) => {
return sendRequest<Result>({
url: `/api/results`,
method: 'POST',
body: { typebotId },
})
}
export const updateResult = async (
resultId: string,
result: Partial<Result>
) => {
return sendRequest<Result>({
url: `/api/results/${resultId}`,
method: 'PATCH',
body: result,
})
}

View File

@ -4,7 +4,7 @@
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
@ -14,8 +14,9 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"composite": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "cypress"]
}