Integrate NavigationMenuItem with feature flag support (#17268)
## Implement Navigation Menu Items Frontend Implements the frontend for navigation menu items, the new system replacing favorites. ### Changes - Added GraphQL fragments and queries for navigation menu items - Added hooks for managing navigation menu items (create, update, delete, sorting, filtering) - Updated components to use navigation menu items instead of favorites - Added test coverage for utility functions ### Migration Note The favorites and navigation menu item modules currently exist in parallel. The favorites code will be removed once all data has been migrated to navigation menu items. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Replaces Favorites with feature-flagged `NavigationMenuItem` across frontend and backend, while keeping Favorites as fallback until migration completes. > > - UI: new `navigation-menu-item` components (folders, orphan items, drag provider/droppable, icons, skeleton), dispatcher components to switch from Favorites, and updated “Add to favorites” action to create `NavigationMenuItem` when `IS_NAVIGATION_MENU_ITEM_ENABLED` > - DnD: shared `validateAndExtractFolderId` and droppable id utils moved to `ui/layout/draggable-list`; favorites DnD updated to use shared utils > - GraphQL (client): add fragments, queries, mutations, hooks (create/update/delete/find), and generated types; added `RecordIdentifier` and `targetRecordIdentifier` on `NavigationMenuItem` > - Prefetch: new prefetch state/effect for navigation menu items; skip favorites prefetch when flag enabled > - Backend: add DTOs (`NavigationMenuItem`, `RecordIdentifier`), resolver `targetRecordIdentifier` field, service logic to fetch record identifiers with permission-aware access and image signing, `getRecordImageIdentifier` util, entity relation to `view`, and migration adding FK on `viewId` > - Feature flags & seeding: add `IS_NAVIGATION_MENU_ITEM_ENABLED` to enums, dev seeder enables it; standard app seeds workspace navigation menu items instead of favorites when flag on > - Tests: add unit tests for sorting/labels/folder id and related utils > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit c99746f08b9f84fc8cec4fcc3a7d7afb8ea92db7. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Aman Raj <92664006+araj00@users.noreply.github.com> Co-authored-by: Félix Malfait <felix@twenty.com> Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions <github-actions@twenty.com>
This commit is contained in:
+25
-6
@@ -1,5 +1,7 @@
|
||||
import { useCreateFavorite } from '@/favorites/hooks/useCreateFavorite';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { useCreateNavigationMenuItem } from '@/navigation-menu-item/hooks/useCreateNavigationMenuItem';
|
||||
import { usePrefetchedNavigationMenuItemsData } from '@/navigation-menu-item/hooks/usePrefetchedNavigationMenuItemsData';
|
||||
import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@@ -10,6 +12,7 @@ import { type View } from '@/views/types/View';
|
||||
import { useDestroyViewFromCurrentState } from '@/views/view-picker/hooks/useDestroyViewFromCurrentState';
|
||||
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
|
||||
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import {
|
||||
IconHeart,
|
||||
@@ -19,7 +22,7 @@ import {
|
||||
useIcons,
|
||||
} from 'twenty-ui/display';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
import { ViewVisibility } from '~/generated-metadata/graphql';
|
||||
import { FeatureFlagKey, ViewVisibility } from '~/generated-metadata/graphql';
|
||||
import { PermissionFlagType } from '~/generated/graphql';
|
||||
|
||||
type ViewPickerOptionDropdownProps = {
|
||||
@@ -57,16 +60,28 @@ export const ViewPickerOptionDropdown = ({
|
||||
|
||||
const { sortedFavorites: favorites } = useFavorites();
|
||||
const { createFavorite } = useCreateFavorite();
|
||||
const isNavigationMenuItemEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_ENABLED,
|
||||
);
|
||||
const { createNavigationMenuItem } = useCreateNavigationMenuItem();
|
||||
const { navigationMenuItems, currentWorkspaceMemberId } =
|
||||
usePrefetchedNavigationMenuItemsData();
|
||||
|
||||
// Users with VIEWS permission can edit all views
|
||||
// Users without VIEWS permission can only edit unlisted views (which are always their own, filtered by backend)
|
||||
const canEditView =
|
||||
hasViewsPermission || view.visibility === ViewVisibility.UNLISTED;
|
||||
|
||||
const isFavorite = favorites.some(
|
||||
(favorite) =>
|
||||
favorite.recordId === view.id && favorite.forWorkspaceMemberId,
|
||||
);
|
||||
const isFavorite = isNavigationMenuItemEnabled
|
||||
? navigationMenuItems.some(
|
||||
(item) =>
|
||||
item.viewId === view.id &&
|
||||
item.userWorkspaceId === currentWorkspaceMemberId,
|
||||
)
|
||||
: favorites.some(
|
||||
(favorite) =>
|
||||
favorite.recordId === view.id && favorite.forWorkspaceMemberId,
|
||||
);
|
||||
|
||||
const handleDelete = () => {
|
||||
setViewPickerReferenceViewId(view.id);
|
||||
@@ -76,7 +91,11 @@ export const ViewPickerOptionDropdown = ({
|
||||
|
||||
const handleAddToFavorites = () => {
|
||||
if (!isFavorite) {
|
||||
createFavorite(view, 'view');
|
||||
if (isNavigationMenuItemEnabled) {
|
||||
createNavigationMenuItem(view, 'view');
|
||||
} else {
|
||||
createFavorite(view, 'view');
|
||||
}
|
||||
} else {
|
||||
setViewPickerReferenceViewId(view.id);
|
||||
setViewPickerMode('favorite-folders-picker');
|
||||
|
||||
Reference in New Issue
Block a user