@ -7,6 +7,10 @@ const withTM = require('next-transpile-modules')(['utils', 'models', 'emails'])
|
||||
const nextConfig = withTM({
|
||||
reactStrictMode: true,
|
||||
output: 'standalone',
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'fr', 'pr'],
|
||||
},
|
||||
experimental: {
|
||||
outputFileTracingRoot: path.join(__dirname, '../../'),
|
||||
},
|
||||
|
@ -28,6 +28,7 @@
|
||||
"@giphy/react-components": "6.5.2",
|
||||
"@googleapis/drive": "4.0.2",
|
||||
"@lezer/css": "^1.1.1",
|
||||
"@paralleldrive/cuid2": "2.2.0",
|
||||
"@sentry/nextjs": "7.38.0",
|
||||
"@stripe/stripe-js": "1.47.0",
|
||||
"@tanstack/react-query": "^4.24.10",
|
||||
@ -36,6 +37,8 @@
|
||||
"@trpc/next": "10.12.0",
|
||||
"@trpc/react-query": "10.12.0",
|
||||
"@trpc/server": "10.12.0",
|
||||
"@typebot.io/js": "workspace:*",
|
||||
"@typebot.io/react": "workspace:*",
|
||||
"@udecode/plate-basic-marks": "19.2.0",
|
||||
"@udecode/plate-common": "^7.0.2",
|
||||
"@udecode/plate-core": "19.2.0",
|
||||
@ -43,18 +46,15 @@
|
||||
"@udecode/plate-serializer-html": "19.2.0",
|
||||
"@udecode/plate-ui-link": "19.2.0",
|
||||
"@udecode/plate-ui-toolbar": "19.2.0",
|
||||
"@uiw/codemirror-extensions-langs": "^4.19.9",
|
||||
"@uiw/codemirror-theme-github": "^4.19.9",
|
||||
"@uiw/codemirror-theme-tokyo-night": "^4.19.9",
|
||||
"@uiw/react-codemirror": "^4.19.9",
|
||||
"@use-gesture/react": "^10.2.24",
|
||||
"aws-sdk": "2.1321.0",
|
||||
"browser-image-compression": "2.0.0",
|
||||
"canvas-confetti": "1.6.0",
|
||||
"codemirror": "6.0.1",
|
||||
"@paralleldrive/cuid2": "2.2.0",
|
||||
"@typebot.io/js": "workspace:*",
|
||||
"@typebot.io/react": "workspace:*",
|
||||
"@uiw/codemirror-extensions-langs": "^4.19.9",
|
||||
"@uiw/codemirror-theme-github": "^4.19.9",
|
||||
"@uiw/codemirror-theme-tokyo-night": "^4.19.9",
|
||||
"@uiw/react-codemirror": "^4.19.9",
|
||||
"deep-object-diff": "1.1.9",
|
||||
"dequal": "2.0.3",
|
||||
"emails": "workspace:*",
|
||||
@ -73,6 +73,7 @@
|
||||
"minio": "7.0.32",
|
||||
"next": "13.1.6",
|
||||
"next-auth": "4.19.2",
|
||||
"@typebot.io/next-international": "^0.3.8",
|
||||
"nextjs-cors": "^2.1.2",
|
||||
"nodemailer": "6.9.1",
|
||||
"nprogress": "0.2.0",
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
AlertDialogOverlay,
|
||||
Button,
|
||||
} from '@chakra-ui/react'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
|
||||
type ConfirmDeleteModalProps = {
|
||||
isOpen: boolean
|
||||
@ -28,6 +29,7 @@ export const ConfirmModal = ({
|
||||
onConfirm,
|
||||
confirmButtonColor = 'red',
|
||||
}: ConfirmDeleteModalProps) => {
|
||||
const scopedT = useScopedI18n('confirmModal')
|
||||
const [confirmLoading, setConfirmLoading] = useState(false)
|
||||
const cancelRef = useRef(null)
|
||||
|
||||
@ -52,7 +54,7 @@ export const ConfirmModal = ({
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||
{title ?? 'Are you sure?'}
|
||||
{title ?? scopedT('defaultTitle')}
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogBody>{message}</AlertDialogBody>
|
||||
|
@ -1,37 +0,0 @@
|
||||
import { CloseButton, Flex, HStack, StackProps } from '@chakra-ui/react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
type VerifyEmailBannerProps = { id: string } & StackProps
|
||||
|
||||
export const Banner = ({ id, ...props }: VerifyEmailBannerProps) => {
|
||||
const [show, setShow] = useState(false)
|
||||
const localStorageKey = `banner-${id}`
|
||||
|
||||
useEffect(() => {
|
||||
if (!localStorage.getItem(localStorageKey)) setShow(true)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const handleCloseClick = () => {
|
||||
localStorage.setItem(localStorageKey, 'hide')
|
||||
setShow(false)
|
||||
}
|
||||
|
||||
if (!show) return <></>
|
||||
return (
|
||||
<HStack
|
||||
h="50px"
|
||||
bgColor="blue.400"
|
||||
color="white"
|
||||
justifyContent="center"
|
||||
align="center"
|
||||
w="full"
|
||||
{...props}
|
||||
>
|
||||
<Flex maxW="1000px" justifyContent="space-between" w="full">
|
||||
<HStack>{props.children}</HStack>
|
||||
<CloseButton rounded="full" onClick={handleCloseClick} />
|
||||
</Flex>
|
||||
</HStack>
|
||||
)
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalCloseButton,
|
||||
ModalBody,
|
||||
Text,
|
||||
Stack,
|
||||
Link,
|
||||
} from '@chakra-ui/react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const localStorageKey = 'typebot-20-modal'
|
||||
export const AnnoucementModal = ({ isOpen, onClose }: Props) => {
|
||||
const [show, setShow] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!localStorage.getItem(localStorageKey)) setShow(true)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const handleCloseClick = () => {
|
||||
localStorage.setItem(localStorageKey, 'hide')
|
||||
setShow(false)
|
||||
onClose()
|
||||
}
|
||||
|
||||
if (!show) return <></>
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={handleCloseClick} size="2xl">
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>What's new in Typebot 2.0?</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody as={Stack} spacing="6" pb="10">
|
||||
<Text>Typebo 2.0 has been launched February the 15th 🎉.</Text>
|
||||
<iframe
|
||||
width="620"
|
||||
height="315"
|
||||
src="https://www.youtube.com/embed/u8FZHvlYviw"
|
||||
title="YouTube video player"
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
style={{ borderRadius: '5px' }}
|
||||
/>
|
||||
<Text>
|
||||
Most questions are answered in this{' '}
|
||||
<Link
|
||||
href="https://docs.typebot.io"
|
||||
color="blue.500"
|
||||
textDecor="underline"
|
||||
>
|
||||
FAQ
|
||||
</Link>
|
||||
. If you have other questions, open up the bot on the bottom right
|
||||
corner. 😃
|
||||
</Text>
|
||||
<Text>Baptiste.</Text>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
@ -8,8 +8,10 @@ import { isNotDefined } from 'utils'
|
||||
import Link from 'next/link'
|
||||
import { WorkspaceSettingsModal } from '@/features/workspace'
|
||||
import { EmojiOrImageIcon } from '@/components/EmojiOrImageIcon'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
|
||||
export const DashboardHeader = () => {
|
||||
const scopedT = useScopedI18n('dashboard.header')
|
||||
const { user } = useUser()
|
||||
const { workspace, switchWorkspace, createWorkspace } = useWorkspace()
|
||||
|
||||
@ -52,7 +54,7 @@ export const DashboardHeader = () => {
|
||||
onClick={onOpen}
|
||||
isLoading={isNotDefined(workspace)}
|
||||
>
|
||||
Settings & Members
|
||||
{scopedT('settingsButton.label')}
|
||||
</Button>
|
||||
<WorkspaceDropdown
|
||||
currentWorkspace={workspace}
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
import { TypebotDndProvider, FolderContent } from '@/features/folders'
|
||||
import { ParentModalProvider } from '@/features/graph'
|
||||
import { useWorkspace } from '@/features/workspace'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
import { Stack, VStack, Spinner, Text } from '@chakra-ui/react'
|
||||
import { Plan } from 'db'
|
||||
import { useRouter } from 'next/router'
|
||||
@ -15,6 +16,7 @@ import { guessIfUserIsEuropean } from 'utils/pricing'
|
||||
import { DashboardHeader } from './DashboardHeader'
|
||||
|
||||
export const DashboardPage = () => {
|
||||
const scopedT = useScopedI18n('dashboard')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const { query } = useRouter()
|
||||
const { user } = useUser()
|
||||
@ -42,7 +44,7 @@ export const DashboardPage = () => {
|
||||
|
||||
return (
|
||||
<Stack minH="100vh">
|
||||
<Seo title={workspace?.name ?? 'My typebots'} />
|
||||
<Seo title={workspace?.name ?? scopedT('title')} />
|
||||
<DashboardHeader />
|
||||
{!workspace?.stripeId && (
|
||||
<ParentModalProvider>
|
||||
@ -57,7 +59,7 @@ export const DashboardPage = () => {
|
||||
<TypebotDndProvider>
|
||||
{isLoading ? (
|
||||
<VStack w="full" justifyContent="center" pt="10" spacing={6}>
|
||||
<Text>You are being redirected...</Text>
|
||||
<Text>{scopedT('redirectionMessage')}</Text>
|
||||
<Spinner />
|
||||
</VStack>
|
||||
) : (
|
||||
|
@ -40,7 +40,7 @@ test('folders and typebots should be deletable', async ({ page }) => {
|
||||
await page.click('li:has-text("Folder #1") >> button:has-text("Delete")')
|
||||
await deleteButtonInConfirmDialog(page).click()
|
||||
await expect(page.locator('span >> text="Folder #1"')).not.toBeVisible()
|
||||
await page.click('button[aria-label="Show Typebot #1 menu"]')
|
||||
await page.click('button[aria-label="Show more options"]')
|
||||
await page.click('li:has-text("Typebot #1") >> button:has-text("Delete")')
|
||||
await deleteButtonInConfirmDialog(page).click()
|
||||
await expect(page.locator('span >> text="Typebot #1"')).not.toBeVisible()
|
||||
|
@ -3,8 +3,10 @@ import { ChevronLeftIcon } from '@/components/icons'
|
||||
import { useTypebotDnd } from '../TypebotDndProvider'
|
||||
import Link from 'next/link'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useI18n } from '@/locales'
|
||||
|
||||
export const BackButton = ({ id }: { id: string | null }) => {
|
||||
const t = useI18n()
|
||||
const { draggedTypebot, setMouseOverFolderId, mouseOverFolderId } =
|
||||
useTypebotDnd()
|
||||
|
||||
@ -26,7 +28,7 @@ export const BackButton = ({ id }: { id: string | null }) => {
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
Back
|
||||
{t('back')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
@ -3,12 +3,14 @@ import { PlusIcon } from '@/components/icons'
|
||||
import { useRouter } from 'next/router'
|
||||
import { stringify } from 'qs'
|
||||
import React from 'react'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
|
||||
export const CreateBotButton = ({
|
||||
folderId,
|
||||
isFirstBot,
|
||||
...props
|
||||
}: { folderId?: string; isFirstBot: boolean } & ButtonProps) => {
|
||||
const scopedT = useScopedI18n('folders.createTypebotButton')
|
||||
const router = useRouter()
|
||||
|
||||
const handleClick = () =>
|
||||
@ -39,7 +41,7 @@ export const CreateBotButton = ({
|
||||
textAlign="center"
|
||||
mt="6"
|
||||
>
|
||||
Create a typebot
|
||||
{scopedT('label')}
|
||||
</Text>
|
||||
</VStack>
|
||||
</Button>
|
||||
|
@ -9,10 +9,12 @@ import {
|
||||
import { useWorkspace } from '@/features/workspace'
|
||||
import { Plan } from 'db'
|
||||
import React from 'react'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
|
||||
type Props = { isLoading: boolean; onClick: () => void }
|
||||
|
||||
export const CreateFolderButton = ({ isLoading, onClick }: Props) => {
|
||||
const scopedT = useScopedI18n('folders.createFolderButton')
|
||||
const { workspace } = useWorkspace()
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
|
||||
@ -27,7 +29,7 @@ export const CreateFolderButton = ({ isLoading, onClick }: Props) => {
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<HStack>
|
||||
<Text>Create a folder</Text>
|
||||
<Text>{scopedT('label')}</Text>
|
||||
{isFreePlan(workspace) && <LockTag plan={Plan.STARTER} />}
|
||||
</HStack>
|
||||
<ChangePlanModal
|
||||
|
@ -25,6 +25,7 @@ import React, { useMemo } from 'react'
|
||||
import { deleteFolderQuery } from '../queries/deleteFolderQuery'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { updateFolderQuery } from '../queries/updateFolderQuery'
|
||||
import { useI18n, useScopedI18n } from '@/locales'
|
||||
|
||||
export const FolderButton = ({
|
||||
folder,
|
||||
@ -35,6 +36,8 @@ export const FolderButton = ({
|
||||
onFolderDeleted: () => void
|
||||
onFolderRenamed: (newName: string) => void
|
||||
}) => {
|
||||
const t = useI18n()
|
||||
const scopedT = useScopedI18n('folders.folderButton')
|
||||
const router = useRouter()
|
||||
const { draggedTypebot, setMouseOverFolderId, mouseOverFolderId } =
|
||||
useTypebotDnd()
|
||||
@ -49,7 +52,6 @@ export const FolderButton = ({
|
||||
const { error } = await deleteFolderQuery(folder.id)
|
||||
return error
|
||||
? showToast({
|
||||
title: "Couldn't delete the folder",
|
||||
description: error.message,
|
||||
})
|
||||
: onFolderDeleted()
|
||||
@ -59,7 +61,7 @@ export const FolderButton = ({
|
||||
if (newName === '' || newName === folder.name) return
|
||||
const { error } = await updateFolderQuery(folder.id, { name: newName })
|
||||
return error
|
||||
? showToast({ title: 'An error occured', description: error.message })
|
||||
? showToast({ title: t('errorMessage'), description: error.message })
|
||||
: onFolderRenamed(newName)
|
||||
}
|
||||
|
||||
@ -106,7 +108,7 @@ export const FolderButton = ({
|
||||
onOpen()
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
{t('delete')}
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
@ -138,8 +140,9 @@ export const FolderButton = ({
|
||||
confirmButtonLabel={'Delete'}
|
||||
message={
|
||||
<Text>
|
||||
Are you sure you want to delete <strong>{folder.name}</strong>{' '}
|
||||
folder? (Everything inside will be move to your dashboard)
|
||||
{scopedT('deleteConfirmationMessage', {
|
||||
folderName: <strong>{folder.name}</strong>,
|
||||
})}
|
||||
</Text>
|
||||
}
|
||||
title={`Delete ${folder.name}?`}
|
||||
|
@ -24,12 +24,14 @@ import { CreateFolderButton } from './CreateFolderButton'
|
||||
import { ButtonSkeleton, FolderButton } from './FolderButton'
|
||||
import { TypebotButton } from './TypebotButton'
|
||||
import { TypebotCardOverlay } from './TypebotButtonOverlay'
|
||||
import { useI18n } from '@/locales'
|
||||
|
||||
type Props = { folder: DashboardFolder | null }
|
||||
|
||||
const dragDistanceTolerance = 20
|
||||
|
||||
export const FolderContent = ({ folder }: Props) => {
|
||||
const t = useI18n()
|
||||
const { workspace, currentRole } = useWorkspace()
|
||||
const [isCreatingFolder, setIsCreatingFolder] = useState(false)
|
||||
const {
|
||||
@ -57,7 +59,9 @@ export const FolderContent = ({ folder }: Props) => {
|
||||
workspaceId: workspace?.id,
|
||||
parentId: folder?.id,
|
||||
onError: (error) => {
|
||||
showToast({ title: "Couldn't fetch folders", description: error.message })
|
||||
showToast({
|
||||
description: error.message,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@ -70,7 +74,6 @@ export const FolderContent = ({ folder }: Props) => {
|
||||
folderId: folder === null ? 'root' : folder.id,
|
||||
onError: (error) => {
|
||||
showToast({
|
||||
title: "Couldn't fetch typebots",
|
||||
description: error.message,
|
||||
})
|
||||
},
|
||||
@ -94,7 +97,7 @@ export const FolderContent = ({ folder }: Props) => {
|
||||
setIsCreatingFolder(false)
|
||||
if (error)
|
||||
return showToast({
|
||||
title: 'An error occured',
|
||||
title: t('errorMessage'),
|
||||
description: error.message,
|
||||
})
|
||||
if (newFolder) mutateFolders({ folders: [...folders, newFolder] })
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Seo } from '@/components/Seo'
|
||||
import { DashboardHeader } from '@/features/dashboard'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { useI18n } from '@/locales'
|
||||
import { Stack, Flex, Spinner } from '@chakra-ui/react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useFolder } from '../hooks/useFolder'
|
||||
@ -8,6 +9,7 @@ import { TypebotDndProvider } from '../TypebotDndProvider'
|
||||
import { FolderContent } from './FolderContent'
|
||||
|
||||
export const FolderPage = () => {
|
||||
const t = useI18n()
|
||||
const router = useRouter()
|
||||
|
||||
const { showToast } = useToast()
|
||||
@ -16,7 +18,6 @@ export const FolderPage = () => {
|
||||
folderId: router.query.id?.toString(),
|
||||
onError: (error) => {
|
||||
showToast({
|
||||
title: "Couldn't fetch folder content",
|
||||
description: error.message,
|
||||
})
|
||||
},
|
||||
@ -24,7 +25,7 @@ export const FolderPage = () => {
|
||||
|
||||
return (
|
||||
<Stack minH="100vh">
|
||||
<Seo title="My typebots" />
|
||||
<Seo title={t('dashboard.title')} />
|
||||
<DashboardHeader />
|
||||
<TypebotDndProvider>
|
||||
{!folder ? (
|
||||
|
@ -1,9 +1,12 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
Alert,
|
||||
AlertIcon,
|
||||
Button,
|
||||
Flex,
|
||||
IconButton,
|
||||
MenuItem,
|
||||
Stack,
|
||||
Tag,
|
||||
Text,
|
||||
useDisclosure,
|
||||
@ -28,6 +31,7 @@ import {
|
||||
import { MoreButton } from './MoreButton'
|
||||
import { EmojiOrImageIcon } from '@/components/EmojiOrImageIcon'
|
||||
import { deletePublishedTypebotQuery } from '@/features/publish/queries/deletePublishedTypebotQuery'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
|
||||
type Props = {
|
||||
typebot: TypebotInDashboard
|
||||
@ -42,6 +46,7 @@ export const TypebotButton = ({
|
||||
onTypebotUpdated,
|
||||
onMouseDown,
|
||||
}: Props) => {
|
||||
const scopedT = useScopedI18n('folders.typebotButton')
|
||||
const router = useRouter()
|
||||
const { workspace } = useWorkspace()
|
||||
const { draggedTypebot } = useTypebotDnd()
|
||||
@ -68,7 +73,6 @@ export const TypebotButton = ({
|
||||
const { error } = await deleteTypebotQuery(typebot.id)
|
||||
if (error)
|
||||
return showToast({
|
||||
title: "Couldn't delete typebot",
|
||||
description: error.message,
|
||||
})
|
||||
onTypebotUpdated()
|
||||
@ -78,14 +82,13 @@ export const TypebotButton = ({
|
||||
e.stopPropagation()
|
||||
const { data } = await getTypebotQuery(typebot.id)
|
||||
const typebotToDuplicate = data?.typebot
|
||||
if (!typebotToDuplicate) return { error: new Error('Typebot not found') }
|
||||
if (!typebotToDuplicate) return
|
||||
const { data: createdTypebot, error } = await importTypebotQuery(
|
||||
data.typebot,
|
||||
workspace?.plan ?? Plan.FREE
|
||||
)
|
||||
if (error)
|
||||
return showToast({
|
||||
title: "Couldn't duplicate typebot",
|
||||
description: error.message,
|
||||
})
|
||||
if (createdTypebot) router.push(`/typebots/${createdTypebot?.id}/edit`)
|
||||
@ -153,14 +156,18 @@ export const TypebotButton = ({
|
||||
pos="absolute"
|
||||
top="20px"
|
||||
right="20px"
|
||||
aria-label={`Show ${typebot.name} menu`}
|
||||
aria-label={scopedT('showMoreOptions')}
|
||||
>
|
||||
{typebot.publishedTypebotId && (
|
||||
<MenuItem onClick={handleUnpublishClick}>Unpublish</MenuItem>
|
||||
<MenuItem onClick={handleUnpublishClick}>
|
||||
{scopedT('unpublish')}
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem onClick={handleDuplicateClick}>Duplicate</MenuItem>
|
||||
<MenuItem onClick={handleDuplicateClick}>
|
||||
{scopedT('duplicate')}
|
||||
</MenuItem>
|
||||
<MenuItem color="red.400" onClick={handleDeleteClick}>
|
||||
Delete
|
||||
{scopedT('delete')}
|
||||
</MenuItem>
|
||||
</MoreButton>
|
||||
</>
|
||||
@ -181,12 +188,17 @@ export const TypebotButton = ({
|
||||
{!isReadOnly && (
|
||||
<ConfirmModal
|
||||
message={
|
||||
<Text>
|
||||
Are you sure you want to delete your Typebot "{typebot.name}
|
||||
".
|
||||
<br />
|
||||
All associated data will be lost.
|
||||
</Text>
|
||||
<Stack spacing="4">
|
||||
<Text>
|
||||
{scopedT('deleteConfirmationMessage', {
|
||||
typebotName: <strong>{typebot.name}</strong>,
|
||||
})}
|
||||
</Text>
|
||||
<Alert status="warning">
|
||||
<AlertIcon />
|
||||
{scopedT('deleteConfirmationMessageWarning')}
|
||||
</Alert>
|
||||
</Stack>
|
||||
}
|
||||
confirmButtonLabel="Delete"
|
||||
onConfirm={handleDeleteTypebotClick}
|
||||
|
@ -169,6 +169,11 @@ const Alert = createMultiStyleConfigHelpers(
|
||||
}
|
||||
},
|
||||
},
|
||||
baseStyle: {
|
||||
container: {
|
||||
borderRadius: 'md',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const Switch = createMultiStyleConfigHelpers(
|
||||
|
22
apps/builder/src/locales/en.ts
Normal file
22
apps/builder/src/locales/en.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
back: 'Back',
|
||||
'confirmModal.defaultTitle': 'Are you sure?',
|
||||
'dashboard.header.settingsButton.label': 'Settings & Members',
|
||||
'dashboard.redirectionMessage': 'You are being redirected...',
|
||||
'dashboard.title': 'My typebots',
|
||||
delete: 'Delete',
|
||||
errorMessage: 'An error occured',
|
||||
'folders.createFolderButton.label': 'Create a folder',
|
||||
'folders.createTypebotButton.label': 'Create a typebot',
|
||||
'folders.folderButton.deleteConfirmationMessage':
|
||||
'Are you sure you want to delete {folderName} folder? (Everything inside will be move to your dashboard)',
|
||||
'folders.typebotButton.live': 'Live',
|
||||
'folders.typebotButton.showMoreOptions': 'Show more options',
|
||||
'folders.typebotButton.unpublish': 'Unpublish',
|
||||
'folders.typebotButton.duplicate': 'Duplicate',
|
||||
'folders.typebotButton.delete': 'Delete',
|
||||
'folders.typebotButton.deleteConfirmationMessage':
|
||||
'Are you sure you want to delete your typebot {typebotName}?',
|
||||
'folders.typebotButton.deleteConfirmationMessageWarning':
|
||||
"All its associated data will be deleted and won't be recoverable.",
|
||||
} as const
|
24
apps/builder/src/locales/fr.ts
Normal file
24
apps/builder/src/locales/fr.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { defineLocale } from './index'
|
||||
|
||||
export default defineLocale({
|
||||
back: 'Retour',
|
||||
'confirmModal.defaultTitle': 'Êtes-vous sûr ?',
|
||||
'dashboard.header.settingsButton.label': 'Paramètres & Membres',
|
||||
'dashboard.redirectionMessage': "Vous êtes en train d'être redirigé...",
|
||||
'dashboard.title': 'Mes typebots',
|
||||
delete: 'Supprimer',
|
||||
errorMessage: "Une erreur s'est produite",
|
||||
'folders.createFolderButton.label': 'Créer un dossier',
|
||||
'folders.createTypebotButton.label': 'Créer un typebot',
|
||||
'folders.folderButton.deleteConfirmationMessage':
|
||||
"Êtes-vous sûr de vouloir supprimer le dossier {folderName} ? (Tout ce qui est à l'intérieur sera déplacé dans le dossier parent ou sur votre tableau de bord)",
|
||||
'folders.typebotButton.live': 'Live',
|
||||
'folders.typebotButton.showMoreOptions': "Afficher plus d'options",
|
||||
'folders.typebotButton.unpublish': 'Dépublier',
|
||||
'folders.typebotButton.duplicate': 'Dupliquer',
|
||||
'folders.typebotButton.delete': 'Supprimer',
|
||||
'folders.typebotButton.deleteConfirmationMessage':
|
||||
'Êtes-vous sûr de vouloir supprimer votre typebot {typebotName} ?',
|
||||
'folders.typebotButton.deleteConfirmationMessageWarning':
|
||||
'Toutes les données associées seront supprimées et ne pourront pas être récupérées.',
|
||||
})
|
14
apps/builder/src/locales/index.ts
Normal file
14
apps/builder/src/locales/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { createI18n } from '@typebot.io/next-international'
|
||||
import type Locale from './en'
|
||||
|
||||
export const {
|
||||
defineLocale,
|
||||
useI18n,
|
||||
useScopedI18n,
|
||||
I18nProvider,
|
||||
getLocaleProps,
|
||||
} = createI18n<typeof Locale>({
|
||||
en: () => import('./en'),
|
||||
fr: () => import('./fr'),
|
||||
pt: () => import('./pt'),
|
||||
})
|
24
apps/builder/src/locales/pt.ts
Normal file
24
apps/builder/src/locales/pt.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { defineLocale } from './index'
|
||||
|
||||
export default defineLocale({
|
||||
back: 'Voltar',
|
||||
'confirmModal.defaultTitle': 'Tem certeza?',
|
||||
'dashboard.header.settingsButton.label': 'Configurações & Membros',
|
||||
'dashboard.redirectionMessage': 'Você está sendo redirecionado...',
|
||||
'dashboard.title': 'Meus typebots',
|
||||
delete: 'Deletar',
|
||||
errorMessage: 'Ocorreu um erro',
|
||||
'folders.createFolderButton.label': 'Criar uma pasta',
|
||||
'folders.createTypebotButton.label': 'Criar um typebot',
|
||||
'folders.folderButton.deleteConfirmationMessage':
|
||||
'Tem certeza de que deseja excluir a pasta {folderName}? (Tudo o que estiver dentro será movido para o seu painel)',
|
||||
'folders.typebotButton.live': 'Live',
|
||||
'folders.typebotButton.showMoreOptions': 'Mostrar mais opções',
|
||||
'folders.typebotButton.unpublish': 'Despublicar',
|
||||
'folders.typebotButton.duplicate': 'Duplicar',
|
||||
'folders.typebotButton.delete': 'Deletar',
|
||||
'folders.typebotButton.deleteConfirmationMessage':
|
||||
'Tem certeza de que deseja excluir seu typebot {typebotName}?',
|
||||
'folders.typebotButton.deleteConfirmationMessageWarning':
|
||||
'Todos os dados associados serão excluídos e não poderão ser recuperados.',
|
||||
})
|
@ -14,17 +14,14 @@ import { useRouter } from 'next/router'
|
||||
import { SupportBubble } from '@/components/SupportBubble'
|
||||
import { WorkspaceProvider } from '@/features/workspace'
|
||||
import { toTitleCase } from 'utils'
|
||||
import { Session } from 'next-auth'
|
||||
import { Plan } from 'db'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import { NewVersionPopup } from '@/components/NewVersionPopup'
|
||||
import { I18nProvider } from '@/locales'
|
||||
|
||||
const { ToastContainer, toast } = createStandaloneToast(customTheme)
|
||||
|
||||
const App = ({
|
||||
Component,
|
||||
pageProps: { session, ...pageProps },
|
||||
}: AppProps<{ session?: Session }>) => {
|
||||
const App = ({ Component, pageProps }: AppProps) => {
|
||||
useRouterProgressBar()
|
||||
const { query, pathname } = useRouter()
|
||||
|
||||
@ -50,19 +47,21 @@ const App = ({
|
||||
return (
|
||||
<>
|
||||
<ToastContainer />
|
||||
<ChakraProvider theme={customTheme}>
|
||||
<SessionProvider session={session}>
|
||||
<UserProvider>
|
||||
<TypebotProvider typebotId={typebotId}>
|
||||
<WorkspaceProvider typebotId={typebotId}>
|
||||
<Component {...pageProps} />
|
||||
<SupportBubble />
|
||||
<NewVersionPopup />
|
||||
</WorkspaceProvider>
|
||||
</TypebotProvider>
|
||||
</UserProvider>
|
||||
</SessionProvider>
|
||||
</ChakraProvider>
|
||||
<I18nProvider locale={pageProps.locale}>
|
||||
<ChakraProvider theme={customTheme}>
|
||||
<SessionProvider session={pageProps.session}>
|
||||
<UserProvider>
|
||||
<TypebotProvider typebotId={typebotId}>
|
||||
<WorkspaceProvider typebotId={typebotId}>
|
||||
<Component {...pageProps} />
|
||||
<SupportBubble />
|
||||
<NewVersionPopup />
|
||||
</WorkspaceProvider>
|
||||
</TypebotProvider>
|
||||
</UserProvider>
|
||||
</SessionProvider>
|
||||
</ChakraProvider>
|
||||
</I18nProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { getLocaleProps } from '@/locales'
|
||||
import { GetServerSidePropsContext } from 'next'
|
||||
import { getSession } from 'next-auth/react'
|
||||
|
||||
@ -5,12 +6,28 @@ export default function Page() {
|
||||
return null
|
||||
}
|
||||
|
||||
export const getServerSideProps = async (
|
||||
context: GetServerSidePropsContext
|
||||
) => {
|
||||
const session = await getSession(context)
|
||||
if (!session?.user) {
|
||||
return { redirect: { permanent: false, destination: '/signin' } }
|
||||
export const getServerSideProps = getLocaleProps(
|
||||
async (context: GetServerSidePropsContext) => {
|
||||
const session = await getSession(context)
|
||||
if (!session?.user) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination:
|
||||
context.locale !== context.defaultLocale
|
||||
? `/${context.locale}/signin`
|
||||
: '/signin',
|
||||
},
|
||||
}
|
||||
}
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination:
|
||||
context.locale !== context.defaultLocale
|
||||
? `/${context.locale}/typebots`
|
||||
: '/typebots',
|
||||
},
|
||||
}
|
||||
}
|
||||
return { redirect: { permanent: false, destination: '/typebots' } }
|
||||
}
|
||||
)
|
||||
|
@ -1,18 +1,21 @@
|
||||
import { NextPageContext } from 'next/types'
|
||||
import { DashboardPage } from '@/features/dashboard'
|
||||
import { getLocaleProps } from '@/locales'
|
||||
import { GetServerSidePropsContext } from 'next'
|
||||
|
||||
export default function Page() {
|
||||
return <DashboardPage />
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context: NextPageContext) {
|
||||
const redirectPath = context.query.redirectPath?.toString()
|
||||
return redirectPath
|
||||
? {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: redirectPath,
|
||||
},
|
||||
}
|
||||
: { props: {} }
|
||||
}
|
||||
export const getServerSideProps = getLocaleProps(
|
||||
async (context: GetServerSidePropsContext) => {
|
||||
const redirectPath = context.query.redirectPath?.toString()
|
||||
return redirectPath
|
||||
? {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: redirectPath,
|
||||
},
|
||||
}
|
||||
: { props: {} }
|
||||
}
|
||||
)
|
||||
|
17
pnpm-lock.yaml
generated
17
pnpm-lock.yaml
generated
@ -44,6 +44,7 @@ importers:
|
||||
'@trpc/react-query': 10.12.0
|
||||
'@trpc/server': 10.12.0
|
||||
'@typebot.io/js': workspace:*
|
||||
'@typebot.io/next-international': ^0.3.8
|
||||
'@typebot.io/react': workspace:*
|
||||
'@types/canvas-confetti': 1.6.0
|
||||
'@types/google-spreadsheet': 3.3.1
|
||||
@ -148,6 +149,7 @@ importers:
|
||||
'@trpc/react-query': 10.12.0_tij422wpfxfl3iixjhzsgwgs2e
|
||||
'@trpc/server': 10.12.0
|
||||
'@typebot.io/js': link:../../packages/js
|
||||
'@typebot.io/next-international': 0.3.8_next@13.1.6+react@18.2.0
|
||||
'@typebot.io/react': link:../../packages/react
|
||||
'@udecode/plate-basic-marks': 19.2.0_opnyntabsv5vdkt3p6hmagoxbq
|
||||
'@udecode/plate-common': 7.0.2_rjhyybmi5zd2n2kz5mt3q7icvy
|
||||
@ -6669,6 +6671,17 @@ packages:
|
||||
- webpack-cli
|
||||
dev: false
|
||||
|
||||
/@typebot.io/next-international/0.3.8_next@13.1.6+react@18.2.0:
|
||||
resolution: {integrity: sha512-X5Q/r7iNhCBlWGeQho2PESDFjYk5NUW72dUEI/ghG6kE1jactXJRC0yuPRef/eILwlbwF5k+YXSo5ueiJZ4H1Q==}
|
||||
peerDependencies:
|
||||
next: ^12.0.0 || ^11.0.0
|
||||
react: ^18.0.0 || ^17.0.0
|
||||
dependencies:
|
||||
international-types: 0.3.8
|
||||
next: 13.1.6_6m24vuloj5ihw4zc5lbsktc4fu
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@types/aos/3.0.4:
|
||||
resolution: {integrity: sha512-mna6Jd6bdK1NpwarLopGvXOgUoCfj0470IwLxuVOFDElTGI0JTd7xSGQ0AjbAEnHErC/b3fA9t2uB3IXVKmckA==}
|
||||
dev: true
|
||||
@ -13026,6 +13039,10 @@ packages:
|
||||
has: 1.0.3
|
||||
side-channel: 1.0.4
|
||||
|
||||
/international-types/0.3.8:
|
||||
resolution: {integrity: sha512-DeAFYOSA2wuUiSkFnAqrIM1UNVSAa8aP6hbXQOK4n74VuP/bCZPlZ7SEnCZ6+JBID/ekH1Ir5xTwsKA2Q++mKw==}
|
||||
dev: false
|
||||
|
||||
/interpret/1.4.0:
|
||||
resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
Reference in New Issue
Block a user