2
0

🌐 Improve i18n collaboration type and timeSince parsing

This commit is contained in:
Baptiste Arnaud
2023-12-29 11:31:31 +01:00
parent 5124373071
commit f26eafd26f
8 changed files with 116 additions and 66 deletions

View File

@ -0,0 +1,46 @@
import { T } from '@tolgee/react'
type Props = {
date: string
}
export const TimeSince = ({ date }: Props) => {
const seconds = Math.floor(
(new Date().getTime() - new Date(date).getTime()) / 1000
)
let interval = seconds / 31536000
if (interval > 1) {
return (
<T keyName="timeSince.years" params={{ count: Math.floor(interval) }} />
)
}
interval = seconds / 2592000
if (interval > 1) {
return (
<T keyName="timeSince.months" params={{ count: Math.floor(interval) }} />
)
}
interval = seconds / 86400
if (interval > 1) {
return (
<T keyName="timeSince.days" params={{ count: Math.floor(interval) }} />
)
}
interval = seconds / 3600
if (interval > 1) {
return (
<T keyName="timeSince.hours" params={{ count: Math.floor(interval) }} />
)
}
interval = seconds / 60
if (interval > 1) {
return (
<T keyName="timeSince.minutes" params={{ count: Math.floor(interval) }} />
)
}
return (
<T keyName="timeSince.seconds" params={{ count: Math.floor(interval) }} />
)
}

View File

