Skip to content

Commit

Permalink
🌐 Translate workspace (#528)
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed May 31, 2023
1 parent 6651c85 commit b2ea8fc
Show file tree
Hide file tree
Showing 18 changed files with 3,035 additions and 3,500 deletions.
2 changes: 1 addition & 1 deletion apps/builder/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const nextConfig = {
],
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'pt'],
locales: ['en', 'fr', 'pt', 'de'],
},
experimental: {
outputFileTracingRoot: path.join(__dirname, '../../'),
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
"@trpc/server": "10.27.3",
"@typebot.io/emails": "workspace:*",
"@typebot.io/js": "workspace:*",
"@typebot.io/next-international": "0.3.8",
"@typebot.io/react": "workspace:*",
"@udecode/plate-basic-marks": "21.1.5",
"@udecode/plate-common": "^21.1.5",
Expand Down Expand Up @@ -71,6 +70,7 @@
"minio": "7.1.1",
"next": "13.4.3",
"next-auth": "4.22.1",
"next-international": "^0.4.1",
"nextjs-cors": "^2.1.2",
"nodemailer": "6.9.2",
"nprogress": "0.2.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
import { Stack, Heading, useColorMode } from '@chakra-ui/react'
import {
Stack,
Heading,
useColorMode,
Menu,
MenuButton,
MenuList,
MenuItem,
Button,
HStack,
} from '@chakra-ui/react'
import { GraphNavigation } from '@typebot.io/prisma'
import React, { useEffect } from 'react'
import { GraphNavigationRadioGroup } from './GraphNavigationRadioGroup'
import { AppearanceRadioGroup } from './AppearanceRadioGroup'
import { useUser } from '../hooks/useUser'
import { useScopedI18n } from '@/locales'
import { useChangeLocale, useCurrentLocale, useScopedI18n } from '@/locales'
import { ChevronDownIcon } from '@/components/icons'
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'

const localeHumanReadable = {
en: 'English',
fr: 'Français',
de: 'Deutsch',
pt: 'Português',
} as const

export const UserPreferencesForm = () => {
const scopedT = useScopedI18n('account.preferences')
const { colorMode, setColorMode } = useColorMode()
const { user, updateUser } = useUser()
const changeLocale = useChangeLocale()
const currentLocale = useCurrentLocale()

useEffect(() => {
if (!user?.graphNavigation)
Expand All @@ -25,8 +46,40 @@ export const UserPreferencesForm = () => {
updateUser({ preferredAppAppearance: value })
}

const updateLocale = (locale: keyof typeof localeHumanReadable) => () => {
changeLocale(locale)
document.cookie = `NEXT_LOCALE=${locale}; path=/; max-age=31536000`
}

return (
<Stack spacing={12}>
<HStack spacing={4}>
<Heading size="md">{scopedT('language.heading')}</Heading>
<Menu>
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
{localeHumanReadable[currentLocale]}
</MenuButton>
<MenuList>
{Object.keys(localeHumanReadable).map((locale) => (
<MenuItem
key={locale}
onClick={updateLocale(
locale as keyof typeof localeHumanReadable
)}
>
{
localeHumanReadable[
locale as keyof typeof localeHumanReadable
]
}
</MenuItem>
))}
</MenuList>
</Menu>
{currentLocale !== 'en' && (
<MoreInfoTooltip>{scopedT('language.tooltip')}</MoreInfoTooltip>
)}
</HStack>
<Stack spacing={6}>
<Heading size="md">{scopedT('graphNavigation.heading')}</Heading>
<GraphNavigationRadioGroup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { WorkspaceInvitation, WorkspaceRole } from '@typebot.io/prisma'
import { FormEvent, useState } from 'react'
import { Member } from '../types'
import { sendInvitationQuery } from '../queries/sendInvitationQuery'
import { useScopedI18n } from '@/locales'

type Props = {
workspaceId: string
Expand All @@ -28,6 +29,7 @@ export const AddMemberForm = ({
isLoading,
isLocked,
}: Props) => {
const scopedT = useScopedI18n('workspace.membersList')
const [invitationEmail, setInvitationEmail] = useState('')
const [invitationRole, setInvitationRole] = useState<WorkspaceRole>(
WorkspaceRole.MEMBER
Expand All @@ -52,7 +54,7 @@ export const AddMemberForm = ({
return (
<HStack as="form" onSubmit={handleInvitationSubmit}>
<Input
placeholder="colleague@company.com"
placeholder={scopedT('inviteInput.placeholder')}
name="inviteEmail"
value={invitationEmail}
onChange={(e) => setInvitationEmail(e.target.value)}
Expand All @@ -73,7 +75,7 @@ export const AddMemberForm = ({
type="submit"
isDisabled={isLoading || isLocked || invitationEmail === ''}
>
Invite
{scopedT('inviteButton.label')}
</Button>
</HStack>
)
Expand Down
60 changes: 33 additions & 27 deletions apps/builder/src/features/workspace/components/MemberItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { WorkspaceRole } from '@typebot.io/prisma'
import React from 'react'
import { convertWorkspaceRoleToReadable } from './AddMemberForm'
import { useI18n } from '@/locales'

type Props = {
image?: string
Expand All @@ -37,6 +38,7 @@ export const MemberItem = ({
onDeleteClick,
onSelectNewRole,
}: Props) => {
const t = useI18n()
const handleAdminClick = () => onSelectNewRole(WorkspaceRole.ADMIN)
const handleMemberClick = () => onSelectNewRole(WorkspaceRole.MEMBER)

Expand Down Expand Up @@ -65,7 +67,7 @@ export const MemberItem = ({
{convertWorkspaceRoleToReadable(WorkspaceRole.MEMBER)}
</MenuItem>
<MenuItem color="red.500" onClick={onDeleteClick}>
Remove
{t('remove')}
</MenuItem>
</MenuList>
)}
Expand All @@ -85,32 +87,36 @@ export const MemberIdentityContent = ({
image?: string
isGuest?: boolean
email: string
}) => (
<HStack justifyContent="space-between" maxW="full" p="2">
<HStack minW={0} spacing="4">
<Avatar name={name} src={image} size="sm" />
<Stack spacing={0} minW="0">
{name && (
<Text textAlign="left" fontSize="15px">
{name}
}) => {
const t = useI18n()

return (
<HStack justifyContent="space-between" maxW="full" p="2">
<HStack minW={0} spacing="4">
<Avatar name={name} src={image} size="sm" />
<Stack spacing={0} minW="0">
{name && (
<Text textAlign="left" fontSize="15px">
{name}
</Text>
)}
<Text
color="gray.500"
fontSize={name ? '14px' : 'inherit'}
noOfLines={1}
>
{email}
</Text>
</Stack>
</HStack>
<HStack flexShrink={0}>
{isGuest && (
<Tag color="gray.400" data-testid="tag">
{t('pending')}
</Tag>
)}
<Text
color="gray.500"
fontSize={name ? '14px' : 'inherit'}
noOfLines={1}
>
{email}
</Text>
</Stack>
</HStack>
<HStack flexShrink={0}>
{isGuest && (
<Tag color="gray.400" data-testid="tag">
Pending
</Tag>
)}
<Tag data-testid="tag">{tag}</Tag>
<Tag data-testid="tag">{tag}</Tag>
</HStack>
</HStack>
</HStack>
)
)
}
11 changes: 4 additions & 7 deletions apps/builder/src/features/workspace/components/MembersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import { updateInvitationQuery } from '../queries/updateInvitationQuery'
import { updateMemberQuery } from '../queries/updateMemberQuery'
import { Member } from '../types'
import { useWorkspace } from '../WorkspaceProvider'
import { useScopedI18n } from '@/locales'

export const MembersList = () => {
const scopedT = useScopedI18n('workspace.membersList')
const { user } = useUser()
const { workspace, currentRole } = useWorkspace()
const { members, invitations, isLoading, mutate } = useMembers({
Expand Down Expand Up @@ -102,16 +104,11 @@ export const MembersList = () => {
return (
<Stack w="full" spacing={3}>
{!canInviteNewMember && (
<UnlockPlanAlertInfo
contentLabel={`
Upgrade your plan to work with more team members, and unlock awesome
power features 🚀
`}
/>
<UnlockPlanAlertInfo contentLabel={scopedT('unlockBanner.label')} />
)}
{isDefined(seatsLimit) && (
<Heading fontSize="2xl">
Members{' '}
{scopedT('title')}{' '}
{seatsLimit === -1 ? '' : `(${currentMembersCount}/${seatsLimit})`}
</Heading>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@/components/icons'
import { PlanTag } from '@/features/billing/components/PlanTag'
import { trpc } from '@/lib/trpc'
import { useScopedI18n } from '@/locales'
import {
Menu,
MenuButton,
Expand All @@ -31,6 +32,7 @@ export const WorkspaceDropdown = ({
onLogoutClick,
onCreateNewWorkspaceClick,
}: Props) => {
const scopedT = useScopedI18n('workspace.dropdown')
const { data } = trpc.workspace.listWorkspaces.useQuery()

const workspaces = data?.workspaces ?? []
Expand Down Expand Up @@ -70,14 +72,14 @@ export const WorkspaceDropdown = ({
</MenuItem>
))}
<MenuItem onClick={onCreateNewWorkspaceClick} icon={<PlusIcon />}>
New workspace
{scopedT('newButton.label')}
</MenuItem>
<MenuItem
onClick={onLogoutClick}
icon={<LogOutIcon />}
color="orange.500"
>
Log out
{scopedT('logoutButton.label')}
</MenuItem>
</MenuList>
</Menu>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import React from 'react'
import { EditableEmojiOrImageIcon } from '@/components/EditableEmojiOrImageIcon'
import { useWorkspace } from '../WorkspaceProvider'
import { TextInput } from '@/components/inputs'
import { useScopedI18n } from '@/locales'

export const WorkspaceSettingsForm = ({ onClose }: { onClose: () => void }) => {
const scopedT = useScopedI18n('workspace.settings')
const { workspace, workspaces, updateWorkspace, deleteCurrentWorkspace } =
useWorkspace()

Expand All @@ -34,7 +36,7 @@ export const WorkspaceSettingsForm = ({ onClose }: { onClose: () => void }) => {
return (
<Stack spacing="6" w="full">
<FormControl>
<FormLabel>Icon</FormLabel>
<FormLabel>{scopedT('icon.title')}</FormLabel>
<Flex>
{workspace && (
<EditableEmojiOrImageIcon
Expand All @@ -48,7 +50,7 @@ export const WorkspaceSettingsForm = ({ onClose }: { onClose: () => void }) => {
</FormControl>
{workspace && (
<TextInput
label="Name:"
label={scopedT('name.label')}
withVariableButton={false}
defaultValue={workspace?.name}
onChange={handleNameChange}
Expand All @@ -71,20 +73,22 @@ const DeleteWorkspaceButton = ({
workspaceName: string
onConfirm: () => Promise<void>
}) => {
const scopedT = useScopedI18n('workspace.settings')
const { isOpen, onOpen, onClose } = useDisclosure()
return (
<>
<Button colorScheme="red" variant="outline" onClick={onOpen}>
Delete workspace
{scopedT('deleteButton.label')}
</Button>
<ConfirmModal
isOpen={isOpen}
onConfirm={onConfirm}
onClose={onClose}
message={
<Text>
Are you sure you want to delete {workspaceName} workspace? All its
folders, typebots and results will be deleted forever.
{scopedT('deleteButton.confirmMessage', {
workspaceName,
})}
</Text>
}
confirmButtonLabel="Delete"
Expand Down

4 comments on commit b2ea8fc

@vercel
Copy link

@vercel vercel bot commented on b2ea8fc May 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs-git-main-typebot-io.vercel.app
docs-typebot-io.vercel.app
docs.typebot.io

@vercel
Copy link

@vercel vercel bot commented on b2ea8fc May 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

bii.bj
1stop.au
wasap.nl
yobot.me
klujo.com
me.cr8.ai
wachat.io
bot.devitus.com
bot.jesopizz.it
bot.reeplai.com
bot.renovato.it
bot.scayver.com
bot.tc-mail.com
chat.lalmon.com
chat.sureb4.com
eventhub.com.au
fitness.riku.ai
games.klujo.com
proscale.com.br
sakuranembro.it
sellmycarbr.com
typebot.aloe.do
bot.contakit.com
bot.piccinato.co
bot.sv-energy.it
botc.ceox.com.br
clo.closeer.work
cockroach.cr8.ai
faqs.nigerias.io
form.syncwin.com
haymanevents.com
kw.wpwakanda.com
myrentalhost.com
stan.vselise.com
start.taxtree.io
typebot.aloe.bot
voicehelp.cr8.ai
zap.fundviser.in
app.bouclidom.com
app.chatforms.net
bot.aldoemaria.it
bot.hostnation.de
bot.maitempah.com
bot.phuonghub.com
bot.reviewzer.com
bot.rihabilita.it
bot.uluhub.com.br
cares.urlabout.me
chat.gaswadern.de
chat.gniorder.com
chat.rojie.online
fmm.wpwakanda.com
footballmeetup.ie
gentleman-shop.fr
k1.kandabrand.com
register.algorithmpress.com
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
bot.seidibergamoseanchetu.it
desabafe.sergiolimajr.com.br
download.venturemarketing.in
open.campus.aalen.university
piazzatorre.barrettamario.it
poll.mosaicohairboutique.com
type.cookieacademyonline.com
upload.atlasoutfittersk9.com
bot.brigadeirosemdrama.com.br
tuttirecepcao.fratucci.com.br
forms.escoladeautomacao.com.br
onboarding.libertydreamcare.ie
recepcao.tutti.fratucci.com.br
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
elevateyourmind.groovepages.com
viewer-v2-typebot-io.vercel.app
yourfeedback.comebackreward.com
baleia.testeeventos.progenbr.com
bot.cabin-rentals-of-georgia.net
open.campus.bot.aalen.university
sondaggio.mosaicohairboutique.it
baleia.testegabinete.progenbr.com
gerador.verificadordehospedes.com
personal-trainer.barrettamario.it
sondaggio.mosaicohairboutique.com
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
bot.studiotecnicoimmobiliaremerelli.it
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
gabinete.baleia.formulario.progenbr.com
chrome-os-inquiry-system.itschromeos.com
viewer-v2-git-main-typebot-io.vercel.app
main-menu-for-itschromeos.itschromeos.com

@vercel
Copy link

@vercel vercel bot commented on b2ea8fc May 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-git-main-typebot-io.vercel.app
builder-v2-typebot-io.vercel.app
app.typebot.io

@vercel
Copy link

@vercel vercel bot commented on b2ea8fc May 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.