fix(front): respect user number format for counts and aggregates (#21894)
## Problem
Several user-facing numbers were rendered raw (e.g. `153909`) instead of
honoring the workspace member's **Number format** preference (e.g. `153
909` with `Spaces and comma`). The formatting utilities already existed
(`formatNumber` / `useNumberFormat`) but were not applied on these
surfaces.
## Root cause
`transformAggregateRawValueIntoAggregateDisplayValue` — the shared
helper behind every table/board/chart aggregate — returned the `COUNT`
branch as a raw string and never threaded the user's locale format into
`formatNumber` for the other branches (so they silently fell back to
`COMMAS_AND_DOT`).
Its existing `numberFormat` param actually held the chart `SHORT`/`FULL`
abbreviation setting, so it is renamed to `chartNumberFormat`, and a new
`numberFormat: NumberFormat` now carries the locale separators.
## Surfaces fixed
- Record table footer aggregates, including the raw **"Count all"**
total
- Record board column / group-section aggregates
- Aggregate chart and pie-chart center metric (including their raw
`COUNT` early-returns)
- View picker `<view> · <count>` total
- Record show breadcrumb pagination `(x/y)`
- Record index header and side panel `N selected` counts
The board-column header needs no change — it now receives an
already-formatted string from the transform.
## Out of scope (intentionally left raw)
The editable `SettingsCounter` input (formatting would break parsing),
the advanced-filter pill, the `+N` overflow badge, and the AI routing
debug display.
## Testing
- New + existing unit tests pass
(`transformAggregateRawValueIntoAggregateDisplayValue`, `formatNumber`,
`useNumberFormat`), with added locale-aware coverage (`SPACES_AND_COMMA`
→ `153 909`, `DOTS_AND_COMMA` → `153.909`).
- `nx typecheck twenty-front`, oxlint and oxfmt on the diff all pass.
> Note: two i18n strings change placeholder shape (`{count} selected` →
`{0} selected`); a `lingui:extract` will refresh the catalogs (runtime
falls back to source text meanwhile).
https://claude.ai/code/session_013XNL2Xa11Bw7fsnPFQgsGX
---
_Generated by [Claude
Code](https://claude.ai/code/session_013XNL2Xa11Bw7fsnPFQgsGX)_
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21894?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
+64
-4
@@ -4,6 +4,7 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte
|
||||
import { transformAggregateRawValueIntoAggregateDisplayValue } from '@/object-record/record-aggregate/utils/transformAggregateRawValueIntoAggregateDisplayValue';
|
||||
import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { DateAggregateOperations } from '@/object-record/record-table/constants/DateAggregateOperations';
|
||||
import { NumberFormat } from '@/localization/constants/NumberFormat';
|
||||
import { enUS } from 'date-fns/locale';
|
||||
import { findByProperty } from 'twenty-shared/utils';
|
||||
import {
|
||||
@@ -48,6 +49,50 @@ describe('transformAggregateRawValueIntoAggregateDisplayValue', () => {
|
||||
).toBe('4');
|
||||
});
|
||||
|
||||
it('should format large COUNT values with the default number format', () => {
|
||||
expect(
|
||||
transformAggregateRawValueIntoAggregateDisplayValue({
|
||||
aggregateFieldMetadataItem: undefined,
|
||||
aggregateOperation: AggregateOperations.COUNT,
|
||||
aggregateRawValue: 153909,
|
||||
dateFormat: DateFormat.DAY_FIRST,
|
||||
timeFormat: TimeFormat.HOUR_24,
|
||||
localeCatalog: enUS,
|
||||
timeZone: 'UTC',
|
||||
}),
|
||||
).toBe('153,909');
|
||||
});
|
||||
|
||||
it('should format large COUNT values respecting the SPACES_AND_COMMA number format', () => {
|
||||
expect(
|
||||
transformAggregateRawValueIntoAggregateDisplayValue({
|
||||
aggregateFieldMetadataItem: undefined,
|
||||
aggregateOperation: AggregateOperations.COUNT,
|
||||
aggregateRawValue: 153909,
|
||||
dateFormat: DateFormat.DAY_FIRST,
|
||||
timeFormat: TimeFormat.HOUR_24,
|
||||
localeCatalog: enUS,
|
||||
timeZone: 'UTC',
|
||||
numberFormat: NumberFormat.SPACES_AND_COMMA,
|
||||
}),
|
||||
).toBe('153\u202F909');
|
||||
});
|
||||
|
||||
it('should format a COUNT value provided as a string', () => {
|
||||
expect(
|
||||
transformAggregateRawValueIntoAggregateDisplayValue({
|
||||
aggregateFieldMetadataItem: undefined,
|
||||
aggregateOperation: AggregateOperations.COUNT,
|
||||
aggregateRawValue: '153909',
|
||||
dateFormat: DateFormat.DAY_FIRST,
|
||||
timeFormat: TimeFormat.HOUR_24,
|
||||
localeCatalog: enUS,
|
||||
timeZone: 'UTC',
|
||||
numberFormat: NumberFormat.DOTS_AND_COMMA,
|
||||
}),
|
||||
).toBe('153.909');
|
||||
});
|
||||
|
||||
it('should return "-" for nullish aggregate raw value', () => {
|
||||
expect(
|
||||
transformAggregateRawValueIntoAggregateDisplayValue({
|
||||
@@ -123,6 +168,21 @@ describe('transformAggregateRawValueIntoAggregateDisplayValue', () => {
|
||||
).toBe('100,000,000');
|
||||
});
|
||||
|
||||
it('should return number formatted value respecting the DOTS_AND_COMMA number format', () => {
|
||||
expect(
|
||||
transformAggregateRawValueIntoAggregateDisplayValue({
|
||||
aggregateFieldMetadataItem: mockCompanyEmployeesFieldMetadataItem,
|
||||
aggregateOperation: AggregateOperations.SUM,
|
||||
aggregateRawValue: 100000000,
|
||||
dateFormat: DateFormat.DAY_FIRST,
|
||||
timeFormat: TimeFormat.HOUR_24,
|
||||
localeCatalog: enUS,
|
||||
timeZone: 'UTC',
|
||||
numberFormat: NumberFormat.DOTS_AND_COMMA,
|
||||
}),
|
||||
).toBe('100.000.000');
|
||||
});
|
||||
|
||||
it('should return full currency formatted value with FULL number format', () => {
|
||||
const mockCurrencyFieldMetadataItem = {
|
||||
...mockCompanyEmployeesFieldMetadataItem,
|
||||
@@ -138,7 +198,7 @@ describe('transformAggregateRawValueIntoAggregateDisplayValue', () => {
|
||||
timeFormat: TimeFormat.HOUR_24,
|
||||
localeCatalog: enUS,
|
||||
timeZone: 'UTC',
|
||||
numberFormat: ChartNumberFormat.FULL,
|
||||
chartNumberFormat: ChartNumberFormat.FULL,
|
||||
}),
|
||||
).toBe('230,440');
|
||||
});
|
||||
@@ -158,7 +218,7 @@ describe('transformAggregateRawValueIntoAggregateDisplayValue', () => {
|
||||
timeFormat: TimeFormat.HOUR_24,
|
||||
localeCatalog: enUS,
|
||||
timeZone: 'UTC',
|
||||
numberFormat: ChartNumberFormat.SHORT,
|
||||
chartNumberFormat: ChartNumberFormat.SHORT,
|
||||
}),
|
||||
).toBe('230.4k');
|
||||
});
|
||||
@@ -173,7 +233,7 @@ describe('transformAggregateRawValueIntoAggregateDisplayValue', () => {
|
||||
timeFormat: TimeFormat.HOUR_24,
|
||||
localeCatalog: enUS,
|
||||
timeZone: 'UTC',
|
||||
numberFormat: ChartNumberFormat.SHORT,
|
||||
chartNumberFormat: ChartNumberFormat.SHORT,
|
||||
}),
|
||||
).toBe('100m');
|
||||
});
|
||||
@@ -188,7 +248,7 @@ describe('transformAggregateRawValueIntoAggregateDisplayValue', () => {
|
||||
timeFormat: TimeFormat.HOUR_24,
|
||||
localeCatalog: enUS,
|
||||
timeZone: 'UTC',
|
||||
numberFormat: ChartNumberFormat.FULL,
|
||||
chartNumberFormat: ChartNumberFormat.FULL,
|
||||
}),
|
||||
).toBe('100,000,000');
|
||||
});
|
||||
|
||||
+11
-8
@@ -1,6 +1,7 @@
|
||||
import { type Locale } from 'date-fns';
|
||||
|
||||
import { type DateFormat } from '@/localization/constants/DateFormat';
|
||||
import { type NumberFormat } from '@/localization/constants/NumberFormat';
|
||||
import { type TimeFormat } from '@/localization/constants/TimeFormat';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { COUNT_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/countAggregateOperationOptions';
|
||||
@@ -26,6 +27,7 @@ export const transformAggregateRawValueIntoAggregateDisplayValue = ({
|
||||
timeZone,
|
||||
localeCatalog,
|
||||
numberFormat,
|
||||
chartNumberFormat,
|
||||
}: {
|
||||
aggregateFieldMetadataItem: Nullable<FieldMetadataItem>;
|
||||
aggregateOperation: ExtendedAggregateOperations;
|
||||
@@ -34,7 +36,8 @@ export const transformAggregateRawValueIntoAggregateDisplayValue = ({
|
||||
timeFormat: TimeFormat;
|
||||
timeZone: string;
|
||||
localeCatalog: Locale;
|
||||
numberFormat?: ChartNumberFormat;
|
||||
numberFormat?: NumberFormat;
|
||||
chartNumberFormat?: ChartNumberFormat;
|
||||
}): string => {
|
||||
if (!isDefined(aggregateRawValue)) {
|
||||
return '-';
|
||||
@@ -43,7 +46,7 @@ export const transformAggregateRawValueIntoAggregateDisplayValue = ({
|
||||
aggregateOperation as AggregateOperations,
|
||||
)
|
||||
) {
|
||||
return `${aggregateRawValue}`;
|
||||
return formatNumber(Number(aggregateRawValue), { format: numberFormat });
|
||||
} else if (!isDefined(aggregateFieldMetadataItem)) {
|
||||
return '-';
|
||||
} else if (
|
||||
@@ -51,13 +54,13 @@ export const transformAggregateRawValueIntoAggregateDisplayValue = ({
|
||||
aggregateOperation as AggregateOperations,
|
||||
)
|
||||
) {
|
||||
return `${formatNumber(Number(aggregateRawValue) * 100)}%`;
|
||||
return `${formatNumber(Number(aggregateRawValue) * 100, { format: numberFormat })}%`;
|
||||
} else {
|
||||
switch (aggregateFieldMetadataItem.type) {
|
||||
case FieldMetadataType.CURRENCY: {
|
||||
const amount = Number(aggregateRawValue) / 1_000_000;
|
||||
return numberFormat === ChartNumberFormat.FULL
|
||||
? formatNumber(amount, { decimals: 2 })
|
||||
return chartNumberFormat === ChartNumberFormat.FULL
|
||||
? formatNumber(amount, { decimals: 2, format: numberFormat })
|
||||
: formatToShortNumber(amount);
|
||||
}
|
||||
|
||||
@@ -65,11 +68,11 @@ export const transformAggregateRawValueIntoAggregateDisplayValue = ({
|
||||
const castedValue = Number(aggregateRawValue);
|
||||
const { decimals, type } = aggregateFieldMetadataItem.settings ?? {};
|
||||
if (type === 'percentage') {
|
||||
return `${formatNumber(castedValue * 100, { decimals })}%`;
|
||||
return `${formatNumber(castedValue * 100, { decimals, format: numberFormat })}%`;
|
||||
}
|
||||
return numberFormat === ChartNumberFormat.SHORT
|
||||
return chartNumberFormat === ChartNumberFormat.SHORT
|
||||
? formatToShortNumber(castedValue)
|
||||
: formatNumber(castedValue, { decimals });
|
||||
: formatNumber(castedValue, { decimals, format: numberFormat });
|
||||
}
|
||||
|
||||
case FieldMetadataType.DATE_TIME: {
|
||||
|
||||
+4
-1
@@ -3,6 +3,7 @@ import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainCo
|
||||
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
|
||||
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { RecordIndexPageHeaderIcon } from '@/object-record/record-index/components/RecordIndexPageHeaderIcon';
|
||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
@@ -39,6 +40,8 @@ export const RecordIndexPageHeader = () => {
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
);
|
||||
|
||||
const { formatNumber } = useNumberFormat();
|
||||
|
||||
const { objectNamePlural } = useRecordIndexContextOrThrow();
|
||||
|
||||
const objectMetadataItem =
|
||||
@@ -52,7 +55,7 @@ export const RecordIndexPageHeader = () => {
|
||||
<StyledTitle>{label}</StyledTitle>
|
||||
<>{'->'}</>
|
||||
<StyledSelectedRecordsCount>
|
||||
{t`${contextStoreNumberOfSelectedRecords} selected`}
|
||||
{t`${formatNumber(contextStoreNumberOfSelectedRecords)} selected`}
|
||||
</StyledSelectedRecordsCount>
|
||||
</StyledTitleWithSelectedRecords>
|
||||
) : (
|
||||
|
||||
+4
@@ -1,3 +1,4 @@
|
||||
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { transformAggregateRawValueIntoAggregateDisplayValue } from '@/object-record/record-aggregate/utils/transformAggregateRawValueIntoAggregateDisplayValue';
|
||||
import { recordIndexAggregateDisplayValueForGroupValueComponentFamilyState } from '@/object-record/record-index/states/recordIndexAggregateDisplayValueForGroupValueComponentFamilyState';
|
||||
@@ -13,6 +14,7 @@ import { dateLocaleState } from '~/localization/states/dateLocaleState';
|
||||
export const useSetRecordIndexAggregateDisplayValueForRecordGroupValue = () => {
|
||||
const { dateFormat, timeFormat, timeZone } = useContext(UserContext);
|
||||
const dateLocale = useAtomStateValue(dateLocaleState);
|
||||
const { numberFormat } = useNumberFormat();
|
||||
|
||||
const recordIndexAggregateValueByGroupValueCallbackState =
|
||||
useAtomComponentFamilyStateCallbackState(
|
||||
@@ -38,6 +40,7 @@ export const useSetRecordIndexAggregateDisplayValueForRecordGroupValue = () => {
|
||||
timeFormat,
|
||||
timeZone,
|
||||
localeCatalog: dateLocale.localeCatalog,
|
||||
numberFormat,
|
||||
});
|
||||
|
||||
store.set(
|
||||
@@ -54,6 +57,7 @@ export const useSetRecordIndexAggregateDisplayValueForRecordGroupValue = () => {
|
||||
store,
|
||||
timeFormat,
|
||||
timeZone,
|
||||
numberFormat,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+4
-1
@@ -1,3 +1,4 @@
|
||||
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
|
||||
import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
@@ -84,6 +85,8 @@ export const ObjectRecordShowPageBreadcrumb = ({
|
||||
const { navigateToIndexView, rankInView, totalCount } =
|
||||
useRecordShowPagePagination(objectNameSingular, objectRecordId);
|
||||
|
||||
const { formatNumber } = useNumberFormat();
|
||||
|
||||
if (!loading && isInitialLoad) {
|
||||
setIsInitialLoad(false);
|
||||
}
|
||||
@@ -136,7 +139,7 @@ export const ObjectRecordShowPageBreadcrumb = ({
|
||||
</FieldContext.Provider>
|
||||
</StyledTitle>
|
||||
<StyledPaginationInformation>
|
||||
{`(${rankInView + 1}/${totalCount})`}
|
||||
{`(${formatNumber(rankInView + 1)}/${formatNumber(totalCount)})`}
|
||||
</StyledPaginationInformation>
|
||||
</StyledEditableTitleContainer>
|
||||
);
|
||||
|
||||
+12
-4
@@ -1,3 +1,4 @@
|
||||
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
|
||||
import { flattenedFieldMetadataItemsSelector } from '@/object-metadata/states/flattenedFieldMetadataItemsSelector';
|
||||
import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
||||
import { transformAggregateRawValueIntoAggregateDisplayValue } from '@/object-record/record-aggregate/utils/transformAggregateRawValueIntoAggregateDisplayValue';
|
||||
@@ -36,6 +37,8 @@ export const useAggregateRecordsForRecordTableColumnFooter = (
|
||||
const { objectMetadataItem } = useRecordTableContextOrThrow();
|
||||
const { recordGroupFilter } = useRecordGroupFilter(objectMetadataItem.fields);
|
||||
|
||||
const { numberFormat, formatNumber } = useNumberFormat();
|
||||
|
||||
const currentRecordFilterGroups = useAtomComponentStateValue(
|
||||
currentRecordFilterGroupsComponentState,
|
||||
);
|
||||
@@ -125,11 +128,15 @@ export const useAggregateRecordsForRecordTableColumnFooter = (
|
||||
);
|
||||
|
||||
if (!isDefined(aggregateFieldMetadataItem)) {
|
||||
const totalCountAggregateValue =
|
||||
data?.[FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION]?.[
|
||||
AggregateOperations.COUNT
|
||||
];
|
||||
|
||||
return {
|
||||
aggregateValue:
|
||||
data?.[FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION]?.[
|
||||
AggregateOperations.COUNT
|
||||
],
|
||||
aggregateValue: isDefined(totalCountAggregateValue)
|
||||
? formatNumber(Number(totalCountAggregateValue))
|
||||
: totalCountAggregateValue,
|
||||
aggregateLabel: getAggregateOperationLabel(AggregateOperations.COUNT),
|
||||
isLoading: loading,
|
||||
};
|
||||
@@ -155,6 +162,7 @@ export const useAggregateRecordsForRecordTableColumnFooter = (
|
||||
localeCatalog: dateLocale.localeCatalog,
|
||||
timeFormat,
|
||||
timeZone,
|
||||
numberFormat,
|
||||
});
|
||||
|
||||
const { aggregateLabel } = getRecordAggregateDisplayLabel({
|
||||
|
||||
+13
-3
@@ -1,3 +1,4 @@
|
||||
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
||||
import { transformAggregateRawValueIntoAggregateDisplayValue } from '@/object-record/record-aggregate/utils/transformAggregateRawValueIntoAggregateDisplayValue';
|
||||
@@ -81,6 +82,7 @@ export const usePieChartCenterMetricData = ({
|
||||
|
||||
const { dateFormat, timeFormat, timeZone } = useContext(UserContext);
|
||||
const dateLocale = useAtomStateValue(dateLocaleState);
|
||||
const { numberFormat, formatNumber } = useNumberFormat();
|
||||
|
||||
const aggregateFieldMetadataItem = objectMetadataItem.readableFields.find(
|
||||
findById(configuration.aggregateFieldMetadataId),
|
||||
@@ -115,9 +117,14 @@ export const usePieChartCenterMetricData = ({
|
||||
|
||||
const centerMetricValue = useMemo(() => {
|
||||
if (!isDefined(aggregateFieldMetadataItem)) {
|
||||
return centerMetricData?.[FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION]?.[
|
||||
AggregateOperations.COUNT
|
||||
];
|
||||
const totalCountValue =
|
||||
centerMetricData?.[FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION]?.[
|
||||
AggregateOperations.COUNT
|
||||
];
|
||||
|
||||
return isDefined(totalCountValue)
|
||||
? formatNumber(Number(totalCountValue))
|
||||
: totalCountValue;
|
||||
}
|
||||
|
||||
const aggregateRawValue =
|
||||
@@ -133,6 +140,7 @@ export const usePieChartCenterMetricData = ({
|
||||
localeCatalog: dateLocale.localeCatalog,
|
||||
timeFormat,
|
||||
timeZone,
|
||||
numberFormat,
|
||||
});
|
||||
}, [
|
||||
aggregateFieldMetadataItem,
|
||||
@@ -143,6 +151,8 @@ export const usePieChartCenterMetricData = ({
|
||||
dateLocale.localeCatalog,
|
||||
timeFormat,
|
||||
timeZone,
|
||||
numberFormat,
|
||||
formatNumber,
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
+12
-5
@@ -1,3 +1,4 @@
|
||||
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
|
||||
import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
||||
import { transformAggregateRawValueIntoAggregateDisplayValue } from '@/object-record/record-aggregate/utils/transformAggregateRawValueIntoAggregateDisplayValue';
|
||||
import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel';
|
||||
@@ -89,6 +90,7 @@ export const useGraphWidgetAggregateQuery = ({
|
||||
|
||||
const { dateFormat, timeFormat, timeZone } = useContext(UserContext);
|
||||
const dateLocale = useAtomStateValue(dateLocaleState);
|
||||
const { numberFormat, formatNumber } = useNumberFormat();
|
||||
|
||||
if (isRatioQuery) {
|
||||
const isRatioLoading = ratioNumeratorLoading || ratioDenominatorLoading;
|
||||
@@ -130,11 +132,15 @@ export const useGraphWidgetAggregateQuery = ({
|
||||
);
|
||||
|
||||
if (!isDefined(aggregateFieldMetadataItem)) {
|
||||
const totalCountValue =
|
||||
data?.[FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION]?.[
|
||||
AggregateOperations.COUNT
|
||||
];
|
||||
|
||||
return {
|
||||
value:
|
||||
data?.[FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION]?.[
|
||||
AggregateOperations.COUNT
|
||||
],
|
||||
value: isDefined(totalCountValue)
|
||||
? formatNumber(Number(totalCountValue))
|
||||
: totalCountValue,
|
||||
label: getAggregateOperationLabel(AggregateOperations.COUNT),
|
||||
loading,
|
||||
error,
|
||||
@@ -158,7 +164,8 @@ export const useGraphWidgetAggregateQuery = ({
|
||||
localeCatalog: dateLocale.localeCatalog,
|
||||
timeFormat,
|
||||
timeZone,
|
||||
numberFormat: configuration.numberFormat ?? undefined,
|
||||
numberFormat,
|
||||
chartNumberFormat: configuration.numberFormat ?? undefined,
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
import { SidePanelPageInfoLayout } from '@/side-panel/components/SidePanelPageInfoLayout';
|
||||
import { useFindManyRecordsSelectedInContextStore } from '@/context-store/hooks/useFindManyRecordsSelectedInContextStore';
|
||||
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext } from 'react';
|
||||
import { IconPencil } from 'twenty-ui/icon';
|
||||
@@ -13,6 +14,7 @@ export const SidePanelMultipleRecordsInfo = ({
|
||||
sidePanelPageInstanceId,
|
||||
}: SidePanelMultipleRecordsInfoProps) => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
const { formatNumber } = useNumberFormat();
|
||||
const { totalCount } = useFindManyRecordsSelectedInContextStore({
|
||||
instanceId: sidePanelPageInstanceId,
|
||||
limit: 1,
|
||||
@@ -25,7 +27,7 @@ export const SidePanelMultipleRecordsInfo = ({
|
||||
}
|
||||
iconColor={theme.font.color.tertiary}
|
||||
title={t`Update records`}
|
||||
label={t`${totalCount} selected`}
|
||||
label={t`${formatNumber(totalCount ?? 0)} selected`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+4
-1
@@ -2,6 +2,7 @@ import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { StyledDropdownButtonContainer } from '@/ui/layout/dropdown/components/StyledDropdownButtonContainer';
|
||||
import { useNumberFormat } from '@/localization/hooks/useNumberFormat';
|
||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useGetRecordIndexTotalCount } from '@/views/hooks/internal/useGetRecordIndexTotalCount';
|
||||
@@ -58,6 +59,8 @@ export const ViewPickerDropdown = () => {
|
||||
|
||||
const { totalCount } = useGetRecordIndexTotalCount();
|
||||
|
||||
const { formatNumber } = useNumberFormat();
|
||||
|
||||
const isDropdownOpen = useAtomComponentStateValue(
|
||||
isDropdownOpenComponentState,
|
||||
VIEW_PICKER_DROPDOWN_ID,
|
||||
@@ -94,7 +97,7 @@ export const ViewPickerDropdown = () => {
|
||||
<OverflowingTextWithTooltip text={currentView?.name ?? t`All`} />
|
||||
</StyledViewName>
|
||||
<StyledDropdownLabelAdornments>
|
||||
{isDefined(totalCount) && <>· {totalCount} </>}
|
||||
{isDefined(totalCount) && <>· {formatNumber(totalCount)} </>}
|
||||
<IconChevronDown size={theme.icon.size.sm} />
|
||||
</StyledDropdownLabelAdornments>
|
||||
</StyledDropdownButtonContainer>
|
||||
|
||||
Reference in New Issue
Block a user