@ -23,9 +23,9 @@ import { byId, isDefined } from '@typebot.io/lib'
import { CreateTokenModal } from './CreateTokenModal'
import { useApiTokens } from '../hooks/useApiTokens'
import { ApiTokenFromServer } from '../types'
import { parseTimeSince } from '@/helpers/parseTimeSince'
import { deleteApiTokenQuery } from '../queries/deleteApiTokenQuery'
import { T, useTranslate } from '@tolgee/react'
import { TimeSince } from '@/components/TimeSince'
type Props = { user: User }
@ -84,7 +84,9 @@ export const ApiTokensList = ({ user }: Props) => {
{apiTokens?.map((token) => (
<Tr key={token.id}>
<Td>{token.name}</Td>
<Td>{parseTimeSince(t, token.createdAt)}</Td>
<Td>
<TimeSince date={token.createdAt} />
</Td>
<Td>
<Button
size="xs"

View File

@ -28,7 +28,8 @@ import { deleteInvitationQuery } from '../queries/deleteInvitationQuery'
import { updateCollaboratorQuery } from '../queries/updateCollaboratorQuery'
import { deleteCollaboratorQuery } from '../queries/deleteCollaboratorQuery'
import { sendInvitationQuery } from '../queries/sendInvitationQuery'
import { TFnType, useTranslate } from '@tolgee/react'
import { useTranslate } from '@tolgee/react'
import { ReadableCollaborationType } from './ReadableCollaborationType'
export const CollaborationList = () => {
const { currentRole, workspace } = useWorkspace()
@ -180,10 +181,7 @@ export const CollaborationList = () => {
</Text>
</HStack>
<Tag flexShrink={0}>
{convertCollaborationTypeEnumToReadable(
t,
CollaborationType.FULL_ACCESS
)}
<ReadableCollaborationType type={CollaborationType.FULL_ACCESS} />
</Tag>
</Flex>
)}
@ -232,43 +230,25 @@ const CollaborationTypeMenuButton = ({
}: {
type: CollaborationType
onChange: (type: CollaborationType) => void
}) => {
const { t } = useTranslate()
return (
<Menu placement="bottom-end">
<MenuButton
flexShrink={0}
size="sm"
as={Button}
rightIcon={<ChevronLeftIcon transform={'rotate(-90deg)'} />}
>
{convertCollaborationTypeEnumToReadable(t, type)}
</MenuButton>
<MenuList minW={0}>
<Stack maxH={'35vh'} overflowY="scroll" spacing="0">
<MenuItem onClick={() => onChange(CollaborationType.READ)}>
{convertCollaborationTypeEnumToReadable(t, CollaborationType.READ)}
</MenuItem>
<MenuItem onClick={() => onChange(CollaborationType.WRITE)}>
{convertCollaborationTypeEnumToReadable(t, CollaborationType.WRITE)}
</MenuItem>
</Stack>
</MenuList>
</Menu>
)
}
export const convertCollaborationTypeEnumToReadable = (
t: TFnType,
type: CollaborationType
) => {
switch (type) {
case CollaborationType.READ:
return t('collaboration.roles.view.label')
case CollaborationType.WRITE:
return t('collaboration.roles.edit.label')
case CollaborationType.FULL_ACCESS:
return t('collaboration.roles.full.label')
}
}
}) => (
<Menu placement="bottom-end">
<MenuButton
flexShrink={0}
size="sm"
as={Button}
rightIcon={<ChevronLeftIcon transform={'rotate(-90deg)'} />}
>
<ReadableCollaborationType type={type} />
</MenuButton>
<MenuList minW={0}>
<Stack maxH={'35vh'} overflowY="scroll" spacing="0">
<MenuItem onClick={() => onChange(CollaborationType.READ)}>
<ReadableCollaborationType type={CollaborationType.READ} />
</MenuItem>
<MenuItem onClick={() => onChange(CollaborationType.WRITE)}>
<ReadableCollaborationType type={CollaborationType.WRITE} />
</MenuItem>
</Stack>
</MenuList>
</Menu>
)

View File

@ -12,8 +12,8 @@ import {
} from '@chakra-ui/react'
import { CollaborationType } from '@typebot.io/prisma'
import React from 'react'
import { convertCollaborationTypeEnumToReadable } from './CollaborationList'
import { useTranslate } from '@tolgee/react'
import { ReadableCollaborationType } from './ReadableCollaborationType'
type Props = {
image?: string
@ -50,16 +50,16 @@ export const CollaboratorItem = ({
name={name}
image={image}
isGuest={isGuest}
tag={convertCollaborationTypeEnumToReadable(t, type)}
type={type}
/>
</MenuButton>
{isOwner && (
<MenuList shadow="lg">
<MenuItem onClick={handleEditClick}>
{convertCollaborationTypeEnumToReadable(t, CollaborationType.WRITE)}
<ReadableCollaborationType type={CollaborationType.WRITE} />
</MenuItem>
<MenuItem onClick={handleViewClick}>
{convertCollaborationTypeEnumToReadable(t, CollaborationType.READ)}
<ReadableCollaborationType type={CollaborationType.READ} />
</MenuItem>
<MenuItem color="red.500" onClick={onDeleteClick}>
{t('remove')}
@ -72,13 +72,13 @@ export const CollaboratorItem = ({
export const CollaboratorIdentityContent = ({
name,
tag,
type,
isGuest = false,
image,
email,
}: {
name?: string
tag?: string
type: CollaborationType
image?: string
isGuest?: boolean
email: string
@ -106,7 +106,9 @@ export const CollaboratorIdentityContent = ({
</HStack>
<HStack flexShrink={0}>
{isGuest && <Tag color="gray.400">{t('pending')}</Tag>}
<Tag>{tag}</Tag>
<Tag>
<ReadableCollaborationType type={type} />
</Tag>
</HStack>
</HStack>
)

View File

@ -0,0 +1,14 @@
import { T } from '@tolgee/react'
import { CollaborationType } from '@typebot.io/prisma'
type Props = { type: CollaborationType }
export const ReadableCollaborationType = ({ type }: Props) => {
switch (type) {
case CollaborationType.READ:
return <T keyName="collaboration.roles.view.label" />
case CollaborationType.WRITE:
return <T keyName="collaboration.roles.edit.label" />
case CollaborationType.FULL_ACCESS:
return <T keyName="collaboration.roles.full.label" />
}
}

View File

@ -24,7 +24,6 @@ import { useRouter } from 'next/router'
import { isNotDefined } from '@typebot.io/lib'
import { ChangePlanModal } from '@/features/billing/components/ChangePlanModal'
import { isFreePlan } from '@/features/billing/helpers/isFreePlan'
import { parseTimeSince } from '@/helpers/parseTimeSince'
import { T, useTranslate } from '@tolgee/react'
import { trpc } from '@/lib/trpc'
import { useToast } from '@/hooks/useToast'
@ -33,6 +32,7 @@ import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/const
import { ConfirmModal } from '@/components/ConfirmModal'
import { TextLink } from '@/components/TextLink'
import { useUser } from '@/features/account/hooks/useUser'
import { useTimeSince } from '@/hooks/useTimeSince'
type Props = ButtonProps & {
isMoreMenuDisabled?: boolean
@ -61,6 +61,9 @@ export const PublishButton = ({
save,
publishedTypebotVersion,
} = useTypebot()
const timeSinceLastPublish = useTimeSince(
publishedTypebot?.updatedAt.toString()
)
const { showToast } = useToast()
const {
@ -181,14 +184,14 @@ export const PublishButton = ({
label={
<Stack>
<Text>{t('publishButton.tooltip.nonPublishedChanges.label')}</Text>
{publishedTypebot ? (
{timeSinceLastPublish ? (
<Text fontStyle="italic">
{t('publishButton.tooltip.publishedVersion.from.label', {
timeSince: parseTimeSince(
t,
publishedTypebot.updatedAt.toString()
),
})}
<T
keyName="publishButton.tooltip.publishedVersion.from.label"
params={{
timeSince: timeSinceLastPublish,
}}
/>
</Text>
) : null}
</Stack>

View File

@ -1,6 +1,10 @@
import { TFnType } from '@tolgee/react'
import { useTranslate } from '@tolgee/react'
export const useTimeSince = (date?: string) => {
const { t } = useTranslate()
if (!date) return
export const parseTimeSince = (t: TFnType, date: string) => {
const seconds = Math.floor(
(new Date().getTime() - new Date(date).getTime()) / 1000
)

View File

@ -255,7 +255,7 @@
"publishButton.published.label": "Published",
"publishButton.tooltip.nonPublishedChanges.label": "There are non published changes.",
"publishButton.tooltip.publishedVersion.ago.label": "ago",
"publishButton.tooltip.publishedVersion.from.label": "Published version from {timeSince}.",
"publishButton.tooltip.publishedVersion.from.label": "Published version from <timeSince>test</timeSince>.",
"remove": "Remove",
"share.button.label": "Share",
"share.button.popover.ariaLabel": "Open share popover",
@ -310,7 +310,6 @@
"templates.modal.product.userOnboarding.description": "A bot that asks for new user information before he start using your product.",
"templates.modal.product.userOnboarding.name": "User Onboarding",
"templates.modal.useTemplateButton.label": "Use this template",
"templates.modal.useTemplateButton.labelt": "Use this templatet",
"timeSince.days": "{count}d ago",
"timeSince.hours": "{count}h ago",
"timeSince.minutes": "{count}m ago",