From ad10e0a0764db995eac171391f8d32c9f262175f Mon Sep 17 00:00:00 2001 From: Abdul Rahman Date: Mon, 2 Feb 2026 07:33:35 +0530 Subject: [PATCH 001/182] Enhance navigation menu item components with icon color customization - Introduced `getNavigationMenuItemIconColors` utility to manage icon colors based on the theme. - Updated `CurrentWorkspaceMemberNavigationMenuItems` and `NavigationDrawerItemForObjectMetadataItem` to utilize the new icon color utility. - Refactored `NavigationMenuItemIcon` to include a styled background for icons, improving visual consistency. - Adjusted `NavigationDrawerItem` and `NavigationDrawerSubItem` to accept and apply background colors for icons. These changes improve the visual representation of navigation items and ensure consistent theming across the application. --- ...rentWorkspaceMemberNavigationMenuItems.tsx | 6 +++ .../components/NavigationMenuItemIcon.tsx | 41 +++++++++++----- .../utils/getNavigationMenuItemIconColors.ts | 7 +++ ...igationDrawerItemForObjectMetadataItem.tsx | 6 +++ .../components/NavigationDrawerItem.tsx | 47 +++++++++++++------ .../components/NavigationDrawerSubItem.tsx | 2 + 6 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 packages/twenty-front/src/modules/navigation-menu-item/utils/getNavigationMenuItemIconColors.ts diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberNavigationMenuItems.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberNavigationMenuItems.tsx index 22d2d52d8fdd3..b4d1858de2422 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberNavigationMenuItems.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberNavigationMenuItems.tsx @@ -1,3 +1,4 @@ +import { useTheme } from '@emotion/react'; import { Droppable } from '@hello-pangea/dnd'; import { useLingui } from '@lingui/react/macro'; import { useContext, useState } from 'react'; @@ -5,6 +6,8 @@ import { createPortal } from 'react-dom'; import { useLocation } from 'react-router-dom'; import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { IconFolder, IconFolderOpen, IconHeartOff } from 'twenty-ui/display'; + +import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; import { LightIconButton } from 'twenty-ui/input'; import { AnimatedExpandableContainer } from 'twenty-ui/layout'; import { useIsMobile } from 'twenty-ui/utilities'; @@ -50,6 +53,8 @@ export const CurrentWorkspaceMemberNavigationMenuItems = ({ isGroup, }: CurrentWorkspaceMemberNavigationMenuItemsProps) => { const { t } = useLingui(); + const theme = useTheme(); + const iconColors = getNavigationMenuItemIconColors(theme); const objectMetadataItems = useRecoilValue(objectMetadataItemsState); const currentPath = useLocation().pathname; const currentViewPath = useLocation().pathname + useLocation().search; @@ -194,6 +199,7 @@ export const CurrentWorkspaceMemberNavigationMenuItems = ({ ` + align-items: center; + background-color: ${({ backgroundColor }) => backgroundColor}; + border-radius: ${({ theme }) => theme.border.radius.xs}; + display: flex; + height: ${({ theme }) => theme.spacing(4)}; + justify-content: center; + width: ${({ theme }) => theme.spacing(4)}; +`; + export const NavigationMenuItemIcon = ({ navigationMenuItem, }: { @@ -11,25 +24,31 @@ export const NavigationMenuItemIcon = ({ }) => { const theme = useTheme(); const { getIcon } = useIcons(); - const { Icon: StandardIcon, IconColor } = useGetStandardObjectIcon( + const { Icon: StandardIcon } = useGetStandardObjectIcon( navigationMenuItem.objectNameSingular || '', ); const IconToUse = StandardIcon || (navigationMenuItem.Icon ? getIcon(navigationMenuItem.Icon) : undefined); - const iconColorToUse = StandardIcon ? IconColor : theme.font.color.secondary; const placeholderColorSeed = navigationMenuItem.targetRecordId ?? undefined; + const iconColors = getNavigationMenuItemIconColors(theme); + const iconBackgroundColor = isDefined(navigationMenuItem.viewId) + ? iconColors.view + : iconColors.object; + return ( - + + + ); }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/getNavigationMenuItemIconColors.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/getNavigationMenuItemIconColors.ts new file mode 100644 index 0000000000000..2481fdafa08f6 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/getNavigationMenuItemIconColors.ts @@ -0,0 +1,7 @@ +import type { Theme } from '@emotion/react'; + +export const getNavigationMenuItemIconColors = (theme: Theme) => ({ + folder: theme.color.orange, + view: theme.grayScale.gray8, + object: theme.color.blue, +}); diff --git a/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx index e152f436129e3..594f40f5f81e4 100644 --- a/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx +++ b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx @@ -1,5 +1,6 @@ import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; +import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; @@ -8,6 +9,7 @@ import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/compo import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { coreViewsFromObjectMetadataItemFamilySelector } from '@/views/states/selectors/coreViewsFromObjectMetadataItemFamilySelector'; +import { useTheme } from '@emotion/react'; import { useLocation } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; import { AppPath } from 'twenty-shared/types'; @@ -22,6 +24,8 @@ export type NavigationDrawerItemForObjectMetadataItemProps = { export const NavigationDrawerItemForObjectMetadataItem = ({ objectMetadataItem, }: NavigationDrawerItemForObjectMetadataItemProps) => { + const theme = useTheme(); + const iconColors = getNavigationMenuItemIconColors(theme); const views = useRecoilValue( coreViewsFromObjectMetadataItemFamilySelector({ objectMetadataItemId: objectMetadataItem.id, @@ -82,6 +86,7 @@ export const NavigationDrawerItemForObjectMetadataItem = ({ label={objectMetadataItem.labelPlural} to={navigationPath} Icon={getIcon(objectMetadataItem.icon)} + iconBackgroundColor={iconColors.object} active={isActive} /> @@ -106,6 +111,7 @@ export const NavigationDrawerItemForObjectMetadataItem = ({ selectedIndex: selectedSubItemIndex, })} Icon={getIcon(view.icon)} + iconBackgroundColor={iconColors.view} key={view.id} /> ))} diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx index 836fcac13a664..70f83b934f557 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx @@ -1,8 +1,7 @@ -import { t } from '@lingui/core/macro'; import { useIsSettingsPage } from '@/navigation/hooks/useIsSettingsPage'; +import { NAVIGATION_DRAWER_COLLAPSED_WIDTH } from '@/ui/layout/resizable-panel/constants/NavigationDrawerCollapsedWidth'; import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper'; import { NavigationDrawerItemBreadcrumb } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemBreadcrumb'; -import { NAVIGATION_DRAWER_COLLAPSED_WIDTH } from '@/ui/layout/resizable-panel/constants/NavigationDrawerCollapsedWidth'; import { useNavigationDrawerTooltip } from '@/ui/navigation/navigation-drawer/hooks/useNavigationDrawerTooltip'; import { type NavigationDrawerSubItemState } from '@/ui/navigation/navigation-drawer/types/NavigationDrawerSubItemState'; import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded'; @@ -10,6 +9,7 @@ import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import isPropValid from '@emotion/is-prop-valid'; import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { t } from '@lingui/core/macro'; import { type ReactNode } from 'react'; import { Link } from 'react-router-dom'; import { useRecoilState } from 'recoil'; @@ -42,6 +42,7 @@ export type NavigationDrawerItemProps = { to?: string; onClick?: () => void; Icon?: IconComponent | ((props: TablerIconsProps) => JSX.Element); + iconBackgroundColor?: string; active?: boolean; danger?: boolean; soon?: boolean; @@ -193,13 +194,22 @@ const StyledSpacer = styled.span` flex-grow: 1; `; -const StyledIcon = styled.div` - flex-shrink: 0; - flex-grow: 0; - display: flex; +const StyledIcon = styled.div<{ $backgroundColor?: string }>` align-items: center; + display: flex; + flex-grow: 0; + flex-shrink: 0; justify-content: center; margin-right: ${({ theme }) => theme.spacing(2)}; + + ${({ theme, $backgroundColor }) => + $backgroundColor && + ` + background-color: ${$backgroundColor}; + border-radius: ${theme.border.radius.xs}; + height: ${theme.spacing(4)}; + width: ${theme.spacing(4)}; + `} `; const StyledRightOptionsContainer = styled.div` @@ -251,6 +261,7 @@ export const NavigationDrawerItem = ({ secondaryLabel, indentationLevel = DEFAULT_INDENTATION_LEVEL, Icon, + iconBackgroundColor, to, onClick, active, @@ -324,17 +335,25 @@ export const NavigationDrawerItem = ({ )} {Icon && ( - + diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx index 9fb2813df294a..f96f4f288d979 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem.tsx @@ -10,6 +10,7 @@ export const NavigationDrawerSubItem = ({ label, secondaryLabel, Icon, + iconBackgroundColor, to, onClick, active, @@ -30,6 +31,7 @@ export const NavigationDrawerSubItem = ({ indentationLevel={2} subItemState={subItemState} Icon={Icon} + iconBackgroundColor={iconBackgroundColor} to={to} onClick={onClick} active={active} From 8cbbc1b6bff26e9a78f3e47b8bdc093d187003dd Mon Sep 17 00:00:00 2001 From: Abdul Rahman Date: Mon, 2 Feb 2026 07:56:13 +0530 Subject: [PATCH 002/182] Refactor NavigationMenuItemIcon to enhance icon color handling - Added `IconColor` to the `useGetStandardObjectIcon` hook for improved icon color customization. - Simplified logic for determining icon background color based on the presence of `targetRecordId` and `viewId`. - Consolidated avatar rendering logic to reduce redundancy and improve readability. These changes enhance the visual consistency and flexibility of navigation menu items. --- .../components/NavigationMenuItemIcon.tsx | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemIcon.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemIcon.tsx index cce2c5956e213..7d04956247b89 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemIcon.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemIcon.tsx @@ -24,7 +24,7 @@ export const NavigationMenuItemIcon = ({ }) => { const theme = useTheme(); const { getIcon } = useIcons(); - const { Icon: StandardIcon } = useGetStandardObjectIcon( + const { Icon: StandardIcon, IconColor } = useGetStandardObjectIcon( navigationMenuItem.objectNameSingular || '', ); const IconToUse = @@ -33,22 +33,39 @@ export const NavigationMenuItemIcon = ({ const placeholderColorSeed = navigationMenuItem.targetRecordId ?? undefined; + const isRecord = isDefined(navigationMenuItem.targetRecordId); const iconColors = getNavigationMenuItemIconColors(theme); - const iconBackgroundColor = isDefined(navigationMenuItem.viewId) - ? iconColors.view - : iconColors.object; + const iconBackgroundColor = isRecord + ? undefined + : isDefined(navigationMenuItem.viewId) + ? iconColors.view + : iconColors.object; + + const iconColorToUse = iconBackgroundColor + ? theme.grayScale.gray1 + : StandardIcon + ? IconColor + : theme.font.color.secondary; + + const avatar = ( + + ); + + if (!iconBackgroundColor) { + return avatar; + } return ( - + {avatar} ); }; From ef617aac547c4b12bdcc80d2ba5b59e3f1b2f5e2 Mon Sep 17 00:00:00 2001 From: Abdul Rahman Date: Mon, 2 Feb 2026 14:40:21 +0530 Subject: [PATCH 003/182] Add IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED feature flag - Introduced the IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED flag across the GraphQL schema and server-side enums. - Updated the workspace entity manager tests to include the new feature flag. - Enhanced the seed feature flags utility to support the new flag. These changes enable editing capabilities for navigation menu items, improving customization options. --- packages/twenty-front/src/generated-metadata/graphql.ts | 1 + packages/twenty-front/src/generated/graphql.ts | 1 + .../core-modules/feature-flag/enums/feature-flag-key.enum.ts | 1 + .../entity-manager/workspace-entity-manager.spec.ts | 1 + .../dev-seeder/core/utils/seed-feature-flags.util.ts | 5 +++++ 5 files changed, 9 insertions(+) diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 51a4edcbe3473..abd5ca0201b10 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1415,6 +1415,7 @@ export enum FeatureFlagKey { IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', IS_JUNCTION_RELATIONS_ENABLED = 'IS_JUNCTION_RELATIONS_ENABLED', IS_MARKETPLACE_ENABLED = 'IS_MARKETPLACE_ENABLED', + IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED = 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED', IS_NAVIGATION_MENU_ITEM_ENABLED = 'IS_NAVIGATION_MENU_ITEM_ENABLED', IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED', IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED = 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED', diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index d38e6dc0c0d80..67673d103d508 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -1387,6 +1387,7 @@ export enum FeatureFlagKey { IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', IS_JUNCTION_RELATIONS_ENABLED = 'IS_JUNCTION_RELATIONS_ENABLED', IS_MARKETPLACE_ENABLED = 'IS_MARKETPLACE_ENABLED', + IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED = 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED', IS_NAVIGATION_MENU_ITEM_ENABLED = 'IS_NAVIGATION_MENU_ITEM_ENABLED', IS_PUBLIC_DOMAIN_ENABLED = 'IS_PUBLIC_DOMAIN_ENABLED', IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED = 'IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED', diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts index 4d3b173b5193a..14b85299c857b 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts @@ -17,5 +17,6 @@ export enum FeatureFlagKey { IS_SSE_DB_EVENTS_ENABLED = 'IS_SSE_DB_EVENTS_ENABLED', IS_COMMAND_MENU_ITEM_ENABLED = 'IS_COMMAND_MENU_ITEM_ENABLED', IS_NAVIGATION_MENU_ITEM_ENABLED = 'IS_NAVIGATION_MENU_ITEM_ENABLED', + IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED = 'IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED', IS_FILES_FIELD_ENABLED = 'IS_FILES_FIELD_ENABLED', } diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts index d337cf3805a86..758226cbe6aa2 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts @@ -240,6 +240,7 @@ describe('WorkspaceEntityManager', () => { IS_SSE_DB_EVENTS_ENABLED: false, IS_COMMAND_MENU_ITEM_ENABLED: false, IS_NAVIGATION_MENU_ITEM_ENABLED: false, + IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED: false, IS_FILES_FIELD_ENABLED: false, IS_APPLICATION_INSTALLATION_FROM_TARBALL_ENABLED: false, IS_MARKETPLACE_ENABLED: false, diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts index f18d284d8fae7..9bd910c79205c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts @@ -86,6 +86,11 @@ export const seedFeatureFlags = async ({ workspaceId: workspaceId, value: true, }, + { + key: FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED, + workspaceId: workspaceId, + value: true, + }, { key: FeatureFlagKey.IS_FILES_FIELD_ENABLED, workspaceId: workspaceId, From 986a36a8fdbf783813a5421714afccc66195b986 Mon Sep 17 00:00:00 2001 From: Abdul Rahman Date: Mon, 2 Feb 2026 14:45:15 +0530 Subject: [PATCH 004/182] Add navigation menu edit mode hooks and state management - Introduced `useNavigationMenuEditModeActions` for managing edit mode actions, including entering and canceling edit mode. - Added `useNavigationMenuItemsDraftState` to handle draft state of navigation menu items, determining workspace items based on edit mode. - Created `isNavigationMenuInEditModeState` and `navigationMenuItemsDraftState` atoms for managing edit mode status and draft items. These additions enhance the functionality for editing navigation menu items, improving user experience and customization options. --- .../hooks/useNavigationMenuEditModeActions.ts | 39 +++++++++++++++++ .../hooks/useNavigationMenuItemsDraftState.ts | 43 +++++++++++++++++++ .../states/isNavigationMenuInEditModeState.ts | 6 +++ .../states/navigationMenuItemsDraftState.ts | 7 +++ 4 files changed, 95 insertions(+) create mode 100644 packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuEditModeActions.ts create mode 100644 packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuItemsDraftState.ts create mode 100644 packages/twenty-front/src/modules/navigation-menu-item/states/isNavigationMenuInEditModeState.ts create mode 100644 packages/twenty-front/src/modules/navigation-menu-item/states/navigationMenuItemsDraftState.ts diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuEditModeActions.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuEditModeActions.ts new file mode 100644 index 0000000000000..cb4ad1a62207a --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuEditModeActions.ts @@ -0,0 +1,39 @@ +import { useRecoilCallback, useSetRecoilState } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; + +import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState'; +import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState'; +import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavigationMenuItemsState'; + +export const useNavigationMenuEditModeActions = () => { + const setIsNavigationMenuInEditMode = useSetRecoilState( + isNavigationMenuInEditModeState, + ); + const setNavigationMenuItemsDraft = useSetRecoilState( + navigationMenuItemsDraftState, + ); + + const enterEditMode = useRecoilCallback( + ({ set, snapshot }) => + () => { + const prefetchNavigationMenuItems = snapshot + .getLoadable(prefetchNavigationMenuItemsState) + .getValue(); + + const workspaceNavigationMenuItems = prefetchNavigationMenuItems.filter( + (item) => !isDefined(item.userWorkspaceId), + ); + + set(navigationMenuItemsDraftState, [...workspaceNavigationMenuItems]); + set(isNavigationMenuInEditModeState, true); + }, + [], + ); + + const cancelEditMode = () => { + setNavigationMenuItemsDraft(null); + setIsNavigationMenuInEditMode(false); + }; + + return { enterEditMode, cancelEditMode }; +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuItemsDraftState.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuItemsDraftState.ts new file mode 100644 index 0000000000000..83e9d9c43e7db --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuItemsDraftState.ts @@ -0,0 +1,43 @@ +import { useRecoilState, useRecoilValue } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; + +import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState'; +import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState'; +import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavigationMenuItemsState'; + +export const useNavigationMenuItemsDraftState = () => { + const isNavigationMenuInEditMode = useRecoilValue( + isNavigationMenuInEditModeState, + ); + const prefetchNavigationMenuItems = useRecoilValue( + prefetchNavigationMenuItemsState, + ); + const [navigationMenuItemsDraft, setNavigationMenuItemsDraft] = + useRecoilState(navigationMenuItemsDraftState); + + const workspaceNavigationMenuItemsFromPrefetch = + prefetchNavigationMenuItems.filter( + (item) => !isDefined(item.userWorkspaceId), + ); + + const workspaceNavigationMenuItems = + isNavigationMenuInEditMode && isDefined(navigationMenuItemsDraft) + ? navigationMenuItemsDraft + : workspaceNavigationMenuItemsFromPrefetch; + + const isDirty = + isNavigationMenuInEditMode && + isDefined(navigationMenuItemsDraft) && + !isDeeplyEqual( + navigationMenuItemsDraft, + workspaceNavigationMenuItemsFromPrefetch, + ); + + return { + workspaceNavigationMenuItems, + navigationMenuItemsDraft, + setNavigationMenuItemsDraft, + isDirty, + }; +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/states/isNavigationMenuInEditModeState.ts b/packages/twenty-front/src/modules/navigation-menu-item/states/isNavigationMenuInEditModeState.ts new file mode 100644 index 0000000000000..1436f18e3241e --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/states/isNavigationMenuInEditModeState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const isNavigationMenuInEditModeState = atom({ + key: 'isNavigationMenuInEditModeState', + default: false, +}); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/states/navigationMenuItemsDraftState.ts b/packages/twenty-front/src/modules/navigation-menu-item/states/navigationMenuItemsDraftState.ts new file mode 100644 index 0000000000000..7673331d23533 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/states/navigationMenuItemsDraftState.ts @@ -0,0 +1,7 @@ +import { atom } from 'recoil'; +import { type NavigationMenuItem } from '~/generated-metadata/graphql'; + +export const navigationMenuItemsDraftState = atom({ + key: 'navigationMenuItemsDraftState', + default: null, +}); From 2affeaff7e373b17b15dac48e17eceadd67a3626 Mon Sep 17 00:00:00 2001 From: Abdul Rahman Date: Mon, 2 Feb 2026 17:09:49 +0530 Subject: [PATCH 005/182] Enhance Save and Cancel Buttons with Inverted Style Support - Added an `inverted` prop to `CancelButton`, allowing for a tertiary button style. - Updated `SaveButton` to support an `inverted` prop, changing its appearance based on the prop value. - Modified `SaveAndCancelButtons` to pass the `inverted` prop to both buttons, ensuring consistent styling. These changes improve the visual flexibility of the buttons in the settings module, enhancing user experience. --- .../SaveAndCancelButtons/CancelButton.tsx | 19 ++++++++++++++++++- .../SaveAndCancelButtons.tsx | 13 ++++++++++++- .../SaveAndCancelButtons/SaveButton.tsx | 13 +++++++++---- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx b/packages/twenty-front/src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx index 871f0964837b9..80582641a0612 100644 --- a/packages/twenty-front/src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx +++ b/packages/twenty-front/src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx @@ -1,16 +1,33 @@ import { useLingui } from '@lingui/react/macro'; -import { LightButton } from 'twenty-ui/input'; +import { Button, LightButton } from 'twenty-ui/input'; type CancelButtonProps = { onCancel?: () => void; disabled?: boolean; + inverted?: boolean; }; export const CancelButton = ({ onCancel, disabled = false, + inverted = false, }: CancelButtonProps) => { const { t } = useLingui(); + + if (inverted) { + return ( +