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:
@@ -7,7 +7,7 @@ import { sidePanelPageInfoState } from '@/side-panel/states/sidePanelPageInfoSta
|
||||
import { sidePanelShouldFocusTitleInputComponentState } from '@/side-panel/states/sidePanelShouldFocusTitleInputComponentState';
|
||||
import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon';
|
||||
import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault';
|
||||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useUpdateFolderInDraft } from '@/navigation-menu-item/hooks/useUpdateFolderInDraft';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
@@ -41,7 +41,7 @@ export const SidePanelFolderInfo = () => {
|
||||
const selectedItem = selectedNavigationMenuItemInEditMode
|
||||
? items.find(
|
||||
(item) =>
|
||||
item.itemType === NavigationMenuItemType.FOLDER &&
|
||||
item.type === NavigationMenuItemType.FOLDER &&
|
||||
item.id === selectedNavigationMenuItemInEditMode,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useLingui } from '@lingui/react/macro';
|
||||
import { IconLink, IconWorld } from 'twenty-ui/display';
|
||||
|
||||
import { LinkIconWithLinkOverlay } from '@/navigation-menu-item/components/LinkIconWithLinkOverlay';
|
||||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useUpdateLinkInDraft } from '@/navigation-menu-item/hooks/useUpdateLinkInDraft';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
@@ -33,7 +33,7 @@ export const SidePanelLinkInfo = () => {
|
||||
const selectedItem = selectedNavigationMenuItemInEditMode
|
||||
? items.find(
|
||||
(item) =>
|
||||
item.itemType === NavigationMenuItemType.LINK &&
|
||||
item.type === NavigationMenuItemType.LINK &&
|
||||
item.id === selectedNavigationMenuItemInEditMode,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
+8
-8
@@ -3,7 +3,7 @@ import { OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
|
||||
import { SidePanelPageInfoLayout } from '@/side-panel/components/SidePanelPageInfoLayout';
|
||||
import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/NavigationMenuItemIcon';
|
||||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import { NavigationMenuItemType } from 'twenty-shared/types';
|
||||
import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem';
|
||||
import { useSelectedNavigationMenuItemEditItemLabel } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel';
|
||||
import { useSelectedNavigationMenuItemEditItemObjectMetadata } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemObjectMetadata';
|
||||
@@ -15,12 +15,12 @@ export const SidePanelObjectViewRecordInfo = () => {
|
||||
const { selectedItemObjectMetadata } =
|
||||
useSelectedNavigationMenuItemEditItemObjectMetadata();
|
||||
|
||||
const processedItem =
|
||||
selectedItem && selectedItem.itemType !== NavigationMenuItemType.FOLDER
|
||||
const navItem =
|
||||
selectedItem && selectedItem.type !== NavigationMenuItemType.FOLDER
|
||||
? selectedItem
|
||||
: undefined;
|
||||
|
||||
if (!processedItem || !selectedItemLabel) {
|
||||
if (!navItem || !selectedItemLabel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -28,22 +28,22 @@ export const SidePanelObjectViewRecordInfo = () => {
|
||||
NavigationMenuItemType.OBJECT,
|
||||
NavigationMenuItemType.VIEW,
|
||||
NavigationMenuItemType.RECORD,
|
||||
].includes(processedItem.itemType);
|
||||
].includes(navItem.type);
|
||||
|
||||
if (!isObjectViewOrRecord) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const label =
|
||||
processedItem.itemType === NavigationMenuItemType.RECORD
|
||||
navItem.type === NavigationMenuItemType.RECORD
|
||||
? selectedItemObjectMetadata?.labelSingular
|
||||
: processedItem.itemType === NavigationMenuItemType.OBJECT
|
||||
: navItem.type === NavigationMenuItemType.OBJECT
|
||||
? t`Object`
|
||||
: t`View`;
|
||||
|
||||
return (
|
||||
<SidePanelPageInfoLayout
|
||||
icon={<NavigationMenuItemIcon navigationMenuItem={processedItem} />}
|
||||
icon={<NavigationMenuItemIcon navigationMenuItem={navItem} />}
|
||||
title={<OverflowingTextWithTooltip text={selectedItemLabel} />}
|
||||
label={label}
|
||||
/>
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
OverflowingTextWithTooltip,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
|
||||
import { NavigationMenuItemType, SidePanelPages } from 'twenty-shared/types';
|
||||
import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
|
||||
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
|
||||
import { SidePanelAskAIInfo } from '@/side-panel/components/SidePanelAskAIInfo';
|
||||
@@ -18,7 +18,6 @@ import { SidePanelPageLayoutInfo } from '@/side-panel/components/SidePanelPageLa
|
||||
import { SidePanelRecordInfo } from '@/side-panel/components/SidePanelRecordInfo';
|
||||
import { SidePanelWorkflowStepInfo } from '@/side-panel/components/SidePanelWorkflowStepInfo';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { SidePanelPages } from 'twenty-shared/types';
|
||||
|
||||
import { type SidePanelContextChipProps } from '@/side-panel/components/SidePanelContextChip';
|
||||
import { useContext } from 'react';
|
||||
@@ -51,20 +50,20 @@ export const SidePanelPageInfo = ({ pageChip }: SidePanelPageInfoProps) => {
|
||||
: undefined;
|
||||
|
||||
if (isNavigationMenuItemEditPage && isDefined(selectedNavItem)) {
|
||||
const itemType = selectedNavItem.itemType;
|
||||
const navItemType = selectedNavItem.type;
|
||||
|
||||
if (itemType === NavigationMenuItemType.FOLDER) {
|
||||
if (navItemType === NavigationMenuItemType.FOLDER) {
|
||||
return <SidePanelFolderInfo />;
|
||||
}
|
||||
|
||||
if (itemType === NavigationMenuItemType.LINK) {
|
||||
if (navItemType === NavigationMenuItemType.LINK) {
|
||||
return <SidePanelLinkInfo />;
|
||||
}
|
||||
|
||||
if (
|
||||
itemType === NavigationMenuItemType.OBJECT ||
|
||||
itemType === NavigationMenuItemType.VIEW ||
|
||||
itemType === NavigationMenuItemType.RECORD
|
||||
navItemType === NavigationMenuItemType.OBJECT ||
|
||||
navItemType === NavigationMenuItemType.VIEW ||
|
||||
navItemType === NavigationMenuItemType.RECORD
|
||||
) {
|
||||
return <SidePanelObjectViewRecordInfo />;
|
||||
}
|
||||
|
||||
+3
-3
@@ -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}
|
||||
/>
|
||||
|
||||
+17
-5
@@ -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(
|
||||
|
||||
+5
-9
@@ -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
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+4
-2
@@ -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
-1
@@ -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';
|
||||
|
||||
+1
-1
@@ -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';
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user