From 86e4685781f24c23ce4125c036c716a0ccb7eee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Fri, 3 Jul 2026 08:28:51 +0200 Subject: [PATCH] fix: format numbers according to user preferences in settings views (#22501) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Context Record and field counts in several settings views were rendered as raw numbers (e.g. `158355`), ignoring the workspace member's number format preference. This PR routes them through the existing `useNumberFormat` hook (which wraps `formatNumber` with the user's `numberFormat` preference), so they render as `158,355` / `158 355` / `158.355` / `158'355` depending on the preference. ## Changes Starting point (from the screenshot): the Settings → Data model objects table. - **`SettingsObjectItemTableRow`** — Fields and Records columns in Settings → Data model - **`SettingsAvailableStandardObjectItemTableRow`** — Fields column in Settings → Data model → New object - **`SettingsDataModelOverviewObject`** — record count next to the object name in the data model graph overview (guards against `undefined` while the count query loads) - **`SettingsLogs`** — "X of Y" record counts above the event logs table - **`SettingsAdminGeneral`** — Users column in the admin panel top workspaces table - **`SettingsAdminWorkspaceContent`** — Members value in the admin panel workspace info card - **`NoteList`** — total notes count in the record page Notes tab ## Test coverage - `npx nx lint:diff-with-main twenty-front` (oxlint + oxfmt) passes - `npx nx typecheck twenty-front` passes - No behavioral change beyond formatting; `formatNumber` defaults to 0 decimals so integer counts stay integers https://claude.ai/code/session_019pXXZSaza8TXPYK8NefQiS --- _Generated by [Claude Code](https://claude.ai/code/session_019pXXZSaza8TXPYK8NefQiS)_ Review in cubic --- .../activities/notes/components/NoteList.tsx | 51 ++++++++++--------- .../components/SettingsAdminGeneral.tsx | 6 ++- .../SettingsAdminWorkspaceContent.tsx | 6 ++- .../SettingsDataModelOverviewObject.tsx | 9 +++- ...ngsAvailableStandardObjectItemTableRow.tsx | 6 ++- .../components/SettingsObjectItemTableRow.tsx | 10 ++-- .../event-logs/components/SettingsLogs.tsx | 4 +- 7 files changed, 59 insertions(+), 33 deletions(-) diff --git a/packages/twenty-front/src/modules/activities/notes/components/NoteList.tsx b/packages/twenty-front/src/modules/activities/notes/components/NoteList.tsx index 1e32ab7548..7e9bdccdd4 100644 --- a/packages/twenty-front/src/modules/activities/notes/components/NoteList.tsx +++ b/packages/twenty-front/src/modules/activities/notes/components/NoteList.tsx @@ -2,6 +2,7 @@ import { styled } from '@linaria/react'; import { type ReactElement } from 'react'; import { type Note } from '@/activities/types/Note'; +import { useNumberFormat } from '@/localization/hooks/useNumberFormat'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { NoteTile } from './NoteTile'; @@ -54,26 +55,30 @@ export const NoteList = ({ notes, totalCount, button, -}: NoteListProps) => ( - <> - {notes.length > 0 && ( - - - - {title} {totalCount} - - {button} - - - {notes.map((note) => ( - - ))} - - - )} - -); +}: NoteListProps) => { + const { formatNumber } = useNumberFormat(); + + return ( + <> + {notes.length > 0 && ( + + + + {title} {formatNumber(totalCount)} + + {button} + + + {notes.map((note) => ( + + ))} + + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx index 913dc55746..31a0a688a5 100644 --- a/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx +++ b/packages/twenty-front/src/modules/settings/admin-panel/components/SettingsAdminGeneral.tsx @@ -1,4 +1,5 @@ import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState'; +import { useNumberFormat } from '@/localization/hooks/useNumberFormat'; import { useApolloAdminClient } from '@/settings/admin-panel/apollo/hooks/useApolloAdminClient'; import { SettingsSectionSkeletonLoader } from '@/settings/components/SettingsSectionSkeletonLoader'; import { SettingsAdminServerAdmins } from '@/settings/admin-panel/components/SettingsAdminServerAdmins'; @@ -45,6 +46,7 @@ const TOP_WORKSPACES_GRID_TEMPLATE_COLUMNS = '2fr 1fr 36px'; export const SettingsAdminGeneral = () => { const { theme } = useContext(ThemeContext); + const { formatNumber } = useNumberFormat(); const apolloAdminClient = useApolloAdminClient(); const [userSearchTerm, setUserSearchTerm] = useState(''); const [debouncedUserSearchTerm] = useDebounce(userSearchTerm, 300); @@ -250,7 +252,9 @@ export const SettingsAdminGeneral = () => { text={workspace.name || '\u2014'} /> - {workspace.totalUsers} + + {formatNumber(workspace.totalUsers)} + { const { t } = useLingui(); + const { formatNumber } = useNumberFormat(); const { dateFormat, timeFormat, timeZone } = useContext(UserContext); const { localeCatalog } = useAtomStateValue(dateLocaleState); @@ -116,7 +118,9 @@ export const SettingsAdminWorkspaceContent = ({ { Icon: IconUser, label: t`Members`, - value: activeWorkspace?.totalUsers, + value: isDefined(activeWorkspace?.totalUsers) + ? formatNumber(activeWorkspace.totalUsers) + : activeWorkspace?.totalUsers, }, { Icon: IconStatusChange, diff --git a/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewObject.tsx b/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewObject.tsx index bf95d9dfc6..82542221d3 100644 --- a/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewObject.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewObject.tsx @@ -3,6 +3,7 @@ import { type Node, type NodeProps } from '@xyflow/react'; import { useContext, useState } from 'react'; import { Link } from 'react-router-dom'; +import { useNumberFormat } from '@/localization/hooks/useNumberFormat'; import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField'; @@ -14,7 +15,7 @@ import { SettingsItemTypeTag } from '@/settings/components/SettingsItemTypeTag'; import { ObjectFieldRowWithoutRelation } from '@/settings/data-model/graph-overview/components/SettingsDataModelOverviewFieldWithoutRelation'; import '@xyflow/react/dist/style.css'; import { SettingsPath } from 'twenty-shared/types'; -import { getSettingsPath } from 'twenty-shared/utils'; +import { getSettingsPath, isDefined } from 'twenty-shared/utils'; import { IconChevronDown, IconChevronUp } from 'twenty-ui/icon'; import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; @@ -108,6 +109,7 @@ export const SettingsDataModelOverviewObject = ({ data: objectMetadataItem, }: SettingsDataModelOverviewObjectProps) => { const { theme } = useContext(ThemeContext); + const { formatNumber } = useNumberFormat(); const [otherFieldsExpanded, setOtherFieldsExpanded] = useState(false); const { totalCount } = useFindManyRecords({ @@ -139,7 +141,10 @@ export const SettingsDataModelOverviewObject = ({ {objectMetadataItem.labelPlural} - · {totalCount} + + {' '} + · {isDefined(totalCount) ? formatNumber(totalCount) : totalCount} + diff --git a/packages/twenty-front/src/modules/settings/data-model/new-object/components/SettingsAvailableStandardObjectItemTableRow.tsx b/packages/twenty-front/src/modules/settings/data-model/new-object/components/SettingsAvailableStandardObjectItemTableRow.tsx index c56d0fc3ad..a6c85822c6 100644 --- a/packages/twenty-front/src/modules/settings/data-model/new-object/components/SettingsAvailableStandardObjectItemTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/new-object/components/SettingsAvailableStandardObjectItemTableRow.tsx @@ -1,3 +1,4 @@ +import { useNumberFormat } from '@/localization/hooks/useNumberFormat'; import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { TableCell } from '@/ui/layout/table/components/TableCell'; @@ -28,6 +29,7 @@ export const SettingsAvailableStandardObjectItemTableRow = ({ onClick, }: SettingsAvailableStandardObjectItemTableRowProps) => { const { theme } = useContext(ThemeContext); + const { formatNumber } = useNumberFormat(); return ( {objectItem.description} - {objectItem.fields.length} + + {formatNumber(objectItem.fields.length)} + ); }; diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectItemTableRow.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectItemTableRow.tsx index 63acf16434..8ee3f85e20 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectItemTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectItemTableRow.tsx @@ -2,6 +2,7 @@ import { styled } from '@linaria/react'; import { useLingui } from '@lingui/react/macro'; import { type ReactNode, useContext } from 'react'; +import { useNumberFormat } from '@/localization/hooks/useNumberFormat'; import { ObjectMetadataIcon } from '@/object-metadata/components/ObjectMetadataIcon'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { isHiddenSystemField } from '@/object-metadata/utils/isHiddenSystemField'; @@ -46,6 +47,7 @@ export const SettingsObjectMetadataItemTableRow = ({ }: SettingsObjectMetadataItemTableRowProps) => { const { t } = useLingui(); const { theme } = useContext(ThemeContext); + const { formatNumber } = useNumberFormat(); return ( - { + {formatNumber( objectMetadataItem.fields.filter( (field) => !isHiddenSystemField(field), - ).length - } + ).length, + )} - {totalObjectCount} + {formatNumber(totalObjectCount)} {action} ); diff --git a/packages/twenty-front/src/modules/settings/event-logs/components/SettingsLogs.tsx b/packages/twenty-front/src/modules/settings/event-logs/components/SettingsLogs.tsx index a700207915..8189ed2219 100644 --- a/packages/twenty-front/src/modules/settings/event-logs/components/SettingsLogs.tsx +++ b/packages/twenty-front/src/modules/settings/event-logs/components/SettingsLogs.tsx @@ -5,6 +5,7 @@ import { useMemo, useState } from 'react'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { billingState } from '@/client-config/states/billingState'; import { isClickHouseConfiguredState } from '@/client-config/states/isClickHouseConfiguredState'; +import { useNumberFormat } from '@/localization/hooks/useNumberFormat'; import { SettingsEmptyPlaceholder } from '@/settings/components/SettingsEmptyPlaceholder'; import { SettingsOptionCardContentButton } from '@/settings/components/SettingsOptions/SettingsOptionCardContentButton'; import { EventLogFilters } from '@/settings/event-logs/components/EventLogFilters'; @@ -89,6 +90,7 @@ const RECORDS_PER_PAGE = 100; export const SettingsLogs = () => { const { t } = useLingui(); + const { formatNumber } = useNumberFormat(); const currentWorkspace = useAtomStateValue(currentWorkspaceState); const isClickHouseConfigured = useAtomStateValue(isClickHouseConfiguredState); @@ -214,7 +216,7 @@ export const SettingsLogs = () => { return ( - {t`${displayedRecords.length} of ${totalCount + liveRecords.length}`} + {t`${formatNumber(displayedRecords.length)} of ${formatNumber(totalCount + liveRecords.length)}`}