🚸 New dedicated onboarding page
This commit is contained in:
@ -10,3 +10,4 @@ NEXT_PUBLIC_E2E_TEST=
|
||||
NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME=
|
||||
NEXT_PUBLIC_UNSPLASH_APP_NAME=
|
||||
NEXT_PUBLIC_UNSPLASH_ACCESS_KEY=
|
||||
NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID=
|
||||
|
@ -628,3 +628,10 @@ export const BookIcon = (props: IconProps) => (
|
||||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"></path>
|
||||
</Icon>
|
||||
)
|
||||
|
||||
export const ChevronLastIcon = (props: IconProps) => (
|
||||
<Icon viewBox="0 0 24 24" {...featherIconsBaseProps} {...props}>
|
||||
<path d="m7 18 6-6-6-6" />
|
||||
<path d="M17 6v12" />
|
||||
</Icon>
|
||||
)
|
||||
|
183
apps/builder/src/features/auth/components/OnboardingPage.tsx
Normal file
183
apps/builder/src/features/auth/components/OnboardingPage.tsx
Normal file
@ -0,0 +1,183 @@
|
||||
import { ChevronLastIcon } from '@/components/icons'
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
HStack,
|
||||
StackProps,
|
||||
VStack,
|
||||
chakra,
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react'
|
||||
import { Standard } from '@typebot.io/nextjs'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import confetti from 'canvas-confetti'
|
||||
import { useUser } from '@/features/account/hooks/useUser'
|
||||
import { env, isEmpty } from '@typebot.io/lib'
|
||||
import { useI18n } from '@/locales'
|
||||
|
||||
const totalSteps = 5
|
||||
|
||||
export const OnboardingPage = () => {
|
||||
const t = useI18n()
|
||||
const { push, replace } = useRouter()
|
||||
const confettiCanvaContainer = useRef<HTMLCanvasElement | null>(null)
|
||||
const confettiCanon = useRef<confetti.CreateTypes>()
|
||||
const { user, updateUser } = useUser()
|
||||
const [currentStep, setCurrentStep] = useState<number>(user?.name ? 2 : 1)
|
||||
|
||||
const isNewUser =
|
||||
user &&
|
||||
new Date(user.createdAt as unknown as string).toDateString() ===
|
||||
new Date().toDateString()
|
||||
|
||||
useEffect(() => {
|
||||
initConfettis()
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!user?.createdAt) return
|
||||
if (isNewUser === false || isEmpty(env('ONBOARDING_TYPEBOT_ID')))
|
||||
replace('/typebots')
|
||||
}, [isNewUser, replace, user?.createdAt])
|
||||
|
||||
const initConfettis = () => {
|
||||
if (!confettiCanvaContainer.current || confettiCanon.current) return
|
||||
confettiCanon.current = confetti.create(confettiCanvaContainer.current, {
|
||||
resize: true,
|
||||
useWorker: true,
|
||||
})
|
||||
}
|
||||
|
||||
const updateUserInfo = async (answer: {
|
||||
message: string
|
||||
blockId: string
|
||||
}) => {
|
||||
const isOtherItem = [
|
||||
'cl126pv7n001o2e6dajltc4qz',
|
||||
'saw904bfzgspmt0l24achtiy',
|
||||
].includes(answer.blockId)
|
||||
if (isOtherItem) return
|
||||
const isName = answer.blockId === 'cl126820m000g2e6dfleq78bt'
|
||||
const isCompany = answer.blockId === 'cl126jioz000v2e6dwrk1f2cb'
|
||||
const isCategories = answer.blockId === 'cl126lb8v00142e6duv5qe08l'
|
||||
if (isName) updateUser({ name: answer.message })
|
||||
if (isCategories) {
|
||||
const onboardingCategories = answer.message.split(', ')
|
||||
updateUser({ onboardingCategories })
|
||||
}
|
||||
if (isCompany) {
|
||||
updateUser({ company: answer.message })
|
||||
if (confettiCanon.current) shootConfettis(confettiCanon.current)
|
||||
}
|
||||
setCurrentStep((prev) => prev + 1)
|
||||
}
|
||||
|
||||
if (!isNewUser) return null
|
||||
return (
|
||||
<VStack h="100vh" flexDir="column" justifyContent="center" spacing="10">
|
||||
<Button
|
||||
rightIcon={<ChevronLastIcon />}
|
||||
pos="fixed"
|
||||
top="5"
|
||||
right="5"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => push('/typebots')}
|
||||
>
|
||||
{t('skip')}
|
||||
</Button>
|
||||
<Dots currentStep={currentStep} pos="fixed" top="9" />
|
||||
<Flex w="full" maxW="800px" h="full" maxH="70vh" rounded="lg">
|
||||
<Standard
|
||||
typebot={env('ONBOARDING_TYPEBOT_ID')}
|
||||
style={{ borderRadius: '1rem' }}
|
||||
prefilledVariables={{ Name: user?.name, Email: user?.email }}
|
||||
onEnd={() => {
|
||||
setTimeout(() => {
|
||||
push('/typebots/create', { query: { isFirstBot: true } })
|
||||
}, 2000)
|
||||
}}
|
||||
onAnswer={updateUserInfo}
|
||||
/>
|
||||
</Flex>
|
||||
<chakra.canvas
|
||||
ref={confettiCanvaContainer}
|
||||
pos="fixed"
|
||||
top="0"
|
||||
left="0"
|
||||
w="full"
|
||||
h="full"
|
||||
zIndex={9999}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
||||
const Dots = ({
|
||||
currentStep,
|
||||
...props
|
||||
}: { currentStep: number } & StackProps) => {
|
||||
const highlightedBgColor = useColorModeValue('gray.500', 'gray.100')
|
||||
const baseBgColor = useColorModeValue('gray.200', 'gray.700')
|
||||
return (
|
||||
<HStack spacing="10" {...props}>
|
||||
{Array.from({ length: totalSteps }).map((_, index) => (
|
||||
<chakra.div
|
||||
key={index}
|
||||
boxSize={'2'}
|
||||
bgColor={currentStep === index + 1 ? highlightedBgColor : baseBgColor}
|
||||
rounded="full"
|
||||
transition="background-color 0.2s ease"
|
||||
/>
|
||||
))}
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
|
||||
const shootConfettis = (confettiCanon: confetti.CreateTypes) => {
|
||||
const count = 200
|
||||
const defaults = {
|
||||
origin: { y: 0.7 },
|
||||
}
|
||||
|
||||
const fire = (
|
||||
particleRatio: number,
|
||||
opts: {
|
||||
spread: number
|
||||
startVelocity?: number
|
||||
decay?: number
|
||||
scalar?: number
|
||||
}
|
||||
) => {
|
||||
confettiCanon(
|
||||
Object.assign({}, defaults, opts, {
|
||||
particleCount: Math.floor(count * particleRatio),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fire(0.25, {
|
||||
spread: 26,
|
||||
startVelocity: 55,
|
||||
})
|
||||
fire(0.2, {
|
||||
spread: 60,
|
||||
})
|
||||
fire(0.35, {
|
||||
spread: 100,
|
||||
decay: 0.91,
|
||||
scalar: 0.8,
|
||||
})
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 25,
|
||||
decay: 0.92,
|
||||
scalar: 1.2,
|
||||
})
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 45,
|
||||
})
|
||||
}
|
@ -48,6 +48,22 @@ export const SignInPage = ({ type }: Props) => {
|
||||
</Text>
|
||||
)}
|
||||
<SignInForm defaultEmail={query.g?.toString()} />
|
||||
{type === 'signup' ? (
|
||||
<Text fontSize="sm" maxW="400px" textAlign="center">
|
||||
{scopedT('register.aggreeToTerms', {
|
||||
termsOfService: (
|
||||
<TextLink href={'https://typebot.io/terms-of-service'}>
|
||||
{scopedT('register.termsOfService')}
|
||||
</TextLink>
|
||||
),
|
||||
privacyPolicy: (
|
||||
<TextLink href={'https://typebot.io/privacy-policies'}>
|
||||
{scopedT('register.privacyPolicy')}
|
||||
</TextLink>
|
||||
),
|
||||
})}
|
||||
</Text>
|
||||
) : null}
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ const parseVideoUrl = (
|
||||
return { type: VideoBubbleContentType.VIMEO, url, id }
|
||||
}
|
||||
if (youtubeRegex.test(url)) {
|
||||
console.log(url.match(youtubeRegex)?.at(2))
|
||||
const id = url.match(youtubeRegex)?.at(2)
|
||||
if (!id) return { type: VideoBubbleContentType.URL, url }
|
||||
return { type: VideoBubbleContentType.YOUTUBE, url, id }
|
||||
|
@ -1,142 +0,0 @@
|
||||
import { chakra, useColorModeValue } from '@chakra-ui/react'
|
||||
import { Popup } from '@typebot.io/nextjs'
|
||||
import { useUser } from '@/features/account/hooks/useUser'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import confetti from 'canvas-confetti'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
type Props = { totalTypebots: number }
|
||||
|
||||
export const OnboardingModal = ({ totalTypebots }: Props) => {
|
||||
const { push } = useRouter()
|
||||
const backgroundColor = useColorModeValue('white', '#171923')
|
||||
const { user, updateUser } = useUser()
|
||||
const confettiCanvaContainer = useRef<HTMLCanvasElement | null>(null)
|
||||
const confettiCanon = useRef<confetti.CreateTypes>()
|
||||
const [chosenCategories, setChosenCategories] = useState<string[]>([])
|
||||
|
||||
const isNewUser =
|
||||
user &&
|
||||
new Date(user?.createdAt as unknown as string).toDateString() ===
|
||||
new Date().toDateString() &&
|
||||
totalTypebots === 0
|
||||
|
||||
useEffect(() => {
|
||||
initConfettis()
|
||||
}, [])
|
||||
|
||||
const initConfettis = () => {
|
||||
if (!confettiCanvaContainer.current || confettiCanon.current) return
|
||||
confettiCanon.current = confetti.create(confettiCanvaContainer.current, {
|
||||
resize: true,
|
||||
useWorker: true,
|
||||
})
|
||||
}
|
||||
|
||||
const handleBotEnd = () => {
|
||||
setTimeout(() => {
|
||||
push('/typebots/create', { query: { isFirstBot: true } })
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
const handleNewAnswer = async (answer: {
|
||||
message: string
|
||||
blockId: string
|
||||
}) => {
|
||||
const isName = answer.blockId === 'cl126820m000g2e6dfleq78bt'
|
||||
const isCompany = answer.blockId === 'cl126jioz000v2e6dwrk1f2cb'
|
||||
const isCategories = answer.blockId === 'cl126lb8v00142e6duv5qe08l'
|
||||
const isOtherCategories = answer.blockId === 'cl126pv7n001o2e6dajltc4qz'
|
||||
if (isName) updateUser({ name: answer.message })
|
||||
if (isCompany) {
|
||||
updateUser({ company: answer.message })
|
||||
if (confettiCanon.current) shootConfettis(confettiCanon.current)
|
||||
}
|
||||
if (isCategories) {
|
||||
const onboardingCategories = answer.message.split(', ')
|
||||
updateUser({ onboardingCategories })
|
||||
setChosenCategories(onboardingCategories)
|
||||
}
|
||||
if (isOtherCategories)
|
||||
updateUser({
|
||||
onboardingCategories: [...chosenCategories, answer.message],
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<chakra.canvas
|
||||
ref={confettiCanvaContainer}
|
||||
pos="fixed"
|
||||
top="0"
|
||||
left="0"
|
||||
w="full"
|
||||
h="full"
|
||||
zIndex={9999}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
{user?.email && (
|
||||
<Popup
|
||||
typebot="onboarding-typebot"
|
||||
prefilledVariables={{
|
||||
Name: user.name?.split(' ')[0] ?? undefined,
|
||||
Email: user.email ?? undefined,
|
||||
}}
|
||||
theme={{
|
||||
backgroundColor,
|
||||
zIndex: 100,
|
||||
}}
|
||||
defaultOpen={isNewUser}
|
||||
onAnswer={handleNewAnswer}
|
||||
onEnd={handleBotEnd}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const shootConfettis = (confettiCanon: confetti.CreateTypes) => {
|
||||
const count = 200
|
||||
const defaults = {
|
||||
origin: { y: 0.7 },
|
||||
}
|
||||
|
||||
const fire = (
|
||||
particleRatio: number,
|
||||
opts: {
|
||||
spread: number
|
||||
startVelocity?: number
|
||||
decay?: number
|
||||
scalar?: number
|
||||
}
|
||||
) => {
|
||||
confettiCanon(
|
||||
Object.assign({}, defaults, opts, {
|
||||
particleCount: Math.floor(count * particleRatio),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fire(0.25, {
|
||||
spread: 26,
|
||||
startVelocity: 55,
|
||||
})
|
||||
fire(0.2, {
|
||||
spread: 60,
|
||||
})
|
||||
fire(0.35, {
|
||||
spread: 100,
|
||||
decay: 0.91,
|
||||
scalar: 0.8,
|
||||
})
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 25,
|
||||
decay: 0.92,
|
||||
scalar: 1.2,
|
||||
})
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 45,
|
||||
})
|
||||
}
|
@ -12,7 +12,6 @@ import {
|
||||
import { useTypebotDnd } from '../TypebotDndProvider'
|
||||
import React, { useState } from 'react'
|
||||
import { BackButton } from './BackButton'
|
||||
import { OnboardingModal } from '../../dashboard/components/OnboardingModal'
|
||||
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { useFolders } from '../hooks/useFolders'
|
||||
@ -26,7 +25,6 @@ import { TypebotCardOverlay } from './TypebotButtonOverlay'
|
||||
import { useI18n } from '@/locales'
|
||||
import { useTypebots } from '@/features/dashboard/hooks/useTypebots'
|
||||
import { TypebotInDashboard } from '@/features/dashboard/types'
|
||||
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
|
||||
|
||||
type Props = { folder: DashboardFolder | null }
|
||||
|
||||
@ -161,9 +159,6 @@ export const FolderContent = ({ folder }: Props) => {
|
||||
|
||||
return (
|
||||
<Flex w="full" flex="1" justify="center">
|
||||
{typebots && isCloudProdInstance && (
|
||||
<OnboardingModal totalTypebots={typebots.length} />
|
||||
)}
|
||||
<Stack w="1000px" spacing={6}>
|
||||
<Skeleton isLoaded={folder?.name !== undefined}>
|
||||
<Heading as="h1">{folder?.name}</Heading>
|
||||
|
@ -12,6 +12,7 @@ export default {
|
||||
downgrade: 'Downgrade',
|
||||
remove: 'Entfernen',
|
||||
pending: 'Ausstehend',
|
||||
skip: 'Überspringen',
|
||||
'folders.createFolderButton.label': 'Ordner erstellen',
|
||||
'folders.createTypebotButton.label': 'Typebot erstellen',
|
||||
'folders.folderButton.deleteConfirmationMessage':
|
||||
@ -77,6 +78,10 @@ export default {
|
||||
'auth.register.alreadyHaveAccountLabel.preLink':
|
||||
'Bereits ein Konto vorhanden?',
|
||||
'auth.register.alreadyHaveAccountLabel.link': 'Anmelden',
|
||||
'auth.register.aggreeToTerms':
|
||||
'Durch die Registrierung stimmst du unseren {termsOfService} und {privacyPolicy} zu.',
|
||||
'auth.register.termsOfService': 'Nutzungsbedingungen',
|
||||
'auth.register.privacyPolicy': 'Datenschutzrichtlinie',
|
||||
'auth.error.default': 'Versuche, dich mit einem anderen Konto anzumelden.',
|
||||
'auth.error.email':
|
||||
'E-Mail nicht gefunden. Versuche, dich mit einem anderen Anbieter anzumelden.',
|
||||
|
@ -12,6 +12,7 @@ export default {
|
||||
downgrade: 'Downgrade',
|
||||
remove: 'Remove',
|
||||
pending: 'Pending',
|
||||
skip: 'Skip',
|
||||
'folders.createFolderButton.label': 'Create a folder',
|
||||
'folders.createTypebotButton.label': 'Create a typebot',
|
||||
'folders.folderButton.deleteConfirmationMessage':
|
||||
@ -75,6 +76,10 @@ export default {
|
||||
'auth.register.heading': 'Create an account',
|
||||
'auth.register.alreadyHaveAccountLabel.preLink': 'Already have an account?',
|
||||
'auth.register.alreadyHaveAccountLabel.link': 'Sign in',
|
||||
'auth.register.aggreeToTerms':
|
||||
'By signing up, you agree to our {termsOfService} and {privacyPolicy}.',
|
||||
'auth.register.termsOfService': 'terms of service',
|
||||
'auth.register.privacyPolicy': 'privacy policy',
|
||||
'auth.error.default': 'Try signing with a different account.',
|
||||
'auth.error.email': 'Email not found. Try signing with a different provider.',
|
||||
'auth.error.oauthNotLinked':
|
||||
|
@ -12,6 +12,7 @@ export default {
|
||||
downgrade: 'Downgrade',
|
||||
remove: 'Retirer',
|
||||
pending: 'En attente',
|
||||
skip: 'Passer',
|
||||
'folders.createFolderButton.label': 'Créer un dossier',
|
||||
'folders.createTypebotButton.label': 'Créer un typebot',
|
||||
'folders.folderButton.deleteConfirmationMessage':
|
||||
@ -75,6 +76,10 @@ export default {
|
||||
'auth.register.heading': 'Créer un compte',
|
||||
'auth.register.alreadyHaveAccountLabel.preLink': 'Tu as déjà un compte?',
|
||||
'auth.register.alreadyHaveAccountLabel.link': 'Se connecter',
|
||||
'auth.register.aggreeToTerms':
|
||||
'En vous inscrivant, vous acceptez nos {termsOfService} et {privacyPolicy}.',
|
||||
'auth.register.termsOfService': "conditions d'utilisation",
|
||||
'auth.register.privacyPolicy': 'politique de confidentialité',
|
||||
'auth.error.default': 'Essaye de te connecter avec un compte différent.',
|
||||
'auth.error.email':
|
||||
'Email non trouvé. Essaye de te connecter avec un fournisseur différent.',
|
||||
|
@ -12,6 +12,7 @@ export default {
|
||||
downgrade: 'Downgrade',
|
||||
remove: 'Remover',
|
||||
pending: 'Pendente',
|
||||
skip: 'Pular',
|
||||
'folders.createFolderButton.label': 'Criar uma pasta',
|
||||
'folders.createTypebotButton.label': 'Criar um typebot',
|
||||
'folders.folderButton.deleteConfirmationMessage':
|
||||
@ -76,6 +77,10 @@ export default {
|
||||
'auth.register.heading': 'Criar uma conta',
|
||||
'auth.register.alreadyHaveAccountLabel.preLink': 'Já tem uma conta?',
|
||||
'auth.register.alreadyHaveAccountLabel.link': 'Entrar',
|
||||
'auth.register.aggreeToTerms':
|
||||
'Ao se cadastrar, você concorda com nossos {termsOfService} e {privacyPolicy}.',
|
||||
'auth.register.termsOfService': 'termos de serviço',
|
||||
'auth.register.privacyPolicy': 'política de privacidade',
|
||||
'auth.error.default': 'Tente entrar com uma conta diferente.',
|
||||
'auth.error.email':
|
||||
'E-mail não encontrado. Tente entrar com um provedor diferente.',
|
||||
|
@ -121,8 +121,8 @@ if (isNotEmpty(process.env.CUSTOM_OAUTH_WELL_KNOWN_URL)) {
|
||||
type: 'oauth',
|
||||
authorization: {
|
||||
params: {
|
||||
scope: process.env.CUSTOM_OAUTH_SCOPE ?? 'openid profile email'
|
||||
}
|
||||
scope: process.env.CUSTOM_OAUTH_SCOPE ?? 'openid profile email',
|
||||
},
|
||||
},
|
||||
clientId: process.env.CUSTOM_OAUTH_CLIENT_ID,
|
||||
clientSecret: process.env.CUSTOM_OAUTH_CLIENT_SECRET,
|
||||
@ -156,6 +156,9 @@ export const authOptions: AuthOptions = {
|
||||
},
|
||||
pages: {
|
||||
signIn: '/signin',
|
||||
newUser: process.env.NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID
|
||||
? '/onboarding'
|
||||
: undefined,
|
||||
},
|
||||
callbacks: {
|
||||
session: async ({ session, user }) => {
|
||||
|
5
apps/builder/src/pages/onboarding.tsx
Normal file
5
apps/builder/src/pages/onboarding.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { OnboardingPage } from '@/features/auth/components/OnboardingPage'
|
||||
|
||||
export default function Page() {
|
||||
return <OnboardingPage />
|
||||
}
|
@ -21,6 +21,7 @@ Parameters marked with <Asterix/> are required.
|
||||
| NEXTAUTH_URL_INTERNAL | | The internal builder base URL. You have to set it only when `NEXTAUTH_URL` can't be reached by your builder container / server. For a docker deployment, you should set it to `http://localhost:3000`. |
|
||||
| DEFAULT_WORKSPACE_PLAN | FREE | Default workspace plan on user creation or when a user creates a new workspace. Possible values are `FREE`, `STARTER`, `PRO`, `LIFETIME`, `UNLIMITED`. The default plan for admin user is `UNLIMITED` |
|
||||
| DISABLE_SIGNUP | false | Disable new user sign ups. Invited users are still able to sign up. |
|
||||
| NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID | | Typebot ID used for the onboarding. Onboarding page is skipped if not provided. |
|
||||
|
||||
## Email (Auth, notifications)
|
||||
|
||||
|
@ -14,12 +14,13 @@ const PrivacyPolicies = () => {
|
||||
<Heading>1. Terms</Heading>
|
||||
|
||||
<p>
|
||||
By accessing this Website, accessible from https://www.typebot.io, you
|
||||
are agreeing to be bound by these Website Terms and Conditions of Use
|
||||
and agree that you are responsible for the agreement with any
|
||||
applicable local laws. If you disagree with any of these terms, you
|
||||
are prohibited from accessing this site. The materials contained in
|
||||
this Website are protected by copyright and trade mark law.
|
||||
By accessing this Website, accessible from https://typebot.io and
|
||||
https://app.typebot.io, you are agreeing to be bound by these Website
|
||||
Terms and Conditions of Use and agree that you are responsible for the
|
||||
agreement with any applicable local laws. If you disagree with any of
|
||||
these terms, you are prohibited from accessing this site. The
|
||||
materials contained in this Website are protected by copyright and
|
||||
trade mark law.
|
||||
</p>
|
||||
|
||||
<Heading>2. Use License</Heading>
|
||||
@ -32,15 +33,6 @@ const PrivacyPolicies = () => {
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>modify or copy the materials;</li>
|
||||
<li>
|
||||
use the materials for any commercial purpose or for any public
|
||||
display;
|
||||
</li>
|
||||
<li>
|
||||
attempt to reverse engineer any software contained on Typebot's
|
||||
Website;
|
||||
</li>
|
||||
<li>
|
||||
remove any copyright or other proprietary notations from the
|
||||
materials; or
|
||||
@ -70,12 +62,12 @@ const PrivacyPolicies = () => {
|
||||
<Heading>3. Disclaimer</Heading>
|
||||
|
||||
<p>
|
||||
All the materials on Typebot’s Website are provided "as is".
|
||||
Typebot makes no warranties, may it be expressed or implied, therefore
|
||||
negates all other warranties. Furthermore, Typebot does not make any
|
||||
representations concerning the accuracy or reliability of the use of
|
||||
the materials on its Website or otherwise relating to such materials
|
||||
or any sites linked to this Website.
|
||||
All the materials on Typebot's Website are provided "as
|
||||
is". Typebot makes no warranties, may it be expressed or implied,
|
||||
therefore negates all other warranties. Furthermore, Typebot does not
|
||||
make any representations concerning the accuracy or reliability of the
|
||||
use of the materials on its Website or otherwise relating to such
|
||||
materials or any sites linked to this Website.
|
||||
</p>
|
||||
|
||||
<Heading>4. Limitations</Heading>
|
||||
@ -83,22 +75,22 @@ const PrivacyPolicies = () => {
|
||||
<p>
|
||||
Typebot or its suppliers will not be hold accountable for any damages
|
||||
that will arise with the use or inability to use the materials on
|
||||
Typebot’s Website, even if Typebot or an authorize representative of
|
||||
this Website has been notified, orally or written, of the possibility
|
||||
of such damage. Some jurisdiction does not allow limitations on
|
||||
implied warranties or limitations of liability for incidental damages,
|
||||
these limitations may not apply to you.
|
||||
Typebot's Website, even if Typebot or an authorize representative
|
||||
of this Website has been notified, orally or written, of the
|
||||
possibility of such damage. Some jurisdiction does not allow
|
||||
limitations on implied warranties or limitations of liability for
|
||||
incidental damages, these limitations may not apply to you.
|
||||
</p>
|
||||
|
||||
<Heading>5. Revisions and Errata</Heading>
|
||||
|
||||
<p>
|
||||
The materials appearing on Typebot’s Website may include technical,
|
||||
typographical, or photographic errors. Typebot will not promise that
|
||||
any of the materials in this Website are accurate, complete, or
|
||||
current. Typebot may change the materials contained on its Website at
|
||||
any time without notice. Typebot does not make any commitment to
|
||||
update the materials.
|
||||
The materials appearing on Typebot's Website may include
|
||||
technical, typographical, or photographic errors. Typebot will not
|
||||
promise that any of the materials in this Website are accurate,
|
||||
complete, or current. Typebot may change the materials contained on
|
||||
its Website at any time without notice. Typebot does not make any
|
||||
commitment to update the materials.
|
||||
</p>
|
||||
|
||||
<Heading>6. Links</Heading>
|
||||
@ -107,7 +99,7 @@ const PrivacyPolicies = () => {
|
||||
Typebot has not reviewed all of the sites linked to its Website and is
|
||||
not responsible for the contents of any such linked site. The presence
|
||||
of any link does not imply endorsement by Typebot of the site. The use
|
||||
of any linked website is at the user’s own risk.
|
||||
of any linked website is at the user's own risk.
|
||||
</p>
|
||||
|
||||
<Heading>7. Site Terms of Use Modifications</Heading>
|
||||
|
Reference in New Issue
Block a user