Fix orphan navigation menu items for deleted views (#18791)
## Summary Fixes #18757 This fixes a set of Favorites / navigation-menu-item integrity problems related to deleted views, stale hidden items, and upgraded workspaces with orphaned navigation items. ## What changed - delete `navigationMenuItem` entries when their favorited view is deleted - keep the client metadata store in sync immediately when a view is deleted - determine whether a view is already favorited from visible valid navigation items instead of raw stale items - add a `1.20.0` upgrade repair command that deletes orphan navigation menu items and normalizes positions - add regression coverage for deletion of both record-based and view-based navigation menu items ## Details Server: - extend `NavigationMenuItemDeletionService` so cleanup applies to deleted views as well as deleted records - add regression tests covering record-based deletion, view-based deletion, and no-op behavior - add `DeleteOrphanNavigationMenuItemsCommand` to remove orphaned items pointing to: - deleted views - deleted records - missing folders - normalize positions per scope (`userWorkspaceId + folderId`) after repair - wire the new repair command into the `1.20.0` upgrade flow Frontend: - add `useRemoveNavigationMenuItemByViewId` - remove the related navigation item from client metadata immediately when deleting a view - use sorted / visible navigation items for favorite detection so stale hidden rows do not block re-adding a favorite ## Why Issue `#18757` reports mismatches between Favorites shown in the UI and rows users can still find in the database. We found that current Favorites behavior is driven by `navigationMenuItem`, not the legacy `favorite` table, and that stale / orphaned `navigationMenuItem` rows could: - remain after deleting a favorited view - stay hidden from the UI if they point to invalid targets - still cause the UI to think a view was already favorited - persist in workspaces with migration damage from skipped sequential upgrades This patch addresses those cases directly and adds an upgrade-time repair path for older corrupted workspaces. ## Validation Passed: - `./node_modules/.bin/jest --config packages/twenty-server/jest.config.mjs --runInBand packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/services/__tests__/navigation-menu-item-deletion.service.spec.ts` - `./node_modules/.bin/tsc -p packages/twenty-front/tsconfig.json --noEmit --pretty false` Known unrelated existing failure: - `./node_modules/.bin/tsc -p packages/twenty-server/tsconfig.json --noEmit --pretty false` The server typecheck failure is pre-existing and unrelated to this branch. Current errors are around `@file-type/pdf` module resolution and `is-psl-parsed-domain.type.ts`. --------- Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
89300564ba
commit
7c8f060b08
+16
-3
@@ -4,10 +4,24 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { type FlatNavigationMenuItem } from 'src/engine/metadata-modules/flat-navigation-menu-item/types/flat-navigation-menu-item.type';
|
||||
import { fromDeleteNavigationMenuItemInputToFlatNavigationMenuItemOrThrow } from 'src/engine/metadata-modules/flat-navigation-menu-item/utils/from-delete-navigation-menu-item-input-to-flat-navigation-menu-item-or-throw.util';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
const isNavigationMenuItemForDeletedRecord = (
|
||||
item: FlatNavigationMenuItem,
|
||||
deletedIdsSet: Set<string>,
|
||||
): boolean =>
|
||||
isDefined(item.targetRecordId) &&
|
||||
!isDefined(item.viewId) &&
|
||||
deletedIdsSet.has(item.targetRecordId);
|
||||
|
||||
const isNavigationMenuItemForDeletedView = (
|
||||
item: FlatNavigationMenuItem,
|
||||
deletedIdsSet: Set<string>,
|
||||
): boolean => isDefined(item.viewId) && deletedIdsSet.has(item.viewId);
|
||||
|
||||
@Injectable()
|
||||
export class NavigationMenuItemDeletionService {
|
||||
constructor(
|
||||
@@ -40,9 +54,8 @@ export class NavigationMenuItemDeletionService {
|
||||
).filter(
|
||||
(item): item is NonNullable<typeof item> =>
|
||||
isDefined(item) &&
|
||||
isDefined(item.targetRecordId) &&
|
||||
!isDefined(item.viewId) &&
|
||||
deletedRecordIdsSet.has(item.targetRecordId),
|
||||
(isNavigationMenuItemForDeletedRecord(item, deletedRecordIdsSet) ||
|
||||
isNavigationMenuItemForDeletedView(item, deletedRecordIdsSet)),
|
||||
);
|
||||
|
||||
if (navigationMenuItemsToDelete.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user