diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuSearchRecords.tsx b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuSearchRecords.tsx index 31ca94ffe3a..9ae2b7dd4da 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuSearchRecords.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuSearchRecords.tsx @@ -5,15 +5,14 @@ import { ActionType } from '@/action-menu/actions/types/ActionType'; import { MAX_SEARCH_RESULTS } from '@/command-menu/constants/MaxSearchResults'; import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu'; import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState'; +import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { AppPath } from '@/types/AppPath'; import { t } from '@lingui/core/macro'; import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; -import { capitalize } from 'twenty-shared/utils'; import { Avatar } from 'twenty-ui/display'; import { useDebounce } from 'use-debounce'; import { useSearchQuery } from '~/generated/graphql'; @@ -73,7 +72,10 @@ export const useCommandMenuSearchRecords = () => { /> ), shouldBeRegistered: () => true, - description: capitalize(searchRecord.objectNameSingular), + description: + objectMetadataItems.find( + (item) => item.nameSingular === searchRecord.objectNameSingular, + )?.labelSingular ?? searchRecord.objectNameSingular, }; if ( @@ -116,7 +118,7 @@ export const useCommandMenuSearchRecords = () => { }; }, ); - }, [searchData, openRecordInCommandMenu]); + }, [searchData, openRecordInCommandMenu, objectMetadataItems]); return { loading, diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts index d81058d8d61..8c5ced59677 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useOpenRecordInCommandMenu.ts @@ -21,7 +21,7 @@ import { useRunWorkflowRunOpeningInCommandMenuSideEffects } from '@/workflow/hoo import { useTheme } from '@emotion/react'; import { t } from '@lingui/core/macro'; import { useRecoilCallback } from 'recoil'; -import { capitalize, isDefined } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; import { v4 } from 'uuid'; @@ -164,13 +164,13 @@ export const useOpenRecordInCommandMenu = () => { theme, }); - const capitalizedObjectNameSingular = capitalize(objectNameSingular); + const objectLabelSingular = objectMetadataItem.labelSingular; navigateCommandMenu({ page: CommandMenuPages.ViewRecord, pageTitle: isNewRecord - ? t`New ${capitalizedObjectNameSingular}` - : capitalizedObjectNameSingular, + ? t`New ${objectLabelSingular}` + : objectLabelSingular, pageIcon: Icon, pageIconColor: IconColor, pageId: pageComponentInstanceId, diff --git a/packages/twenty-front/src/modules/command-menu/utils/getRecordContextText.ts b/packages/twenty-front/src/modules/command-menu/utils/getRecordContextText.ts index c6ba13d2c00..8bfec01d355 100644 --- a/packages/twenty-front/src/modules/command-menu/utils/getRecordContextText.ts +++ b/packages/twenty-front/src/modules/command-menu/utils/getRecordContextText.ts @@ -1,7 +1,6 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; -import { capitalize } from 'twenty-shared/utils'; export const getSelectedRecordsContextText = ( objectMetadataItem: ObjectMetadataItem, @@ -10,5 +9,5 @@ export const getSelectedRecordsContextText = ( ) => { return totalCount === 1 ? getObjectRecordIdentifier({ objectMetadataItem, record: records[0] }).name - : `${totalCount} ${capitalize(objectMetadataItem.namePlural)}`; + : `${totalCount} ${objectMetadataItem.labelPlural}`; }; diff --git a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx index f2731d522ab..05d7cc13db8 100644 --- a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx +++ b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberFavorites.tsx @@ -1,5 +1,6 @@ import { FavoriteFolderNavigationDrawerItemDropdown } from '@/favorites/components/FavoriteFolderNavigationDrawerItemDropdown'; import { FavoriteIcon } from '@/favorites/components/FavoriteIcon'; +import { FavoriteNavigationDrawerSubItem } from '@/favorites/components/FavoriteNavigationDrawerSubItem'; import { FavoritesDroppable } from '@/favorites/components/FavoritesDroppable'; import { FAVORITE_FOLDER_DELETE_MODAL_ID } from '@/favorites/constants/FavoriteFolderDeleteModalId'; import { FavoritesDragContext } from '@/favorites/contexts/FavoritesDragContext'; @@ -18,7 +19,6 @@ import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpe import { NavigationDrawerInput } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerInput'; import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; import { NavigationDrawerItemsCollapsableContainer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemsCollapsableContainer'; -import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem'; import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { Droppable } from '@hello-pangea/dnd'; @@ -196,9 +196,9 @@ export const CurrentWorkspaceMemberFavorites = ({ index={index} isInsideScrollableContainer itemComponent={ - } to={isDragging ? undefined : favorite.link} active={index === selectedFavoriteIndex} diff --git a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberOrphanFavorites.tsx b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberOrphanFavorites.tsx index b2bc671c736..d9867079722 100644 --- a/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberOrphanFavorites.tsx +++ b/packages/twenty-front/src/modules/favorites/components/CurrentWorkspaceMemberOrphanFavorites.tsx @@ -1,11 +1,11 @@ import { FavoriteIcon } from '@/favorites/components/FavoriteIcon'; +import { FavoriteNavigationDrawerItem } from '@/favorites/components/FavoriteNavigationDrawerItem'; import { FavoritesDroppable } from '@/favorites/components/FavoritesDroppable'; import { FavoritesDragContext } from '@/favorites/contexts/FavoritesDragContext'; import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite'; import { useFavorites } from '@/favorites/hooks/useFavorites'; import { isLocationMatchingFavorite } from '@/favorites/utils/isLocationMatchingFavorite'; import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem'; -import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; import styled from '@emotion/styled'; import { useContext } from 'react'; import { useLocation } from 'react-router-dom'; @@ -42,7 +42,8 @@ export const CurrentWorkspaceMemberOrphanFavorites = () => { isInsideScrollableContainer={true} itemComponent={ - } active={isLocationMatchingFavorite( @@ -58,7 +59,6 @@ export const CurrentWorkspaceMemberOrphanFavorites = () => { accent="tertiary" /> } - objectName={favorite.objectNameSingular} isDragging={isDragging} triggerEvent="CLICK" /> diff --git a/packages/twenty-front/src/modules/favorites/components/FavoriteNavigationDrawerItem.tsx b/packages/twenty-front/src/modules/favorites/components/FavoriteNavigationDrawerItem.tsx new file mode 100644 index 00000000000..5cf693bfcef --- /dev/null +++ b/packages/twenty-front/src/modules/favorites/components/FavoriteNavigationDrawerItem.tsx @@ -0,0 +1,50 @@ +import { ProcessedFavorite } from '@/favorites/utils/sortFavorites'; +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; + +type FavoriteNavigationDrawerItemProps = { + favorite: ProcessedFavorite; + label: string; + Icon?: React.ComponentProps['Icon']; + to?: string; + active?: boolean; + rightOptions?: React.ComponentProps< + typeof NavigationDrawerItem + >['rightOptions']; + isDragging?: boolean; + triggerEvent?: React.ComponentProps< + typeof NavigationDrawerItem + >['triggerEvent']; +}; + +export const FavoriteNavigationDrawerItem = ({ + favorite, + label, + Icon, + to, + active, + rightOptions, + isDragging, + triggerEvent, +}: FavoriteNavigationDrawerItemProps) => { + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular: favorite.objectNameSingular || '', + }); + + if (!favorite.objectNameSingular) { + return null; + } + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/favorites/components/FavoriteNavigationDrawerSubItem.tsx b/packages/twenty-front/src/modules/favorites/components/FavoriteNavigationDrawerSubItem.tsx new file mode 100644 index 00000000000..82e18a62d6e --- /dev/null +++ b/packages/twenty-front/src/modules/favorites/components/FavoriteNavigationDrawerSubItem.tsx @@ -0,0 +1,55 @@ +import { ProcessedFavorite } from '@/favorites/utils/sortFavorites'; +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem'; + +type FavoriteNavigationDrawerSubItemProps = { + favorite: ProcessedFavorite; + label: string; + Icon?: React.ComponentProps['Icon']; + to?: string; + active?: boolean; + subItemState?: React.ComponentProps< + typeof NavigationDrawerSubItem + >['subItemState']; + rightOptions?: React.ComponentProps< + typeof NavigationDrawerSubItem + >['rightOptions']; + isDragging?: boolean; + triggerEvent?: React.ComponentProps< + typeof NavigationDrawerSubItem + >['triggerEvent']; +}; + +export const FavoriteNavigationDrawerSubItem = ({ + favorite, + label, + Icon, + to, + active, + subItemState, + rightOptions, + isDragging, + triggerEvent, +}: FavoriteNavigationDrawerSubItemProps) => { + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular: favorite.objectNameSingular || '', + }); + + if (!favorite.objectNameSingular) { + return null; + } + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerGater.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerGater.tsx index 40aef77c93f..fb80ef43b6f 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerGater.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerGater.tsx @@ -24,7 +24,6 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/ import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import styled from '@emotion/styled'; import { useRecoilCallback } from 'recoil'; -import { capitalize } from 'twenty-shared/utils'; import { NotFound } from '~/pages/not-found/NotFound'; const StyledIndexContainer = styled.div` @@ -102,9 +101,7 @@ export const RecordIndexContainerGater = () => { instanceId: getActionMenuIdFromRecordIndexId(recordIndexId), }} > - + { const { getIcon } = useIcons(); const Icon = getIcon(objectMetadataItem?.icon); - const label = objectMetadataItem?.labelPlural ?? capitalize(objectNamePlural); + const label = objectMetadataItem?.labelPlural ?? objectNamePlural; const pageHeaderTitle = contextStoreNumberOfSelectedRecords > 0 ? ( diff --git a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx index 647485fc3e2..186fce85b7f 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerMenuItemContent.tsx @@ -10,7 +10,6 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; -import { capitalize } from 'twenty-shared/utils'; import { Avatar } from 'twenty-ui/display'; import { MenuItemMultiSelectAvatar } from 'twenty-ui/navigation'; import { SearchRecord } from '~/generated-metadata/graphql'; @@ -61,7 +60,7 @@ export const MultipleRecordPickerMenuItemContent = ({ const displayText = searchRecord.label?.trim() || - `Untitled ${capitalize(objectMetadataItem.nameSingular)}`; + `Untitled ${objectMetadataItem.labelSingular}`; return ( { const { loading } = useFindOneRecord({ @@ -95,7 +94,7 @@ export const ObjectRecordShowPageBreadcrumb = ({ }} > {HeaderIcon && } - {capitalize(objectLabelPlural)} + {objectLabel} {' / '} diff --git a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPagePagination.ts b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPagePagination.ts index 845e7afcf64..5ccac5b2435 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPagePagination.ts +++ b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPagePagination.ts @@ -9,7 +9,7 @@ import { lastShowPageRecordIdState } from '@/object-record/record-field/states/l import { useRecordIdsFromFindManyCacheRootQuery } from '@/object-record/record-show/hooks/useRecordIdsFromFindManyCacheRootQuery'; import { AppPath } from '@/types/AppPath'; import { useQueryVariablesFromParentView } from '@/views/hooks/useQueryVariablesFromParentView'; -import { capitalize, isDefined } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; import { useNavigateApp } from '~/hooks/useNavigateApp'; export const useRecordShowPagePagination = ( @@ -216,13 +216,13 @@ export const useRecordShowPagePagination = ( const rankFoundInView = rankInView > -1; - const objectLabel = capitalize(objectMetadataItem.labelPlural); + const objectLabelPlural = objectMetadataItem.labelPlural; const totalCount = 1 + Math.max(totalCountBefore, totalCountAfter); const viewNameWithCount = rankFoundInView - ? `${rankInView + 1} of ${totalCount} in ${objectLabel}` - : `${objectLabel} (${totalCount})`; + ? `${rankInView + 1} of ${totalCount} in ${objectLabelPlural}` + : `${objectLabelPlural} (${totalCount})`; return { viewName: viewNameWithCount, diff --git a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll.tsx b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll.tsx index e9d05cab854..d4bbbd3a7ee 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll.tsx @@ -17,18 +17,18 @@ export const RecordTableEmptyStateNoGroupNoRecordAtAll = () => { createNewIndexRecord(); }; - const objectLabel = useObjectLabel(objectMetadataItem); + const objectLabelSingular = useObjectLabel(objectMetadataItem); - const buttonTitle = `Add a ${objectLabel}`; + const buttonTitle = `Add a ${objectLabelSingular}`; const title = getEmptyStateTitle( objectMetadataItem.nameSingular, - objectLabel, + objectLabelSingular, ); const subTitle = getEmptyStateSubTitle( objectMetadataItem.nameSingular, - objectLabel, + objectLabelSingular, ); return ( diff --git a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoRecordFoundForFilter.tsx b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoRecordFoundForFilter.tsx index d5eaf46e17f..3dfcc964c26 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoRecordFoundForFilter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoRecordFoundForFilter.tsx @@ -15,11 +15,11 @@ export const RecordTableEmptyStateNoRecordFoundForFilter = () => { createNewIndexRecord(); }; - const objectLabel = useObjectLabel(objectMetadataItem); + const objectLabelSingular = useObjectLabel(objectMetadataItem); - const buttonTitle = `Add a ${objectLabel}`; + const buttonTitle = `Add a ${objectLabelSingular}`; - const title = `No ${objectLabel} found`; + const title = `No ${objectLabelSingular} found`; const subTitle = 'No records matching the filter criteria were found.'; diff --git a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateReadOnly.tsx b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateReadOnly.tsx index 9231ac27fe8..8083e7fa512 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateReadOnly.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateReadOnly.tsx @@ -7,9 +7,9 @@ import { IconPlus } from 'twenty-ui/display'; export const RecordTableEmptyStateReadOnly = () => { const { objectMetadataItem } = useRecordTableContextOrThrow(); - const objectLabel = useObjectLabel(objectMetadataItem); + const objectLabelSingular = useObjectLabel(objectMetadataItem); - const buttonTitle = `Add a ${objectLabel}`; + const buttonTitle = `Add a ${objectLabelSingular}`; return ( { toggleSoftDeleteFilterState(false); }; - const objectLabel = useObjectLabel(objectMetadataItem); + const objectLabelSingular = useObjectLabel(objectMetadataItem); return ( {Icon && } - {capitalize(objectMetadataItem.namePlural)} + {objectMetadataItem.labelPlural} · {totalCount} diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx index 2b658264229..3c0d9da2385 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelTableRow.tsx @@ -44,7 +44,7 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({ const Icon = getIcon(objectMetadataItem.icon); - const objectLabel = objectMetadataItem.labelPlural; + const objectLabelPlural = objectMetadataItem.labelPlural; return ( )} - - + + diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel.tsx index fa7aa015b87..f71f5c83fd8 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/object-level-permissions/object-form/components/SettingsRolePermissionsObjectLevelObjectFormObjectLevel.tsx @@ -41,7 +41,7 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({ return null; } - const objectLabel = objectMetadataItem.labelPlural; + const objectLabelPlural = objectMetadataItem.labelPlural; const updateObjectPermission = ( permissionKey: SettingsRoleObjectPermissionKey, @@ -85,7 +85,7 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({ [ { key: 'canReadObjectRecords', - label: t`See ${objectLabel}`, + label: t`See ${objectLabelPlural}`, value: settingsDraftRoleObjectPermissions.canReadObjectRecords, setValue: (value: boolean | null) => { updateObjectPermission('canReadObjectRecords', value); @@ -93,7 +93,7 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({ }, { key: 'canUpdateObjectRecords', - label: t`Edit ${objectLabel}`, + label: t`Edit ${objectLabelPlural}`, value: settingsDraftRoleObjectPermissions.canUpdateObjectRecords, setValue: (value: boolean | null) => { updateObjectPermission('canUpdateObjectRecords', value); @@ -101,7 +101,7 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({ }, { key: 'canSoftDeleteObjectRecords', - label: t`Delete ${objectLabel}`, + label: t`Delete ${objectLabelPlural}`, value: settingsDraftRoleObjectPermissions.canSoftDeleteObjectRecords, setValue: (value: boolean | null) => { updateObjectPermission('canSoftDeleteObjectRecords', value); @@ -109,7 +109,7 @@ export const SettingsRolePermissionsObjectLevelObjectFormObjectLevel = ({ }, { key: 'canDestroyObjectRecords', - label: t`Destroy ${objectLabel}`, + label: t`Destroy ${objectLabelPlural}`, value: settingsDraftRoleObjectPermissions.canDestroyObjectRecords, setValue: (value: boolean | null) => { updateObjectPermission('canDestroyObjectRecords', value); diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx index c9779b16a0f..e04f36b13fa 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx @@ -11,7 +11,6 @@ import styled from '@emotion/styled'; import { ReactNode } from 'react'; import { Link } from 'react-router-dom'; import { useRecoilState } from 'recoil'; -import { capitalize } from 'twenty-shared/utils'; import { Pill } from 'twenty-ui/components'; import { IconComponent, Label, TablerIconsProps } from 'twenty-ui/display'; import { MOBILE_VIEWPORT } from 'twenty-ui/theme'; @@ -24,7 +23,7 @@ export type NavigationDrawerItemIndentationLevel = 1 | 2; export type NavigationDrawerItemProps = { className?: string; label: string; - objectName?: string; + secondaryLabel?: string; indentationLevel?: NavigationDrawerItemIndentationLevel; subItemState?: NavigationDrawerSubItemState; to?: string; @@ -142,7 +141,7 @@ const StyledItemLabel = styled.span` font-weight: ${({ theme }) => theme.font.weight.medium}; `; -const StyledItemObjectName = styled.span` +const StyledItemSecondaryLabel = styled.span` color: ${({ theme }) => theme.font.color.light}; font-weight: ${({ theme }) => theme.font.weight.regular}; `; @@ -240,7 +239,7 @@ const StyledRightOptionsVisbility = styled.div<{ export const NavigationDrawerItem = ({ className, label, - objectName, + secondaryLabel, indentationLevel = DEFAULT_INDENTATION_LEVEL, Icon, to, @@ -263,6 +262,7 @@ export const NavigationDrawerItem = ({ const isSettingsPage = useIsSettingsPage(); const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] = useRecoilState(isNavigationDrawerExpandedState); + const showBreadcrumb = indentationLevel === 2; const showStyledSpacer = Boolean( soon || isNew || count || keyboard || rightOptions, @@ -330,11 +330,11 @@ export const NavigationDrawerItem = ({ {label} - {objectName && ( - + {secondaryLabel && ( + {' · '} - {capitalize(objectName)} - + {secondaryLabel} + )} diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx index f2635a965f7..5acd784d1f2 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx @@ -8,7 +8,7 @@ type NavigationDrawerSubItemProps = NavigationDrawerItemProps; export const NavigationDrawerSubItem = ({ className, label, - objectName, + secondaryLabel, Icon, to, onClick, @@ -26,7 +26,7 @@ export const NavigationDrawerSubItem = ({ { const { objectNamePlural } = useParams(); - const { currentView } = useGetCurrentViewOnly(); - if (!objectNamePlural) { - return; - } + const { objectNameSingular } = useObjectNameSingularFromPlural({ + objectNamePlural: objectNamePlural ?? '', + }); + + const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular }); const pageTitle = currentView?.name - ? `${currentView?.name} - ${capitalize(objectNamePlural)}` - : capitalize(objectNamePlural); + ? `${currentView?.name} - ${objectMetadataItem.labelPlural}` + : objectMetadataItem.labelPlural; return ; }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx index d26614c15a2..f123965caea 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx @@ -5,16 +5,16 @@ import { WorkflowDeleteRecordAction } from '@/workflow/types/Workflow'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { useEffect, useState } from 'react'; +import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; +import { useTheme } from '@emotion/react'; import { isDefined } from 'twenty-shared/utils'; import { HorizontalSeparator, useIcons } from 'twenty-ui/display'; import { SelectOption } from 'twenty-ui/input'; import { JsonValue } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; -import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; -import { useTheme } from '@emotion/react'; type WorkflowEditActionDeleteRecordProps = { action: WorkflowDeleteRecordAction; @@ -29,7 +29,7 @@ type WorkflowEditActionDeleteRecordProps = { }; type DeleteRecordFormData = { - objectName: string; + objectNameSingular: string; objectRecordId: string; }; @@ -52,7 +52,7 @@ export const WorkflowEditActionDeleteRecord = ({ })); const [formData, setFormData] = useState({ - objectName: action.settings.input.objectName, + objectNameSingular: action.settings.input.objectName, objectRecordId: action.settings.input.objectRecordId, }); const isFormDisabled = actionOptions.readonly; @@ -72,7 +72,7 @@ export const WorkflowEditActionDeleteRecord = ({ }; const objectNameSingular = activeNonSystemObjectMetadataItems.find( - (item) => item.nameSingular === formData.objectName, + (item) => item.nameSingular === formData.objectNameSingular, )?.nameSingular; const saveAction = useDebouncedCallback( @@ -82,7 +82,7 @@ export const WorkflowEditActionDeleteRecord = ({ } const { - objectName: updatedObjectName, + objectNameSingular: updatedObjectName, objectRecordId: updatedObjectRecordId, } = formData; @@ -137,12 +137,12 @@ export const WorkflowEditActionDeleteRecord = ({ label="Object" fullWidth disabled={isFormDisabled} - value={formData.objectName} + value={formData.objectNameSingular} emptyOption={{ label: 'Select an option', value: '' }} options={availableMetadata} - onChange={(objectName) => { + onChange={(objectNameSingular) => { const newFormData: DeleteRecordFormData = { - objectName, + objectNameSingular, objectRecordId: '', }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx index f40638a007b..56fe46b0a2a 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx @@ -35,7 +35,7 @@ type WorkflowEditActionUpdateRecordProps = { }; type UpdateRecordFormData = { - objectName: string; + objectNameSingular: string; objectRecordId: string; fieldsToUpdate: string[]; [field: string]: unknown; @@ -60,7 +60,7 @@ export const WorkflowEditActionUpdateRecord = ({ })); const [formData, setFormData] = useState({ - objectName: action.settings.input.objectName, + objectNameSingular: action.settings.input.objectName, objectRecordId: action.settings.input.objectRecordId, fieldsToUpdate: action.settings.input.fieldsToUpdate ?? [], ...action.settings.input.objectRecord, @@ -83,7 +83,7 @@ export const WorkflowEditActionUpdateRecord = ({ }; const selectedObjectMetadataItem = activeNonSystemObjectMetadataItems.find( - (item) => item.nameSingular === formData.objectName, + (item) => item.nameSingular === formData.objectNameSingular, ); const objectNameSingular = selectedObjectMetadataItem?.nameSingular; @@ -114,7 +114,7 @@ export const WorkflowEditActionUpdateRecord = ({ } const { - objectName: updatedObjectName, + objectNameSingular: updatedObjectName, objectRecordId: updatedObjectRecordId, fieldsToUpdate: updatedFieldsToUpdate, ...updatedOtherFields @@ -174,12 +174,12 @@ export const WorkflowEditActionUpdateRecord = ({ label="Object" fullWidth disabled={isFormDisabled} - value={formData.objectName} + value={formData.objectNameSingular} emptyOption={{ label: 'Select an option', value: '' }} options={availableMetadata} onChange={(updatedObjectName) => { const newFormData: UpdateRecordFormData = { - objectName: updatedObjectName, + objectNameSingular: updatedObjectName, objectRecordId: '', fieldsToUpdate: [], }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowEditActionFindRecords.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowEditActionFindRecords.tsx index 245ce66e8ca..f3b757fd06b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowEditActionFindRecords.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowEditActionFindRecords.tsx @@ -36,7 +36,7 @@ type WorkflowEditActionFindRecordsProps = { }; type FindRecordsFormData = { - objectName: string; + objectNameSingular: string; filter?: FindRecordsActionFilter; limit?: number; }; @@ -65,14 +65,14 @@ export const WorkflowEditActionFindRecords = ({ })); const [formData, setFormData] = useState({ - objectName: action.settings.input.objectName, + objectNameSingular: action.settings.input.objectName, limit: action.settings.input.limit, filter: action.settings.input.filter as FindRecordsActionFilter, }); const isFormDisabled = actionOptions.readonly; const selectedObjectMetadataItem = activeNonSystemObjectMetadataItems.find( - (item) => item.nameSingular === formData.objectName, + (item) => item.nameSingular === formData.objectNameSingular, ); const selectedObjectMetadataItemNameSingular = @@ -87,7 +87,7 @@ export const WorkflowEditActionFindRecords = ({ } const { - objectName: updatedObjectName, + objectNameSingular: updatedObjectName, limit: updatedLimit, filter: updatedFilter, } = formData; @@ -118,7 +118,7 @@ export const WorkflowEditActionFindRecords = ({ action, defaultTitle: 'Search Records', }); - const instanceId = `workflow-edit-action-record-find-records-${action.id}-${formData.objectName}`; + const instanceId = `workflow-edit-action-record-find-records-${action.id}-${formData.objectNameSingular}`; return ( <> @@ -148,9 +148,9 @@ export const WorkflowEditActionFindRecords = ({ value={selectedObjectMetadataItemNameSingular} emptyOption={{ label: 'Select an option', value: '' }} options={availableMetadata} - onChange={(objectName) => { + onChange={(objectNameSingular) => { const newFormData: FindRecordsFormData = { - objectName, + objectNameSingular, limit: 1, }; diff --git a/packages/twenty-front/src/pages/object-record/RecordShowPageHeader.tsx b/packages/twenty-front/src/pages/object-record/RecordShowPageHeader.tsx index 814e3ab8341..4f7a5e2ac2c 100644 --- a/packages/twenty-front/src/pages/object-record/RecordShowPageHeader.tsx +++ b/packages/twenty-front/src/pages/object-record/RecordShowPageHeader.tsx @@ -26,7 +26,7 @@ export const RecordShowPageHeader = ({ } diff --git a/packages/twenty-front/src/pages/object-record/RecordShowPageTitle.tsx b/packages/twenty-front/src/pages/object-record/RecordShowPageTitle.tsx index da8f25d08e7..cb2a98b9e2a 100644 --- a/packages/twenty-front/src/pages/object-record/RecordShowPageTitle.tsx +++ b/packages/twenty-front/src/pages/object-record/RecordShowPageTitle.tsx @@ -1,9 +1,10 @@ import { useLabelIdentifierFieldMetadataItem } from '@/object-metadata/hooks/useLabelIdentifierFieldMetadataItem'; +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { getLabelIdentifierFieldValue } from '@/object-metadata/utils/getLabelIdentifierFieldValue'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle'; import { useRecoilValue } from 'recoil'; -import { FieldMetadataType } from 'twenty-shared/types'; -import { capitalize, isDefined } from 'twenty-shared/utils'; +import { isDefined } from 'twenty-shared/utils'; export const RecordShowPageTitle = ({ objectNameSingular, @@ -15,22 +16,21 @@ export const RecordShowPageTitle = ({ const { labelIdentifierFieldMetadataItem } = useLabelIdentifierFieldMetadataItem({ objectNameSingular }); - const record = useRecoilValue(recordStoreFamilyState(objectRecordId)); - const labelIdentifierFieldValue = record?.labelIdentifierFieldValue; + const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular }); - const pageName = - labelIdentifierFieldMetadataItem?.type === FieldMetadataType.FULL_NAME - ? [ - labelIdentifierFieldValue?.firstName, - labelIdentifierFieldValue?.lastName, - ].join(' ') - : isDefined(labelIdentifierFieldValue) - ? `${labelIdentifierFieldValue}` - : ''; + const record = useRecoilValue(recordStoreFamilyState(objectRecordId)); + + const pageName = isDefined(record) + ? getLabelIdentifierFieldValue( + record, + labelIdentifierFieldMetadataItem, + objectNameSingular, + ) + : ''; const pageTitle = pageName.trim() - ? `${pageName} - ${capitalize(objectNameSingular)}` - : capitalize(objectNameSingular); + ? `${pageName} - ${objectMetadataItem.labelSingular}` + : objectMetadataItem.labelSingular; return ; };