From b8ea742a886894161fa29e94b35e3f1c3afe1c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Sat, 20 Jun 2026 14:28:12 +0200 Subject: [PATCH] fix(front): respect user number format for counts and aggregates (#21894) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 ` · ` 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)_ Review in cubic --- ...eRawValueIntoAggregateDisplayValue.test.ts | 68 +++++++++++++++++-- ...regateRawValueIntoAggregateDisplayValue.ts | 19 +++--- .../components/RecordIndexPageHeader.tsx | 5 +- ...ggregateDisplayValueForRecordGroupValue.ts | 4 ++ .../ObjectRecordShowPageBreadcrumb.tsx | 5 +- ...egateRecordsForRecordTableColumnFooter.tsx | 16 +++-- .../hooks/usePieChartCenterMetricData.ts | 16 ++++- .../hooks/useGraphWidgetAggregateQuery.ts | 17 +++-- .../SidePanelMultipleRecordsInfo.tsx | 4 +- .../components/ViewPickerDropdown.tsx | 5 +- 10 files changed, 131 insertions(+), 28 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-aggregate/utils/__tests__/transformAggregateRawValueIntoAggregateDisplayValue.test.ts b/packages/twenty-front/src/modules/object-record/record-aggregate/utils/__tests__/transformAggregateRawValueIntoAggregateDisplayValue.test.ts index 1b12254816..c6fd44b0bf 100644 --- a/packages/twenty-front/src/modules/object-record/record-aggregate/utils/__tests__/transformAggregateRawValueIntoAggregateDisplayValue.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-aggregate/utils/__tests__/transformAggregateRawValueIntoAggregateDisplayValue.test.ts @@ -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'); }); diff --git a/packages/twenty-front/src/modules/object-record/record-aggregate/utils/transformAggregateRawValueIntoAggregateDisplayValue.ts b/packages/twenty-front/src/modules/object-record/record-aggregate/utils/transformAggregateRawValueIntoAggregateDisplayValue.ts index 000b5d6720..d7be000a9e 100644 --- a/packages/twenty-front/src/modules/object-record/record-aggregate/utils/transformAggregateRawValueIntoAggregateDisplayValue.ts +++ b/packages/twenty-front/src/modules/object-record/record-aggregate/utils/transformAggregateRawValueIntoAggregateDisplayValue.ts @@ -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; 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: { diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx index b90d6563cc..79b7237b24 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx @@ -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 = () => { {label} <>{'->'} - {t`${contextStoreNumberOfSelectedRecords} selected`} + {t`${formatNumber(contextStoreNumberOfSelectedRecords)} selected`} ) : ( diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useSetRecordIndexAggregateDisplayValueForRecordGroupValue.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useSetRecordIndexAggregateDisplayValueForRecordGroupValue.ts index 5babadc8d6..880a985f31 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useSetRecordIndexAggregateDisplayValueForRecordGroupValue.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useSetRecordIndexAggregateDisplayValueForRecordGroupValue.ts @@ -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, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx index 60d7ade549..86c37856a0 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx @@ -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 = ({ - {`(${rankInView + 1}/${totalCount})`} + {`(${formatNumber(rankInView + 1)}/${formatNumber(totalCount)})`} ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx index 1027803baf..ad32f6f268 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx @@ -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({ diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graph-widget-pie-chart/hooks/usePieChartCenterMetricData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graph-widget-pie-chart/hooks/usePieChartCenterMetricData.ts index ae1aee06c9..1b510cadfc 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graph-widget-pie-chart/hooks/usePieChartCenterMetricData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graph-widget-pie-chart/hooks/usePieChartCenterMetricData.ts @@ -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 { diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts index e49bf39ffc..b4bd666d3b 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/hooks/useGraphWidgetAggregateQuery.ts @@ -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 { diff --git a/packages/twenty-front/src/modules/side-panel/components/SidePanelMultipleRecordsInfo.tsx b/packages/twenty-front/src/modules/side-panel/components/SidePanelMultipleRecordsInfo.tsx index f0471b29ac..2b9cb62aea 100644 --- a/packages/twenty-front/src/modules/side-panel/components/SidePanelMultipleRecordsInfo.tsx +++ b/packages/twenty-front/src/modules/side-panel/components/SidePanelMultipleRecordsInfo.tsx @@ -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`} /> ); }; diff --git a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx index 26de461c06..518ac908eb 100644 --- a/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx +++ b/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerDropdown.tsx @@ -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 = () => { - {isDefined(totalCount) && <>· {totalCount} } + {isDefined(totalCount) && <>· {formatNumber(totalCount)} }