Add opened section (#7265)
When object is not part of the workspace favorite list, we want to show it in the "opened section" while its record page is accessed. This PR: - adds a new component `NavigationDrawerOpenedSection` - makes workflow versions and runs not system object + creates a prefilled view index for these - do not create workspace favorites for these so these do not appear in the workspace section <img width="1129" alt="Capture d’écran 2024-09-26 à 11 45 25" src="https://github.com/user-attachments/assets/c84d773c-0bef-4dce-b66a-55d7d00b0fb6">
This commit is contained in:
@ -1,7 +1,6 @@
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { useFilteredObjectMetadataItemsForWorkspaceFavorites } from '@/navigation/hooks/useObjectMetadataItemsInWorkspaceFavorites';
|
||||
import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
|
||||
import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
@ -9,34 +8,18 @@ import { View } from '@/views/types/View';
|
||||
|
||||
export const WorkspaceFavorites = () => {
|
||||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||
|
||||
const { activeObjectMetadataItems: objectMetadataItemsToDisplay } =
|
||||
useFilteredObjectMetadataItemsForWorkspaceFavorites();
|
||||
|
||||
const loading = useIsPrefetchLoading();
|
||||
|
||||
const { workspaceFavorites } = useFavorites();
|
||||
|
||||
const workspaceFavoriteIds = new Set(
|
||||
workspaceFavorites.map((favorite) => favorite.recordId),
|
||||
);
|
||||
|
||||
const favoriteViewObjectMetadataIds = views.reduce<string[]>((acc, view) => {
|
||||
if (workspaceFavoriteIds.has(view.id)) {
|
||||
acc.push(view.objectMetadataId);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const { objectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
|
||||
const objectMetadataItemsToDisplay = objectMetadataItems.filter((item) =>
|
||||
favoriteViewObjectMetadataIds.includes(item.id),
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return <NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationDrawerSectionForObjectMetadataItems
|
||||
sectionTitle={'Workspace Favorites'}
|
||||
sectionTitle={'Workspace'}
|
||||
objectMetadataItems={objectMetadataItemsToDisplay}
|
||||
views={views}
|
||||
isRemote={false}
|
||||
|
||||
@ -5,6 +5,7 @@ import { IconSearch, IconSettings } from 'twenty-ui';
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { CurrentWorkspaceMemberFavorites } from '@/favorites/components/CurrentWorkspaceMemberFavorites';
|
||||
import { WorkspaceFavorites } from '@/favorites/components/WorkspaceFavorites';
|
||||
import { NavigationDrawerOpenedSection } from '@/object-metadata/components/NavigationDrawerOpenedSection';
|
||||
import { NavigationDrawerSectionForObjectMetadataItemsWrapper } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsWrapper';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||
@ -44,6 +45,8 @@ export const MainNavigationDrawerItems = () => {
|
||||
</NavigationDrawerSection>
|
||||
)}
|
||||
|
||||
{isWorkspaceFavoriteEnabled && <NavigationDrawerOpenedSection />}
|
||||
|
||||
<CurrentWorkspaceMemberFavorites />
|
||||
|
||||
{isWorkspaceFavoriteEnabled ? (
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { View } from '@/views/types/View';
|
||||
|
||||
export const useFilteredObjectMetadataItemsForWorkspaceFavorites = () => {
|
||||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||
|
||||
const { workspaceFavorites } = useFavorites();
|
||||
|
||||
const workspaceFavoriteIds = new Set(
|
||||
workspaceFavorites.map((favorite) => favorite.recordId),
|
||||
);
|
||||
|
||||
const favoriteViewObjectMetadataIds = new Set(
|
||||
views.reduce<string[]>((acc, view) => {
|
||||
if (workspaceFavoriteIds.has(view.id)) {
|
||||
acc.push(view.objectMetadataId);
|
||||
}
|
||||
return acc;
|
||||
}, []),
|
||||
);
|
||||
|
||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
|
||||
const activeObjectMetadataItemsInWorkspaceFavorites =
|
||||
activeObjectMetadataItems.filter((item) =>
|
||||
favoriteViewObjectMetadataIds.has(item.id),
|
||||
);
|
||||
|
||||
return {
|
||||
activeObjectMetadataItems: activeObjectMetadataItemsInWorkspaceFavorites,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,57 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { useFilteredObjectMetadataItemsForWorkspaceFavorites } from '@/navigation/hooks/useObjectMetadataItemsInWorkspaceFavorites';
|
||||
import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
|
||||
import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader';
|
||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { View } from '@/views/types/View';
|
||||
|
||||
export const NavigationDrawerOpenedSection = () => {
|
||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||
const filteredActiveObjectMetadataItems = activeObjectMetadataItems.filter(
|
||||
(item) => !item.isRemote,
|
||||
);
|
||||
|
||||
const { records: views } = usePrefetchedData<View>(PrefetchKey.AllViews);
|
||||
const loading = useIsPrefetchLoading();
|
||||
|
||||
const currentObjectNamePlural = useParams().objectNamePlural;
|
||||
|
||||
const { activeObjectMetadataItems: workspaceFavoritesObjectMetadataItems } =
|
||||
useFilteredObjectMetadataItemsForWorkspaceFavorites();
|
||||
|
||||
if (!currentObjectNamePlural) {
|
||||
return;
|
||||
}
|
||||
|
||||
const objectMetadataItem = filteredActiveObjectMetadataItems.find(
|
||||
(item) => item.namePlural === currentObjectNamePlural,
|
||||
);
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldDisplayObjectInOpenedSection =
|
||||
!workspaceFavoritesObjectMetadataItems
|
||||
.map((item) => item.id)
|
||||
.includes(objectMetadataItem.id);
|
||||
|
||||
if (loading) {
|
||||
return <NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
shouldDisplayObjectInOpenedSection && (
|
||||
<NavigationDrawerSectionForObjectMetadataItems
|
||||
sectionTitle={'Opened'}
|
||||
objectMetadataItems={[objectMetadataItem]}
|
||||
views={views}
|
||||
isRemote={false}
|
||||
/>
|
||||
)
|
||||
);
|
||||
};
|
||||
@ -1,40 +0,0 @@
|
||||
import * as reactRouterDom from 'react-router-dom';
|
||||
|
||||
import { useIsMenuNavbarDisplayed } from '../useIsMenuNavbarDisplayed';
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useLocation: jest.fn(),
|
||||
}));
|
||||
|
||||
const setupMockLocation = (pathname: string) => {
|
||||
jest.spyOn(reactRouterDom, 'useLocation').mockReturnValueOnce({
|
||||
pathname,
|
||||
state: undefined,
|
||||
key: '',
|
||||
search: '',
|
||||
hash: '',
|
||||
});
|
||||
};
|
||||
|
||||
describe('useIsMenuNavbarDisplayed', () => {
|
||||
it('Should return true for paths starting with "/companies"', () => {
|
||||
setupMockLocation('/companies');
|
||||
|
||||
const result = useIsMenuNavbarDisplayed();
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Should return true for paths starting with "/companies/"', () => {
|
||||
setupMockLocation('/companies/test-some-subpath');
|
||||
|
||||
const result = useIsMenuNavbarDisplayed();
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Should return false for paths not starting with "/companies"', () => {
|
||||
setupMockLocation('/test-path');
|
||||
|
||||
const result = useIsMenuNavbarDisplayed();
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
export const useIsMenuNavbarDisplayed = () => {
|
||||
const currentPath = useLocation().pathname;
|
||||
return currentPath.match(/^\/companies(\/.*)?$/) !== null;
|
||||
};
|
||||
Reference in New Issue
Block a user