🧰 Aggregate utils & set up results collection in viewer
This commit is contained in:
@ -10,7 +10,7 @@ import { Block, StartBlock } from 'bot-engine'
|
||||
import { useGraph } from 'contexts/GraphContext'
|
||||
import { useDnd } from 'contexts/DndContext'
|
||||
import { StepsList } from './StepsList'
|
||||
import { isDefined } from 'services/utils'
|
||||
import { isDefined } from 'utils'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { ContextMenu } from 'components/shared/ContextMenu'
|
||||
import { BlockNodeContextMenu } from './BlockNodeContextMenu'
|
||||
|
@ -4,7 +4,7 @@ import { Block, StartStep, Step, StepType } from 'bot-engine'
|
||||
import { SourceEndpoint } from './SourceEndpoint'
|
||||
import { useGraph } from 'contexts/GraphContext'
|
||||
import { StepIcon } from 'components/board/StepTypesList/StepIcon'
|
||||
import { isDefined } from 'services/utils'
|
||||
import { isDefined } from 'utils'
|
||||
import { Coordinates } from '@dnd-kit/core/dist/types'
|
||||
import { TextEditor } from './TextEditor/TextEditor'
|
||||
import { StepContent } from './StepContent'
|
||||
|
@ -91,7 +91,7 @@ export const PreviewDrawer = () => {
|
||||
>
|
||||
<TypebotViewer
|
||||
typebot={publicTypebot}
|
||||
onNewBlockVisisble={handleNewBlockVisible}
|
||||
onNewBlockVisible={handleNewBlockVisible}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
|
@ -35,11 +35,11 @@ import {
|
||||
import {
|
||||
fetcher,
|
||||
insertItemInList,
|
||||
isDefined,
|
||||
omit,
|
||||
preventUserFromRefreshing,
|
||||
} from 'services/utils'
|
||||
import useSWR from 'swr'
|
||||
import { isDefined } from 'utils'
|
||||
import { NewBlockPayload, Coordinates } from './GraphContext'
|
||||
|
||||
const typebotContext = createContext<{
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { isDefined } from 'services/utils'
|
||||
import { isDefined } from 'utils'
|
||||
import { updateUser as updateUserInDb } from 'services/user'
|
||||
import { useToast } from '@chakra-ui/react'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
6
apps/builder/next.config.js
Normal file
6
apps/builder/next.config.js
Normal 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,
|
||||
})
|
@ -32,6 +32,7 @@
|
||||
"framer-motion": "^4",
|
||||
"htmlparser2": "^7.2.0",
|
||||
"kbar": "^0.1.0-beta.24",
|
||||
"micro": "^9.3.4",
|
||||
"micro-cors": "^0.1.1",
|
||||
"next": "^12.0.7",
|
||||
"next-auth": "beta",
|
||||
@ -50,7 +51,8 @@
|
||||
"styled-components": "^5.3.3",
|
||||
"svg-round-corners": "^0.3.0",
|
||||
"swr": "^1.1.1",
|
||||
"use-debounce": "^7.0.1"
|
||||
"use-debounce": "^7.0.1",
|
||||
"utils": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/cypress": "^8.0.2",
|
||||
@ -69,6 +71,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"
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { DashboardFolder, User } from 'db'
|
||||
import prisma from 'libs/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const session = await getSession({ req })
|
||||
|
@ -2,7 +2,7 @@ import { DashboardFolder } from 'db'
|
||||
import prisma from 'libs/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const session = await getSession({ req })
|
||||
|
@ -1,7 +1,7 @@
|
||||
import prisma from 'libs/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const session = await getSession({ req })
|
||||
|
@ -1,7 +1,7 @@
|
||||
import prisma from 'libs/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const session = await getSession({ req })
|
||||
|
@ -1,7 +1,7 @@
|
||||
import aws from 'aws-sdk'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
|
||||
const maxUploadFileSize = 10485760 // 10 MB
|
||||
const handler = async (
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
import Stripe from 'stripe'
|
||||
|
||||
const usdPriceIdTest = 'price_1Jc4TQKexUFvKTWyGvsH4Ff5'
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { User } from 'db'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
import Stripe from 'stripe'
|
||||
|
||||
const createCheckoutSession = async (
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
import Stripe from 'stripe'
|
||||
import Cors from 'micro-cors'
|
||||
import { buffer } from 'micro'
|
||||
|
@ -3,7 +3,7 @@ import { User } from 'db'
|
||||
import prisma from 'libs/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const session = await getSession({ req })
|
||||
|
@ -1,7 +1,7 @@
|
||||
import prisma from 'libs/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const session = await getSession({ req })
|
||||
|
@ -1,7 +1,7 @@
|
||||
import prisma from 'libs/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
import { methodNotAllowed } from 'services/api/utils'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const session = await getSession({ req })
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
firstStepOffsetY,
|
||||
} from 'contexts/GraphContext'
|
||||
import { roundCorners } from 'svg-round-corners'
|
||||
import { isDefined } from './utils'
|
||||
import { isDefined } from 'utils'
|
||||
|
||||
export const computeFlowChartConnectorPath = ({
|
||||
sourcePosition,
|
||||
|
@ -39,10 +39,6 @@ export const insertItemInList = <T>(
|
||||
newItem: T
|
||||
): T[] => [...arr.slice(0, index), newItem, ...arr.slice(index)]
|
||||
|
||||
export const isDefined = <T>(value: T | undefined | null): value is T => {
|
||||
return <T>value !== undefined && <T>value !== null
|
||||
}
|
||||
|
||||
export const preventUserFromRefreshing = (e: BeforeUnloadEvent) => {
|
||||
e.preventDefault()
|
||||
e.returnValue = ''
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
6
apps/viewer/next.config.js
Normal file
6
apps/viewer/next.config.js
Normal 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,
|
||||
})
|
@ -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"
|
||||
}
|
||||
|
@ -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 },
|
||||
})
|
||||
|
16
apps/viewer/pages/api/results.ts
Normal file
16
apps/viewer/pages/api/results.ts
Normal 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
|
18
apps/viewer/pages/api/results/[id].ts
Normal file
18
apps/viewer/pages/api/results/[id].ts
Normal 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
|
21
apps/viewer/services/result.ts
Normal file
21
apps/viewer/services/result.ts
Normal 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,
|
||||
})
|
||||
}
|
@ -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"]
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
"types": "dist/index.d.ts",
|
||||
"dependencies": {
|
||||
"db": "*",
|
||||
"fast-equals": "^2.0.4",
|
||||
"react-frame-component": "^5.2.1",
|
||||
"react-scroll": "^1.8.4",
|
||||
"react-transition-group": "^4.4.2"
|
||||
@ -15,18 +16,18 @@
|
||||
"@rollup/plugin-commonjs": "^21.0.1",
|
||||
"@rollup/plugin-node-resolve": "^13.1.1",
|
||||
"@rollup/plugin-typescript": "^8.3.0",
|
||||
"@types/react": "^17.0.37",
|
||||
"@types/react": "^17.0.38",
|
||||
"@types/react-scroll": "^1.8.3",
|
||||
"@types/react-transition-group": "^4.4.4",
|
||||
"autoprefixer": "^10.4.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.5",
|
||||
"rollup": "^2.61.1",
|
||||
"rollup-plugin-dts": "^4.0.1",
|
||||
"rollup": "^2.62.0",
|
||||
"rollup-plugin-dts": "^4.1.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||
"rollup-plugin-postcss": "^4.0.2",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"tailwindcss": "^3.0.7",
|
||||
"tailwindcss": "^3.0.8",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -8,7 +8,7 @@ import { HostAvatarsContext } from '../../contexts/HostAvatarsContext'
|
||||
|
||||
type ChatBlockProps = {
|
||||
block: Block
|
||||
onBlockEnd: (nextBlockId: string) => void
|
||||
onBlockEnd: (nextBlockId?: string) => void
|
||||
}
|
||||
|
||||
export const ChatBlock = ({ block, onBlockEnd }: ChatBlockProps) => {
|
||||
@ -31,7 +31,10 @@ export const ChatBlock = ({ block, onBlockEnd }: ChatBlockProps) => {
|
||||
|
||||
const displayNextStep = () => {
|
||||
const currentStep = [...displayedSteps].pop()
|
||||
if (currentStep?.target?.blockId)
|
||||
if (
|
||||
currentStep?.target?.blockId ||
|
||||
displayedSteps.length === block.steps.length
|
||||
)
|
||||
return onBlockEnd(currentStep?.target?.blockId)
|
||||
const nextStep = block.steps[displayedSteps.length]
|
||||
if (nextStep) setDisplayedSteps([...displayedSteps, nextStep])
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useAnswers } from '../../../contexts/AnswersContext'
|
||||
import { useHostAvatars } from '../../../contexts/HostAvatarsContext'
|
||||
import { Step } from '../../../models'
|
||||
import { isTextInputStep, isTextStep } from '../../../services/utils'
|
||||
@ -13,13 +14,21 @@ export const ChatStep = ({
|
||||
step: Step
|
||||
onTransitionEnd: () => void
|
||||
}) => {
|
||||
const { addAnswer } = useAnswers()
|
||||
|
||||
const handleInputSubmit = (content: string) => {
|
||||
addAnswer({ stepId: step.id, blockId: step.blockId, content })
|
||||
onTransitionEnd()
|
||||
}
|
||||
|
||||
if (isTextStep(step))
|
||||
return <HostMessageBubble step={step} onTransitionEnd={onTransitionEnd} />
|
||||
if (isTextInputStep(step)) return <InputChatStep onSubmit={onTransitionEnd} />
|
||||
if (isTextInputStep(step))
|
||||
return <InputChatStep onSubmit={handleInputSubmit} />
|
||||
return <span>No step</span>
|
||||
}
|
||||
|
||||
const InputChatStep = ({ onSubmit }: { onSubmit: () => void }) => {
|
||||
const InputChatStep = ({ onSubmit }: { onSubmit: (value: string) => void }) => {
|
||||
const { addNewAvatarOffset } = useHostAvatars()
|
||||
const [answer, setAnswer] = useState<string>()
|
||||
|
||||
@ -29,7 +38,7 @@ const InputChatStep = ({ onSubmit }: { onSubmit: () => void }) => {
|
||||
|
||||
const handleSubmit = (value: string) => {
|
||||
setAnswer(value)
|
||||
onSubmit()
|
||||
onSubmit(value)
|
||||
}
|
||||
|
||||
if (answer) {
|
||||
|
@ -1,28 +1,36 @@
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { PublicTypebot } from '..'
|
||||
import { Answer, PublicTypebot } from '..'
|
||||
|
||||
import { Block } from '..'
|
||||
import { ChatBlock } from './ChatBlock/ChatBlock'
|
||||
import { useFrame } from 'react-frame-component'
|
||||
import { setCssVariablesValue } from '../services/theme'
|
||||
import { useAnswers } from '../contexts/AnswersContext'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
type Props = {
|
||||
typebot: PublicTypebot
|
||||
onNewBlockVisible: (blockId: string) => void
|
||||
onAnswersUpdate: (answers: Answer[]) => void
|
||||
onCompleted: () => void
|
||||
}
|
||||
export const ConversationContainer = ({
|
||||
typebot,
|
||||
onNewBlockVisisble,
|
||||
}: {
|
||||
typebot: PublicTypebot
|
||||
onNewBlockVisisble: (blockId: string) => void
|
||||
}) => {
|
||||
onNewBlockVisible,
|
||||
onAnswersUpdate,
|
||||
onCompleted,
|
||||
}: Props) => {
|
||||
const { document: frameDocument } = useFrame()
|
||||
const [displayedBlocks, setDisplayedBlocks] = useState<Block[]>([])
|
||||
|
||||
const [isConversationEnded, setIsConversationEnded] = useState(false)
|
||||
const [localAnswers, setLocalAnswers] = useState<Answer[]>([])
|
||||
const { answers } = useAnswers()
|
||||
const bottomAnchor = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const displayNextBlock = (blockId: string) => {
|
||||
const displayNextBlock = (blockId?: string) => {
|
||||
if (!blockId) return onCompleted()
|
||||
const nextBlock = typebot.blocks.find((b) => b.id === blockId)
|
||||
if (!nextBlock) return
|
||||
onNewBlockVisisble(blockId)
|
||||
if (!nextBlock) return onCompleted()
|
||||
onNewBlockVisible(blockId)
|
||||
setDisplayedBlocks([...displayedBlocks, nextBlock])
|
||||
}
|
||||
|
||||
@ -35,6 +43,12 @@ export const ConversationContainer = ({
|
||||
setCssVariablesValue(typebot.theme, frameDocument.body.style)
|
||||
}, [typebot.theme, frameDocument])
|
||||
|
||||
useEffect(() => {
|
||||
if (deepEqual(localAnswers, answers)) return
|
||||
setLocalAnswers(answers)
|
||||
onAnswersUpdate(answers)
|
||||
}, [answers])
|
||||
|
||||
return (
|
||||
<div
|
||||
className="overflow-y-scroll w-full lg:w-3/4 min-h-full rounded lg:px-5 px-3 pt-10 relative scrollable-container typebot-chat-view"
|
||||
@ -48,14 +62,7 @@ export const ConversationContainer = ({
|
||||
/>
|
||||
))}
|
||||
{/* We use a block to simulate padding because it makes iOS scroll flicker */}
|
||||
<div
|
||||
className="w-full"
|
||||
ref={bottomAnchor}
|
||||
style={{
|
||||
transition: isConversationEnded ? 'height 1s' : '',
|
||||
height: isConversationEnded ? '5%' : '20%',
|
||||
}}
|
||||
/>
|
||||
<div className="w-full" ref={bottomAnchor} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,19 +1,23 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import { BackgroundType, PublicTypebot } from '../models'
|
||||
import { Answer, BackgroundType, PublicTypebot } from '../models'
|
||||
import { TypebotContext } from '../contexts/TypebotContext'
|
||||
import Frame from 'react-frame-component'
|
||||
//@ts-ignore
|
||||
import style from '../assets/style.css'
|
||||
import { ConversationContainer } from './ConversationContainer'
|
||||
import { ResultContext } from '../contexts/ResultsContext'
|
||||
import { AnswersContext } from '../contexts/AnswersContext'
|
||||
|
||||
export type TypebotViewerProps = {
|
||||
typebot: PublicTypebot
|
||||
onNewBlockVisisble?: (blockId: string) => void
|
||||
onNewBlockVisible?: (blockId: string) => void
|
||||
onAnswersUpdate?: (answers: Answer[]) => void
|
||||
onCompleted?: () => void
|
||||
}
|
||||
export const TypebotViewer = ({
|
||||
typebot,
|
||||
onNewBlockVisisble,
|
||||
onNewBlockVisible,
|
||||
onAnswersUpdate,
|
||||
onCompleted,
|
||||
}: TypebotViewerProps) => {
|
||||
const containerBgColor = useMemo(
|
||||
() =>
|
||||
@ -23,7 +27,13 @@ export const TypebotViewer = ({
|
||||
[typebot.theme.general.background]
|
||||
)
|
||||
const handleNewBlockVisible = (blockId: string) => {
|
||||
if (onNewBlockVisisble) onNewBlockVisisble(blockId)
|
||||
if (onNewBlockVisible) onNewBlockVisible(blockId)
|
||||
}
|
||||
const handleAnswersUpdate = (answers: Answer[]) => {
|
||||
if (onAnswersUpdate) onAnswersUpdate(answers)
|
||||
}
|
||||
const handleCompleted = () => {
|
||||
if (onCompleted) onCompleted()
|
||||
}
|
||||
|
||||
return (
|
||||
@ -38,22 +48,24 @@ export const TypebotViewer = ({
|
||||
}}
|
||||
/>
|
||||
<TypebotContext typebot={typebot}>
|
||||
<ResultContext typebotId={typebot.id}>
|
||||
<AnswersContext typebotId={typebot.id}>
|
||||
<div
|
||||
className="flex text-base overflow-hidden bg-cover h-screen w-screen flex-col items-center typebot-container"
|
||||
style={{
|
||||
// We set this as inline style to avoid color for SSR
|
||||
// We set this as inline style to avoid color flash for SSR
|
||||
backgroundColor: containerBgColor,
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full h-full justify-center">
|
||||
<ConversationContainer
|
||||
typebot={typebot}
|
||||
onNewBlockVisisble={handleNewBlockVisible}
|
||||
onNewBlockVisible={handleNewBlockVisible}
|
||||
onAnswersUpdate={handleAnswersUpdate}
|
||||
onCompleted={handleCompleted}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ResultContext>
|
||||
</AnswersContext>
|
||||
</TypebotContext>
|
||||
</Frame>
|
||||
)
|
||||
|
34
packages/bot-engine/src/contexts/AnswersContext.tsx
Normal file
34
packages/bot-engine/src/contexts/AnswersContext.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { Answer } from '../models'
|
||||
import React, { createContext, ReactNode, useContext, useState } from 'react'
|
||||
|
||||
const answersContext = createContext<{
|
||||
answers: Answer[]
|
||||
addAnswer: (answer: Answer) => void
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
|
||||
export const AnswersContext = ({
|
||||
children,
|
||||
typebotId,
|
||||
}: {
|
||||
children: ReactNode
|
||||
typebotId: string
|
||||
}) => {
|
||||
const [answers, setAnswers] = useState<Answer[]>([])
|
||||
|
||||
const addAnswer = (answer: Answer) =>
|
||||
setAnswers((answers) => [...answers, answer])
|
||||
|
||||
return (
|
||||
<answersContext.Provider
|
||||
value={{
|
||||
answers,
|
||||
addAnswer,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</answersContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useAnswers = () => useContext(answersContext)
|
@ -1,50 +0,0 @@
|
||||
import { Answer, Result } from '../models'
|
||||
import React, {
|
||||
createContext,
|
||||
Dispatch,
|
||||
ReactNode,
|
||||
SetStateAction,
|
||||
useContext,
|
||||
useState,
|
||||
} from 'react'
|
||||
|
||||
const resultContext = createContext<{
|
||||
result: Result
|
||||
setResult: Dispatch<SetStateAction<Result>>
|
||||
addAnswer: (answer: Answer) => void
|
||||
//@ts-ignore
|
||||
}>({})
|
||||
|
||||
export const ResultContext = ({
|
||||
children,
|
||||
typebotId,
|
||||
}: {
|
||||
children: ReactNode
|
||||
typebotId: string
|
||||
}) => {
|
||||
const [result, setResult] = useState<Result>({
|
||||
id: 'tmp',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
answers: [],
|
||||
typebotId,
|
||||
isCompleted: false,
|
||||
})
|
||||
|
||||
const addAnswer = (answer: Answer) =>
|
||||
setResult({ ...result, answers: [...result.answers, answer] })
|
||||
|
||||
return (
|
||||
<resultContext.Provider
|
||||
value={{
|
||||
result,
|
||||
setResult,
|
||||
addAnswer,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</resultContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useResult = () => useContext(resultContext)
|
2
packages/utils/index.ts
Normal file
2
packages/utils/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './utils'
|
||||
export * from './apiUtils'
|
13
packages/utils/package.json
Normal file
13
packages/utils/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "utils",
|
||||
"version": "1.0.0",
|
||||
"main": "index.ts",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^12.0.7"
|
||||
}
|
||||
}
|
10
packages/utils/tsconfig.json
Normal file
10
packages/utils/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
27
packages/utils/utils.ts
Normal file
27
packages/utils/utils.ts
Normal file
@ -0,0 +1,27 @@
|
||||
export const sendRequest = async <ResponseData>({
|
||||
url,
|
||||
method,
|
||||
body,
|
||||
}: {
|
||||
url: string
|
||||
method: string
|
||||
body?: Record<string, unknown>
|
||||
}): Promise<{ data?: ResponseData; error?: Error }> => {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
mode: 'cors',
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
})
|
||||
if (!response.ok) throw new Error(response.statusText)
|
||||
const data = await response.json()
|
||||
return { data }
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return { error: e as Error }
|
||||
}
|
||||
}
|
||||
|
||||
export const isDefined = <T>(value: T | undefined | null): value is T => {
|
||||
return <T>value !== undefined && <T>value !== null
|
||||
}
|
107
yarn.lock
107
yarn.lock
@ -1903,6 +1903,11 @@ arch@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
|
||||
integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
|
||||
|
||||
arg@4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0"
|
||||
integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==
|
||||
|
||||
arg@^4.1.0:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
|
||||
@ -2309,6 +2314,11 @@ builtin-status-codes@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
|
||||
|
||||
bytes@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
||||
integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
|
||||
|
||||
bytes@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
|
||||
@ -2588,6 +2598,11 @@ constants-browserify@1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
|
||||
integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
|
||||
|
||||
content-type@1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||
|
||||
convert-source-map@1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
|
||||
@ -2999,6 +3014,11 @@ delayed-stream@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
depd@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
|
||||
integrity sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=
|
||||
|
||||
depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
@ -3204,6 +3224,14 @@ end-of-stream@^1.1.0:
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
enhanced-resolve@^5.7.0:
|
||||
version "5.8.3"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0"
|
||||
integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
|
||||
enquirer@^2.3.5, enquirer@^2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
|
||||
@ -3986,7 +4014,7 @@ globby@^11.0.4:
|
||||
merge2 "^1.3.0"
|
||||
slash "^3.0.0"
|
||||
|
||||
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
|
||||
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
|
||||
@ -4083,6 +4111,16 @@ htmlparser2@^7.2.0:
|
||||
domutils "^2.8.0"
|
||||
entities "^3.0.1"
|
||||
|
||||
http-errors@1.6.2:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
|
||||
integrity sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=
|
||||
dependencies:
|
||||
depd "1.1.1"
|
||||
inherits "2.0.3"
|
||||
setprototypeof "1.0.3"
|
||||
statuses ">= 1.3.1 < 2"
|
||||
|
||||
http-errors@1.7.3:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
|
||||
@ -4126,6 +4164,11 @@ hyphenate-style-name@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
|
||||
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
|
||||
|
||||
iconv-lite@0.4.19:
|
||||
version "0.4.19"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
|
||||
integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==
|
||||
|
||||
iconv-lite@0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
@ -4232,6 +4275,11 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
inherits@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
ini@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
|
||||
@ -4418,6 +4466,11 @@ is-shared-array-buffer@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6"
|
||||
integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==
|
||||
|
||||
is-stream@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
|
||||
|
||||
is-stream@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
|
||||
@ -4862,6 +4915,16 @@ micro-cors@^0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/micro-cors/-/micro-cors-0.1.1.tgz#af7a480182c114ffd1ada84ad9dffc52bb4f4054"
|
||||
integrity sha512-6WqIahA5sbQR1Gjexp1VuWGFDKbZZleJb/gy1khNGk18a6iN1FdTcr3Q8twaxkV5H94RjxIBjirYbWCehpMBFw==
|
||||
|
||||
micro@^9.3.4:
|
||||
version "9.3.4"
|
||||
resolved "https://registry.yarnpkg.com/micro/-/micro-9.3.4.tgz#745a494e53c8916f64fb6a729f8cbf2a506b35ad"
|
||||
integrity sha512-smz9naZwTG7qaFnEZ2vn248YZq9XR+XoOH3auieZbkhDL4xLOxiE+KqG8qqnBeKfXA9c1uEFGCxPN1D+nT6N7w==
|
||||
dependencies:
|
||||
arg "4.1.0"
|
||||
content-type "1.0.4"
|
||||
is-stream "1.1.0"
|
||||
raw-body "2.3.2"
|
||||
|
||||
micromatch@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
|
||||
@ -4983,6 +5046,14 @@ next-auth@beta:
|
||||
preact-render-to-string "^5.1.19"
|
||||
uuid "^8.3.2"
|
||||
|
||||
next-transpile-modules@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/next-transpile-modules/-/next-transpile-modules-9.0.0.tgz#133b1742af082e61cc76b02a0f12ffd40ce2bf90"
|
||||
integrity sha512-VCNFOazIAnXn1hvgYYSTYMnoWgKgwlYh4lm1pKbSfiB3kj5ZYLcKVhfh3jkPOg1cnd9DP+pte9yCUocdPEUBTQ==
|
||||
dependencies:
|
||||
enhanced-resolve "^5.7.0"
|
||||
escalade "^3.1.1"
|
||||
|
||||
next@^12.0.7:
|
||||
version "12.0.7"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-12.0.7.tgz#33ebf229b81b06e583ab5ae7613cffe1ca2103fc"
|
||||
@ -6029,6 +6100,16 @@ randomfill@^1.0.3:
|
||||
randombytes "^2.0.5"
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
raw-body@2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
|
||||
integrity sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=
|
||||
dependencies:
|
||||
bytes "3.0.0"
|
||||
http-errors "1.6.2"
|
||||
iconv-lite "0.4.19"
|
||||
unpipe "1.0.0"
|
||||
|
||||
raw-body@2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c"
|
||||
@ -6347,7 +6428,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
hash-base "^3.0.0"
|
||||
inherits "^2.0.1"
|
||||
|
||||
rollup-plugin-dts@^4.0.1:
|
||||
rollup-plugin-dts@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-dts/-/rollup-plugin-dts-4.1.0.tgz#63b1e7de3970bb6d50877e60df2150a3892bc49c"
|
||||
integrity sha512-rriXIm3jdUiYeiAAd1Fv+x2AxK6Kq6IybB2Z/IdoAW95fb4uRUurYsEYKa8L1seedezDeJhy8cfo8FEL9aZzqg==
|
||||
@ -6397,7 +6478,7 @@ rollup-pluginutils@^2.8.2:
|
||||
dependencies:
|
||||
estree-walker "^0.6.1"
|
||||
|
||||
rollup@^2.61.1:
|
||||
rollup@^2.62.0:
|
||||
version "2.62.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.62.0.tgz#9e640b419fc5b9e0241844f6d55258bd79986ecc"
|
||||
integrity sha512-cJEQq2gwB0GWMD3rYImefQTSjrPYaC6s4J9pYqnstVLJ1CHa/aZNVkD4Epuvg4iLeMA4KRiq7UM7awKK6j7jcw==
|
||||
@ -6509,6 +6590,11 @@ setimmediate@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
|
||||
|
||||
setprototypeof@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
|
||||
integrity sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=
|
||||
|
||||
setprototypeof@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
|
||||
@ -6778,7 +6864,7 @@ stacktrace-parser@0.1.10:
|
||||
dependencies:
|
||||
type-fest "^0.7.1"
|
||||
|
||||
"statuses@>= 1.5.0 < 2":
|
||||
"statuses@>= 1.3.1 < 2", "statuses@>= 1.5.0 < 2":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
||||
@ -7028,10 +7114,10 @@ table@^6.0.9:
|
||||
string-width "^4.2.3"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
tailwindcss@^3.0.7:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.7.tgz#15936881f042a7eb8d6f2b6a454bac9f51181bbd"
|
||||
integrity sha512-rZdKNHtC64jcQncLoWOuCzj4lQDTAgLtgK3WmQS88tTdpHh9OwLqULTQxI3tw9AMJsqSpCKlmcjW/8CSnni6zQ==
|
||||
tailwindcss@^3.0.8:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.8.tgz#6c751c3d2ee8e1fa18b108303b73f44a5e868992"
|
||||
integrity sha512-Yww1eRYO1AxITJmW/KduZPxNvYdHuedeKwPju9Oakp7MdiixRi5xkpLhirsc81QCxHL0eoce6qKmxXwYGt4Cjw==
|
||||
dependencies:
|
||||
arg "^5.0.1"
|
||||
chalk "^4.1.2"
|
||||
@ -7055,6 +7141,11 @@ tailwindcss@^3.0.7:
|
||||
resolve "^1.20.0"
|
||||
tmp "^0.2.1"
|
||||
|
||||
tapable@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
|
||||
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
|
||||
|
||||
terser@^5.0.0:
|
||||
version "5.10.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc"
|
||||
|
Reference in New Issue
Block a user