diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/hooks/useObjectNavItemColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/hooks/useObjectNavItemColor.ts deleted file mode 100644 index 18b4a559a2..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/hooks/useObjectNavItemColor.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { isNonEmptyString } from '@sniptt/guards'; - -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; -import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; - -export const useObjectNavItemColor = (objectNameSingular: string): string => { - const { objectMetadataItems } = useObjectMetadataItems(); - const objectMetadataItem = objectMetadataItems.find( - (item) => item.nameSingular === objectNameSingular, - ); - - const storedColor = objectMetadataItem?.color; - const fallbackColor = getStandardObjectIconColor(objectNameSingular); - - if (isNonEmptyString(storedColor)) { - return storedColor; - } - - return fallbackColor; -}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getEffectiveNavigationMenuItemColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getEffectiveNavigationMenuItemColor.ts deleted file mode 100644 index dc096f8c95..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getEffectiveNavigationMenuItemColor.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { isNonEmptyString } from '@sniptt/guards'; - -import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultColorFolder'; -import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultColorLink'; -import { NavigationMenuItemType } from 'twenty-shared/types'; - -export const getEffectiveNavigationMenuItemColor = ( - navigationMenuItem: { - type: NavigationMenuItemType; - color?: string | null; - }, - objectColor?: string, -): string | undefined => { - if (navigationMenuItem.type === NavigationMenuItemType.FOLDER) { - return isNonEmptyString(navigationMenuItem.color) - ? navigationMenuItem.color - : DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER; - } - if (navigationMenuItem.type === NavigationMenuItemType.OBJECT) { - return objectColor; - } - if (navigationMenuItem.type === NavigationMenuItemType.VIEW) { - return objectColor; - } - if (navigationMenuItem.type === NavigationMenuItemType.LINK) { - return DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK; - } - return undefined; -}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemColor.ts new file mode 100644 index 0000000000..0000178d40 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemColor.ts @@ -0,0 +1,42 @@ +import { isNonEmptyString } from '@sniptt/guards'; + +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultColorFolder'; +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultColorLink'; +import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultIconColor.constant'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; +import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; +import { NavigationMenuItemType } from 'twenty-shared/types'; +import { type ThemeColor } from 'twenty-ui/theme'; + +export const getNavigationMenuItemColor = ( + navigationMenuItem: { + type: NavigationMenuItemType; + color?: string | null; + }, + objectMetadataItem?: Pick< + EnrichedObjectMetadataItem, + 'nameSingular' | 'color' | 'isSystem' + >, +): ThemeColor => { + if (navigationMenuItem.type === NavigationMenuItemType.FOLDER) { + return isNonEmptyString(navigationMenuItem.color) + ? (navigationMenuItem.color as ThemeColor) + : DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER; + } + + if (navigationMenuItem.type === NavigationMenuItemType.LINK) { + return DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK; + } + + if ( + navigationMenuItem.type === NavigationMenuItemType.OBJECT || + navigationMenuItem.type === NavigationMenuItemType.VIEW + ) { + if (objectMetadataItem) { + return getObjectColorWithFallback(objectMetadataItem); + } + return DEFAULT_NAV_ITEM_ICON_COLOR; + } + + return DEFAULT_NAV_ITEM_ICON_COLOR; +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconBorderColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconBorderColor.ts deleted file mode 100644 index e0f94480e7..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconBorderColor.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { ThemeColor } from 'twenty-ui/theme'; - -import { getColorFromTheme } from '@/navigation-menu-item/common/utils/getColorFromTheme'; -import { COLOR_SHADE_BORDER } from '@/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBorder'; - -export const getNavigationMenuItemIconBorderColor = ( - themeColor: ThemeColor, -): string => getColorFromTheme(themeColor, COLOR_SHADE_BORDER); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor.ts index 22c6be939a..e775304c2e 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor.ts @@ -1,4 +1,3 @@ -import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultIconColor.constant'; import { getColorFromTheme } from '@/navigation-menu-item/common/utils/getColorFromTheme'; import { COLOR_SHADE_BACKGROUND } from '@/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBackground'; import { COLOR_SHADE_BORDER } from '@/navigation-menu-item/common/utils/navigationMenuItemIconColorShadeBorder'; @@ -9,7 +8,7 @@ import { parseThemeColor } from '@/navigation-menu-item/common/utils/parseThemeC export const getNavigationMenuItemIconStyleFromColor = ( color: string | null | undefined, ): NavigationMenuItemIconStyle => { - const themeColor = parseThemeColor(color ?? DEFAULT_NAV_ITEM_ICON_COLOR); + const themeColor = parseThemeColor(color); return { backgroundColor: getColorFromTheme(themeColor, COLOR_SHADE_BACKGROUND), iconColor: getColorFromTheme(themeColor, COLOR_SHADE_ICON), diff --git a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getStandardObjectIconColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getStandardObjectIconColor.ts deleted file mode 100644 index 8c3a04b69f..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/common/utils/getStandardObjectIconColor.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { type ThemeColor, MAIN_COLOR_NAMES } from 'twenty-ui/theme'; - -import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/common/constants/NavigationMenuItemDefaultIconColor.constant'; -import { CoreObjectNameSingular } from 'twenty-shared/types'; - -const STANDARD_OBJECT_ICON_COLOR: Partial< - Record -> = { - [CoreObjectNameSingular.Company]: 'blue', - [CoreObjectNameSingular.Person]: 'blue', - [CoreObjectNameSingular.Task]: 'turquoise', - [CoreObjectNameSingular.TaskTarget]: 'turquoise', - [CoreObjectNameSingular.Note]: 'turquoise', - [CoreObjectNameSingular.NoteTarget]: 'turquoise', - [CoreObjectNameSingular.Opportunity]: 'red', - [CoreObjectNameSingular.Dashboard]: DEFAULT_NAV_ITEM_ICON_COLOR, - [CoreObjectNameSingular.Workflow]: DEFAULT_NAV_ITEM_ICON_COLOR, - [CoreObjectNameSingular.WorkflowRun]: DEFAULT_NAV_ITEM_ICON_COLOR, - [CoreObjectNameSingular.WorkflowVersion]: DEFAULT_NAV_ITEM_ICON_COLOR, -}; - -const CUSTOM_OBJECT_ICON_COLORS: ThemeColor[] = MAIN_COLOR_NAMES.filter( - (color) => color !== 'gray', -); - -const getColorForCustomObject = (seed: string): ThemeColor => { - let hash = 0; - for (let index = 0; index < seed.length; index += 1) { - hash = (hash << 5) - hash + seed.charCodeAt(index); - hash |= 0; - } - const index = Math.abs(hash) % CUSTOM_OBJECT_ICON_COLORS.length; - return CUSTOM_OBJECT_ICON_COLORS[index]; -}; - -export const getStandardObjectIconColor = (nameSingular: string): ThemeColor => - STANDARD_OBJECT_ICON_COLOR[nameSingular as CoreObjectNameSingular] ?? - getColorForCustomObject(nameSingular); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIcon.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIcon.tsx index 1d0cd6ef92..caef55c0bb 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIcon.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/components/NavigationMenuItemIcon.tsx @@ -1,4 +1,3 @@ -import { isNonEmptyString } from '@sniptt/guards'; import { NavigationMenuItemType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { Avatar, IconLink, IconWorld, useIcons } from 'twenty-ui/display'; @@ -8,8 +7,7 @@ import { type NavigationMenuItem } from '~/generated-metadata/graphql'; import { LinkIconWithLinkOverlay } from '@/navigation-menu-item/display/link/components/LinkIconWithLinkOverlay'; import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/display/components/NavigationMenuItemIconContainer'; import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay'; -import { useObjectNavItemColor } from '@/navigation-menu-item/common/hooks/useObjectNavItemColor'; -import { getEffectiveNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getEffectiveNavigationMenuItemColor'; +import { getNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemColor'; import { getNavigationMenuItemComputedLink } from '@/navigation-menu-item/display/utils/getNavigationMenuItemComputedLink'; import { getNavigationMenuItemIconStyleFromColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemIconStyleFromColor'; import { getNavigationMenuItemLabel } from '@/navigation-menu-item/display/utils/getNavigationMenuItemLabel'; @@ -53,7 +51,6 @@ export const NavigationMenuItemIcon = ({ const objectMetadataItem = objectMetadataItems.find( (item) => item.nameSingular === objectNameSingular, ); - const objectNavItemColor = useObjectNavItemColor(objectNameSingular); const objectIconForView = objectMetadataItem?.icon != null ? getIcon(objectMetadataItem.icon) @@ -67,7 +64,10 @@ export const NavigationMenuItemIcon = ({ ); } @@ -83,7 +83,7 @@ export const NavigationMenuItemIcon = ({ link={computedLink} LinkIcon={IconLink} DefaultIcon={IconWorld} - color={getEffectiveNavigationMenuItemColor(navigationMenuItem)} + color={getNavigationMenuItemColor(navigationMenuItem)} /> ); } @@ -95,11 +95,11 @@ export const NavigationMenuItemIcon = ({ : undefined; const iconToUse = StandardIcon ?? itemIcon; - const effectiveColor = getEffectiveNavigationMenuItemColor( + const effectiveColor = getNavigationMenuItemColor( navigationMenuItem, - objectNavItemColor, + objectMetadataItem, ); - const useStyledIcon = !isRecord && isNonEmptyString(effectiveColor); + const useStyledIcon = !isRecord; const iconStyle = useStyledIcon ? getNavigationMenuItemIconStyleFromColor(effectiveColor) : null; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle.tsx index b8c235cb90..0804248746 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/components/AddToNavigationDragHandle.tsx @@ -7,7 +7,7 @@ import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; import { NavigationMenuItemType } from 'twenty-shared/types'; import type { AddToNavigationDragPayload } from '@/navigation-menu-item/common/types/add-to-navigation-drag-payload'; -import { getEffectiveNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getEffectiveNavigationMenuItemColor'; +import { getNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemColor'; const StyledIconSlot = styled.div<{ $hasFixedSize: boolean; @@ -81,18 +81,18 @@ export const AddToNavigationDragHandle = ({ disableDrag = false, }: AddToNavigationDragHandleProps) => { const { theme } = useContext(ThemeContext); - const objectColor = - payload.type === NavigationMenuItemType.OBJECT - ? payload.iconColor - : undefined; - const effectiveColor = getEffectiveNavigationMenuItemColor( + const effectiveColor = getNavigationMenuItemColor( { type: payload.type as NavigationMenuItemType }, - objectColor, + payload.type === NavigationMenuItemType.OBJECT && payload.iconColor + ? { + nameSingular: '', + color: payload.iconColor, + isSystem: false, + } + : undefined, ); const hasBackgroundColor = - payload.type !== NavigationMenuItemType.RECORD && - isDefined(effectiveColor) && - !isHovered; + payload.type !== NavigationMenuItemType.RECORD && !isHovered; const showCustomContentWithoutWrapper = isDefined(customIconContent); return ( diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/hooks/useHandleAddToNavigationDrop.ts b/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/hooks/useHandleAddToNavigationDrop.ts index 544873a4d6..4ca9888d57 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/hooks/useHandleAddToNavigationDrop.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/dnd/hooks/useHandleAddToNavigationDrop.ts @@ -10,8 +10,8 @@ import { addToNavPayloadRegistryState } from '@/navigation-menu-item/common/stat import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState'; import { openNavigationMenuItemFolderIdsState } from '@/navigation-menu-item/common/states/openNavigationMenuItemFolderIdsState'; import { canNavigationMenuItemBeDroppedIn } from '@/navigation-menu-item/common/utils/canNavigationMenuItemBeDroppedIn'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { getObjectMetadataIdsInDraft } from '@/navigation-menu-item/common/utils/getObjectMetadataIdsInDraft'; -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; import { validateAndExtractWorkspaceFolderId } from '@/navigation-menu-item/common/utils/validateAndExtractWorkspaceFolderId'; import { useAddFolderToNavigationMenuDraft } from '@/navigation-menu-item/edit/folder/hooks/useAddFolderToNavigationMenuDraft'; import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/edit/hooks/useNavigationMenuItemsDraftState'; @@ -139,16 +139,17 @@ export const useHandleAddToNavigationDrop = () => { const objectMetadataItem = objectMetadataItems.find( (item) => item.id === payload.objectMetadataId, ); - const newItemId = addObjectToDraft( - payload.objectMetadataId, + const newItemId = addObjectToDraft({ + objectMetadataId: payload.objectMetadataId, currentDraft, - folderId, - index, - payload.iconColor ?? + targetFolderId: folderId, + targetIndex: index, + color: + payload.iconColor ?? (objectMetadataItem - ? getStandardObjectIconColor(objectMetadataItem.nameSingular) + ? getObjectColorWithFallback(objectMetadataItem) : undefined), - ); + }); openEditForNewNavItem(newItemId, { pageTitle: objectMetadataItem?.labelPlural ?? payload.label, pageIcon: objectMetadataItem @@ -170,7 +171,7 @@ export const useHandleAddToNavigationDrop = () => { folderId, index, viewObjectMetadataItem - ? getStandardObjectIconColor(viewObjectMetadataItem.nameSingular) + ? getObjectColorWithFallback(viewObjectMetadataItem) : undefined, ); openEditForNewNavItem(newItemId, { diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/folder/components/NavigationMenuItemFolderSubItem.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/folder/components/NavigationMenuItemFolderSubItem.tsx index fae897326e..09f226be77 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/folder/components/NavigationMenuItemFolderSubItem.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/folder/components/NavigationMenuItemFolderSubItem.tsx @@ -3,7 +3,7 @@ import { NavigationMenuItemType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { type NavigationMenuItem } from '~/generated-metadata/graphql'; -import { getEffectiveNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getEffectiveNavigationMenuItemColor'; +import { getNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemColor'; import { NavigationMenuItemIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemIcon'; import type { EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { getObjectMetadataForNavigationMenuItem } from '@/navigation-menu-item/display/object/utils/getObjectMetadataForNavigationMenuItem'; @@ -109,7 +109,10 @@ export const NavigationMenuItemFolderSubItem = ({ Icon={() => ( )} - iconColor={getEffectiveNavigationMenuItemColor(navigationMenuItem)} + iconColor={getNavigationMenuItemColor( + navigationMenuItem, + objectMetadataItem ?? undefined, + )} to={isDragging || handleClick ? undefined : computedLink} onClick={handleClick} active={index === selectedNavigationMenuItemIndex} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/display/object/components/NavigationDrawerItemForObjectMetadataItem.tsx b/packages/twenty-front/src/modules/navigation-menu-item/display/object/components/NavigationDrawerItemForObjectMetadataItem.tsx index 0bf46c254f..2a8619347b 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/display/object/components/NavigationDrawerItemForObjectMetadataItem.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/display/object/components/NavigationDrawerItemForObjectMetadataItem.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react'; import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState'; import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/display/view/components/ObjectIconWithViewOverlay'; -import { useObjectNavItemColor } from '@/navigation-menu-item/common/hooks/useObjectNavItemColor'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { getNavigationMenuItemComputedLink } from '@/navigation-menu-item/display/utils/getNavigationMenuItemComputedLink'; import { getNavigationMenuItemLabel } from '@/navigation-menu-item/display/utils/getNavigationMenuItemLabel'; import { recordIdentifierToObjectRecordIdentifier } from '@/navigation-menu-item/common/utils/recordIdentifierToObjectRecordIdentifier'; @@ -55,9 +55,7 @@ export const NavigationDrawerItemForObjectMetadataItem = ({ lastVisitedViewPerObjectMetadataItem?.[objectMetadataItem.id]; const { getIcon } = useIcons(); - const objectNavItemColor = useObjectNavItemColor( - objectMetadataItem.nameSingular, - ); + const objectNavItemColor = getObjectColorWithFallback(objectMetadataItem); const location = useLocation(); const currentPath = location.pathname; const currentPathWithSearch = `${location.pathname}${location.search}`; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/object/hooks/useAddObjectToNavigationMenuDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/edit/object/hooks/useAddObjectToNavigationMenuDraft.ts index cb58f2c665..ba36d9dfac 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/object/hooks/useAddObjectToNavigationMenuDraft.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/object/hooks/useAddObjectToNavigationMenuDraft.ts @@ -7,18 +7,26 @@ import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/sta import { computeInsertIndexAndPosition } from '@/navigation-menu-item/common/utils/computeInsertIndexAndPosition'; import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; +export type AddObjectToNavigationMenuDraftParams = { + objectMetadataId: string; + currentDraft: NavigationMenuItem[]; + targetFolderId?: string | null; + targetIndex?: number; + color?: string | null; +}; + export const useAddObjectToNavigationMenuDraft = () => { const setNavigationMenuItemsDraft = useSetAtomState( navigationMenuItemsDraftState, ); - const addObjectToDraft = ( - objectMetadataId: string, - currentDraft: NavigationMenuItem[], - targetFolderId?: string | null, - targetIndex?: number, - color?: string | null, - ): string => { + const addObjectToDraft = ({ + objectMetadataId, + currentDraft, + targetFolderId, + targetIndex, + color, + }: AddObjectToNavigationMenuDraftParams): string => { const folderId = targetFolderId ?? null; const itemsInFolder = currentDraft.filter( diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditObjectViewBase.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditObjectViewBase.tsx index 5c5a43896f..7cf06107f2 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditObjectViewBase.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelEditObjectViewBase.tsx @@ -1,6 +1,5 @@ -import { useObjectNavItemColor } from '@/navigation-menu-item/common/hooks/useObjectNavItemColor'; import { navigationMenuItemsSelector } from '@/navigation-menu-item/common/states/navigationMenuItemsSelector'; -import { getEffectiveNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getEffectiveNavigationMenuItemColor'; +import { getNavigationMenuItemColor } from '@/navigation-menu-item/common/utils/getNavigationMenuItemColor'; import { getNavigationMenuItemObjectNameSingular } from '@/navigation-menu-item/display/object/utils/getNavigationMenuItemObjectNameSingular'; import { parseThemeColor } from '@/navigation-menu-item/common/utils/parseThemeColor'; import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector'; @@ -50,7 +49,9 @@ export const SidePanelEditObjectViewBase = ({ ) ?? '') : ''; - const objectColor = useObjectNavItemColor(objectNameSingular); + const objectMetadataItem = objectMetadataItems.find( + (item) => item.nameSingular === objectNameSingular, + ); const navigationMenuItems = useAtomStateValue(navigationMenuItemsSelector); const persistedNavItem = navigationMenuItems.find( @@ -61,7 +62,7 @@ export const SidePanelEditObjectViewBase = ({ selectedItem.color !== (persistedNavItem?.color ?? undefined); const effectiveColor = isDefined(selectedItem) - ? getEffectiveNavigationMenuItemColor(selectedItem, objectColor) + ? getNavigationMenuItemColor(selectedItem, objectMetadataItem) : undefined; const displayColor = hasUserChangedColor ? selectedItem.color @@ -69,14 +70,16 @@ export const SidePanelEditObjectViewBase = ({ return ( - {showColorOption && isDefined(selectedItem) && ( - - - - )} + {showColorOption && + isDefined(selectedItem) && + objectMetadataItem?.isSystem !== true && ( + + + + )} { if (objectMetadataIdsInWorkspace.has(objectMetadataItem.id)) { return; } - const itemId = addObjectToDraft( - objectMetadataItem.id, + const itemId = addObjectToDraft({ + objectMetadataId: objectMetadataItem.id, currentDraft, - pendingInsertionNavigationMenuItem?.folderId, - pendingInsertionNavigationMenuItem?.position, - getStandardObjectIconColor(objectMetadataItem.nameSingular), - ); + targetFolderId: pendingInsertionNavigationMenuItem?.folderId, + targetIndex: pendingInsertionNavigationMenuItem?.position, + color: getObjectColorWithFallback(objectMetadataItem), + }); setPendingInsertionNavigationMenuItem(null); openNavigationMenuItemInSidePanel({ itemId, diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemObjectSystemPickerSubPage.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemObjectSystemPickerSubPage.tsx index 5183fea87a..7c8e3f8071 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemObjectSystemPickerSubPage.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemObjectSystemPickerSubPage.tsx @@ -1,16 +1,16 @@ import { useLingui } from '@lingui/react/macro'; -import { useAddObjectToNavigationMenuDraft } from '@/navigation-menu-item/edit/object/hooks/useAddObjectToNavigationMenuDraft'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { useDraftNavigationMenuItems } from '@/navigation-menu-item/edit/hooks/useDraftNavigationMenuItems'; import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/edit/hooks/useNavigationMenuObjectMetadataFromDraft'; import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/edit/hooks/useOpenNavigationMenuItemInSidePanel'; import { pendingInsertionNavigationMenuItemState } from '@/navigation-menu-item/common/states/pendingInsertionNavigationMenuItemState'; -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; +import { useAddObjectToNavigationMenuDraft } from '@/navigation-menu-item/edit/object/hooks/useAddObjectToNavigationMenuDraft'; +import { SidePanelSystemObjectPickerSubView } from '@/navigation-menu-item/edit/side-panel/components/SidePanelSystemObjectPickerSubView'; +import { getAvailableObjectMetadataForNewSidebarItem } from '@/navigation-menu-item/edit/side-panel/utils/getAvailableObjectMetadataForNewSidebarItem'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; -import { SidePanelSystemObjectPickerSubView } from '@/navigation-menu-item/edit/side-panel/components/SidePanelSystemObjectPickerSubView'; -import { getAvailableObjectMetadataForNewSidebarItem } from '@/navigation-menu-item/edit/side-panel/utils/getAvailableObjectMetadataForNewSidebarItem'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; import { ViewKey } from '@/views/types/ViewKey'; import { useState } from 'react'; @@ -58,13 +58,13 @@ export const SidePanelNewSidebarItemObjectSystemPickerSubPage = () => { if (objectMetadataIdsInWorkspace.has(objectMetadataItem.id)) { return; } - const itemId = addObjectToDraft( - objectMetadataItem.id, + const itemId = addObjectToDraft({ + objectMetadataId: objectMetadataItem.id, currentDraft, - pendingInsertionNavigationMenuItem?.folderId, - pendingInsertionNavigationMenuItem?.position, - getStandardObjectIconColor(objectMetadataItem.nameSingular), - ); + targetFolderId: pendingInsertionNavigationMenuItem?.folderId, + targetIndex: pendingInsertionNavigationMenuItem?.position, + color: getObjectColorWithFallback(objectMetadataItem), + }); setPendingInsertionNavigationMenuItem(null); openNavigationMenuItemInSidePanel({ itemId, diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewObjectPickerSubView.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewObjectPickerSubView.tsx index 0f169ead97..a862c8e52d 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewObjectPickerSubView.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewObjectPickerSubView.tsx @@ -3,7 +3,7 @@ import { IconSettings, useIcons } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; import { SidePanelList } from '@/side-panel/components/SidePanelList'; @@ -64,9 +64,7 @@ export const SidePanelNewSidebarItemViewObjectPickerSubView = ({ Icon={() => ( )} label={objectMetadataItem.labelPlural} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewPickerSubView.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewPickerSubView.tsx index 3ede954a52..1c6011fbec 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewPickerSubView.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewPickerSubView.tsx @@ -5,7 +5,7 @@ import { useDraftNavigationMenuItems } from '@/navigation-menu-item/edit/hooks/u import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/edit/hooks/useNavigationMenuObjectMetadataFromDraft'; import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/edit/hooks/useOpenNavigationMenuItemInSidePanel'; import { pendingInsertionNavigationMenuItemState } from '@/navigation-menu-item/common/states/pendingInsertionNavigationMenuItemState'; -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { SidePanelAddToNavigationDroppable } from '@/side-panel/components/SidePanelAddToNavigationDroppable'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; @@ -73,15 +73,17 @@ export const SidePanelNewSidebarItemViewPickerSubView = ({ ? t`No results found` : t`No custom views available`; + const selectedObjectIconColor = isDefined(selectedObjectMetadataItem) + ? getObjectColorWithFallback(selectedObjectMetadataItem) + : undefined; + const handleSelectView = (view: View) => { const itemId = addViewToDraft( view.id, currentDraft, pendingInsertionNavigationMenuItem?.folderId ?? null, pendingInsertionNavigationMenuItem?.position, - isDefined(selectedObjectMetadataItem) - ? getStandardObjectIconColor(selectedObjectMetadataItem.nameSingular) - : undefined, + selectedObjectIconColor, ); setPendingInsertionNavigationMenuItem(null); openNavigationMenuItemInSidePanel({ @@ -122,9 +124,7 @@ export const SidePanelNewSidebarItemViewPickerSubView = ({ selectedObjectMetadataItem.icon, )} ViewIcon={getIcon(view.icon)} - objectColor={getStandardObjectIconColor( - selectedObjectMetadataItem.nameSingular, - )} + objectColor={selectedObjectIconColor} /> ) : undefined } diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewSystemSubView.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewSystemSubView.tsx index d9820720d0..64923b2df2 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewSystemSubView.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemViewSystemSubView.tsx @@ -3,7 +3,7 @@ import { useIcons } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup'; import { SidePanelList } from '@/side-panel/components/SidePanelList'; @@ -63,9 +63,7 @@ export const SidePanelNewSidebarItemViewSystemSubView = ({ Icon={() => ( )} label={objectMetadataItem.labelPlural} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem.tsx index ad70a90e71..9ef0898d99 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem.tsx @@ -7,7 +7,7 @@ import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/comp import { NavigationMenuItemType } from 'twenty-shared/types'; import { useDraftNavigationMenuItems } from '@/navigation-menu-item/edit/hooks/useDraftNavigationMenuItems'; import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/edit/hooks/useNavigationMenuObjectMetadataFromDraft'; -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SidePanelItemWithAddToNavigationDrag } from '@/side-panel/components/SidePanelItemWithAddToNavigationDrag'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -42,7 +42,7 @@ export const SidePanelObjectMenuItem = ({ { objectMetadataItemId: objectMetadataItem.id }, ); const Icon = getIcon(objectMetadataItem.icon); - const iconColor = getStandardObjectIconColor(objectMetadataItem.nameSingular); + const iconColor = getObjectColorWithFallback(objectMetadataItem); const isDisabled = isAlreadyInNavbar || !isDefined(defaultViewId); const handleClick = () => { diff --git a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem.tsx b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem.tsx index 9e78acd180..5a6684b99a 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerItem.tsx @@ -3,7 +3,7 @@ import { useIcons } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { SidePanelObjectMenuItem } from '@/navigation-menu-item/edit/side-panel/components/SidePanelObjectMenuItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -41,9 +41,7 @@ export const SidePanelObjectPickerItem = ({ Icon={() => ( )} label={objectMetadataItem.labelPlural} diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getObjectColorWithFallback.ts b/packages/twenty-front/src/modules/object-metadata/utils/getObjectColorWithFallback.ts new file mode 100644 index 0000000000..35e60e4290 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/utils/getObjectColorWithFallback.ts @@ -0,0 +1,58 @@ +import { isNonEmptyString } from '@sniptt/guards'; + +import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; +import { CoreObjectNameSingular } from 'twenty-shared/types'; +import { type ThemeColor, MAIN_COLOR_NAMES } from 'twenty-ui/theme'; + +const SYSTEM_OBJECT_COLOR: ThemeColor = 'gray'; + +const STANDARD_OBJECT_FALLBACK_COLOR: Partial< + Record +> = { + [CoreObjectNameSingular.Company]: 'blue', + [CoreObjectNameSingular.Person]: 'blue', + [CoreObjectNameSingular.Task]: 'turquoise', + [CoreObjectNameSingular.TaskTarget]: 'turquoise', + [CoreObjectNameSingular.Note]: 'turquoise', + [CoreObjectNameSingular.NoteTarget]: 'turquoise', + [CoreObjectNameSingular.Opportunity]: 'red', + [CoreObjectNameSingular.Dashboard]: SYSTEM_OBJECT_COLOR, + [CoreObjectNameSingular.Workflow]: SYSTEM_OBJECT_COLOR, + [CoreObjectNameSingular.WorkflowRun]: SYSTEM_OBJECT_COLOR, + [CoreObjectNameSingular.WorkflowVersion]: SYSTEM_OBJECT_COLOR, +}; + +const CUSTOM_OBJECT_ICON_COLORS: ThemeColor[] = MAIN_COLOR_NAMES.filter( + (color) => color !== 'gray', +); + +const getColorForCustomObject = (seed: string): ThemeColor => { + let hash = 0; + for (let index = 0; index < seed.length; index += 1) { + hash = (hash << 5) - hash + seed.charCodeAt(index); + hash |= 0; + } + const colorIndex = Math.abs(hash) % CUSTOM_OBJECT_ICON_COLORS.length; + return CUSTOM_OBJECT_ICON_COLORS[colorIndex]; +}; + +export const getObjectColorWithFallback = ( + objectMetadataItem: Pick< + EnrichedObjectMetadataItem, + 'nameSingular' | 'color' | 'isSystem' + >, +): ThemeColor => { + if (objectMetadataItem.isSystem) { + return SYSTEM_OBJECT_COLOR; + } + + if (isNonEmptyString(objectMetadataItem.color)) { + return objectMetadataItem.color as ThemeColor; + } + + return ( + STANDARD_OBJECT_FALLBACK_COLOR[ + objectMetadataItem.nameSingular as CoreObjectNameSingular + ] ?? getColorForCustomObject(objectMetadataItem.nameSingular) + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeaderIcon.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeaderIcon.tsx index 2b89f6ae73..86c69e4805 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeaderIcon.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeaderIcon.tsx @@ -1,7 +1,5 @@ -import { isNonEmptyString } from '@sniptt/guards'; - import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/EnrichedObjectMetadataItem'; import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; @@ -14,18 +12,11 @@ export const RecordIndexPageHeaderIcon = ({ const { getIcon } = useIcons(); const ObjectIcon = getIcon(objectMetadataItem?.icon); - if (!isDefined(ObjectIcon)) { + if (!isDefined(ObjectIcon) || !isDefined(objectMetadataItem)) { return null; } - const iconColor = isNonEmptyString(objectMetadataItem?.color) - ? objectMetadataItem.color - : getStandardObjectIconColor(objectMetadataItem?.nameSingular ?? ''); + const iconColor = getObjectColorWithFallback(objectMetadataItem); - return ( - - ); + return ; }; diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx index 401edcb393..bce68d646d 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/ObjectRecordShowPageBreadcrumb.tsx @@ -1,7 +1,5 @@ -import { isNonEmptyString } from '@sniptt/guards'; - import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/display/components/NavigationMenuItemStyleIcon'; -import { getStandardObjectIconColor } from '@/navigation-menu-item/common/utils/getStandardObjectIconColor'; +import { getObjectColorWithFallback } from '@/object-metadata/utils/getObjectColorWithFallback'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; @@ -85,9 +83,7 @@ export const ObjectRecordShowPageBreadcrumb = ({ objectRecordId, ); - const iconColor = isNonEmptyString(objectMetadataItem?.color) - ? objectMetadataItem.color - : getStandardObjectIconColor(objectNameSingular); + const iconColor = getObjectColorWithFallback(objectMetadataItem); if (loading) { return null; @@ -101,10 +97,7 @@ export const ObjectRecordShowPageBreadcrumb = ({ }} > {isDefined(HeaderIcon) && ( - + )} {objectLabel} {' / '}