Continue Frontend localization (#9909)
Translation more content on the frontend
This commit is contained in:
@ -6,6 +6,7 @@ import { EventFieldDiffValueEffect } from '@/activities/timeline-activities/rows
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
|
||||
type EventFieldDiffProps = {
|
||||
diffRecord: Record<string, any>;
|
||||
@ -57,7 +58,9 @@ export const EventFieldDiff = ({
|
||||
<StyledEventFieldDiffContainer>
|
||||
<EventFieldDiffLabel fieldMetadataItem={fieldMetadataItem} />→
|
||||
{isUpdatedToEmpty ? (
|
||||
<StyledEmptyValue>Empty</StyledEmptyValue>
|
||||
<StyledEmptyValue>
|
||||
<Trans>Empty</Trans>
|
||||
</StyledEmptyValue>
|
||||
) : (
|
||||
<>
|
||||
<EventFieldDiffValueEffect
|
||||
|
||||
@ -11,8 +11,9 @@ import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-sto
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { IconX, LightIconButton, isDefined, useIsMobile } from 'twenty-ui';
|
||||
import { IconX, isDefined, LightIconButton, useIsMobile } from 'twenty-ui';
|
||||
|
||||
const StyledInputContainer = styled.div`
|
||||
align-items: center;
|
||||
@ -71,6 +72,8 @@ export const CommandMenuTopBar = () => {
|
||||
commandMenuSearchState,
|
||||
);
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setCommandMenuSearch(event.target.value);
|
||||
};
|
||||
@ -108,7 +111,7 @@ export const CommandMenuTopBar = () => {
|
||||
<StyledInput
|
||||
autoFocus
|
||||
value={commandMenuSearch}
|
||||
placeholder="Type anything"
|
||||
placeholder={t`Type anything`}
|
||||
onChange={handleSearchChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -11,6 +11,7 @@ import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigat
|
||||
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
|
||||
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { IconFolderPlus, LightIconButton, isDefined } from 'twenty-ui';
|
||||
|
||||
@ -24,6 +25,8 @@ export const CurrentWorkspaceMemberFavoritesFolders = () => {
|
||||
|
||||
const loading = useIsPrefetchLoading();
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
const {
|
||||
toggleNavigationSection,
|
||||
isNavigationSectionOpenState,
|
||||
@ -52,7 +55,7 @@ export const CurrentWorkspaceMemberFavoritesFolders = () => {
|
||||
<NavigationDrawerSection>
|
||||
<NavigationDrawerAnimatedCollapseWrapper>
|
||||
<NavigationDrawerSectionTitle
|
||||
label="Favorites"
|
||||
label={t`Favorites`}
|
||||
onClick={toggleNavigationSection}
|
||||
rightIcon={
|
||||
<LightIconButton
|
||||
|
||||
@ -2,11 +2,13 @@ import { useWorkspaceFavorites } from '@/favorites/hooks/useWorkspaceFavorites';
|
||||
import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
|
||||
import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader';
|
||||
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
export const WorkspaceFavorites = () => {
|
||||
const { workspaceFavoritesObjectMetadataItems } = useWorkspaceFavorites();
|
||||
|
||||
const loading = useIsPrefetchLoading();
|
||||
const { t } = useLingui();
|
||||
|
||||
if (loading) {
|
||||
return <NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader />;
|
||||
@ -14,7 +16,7 @@ export const WorkspaceFavorites = () => {
|
||||
|
||||
return (
|
||||
<NavigationDrawerSectionForObjectMetadataItems
|
||||
sectionTitle={'Workspace'}
|
||||
sectionTitle={t`Workspace`}
|
||||
objectMetadataItems={workspaceFavoritesObjectMetadataItems}
|
||||
isRemote={false}
|
||||
/>
|
||||
|
||||
@ -5,8 +5,11 @@ import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata
|
||||
import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
export const NavigationDrawerOpenedSection = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
const filteredActiveObjectMetadataItems = activeObjectMetadataItems.filter(
|
||||
(item) => !item.isRemote,
|
||||
@ -47,7 +50,7 @@ export const NavigationDrawerOpenedSection = () => {
|
||||
return (
|
||||
shouldDisplayObjectInOpenedSection && (
|
||||
<NavigationDrawerSectionForObjectMetadataItems
|
||||
sectionTitle={'Opened'}
|
||||
sectionTitle={t`Opened`}
|
||||
objectMetadataItems={[objectMetadataItem]}
|
||||
isRemote={false}
|
||||
/>
|
||||
|
||||
@ -2,6 +2,7 @@ import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdow
|
||||
import { useResetFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useResetFilterDropdown';
|
||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
|
||||
export const MultipleFiltersButton = () => {
|
||||
const { resetFilterDropdown } = useResetFilterDropdown();
|
||||
@ -20,7 +21,7 @@ export const MultipleFiltersButton = () => {
|
||||
onClick={handleClick}
|
||||
isUnfolded={isDropdownOpen}
|
||||
>
|
||||
Filter
|
||||
<Trans>Filter</Trans>
|
||||
</StyledHeaderDropdownButton>
|
||||
);
|
||||
};
|
||||
|
||||
@ -26,6 +26,8 @@ import { FeatureFlagKey } from '~/generated/graphql';
|
||||
|
||||
import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState';
|
||||
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
export const StyledInput = styled.input`
|
||||
background: transparent;
|
||||
border: none;
|
||||
@ -161,12 +163,14 @@ export const ObjectFilterDropdownFilterSelect = ({
|
||||
isAdvancedFilterButtonVisible &&
|
||||
isAdvancedFiltersEnabled;
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledInput
|
||||
value={objectFilterDropdownSearchInput}
|
||||
autoFocus
|
||||
placeholder="Search fields"
|
||||
placeholder={t`Search fields`}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setObjectFilterDropdownSearchInput(event.target.value)
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import { selectedOperandInDropdownComponentState } from '@/object-record/object-
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { getRecordFilterOperandsForRecordFilterDefinition } from '../../record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition';
|
||||
import { GenericEntityFilterChip } from './GenericEntityFilterChip';
|
||||
import { ObjectFilterDropdownRecordSelect } from './ObjectFilterDropdownRecordSelect';
|
||||
@ -58,6 +59,7 @@ export const SingleEntityObjectFilterDropdownButton = ({
|
||||
]);
|
||||
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
@ -76,7 +78,7 @@ export const SingleEntityObjectFilterDropdownButton = ({
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
'Filter'
|
||||
t`Filter`
|
||||
)}
|
||||
<IconChevronDown size={theme.icon.size.md} />
|
||||
</StyledHeaderDropdownButton>
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
getCanvasElementForDropdownTesting,
|
||||
} from 'twenty-ui';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
@ -130,6 +131,7 @@ const meta: Meta<typeof MultipleFiltersDropdownButton> = {
|
||||
SnackBarDecorator,
|
||||
ComponentDecorator,
|
||||
IconsProviderDecorator,
|
||||
I18nFrontDecorator,
|
||||
],
|
||||
args: {
|
||||
hotkeyScope: {
|
||||
|
||||
@ -11,6 +11,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
|
||||
type ObjectOptionsDropdownProps = {
|
||||
viewType: ViewType;
|
||||
@ -36,7 +37,7 @@ export const ObjectOptionsDropdown = ({
|
||||
dropdownOffset={{ y: DROPDOWN_OFFSET_Y }}
|
||||
clickableComponent={
|
||||
<StyledHeaderDropdownButton isUnfolded={isDropdownOpen}>
|
||||
Options
|
||||
<Trans>Options</Trans>
|
||||
</StyledHeaderDropdownButton>
|
||||
}
|
||||
onClose={handleResetContent}
|
||||
|
||||
@ -25,6 +25,7 @@ import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { SORT_DIRECTIONS, SortDirection } from '../types/SortDirection';
|
||||
|
||||
export const StyledInput = styled.input`
|
||||
@ -172,6 +173,8 @@ export const ObjectSortDropdownButton = ({
|
||||
|
||||
const { isDropdownOpen } = useDropdown(OBJECT_SORT_DROPDOWN_ID);
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownId={OBJECT_SORT_DROPDOWN_ID}
|
||||
@ -182,7 +185,7 @@ export const ObjectSortDropdownButton = ({
|
||||
onClick={handleButtonClick}
|
||||
isUnfolded={isDropdownOpen}
|
||||
>
|
||||
Sort
|
||||
<Trans>Sort</Trans>
|
||||
</StyledHeaderDropdownButton>
|
||||
}
|
||||
dropdownComponents={
|
||||
@ -194,7 +197,9 @@ export const ObjectSortDropdownButton = ({
|
||||
<MenuItem
|
||||
key={index}
|
||||
onClick={() => handleSortDirectionClick(sortDirection)}
|
||||
text={sortDirection === 'asc' ? 'Ascending' : 'Descending'}
|
||||
text={
|
||||
sortDirection === 'asc' ? t`Ascending` : t`Descending`
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
@ -206,12 +211,12 @@ export const ObjectSortDropdownButton = ({
|
||||
setIsSortDirectionMenuUnfolded(!isSortDirectionMenuUnfolded)
|
||||
}
|
||||
>
|
||||
{selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'}
|
||||
{selectedSortDirection === 'asc' ? t`Ascending` : t`Descending`}
|
||||
</DropdownMenuHeader>
|
||||
<StyledInput
|
||||
autoFocus
|
||||
value={objectSortDropdownSearchInput}
|
||||
placeholder="Search fields"
|
||||
placeholder={t`Search fields`}
|
||||
onChange={(event) =>
|
||||
setObjectSortDropdownSearchInput(event.target.value)
|
||||
}
|
||||
|
||||
@ -10,9 +10,11 @@ import {
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { onContentChange, closeDropdown } =
|
||||
useDropdown<RecordBoardColumnHeaderAggregateDropdownContextValue>({
|
||||
context: RecordBoardColumnHeaderAggregateDropdownContext,
|
||||
@ -33,14 +35,14 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
|
||||
onClick={() => {
|
||||
onContentChange('countAggregateOperationsOptions');
|
||||
}}
|
||||
text={'Count'}
|
||||
text={t`Count`}
|
||||
hasSubMenu
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
onContentChange('percentAggregateOperationsOptions');
|
||||
}}
|
||||
text={'Percent'}
|
||||
text={t`Percent`}
|
||||
hasSubMenu
|
||||
/>
|
||||
<MenuItem
|
||||
@ -54,7 +56,7 @@ export const RecordBoardColumnHeaderAggregateDropdownMenuContent = () => {
|
||||
onClick={() => {
|
||||
onContentChange('moreAggregateOperationOptions');
|
||||
}}
|
||||
text={'More options'}
|
||||
text={t`More options`}
|
||||
hasSubMenu
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
|
||||
@ -166,7 +166,7 @@ describe('computeAggregateValueAndLabel', () => {
|
||||
|
||||
expect(result).toEqual({
|
||||
label: 'Earliest',
|
||||
labelWithFieldName: 'Earliest date of Created At',
|
||||
labelWithFieldName: 'Earliest of Created At',
|
||||
value: '1 Jan, 2023 12:00',
|
||||
});
|
||||
});
|
||||
@ -201,7 +201,7 @@ describe('computeAggregateValueAndLabel', () => {
|
||||
expect(result).toEqual({
|
||||
value: '31 Dec, 2023 23:59',
|
||||
label: 'Latest',
|
||||
labelWithFieldName: 'Latest date of Updated At',
|
||||
labelWithFieldName: 'Latest of Updated At',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/Agg
|
||||
import { COUNT_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/countAggregateOperationOptions';
|
||||
import { PERCENT_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/percentAggregateOperationOptions';
|
||||
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION } from 'twenty-shared';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
@ -47,8 +48,10 @@ export const computeAggregateValueAndLabel = ({
|
||||
data?.[FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION]?.[
|
||||
AGGREGATE_OPERATIONS.count
|
||||
],
|
||||
label: `${getAggregateOperationLabel(AGGREGATE_OPERATIONS.count)}`,
|
||||
labelWithFieldName: `${getAggregateOperationLabel(AGGREGATE_OPERATIONS.count)}`,
|
||||
label: getAggregateOperationLabel(AGGREGATE_OPERATIONS.count),
|
||||
labelWithFieldName: getAggregateOperationLabel(
|
||||
AGGREGATE_OPERATIONS.count,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@ -118,15 +121,16 @@ export const computeAggregateValueAndLabel = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
const label = getAggregateOperationShortLabel(aggregateOperation);
|
||||
const aggregateLabel = t(getAggregateOperationShortLabel(aggregateOperation));
|
||||
const fieldLabel = field.label;
|
||||
const labelWithFieldName =
|
||||
aggregateOperation === AGGREGATE_OPERATIONS.count
|
||||
? `${getAggregateOperationLabel(AGGREGATE_OPERATIONS.count)}`
|
||||
: `${getAggregateOperationLabel(aggregateOperation)} of ${field.label}`;
|
||||
: t`${aggregateLabel} of ${fieldLabel}`;
|
||||
|
||||
return {
|
||||
value,
|
||||
label,
|
||||
label: aggregateLabel,
|
||||
labelWithFieldName,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,35 +1,36 @@
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { DATE_AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/DateAggregateOperations';
|
||||
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
export const getAggregateOperationLabel = (
|
||||
operation: ExtendedAggregateOperations,
|
||||
) => {
|
||||
switch (operation) {
|
||||
case AGGREGATE_OPERATIONS.min:
|
||||
return 'Min';
|
||||
return t`Min`;
|
||||
case AGGREGATE_OPERATIONS.max:
|
||||
return 'Max';
|
||||
return t`Max`;
|
||||
case AGGREGATE_OPERATIONS.avg:
|
||||
return 'Average';
|
||||
return t`Average`;
|
||||
case AGGREGATE_OPERATIONS.sum:
|
||||
return 'Sum';
|
||||
return t`Sum`;
|
||||
case AGGREGATE_OPERATIONS.count:
|
||||
return 'Count all';
|
||||
return t`Count all`;
|
||||
case AGGREGATE_OPERATIONS.countEmpty:
|
||||
return 'Count empty';
|
||||
return t`Count empty`;
|
||||
case AGGREGATE_OPERATIONS.countNotEmpty:
|
||||
return 'Count not empty';
|
||||
return t`Count not empty`;
|
||||
case AGGREGATE_OPERATIONS.countUniqueValues:
|
||||
return 'Count unique values';
|
||||
return t`Count unique values`;
|
||||
case AGGREGATE_OPERATIONS.percentageEmpty:
|
||||
return 'Percent empty';
|
||||
return t`Percent empty`;
|
||||
case AGGREGATE_OPERATIONS.percentageNotEmpty:
|
||||
return 'Percent not empty';
|
||||
return t`Percent not empty`;
|
||||
case DATE_AGGREGATE_OPERATIONS.earliest:
|
||||
return 'Earliest date';
|
||||
return t`Earliest date`;
|
||||
case DATE_AGGREGATE_OPERATIONS.latest:
|
||||
return 'Latest date';
|
||||
return t`Latest date`;
|
||||
default:
|
||||
throw new Error(`Unknown aggregate operation: ${operation}`);
|
||||
}
|
||||
|
||||
@ -1,33 +1,34 @@
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { DATE_AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/DateAggregateOperations';
|
||||
import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
|
||||
export const getAggregateOperationShortLabel = (
|
||||
operation: ExtendedAggregateOperations,
|
||||
) => {
|
||||
switch (operation) {
|
||||
case AGGREGATE_OPERATIONS.min:
|
||||
return 'Min';
|
||||
return msg`Min`;
|
||||
case AGGREGATE_OPERATIONS.max:
|
||||
return 'Max';
|
||||
return msg`Max`;
|
||||
case AGGREGATE_OPERATIONS.avg:
|
||||
return 'Average';
|
||||
return msg`Average`;
|
||||
case AGGREGATE_OPERATIONS.sum:
|
||||
return 'Sum';
|
||||
return msg`Sum`;
|
||||
case AGGREGATE_OPERATIONS.count:
|
||||
return 'All';
|
||||
return msg`All`;
|
||||
case AGGREGATE_OPERATIONS.countEmpty:
|
||||
case AGGREGATE_OPERATIONS.percentageEmpty:
|
||||
return 'Empty';
|
||||
return msg`Empty`;
|
||||
case AGGREGATE_OPERATIONS.countNotEmpty:
|
||||
case AGGREGATE_OPERATIONS.percentageNotEmpty:
|
||||
return 'Not empty';
|
||||
return msg`Not empty`;
|
||||
case AGGREGATE_OPERATIONS.countUniqueValues:
|
||||
return 'Unique';
|
||||
return msg`Unique`;
|
||||
case DATE_AGGREGATE_OPERATIONS.earliest:
|
||||
return 'Earliest';
|
||||
return msg`Earliest`;
|
||||
case DATE_AGGREGATE_OPERATIONS.latest:
|
||||
return 'Latest';
|
||||
return msg`Latest`;
|
||||
default:
|
||||
throw new Error(`Unknown aggregate operation: ${operation}`);
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
useRecordInlineCellContext,
|
||||
} from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
|
||||
import { RecordInlineCellButton } from '@/object-record/record-inline-cell/components/RecordInlineCellEditButton';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
const StyledRecordInlineCellNormalModeOuterContainer = styled.div<
|
||||
Pick<
|
||||
@ -61,6 +62,8 @@ export const RecordInlineCellDisplayMode = ({
|
||||
}: React.PropsWithChildren<unknown>) => {
|
||||
const { isFocused } = useFieldFocus();
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
const {
|
||||
editModeContentOnly,
|
||||
|
||||
@ -80,7 +83,7 @@ export const RecordInlineCellDisplayMode = ({
|
||||
|
||||
const shouldDisplayEditModeOnFocus = isFocused && isFieldInputOnly;
|
||||
|
||||
const emptyPlaceHolder = showLabel ? 'Empty' : label;
|
||||
const emptyPlaceHolder = showLabel ? t`Empty` : label;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -32,6 +32,7 @@ import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { View } from '@/views/types/View';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
||||
import { getAppPath } from '~/utils/navigation/getAppPath';
|
||||
type RecordDetailRelationSectionProps = {
|
||||
@ -45,6 +46,7 @@ const StyledAddDropdown = styled(Dropdown)`
|
||||
export const RecordDetailRelationSection = ({
|
||||
loading,
|
||||
}: RecordDetailRelationSectionProps) => {
|
||||
const { t } = useLingui();
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
const {
|
||||
fieldName,
|
||||
@ -168,6 +170,8 @@ export const RecordDetailRelationSection = ({
|
||||
|
||||
if (loading) return null;
|
||||
|
||||
const relationRecordsCount = relationRecords.length;
|
||||
|
||||
return (
|
||||
<RecordDetailSection>
|
||||
<RecordDetailSectionHeader
|
||||
@ -177,8 +181,8 @@ export const RecordDetailRelationSection = ({
|
||||
? {
|
||||
to: filterLinkHref,
|
||||
label:
|
||||
relationRecords.length > 0
|
||||
? `All (${relationRecords.length})`
|
||||
relationRecordsCount > 0
|
||||
? t`All (${relationRecordsCount})`
|
||||
: '',
|
||||
}
|
||||
: undefined
|
||||
|
||||
@ -9,9 +9,11 @@ import { DATE_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/r
|
||||
import { PERCENT_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/percentAggregateOperationOptions';
|
||||
import { STANDARD_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/standardAggregateOperationOptions';
|
||||
import { getAvailableAggregateOperationsForFieldMetadataType } from '@/object-record/record-table/record-table-footer/utils/getAvailableAggregateOperationsForFieldMetadataType';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
export const RecordTableColumnAggregateFooterDropdownContent = () => {
|
||||
const { t } = useLingui();
|
||||
const { currentContentId, fieldMetadataType } = useDropdown({
|
||||
context: RecordTableColumnAggregateFooterDropdownContext,
|
||||
});
|
||||
@ -33,7 +35,7 @@ export const RecordTableColumnAggregateFooterDropdownContent = () => {
|
||||
return (
|
||||
<RecordTableColumnAggregateFooterDropdownSubmenuContent
|
||||
aggregateOperations={aggregateOperations}
|
||||
title="More options"
|
||||
title={t`More options`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -47,7 +49,7 @@ export const RecordTableColumnAggregateFooterDropdownContent = () => {
|
||||
return (
|
||||
<RecordTableColumnAggregateFooterDropdownSubmenuContent
|
||||
aggregateOperations={aggregateOperations}
|
||||
title="Count"
|
||||
title={t`Count`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -61,7 +63,7 @@ export const RecordTableColumnAggregateFooterDropdownContent = () => {
|
||||
return (
|
||||
<RecordTableColumnAggregateFooterDropdownSubmenuContent
|
||||
aggregateOperations={aggregateOperations}
|
||||
title="Percent"
|
||||
title={t`Percent`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import { onToggleColumnFilterComponentState } from '@/object-record/record-table
|
||||
import { onToggleColumnSortComponentState } from '@/object-record/record-table/states/onToggleColumnSortComponentState';
|
||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useTableColumns } from '../../hooks/useTableColumns';
|
||||
import { ColumnDefinition } from '../../types/ColumnDefinition';
|
||||
|
||||
@ -26,6 +27,8 @@ export type RecordTableColumnHeadDropdownMenuProps = {
|
||||
export const RecordTableColumnHeadDropdownMenu = ({
|
||||
column,
|
||||
}: RecordTableColumnHeadDropdownMenuProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const visibleTableColumns = useRecoilComponentValueV2(
|
||||
visibleTableColumnsComponentSelector,
|
||||
);
|
||||
@ -96,14 +99,14 @@ export const RecordTableColumnHeadDropdownMenu = ({
|
||||
<MenuItem
|
||||
LeftIcon={IconFilter}
|
||||
onClick={handleFilterClick}
|
||||
text="Filter"
|
||||
text={t`Filter`}
|
||||
/>
|
||||
)}
|
||||
{isSortable && (
|
||||
<MenuItem
|
||||
LeftIcon={IconSortDescending}
|
||||
onClick={handleSortClick}
|
||||
text="Sort"
|
||||
text={t`Sort`}
|
||||
/>
|
||||
)}
|
||||
{showSeparator && <DropdownMenuSeparator />}
|
||||
@ -111,21 +114,21 @@ export const RecordTableColumnHeadDropdownMenu = ({
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowLeft}
|
||||
onClick={handleColumnMoveLeft}
|
||||
text="Move left"
|
||||
text={t`Move left`}
|
||||
/>
|
||||
)}
|
||||
{canMoveRight && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowRight}
|
||||
onClick={handleColumnMoveRight}
|
||||
text="Move right"
|
||||
text={t`Move right`}
|
||||
/>
|
||||
)}
|
||||
{canHide && (
|
||||
<MenuItem
|
||||
LeftIcon={IconEyeOff}
|
||||
onClick={handleColumnVisibility}
|
||||
text="Hide"
|
||||
text={t`Hide`}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenuItemsContainer>
|
||||
|
||||
@ -31,13 +31,6 @@ const StyledImage = styled.img<{ isFirstCard: boolean }>`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledFallbackDiv = styled.div<{ isFirstCard: boolean }>`
|
||||
background-color: ${({ theme }) => theme.background.tertiary};
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
height: ${({ isFirstCard }) => (isFirstCard ? '240px' : '120px')};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const SettingsLabContent = () => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const { labPublicFeatureFlags, handleLabPublicFeatureFlagUpdate } =
|
||||
@ -57,27 +50,36 @@ export const SettingsLabContent = () => {
|
||||
return (
|
||||
currentWorkspace?.id && (
|
||||
<StyledCardGrid>
|
||||
{labPublicFeatureFlags.map((flag, index) => (
|
||||
<Card key={flag.key} rounded>
|
||||
{flag.metadata.imagePath && !hasImageLoadingError[flag.key] ? (
|
||||
<StyledImage
|
||||
src={flag.metadata.imagePath}
|
||||
alt={flag.metadata.label}
|
||||
isFirstCard={index === 0}
|
||||
onError={() => handleImageError(flag.key)}
|
||||
{[...labPublicFeatureFlags]
|
||||
.sort((a, b) => {
|
||||
// Sort flags with images first
|
||||
if (a.metadata.imagePath !== '' && b.metadata.imagePath === '')
|
||||
return -1;
|
||||
if (a.metadata.imagePath === '' && b.metadata.imagePath !== '')
|
||||
return 1;
|
||||
return 0;
|
||||
})
|
||||
.map((flag, index) => (
|
||||
<Card key={flag.key} rounded>
|
||||
{flag.metadata.imagePath && !hasImageLoadingError[flag.key] ? (
|
||||
<StyledImage
|
||||
src={flag.metadata.imagePath}
|
||||
alt={flag.metadata.label}
|
||||
isFirstCard={index === 0}
|
||||
onError={() => handleImageError(flag.key)}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<SettingsOptionCardContentToggle
|
||||
title={flag.metadata.label}
|
||||
description={flag.metadata.description}
|
||||
checked={flag.value}
|
||||
onChange={(value) => handleToggle(flag.key, value)}
|
||||
toggleCentered={false}
|
||||
/>
|
||||
) : (
|
||||
<StyledFallbackDiv isFirstCard={index === 0} />
|
||||
)}
|
||||
<SettingsOptionCardContentToggle
|
||||
title={flag.metadata.label}
|
||||
description={flag.metadata.description}
|
||||
checked={flag.value}
|
||||
onChange={(value) => handleToggle(flag.key, value)}
|
||||
toggleCentered={false}
|
||||
/>
|
||||
</Card>
|
||||
))}
|
||||
</Card>
|
||||
))}
|
||||
</StyledCardGrid>
|
||||
)
|
||||
);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { ChangeEvent, ReactNode, useRef } from 'react';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
import { AppTooltip, Avatar, AvatarType, IconComponent } from 'twenty-ui';
|
||||
@ -153,7 +154,7 @@ export const ShowPageSummaryCard = ({
|
||||
<StyledTitle isMobile={isMobile}>{title}</StyledTitle>
|
||||
{beautifiedCreatedAt && (
|
||||
<StyledDate isMobile={isMobile} id={dateElementId}>
|
||||
Added {beautifiedCreatedAt}
|
||||
<Trans>Added {beautifiedCreatedAt}</Trans>
|
||||
</StyledDate>
|
||||
)}
|
||||
<AppTooltip
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { MessageDescriptor } from '@lingui/core';
|
||||
import { IconComponent } from 'twenty-ui';
|
||||
|
||||
export type TableFieldMetadata<ItemType> = {
|
||||
fieldLabel: string;
|
||||
fieldLabel: MessageDescriptor;
|
||||
fieldName: keyof ItemType;
|
||||
fieldType: 'string' | 'number';
|
||||
align: 'left' | 'right';
|
||||
|
||||
@ -35,6 +35,7 @@ import { mockedWorkspaceMemberData } from '~/testing/mock-data/users';
|
||||
|
||||
import { CurrentWorkspaceMemberFavoritesFolders } from '@/favorites/components/CurrentWorkspaceMemberFavoritesFolders';
|
||||
import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import jsonPage from '../../../../../../../package.json';
|
||||
import { NavigationDrawer } from '../NavigationDrawer';
|
||||
@ -50,6 +51,7 @@ const meta: Meta<typeof NavigationDrawer> = {
|
||||
SnackBarDecorator,
|
||||
ObjectMetadataItemsDecorator,
|
||||
PrefetchLoadedDecorator,
|
||||
I18nFrontDecorator,
|
||||
(Story) => {
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
|
||||
Reference in New Issue
Block a user