Feat: Navbar customization (#17728)

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
This commit is contained in:
Abdul Rahman
2026-02-11 21:03:51 +05:30
committed by GitHub
parent 52e57e70fd
commit 0902579fbe
166 changed files with 7716 additions and 667 deletions
@@ -1,26 +1,188 @@
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import {
useRecoilCallback,
useRecoilState,
useRecoilValue,
useSetRecoilState,
} from 'recoil';
import {
IconFolder,
IconLink,
IconPlus,
IconTool,
useIcons,
} from 'twenty-ui/display';
import { LightIconButton } from 'twenty-ui/input';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { useWorkspaceNavigationMenuItems } from '@/navigation-menu-item/hooks/useWorkspaceNavigationMenuItems';
import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu';
import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
import { useOpenNavigationMenuItemInCommandMenu } from '@/navigation-menu-item/hooks/useOpenNavigationMenuItemInCommandMenu';
import {
type NavigationMenuItemClickParams,
useWorkspaceSectionItems,
} from '@/navigation-menu-item/hooks/useWorkspaceSectionItems';
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState';
import { NAVIGATION_MENU_ITEM_TYPE } from '@/navigation-menu-item/types/navigation-menu-item-type';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState';
import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/utils/filterWorkspaceNavigationMenuItems';
import { openNavigationMenuItemFolderIdsState } from '@/navigation-menu-item/states/openNavigationMenuItemFolderIdsState';
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState';
import { NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader';
import { NavigationDrawerSectionForWorkspaceItems } from '@/object-metadata/components/NavigationDrawerSectionForWorkspaceItems';
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavigationMenuItemsState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { isDefined } from 'twenty-shared/utils';
const StyledRightIconsContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(1)};
`;
export const WorkspaceNavigationMenuItems = () => {
const { workspaceNavigationMenuItemsObjectMetadataItems } =
useWorkspaceNavigationMenuItems();
const items = useWorkspaceSectionItems();
const enterEditMode = useRecoilCallback(
({ set, snapshot }) =>
() => {
const prefetchNavigationMenuItems = snapshot
.getLoadable(prefetchNavigationMenuItemsState)
.getValue();
const workspaceNavigationMenuItems = filterWorkspaceNavigationMenuItems(
prefetchNavigationMenuItems,
);
set(navigationMenuItemsDraftState, workspaceNavigationMenuItems);
set(isNavigationMenuInEditModeState, true);
},
[],
);
const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled(
FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED,
);
const isNavigationMenuInEditMode = useRecoilValue(
isNavigationMenuInEditModeState,
);
const [
selectedNavigationMenuItemInEditMode,
setSelectedNavigationMenuItemInEditMode,
] = useRecoilState(selectedNavigationMenuItemInEditModeState);
const setOpenNavigationMenuItemFolderIds = useSetRecoilState(
openNavigationMenuItemFolderIdsState,
);
const { navigateCommandMenu } = useNavigateCommandMenu();
const { openNavigationMenuItemInCommandMenu } =
useOpenNavigationMenuItemInCommandMenu();
const { getIcon } = useIcons();
const loading = useIsPrefetchLoading();
const { t } = useLingui();
const handleEditClick = (event: React.MouseEvent) => {
event.stopPropagation();
enterEditMode();
};
const handleNavigationMenuItemClick = (
params: NavigationMenuItemClickParams,
) => {
const { item, objectMetadataItem } = params;
const id = item.id;
setSelectedNavigationMenuItemInEditMode(id);
if (item.itemType === NAVIGATION_MENU_ITEM_TYPE.FOLDER) {
setOpenNavigationMenuItemFolderIds((currentOpenFolders) =>
currentOpenFolders.includes(id)
? currentOpenFolders
: [...currentOpenFolders, id],
);
openNavigationMenuItemInCommandMenu({
pageTitle: t`Edit folder`,
pageIcon: IconFolder,
});
} else if (item.itemType === NAVIGATION_MENU_ITEM_TYPE.LINK) {
openNavigationMenuItemInCommandMenu({
pageTitle: t`Edit link`,
pageIcon: IconLink,
});
} else if (isDefined(objectMetadataItem)) {
openNavigationMenuItemInCommandMenu({
pageTitle: objectMetadataItem.labelPlural,
pageIcon: getIcon(objectMetadataItem.icon),
});
}
};
const handleActiveObjectMetadataItemClick = (
objectMetadataItem: ObjectMetadataItem,
navigationMenuItemId: string,
) => {
enterEditMode();
setSelectedNavigationMenuItemInEditMode(navigationMenuItemId);
openNavigationMenuItemInCommandMenu({
pageTitle: objectMetadataItem.labelPlural,
pageIcon: getIcon(objectMetadataItem.icon),
});
};
const handleAddMenuItem = (event?: React.MouseEvent) => {
event?.stopPropagation();
navigateCommandMenu({
page: CommandMenuPages.NavigationMenuAddItem,
pageTitle: t`New sidebar item`,
pageIcon: IconPlus,
resetNavigationStack: true,
});
};
const isEditMode =
isNavigationMenuItemEditingEnabled && isNavigationMenuInEditMode;
if (loading) {
return <NavigationDrawerSectionForObjectMetadataItemsSkeletonLoader />;
}
return (
<NavigationDrawerSectionForObjectMetadataItems
<NavigationDrawerSectionForWorkspaceItems
sectionTitle={t`Workspace`}
objectMetadataItems={workspaceNavigationMenuItemsObjectMetadataItems}
isRemote={false}
items={items}
rightIcon={
isNavigationMenuItemEditingEnabled ? (
<StyledRightIconsContainer>
{isEditMode ? (
<LightIconButton
Icon={IconPlus}
accent="tertiary"
size="small"
onClick={handleAddMenuItem}
/>
) : (
<LightIconButton
Icon={IconTool}
accent="tertiary"
size="small"
onClick={handleEditClick}
/>
)}
</StyledRightIconsContainer>
) : undefined
}
onAddMenuItem={
isNavigationMenuItemEditingEnabled && isEditMode
? handleAddMenuItem
: undefined
}
isEditMode={isEditMode}
selectedNavigationMenuItemId={selectedNavigationMenuItemInEditMode}
onNavigationMenuItemClick={
isEditMode ? handleNavigationMenuItemClick : undefined
}
onActiveObjectMetadataItemClick={
isNavigationMenuItemEditingEnabled
? handleActiveObjectMetadataItemClick
: undefined
}
/>
);
};