feat: add lingui/no-unlocalized-strings ESLint rule and fix translations (#16610)
## Summary
This PR adds the `lingui/no-unlocalized-strings` ESLint rule to detect
untranslated strings and fixes translation issues across multiple
components.
## Changes
### ESLint Configuration (`eslint.config.react.mjs`)
- Added comprehensive `ignore` patterns for non-translatable strings
(CSS values, HTML attributes, technical identifiers)
- Added `ignoreNames` for props that don't need translation (className,
data-*, aria-*, etc.)
- Added `ignoreFunctions` for console methods, URL APIs, and other
non-user-facing functions
- Disabled rule for debug files, storybook, and test files
### Components Fixed (~19 files)
- Object record components (field inputs, pickers, merge dialogs)
- Settings components (accounts, admin panel)
- Serverless function components
- Record table and title cell components
## Status
🚧 **Work in Progress** - ~124 files remaining to fix
This PR is being submitted as draft to allow progressive fixing of
remaining translation issues.
## Testing
- Run `npx eslint "src/**/*.tsx"` in `packages/twenty-front` to check
remaining issues
This commit is contained in:
+1
-1
@@ -96,7 +96,7 @@ export const SettingsAccountsBlocklistInput = ({
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<SettingsTextInput
|
||||
instanceId="settings-accounts-blocklist-input"
|
||||
placeholder="eddy@gmail.com, @apple.com"
|
||||
placeholder={t`eddy@gmail.com, @apple.com`}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
error={error?.message}
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ export const SettingsAccountsCalendarChannelsGeneral = () => {
|
||||
description: '',
|
||||
isCanceled: false,
|
||||
location: '',
|
||||
title: 'Onboarding call',
|
||||
title: t`Onboarding call`,
|
||||
visibility: CalendarChannelVisibility.SHARE_EVERYTHING,
|
||||
};
|
||||
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type MessageFolder } from '@/accounts/types/MessageFolder';
|
||||
import { SettingsAccountsMessageFolderIcon } from '@/settings/accounts/components/message-folders/SettingsAccountsMessageFolderIcon';
|
||||
|
||||
@@ -127,7 +128,7 @@ export const SettingsMessageFoldersTreeItem = ({
|
||||
<StyledExpandButton
|
||||
isExpanded={isExpanded}
|
||||
onClick={handleExpandToggle}
|
||||
aria-label={isExpanded ? 'Collapse folder' : 'Expand folder'}
|
||||
aria-label={isExpanded ? t`Collapse folder` : t`Expand folder`}
|
||||
>
|
||||
<IconChevronRight size={16} />
|
||||
</StyledExpandButton>
|
||||
|
||||
+5
-2
@@ -98,8 +98,9 @@ export const SettingsAdminWorkspaceContent = ({
|
||||
);
|
||||
},
|
||||
onError: (error) => {
|
||||
const errorMessage = error.message;
|
||||
enqueueErrorSnackBar({
|
||||
message: `Failed to impersonate user. ${error.message}`,
|
||||
message: t`Failed to impersonate user. ${errorMessage}`,
|
||||
});
|
||||
},
|
||||
}).finally(() => {
|
||||
@@ -128,8 +129,9 @@ export const SettingsAdminWorkspaceContent = ({
|
||||
if (isDefined(previousValue)) {
|
||||
updateFeatureFlagState(workspaceId, featureFlag, previousValue);
|
||||
}
|
||||
const errorMessage = error.message;
|
||||
enqueueErrorSnackBar({
|
||||
message: `Failed to update feature flag. ${error.message}`,
|
||||
message: t`Failed to update feature flag. ${errorMessage}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -146,6 +148,7 @@ export const SettingsAdminWorkspaceContent = ({
|
||||
value: (
|
||||
<Chip
|
||||
label={activeWorkspace?.name ?? ''}
|
||||
emptyLabel={t`Untitled`}
|
||||
leftComponent={
|
||||
<AvatarChip
|
||||
avatarUrl={
|
||||
|
||||
+4
-3
@@ -5,6 +5,7 @@ import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type ConfigVariableValue } from 'twenty-shared/types';
|
||||
import { CustomError } from 'twenty-shared/utils';
|
||||
import { MenuItemMultiSelect } from 'twenty-ui/navigation';
|
||||
@@ -109,7 +110,7 @@ export const ConfigVariableDatabaseInput = ({
|
||||
label:
|
||||
Array.isArray(value) && value.length > 0
|
||||
? value.join(', ')
|
||||
: 'Select options',
|
||||
: t`Select options`,
|
||||
}}
|
||||
isDisabled={disabled}
|
||||
hasRightElement={false}
|
||||
@@ -152,7 +153,7 @@ export const ConfigVariableDatabaseInput = ({
|
||||
}
|
||||
}}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder || 'Enter JSON array'}
|
||||
placeholder={placeholder || t`Enter JSON array`}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -184,7 +185,7 @@ export const ConfigVariableDatabaseInput = ({
|
||||
}
|
||||
onChange={(text) => onChange(text)}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder || 'Enter value'}
|
||||
placeholder={placeholder || t`Enter value`}
|
||||
fullWidth
|
||||
/>
|
||||
);
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ export const ConfigVariableValueInput = ({
|
||||
options={variable.options}
|
||||
disabled={disabled}
|
||||
placeholder={
|
||||
disabled ? 'Undefined' : t`Enter a value to store in database`
|
||||
disabled ? t`Undefined` : t`Enter a value to store in database`
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ export const SettingsAdminConfigVariables = () => {
|
||||
|
||||
const groupOptions = useMemo(
|
||||
() => [
|
||||
{ value: 'all', label: 'All Groups' },
|
||||
{ value: 'all', label: t`All Groups` },
|
||||
...allGroups.map((group) => ({
|
||||
value: group.name,
|
||||
label: group.name,
|
||||
|
||||
+3
-2
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { SettingsAdminConfigVariablesRow } from '@/settings/admin-panel/config-variables/components/SettingsAdminConfigVariablesRow';
|
||||
import { Table } from '@/ui/layout/table/components/Table';
|
||||
import { TableBody } from '@/ui/layout/table/components/TableBody';
|
||||
@@ -20,8 +21,8 @@ export const SettingsAdminConfigVariablesTable = ({
|
||||
return (
|
||||
<Table>
|
||||
<TableRow gridAutoColumns="5fr 3fr 1fr">
|
||||
<TableHeader>Name</TableHeader>
|
||||
<TableHeader align="right">Value</TableHeader>
|
||||
<TableHeader>{t`Name`}</TableHeader>
|
||||
<TableHeader align="right">{t`Value`}</TableHeader>
|
||||
<TableHeader align="right"></TableHeader>
|
||||
</TableRow>
|
||||
<StyledTableBody>
|
||||
|
||||
+7
-4
@@ -1,5 +1,6 @@
|
||||
import { SettingsAdminTableCard } from '@/settings/admin-panel/components/SettingsAdminTableCard';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { H2Title } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
|
||||
@@ -17,25 +18,27 @@ export const SettingsAdminHealthAccountSyncCountersTable = ({
|
||||
title: string;
|
||||
description: string;
|
||||
}) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
if (!details) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const items = [
|
||||
{
|
||||
label: 'Active Sync',
|
||||
label: t`Active Sync`,
|
||||
value: details.counters.ACTIVE,
|
||||
},
|
||||
{
|
||||
label: 'Total Jobs',
|
||||
label: t`Total Jobs`,
|
||||
value: details.totalJobs,
|
||||
},
|
||||
{
|
||||
label: 'Failed Jobs',
|
||||
label: t`Failed Jobs`,
|
||||
value: details.failedJobs,
|
||||
},
|
||||
{
|
||||
label: 'Failure Rate',
|
||||
label: t`Failure Rate`,
|
||||
value: `${details.failureRate}%`,
|
||||
},
|
||||
];
|
||||
|
||||
+3
-2
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Status } from 'twenty-ui/display';
|
||||
import { AdminPanelHealthServiceStatus } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -9,10 +10,10 @@ export const SettingsAdminHealthStatusRightContainer = ({
|
||||
return (
|
||||
<>
|
||||
{status === AdminPanelHealthServiceStatus.OPERATIONAL && (
|
||||
<Status color="green" text="Operational" weight="medium" />
|
||||
<Status color="green" text={t`Operational`} weight="medium" />
|
||||
)}
|
||||
{status === AdminPanelHealthServiceStatus.OUTAGE && (
|
||||
<Status color="red" text="Outage" weight="medium" />
|
||||
<Status color="red" text={t`Outage`} weight="medium" />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
+3
-1
@@ -38,12 +38,14 @@ export const SettingsAdminJsonDataIndicatorHealthStatus = () => {
|
||||
|
||||
const isAnyNode = () => true;
|
||||
|
||||
const serviceLabel = indicatorHealth.label;
|
||||
|
||||
return (
|
||||
<Section>
|
||||
{isDown && (
|
||||
<StyledErrorMessage>
|
||||
{indicatorHealth.errorMessage ||
|
||||
`${indicatorHealth.label} service is unreachable`}
|
||||
t`${serviceLabel} service is unreachable`}
|
||||
</StyledErrorMessage>
|
||||
)}
|
||||
{parsedDetails && (
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ export const SettingsAdminQueueJobRowDropdownMenu = ({
|
||||
dropdownPlacement="right-start"
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
aria-label="Job Actions"
|
||||
aria-label={t`Job Actions`}
|
||||
Icon={IconDotsVertical}
|
||||
accent="tertiary"
|
||||
/>
|
||||
|
||||
+2
-1
@@ -53,8 +53,9 @@ export const SettingsAdminWorkerMetricsGraph = ({
|
||||
},
|
||||
fetchPolicy: 'no-cache',
|
||||
onError: (error) => {
|
||||
const errorMessage = error.message;
|
||||
enqueueErrorSnackBar({
|
||||
message: `Error fetching worker metrics: ${error.message}`,
|
||||
message: t`Error fetching worker metrics: ${errorMessage}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Card, CardContent } from 'twenty-ui/layout';
|
||||
import { IconChevronRight } from 'twenty-ui/display';
|
||||
import { Pill } from 'twenty-ui/components';
|
||||
@@ -97,7 +98,7 @@ export const SettingsCard = ({
|
||||
<StyledIconContainer>{Icon}</StyledIconContainer>
|
||||
<StyledTitle disabled={disabled}>
|
||||
{title}
|
||||
{soon && <Pill label="Soon" />}
|
||||
{soon && <Pill label={t`Soon`} />}
|
||||
</StyledTitle>
|
||||
{Status && Status}
|
||||
<StyledIconChevronRight size={theme.icon.size.sm} />
|
||||
|
||||
+7
-6
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconButton, type SelectOption } from 'twenty-ui/input';
|
||||
@@ -55,7 +56,7 @@ export const SettingsDatabaseEventsForm = ({
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const objectOptions: SelectOption<string>[] = [
|
||||
{ label: 'All Objects', value: '*', Icon: IconNorthStar },
|
||||
{ label: t`All Objects`, value: '*', Icon: IconNorthStar },
|
||||
...objectMetadataItems.map((item) => ({
|
||||
label: item.labelPlural,
|
||||
value: item.nameSingular,
|
||||
@@ -64,10 +65,10 @@ export const SettingsDatabaseEventsForm = ({
|
||||
];
|
||||
|
||||
const actionOptions: SelectOption<string>[] = [
|
||||
{ label: 'All', value: '*', Icon: IconNorthStar },
|
||||
{ label: 'Created', value: 'created', Icon: IconPlus },
|
||||
{ label: 'Updated', value: 'updated', Icon: IconBox },
|
||||
{ label: 'Deleted', value: 'deleted', Icon: IconTrash },
|
||||
{ label: t`All`, value: '*', Icon: IconNorthStar },
|
||||
{ label: t`Created`, value: 'created', Icon: IconPlus },
|
||||
{ label: t`Updated`, value: 'updated', Icon: IconBox },
|
||||
{ label: t`Deleted`, value: 'deleted', Icon: IconTrash },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -82,7 +83,7 @@ export const SettingsDatabaseEventsForm = ({
|
||||
updateOperation?.(index, 'object', newValue)
|
||||
}
|
||||
fullWidth
|
||||
emptyOption={{ label: 'Object', value: null }}
|
||||
emptyOption={{ label: t`Object`, value: null }}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Select
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ export const SettingsDataModelNewFieldBreadcrumbDropDown = () => {
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
New Field <StyledSpan>-</StyledSpan>
|
||||
{t`New Field`} <StyledSpan>-</StyledSpan>
|
||||
<Dropdown
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownId={dropdownId}
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type MouseEvent, useMemo, useState } from 'react';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
import { type SelectOption } from 'twenty-ui/input';
|
||||
@@ -72,7 +73,7 @@ export const MultiSelectAddressFields = <Value extends SelectValue>({
|
||||
selectedOption={{
|
||||
label:
|
||||
values?.length === options.length
|
||||
? 'Default'
|
||||
? t`Default`
|
||||
: values?.length.toString(),
|
||||
value: values?.length,
|
||||
}}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ export const SettingsDataModelFieldAddressForm = ({
|
||||
const { control } = useFormContext<SettingsDataModelFieldTextFormValues>();
|
||||
const countries = [
|
||||
{
|
||||
label: 'No country',
|
||||
label: t`No country`,
|
||||
value: '',
|
||||
Icon: IconCircleOff,
|
||||
},
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ export const SettingsObjectNewFieldSelector = ({
|
||||
[
|
||||
key,
|
||||
key === FieldMetadataType.MORPH_RELATION
|
||||
? { ...config, label: 'Relation' }
|
||||
? { ...config, label: t`Relation` }
|
||||
: config,
|
||||
] as [string, SettingsFieldTypeConfig<any>],
|
||||
)
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Separator } from '@/settings/components/Separator';
|
||||
import { SettingsDataModelPreviewFormCard } from '@/settings/data-model/components/SettingsDataModelPreviewFormCard';
|
||||
import { SettingsDataModelFieldIsUniqueForm } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm';
|
||||
@@ -25,7 +26,7 @@ export const SettingsDataModelFieldNumberSettingsFormCard = ({
|
||||
<SettingsDataModelFieldPreviewWidget
|
||||
fieldMetadataItem={{
|
||||
icon: watch('icon'),
|
||||
label: watch('label') || 'New Field',
|
||||
label: watch('label') || t`New Field`,
|
||||
settings: watch('settings') || null,
|
||||
type: FieldMetadataType.NUMBER,
|
||||
}}
|
||||
|
||||
+36
-2
@@ -18,10 +18,42 @@ import {
|
||||
IconX,
|
||||
} from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { MenuItem, MenuItemSelectColor } from 'twenty-ui/navigation';
|
||||
import {
|
||||
type ColorLabels,
|
||||
MenuItem,
|
||||
MenuItemSelectColor,
|
||||
} from 'twenty-ui/navigation';
|
||||
import { MAIN_COLOR_NAMES } from 'twenty-ui/theme';
|
||||
import { computeOptionValueFromLabel } from '~/pages/settings/data-model/utils/computeOptionValueFromLabel';
|
||||
|
||||
const useColorLabels = (): ColorLabels => ({
|
||||
gray: t`Gray`,
|
||||
tomato: t`Tomato`,
|
||||
red: t`Red`,
|
||||
ruby: t`Ruby`,
|
||||
crimson: t`Crimson`,
|
||||
pink: t`Pink`,
|
||||
plum: t`Plum`,
|
||||
purple: t`Purple`,
|
||||
violet: t`Violet`,
|
||||
iris: t`Iris`,
|
||||
cyan: t`Cyan`,
|
||||
turquoise: t`Turquoise`,
|
||||
sky: t`Sky`,
|
||||
blue: t`Blue`,
|
||||
jade: t`Jade`,
|
||||
green: t`Green`,
|
||||
grass: t`Grass`,
|
||||
mint: t`Mint`,
|
||||
lime: t`Lime`,
|
||||
bronze: t`Bronze`,
|
||||
gold: t`Gold`,
|
||||
brown: t`Brown`,
|
||||
orange: t`Orange`,
|
||||
amber: t`Amber`,
|
||||
yellow: t`Yellow`,
|
||||
});
|
||||
|
||||
type SettingsDataModelFieldSelectFormOptionRowProps = {
|
||||
className?: string;
|
||||
isDefault?: boolean;
|
||||
@@ -51,7 +83,7 @@ const StyledColorSample = styled(ColorSample)`
|
||||
margin-left: ${({ theme }) => theme.spacing(3.5)};
|
||||
`;
|
||||
|
||||
const StyledOptionInput = styled(SettingsTextInput)`
|
||||
const StyledOptionInput = styled(SettingsTextInput)`Chip
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
& input {
|
||||
@@ -80,6 +112,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
fieldIsNullable,
|
||||
}: SettingsDataModelFieldSelectFormOptionRowProps) => {
|
||||
const theme = useTheme();
|
||||
const colorLabels = useColorLabels();
|
||||
const SELECT_COLOR_DROPDOWN_ID = `select-color-dropdown-${option.id}`;
|
||||
const SELECT_ACTIONS_DROPDOWN_ID = `select-actions-dropdown-${option.id}`;
|
||||
|
||||
@@ -130,6 +163,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
}}
|
||||
color={colorName}
|
||||
selected={colorName === option.color}
|
||||
colorLabels={colorLabels}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
|
||||
+11
-7
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useMemo } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { v4 } from 'uuid';
|
||||
@@ -7,12 +8,15 @@ import { type FieldMetadataItemOption } from '@/object-metadata/types/FieldMetad
|
||||
import { type SettingsDataModelFieldSelectFormValues } from '@/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm';
|
||||
import { computeOptionValueFromLabel } from '~/pages/settings/data-model/utils/computeOptionValueFromLabel';
|
||||
|
||||
const DEFAULT_OPTION: FieldMetadataItemOption = {
|
||||
color: 'green',
|
||||
id: v4(),
|
||||
label: 'Option 1',
|
||||
position: 0,
|
||||
value: computeOptionValueFromLabel('Option 1'),
|
||||
const getDefaultOption = (): FieldMetadataItemOption => {
|
||||
const label = t`Option 1`;
|
||||
return {
|
||||
color: 'green',
|
||||
id: v4(),
|
||||
label,
|
||||
position: 0,
|
||||
value: computeOptionValueFromLabel(label),
|
||||
};
|
||||
};
|
||||
|
||||
type UseSelectSettingsFormInitialValuesProps = {
|
||||
@@ -33,7 +37,7 @@ export const useSelectSettingsFormInitialValues = ({
|
||||
? [...fieldMetadataItem.options].sort(
|
||||
(optionA, optionB) => optionA.position - optionB.position,
|
||||
)
|
||||
: [DEFAULT_OPTION],
|
||||
: [getDefaultOption()],
|
||||
[fieldMetadataItem?.options],
|
||||
);
|
||||
|
||||
|
||||
+3
-3
@@ -30,9 +30,9 @@ export const SettingsAvailableStandardObjectsSection = ({
|
||||
<Table>
|
||||
<StyledAvailableStandardObjectTableRow>
|
||||
<TableHeader></TableHeader>
|
||||
<TableHeader>Name</TableHeader>
|
||||
<TableHeader>Description</TableHeader>
|
||||
<TableHeader align="right">Fields</TableHeader>
|
||||
<TableHeader>{t`Name`}</TableHeader>
|
||||
<TableHeader>{t`Description`}</TableHeader>
|
||||
<TableHeader align="right">{t`Fields`}</TableHeader>
|
||||
</StyledAvailableStandardObjectTableRow>
|
||||
<TableBody>
|
||||
{objectItems.map((objectItem) => (
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ export const SettingsObjectFieldInactiveActionDropdown = ({
|
||||
dropdownId={dropdownId}
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
aria-label="Inactive Field Options"
|
||||
aria-label={t`Inactive Field Options`}
|
||||
Icon={IconDotsVertical}
|
||||
accent="tertiary"
|
||||
/>
|
||||
|
||||
+7
-6
@@ -163,15 +163,15 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
const typeLabel =
|
||||
variant === 'field-type'
|
||||
? isRemoteObjectField
|
||||
? 'Remote'
|
||||
? t`Remote`
|
||||
: fieldMetadataItem.isCustom
|
||||
? 'Custom'
|
||||
: 'Standard'
|
||||
? t`Custom`
|
||||
: t`Standard`
|
||||
: variant === 'identifier'
|
||||
? isDefined(identifierType)
|
||||
? identifierType === 'label'
|
||||
? 'Record text'
|
||||
: 'Record image'
|
||||
? t`Record text`
|
||||
: t`Record image`
|
||||
: ''
|
||||
: '';
|
||||
|
||||
@@ -181,9 +181,10 @@ export const SettingsObjectFieldItemTableRow = ({
|
||||
isDefined(relationObjectMetadataItem?.namePlural) &&
|
||||
!relationObjectMetadataItem.isSystem;
|
||||
|
||||
const morphRelationCount = fieldMetadataItem.morphRelations?.length;
|
||||
const morphRelationLabel =
|
||||
fieldMetadataItem.type === FieldMetadataType.MORPH_RELATION
|
||||
? `${fieldMetadataItem.morphRelations?.length} Objects`
|
||||
? t`${morphRelationCount} Objects`
|
||||
: undefined;
|
||||
|
||||
const label = morphRelationLabel
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
@@ -113,7 +114,7 @@ const SettingsDataModelObjectPreviewOtherObjects = ({
|
||||
/>
|
||||
</StyledIconContainer>
|
||||
<StyledOverflowingTextWithTooltip>
|
||||
<OverflowingTextWithTooltip text={`Other objects`} />
|
||||
<OverflowingTextWithTooltip text={t`Other objects`} />
|
||||
</StyledOverflowingTextWithTooltip>
|
||||
</StyledObjectName>
|
||||
<StyledNumber>
|
||||
|
||||
+4
-3
@@ -3,6 +3,7 @@ import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { IconArchiveOff, IconDotsVertical, IconTrash } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
@@ -39,7 +40,7 @@ export const SettingsObjectInactiveMenuDropDown = ({
|
||||
dropdownId={dropdownId}
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
aria-label="Inactive Object Options"
|
||||
aria-label={t`Inactive Object Options`}
|
||||
Icon={IconDotsVertical}
|
||||
accent="tertiary"
|
||||
/>
|
||||
@@ -48,13 +49,13 @@ export const SettingsObjectInactiveMenuDropDown = ({
|
||||
<DropdownContent widthInPixels={GenericDropdownContentWidth.Narrow}>
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
text="Activate"
|
||||
text={t`Activate`}
|
||||
LeftIcon={IconArchiveOff}
|
||||
onClick={handleActivate}
|
||||
/>
|
||||
{isCustomObject && (
|
||||
<MenuItem
|
||||
text="Delete"
|
||||
text={t`Delete`}
|
||||
LeftIcon={IconTrash}
|
||||
accent="danger"
|
||||
onClick={handleDelete}
|
||||
|
||||
+2
-1
@@ -2,6 +2,7 @@ import { type Decorator, type Meta, type StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { SettingsObjectInactiveMenuDropDown } from '../SettingsObjectInactiveMenuDropDown';
|
||||
|
||||
const handleActivateMockFunction = fn();
|
||||
@@ -23,7 +24,7 @@ const meta: Meta<typeof SettingsObjectInactiveMenuDropDown> = {
|
||||
onActivate: handleActivateMockFunction,
|
||||
onDelete: handleDeleteMockFunction,
|
||||
},
|
||||
decorators: [ComponentDecorator, ClearMocksDecorator],
|
||||
decorators: [I18nFrontDecorator, ComponentDecorator, ClearMocksDecorator],
|
||||
parameters: {
|
||||
clearMocks: true,
|
||||
},
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ export const SettingsDataModelObjectIdentifiersForm = ({
|
||||
|
||||
const emptyOption: SelectOption<string | null> = {
|
||||
Icon: IconCircleOff,
|
||||
label: 'None',
|
||||
label: t`None`,
|
||||
value: null,
|
||||
};
|
||||
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import styled from '@emotion/styled';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
@@ -66,7 +67,7 @@ export const ApiKeyNameInput = ({
|
||||
<StyledComboInputContainer>
|
||||
<SettingsTextInput
|
||||
instanceId={nameTextInputId}
|
||||
placeholder="E.g. backoffice integration"
|
||||
placeholder={t`E.g. backoffice integration`}
|
||||
onChange={onNameUpdate}
|
||||
fullWidth
|
||||
value={apiKeyName}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ export const SettingsDevelopersWebhookForm = ({
|
||||
|
||||
const getTitle = () => {
|
||||
if (isCreationMode) {
|
||||
return 'New Webhook';
|
||||
return t`New Webhook`;
|
||||
}
|
||||
|
||||
const targetUrl = formConfig.watch('targetUrl');
|
||||
|
||||
+2
-2
@@ -54,13 +54,13 @@ export const SettingsPublicDomainsListCard = () => {
|
||||
RowRightComponent={({ item: publicDomain }) => (
|
||||
<>
|
||||
{!publicDomain.isValidated && (
|
||||
<Status color="orange" text="Pending" />
|
||||
<Status color="orange" text={t`Pending`} />
|
||||
)}
|
||||
<SettingPublicDomainRowDropdownMenu publicDomain={publicDomain} />
|
||||
</>
|
||||
)}
|
||||
hasFooter
|
||||
footerButtonLabel="Add Public Domain"
|
||||
footerButtonLabel={t`Add Public Domain`}
|
||||
onFooterButtonClick={() => {
|
||||
setSelectedPublicDomain(undefined);
|
||||
navigate(SettingsPath.PublicDomain);
|
||||
|
||||
+2
-2
@@ -86,9 +86,9 @@ export const SettingsIntegrationComponent = ({
|
||||
{integration.text}
|
||||
</StyledSection>
|
||||
{integration.type === 'Soon' ? (
|
||||
<StyledSoonPill label="Soon" />
|
||||
<StyledSoonPill label={t`Soon`} />
|
||||
) : integration.type === 'Active' ? (
|
||||
<Status color="green" text="Active" />
|
||||
<Status color="green" text={t`Active`} />
|
||||
) : integration.type === 'Add' ? (
|
||||
<Button
|
||||
to={integration.link}
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import PreviewBackgroundImage from '../assets/preview-background.svg';
|
||||
@@ -69,7 +70,7 @@ export const SettingsIntegrationPreview = ({
|
||||
<StyledSyncImage />
|
||||
<StyledTwentyLogo alt="" src="/images/integrations/twenty-logo.svg" />
|
||||
</StyledLogosContainer>
|
||||
<StyledLabel>Import your tables as remote objects</StyledLabel>
|
||||
<StyledLabel>{t`Import your tables as remote objects`}</StyledLabel>
|
||||
</StyledCardContent>
|
||||
</StyledCard>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -43,7 +44,7 @@ export const useLabPublicFeatureFlags = () => {
|
||||
value: boolean,
|
||||
) => {
|
||||
if (!isDefined(currentWorkspace)) {
|
||||
setError('No workspace selected');
|
||||
setError(t`No workspace selected`);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export const MemberNameFields = ({
|
||||
instanceId={firstNameInstanceId}
|
||||
label={t`First Name`}
|
||||
value={firstName}
|
||||
placeholder="Tim"
|
||||
placeholder={t`Tim`}
|
||||
onChange={(value) => {
|
||||
onChange('firstName', value);
|
||||
}}
|
||||
@@ -33,7 +33,7 @@ export const MemberNameFields = ({
|
||||
instanceId={lastNameInstanceId}
|
||||
label={t`Last name`}
|
||||
value={lastName}
|
||||
placeholder="Cook"
|
||||
placeholder={t`Cook`}
|
||||
onChange={(value) => {
|
||||
onChange('lastName', value);
|
||||
}}
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ export const PlaygroundSetupForm = () => {
|
||||
<SettingsTextInput
|
||||
instanceId="playground-api-key"
|
||||
label={t`API Key`}
|
||||
placeholder="Enter your API key"
|
||||
placeholder={t`Enter your API key`}
|
||||
value={value}
|
||||
onChange={(newValue) => {
|
||||
onChange(newValue);
|
||||
|
||||
@@ -115,7 +115,7 @@ export const NameFields = ({ autoSave = true }: NameFieldsProps) => {
|
||||
label={t`First Name`}
|
||||
value={firstName}
|
||||
onChange={setFirstName}
|
||||
placeholder="Tim"
|
||||
placeholder={t`Tim`}
|
||||
fullWidth
|
||||
disabled={!canEditFirstName}
|
||||
/>
|
||||
@@ -124,7 +124,7 @@ export const NameFields = ({ autoSave = true }: NameFieldsProps) => {
|
||||
label={t`Last Name`}
|
||||
value={lastName}
|
||||
onChange={setLastName}
|
||||
placeholder="Cook"
|
||||
placeholder={t`Cook`}
|
||||
fullWidth
|
||||
disabled={!canEditLastName}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ApolloError } from '@apollo/client';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
@@ -26,7 +27,7 @@ export const useUpdateEmail = () => {
|
||||
});
|
||||
|
||||
enqueueInfoSnackBar({
|
||||
message: 'Check your inbox to verify your new email address.',
|
||||
message: t`Check your inbox to verify your new email address.`,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof ApolloError) {
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { UserContext } from '@/users/contexts/UserContext';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import styled from '@emotion/styled';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@@ -111,7 +112,7 @@ export const SettingsRoleAssignmentTableRow = ({
|
||||
dateFormat,
|
||||
localeCatalog: dateLocale.localeCatalog,
|
||||
})
|
||||
: 'Never expires';
|
||||
: t`Never expires`;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -133,11 +133,11 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
title={<SettingsRoleLabelContainer roleId={roleId} />}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
children: t`Workspace`,
|
||||
href: getSettingsPath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Roles',
|
||||
children: t`Roles`,
|
||||
href: getSettingsPath(SettingsPath.Roles),
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ export const SettingsSSOIdentitiesProvidersForm = () => {
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
fullWidth
|
||||
placeholder="Google OIDC"
|
||||
placeholder={t`Google OIDC`}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
/* @license Enterprise */
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { SettingsListCard } from '@/settings/components/SettingsListCard';
|
||||
import { SettingsSSOIdentityProviderRowRightContainer } from '@/settings/security/components/SSO/SettingsSSOIdentityProviderRowRightContainer';
|
||||
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState';
|
||||
@@ -26,7 +27,7 @@ export const SettingsSSOIdentitiesProvidersListCardWrapper = () => {
|
||||
<SettingsSSOIdentityProviderRowRightContainer SSOIdp={SSOIdp} />
|
||||
)}
|
||||
hasFooter
|
||||
footerButtonLabel="Add SSO Identity Provider"
|
||||
footerButtonLabel={t`Add SSO Identity Provider`}
|
||||
onFooterButtonClick={() => navigate(SettingsPath.NewSSOIdentityProvider)}
|
||||
/>
|
||||
);
|
||||
|
||||
+2
-2
@@ -173,7 +173,7 @@ export const SettingsSSOSAMLForm = () => {
|
||||
<SettingsTextInput
|
||||
instanceId="sso-saml-acs-url"
|
||||
disabled={true}
|
||||
label="ACS Url"
|
||||
label={t`ACS Url`}
|
||||
value={acsUrl}
|
||||
fullWidth
|
||||
/>
|
||||
@@ -194,7 +194,7 @@ export const SettingsSSOSAMLForm = () => {
|
||||
<SettingsTextInput
|
||||
instanceId="sso-saml-entity-id"
|
||||
disabled={true}
|
||||
label="Entity ID"
|
||||
label={t`Entity ID`}
|
||||
value={entityID}
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
+2
-2
@@ -70,7 +70,7 @@ export const SettingsApprovedAccessDomainsListCard = () => {
|
||||
RowRightComponent={({ item: approvedAccessDomain }) => (
|
||||
<>
|
||||
{!approvedAccessDomain.isValidated && (
|
||||
<Status color="orange" text="Pending" />
|
||||
<Status color="orange" text={t`Pending`} />
|
||||
)}
|
||||
<SettingsSecurityApprovedAccessDomainRowDropdownMenu
|
||||
approvedAccessDomain={approvedAccessDomain}
|
||||
@@ -78,7 +78,7 @@ export const SettingsApprovedAccessDomainsListCard = () => {
|
||||
</>
|
||||
)}
|
||||
hasFooter
|
||||
footerButtonLabel="Add Approved Access Domain"
|
||||
footerButtonLabel={t`Add Approved Access Domain`}
|
||||
onFooterButtonClick={() =>
|
||||
navigate(getSettingsPath(SettingsPath.NewApprovedAccessDomain))
|
||||
}
|
||||
|
||||
+6
-4
@@ -69,15 +69,17 @@ export const SettingsServerlessFunctionCodeEditor = ({
|
||||
const environmentVariables = {};
|
||||
|
||||
if (isDefined(environmentVariables)) {
|
||||
const envTypeDefinitions = Object.keys(environmentVariables)
|
||||
// eslint-disable-next-line lingui/no-unlocalized-strings
|
||||
.map((key) => `${key}: string;`)
|
||||
.join('\n');
|
||||
const environmentDefinition = `
|
||||
declare namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
${Object.keys(environmentVariables)
|
||||
.map((key) => `${key}: string;`)
|
||||
.join('\n')}
|
||||
${envTypeDefinitions}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
declare const process: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
};
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ export const SettingsServerlessFunctionNewForm = ({
|
||||
<StyledInputsContainer>
|
||||
<SettingsTextInput
|
||||
instanceId={nameTextInputId}
|
||||
placeholder="Name"
|
||||
placeholder={t`Name`}
|
||||
fullWidth
|
||||
autoFocusOnMount
|
||||
value={formValues.name}
|
||||
@@ -42,7 +42,7 @@ export const SettingsServerlessFunctionNewForm = ({
|
||||
/>
|
||||
<TextArea
|
||||
textAreaId={descriptionTextAreaId}
|
||||
placeholder="Description"
|
||||
placeholder={t`Description`}
|
||||
minRows={4}
|
||||
value={formValues.description}
|
||||
onChange={onChange('description')}
|
||||
|
||||
+18
-15
@@ -5,6 +5,7 @@ import { LinkChip } from 'twenty-ui/components';
|
||||
import { getSettingsPath } from 'twenty-shared/utils';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
|
||||
export const SettingsServerlessFunctionTabEnvironmentVariablesSection = () => {
|
||||
const { applicationId = '' } = useParams<{ applicationId: string }>();
|
||||
@@ -12,22 +13,24 @@ export const SettingsServerlessFunctionTabEnvironmentVariablesSection = () => {
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`Environment Variables`}
|
||||
description="Accessible in your function via process.env.KEY"
|
||||
description={t`Accessible in your function via process.env.KEY`}
|
||||
/>
|
||||
Environment variables are defined at application level for all functions.
|
||||
Please check{' '}
|
||||
<LinkChip
|
||||
label={'application detail page'}
|
||||
to={getSettingsPath(
|
||||
SettingsPath.ApplicationDetail,
|
||||
{
|
||||
applicationId,
|
||||
},
|
||||
undefined,
|
||||
'settings',
|
||||
)}
|
||||
/>
|
||||
.
|
||||
<Trans>
|
||||
Environment variables are defined at application level for all
|
||||
functions. Please check{' '}
|
||||
<LinkChip
|
||||
label={t`application detail page`}
|
||||
to={getSettingsPath(
|
||||
SettingsPath.ApplicationDetail,
|
||||
{
|
||||
applicationId,
|
||||
},
|
||||
undefined,
|
||||
'settings',
|
||||
)}
|
||||
/>
|
||||
.
|
||||
</Trans>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ export const SettingsServerlessFunctionsTable = ({
|
||||
<Table>
|
||||
<StyledTableRow>
|
||||
<TableHeader>{t`Name`}</TableHeader>
|
||||
<TableHeader>Runtime</TableHeader>
|
||||
<TableHeader>{t`Runtime`}</TableHeader>
|
||||
<TableHeader></TableHeader>
|
||||
</StyledTableRow>
|
||||
<StyledTableBody>
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ export const SettingsServerlessFunctionCodeEditorTab = ({
|
||||
options={{
|
||||
readOnly: true,
|
||||
readOnlyMessage: {
|
||||
value: 'Managed serverless functions are not editable',
|
||||
value: t`Managed serverless functions are not editable`,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ export const SettingsServerlessFunctionTabEnvironmentVariableTableRow = ({
|
||||
onChange={(newKey) =>
|
||||
setEditedEnvVariable({ ...editedEnvVariable, key: newKey })
|
||||
}
|
||||
placeholder="Name"
|
||||
placeholder={t`Name`}
|
||||
fullWidth
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -105,7 +105,7 @@ export const SettingsServerlessFunctionTabEnvironmentVariableTableRow = ({
|
||||
dropdownId={dropDownId}
|
||||
clickableComponent={
|
||||
<LightIconButton
|
||||
aria-label="Env Variable Options"
|
||||
aria-label={t`Env Variable Options`}
|
||||
Icon={IconDotsVertical}
|
||||
accent="tertiary"
|
||||
/>
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ export const SettingsServerlessFunctionTestTab = ({
|
||||
/>
|
||||
{serverlessFunctionTestData.output.logs.length > 0 && (
|
||||
<StyledCodeEditorContainer>
|
||||
<InputLabel>Logs</InputLabel>
|
||||
<InputLabel>{t`Logs`}</InputLabel>
|
||||
<TextArea
|
||||
textAreaId={testLogsTextAreaId}
|
||||
value={isTesting ? '' : serverlessFunctionTestData.output.logs}
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ export const SettingsServerlessFunctionTriggersTab = ({
|
||||
<StyledTableCell>{routeTrigger.httpMethod}</StyledTableCell>
|
||||
<StyledTableCell>
|
||||
<Tag
|
||||
text={routeTrigger.isAuthRequired ? 'True' : 'False'}
|
||||
text={routeTrigger.isAuthRequired ? t`True` : t`False`}
|
||||
color={routeTrigger.isAuthRequired ? 'green' : 'orange'}
|
||||
weight="medium"
|
||||
/>
|
||||
|
||||
+5
-5
@@ -6,7 +6,7 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { useLoadCurrentUser } from '@/users/hooks/useLoadCurrentUser';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -103,7 +103,7 @@ export const DeleteTwoFactorAuthentication = () => {
|
||||
title={t`2FA Method Reset`}
|
||||
subtitle={
|
||||
isTwoFactorAuthenticationEnforced ? (
|
||||
<>
|
||||
<Trans>
|
||||
This will permanently delete your two factor authentication
|
||||
method.
|
||||
<br />
|
||||
@@ -111,13 +111,13 @@ export const DeleteTwoFactorAuthentication = () => {
|
||||
after deletion and will be asked to configure it again upon login.{' '}
|
||||
<br />
|
||||
Please type in your email to confirm.
|
||||
</>
|
||||
</Trans>
|
||||
) : (
|
||||
<>
|
||||
<Trans>
|
||||
This action cannot be undone. This will permanently reset your two
|
||||
factor authentication method. <br /> Please type in your email to
|
||||
confirm.
|
||||
</>
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
onConfirmClick={reset2FA}
|
||||
|
||||
+5
-4
@@ -1,3 +1,4 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
@@ -94,8 +95,8 @@ export const WorkspaceMemberPictureUploader = ({
|
||||
setErrorMessage(null);
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : 'Failed to upload picture';
|
||||
setErrorMessage('An error occurred while uploading the picture.');
|
||||
error instanceof Error ? error.message : t`Failed to upload picture`;
|
||||
setErrorMessage(t`An error occurred while uploading the picture.`);
|
||||
enqueueErrorSnackBar({ message });
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
@@ -130,8 +131,8 @@ export const WorkspaceMemberPictureUploader = ({
|
||||
setErrorMessage(null);
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : 'Failed to remove picture';
|
||||
setErrorMessage('An error occurred while removing the picture.');
|
||||
error instanceof Error ? error.message : t`Failed to remove picture`;
|
||||
setErrorMessage(t`An error occurred while removing the picture.`);
|
||||
enqueueErrorSnackBar({ message });
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
|
||||
@@ -92,7 +92,7 @@ export const NameField = ({
|
||||
label={t`Name`}
|
||||
value={displayName}
|
||||
onChange={setDisplayName}
|
||||
placeholder="Apple"
|
||||
placeholder={t`Apple`}
|
||||
fullWidth
|
||||
/>
|
||||
</StyledComboInputContainer>
|
||||
|
||||
Reference in New Issue
Block a user