feat: deprecate WorkspaceFavorite in favor of NavigationMenuItem (#18624)

## Summary

- **Removes the entire `modules/favorites/` directory** (~66 files,
~5000 lines deleted) — components, hooks, states, types, utils, tests,
and the favorite-folder-picker sub-module
- **Eliminates the dual-write pattern** where creating a favorite also
created a NavigationMenuItem — all consumers now use
`useCreateNavigationMenuItem` directly
- **Removes `IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED` feature flag
checks** from ~12 files, always taking the NavigationMenuItem code path
- **Cleans up backend dual-writes** in `object-metadata.service.ts` and
`twenty-standard-application.service.ts` that were creating Favorite
records alongside NavigationMenuItems
- **Updates prefetch system** to only load NavigationMenuItems (removes
favorites prefetch effects and states)
- **Cleans up test infrastructure** — updates Storybook decorators, mock
data, and graphql mocks to remove favorites references

### What was intentionally kept
- **Backend entity definitions** (`FavoriteWorkspaceEntity`,
`FavoriteFolderWorkspaceEntity`) — these define the database schema and
need a proper database migration to remove
- **Cascade deletion listeners** — still needed to clean up existing
Favorite data in workspaces that haven't been fully migrated
- **v1.18 migration commands** — needed for workspaces upgrading from
older versions

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Charles Bochet
2026-03-13 19:45:40 +01:00
committed by GitHub
parent 2a8912b17a
commit 6b48f197d4
152 changed files with 444 additions and 5575 deletions
@@ -11,7 +11,6 @@ import {
} from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault';
import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType';
@@ -27,16 +26,13 @@ import { openNavigationMenuItemFolderIdsState } from '@/navigation-menu-item/sta
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/utils/filterWorkspaceNavigationMenuItems';
import { preloadWorkspaceDndKit } from '@/navigation/preloadWorkspaceDndKit';
import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader';
import { NavigationDrawerSectionForWorkspaceItems } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItems';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavigationMenuItemsState';
import { navigationMenuItemsState } from '@/navigation-menu-item/states/navigationMenuItemsState';
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useStore } from 'jotai';
import { SidePanelPages } from 'twenty-shared/types';
@@ -51,18 +47,13 @@ export const WorkspaceNavigationMenuItems = () => {
const { workspaceNavigationMenuItemsSorted } = useSortedNavigationMenuItems();
const store = useStore();
const enterEditMode = () => {
const prefetchNavigationMenuItems = store.get(
prefetchNavigationMenuItemsState.atom,
);
const currentNavigationMenuItems = store.get(navigationMenuItemsState.atom);
const workspaceNavigationMenuItems = filterWorkspaceNavigationMenuItems(
prefetchNavigationMenuItems,
currentNavigationMenuItems,
);
store.set(navigationMenuItemsDraftState.atom, workspaceNavigationMenuItems);
store.set(isNavigationMenuInEditModeState.atom, true);
};
const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
);
const isNavigationMenuInEditMode = useAtomStateValue(
isNavigationMenuInEditModeState,
);
@@ -79,7 +70,6 @@ export const WorkspaceNavigationMenuItems = () => {
useOpenNavigationMenuItemInSidePanel();
const { getIcon } = useIcons();
const loading = useIsPrefetchLoading();
const { t } = useLingui();
const handleEditClick = (event: React.MouseEvent) => {
@@ -171,46 +161,36 @@ export const WorkspaceNavigationMenuItems = () => {
});
};
if (loading) {
return <NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader />;
}
return (
<NavigationDrawerSectionForWorkspaceItems
sectionTitle={t`Workspace`}
items={items}
rightIcon={
isNavigationMenuItemEditingEnabled ? (
<StyledRightIconsContainer>
{isNavigationMenuInEditMode ? (
<StyledRightIconsContainer>
{isNavigationMenuInEditMode ? (
<LightIconButton
Icon={IconPlus}
accent="tertiary"
size="small"
onClick={handleAddMenuItem}
/>
) : (
<div onMouseEnter={preloadWorkspaceDndKit}>
<LightIconButton
Icon={IconPlus}
Icon={IconTool}
accent="tertiary"
size="small"
onClick={handleAddMenuItem}
onClick={handleEditClick}
/>
) : (
<div onMouseEnter={preloadWorkspaceDndKit}>
<LightIconButton
Icon={IconTool}
accent="tertiary"
size="small"
onClick={handleEditClick}
/>
</div>
)}
</StyledRightIconsContainer>
) : undefined
</div>
)}
</StyledRightIconsContainer>
}
selectedNavigationMenuItemId={selectedNavigationMenuItemInEditMode}
onNavigationMenuItemClick={
isNavigationMenuInEditMode ? handleNavigationMenuItemClick : undefined
}
onActiveObjectMetadataItemClick={
isNavigationMenuItemEditingEnabled
? handleActiveObjectMetadataItemClick
: undefined
}
onActiveObjectMetadataItemClick={handleActiveObjectMetadataItemClick}
/>
);
};