🚸 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_VERCEL_VIEWER_PROJECT_NAME=
|
||||||
NEXT_PUBLIC_UNSPLASH_APP_NAME=
|
NEXT_PUBLIC_UNSPLASH_APP_NAME=
|
||||||
NEXT_PUBLIC_UNSPLASH_ACCESS_KEY=
|
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>
|
<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>
|
</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>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<SignInForm defaultEmail={query.g?.toString()} />
|
<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>
|
</VStack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ const parseVideoUrl = (
|
|||||||
return { type: VideoBubbleContentType.VIMEO, url, id }
|
return { type: VideoBubbleContentType.VIMEO, url, id }
|
||||||
}
|
}
|
||||||
if (youtubeRegex.test(url)) {
|
if (youtubeRegex.test(url)) {
|
||||||
console.log(url.match(youtubeRegex)?.at(2))
|
|
||||||
const id = url.match(youtubeRegex)?.at(2)
|
const id = url.match(youtubeRegex)?.at(2)
|
||||||
if (!id) return { type: VideoBubbleContentType.URL, url }
|
if (!id) return { type: VideoBubbleContentType.URL, url }
|
||||||
return { type: VideoBubbleContentType.YOUTUBE, url, id }
|
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 { useTypebotDnd } from '../TypebotDndProvider'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { BackButton } from './BackButton'
|
import { BackButton } from './BackButton'
|
||||||
import { OnboardingModal } from '../../dashboard/components/OnboardingModal'
|
|
||||||
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
||||||
import { useToast } from '@/hooks/useToast'
|
import { useToast } from '@/hooks/useToast'
|
||||||
import { useFolders } from '../hooks/useFolders'
|
import { useFolders } from '../hooks/useFolders'
|
||||||
@@ -26,7 +25,6 @@ import { TypebotCardOverlay } from './TypebotButtonOverlay'
|
|||||||
import { useI18n } from '@/locales'
|
import { useI18n } from '@/locales'
|
||||||
import { useTypebots } from '@/features/dashboard/hooks/useTypebots'
|
import { useTypebots } from '@/features/dashboard/hooks/useTypebots'
|
||||||
import { TypebotInDashboard } from '@/features/dashboard/types'
|
import { TypebotInDashboard } from '@/features/dashboard/types'
|
||||||
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
|
|
||||||
|
|
||||||
type Props = { folder: DashboardFolder | null }
|
type Props = { folder: DashboardFolder | null }
|
||||||
|
|
||||||
@@ -161,9 +159,6 @@ export const FolderContent = ({ folder }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex w="full" flex="1" justify="center">
|
<Flex w="full" flex="1" justify="center">
|
||||||
{typebots && isCloudProdInstance && (
|
|
||||||
<OnboardingModal totalTypebots={typebots.length} />
|
|
||||||
)}
|
|
||||||
<Stack w="1000px" spacing={6}>
|
<Stack w="1000px" spacing={6}>
|
||||||
<Skeleton isLoaded={folder?.name !== undefined}>
|
<Skeleton isLoaded={folder?.name !== undefined}>
|
||||||
<Heading as="h1">{folder?.name}</Heading>
|
<Heading as="h1">{folder?.name}</Heading>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default {
|
|||||||
downgrade: 'Downgrade',
|
downgrade: 'Downgrade',
|
||||||
remove: 'Entfernen',
|
remove: 'Entfernen',
|
||||||
pending: 'Ausstehend',
|
pending: 'Ausstehend',
|
||||||
|
skip: 'Überspringen',
|
||||||
'folders.createFolderButton.label': 'Ordner erstellen',
|
'folders.createFolderButton.label': 'Ordner erstellen',
|
||||||
'folders.createTypebotButton.label': 'Typebot erstellen',
|
'folders.createTypebotButton.label': 'Typebot erstellen',
|
||||||
'folders.folderButton.deleteConfirmationMessage':
|
'folders.folderButton.deleteConfirmationMessage':
|
||||||
@@ -77,6 +78,10 @@ export default {
|
|||||||
'auth.register.alreadyHaveAccountLabel.preLink':
|
'auth.register.alreadyHaveAccountLabel.preLink':
|
||||||
'Bereits ein Konto vorhanden?',
|
'Bereits ein Konto vorhanden?',
|
||||||
'auth.register.alreadyHaveAccountLabel.link': 'Anmelden',
|
'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.default': 'Versuche, dich mit einem anderen Konto anzumelden.',
|
||||||
'auth.error.email':
|
'auth.error.email':
|
||||||
'E-Mail nicht gefunden. Versuche, dich mit einem anderen Anbieter anzumelden.',
|
'E-Mail nicht gefunden. Versuche, dich mit einem anderen Anbieter anzumelden.',
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default {
|
|||||||
downgrade: 'Downgrade',
|
downgrade: 'Downgrade',
|
||||||
remove: 'Remove',
|
remove: 'Remove',
|
||||||
pending: 'Pending',
|
pending: 'Pending',
|
||||||
|
skip: 'Skip',
|
||||||
'folders.createFolderButton.label': 'Create a folder',
|
'folders.createFolderButton.label': 'Create a folder',
|
||||||
'folders.createTypebotButton.label': 'Create a typebot',
|
'folders.createTypebotButton.label': 'Create a typebot',
|
||||||
'folders.folderButton.deleteConfirmationMessage':
|
'folders.folderButton.deleteConfirmationMessage':
|
||||||
@@ -75,6 +76,10 @@ export default {
|
|||||||
'auth.register.heading': 'Create an account',
|
'auth.register.heading': 'Create an account',
|
||||||
'auth.register.alreadyHaveAccountLabel.preLink': 'Already have an account?',
|
'auth.register.alreadyHaveAccountLabel.preLink': 'Already have an account?',
|
||||||
'auth.register.alreadyHaveAccountLabel.link': 'Sign in',
|
'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.default': 'Try signing with a different account.',
|
||||||
'auth.error.email': 'Email not found. Try signing with a different provider.',
|
'auth.error.email': 'Email not found. Try signing with a different provider.',
|
||||||
'auth.error.oauthNotLinked':
|
'auth.error.oauthNotLinked':
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default {
|
|||||||
downgrade: 'Downgrade',
|
downgrade: 'Downgrade',
|
||||||
remove: 'Retirer',
|
remove: 'Retirer',
|
||||||
pending: 'En attente',
|
pending: 'En attente',
|
||||||
|
skip: 'Passer',
|
||||||
'folders.createFolderButton.label': 'Créer un dossier',
|
'folders.createFolderButton.label': 'Créer un dossier',
|
||||||
'folders.createTypebotButton.label': 'Créer un typebot',
|
'folders.createTypebotButton.label': 'Créer un typebot',
|
||||||
'folders.folderButton.deleteConfirmationMessage':
|
'folders.folderButton.deleteConfirmationMessage':
|
||||||
@@ -75,6 +76,10 @@ export default {
|
|||||||
'auth.register.heading': 'Créer un compte',
|
'auth.register.heading': 'Créer un compte',
|
||||||
'auth.register.alreadyHaveAccountLabel.preLink': 'Tu as déjà un compte?',
|
'auth.register.alreadyHaveAccountLabel.preLink': 'Tu as déjà un compte?',
|
||||||
'auth.register.alreadyHaveAccountLabel.link': 'Se connecter',
|
'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.default': 'Essaye de te connecter avec un compte différent.',
|
||||||
'auth.error.email':
|
'auth.error.email':
|
||||||
'Email non trouvé. Essaye de te connecter avec un fournisseur différent.',
|
'Email non trouvé. Essaye de te connecter avec un fournisseur différent.',
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export default {
|
|||||||
downgrade: 'Downgrade',
|
downgrade: 'Downgrade',
|
||||||
remove: 'Remover',
|
remove: 'Remover',
|
||||||
pending: 'Pendente',
|
pending: 'Pendente',
|
||||||
|
skip: 'Pular',
|
||||||
'folders.createFolderButton.label': 'Criar uma pasta',
|
'folders.createFolderButton.label': 'Criar uma pasta',
|
||||||
'folders.createTypebotButton.label': 'Criar um typebot',
|
'folders.createTypebotButton.label': 'Criar um typebot',
|
||||||
'folders.folderButton.deleteConfirmationMessage':
|
'folders.folderButton.deleteConfirmationMessage':
|
||||||
@@ -76,6 +77,10 @@ export default {
|
|||||||
'auth.register.heading': 'Criar uma conta',
|
'auth.register.heading': 'Criar uma conta',
|
||||||
'auth.register.alreadyHaveAccountLabel.preLink': 'Já tem uma conta?',
|
'auth.register.alreadyHaveAccountLabel.preLink': 'Já tem uma conta?',
|
||||||
'auth.register.alreadyHaveAccountLabel.link': 'Entrar',
|
'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.default': 'Tente entrar com uma conta diferente.',
|
||||||
'auth.error.email':
|
'auth.error.email':
|
||||||
'E-mail não encontrado. Tente entrar com um provedor diferente.',
|
'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',
|
type: 'oauth',
|
||||||
authorization: {
|
authorization: {
|
||||||
params: {
|
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,
|
clientId: process.env.CUSTOM_OAUTH_CLIENT_ID,
|
||||||
clientSecret: process.env.CUSTOM_OAUTH_CLIENT_SECRET,
|
clientSecret: process.env.CUSTOM_OAUTH_CLIENT_SECRET,
|
||||||
@@ -156,6 +156,9 @@ export const authOptions: AuthOptions = {
|
|||||||
},
|
},
|
||||||
pages: {
|
pages: {
|
||||||
signIn: '/signin',
|
signIn: '/signin',
|
||||||
|
newUser: process.env.NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID
|
||||||
|
? '/onboarding'
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
callbacks: {
|
callbacks: {
|
||||||
session: async ({ session, user }) => {
|
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`. |
|
| 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` |
|
| 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. |
|
| 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)
|
## Email (Auth, notifications)
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ const PrivacyPolicies = () => {
|
|||||||
<Heading>1. Terms</Heading>
|
<Heading>1. Terms</Heading>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
By accessing this Website, accessible from https://www.typebot.io, you
|
By accessing this Website, accessible from https://typebot.io and
|
||||||
are agreeing to be bound by these Website Terms and Conditions of Use
|
https://app.typebot.io, you are agreeing to be bound by these Website
|
||||||
and agree that you are responsible for the agreement with any
|
Terms and Conditions of Use and agree that you are responsible for the
|
||||||
applicable local laws. If you disagree with any of these terms, you
|
agreement with any applicable local laws. If you disagree with any of
|
||||||
are prohibited from accessing this site. The materials contained in
|
these terms, you are prohibited from accessing this site. The
|
||||||
this Website are protected by copyright and trade mark law.
|
materials contained in this Website are protected by copyright and
|
||||||
|
trade mark law.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Heading>2. Use License</Heading>
|
<Heading>2. Use License</Heading>
|
||||||
@@ -32,15 +33,6 @@ const PrivacyPolicies = () => {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ul>
|
<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>
|
<li>
|
||||||
remove any copyright or other proprietary notations from the
|
remove any copyright or other proprietary notations from the
|
||||||
materials; or
|
materials; or
|
||||||
@@ -70,12 +62,12 @@ const PrivacyPolicies = () => {
|
|||||||
<Heading>3. Disclaimer</Heading>
|
<Heading>3. Disclaimer</Heading>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
All the materials on Typebot’s Website are provided "as is".
|
All the materials on Typebot's Website are provided "as
|
||||||
Typebot makes no warranties, may it be expressed or implied, therefore
|
is". Typebot makes no warranties, may it be expressed or implied,
|
||||||
negates all other warranties. Furthermore, Typebot does not make any
|
therefore negates all other warranties. Furthermore, Typebot does not
|
||||||
representations concerning the accuracy or reliability of the use of
|
make any representations concerning the accuracy or reliability of the
|
||||||
the materials on its Website or otherwise relating to such materials
|
use of the materials on its Website or otherwise relating to such
|
||||||
or any sites linked to this Website.
|
materials or any sites linked to this Website.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Heading>4. Limitations</Heading>
|
<Heading>4. Limitations</Heading>
|
||||||
@@ -83,22 +75,22 @@ const PrivacyPolicies = () => {
|
|||||||
<p>
|
<p>
|
||||||
Typebot or its suppliers will not be hold accountable for any damages
|
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
|
that will arise with the use or inability to use the materials on
|
||||||
Typebot’s Website, even if Typebot or an authorize representative of
|
Typebot's Website, even if Typebot or an authorize representative
|
||||||
this Website has been notified, orally or written, of the possibility
|
of this Website has been notified, orally or written, of the
|
||||||
of such damage. Some jurisdiction does not allow limitations on
|
possibility of such damage. Some jurisdiction does not allow
|
||||||
implied warranties or limitations of liability for incidental damages,
|
limitations on implied warranties or limitations of liability for
|
||||||
these limitations may not apply to you.
|
incidental damages, these limitations may not apply to you.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Heading>5. Revisions and Errata</Heading>
|
<Heading>5. Revisions and Errata</Heading>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The materials appearing on Typebot’s Website may include technical,
|
The materials appearing on Typebot's Website may include
|
||||||
typographical, or photographic errors. Typebot will not promise that
|
technical, typographical, or photographic errors. Typebot will not
|
||||||
any of the materials in this Website are accurate, complete, or
|
promise that any of the materials in this Website are accurate,
|
||||||
current. Typebot may change the materials contained on its Website at
|
complete, or current. Typebot may change the materials contained on
|
||||||
any time without notice. Typebot does not make any commitment to
|
its Website at any time without notice. Typebot does not make any
|
||||||
update the materials.
|
commitment to update the materials.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Heading>6. Links</Heading>
|
<Heading>6. Links</Heading>
|
||||||
@@ -107,7 +99,7 @@ const PrivacyPolicies = () => {
|
|||||||
Typebot has not reviewed all of the sites linked to its Website and is
|
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
|
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 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>
|
</p>
|
||||||
|
|
||||||
<Heading>7. Site Terms of Use Modifications</Heading>
|
<Heading>7. Site Terms of Use Modifications</Heading>
|
||||||
|
|||||||
Reference in New Issue
Block a user