2
0

♻️ Normalize data

This commit is contained in:
Baptiste Arnaud
2022-01-06 09:40:56 +01:00
parent 6c1e0fd345
commit 9fa4c7dffa
114 changed files with 1545 additions and 1632 deletions

View File

@ -8,26 +8,28 @@
"dependencies": {
"db": "*",
"fast-equals": "^2.0.4",
"models": "*",
"react-frame-component": "^5.2.1",
"react-scroll": "^1.8.4",
"react-transition-group": "^4.4.2"
"react-transition-group": "^4.4.2",
"utils": "*"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.1.1",
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-typescript": "^8.3.0",
"@types/react": "^17.0.38",
"@types/react-scroll": "^1.8.3",
"@types/react-transition-group": "^4.4.4",
"autoprefixer": "^10.4.0",
"autoprefixer": "^10.4.1",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.5",
"rollup": "^2.62.0",
"rollup": "^2.63.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.8",
"tailwindcss": "^3.0.11",
"typescript": "^4.5.4"
},
"peerDependencies": {

View File

@ -1,21 +1,21 @@
import React, { useEffect, useState } from 'react'
import { Block, Step } from '../../models'
import { animateScroll as scroll } from 'react-scroll'
import { TransitionGroup, CSSTransition } from 'react-transition-group'
import { ChatStep } from './ChatStep'
import { AvatarSideContainer } from './AvatarSideContainer'
import { HostAvatarsContext } from '../../contexts/HostAvatarsContext'
import { Step, Table } from 'models'
type ChatBlockProps = {
block: Block
steps: Table<Step>
onBlockEnd: (nextBlockId?: string) => void
}
export const ChatBlock = ({ block, onBlockEnd }: ChatBlockProps) => {
export const ChatBlock = ({ steps, onBlockEnd }: ChatBlockProps) => {
const [displayedSteps, setDisplayedSteps] = useState<Step[]>([])
useEffect(() => {
setDisplayedSteps([block.steps[0]])
setDisplayedSteps([steps.byId[steps.allIds[0]]])
}, [])
useEffect(() => {
@ -33,10 +33,10 @@ export const ChatBlock = ({ block, onBlockEnd }: ChatBlockProps) => {
const currentStep = [...displayedSteps].pop()
if (
currentStep?.target?.blockId ||
displayedSteps.length === block.steps.length
displayedSteps.length === steps.allIds.length
)
return onBlockEnd(currentStep?.target?.blockId)
const nextStep = block.steps[displayedSteps.length]
const nextStep = steps.byId[displayedSteps.length]
if (nextStep) setDisplayedSteps([...displayedSteps, nextStep])
}

View File

@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react'
import { useAnswers } from '../../../contexts/AnswersContext'
import { useHostAvatars } from '../../../contexts/HostAvatarsContext'
import { Step } from '../../../models'
import { Step } from 'models'
import { isTextInputStep, isTextStep } from '../../../services/utils'
import { GuestBubble } from './bubbles/GuestBubble'
import { HostMessageBubble } from './bubbles/HostMessageBubble'

View File

@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from 'react'
import { useHostAvatars } from '../../../../contexts/HostAvatarsContext'
import { useTypebot } from '../../../../contexts/TypebotContext'
import { StepType, TextStep } from '../../../../models'
import { StepType, TextStep } from 'models'
import { computeTypingTimeout } from '../../../../services/chat'
import { TypingContent } from './TypingContent'

View File

@ -1,12 +1,12 @@
import React, { useEffect, useRef, useState } from 'react'
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'
import { Answer, Block, PublicTypebot } from 'models'
import { filterTable } from 'utils'
type Props = {
typebot: PublicTypebot
@ -28,14 +28,17 @@ export const ConversationContainer = ({
const displayNextBlock = (blockId?: string) => {
if (!blockId) return onCompleted()
const nextBlock = typebot.blocks.find((b) => b.id === blockId)
const nextBlock = typebot.blocks.byId[blockId]
if (!nextBlock) return onCompleted()
onNewBlockVisible(blockId)
setDisplayedBlocks([...displayedBlocks, nextBlock])
}
useEffect(() => {
const firstBlockId = typebot.startBlock.steps[0].target?.blockId
const blocks = typebot.blocks
const firstBlockId =
typebot.steps.byId[blocks.byId[blocks.allIds[0]].stepIds[0]].target
?.blockId
if (firstBlockId) displayNextBlock(firstBlockId)
}, [])
@ -58,7 +61,7 @@ export const ConversationContainer = ({
{displayedBlocks.map((block, idx) => (
<ChatBlock
key={block.id + idx}
block={block}
steps={filterTable(block.stepIds, typebot.steps)}
onBlockEnd={displayNextBlock}
/>
))}

View File

@ -1,11 +1,11 @@
import React, { useMemo } from 'react'
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 { AnswersContext } from '../contexts/AnswersContext'
import { Answer, BackgroundType, PublicTypebot } from 'models'
export type TypebotViewerProps = {
typebot: PublicTypebot

View File

@ -1,4 +1,4 @@
import { Answer } from '../models'
import { Answer } from 'models'
import React, { createContext, ReactNode, useContext, useState } from 'react'
const answersContext = createContext<{

View File

@ -1,5 +1,5 @@
import { PublicTypebot } from 'models'
import React, { createContext, ReactNode, useContext } from 'react'
import { PublicTypebot } from '../models/publicTypebot'
const typebotContext = createContext<{
typebot: PublicTypebot

View File

@ -1,3 +1 @@
export * from './components/TypebotViewer'
export * from './models'
export { parseNewTypebot } from './services/utils'

View File

@ -1,9 +0,0 @@
import { Answer as AnswerFromPrisma } from 'db'
export type Answer = Omit<AnswerFromPrisma, 'resultId' | 'createdAt'>
export type Stats = {
totalViews: number
totalStarts: number
completionRate: number
}

View File

@ -1,4 +0,0 @@
export * from './typebot'
export * from './publicTypebot'
export * from './result'
export * from './answer'

View File

@ -1,12 +0,0 @@
import { PublicTypebot as PublicTypebotFromPrisma } from 'db'
import { Block, Settings, StartBlock, Theme } from '.'
export type PublicTypebot = Omit<
PublicTypebotFromPrisma,
'blocks' | 'startBlock' | 'theme' | 'settings'
> & {
blocks: Block[]
startBlock: StartBlock
theme: Theme
settings: Settings
}

View File

@ -1,3 +0,0 @@
import { Result as ResultFromPrisma } from 'db'
export type Result = Omit<ResultFromPrisma, 'createdAt'> & { createdAt: string }

View File

@ -1,87 +0,0 @@
import { Typebot as TypebotFromPrisma } from 'db'
export type Typebot = Omit<
TypebotFromPrisma,
'blocks' | 'startBlock' | 'theme' | 'settings'
> & {
blocks: Block[]
startBlock: StartBlock
theme: Theme
settings: Settings
}
export type StartBlock = {
id: `start-block`
graphCoordinates: {
x: number
y: number
}
title: string
steps: [StartStep]
}
export type StartStep = {
id: 'start-step'
blockId: 'start-block'
target?: Target
type: StepType.START
label: string
}
export type Block = {
id: string
title: string
steps: Step[]
graphCoordinates: {
x: number
y: number
}
}
export enum StepType {
START = 'start',
TEXT = 'text',
TEXT_INPUT = 'text input',
}
export type Target = { blockId: string; stepId?: string }
export type Step = BubbleStep | InputStep
export type BubbleStep = TextStep
export type InputStep = TextInputStep
export type StepBase = { id: string; blockId: string; target?: Target }
export type TextStep = StepBase & {
type: StepType.TEXT
content: { html: string; richText: unknown[]; plainText: string }
}
export type TextInputStep = StepBase & {
type: StepType.TEXT_INPUT
}
export type Theme = {
general: {
font: string
background: Background
}
}
export enum BackgroundType {
COLOR = 'Color',
IMAGE = 'Image',
NONE = 'None',
}
export type Background = {
type: BackgroundType
content: string
}
export type Settings = {
typingEmulation: TypingEmulationSettings
}
export type TypingEmulationSettings = {
enabled: boolean
speed: number
maxDelay: number
}

View File

@ -1,4 +1,4 @@
import { TypingEmulationSettings } from '../models'
import { TypingEmulationSettings } from 'models'
export const computeTypingTimeout = (
bubbleContent: string,

View File

@ -1,4 +1,4 @@
import { BackgroundType, Theme } from '../models'
import { BackgroundType, Theme } from 'models'
const cssVariableNames = {
container: {

View File

@ -1,55 +1,7 @@
import { Prisma } from 'db'
import {
Step,
TextStep,
StepType,
TextInputStep,
BackgroundType,
Settings,
StartBlock,
Theme,
} from '../models'
import { Step, TextStep, StepType, TextInputStep } from 'models'
export const isTextStep = (step: Step): step is TextStep =>
step.type === StepType.TEXT
export const isTextInputStep = (step: Step): step is TextInputStep =>
step.type === StepType.TEXT_INPUT
export const parseNewTypebot = ({
ownerId,
folderId,
name,
}: {
ownerId: string
folderId: string | null
name: string
}): Prisma.TypebotUncheckedCreateInput => {
const startBlock: StartBlock = {
id: 'start-block',
title: 'Start',
graphCoordinates: { x: 0, y: 0 },
steps: [
{
id: 'start-step',
blockId: 'start-block',
label: 'Form starts here',
type: StepType.START,
},
],
}
const theme: Theme = {
general: {
font: 'Open Sans',
background: { type: BackgroundType.NONE, content: '#ffffff' },
},
}
const settings: Settings = {
typingEmulation: {
enabled: true,
speed: 300,
maxDelay: 1.5,
},
}
return { folderId, name, ownerId, startBlock, theme, settings }
}