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
@@ -7,8 +7,7 @@ import { contextStoreIsPageInEditModeComponentState } from '@/context-store/stat
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { usePrefetchedNavigationMenuItemsData } from '@/navigation-menu-item/hooks/usePrefetchedNavigationMenuItemsData';
import { useNavigationMenuItemsData } from '@/navigation-menu-item/hooks/useNavigationMenuItemsData';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject';
import { hasAnySoftDeleteFilterOnViewComponentSelector } from '@/object-record/record-filter/states/hasAnySoftDeleteFilterOnView';
@@ -18,7 +17,6 @@ import { useAtomComponentSelectorValue } from '@/ui/utilities/state/jotai/hooks/
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { isNonEmptyArray } from '@sniptt/guards';
import { useStore } from 'jotai';
import { useContext } from 'react';
@@ -27,7 +25,6 @@ import {
type CommandMenuContextApi,
} from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
export const useCommandMenuContextApi = (): CommandMenuContextApi => {
const store = useStore();
@@ -42,18 +39,13 @@ export const useCommandMenuContextApi = (): CommandMenuContextApi => {
contextStoreTargetedRecordsRuleComponentState,
);
const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
);
const { objectMetadataItems } = useObjectMetadataItems();
const objectMetadataItem = objectMetadataItems.find(
(item) => item.id === contextStoreCurrentObjectMetadataItemId,
);
const { sortedFavorites: favorites } = useFavorites();
const { navigationMenuItems } = usePrefetchedNavigationMenuItemsData();
const { navigationMenuItems } = useNavigationMenuItemsData();
const recordIds =
contextStoreTargetedRecordsRule.mode === 'selection'
@@ -61,22 +53,16 @@ export const useCommandMenuContextApi = (): CommandMenuContextApi => {
: undefined;
const favoriteRecordIds = (() => {
if (!isNonEmptyArray(recordIds)) {
if (!isNonEmptyArray(recordIds) || !isDefined(objectMetadataItem)) {
return [];
}
if (isNavigationMenuItemEditingEnabled && isDefined(objectMetadataItem)) {
return recordIds.filter((recordId) =>
navigationMenuItems?.some(
(item) =>
item.targetRecordId === recordId &&
item.targetObjectMetadataId === objectMetadataItem.id,
),
);
}
return recordIds.filter((recordId) =>
favorites?.some((favorite) => favorite.recordId === recordId),
navigationMenuItems?.some(
(item) =>
item.targetRecordId === recordId &&
item.targetObjectMetadataId === objectMetadataItem.id,
),
);
})();