🧑💻 Migrate to Tolgee (#976)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ### Summary by CodeRabbit - Refactor: Transitioned to a new translation library (`@tolgee/react`) across the application, enhancing the localization capabilities and consistency. - New Feature: Introduced a JSON configuration file for application settings, improving customization and flexibility. - Refactor: Updated SVG attribute naming convention in the `WhatsAppLogo` component to align with React standards. - Chore: Adjusted the `.gitignore` file and added a new line at the end. - Documentation: Added instructions for setting up environment variables for the Tolgee i18n contribution dev tool, improving the self-hosting configuration guide. - Style: Updated the `CollaborationMenuButton` to hide the `PopoverContent` component by scaling it down to zero. - Refactor: Simplified error handling logic for fetching and updating typebots in `TypebotProvider.tsx`, improving code readability and maintenance. - Refactor: Removed the dependency on the `parseGroupTitle` function, simplifying the code in several components. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -14,7 +14,7 @@ import { Plan } from '@typebot.io/prisma'
|
||||
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
||||
import { BlockLabel } from './BlockLabel'
|
||||
import { LockTag } from '@/features/billing/components/LockTag'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
|
||||
type Props = {
|
||||
type: DraggableBlockType
|
||||
@@ -27,7 +27,7 @@ type Props = {
|
||||
export const BlockCard = (
|
||||
props: Pick<Props, 'type' | 'onMouseDown'>
|
||||
): JSX.Element => {
|
||||
const scopedT = useScopedI18n('editor.blockCard')
|
||||
const { t } = useTranslate()
|
||||
const { workspace } = useWorkspace()
|
||||
|
||||
switch (props.type) {
|
||||
@@ -35,7 +35,7 @@ export const BlockCard = (
|
||||
return (
|
||||
<BlockCardLayout
|
||||
{...props}
|
||||
tooltip={scopedT('bubbleBlock.tooltip.label')}
|
||||
tooltip={t('blocks.bubbles.embed.blockCard.tooltip')}
|
||||
>
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
@@ -45,7 +45,7 @@ export const BlockCard = (
|
||||
return (
|
||||
<BlockCardLayout
|
||||
{...props}
|
||||
tooltip={scopedT('inputBlock.tooltip.files.label')}
|
||||
tooltip={t('blocks.inputs.fileUpload.blockCard.tooltip')}
|
||||
>
|
||||
<BlockIcon type={props.type} />
|
||||
<HStack>
|
||||
@@ -58,7 +58,7 @@ export const BlockCard = (
|
||||
return (
|
||||
<BlockCardLayout
|
||||
{...props}
|
||||
tooltip={scopedT('logicBlock.tooltip.code.label')}
|
||||
tooltip={t('editor.blockCard.logicBlock.tooltip.code.label')}
|
||||
>
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
@@ -68,7 +68,7 @@ export const BlockCard = (
|
||||
return (
|
||||
<BlockCardLayout
|
||||
{...props}
|
||||
tooltip={scopedT('logicBlock.tooltip.typebotLink.label')}
|
||||
tooltip={t('editor.blockCard.logicBlock.tooltip.typebotLink.label')}
|
||||
>
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
@@ -78,7 +78,7 @@ export const BlockCard = (
|
||||
return (
|
||||
<BlockCardLayout
|
||||
{...props}
|
||||
tooltip={scopedT('logicBlock.tooltip.jump.label')}
|
||||
tooltip={t('editor.blockCard.logicBlock.tooltip.jump.label')}
|
||||
>
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
@@ -88,7 +88,7 @@ export const BlockCard = (
|
||||
return (
|
||||
<BlockCardLayout
|
||||
{...props}
|
||||
tooltip={scopedT('integrationBlock.tooltip.googleSheets.label')}
|
||||
tooltip={t('blocks.integrations.googleSheets.blockCard.tooltip')}
|
||||
>
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
@@ -98,7 +98,7 @@ export const BlockCard = (
|
||||
return (
|
||||
<BlockCardLayout
|
||||
{...props}
|
||||
tooltip={scopedT('integrationBlock.tooltip.googleAnalytics.label')}
|
||||
tooltip={t('blocks.integrations.googleAnalytics.blockCard.tooltip')}
|
||||
>
|
||||
<BlockIcon type={props.type} />
|
||||
<BlockLabel type={props.type} />
|
||||
|
||||
@@ -7,84 +7,98 @@ import {
|
||||
BlockType,
|
||||
} from '@typebot.io/schemas'
|
||||
import React from 'react'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
|
||||
type Props = { type: BlockType }
|
||||
|
||||
export const BlockLabel = ({ type }: Props): JSX.Element => {
|
||||
const scopedT = useScopedI18n('editor.sidebarBlock')
|
||||
const { t } = useTranslate()
|
||||
|
||||
switch (type) {
|
||||
case 'start':
|
||||
return <Text fontSize="sm">{scopedT('start.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.start.label')}</Text>
|
||||
case BubbleBlockType.TEXT:
|
||||
case InputBlockType.TEXT:
|
||||
return <Text fontSize="sm">{scopedT('text.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.text.label')}</Text>
|
||||
case BubbleBlockType.IMAGE:
|
||||
return <Text fontSize="sm">{scopedT('image.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.image.label')}</Text>
|
||||
case BubbleBlockType.VIDEO:
|
||||
return <Text fontSize="sm">{scopedT('video.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.video.label')}</Text>
|
||||
case BubbleBlockType.EMBED:
|
||||
return <Text fontSize="sm">{scopedT('embed.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.embed.label')}</Text>
|
||||
case BubbleBlockType.AUDIO:
|
||||
return <Text fontSize="sm">{scopedT('audio.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.audio.label')}</Text>
|
||||
case InputBlockType.NUMBER:
|
||||
return <Text fontSize="sm">{scopedT('number.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.number.label')}</Text>
|
||||
case InputBlockType.EMAIL:
|
||||
return <Text fontSize="sm">{scopedT('email.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.email.label')}</Text>
|
||||
case InputBlockType.URL:
|
||||
return <Text fontSize="sm">{scopedT('website.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.website.label')}</Text>
|
||||
case InputBlockType.DATE:
|
||||
return <Text fontSize="sm">{scopedT('date.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.date.label')}</Text>
|
||||
case InputBlockType.PHONE:
|
||||
return <Text fontSize="sm">{scopedT('phone.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.phone.label')}</Text>
|
||||
case InputBlockType.CHOICE:
|
||||
return <Text fontSize="sm">{scopedT('button.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.button.label')}</Text>
|
||||
case InputBlockType.PICTURE_CHOICE:
|
||||
return <Text fontSize="sm">{scopedT('picChoice.label')}</Text>
|
||||
return (
|
||||
<Text fontSize="sm">{t('editor.sidebarBlock.picChoice.label')}</Text>
|
||||
)
|
||||
case InputBlockType.PAYMENT:
|
||||
return <Text fontSize="sm">{scopedT('payment.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.payment.label')}</Text>
|
||||
case InputBlockType.RATING:
|
||||
return <Text fontSize="sm">{scopedT('rating.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.rating.label')}</Text>
|
||||
case InputBlockType.FILE:
|
||||
return <Text fontSize="sm">{scopedT('file.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.file.label')}</Text>
|
||||
case LogicBlockType.SET_VARIABLE:
|
||||
return <Text fontSize="sm">{scopedT('setVariable.label')}</Text>
|
||||
return (
|
||||
<Text fontSize="sm">{t('editor.sidebarBlock.setVariable.label')}</Text>
|
||||
)
|
||||
case LogicBlockType.CONDITION:
|
||||
return <Text fontSize="sm">{scopedT('condition.label')}</Text>
|
||||
return (
|
||||
<Text fontSize="sm">{t('editor.sidebarBlock.condition.label')}</Text>
|
||||
)
|
||||
case LogicBlockType.REDIRECT:
|
||||
return <Text fontSize="sm">{scopedT('redirect.label')}</Text>
|
||||
return (
|
||||
<Text fontSize="sm">{t('editor.sidebarBlock.redirect.label')}</Text>
|
||||
)
|
||||
case LogicBlockType.SCRIPT:
|
||||
return <Text fontSize="sm">{scopedT('script.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.script.label')}</Text>
|
||||
case LogicBlockType.TYPEBOT_LINK:
|
||||
return <Text fontSize="sm">{scopedT('typebot.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.typebot.label')}</Text>
|
||||
case LogicBlockType.WAIT:
|
||||
return <Text fontSize="sm">{scopedT('wait.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.wait.label')}</Text>
|
||||
case LogicBlockType.JUMP:
|
||||
return <Text fontSize="sm">{scopedT('jump.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.jump.label')}</Text>
|
||||
case LogicBlockType.AB_TEST:
|
||||
return <Text fontSize="sm">{scopedT('abTest.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.abTest.label')}</Text>
|
||||
case IntegrationBlockType.GOOGLE_SHEETS:
|
||||
return <Text fontSize="sm">{scopedT('sheets.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.sheets.label')}</Text>
|
||||
case IntegrationBlockType.GOOGLE_ANALYTICS:
|
||||
return <Text fontSize="sm">{scopedT('analytics.label')}</Text>
|
||||
return (
|
||||
<Text fontSize="sm">{t('editor.sidebarBlock.analytics.label')}</Text>
|
||||
)
|
||||
case IntegrationBlockType.WEBHOOK:
|
||||
return <Text fontSize="sm">{scopedT('webhook.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.webhook.label')}</Text>
|
||||
case IntegrationBlockType.ZAPIER:
|
||||
return <Text fontSize="sm">{scopedT('zapier.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.zapier.label')}</Text>
|
||||
case IntegrationBlockType.MAKE_COM:
|
||||
return <Text fontSize="sm">{scopedT('makecom.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.makecom.label')}</Text>
|
||||
case IntegrationBlockType.PABBLY_CONNECT:
|
||||
return <Text fontSize="sm">{scopedT('pabbly.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.pabbly.label')}</Text>
|
||||
case IntegrationBlockType.EMAIL:
|
||||
return <Text fontSize="sm">{scopedT('email.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.email.label')}</Text>
|
||||
case IntegrationBlockType.CHATWOOT:
|
||||
return <Text fontSize="sm">{scopedT('chatwoot.label')}</Text>
|
||||
return (
|
||||
<Text fontSize="sm">{t('editor.sidebarBlock.chatwoot.label')}</Text>
|
||||
)
|
||||
case IntegrationBlockType.OPEN_AI:
|
||||
return <Text fontSize="sm">{scopedT('openai.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.openai.label')}</Text>
|
||||
case IntegrationBlockType.PIXEL:
|
||||
return <Text fontSize="sm">{scopedT('pixel.label')}</Text>
|
||||
return <Text fontSize="sm">{t('editor.sidebarBlock.pixel.label')}</Text>
|
||||
case IntegrationBlockType.ZEMANTIC_AI:
|
||||
return <Text fontSize="sm">{scopedT('zemanticAi.label')}</Text>
|
||||
return (
|
||||
<Text fontSize="sm">{t('editor.sidebarBlock.zemanticAi.label')}</Text>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ import { BlockCard } from './BlockCard'
|
||||
import { LockedIcon, UnlockedIcon } from '@/components/icons'
|
||||
import { BlockCardOverlay } from './BlockCardOverlay'
|
||||
import { headerHeight } from '../constants'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
|
||||
export const BlocksSideBar = () => {
|
||||
const scopedT = useScopedI18n('editor.sidebarBlocks')
|
||||
const { t } = useTranslate()
|
||||
const { setDraggedBlockType, draggedBlockType } = useBlockDnd()
|
||||
const [position, setPosition] = useState({
|
||||
x: 0,
|
||||
@@ -107,16 +107,16 @@ export const BlocksSideBar = () => {
|
||||
<Tooltip
|
||||
label={
|
||||
isLocked
|
||||
? scopedT('sidebar.unlock.label')
|
||||
: scopedT('sidebar.lock.label')
|
||||
? t('editor.sidebarBlocks.sidebar.unlock.label')
|
||||
: t('editor.sidebarBlocks.sidebar.lock.label')
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
icon={isLocked ? <LockedIcon /> : <UnlockedIcon />}
|
||||
aria-label={
|
||||
isLocked
|
||||
? scopedT('sidebar.icon.unlock.label')
|
||||
: scopedT('sidebar.icon.lock.label')
|
||||
? t('editor.sidebarBlocks.sidebar.icon.unlock.label')
|
||||
: t('editor.sidebarBlocks.sidebar.icon.lock.label')
|
||||
}
|
||||
size="sm"
|
||||
onClick={handleLockClick}
|
||||
@@ -126,7 +126,7 @@ export const BlocksSideBar = () => {
|
||||
|
||||
<Stack>
|
||||
<Text fontSize="sm" fontWeight="semibold">
|
||||
{scopedT('blockType.bubbles.heading')}
|
||||
{t('editor.sidebarBlocks.blockType.bubbles.heading')}
|
||||
</Text>
|
||||
<SimpleGrid columns={2} spacing="3">
|
||||
{Object.values(BubbleBlockType).map((type) => (
|
||||
@@ -137,7 +137,7 @@ export const BlocksSideBar = () => {
|
||||
|
||||
<Stack>
|
||||
<Text fontSize="sm" fontWeight="semibold">
|
||||
{scopedT('blockType.inputs.heading')}
|
||||
{t('editor.sidebarBlocks.blockType.inputs.heading')}
|
||||
</Text>
|
||||
<SimpleGrid columns={2} spacing="3">
|
||||
{Object.values(InputBlockType).map((type) => (
|
||||
@@ -148,7 +148,7 @@ export const BlocksSideBar = () => {
|
||||
|
||||
<Stack>
|
||||
<Text fontSize="sm" fontWeight="semibold">
|
||||
{scopedT('blockType.logic.heading')}
|
||||
{t('editor.sidebarBlocks.blockType.logic.heading')}
|
||||
</Text>
|
||||
<SimpleGrid columns={2} spacing="3">
|
||||
{Object.values(LogicBlockType).map((type) => (
|
||||
@@ -159,7 +159,7 @@ export const BlocksSideBar = () => {
|
||||
|
||||
<Stack>
|
||||
<Text fontSize="sm" fontWeight="semibold">
|
||||
{scopedT('blockType.integrations.heading')}
|
||||
{t('editor.sidebarBlocks.blockType.integrations.heading')}
|
||||
</Text>
|
||||
<SimpleGrid columns={2} spacing="3">
|
||||
{Object.values(IntegrationBlockType).map((type) => (
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react'
|
||||
import React, { useState } from 'react'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
|
||||
type EditableProps = {
|
||||
defaultName: string
|
||||
@@ -16,7 +16,7 @@ export const EditableTypebotName = ({
|
||||
defaultName,
|
||||
onNewName,
|
||||
}: EditableProps) => {
|
||||
const scopedT = useScopedI18n('editor.editableTypebotName')
|
||||
const { t } = useTranslate()
|
||||
const emptyNameBg = useColorModeValue('gray.100', 'gray.700')
|
||||
const [currentName, setCurrentName] = useState(defaultName)
|
||||
|
||||
@@ -27,7 +27,7 @@ export const EditableTypebotName = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip label={scopedT('tooltip.rename.label')}>
|
||||
<Tooltip label={t('editor.editableTypebotName.tooltip.rename.label')}>
|
||||
<Editable
|
||||
value={currentName}
|
||||
onChange={setCurrentName}
|
||||
|
||||
@@ -21,10 +21,10 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect } from 'react'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
|
||||
export const GettingStartedModal = () => {
|
||||
const scopedT = useScopedI18n('editor.gettingStartedModal')
|
||||
const { t } = useTranslate()
|
||||
const { query } = useRouter()
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
|
||||
@@ -40,7 +40,9 @@ export const GettingStartedModal = () => {
|
||||
<ModalCloseButton />
|
||||
<ModalBody as={Stack} spacing="8" py="10">
|
||||
<Stack spacing={4}>
|
||||
<Heading fontSize="xl">{scopedT('editorBasics.heading')}</Heading>
|
||||
<Heading fontSize="xl">
|
||||
{t('editor.gettingStartedModal.editorBasics.heading')}
|
||||
</Heading>
|
||||
<List spacing={4}>
|
||||
<HStack as={ListItem}>
|
||||
<Flex
|
||||
@@ -56,7 +58,9 @@ export const GettingStartedModal = () => {
|
||||
>
|
||||
1
|
||||
</Flex>
|
||||
<Text>{scopedT('editorBasics.list.one.label')}</Text>
|
||||
<Text>
|
||||
{t('editor.gettingStartedModal.editorBasics.list.one.label')}
|
||||
</Text>
|
||||
</HStack>
|
||||
<HStack as={ListItem}>
|
||||
<Flex
|
||||
@@ -72,7 +76,9 @@ export const GettingStartedModal = () => {
|
||||
>
|
||||
2
|
||||
</Flex>
|
||||
<Text>{scopedT('editorBasics.list.two.label')}</Text>
|
||||
<Text>
|
||||
{t('editor.gettingStartedModal.editorBasics.list.two.label')}
|
||||
</Text>
|
||||
</HStack>
|
||||
<HStack as={ListItem}>
|
||||
<Flex
|
||||
@@ -88,7 +94,11 @@ export const GettingStartedModal = () => {
|
||||
>
|
||||
3
|
||||
</Flex>
|
||||
<Text>{scopedT('editorBasics.list.three.label')}</Text>
|
||||
<Text>
|
||||
{t(
|
||||
'editor.gettingStartedModal.editorBasics.list.three.label'
|
||||
)}
|
||||
</Text>
|
||||
</HStack>
|
||||
<HStack as={ListItem}>
|
||||
<Flex
|
||||
@@ -104,15 +114,18 @@ export const GettingStartedModal = () => {
|
||||
>
|
||||
4
|
||||
</Flex>
|
||||
<Text>{scopedT('editorBasics.list.four.label')}</Text>
|
||||
<Text>
|
||||
{t('editor.gettingStartedModal.editorBasics.list.four.label')}
|
||||
</Text>
|
||||
</HStack>
|
||||
</List>
|
||||
</Stack>
|
||||
|
||||
<Text>{scopedT('editorBasics.list.label')}</Text>
|
||||
<Text>{t('editor.gettingStartedModal.editorBasics.list.label')}</Text>
|
||||
<Stack spacing={4}>
|
||||
<Heading fontSize="xl">
|
||||
{scopedT('seeAction.label')} ({`<`} {scopedT('seeAction.time')})
|
||||
{t('editor.gettingStartedModal.seeAction.label')} ({`<`}{' '}
|
||||
{t('editor.gettingStartedModal.seeAction.time')})
|
||||
</Heading>
|
||||
<iframe
|
||||
width="100%"
|
||||
@@ -127,7 +140,7 @@ export const GettingStartedModal = () => {
|
||||
<AccordionItem>
|
||||
<AccordionButton>
|
||||
<Box flex="1" textAlign="left">
|
||||
{scopedT('seeAction.item.label')}
|
||||
{t('editor.gettingStartedModal.seeAction.item.label')}
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
|
||||
@@ -30,10 +30,10 @@ import { RightPanel, useEditor } from '../providers/EditorProvider'
|
||||
import { useTypebot } from '../providers/TypebotProvider'
|
||||
import { SupportBubble } from '@/components/SupportBubble'
|
||||
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
|
||||
export const TypebotHeader = () => {
|
||||
const scopedT = useScopedI18n('editor.headers')
|
||||
const { t } = useTranslate()
|
||||
const router = useRouter()
|
||||
const {
|
||||
typebot,
|
||||
@@ -105,7 +105,7 @@ export const TypebotHeader = () => {
|
||||
variant={router.pathname.includes('/edit') ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
>
|
||||
{scopedT('flowButton.label')}
|
||||
{t('editor.headers.flowButton.label')}
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
@@ -114,7 +114,7 @@ export const TypebotHeader = () => {
|
||||
variant={router.pathname.endsWith('theme') ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
>
|
||||
{scopedT('themeButton.label')}
|
||||
{t('editor.headers.themeButton.label')}
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
@@ -123,7 +123,7 @@ export const TypebotHeader = () => {
|
||||
variant={router.pathname.endsWith('settings') ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
>
|
||||
{scopedT('settingsButton.label')}
|
||||
{t('editor.headers.settingsButton.label')}
|
||||
</Button>
|
||||
<Button
|
||||
as={Link}
|
||||
@@ -132,7 +132,7 @@ export const TypebotHeader = () => {
|
||||
variant={router.pathname.endsWith('share') ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
>
|
||||
{scopedT('shareButton.label')}
|
||||
{t('editor.headers.shareButton.label')}
|
||||
</Button>
|
||||
{isDefined(publishedTypebot) && (
|
||||
<Button
|
||||
@@ -142,7 +142,7 @@ export const TypebotHeader = () => {
|
||||
variant={router.pathname.includes('results') ? 'outline' : 'ghost'}
|
||||
size="sm"
|
||||
>
|
||||
{scopedT('resultsButton.label')}
|
||||
{t('editor.headers.resultsButton.label')}
|
||||
</Button>
|
||||
)}
|
||||
</HStack>
|
||||
@@ -225,21 +225,23 @@ export const TypebotHeader = () => {
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
<Button leftIcon={<BuoyIcon />} onClick={handleHelpClick} size="sm">
|
||||
{scopedT('helpButton.label')}
|
||||
{t('editor.headers.helpButton.label')}
|
||||
</Button>
|
||||
</HStack>
|
||||
{isSavingLoading && (
|
||||
<HStack>
|
||||
<Spinner speed="0.7s" size="sm" color="gray.400" />
|
||||
<Text fontSize="sm" color="gray.400">
|
||||
{scopedT('savingSpinner.label')}
|
||||
{t('editor.headers.savingSpinner.label')}
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
<HStack right="40px" pos="absolute" display={['none', 'flex']}>
|
||||
<CollaborationMenuButton isLoading={isNotDefined(typebot)} />
|
||||
<Flex pos="relative">
|
||||
<CollaborationMenuButton isLoading={isNotDefined(typebot)} />
|
||||
</Flex>
|
||||
{router.pathname.includes('/edit') && isNotDefined(rightPanel) && (
|
||||
<Button
|
||||
colorScheme="gray"
|
||||
@@ -247,7 +249,7 @@ export const TypebotHeader = () => {
|
||||
isLoading={isNotDefined(typebot)}
|
||||
size="sm"
|
||||
>
|
||||
{scopedT('previewButton.label')}
|
||||
{t('editor.headers.previewButton.label')}
|
||||
</Button>
|
||||
)}
|
||||
<PublishButton size="sm" />
|
||||
|
||||
@@ -23,7 +23,6 @@ import { areTypebotsEqual } from '@/features/publish/helpers/areTypebotsEqual'
|
||||
import { isPublished as isPublishedHelper } from '@/features/publish/helpers/isPublished'
|
||||
import { convertPublicTypebotToTypebot } from '@/features/publish/helpers/convertPublicTypebotToTypebot'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import { useScopedI18n } from '@/locales'
|
||||
|
||||
const autoSaveTimeout = 10000
|
||||
|
||||
@@ -80,7 +79,6 @@ export const TypebotProvider = ({
|
||||
children: ReactNode
|
||||
typebotId?: string
|
||||
}) => {
|
||||
const scopedT = useScopedI18n('editor.provider')
|
||||
const { push } = useRouter()
|
||||
const { showToast } = useToast()
|
||||
|
||||
@@ -96,15 +94,11 @@ export const TypebotProvider = ({
|
||||
if (error.data?.httpStatus === 404) {
|
||||
showToast({
|
||||
status: 'info',
|
||||
description: scopedT('messages.getTypebotError.description'),
|
||||
description: "Couldn't find typebot.",
|
||||
})
|
||||
push('/typebots')
|
||||
return
|
||||
}
|
||||
showToast({
|
||||
title: scopedT('messages.getTypebotError.title'),
|
||||
description: error.message,
|
||||
})
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -114,13 +108,6 @@ export const TypebotProvider = ({
|
||||
{ typebotId: typebotId as string },
|
||||
{
|
||||
enabled: isDefined(typebotId),
|
||||
onError: (error) => {
|
||||
if (error.data?.httpStatus === 404) return
|
||||
showToast({
|
||||
title: scopedT('messages.publishedTypebotError.title'),
|
||||
description: error.message,
|
||||
})
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -128,7 +115,7 @@ export const TypebotProvider = ({
|
||||
trpc.typebot.updateTypebot.useMutation({
|
||||
onError: (error) =>
|
||||
showToast({
|
||||
title: scopedT('messages.updateTypebotError.title'),
|
||||
title: 'Error while updating typebot',
|
||||
description: error.message,
|
||||
}),
|
||||
onSuccess: () => {
|
||||
@@ -264,10 +251,7 @@ export const TypebotProvider = ({
|
||||
isPublished,
|
||||
updateTypebot: updateLocalTypebot,
|
||||
restorePublishedTypebot,
|
||||
...groupsActions(
|
||||
setLocalTypebot as SetTypebot,
|
||||
scopedT('groups.copy.title')
|
||||
),
|
||||
...groupsActions(setLocalTypebot as SetTypebot),
|
||||
...blocksAction(setLocalTypebot as SetTypebot),
|
||||
...variablesAction(setLocalTypebot as SetTypebot),
|
||||
...edgesAction(setLocalTypebot as SetTypebot),
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
createBlockDraft,
|
||||
duplicateBlockDraft,
|
||||
} from './blocks'
|
||||
import { isEmpty, parseGroupTitle } from '@typebot.io/lib'
|
||||
import { isEmpty } from '@typebot.io/lib'
|
||||
import { Coordinates } from '@/features/graph/types'
|
||||
|
||||
export type GroupsActions = {
|
||||
@@ -28,10 +28,7 @@ export type GroupsActions = {
|
||||
deleteGroup: (groupIndex: number) => void
|
||||
}
|
||||
|
||||
const groupsActions = (
|
||||
setTypebot: SetTypebot,
|
||||
groupCopyLabel: string
|
||||
): GroupsActions => ({
|
||||
const groupsActions = (setTypebot: SetTypebot): GroupsActions => ({
|
||||
createGroup: ({
|
||||
id,
|
||||
block,
|
||||
@@ -67,11 +64,19 @@ const groupsActions = (
|
||||
const group = typebot.groups[groupIndex]
|
||||
const id = createId()
|
||||
|
||||
const totalGroupsWithSameTitle = typebot.groups.filter(
|
||||
(group) => group.title === group.title
|
||||
).length
|
||||
|
||||
const newGroup: Group = {
|
||||
...group,
|
||||
title: isEmpty(group.title)
|
||||
? ''
|
||||
: `${parseGroupTitle(group.title)} ${groupCopyLabel}`,
|
||||
: `${group.title}${
|
||||
totalGroupsWithSameTitle > 0
|
||||
? ` (${totalGroupsWithSameTitle})`
|
||||
: ''
|
||||
}}`,
|
||||
id,
|
||||
blocks: group.blocks.map((block) => duplicateBlockDraft(id)(block)),
|
||||
graphCoordinates: {
|
||||
|
||||
Reference in New Issue
Block a user