Add more translations (#9707)

As per title
This commit is contained in:
Félix Malfait
2025-01-17 12:50:28 +01:00
committed by GitHub
parent 55feeaeef1
commit f38a25412e
43 changed files with 2895 additions and 651 deletions

View File

@ -1,5 +1,6 @@
import { SignInBackgroundMockPage } from '@/sign-in-background-mock/components/SignInBackgroundMockPage';
import { AppPath } from '@/types/AppPath';
import { Trans, useLingui } from '@lingui/react/macro';
import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
import styled from '@emotion/styled';
@ -33,24 +34,28 @@ const StyledButtonContainer = styled.div`
`;
export const NotFound = () => {
const { t } = useLingui();
return (
<>
<PageTitle title="Page Not Found | Twenty" />
<PageTitle title={t`Page Not Found | Twenty`} />
<StyledBackDrop>
<AnimatedPlaceholderErrorContainer>
<AnimatedPlaceholder type="error404" />
<AnimatedPlaceholderEmptyTextContainer>
<AnimatedPlaceholderErrorTitle>
Off the beaten path
<Trans>Off the beaten path</Trans>
</AnimatedPlaceholderErrorTitle>
<AnimatedPlaceholderErrorSubTitle>
The page you're seeking is either gone or never was. Let's get you
back on track
<Trans>
The page you're seeking is either gone or never was. Let's get
you back on track
</Trans>
</AnimatedPlaceholderErrorSubTitle>
</AnimatedPlaceholderEmptyTextContainer>
<StyledButtonContainer>
<UndecoratedLink to={AppPath.Index}>
<MainButton title="Back to content" fullWidth />
<MainButton title={t`Back to content`} fullWidth />
</UndecoratedLink>
</StyledButtonContainer>
</AnimatedPlaceholderErrorContainer>

View File

@ -11,6 +11,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { useCallback } from 'react';
import {
Controller,
@ -22,7 +23,6 @@ import { useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import {
ActionLink,
AnimatedTranslation,
IconCopy,
LightButton,
MainButton,
@ -68,6 +68,7 @@ const validationSchema = z.object({
type FormInput = z.infer<typeof validationSchema>;
export const InviteTeam = () => {
const { t } = useLingui();
const theme = useTheme();
const { enqueueSnackBar } = useSnackBar();
const { sendInvitation } = useCreateWorkspaceInvitation();
@ -123,7 +124,7 @@ export const InviteTeam = () => {
if (isDefined(currentWorkspace?.inviteHash)) {
const inviteLink = `${window.location.origin}/invite/${currentWorkspace?.inviteHash}`;
navigator.clipboard.writeText(inviteLink);
enqueueSnackBar('Link copied to clipboard', {
enqueueSnackBar(t`Link copied to clipboard`, {
variant: SnackBarVariant.Success,
icon: <IconCopy size={theme.icon.size.md} />,
duration: 2000,
@ -148,13 +149,13 @@ export const InviteTeam = () => {
throw result.errors;
}
if (emails.length > 0) {
enqueueSnackBar('Invite link sent to email addresses', {
enqueueSnackBar(t`Invite link sent to email addresses`, {
variant: SnackBarVariant.Success,
duration: 2000,
});
}
},
[enqueueSnackBar, sendInvitation, setNextOnboardingStatus],
[enqueueSnackBar, sendInvitation, setNextOnboardingStatus, t],
);
const handleSkip = async () => {
@ -176,12 +177,14 @@ export const InviteTeam = () => {
return (
<>
<Title noMarginTop>Invite your team</Title>
<Title>
<Trans>Invite your team</Trans>
</Title>
<SubTitle>
Get the most out of your workspace by inviting your team.
<Trans>Get the most out of your workspace by inviting your team.</Trans>
</SubTitle>
<StyledAnimatedContainer>
{fields.map((_field, index) => (
{fields.map((field, index) => (
<Controller
key={index}
name={`emails.${index}.email`}
@ -190,28 +193,28 @@ export const InviteTeam = () => {
field: { onChange, onBlur, value },
fieldState: { error },
}) => (
<AnimatedTranslation>
<TextInputV2
autoFocus={index === 0}
type="email"
value={value}
placeholder={getPlaceholder(index)}
onBlur={onBlur}
error={error?.message}
onChange={onChange}
noErrorHelper
fullWidth
/>
</AnimatedTranslation>
<TextInputV2
autoFocus={index === 0}
type="email"
value={value}
placeholder={getPlaceholder(index)}
onBlur={onBlur}
error={error?.message}
onChange={onChange}
noErrorHelper
fullWidth
/>
)}
/>
))}
{isDefined(currentWorkspace?.inviteHash) && (
<>
<SeparatorLineText>Or</SeparatorLineText>
<SeparatorLineText>
<Trans>or</Trans>
</SeparatorLineText>
<StyledActionLinkContainer>
<LightButton
title="Copy invitation link"
title={t`Copy invitation link`}
accent="tertiary"
onClick={copyInviteLink}
Icon={IconCopy}
@ -222,14 +225,16 @@ export const InviteTeam = () => {
</StyledAnimatedContainer>
<StyledButtonContainer>
<MainButton
title="Finish"
title={t`Continue`}
disabled={!isValid || isSubmitting}
onClick={handleSubmit(onSubmit)}
fullWidth
/>
</StyledButtonContainer>
<StyledActionSkipLinkContainer>
<ActionLink onClick={handleSkip}>Skip</ActionLink>
<ActionLink onClick={handleSkip}>
<Trans>Skip</Trans>
</ActionLink>
</StyledActionSkipLinkContainer>
</>
);

View File

@ -1,4 +1,5 @@
import styled from '@emotion/styled';
import { Trans, useLingui } from '@lingui/react/macro';
import React, { useEffect, useState } from 'react';
import rehypeStringify from 'rehype-stringify';
import remarkParse from 'remark-parse';
@ -79,6 +80,7 @@ const StyledReleaseDate = styled.span`
`;
export const Releases = () => {
const { t } = useLingui();
const [releases, setReleases] = useState<ReleaseNote[]>([]);
useEffect(() => {
@ -106,15 +108,13 @@ export const Releases = () => {
return (
<SubMenuTopBarContainer
title="Releases"
title={t`Releases`}
links={[
{
children: 'Others',
href: getSettingsPagePath(SettingsPath.Releases),
},
{
children: 'Releases',
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: <Trans>Releases</Trans> },
]}
>
<SettingsPageContainer>

View File

@ -1,4 +1,4 @@
import { useLingui } from '@lingui/react/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { useState } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import {
@ -129,10 +129,10 @@ export const SettingsBilling = () => {
title={t`Billing`}
links={[
{
children: t`Workspace`,
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: t`Billing` },
{ children: <Trans>Billing</Trans> },
]}
>
<SettingsPageContainer>
@ -186,7 +186,10 @@ export const SettingsBilling = () => {
isOpen={isSwitchingIntervalModalOpen}
setIsOpen={setIsSwitchingIntervalModalOpen}
title={t`Switch billing ${to}`}
subtitle={t`Are you sure that you want to change your billing interval? ${impact}`}
subtitle={
t`Are you sure that you want to change your billing interval?` +
` ${impact}`
}
onConfirmClick={switchInterval}
deleteButtonText={t`Change ${to}`}
confirmButtonAccent={'blue'}

View File

@ -1,4 +1,4 @@
import { useLingui } from '@lingui/react/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { H2Title, Section } from 'twenty-ui';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
@ -19,10 +19,10 @@ export const SettingsProfile = () => {
title={t`Profile`}
links={[
{
children: t`User`,
children: <Trans>User</Trans>,
href: getSettingsPagePath(SettingsPath.ProfilePage),
},
{ children: t`Profile` },
{ children: <Trans>Profile</Trans> },
]}
>
<SettingsPageContainer>

View File

@ -1,5 +1,6 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { Trans, useLingui } from '@lingui/react/macro';
import { isNonEmptyArray } from '@sniptt/guards';
import { useState } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
@ -70,6 +71,7 @@ const StyledTextContainerWithEllipsis = styled.div`
`;
export const SettingsWorkspaceMembers = () => {
const { t } = useLingui();
const { enqueueSnackBar } = useSnackBar();
const theme = useTheme();
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false);
@ -138,13 +140,13 @@ export const SettingsWorkspaceMembers = () => {
return (
<SubMenuTopBarContainer
title="Members"
title={t`Members`}
links={[
{
children: 'Workspace',
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: 'Members' },
{ children: <Trans>Members</Trans> },
]}
>
<SettingsPageContainer>
@ -152,8 +154,8 @@ export const SettingsWorkspaceMembers = () => {
currentWorkspace?.isPublicInviteLinkEnabled && (
<Section>
<H2Title
title="Invite by link"
description="Share this link to invite users to join your workspace"
title={t`Invite by link`}
description={t`Share this link to invite users to join your workspace`}
/>
<WorkspaceInviteLink
inviteLink={`${window.location.origin}/invite/${currentWorkspace?.inviteHash}`}
@ -162,8 +164,8 @@ export const SettingsWorkspaceMembers = () => {
)}
<Section>
<H2Title
title="Manage Members"
description="Manage the members of your space here"
title={t`Manage Members`}
description={t`Manage the members of your space here`}
/>
<Table>
<StyledTableHeaderRow>
@ -171,8 +173,12 @@ export const SettingsWorkspaceMembers = () => {
gridAutoColumns="150px 1fr 1fr"
mobileGridAutoColumns="100px 1fr 1fr"
>
<TableHeader>Name</TableHeader>
<TableHeader>Email</TableHeader>
<TableHeader>
<Trans>Name</Trans>
</TableHeader>
<TableHeader>
<Trans>Email</Trans>
</TableHeader>
<TableHeader align={'right'}></TableHeader>
</TableRow>
</StyledTableHeaderRow>
@ -235,8 +241,8 @@ export const SettingsWorkspaceMembers = () => {
</Section>
<Section>
<H2Title
title="Invite by email"
description="Send an invite email to your team"
title={t`Invite by email`}
description={t`Send an invite email to your team`}
/>
<WorkspaceInviteTeam />
{isNonEmptyArray(workspaceInvitations) && (
@ -246,8 +252,12 @@ export const SettingsWorkspaceMembers = () => {
gridAutoColumns="150px 1fr 1fr"
mobileGridAutoColumns="100px 1fr 1fr"
>
<TableHeader>Email</TableHeader>
<TableHeader align={'right'}>Expires in</TableHeader>
<TableHeader>
<Trans>Email</Trans>
</TableHeader>
<TableHeader align={'right'}>
<Trans>Expires in</Trans>
</TableHeader>
<TableHeader></TableHeader>
</TableRow>
</StyledTableHeaderRow>
@ -308,18 +318,18 @@ export const SettingsWorkspaceMembers = () => {
<ConfirmationModal
isOpen={isConfirmationModalOpen}
setIsOpen={setIsConfirmationModalOpen}
title="Account Deletion"
title={t`Account Deletion`}
subtitle={
<>
<Trans>
This action cannot be undone. This will permanently delete this user
and remove them from all their assignements.
</>
</Trans>
}
onConfirmClick={() =>
workspaceMemberToDelete &&
handleRemoveWorkspaceMember(workspaceMemberToDelete)
}
deleteButtonText="Delete account"
deleteButtonText={t`Delete account`}
/>
</SubMenuTopBarContainer>
);

View File

@ -3,22 +3,25 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { Trans, useLingui } from '@lingui/react/macro';
import { Section } from 'twenty-ui';
export const SettingsAccountsCalendars = () => {
const { t } = useLingui();
return (
<SubMenuTopBarContainer
title="Calendars"
title={t`Calendars`}
links={[
{
children: 'User',
children: <Trans>User</Trans>,
href: getSettingsPagePath(SettingsPath.ProfilePage),
},
{
children: 'Accounts',
children: <Trans>Accounts</Trans>,
href: getSettingsPagePath(SettingsPath.Accounts),
},
{ children: 'Calendars' },
{ children: <Trans>Calendars</Trans> },
]}
>
<SettingsPageContainer>

View File

@ -8,22 +8,24 @@ import { SettingsReadDocumentationButton } from '@/settings/developers/component
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { Trans, useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { Section } from 'twenty-ui';
const REVERT_PUBLIC_KEY = 'pk_live_a87fee8c-28c7-494f-99a3-996ff89f9918';
export const SettingsCRMMigration = () => {
const { t } = useLingui();
const currentWorkspace = useRecoilValue(currentWorkspaceState);
return (
<SubMenuTopBarContainer
title="Migrate"
title={t`Migrate`}
links={[
{
children: 'Workspace',
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: 'Migrate' },
{ children: <Trans>Migrate</Trans> },
]}
actionButton={<SettingsReadDocumentationButton />}
>

View File

@ -1,5 +1,5 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useLingui } from '@lingui/react/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { H2Title, Section } from 'twenty-ui';
@ -67,14 +67,14 @@ export const SettingsNewObject = () => {
title={t`New Object`}
links={[
{
children: t`Workspace`,
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{
children: t`Objects`,
children: <Trans>Objects</Trans>,
href: getSettingsPagePath(SettingsPath.Objects),
},
{ children: t`New` },
{ children: <Trans>New</Trans> },
]}
>
<SettingsPageContainer>

View File

@ -12,6 +12,7 @@ import { TableSection } from '@/ui/layout/table/components/TableSection';
import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray';
import { TableMetadata } from '@/ui/layout/table/types/TableMetadata';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyArray } from '@sniptt/guards';
import { useEffect, useMemo, useState } from 'react';
import { useRecoilState } from 'recoil';
@ -19,63 +20,65 @@ import { IconSearch } from 'twenty-ui';
import { useMapFieldMetadataItemToSettingsObjectDetailTableItem } from '~/pages/settings/data-model/hooks/useMapFieldMetadataItemToSettingsObjectDetailTableItem';
import { SettingsObjectDetailTableItem } from '~/pages/settings/data-model/types/SettingsObjectDetailTableItem';
const SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD: TableMetadata<SettingsObjectDetailTableItem> =
{
tableId: 'settingsObjectDetail',
fields: [
{
fieldLabel: 'Name',
fieldName: 'label',
fieldType: 'string',
align: 'left',
},
{
fieldLabel: 'Field type',
fieldName: 'fieldType',
fieldType: 'string',
align: 'left',
},
{
fieldLabel: 'Data type',
fieldName: 'dataType',
fieldType: 'string',
align: 'left',
},
],
initialSort: {
const GET_SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD = (
t: (literals: TemplateStringsArray) => string,
): TableMetadata<SettingsObjectDetailTableItem> => ({
tableId: 'settingsObjectDetail',
fields: [
{
fieldLabel: t`Name`,
fieldName: 'label',
orderBy: 'AscNullsLast',
fieldType: 'string',
align: 'left',
},
};
{
fieldLabel: t`Field type`,
fieldName: 'fieldType',
fieldType: 'string',
align: 'left',
},
{
fieldLabel: t`Data type`,
fieldName: 'dataType',
fieldType: 'string',
align: 'left',
},
],
initialSort: {
fieldName: 'label',
orderBy: 'AscNullsLast',
},
});
const SETTINGS_OBJECT_DETAIL_TABLE_METADATA_CUSTOM: TableMetadata<SettingsObjectDetailTableItem> =
{
tableId: 'settingsObjectDetail',
fields: [
{
fieldLabel: 'Name',
fieldName: 'label',
fieldType: 'string',
align: 'left',
},
{
fieldLabel: 'Identifier',
fieldName: 'identifierType',
fieldType: 'string',
align: 'left',
},
{
fieldLabel: 'Data type',
fieldName: 'dataType',
fieldType: 'string',
align: 'left',
},
],
initialSort: {
const GET_SETTINGS_OBJECT_DETAIL_TABLE_METADATA_CUSTOM = (
t: (literals: TemplateStringsArray) => string,
): TableMetadata<SettingsObjectDetailTableItem> => ({
tableId: 'settingsObjectDetail',
fields: [
{
fieldLabel: t`Name`,
fieldName: 'label',
orderBy: 'AscNullsLast',
fieldType: 'string',
align: 'left',
},
};
{
fieldLabel: t`Identifier`,
fieldName: 'identifierType',
fieldType: 'string',
align: 'left',
},
{
fieldLabel: t`Data type`,
fieldName: 'dataType',
fieldType: 'string',
align: 'left',
},
],
initialSort: {
fieldName: 'label',
orderBy: 'AscNullsLast',
},
});
const StyledSearchInput = styled(TextInput)`
padding-bottom: ${({ theme }) => theme.spacing(2)};
@ -91,11 +94,12 @@ export const SettingsObjectFieldTable = ({
objectMetadataItem,
mode,
}: SettingsObjectFieldTableProps) => {
const { t } = useLingui();
const [searchTerm, setSearchTerm] = useState('');
const tableMetadata = objectMetadataItem.isCustom
? SETTINGS_OBJECT_DETAIL_TABLE_METADATA_CUSTOM
: SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD;
? GET_SETTINGS_OBJECT_DETAIL_TABLE_METADATA_CUSTOM(t)
: GET_SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD(t);
const { mapFieldMetadataItemToSettingsObjectDetailTableItem } =
useMapFieldMetadataItemToSettingsObjectDetailTableItem(objectMetadataItem);
@ -176,7 +180,7 @@ export const SettingsObjectFieldTable = ({
<>
<StyledSearchInput
LeftIcon={IconSearch}
placeholder="Search a field..."
placeholder={t`Search a field...`}
value={searchTerm}
onChange={setSearchTerm}
/>
@ -194,7 +198,7 @@ export const SettingsObjectFieldTable = ({
<TableHeader></TableHeader>
</StyledObjectFieldTableRow>
{isNonEmptyArray(filteredActiveItems) && (
<TableSection title="Active">
<TableSection title={t`Active`}>
{filteredActiveItems.map((objectSettingsDetailItem) => (
<SettingsObjectFieldItemTableRow
key={objectSettingsDetailItem.fieldMetadataItem.id}
@ -208,7 +212,7 @@ export const SettingsObjectFieldTable = ({
{isNonEmptyArray(filteredDisabledItems) && (
<TableSection
isInitiallyExpanded={mode === 'new-field' ? true : false}
title="Inactive"
title={t`Inactive`}
>
{filteredDisabledItems.map((objectSettingsDetailItem) => (
<SettingsObjectFieldItemTableRow

View File

@ -1,5 +1,4 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { settingsObjectIndexesFamilyState } from '@/settings/data-model/object-details/states/settingsObjectIndexesFamilyState';
import { TextInput } from '@/ui/input/components/TextInput';
import { SortableTableHeader } from '@/ui/layout/table/components/SortableTableHeader';
@ -10,6 +9,7 @@ import { TableRow } from '@/ui/layout/table/components/TableRow';
import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray';
import { TableMetadata } from '@/ui/layout/table/types/TableMetadata';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyArray } from '@sniptt/guards';
import { useEffect, useMemo, useState } from 'react';
import { useRecoilState } from 'recoil';
@ -20,12 +20,26 @@ export const StyledObjectIndexTableRow = styled(TableRow)`
grid-template-columns: 350px 70px 80px;
`;
const SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD: TableMetadata<SettingsObjectIndexesTableItem> =
{
const StyledSearchInput = styled(TextInput)`
padding-bottom: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
export type SettingsObjectIndexTableProps = {
objectMetadataItem: ObjectMetadataItem;
};
export const SettingsObjectIndexTable = ({
objectMetadataItem,
}: SettingsObjectIndexTableProps) => {
const { t } = useLingui();
const [searchTerm, setSearchTerm] = useState('');
const tableMetadata: TableMetadata<SettingsObjectIndexesTableItem> = {
tableId: 'settingsObjectIndexs',
fields: [
{
fieldLabel: 'Fields',
fieldLabel: t`Fields`,
fieldName: 'indexFields',
fieldType: 'string',
align: 'left',
@ -38,7 +52,7 @@ const SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD: TableMetadata<SettingsObje
align: 'left',
},
{
fieldLabel: 'Type',
fieldLabel: t`Type`,
fieldName: 'indexType',
fieldType: 'string',
align: 'right',
@ -50,19 +64,6 @@ const SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD: TableMetadata<SettingsObje
},
};
const StyledSearchInput = styled(TextInput)`
padding-bottom: ${({ theme }) => theme.spacing(2)};
width: 100%;
`;
export type SettingsObjectIndexTableProps = {
objectMetadataItem: ObjectMetadataItem;
};
export const SettingsObjectIndexTable = ({
objectMetadataItem,
}: SettingsObjectIndexTableProps) => {
const [searchTerm, setSearchTerm] = useState('');
const [settingsObjectIndexes, setSettingsObjectIndexes] = useRecoilState(
settingsObjectIndexesFamilyState({
objectMetadataItemId: objectMetadataItem.id,
@ -95,7 +96,7 @@ export const SettingsObjectIndexTable = ({
const sortedActiveObjectSettingsDetailItems = useSortedArray(
objectSettingsDetailItems,
SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD,
tableMetadata,
);
const filteredActiveItems = useMemo(
@ -112,22 +113,20 @@ export const SettingsObjectIndexTable = ({
<>
<StyledSearchInput
LeftIcon={IconSearch}
placeholder="Search an index..."
placeholder={t`Search an index...`}
value={searchTerm}
onChange={setSearchTerm}
/>
<Table>
<StyledObjectIndexTableRow>
{SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD.fields.map((item) => (
{tableMetadata.fields.map((item) => (
<SortableTableHeader
key={item.fieldName}
fieldName={item.fieldName}
label={item.fieldLabel}
Icon={item.FieldIcon}
tableId={SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD.tableId}
initialSort={
SETTINGS_OBJECT_DETAIL_TABLE_METADATA_STANDARD.initialSort
}
tableId={tableMetadata.tableId}
initialSort={tableMetadata.initialSort}
/>
))}
<TableHeader></TableHeader>

View File

@ -21,7 +21,7 @@ import { TableSection } from '@/ui/layout/table/components/TableSection';
import { useSortedArray } from '@/ui/layout/table/hooks/useSortedArray';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { isNonEmptyArray } from '@sniptt/guards';
import { useMemo, useState } from 'react';
import {
@ -33,7 +33,7 @@ import {
Section,
UndecoratedLink,
} from 'twenty-ui';
import { SETTINGS_OBJECT_TABLE_METADATA } from '~/pages/settings/data-model/constants/SettingsObjectTableMetadata';
import { GET_SETTINGS_OBJECT_TABLE_METADATA } from '~/pages/settings/data-model/constants/SettingsObjectTableMetadata';
import { SettingsObjectTableItem } from '~/pages/settings/data-model/types/SettingsObjectTableItem';
const StyledIconChevronRight = styled(IconChevronRight)`
@ -46,8 +46,8 @@ const StyledSearchInput = styled(TextInput)`
`;
export const SettingsObjects = () => {
const theme = useTheme();
const { t } = useLingui();
const theme = useTheme();
const [searchTerm, setSearchTerm] = useState('');
const { deleteOneObjectMetadataItem } = useDeleteOneObjectMetadataItem();
const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem();
@ -104,14 +104,19 @@ export const SettingsObjects = () => {
[inactiveObjectMetadataItems, totalCountByObjectMetadataItemNamePlural],
);
const tableMetadata = useMemo(
() => GET_SETTINGS_OBJECT_TABLE_METADATA(t),
[t],
);
const sortedActiveObjectSettingsItems = useSortedArray(
activeObjectSettingsArray,
SETTINGS_OBJECT_TABLE_METADATA,
tableMetadata,
);
const sortedInactiveObjectSettingsItems = useSortedArray(
inactiveObjectSettingsArray,
SETTINGS_OBJECT_TABLE_METADATA,
tableMetadata,
);
const filteredActiveObjectSettingsItems = useMemo(
@ -149,12 +154,10 @@ export const SettingsObjects = () => {
}
links={[
{
children: t`Workspace`,
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{
children: t`Objects`,
},
{ children: <Trans>Objects</Trans> },
]}
>
<SettingsPageContainer>
@ -165,22 +168,22 @@ export const SettingsObjects = () => {
<StyledSearchInput
LeftIcon={IconSearch}
placeholder={t`Search an object...`}
placeholder={t`Search for an object...`}
value={searchTerm}
onChange={setSearchTerm}
/>
<Table>
<StyledObjectTableRow>
{SETTINGS_OBJECT_TABLE_METADATA.fields.map(
{tableMetadata.fields.map(
(settingsObjectsTableMetadataField) => (
<SortableTableHeader
key={settingsObjectsTableMetadataField.fieldName}
fieldName={settingsObjectsTableMetadataField.fieldName}
label={settingsObjectsTableMetadataField.fieldLabel}
tableId={SETTINGS_OBJECT_TABLE_METADATA.tableId}
tableId={tableMetadata.tableId}
align={settingsObjectsTableMetadataField.align}
initialSort={SETTINGS_OBJECT_TABLE_METADATA.initialSort}
initialSort={tableMetadata.initialSort}
/>
),
)}

View File

@ -1,37 +1,43 @@
import { TableMetadata } from '@/ui/layout/table/types/TableMetadata';
import { SettingsObjectTableItem } from '~/pages/settings/data-model/types/SettingsObjectTableItem';
export const SETTINGS_OBJECT_TABLE_METADATA: TableMetadata<SettingsObjectTableItem> =
{
tableId: 'settingsObject',
fields: [
{
fieldLabel: 'Name',
fieldName: 'labelPlural',
fieldType: 'string',
align: 'left',
},
{
fieldLabel: 'Type',
fieldName: 'objectTypeLabel',
fieldType: 'string',
align: 'left',
},
{
fieldLabel: 'Fields',
fieldName: 'fieldsCount',
fieldType: 'number',
align: 'right',
},
{
fieldLabel: 'Instances',
fieldName: 'totalObjectCount',
fieldType: 'number',
align: 'right',
},
],
initialSort: {
type GET_SETTINGS_OBJECT_TABLE_METADATAProps = {
(descriptor: { id: string }): string;
(literals: TemplateStringsArray, ...placeholders: any[]): string;
};
export const GET_SETTINGS_OBJECT_TABLE_METADATA = (
t: GET_SETTINGS_OBJECT_TABLE_METADATAProps,
): TableMetadata<SettingsObjectTableItem> => ({
tableId: 'settingsObject',
fields: [
{
fieldLabel: t({ id: 'Name' }),
fieldName: 'labelPlural',
orderBy: 'AscNullsLast',
fieldType: 'string',
align: 'left',
},
};
{
fieldLabel: t({ id: 'Type' }),
fieldName: 'objectTypeLabel',
fieldType: 'string',
align: 'left',
},
{
fieldLabel: t({ id: 'Fields' }),
fieldName: 'fieldsCount',
fieldType: 'number',
align: 'right',
},
{
fieldLabel: t({ id: 'Instances' }),
fieldName: 'totalObjectCount',
fieldType: 'number',
align: 'right',
},
],
initialSort: {
fieldName: 'labelPlural',
orderBy: 'AscNullsLast',
},
});

View File

@ -7,7 +7,7 @@ import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { Button, H2Title, IconPlus, MOBILE_VIEWPORT, Section } from 'twenty-ui';
const StyledButtonContainer = styled.div`
@ -36,10 +36,10 @@ export const SettingsDevelopers = () => {
actionButton={<SettingsReadDocumentationButton />}
links={[
{
children: t`Workspace`,
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: t`Developers` },
{ children: <Trans>Developers</Trans> },
]}
>
<SettingsPageContainer>
@ -47,7 +47,7 @@ export const SettingsDevelopers = () => {
<Section>
<H2Title
title={t`API keys`}
description={t`Active APIs keys created by you or your team.`}
description={t`Active API keys created by you or your team.`}
/>
<SettingsApiKeysTable />
<StyledButtonContainer>

View File

@ -4,19 +4,21 @@ import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { Trans, useLingui } from '@lingui/react/macro';
export const SettingsIntegrations = () => {
const { t } = useLingui();
const integrationCategories = useSettingsIntegrationCategories();
return (
<SubMenuTopBarContainer
title="Integrations"
title={t`Integrations`}
links={[
{
children: 'Workspace',
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: 'Integrations' },
{ children: <Trans>Integrations</Trans> },
]}
>
<SettingsPageContainer>

View File

@ -8,6 +8,7 @@ import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { Select } from '@/ui/input/components/Select';
import { i18n } from '@lingui/core';
import { useLingui } from '@lingui/react/macro';
import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
import { isDefined } from '~/utils/isDefined';
import { logError } from '~/utils/logError';
@ -19,6 +20,7 @@ const StyledContainer = styled.div`
`;
export const LocalePicker = () => {
const { t } = useLingui();
const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState(
currentWorkspaceMemberState,
);
@ -57,38 +59,38 @@ export const LocalePicker = () => {
const localeOptions = [
{
label: 'Portuguese',
label: t`Portuguese`,
value: 'pt',
},
{
label: 'French',
label: t`French`,
value: 'fr',
},
{
label: 'German',
label: t`German`,
value: 'de',
},
{
label: 'Italian',
label: t`Italian`,
value: 'it',
},
{
label: 'Spanish',
label: t`Spanish`,
value: 'es',
},
{
label: 'English',
label: t`English`,
value: 'en',
},
{
label: 'Chinese',
label: t`Chinese`,
value: 'zh',
},
];
if (isDebugMode) {
localeOptions.push({
label: 'Pseudo-English',
label: t`Pseudo-English`,
value: 'pseudo-en',
});
}

View File

@ -7,7 +7,7 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBa
import { useColorScheme } from '@/ui/theme/hooks/useColorScheme';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useLingui } from '@lingui/react/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { FeatureFlagKey } from '~/generated/graphql';
import { DateTimeSettings } from '~/pages/settings/profile/appearance/components/DateTimeSettings';
import { LocalePicker } from '~/pages/settings/profile/appearance/components/LocalePicker';
@ -23,24 +23,24 @@ export const SettingsExperience = () => {
return (
<SubMenuTopBarContainer
title="Experience"
title={t`Experience`}
links={[
{
children: 'User',
children: <Trans>User</Trans>,
href: getSettingsPagePath(SettingsPath.ProfilePage),
},
{ children: 'Experience' },
{ children: <Trans>Experience</Trans> },
]}
>
<SettingsPageContainer>
<Section>
<H2Title title="Appearance" />
<H2Title title={t`Appearance`} />
<ColorSchemePicker value={colorScheme} onChange={setColorScheme} />
</Section>
<Section>
<H2Title
title="Date and time"
description="Configure how dates are displayed across the app"
title={t`Date and time`}
description={t`Configure how dates are displayed across the app`}
/>
<DateTimeSettings />
</Section>

View File

@ -1,5 +1,5 @@
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { H2Title, IconLock, Section, Tag } from 'twenty-ui';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
@ -34,10 +34,10 @@ export const SettingsSecurity = () => {
actionButton={<SettingsReadDocumentationButton />}
links={[
{
children: t`Workspace`,
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: t`Security` },
{ children: <Trans>Security</Trans> },
]}
>
<SettingsPageContainer>

View File

@ -11,7 +11,7 @@ import { TextInputV2 } from '@/ui/input/components/TextInputV2';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import styled from '@emotion/styled';
import { zodResolver } from '@hookform/resolvers/zod';
import { useLingui } from '@lingui/react/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { Controller, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { useRecoilState, useRecoilValue } from 'recoil';
@ -122,17 +122,17 @@ export const SettingsDomain = () => {
return (
<SubMenuTopBarContainer
title={t`General`}
title={t`Domain`}
links={[
{
children: t`Workspace`,
children: <Trans>Workspace</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{
children: t`General`,
children: <Trans>General</Trans>,
href: getSettingsPagePath(SettingsPath.Workspace),
},
{ children: t`Domain` },
{ children: <Trans>Domain</Trans> },
]}
actionButton={
<SaveAndCancelButtons