refactor: remove ProcessedNavigationMenuItem, derive display fields at point of use (#18687)

## Summary

- Removes `ProcessedNavigationMenuItem` type and the
`sortNavigationMenuItems` enrichment pipeline that pre-computed display
fields (label, link, icon, avatarUrl, etc.) for every navigation menu
item upfront
- Replaces with `filterAndSortNavigationMenuItems` (pure filter+sort
returning raw `NavigationMenuItem[]`) and small utility functions
(`getNavigationMenuItemLabel`, `getNavigationMenuItemComputedLink`,
`getNavigationMenuItemObjectNameSingular`) that components call on
demand
- Eliminates the redundant `itemType` field (was identical to the raw
`type` field) across ~30 consumer files
- Each type-specific renderer
(`NavigationDrawerItemForObjectMetadataItem`, `NavigationMenuItemIcon`,
link/folder components) now derives only the 1-2 display fields it
actually needs from the raw item + globally available Jotai atoms

Net result: -1199 / +1177 lines, 7 files deleted, 4 new utility files.
This commit is contained in:
Charles Bochet
2026-03-17 00:33:06 +01:00
committed by GitHub
parent 48285be164
commit 1bb642d7cd
55 changed files with 1188 additions and 1227 deletions
@@ -2,8 +2,8 @@ import { useLingui } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { useState } from 'react';
import { getAbsoluteUrl } from 'twenty-shared/utils';
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/types/processed-navigation-menu-item';
import { extractDomainFromUrl } from '@/navigation-menu-item/utils/extractDomainFromUrl';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
@@ -16,7 +16,7 @@ import { getOrganizeActionsSelectableItemIds } from '@/side-panel/pages/navigati
import { TextInput } from '@/ui/input/components/TextInput';
type SidePanelEditLinkItemViewProps = OrganizeActionsProps & {
selectedItem: ProcessedNavigationMenuItem;
selectedItem: NavigationMenuItem;
onUpdateLink: (
linkId: string,
updates: { link?: string; name?: string },
@@ -78,7 +78,7 @@ export const SidePanelEditLinkItemView = ({
<TextInput
fullWidth
placeholder="www.google.com"
value={urlEditInput || selectedItem.link}
value={urlEditInput || selectedItem.link || ''}
onChange={handleUrlChange}
onBlur={handleUrlBlur}
/>
@@ -1,8 +1,9 @@
import { useObjectNavItemColor } from '@/navigation-menu-item/hooks/useObjectNavItemColor';
import { navigationMenuItemsSelector } from '@/navigation-menu-item/states/navigationMenuItemsSelector';
import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/types/processed-navigation-menu-item';
import { getEffectiveNavigationMenuItemColor } from '@/navigation-menu-item/utils/getEffectiveNavigationMenuItemColor';
import { getNavigationMenuItemObjectNameSingular } from '@/navigation-menu-item/utils/getNavigationMenuItemObjectNameSingular';
import { parseThemeColor } from '@/navigation-menu-item/utils/parseThemeColor';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
import { SidePanelEditColorOption } from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditColorOption';
@@ -12,14 +13,16 @@ import {
} from '@/side-panel/pages/navigation-menu-item/components/SidePanelEditOrganizeActions';
import { getOrganizeActionsSelectableItemIds } from '@/side-panel/pages/navigation-menu-item/utils/getOrganizeActionsSelectableItemIds';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { viewsSelector } from '@/views/states/selectors/viewsSelector';
import { useLingui } from '@lingui/react/macro';
import { isNonEmptyString } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { type NavigationMenuItem } from '~/generated-metadata/graphql';
type SidePanelEditObjectViewBaseProps = OrganizeActionsProps & {
onOpenFolderPicker: () => void;
showColorOption?: boolean;
selectedItem?: ProcessedNavigationMenuItem | null;
selectedItem?: NavigationMenuItem | null;
};
export const SidePanelEditObjectViewBase = ({
@@ -36,9 +39,18 @@ export const SidePanelEditObjectViewBase = ({
}: SidePanelEditObjectViewBaseProps) => {
const { t } = useLingui();
const selectableItemIds = getOrganizeActionsSelectableItemIds(true);
const objectColor = useObjectNavItemColor(
selectedItem?.objectNameSingular ?? '',
);
const objectMetadataItems = useAtomStateValue(objectMetadataItemsState);
const views = useAtomStateValue(viewsSelector);
const objectNameSingular = isDefined(selectedItem)
? (getNavigationMenuItemObjectNameSingular(
selectedItem,
objectMetadataItems,
views,
) ?? '')
: '';
const objectColor = useObjectNavItemColor(objectNameSingular);
const navigationMenuItems = useAtomStateValue(navigationMenuItemsSelector);
const persistedNavItem = navigationMenuItems.find(
@@ -1,12 +1,11 @@
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/hooks/useNavigationMenuItemsDraftState';
import { useOpenAddItemToFolderPage } from '@/navigation-menu-item/hooks/useOpenAddItemToFolderPage';
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem';
import { useSelectedNavigationMenuItemEditItemLabel } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel';
import { useUpdateLinkInDraft } from '@/navigation-menu-item/hooks/useUpdateLinkInDraft';
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/types/processed-navigation-menu-item';
import { parseThemeColor } from '@/navigation-menu-item/utils/parseThemeColor';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
import { SidePanelList } from '@/side-panel/components/SidePanelList';
@@ -46,7 +45,7 @@ export const SidePanelNavigationMenuItemEditPage = () => {
);
const { selectedItemLabel } = useSelectedNavigationMenuItemEditItemLabel();
const { selectedItem } = useSelectedNavigationMenuItemEditItem();
const selectedItemType = selectedItem?.itemType ?? null;
const selectedItemType = selectedItem?.type ?? null;
const { navigateToSidePanelSubPage } = useSidePanelSubPageHistory();
const openFolderPicker = () =>
@@ -67,10 +66,7 @@ export const SidePanelNavigationMenuItemEditPage = () => {
const { workspaceNavigationMenuItems } = useNavigationMenuItemsDraftState();
const handleAddItemToFolder = () => {
if (
!selectedItem ||
selectedItem.itemType !== NavigationMenuItemType.FOLDER
) {
if (!selectedItem || selectedItem.type !== NavigationMenuItemType.FOLDER) {
return;
}
const folderItemCount = workspaceNavigationMenuItems.filter(
@@ -106,7 +102,7 @@ export const SidePanelNavigationMenuItemEditPage = () => {
onAddBefore={onAddBefore}
onAddAfter={onAddAfter}
showColorOption={isDefined(selectedItem)}
selectedItem={selectedItem as ProcessedNavigationMenuItem | undefined}
selectedItem={selectedItem}
/>
);
case NavigationMenuItemType.VIEW:
@@ -125,7 +121,7 @@ export const SidePanelNavigationMenuItemEditPage = () => {
case NavigationMenuItemType.LINK:
if (
isDefined(selectedItem) &&
selectedItem.itemType === NavigationMenuItemType.LINK
selectedItem.type === NavigationMenuItemType.LINK
) {
return (
<SidePanelEditLinkItemView
@@ -11,7 +11,7 @@ import { themeCssVariables } from 'twenty-ui/theme-constants';
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState';
import { SidePanelAddToNavigationDroppable } from '@/side-panel/components/SidePanelAddToNavigationDroppable';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
@@ -1,6 +1,5 @@
import { Avatar, IconFolder, useIcons } from 'twenty-ui/display';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { useAddRecordToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddRecordToNavigationMenuDraft';
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInSidePanel';
@@ -11,7 +10,10 @@ import { SidePanelItemWithAddToNavigationDrag } from '@/side-panel/components/Si
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { CoreObjectNameSingular } from 'twenty-shared/types';
import {
CoreObjectNameSingular,
NavigationMenuItemType,
} from 'twenty-shared/types';
type SearchRecord = {
recordId: string;
@@ -1,5 +1,5 @@
import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/components/ObjectIconWithViewOverlay';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { useAddViewToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddViewToNavigationMenuDraft';
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
@@ -4,7 +4,7 @@ import { useIcons } from 'twenty-ui/display';
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems';
import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft';
import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor';
@@ -1,6 +1,6 @@
import { isDefined } from 'twenty-shared/utils';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { useDraftNavigationMenuItemsAllFolders } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItemsAllFolders';
import { useDraftNavigationMenuItemsWorkspaceFolders } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItemsWorkspaceFolders';
import { useNavigationMenuItemMoveRemove } from '@/navigation-menu-item/hooks/useNavigationMenuItemMoveRemove';
@@ -45,7 +45,7 @@ export const useFolderPickerSelectionData = () => {
selectedNavigationMenuItemInEditModeState,
);
const { selectedItem } = useSelectedNavigationMenuItemEditItem();
const selectedItemType = selectedItem?.itemType ?? null;
const selectedItemType = selectedItem?.type ?? null;
const { allFolders } = useDraftNavigationMenuItemsAllFolders();
const { workspaceFolders } = useDraftNavigationMenuItemsWorkspaceFolders();