Set system object icon colors to gray in New menu item flows (#18862)

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Abdul Rahman
2026-03-25 04:47:23 +05:30
committed by GitHub
parent 981e83ecb1
commit 766f956a15
23 changed files with 209 additions and 214 deletions
@@ -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;
};
@@ -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;
};
@@ -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;
};
@@ -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);
@@ -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),
@@ -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, ThemeColor>
> = {
[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);
@@ -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 = ({
<ObjectIconWithViewOverlay
ObjectIcon={objectIconForView}
ViewIcon={getIcon(view!.icon)}
objectColor={objectNavItemColor}
objectColor={getNavigationMenuItemColor(
navigationMenuItem,
objectMetadataItem,
)}
/>
);
}
@@ -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;
@@ -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 (
@@ -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, {
@@ -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={() => (
<NavigationMenuItemIcon navigationMenuItem={navigationMenuItem} />
)}
iconColor={getEffectiveNavigationMenuItemColor(navigationMenuItem)}
iconColor={getNavigationMenuItemColor(
navigationMenuItem,
objectMetadataItem ?? undefined,
)}
to={isDragging || handleClick ? undefined : computedLink}
onClick={handleClick}
active={index === selectedNavigationMenuItemIndex}
@@ -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}`;
@@ -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(
@@ -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 (
<SidePanelList commandGroups={[]} selectableItemIds={selectableItemIds}>
{showColorOption && isDefined(selectedItem) && (
<SidePanelGroup heading={t`Customize`}>
<SidePanelEditColorOption
navigationMenuItemId={selectedItem.id}
color={parseThemeColor(displayColor)}
/>
</SidePanelGroup>
)}
{showColorOption &&
isDefined(selectedItem) &&
objectMetadataItem?.isSystem !== true && (
<SidePanelGroup heading={t`Customize`}>
<SidePanelEditColorOption
navigationMenuItemId={selectedItem.id}
color={parseThemeColor(displayColor)}
/>
</SidePanelGroup>
)}
<SidePanelEditOrganizeActions
canMoveUp={canMoveUp}
canMoveDown={canMoveDown}
@@ -1,17 +1,17 @@
import { useState } from 'react';
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 { SidePanelObjectPickerSubView } from '@/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerSubView';
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 { useSidePanelSubPageHistory } from '@/side-panel/hooks/useSidePanelSubPageHistory';
import { SidePanelObjectPickerSubView } from '@/navigation-menu-item/edit/side-panel/components/SidePanelObjectPickerSubView';
import { getAvailableObjectMetadataForNewSidebarItem } from '@/navigation-menu-item/edit/side-panel/utils/getAvailableObjectMetadataForNewSidebarItem';
import { SidePanelSubPages } from '@/side-panel/types/SidePanelSubPages';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { ViewKey } from '@/views/types/ViewKey';
@@ -59,13 +59,13 @@ export const SidePanelNewSidebarItemObjectSubPage = () => {
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,
@@ -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,
@@ -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={() => (
<NavigationMenuItemStyleIcon
Icon={getIcon(objectMetadataItem.icon)}
color={getStandardObjectIconColor(
objectMetadataItem.nameSingular,
)}
color={getObjectColorWithFallback(objectMetadataItem)}
/>
)}
label={objectMetadataItem.labelPlural}
@@ -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
}
@@ -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={() => (
<NavigationMenuItemStyleIcon
Icon={getIcon(objectMetadataItem.icon)}
color={getStandardObjectIconColor(
objectMetadataItem.nameSingular,
)}
color={getObjectColorWithFallback(objectMetadataItem)}
/>
)}
label={objectMetadataItem.labelPlural}
@@ -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 = () => {
@@ -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={() => (
<NavigationMenuItemStyleIcon
Icon={getIcon(objectMetadataItem.icon)}
color={getStandardObjectIconColor(
objectMetadataItem.nameSingular,
)}
color={getObjectColorWithFallback(objectMetadataItem)}
/>
)}
label={objectMetadataItem.labelPlural}
@@ -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, ThemeColor>
> = {
[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)
);
};
@@ -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 (
<NavigationMenuItemStyleIcon
Icon={ObjectIcon}
color={iconColor ?? undefined}
/>
);
return <NavigationMenuItemStyleIcon Icon={ObjectIcon} color={iconColor} />;
};
@@ -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) && (
<NavigationMenuItemStyleIcon
Icon={HeaderIcon}
color={iconColor ?? undefined}
/>
<NavigationMenuItemStyleIcon Icon={HeaderIcon} color={iconColor} />
)}
{objectLabel}
<span>{' / '}</span>