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:
Thomas Trompette
2024-10-07 13:45:29 +02:00
committed by GitHub
parent 2bc7974da9
commit ce676f699d
25 changed files with 311 additions and 95 deletions

View File

@ -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}

View File

@ -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 ? (

View File

@ -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,
};
};

View File

@ -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}
/>
)
);
};

View File

@ -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();
});
});

View File

@ -1,6 +0,0 @@
import { useLocation } from 'react-router-dom';
export const useIsMenuNavbarDisplayed = () => {
const currentPath = useLocation().pathname;
return currentPath.match(/^\/companies(\/.*)?$/) !== null;
};