diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 9809608d27..6058b05756 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1063,6 +1063,7 @@ export type CreateLogicFunctionFromSourceInput = { }; export type CreateNavigationMenuItemInput = { + color?: InputMaybe; folderId?: InputMaybe; icon?: InputMaybe; link?: InputMaybe; @@ -3301,6 +3302,7 @@ export type NativeModelCapabilities = { export type NavigationMenuItem = { __typename?: 'NavigationMenuItem'; applicationId?: Maybe; + color?: Maybe; createdAt: Scalars['DateTime']; folderId?: Maybe; icon?: Maybe; @@ -4644,6 +4646,7 @@ export type UpdateLogicFunctionFromSourceInputUpdates = { }; export type UpdateNavigationMenuItemInput = { + color?: InputMaybe; folderId?: InputMaybe; icon?: InputMaybe; link?: InputMaybe; @@ -5972,42 +5975,42 @@ export type FindManyMarketplaceAppsQueryVariables = Exact<{ [key: string]: never export type FindManyMarketplaceAppsQuery = { __typename?: 'Query', findManyMarketplaceApps: Array<{ __typename?: 'MarketplaceApp', id: string, name: string, description: string, icon: string, version: string, author: string, category: string, logo?: string | null, screenshots: Array, aboutDescription: string, providers: Array, websiteUrl?: string | null, termsUrl?: string | null, objects: Array<{ __typename?: 'MarketplaceAppObject', universalIdentifier: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, fields: Array<{ __typename?: 'MarketplaceAppField', universalIdentifier?: string | null, name: string, type: string, label: string, description?: string | null, icon?: string | null }> }>, fields: Array<{ __typename?: 'MarketplaceAppField', name: string, type: string, label: string, description?: string | null, icon?: string | null, objectUniversalIdentifier?: string | null }>, logicFunctions: Array<{ __typename?: 'MarketplaceAppLogicFunction', name: string, description?: string | null, timeoutSeconds?: number | null }>, frontComponents: Array<{ __typename?: 'MarketplaceAppFrontComponent', name: string, description?: string | null }>, defaultRole?: { __typename?: 'MarketplaceAppDefaultRole', id: string, label: string, description?: string | null, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canUpdateAllSettings: boolean, canAccessAllTools: boolean, permissionFlags: Array, objectPermissions: Array<{ __typename?: 'MarketplaceAppRoleObjectPermission', objectUniversalIdentifier: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }>, fieldPermissions: Array<{ __typename?: 'MarketplaceAppRoleFieldPermission', objectUniversalIdentifier: string, fieldUniversalIdentifier: string, canReadFieldValue?: boolean | null, canUpdateFieldValue?: boolean | null }> } | null }> }; -export type NavigationMenuItemFieldsFragment = { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string }; +export type NavigationMenuItemFieldsFragment = { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, color?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string }; -export type NavigationMenuItemQueryFieldsFragment = { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string, targetRecordIdentifier?: { __typename?: 'RecordIdentifier', id: string, labelIdentifier: string, imageIdentifier?: string | null } | null }; +export type NavigationMenuItemQueryFieldsFragment = { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, color?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string, targetRecordIdentifier?: { __typename?: 'RecordIdentifier', id: string, labelIdentifier: string, imageIdentifier?: string | null } | null }; export type CreateNavigationMenuItemMutationVariables = Exact<{ input: CreateNavigationMenuItemInput; }>; -export type CreateNavigationMenuItemMutation = { __typename?: 'Mutation', createNavigationMenuItem: { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string } }; +export type CreateNavigationMenuItemMutation = { __typename?: 'Mutation', createNavigationMenuItem: { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, color?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string } }; export type DeleteNavigationMenuItemMutationVariables = Exact<{ id: Scalars['UUID']; }>; -export type DeleteNavigationMenuItemMutation = { __typename?: 'Mutation', deleteNavigationMenuItem: { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string } }; +export type DeleteNavigationMenuItemMutation = { __typename?: 'Mutation', deleteNavigationMenuItem: { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, color?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string } }; export type UpdateNavigationMenuItemMutationVariables = Exact<{ input: UpdateOneNavigationMenuItemInput; }>; -export type UpdateNavigationMenuItemMutation = { __typename?: 'Mutation', updateNavigationMenuItem: { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string } }; +export type UpdateNavigationMenuItemMutation = { __typename?: 'Mutation', updateNavigationMenuItem: { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, color?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string } }; export type FindManyNavigationMenuItemsQueryVariables = Exact<{ [key: string]: never; }>; -export type FindManyNavigationMenuItemsQuery = { __typename?: 'Query', navigationMenuItems: Array<{ __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string, targetRecordIdentifier?: { __typename?: 'RecordIdentifier', id: string, labelIdentifier: string, imageIdentifier?: string | null } | null }> }; +export type FindManyNavigationMenuItemsQuery = { __typename?: 'Query', navigationMenuItems: Array<{ __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, color?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string, targetRecordIdentifier?: { __typename?: 'RecordIdentifier', id: string, labelIdentifier: string, imageIdentifier?: string | null } | null }> }; export type FindOneNavigationMenuItemQueryVariables = Exact<{ id: Scalars['UUID']; }>; -export type FindOneNavigationMenuItemQuery = { __typename?: 'Query', navigationMenuItem?: { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string, targetRecordIdentifier?: { __typename?: 'RecordIdentifier', id: string, labelIdentifier: string, imageIdentifier?: string | null } | null } | null }; +export type FindOneNavigationMenuItemQuery = { __typename?: 'Query', navigationMenuItem?: { __typename?: 'NavigationMenuItem', id: string, userWorkspaceId?: string | null, targetRecordId?: string | null, targetObjectMetadataId?: string | null, viewId?: string | null, folderId?: string | null, name?: string | null, link?: string | null, icon?: string | null, color?: string | null, position: number, applicationId?: string | null, createdAt: string, updatedAt: string, targetRecordIdentifier?: { __typename?: 'RecordIdentifier', id: string, labelIdentifier: string, imageIdentifier?: string | null } | null } | null }; export type ObjectMetadataFieldsFragment = { __typename?: 'Object', id: string, universalIdentifier: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, isUIReadOnly: boolean, createdAt: string, updatedAt: string, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, applicationId: string, shortcut?: string | null, isLabelSyncedWithName: boolean, isSearchable: boolean, duplicateCriteria?: Array> | null, indexMetadataList: Array<{ __typename?: 'Index', id: string, createdAt: string, updatedAt: string, name: string, indexWhereClause?: string | null, indexType: IndexType, isUnique: boolean, isCustom?: boolean | null, indexFieldMetadataList: Array<{ __typename?: 'IndexField', id: string, fieldMetadataId: string, createdAt: string, updatedAt: string, order: number }> }>, fieldsList: Array<{ __typename?: 'Field', id: string, universalIdentifier: string, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isUIReadOnly?: boolean | null, isNullable?: boolean | null, isUnique?: boolean | null, createdAt: string, updatedAt: string, defaultValue?: any | null, options?: any | null, settings?: any | null, isLabelSyncedWithName?: boolean | null, morphId?: string | null, applicationId: string, relation?: { __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } } | null, morphRelations?: Array<{ __typename?: 'Relation', type: RelationType, sourceObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, targetObjectMetadata: { __typename?: 'Object', id: string, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'Field', id: string, name: string }, targetFieldMetadata: { __typename?: 'Field', id: string, name: string } }> | null }> }; @@ -7568,6 +7571,7 @@ export const NavigationMenuItemFieldsFragmentDoc = gql` name link icon + color position applicationId createdAt diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuFolderInfo.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuFolderInfo.tsx index e98b02655c..7cb95046fd 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuFolderInfo.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuFolderInfo.tsx @@ -1,4 +1,3 @@ -import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { useIcons } from 'twenty-ui/display'; @@ -6,24 +5,22 @@ import { useIcons } from 'twenty-ui/display'; import { CommandMenuPageInfoLayout } from '@/command-menu/components/CommandMenuPageInfoLayout'; import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState'; import { commandMenuShouldFocusTitleInputComponentState } from '@/command-menu/states/commandMenuShouldFocusTitleInputComponentState'; -import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/components/NavigationMenuItemIconContainer'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; import { useUpdateFolderInDraft } from '@/navigation-menu-item/hooks/useUpdateFolderInDraft'; import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems'; import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; import { IconPicker } from '@/ui/input/components/IconPicker'; import { TitleInput } from '@/ui/input/components/TitleInput'; import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; const StyledClickableIconWrapper = styled.div` cursor: pointer; `; export const CommandMenuFolderInfo = () => { - const theme = useTheme(); const { t } = useLingui(); const { getIcon } = useIcons(); const commandMenuPageInfo = useAtomStateValue(commandMenuPageInfoState); @@ -83,15 +80,10 @@ export const CommandMenuFolderInfo = () => { } clickableComponent={ - - - + } /> @@ -112,6 +104,7 @@ export const CommandMenuFolderInfo = () => { onFocus={() => setCommandMenuShouldFocusTitleInput(false)} /> } + label={t`Folder`} /> ); }; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx index 7f985cb3f1..a62d2bc8eb 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItem.tsx @@ -67,7 +67,7 @@ export const CommandMenuItem = ({ }) : undefined } - focused={isSelectedItemId} + focused={!disabled && isSelectedItemId} RightComponent={RightComponent} hasSubMenu={hasSubMenu} isSubMenuOpened={isSubMenuOpened} diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuLinkInfo.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuLinkInfo.tsx index bc74cfa0f1..57a585dae9 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuLinkInfo.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuLinkInfo.tsx @@ -1,22 +1,19 @@ -import { useTheme } from '@emotion/react'; import { useLingui } from '@lingui/react/macro'; import { IconLink } from 'twenty-ui/display'; import { CommandMenuPageInfoLayout } from '@/command-menu/components/CommandMenuPageInfoLayout'; import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageInfoState'; import { commandMenuShouldFocusTitleInputComponentState } from '@/command-menu/states/commandMenuShouldFocusTitleInputComponentState'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; -import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/components/NavigationMenuItemIconContainer'; import { useUpdateLinkInDraft } from '@/navigation-menu-item/hooks/useUpdateLinkInDraft'; import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems'; import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; import { TitleInput } from '@/ui/input/components/TitleInput'; import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; export const CommandMenuLinkInfo = () => { - const theme = useTheme(); const { t } = useLingui(); const commandMenuPageInfo = useAtomStateValue(commandMenuPageInfoState); const [ @@ -64,15 +61,10 @@ export const CommandMenuLinkInfo = () => { return ( - - + } title={ { onFocus={() => setCommandMenuShouldFocusTitleInput(false)} /> } - label={t`link`} + label={t`Link`} /> ); }; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuObjectViewRecordInfo.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuObjectViewRecordInfo.tsx index 6457080187..2d3df58075 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuObjectViewRecordInfo.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuObjectViewRecordInfo.tsx @@ -6,11 +6,15 @@ import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/Naviga import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem'; import { useSelectedNavigationMenuItemEditItemLabel } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel'; +import { useSelectedNavigationMenuItemEditItemObjectMetadata } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemObjectMetadata'; +import { ViewKey } from '@/views/types/ViewKey'; export const CommandMenuObjectViewRecordInfo = () => { const { t } = useLingui(); const { selectedItem } = useSelectedNavigationMenuItemEditItem(); const { selectedItemLabel } = useSelectedNavigationMenuItemEditItemLabel(); + const { selectedItemObjectMetadata } = + useSelectedNavigationMenuItemEditItemObjectMetadata(); const processedItem = selectedItem && selectedItem.itemType !== NavigationMenuItemType.FOLDER @@ -30,11 +34,12 @@ export const CommandMenuObjectViewRecordInfo = () => { return null; } - const label = isViewOrRecord - ? processedItem.itemType === NavigationMenuItemType.RECORD - ? t`record` - : t`view` - : t`object`; + const label = + processedItem.itemType === NavigationMenuItemType.RECORD + ? selectedItemObjectMetadata?.labelSingular + : processedItem.viewKey === ViewKey.Index + ? t`Object` + : t`View`; return ( { + const theme = useTheme(); const selectedNavigationMenuItemInEditMode = useAtomStateValue( selectedNavigationMenuItemInEditModeState, ); @@ -121,6 +127,20 @@ export const CommandMenuPageInfo = ({ pageChip }: CommandMenuPageInfoProps) => { return ; } + if (pageChip.page?.page === CommandMenuPages.NavigationMenuAddItem) { + return ( + + } + title={} + /> + ); + } + return ( diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuSubViewWithSearch.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuSubViewWithSearch.tsx index c157900b57..da0266409a 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuSubViewWithSearch.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuSubViewWithSearch.tsx @@ -12,9 +12,12 @@ const StyledSubViewContainer = styled.div` `; const StyledSearchContainer = styled.div` + align-items: center; border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; + display: flex; + height: ${({ theme }) => theme.spacing(10)}; min-width: 0; - padding: ${({ theme }) => theme.spacing(2, 3)}; + padding-inline: ${({ theme }) => theme.spacing(3)}; `; const StyledSearchInput = styled.input` @@ -22,8 +25,7 @@ const StyledSearchInput = styled.input` border: none; border-radius: ${({ theme }) => theme.border.radius.sm}; box-sizing: border-box; - color: ${({ theme }) => theme.font.color.primary}; - font-size: ${({ theme }) => theme.font.size.md}; + padding: 0; width: 100%; outline: none; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts index ddcbfdfee1..ba81728d1a 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts @@ -4,14 +4,17 @@ import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchS import { isCommandMenuClosingState } from '@/command-menu/states/isCommandMenuClosingState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { addToNavPayloadRegistryState } from '@/navigation-menu-item/states/addToNavPayloadRegistryState'; +import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/isNavigationMenuInEditModeState'; +import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState'; import { useCloseAnyOpenDropdown } from '@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown'; import { emitSidePanelOpenEvent } from '@/ui/layout/right-drawer/utils/emitSidePanelOpenEvent'; import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById'; import { t } from '@lingui/core/macro'; +import { useStore } from 'jotai'; import { useCallback } from 'react'; import { CommandMenuPages } from 'twenty-shared/types'; -import { IconDotsVertical } from 'twenty-ui/display'; -import { useStore } from 'jotai'; +import { isDefined } from 'twenty-shared/utils'; +import { IconColumnInsertRight, IconDotsVertical } from 'twenty-ui/display'; export const useCommandMenu = () => { const store = useStore(); @@ -38,13 +41,35 @@ export const useCommandMenu = () => { const openCommandMenu = useCallback(() => { emitSidePanelOpenEvent(); closeAnyOpenDropdown(); - navigateCommandMenu({ - page: CommandMenuPages.Root, - pageTitle: t`Command Menu`, - pageIcon: IconDotsVertical, - resetNavigationStack: true, - }); - }, [closeAnyOpenDropdown, navigateCommandMenu]); + const isNavigationMenuInEditMode = store.get( + isNavigationMenuInEditModeState.atom, + ); + const selectedNavigationItemId = store.get( + selectedNavigationMenuItemInEditModeState.atom, + ); + if (isNavigationMenuInEditMode && isDefined(selectedNavigationItemId)) { + navigateCommandMenu({ + page: CommandMenuPages.NavigationMenuItemEdit, + pageTitle: t`Edit`, + pageIcon: IconDotsVertical, + resetNavigationStack: true, + }); + } else if (isNavigationMenuInEditMode) { + navigateCommandMenu({ + page: CommandMenuPages.NavigationMenuAddItem, + pageTitle: t`New sidebar item`, + pageIcon: IconColumnInsertRight, + resetNavigationStack: true, + }); + } else { + navigateCommandMenu({ + page: CommandMenuPages.Root, + pageTitle: t`Command Menu`, + pageIcon: IconDotsVertical, + resetNavigationStack: true, + }); + } + }, [closeAnyOpenDropdown, navigateCommandMenu, store]); const toggleCommandMenu = useCallback(() => { const isCommandMenuOpened = store.get(isCommandMenuOpenedState.atom); diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditColorOption.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditColorOption.tsx new file mode 100644 index 0000000000..4ee7ae8ef3 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditColorOption.tsx @@ -0,0 +1,93 @@ +import { CommandMenuItemDropdown } from '@/command-menu/components/CommandMenuItemDropdown'; +import { useUpdateNavigationMenuItemInDraft } from '@/navigation-menu-item/hooks/useUpdateNavigationMenuItemInDraft'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import styled from '@emotion/styled'; +import { useLingui } from '@lingui/react/macro'; +import { isNonEmptyString } from '@sniptt/guards'; +import { useState } from 'react'; +import { capitalize } from 'twenty-shared/utils'; +import { IconColorSwatch } from 'twenty-ui/display'; +import { + DEFAULT_COLOR_LABELS, + MenuItemSelectColor, +} from 'twenty-ui/navigation'; +import { MAIN_COLOR_NAMES, type ThemeColor } from 'twenty-ui/theme'; + +const NAVIGATION_MENU_ITEM_COLOR_DROPDOWN_ID = 'navigation-menu-item-color'; + +const StyledMenuStyleText = styled.span` + color: ${({ theme }) => theme.font.color.light}; + font-size: 13px; +`; + +type CommandMenuEditColorOptionProps = { + navigationMenuItemId: string; + color: ThemeColor; +}; + +export const CommandMenuEditColorOption = ({ + navigationMenuItemId, + color, +}: CommandMenuEditColorOptionProps) => { + const { t } = useLingui(); + const { updateNavigationMenuItemInDraft } = + useUpdateNavigationMenuItemInDraft(); + const { closeDropdown } = useCloseDropdown(); + + const [searchValue, setSearchValue] = useState(''); + const themeColor = color ?? 'gray'; + const colorLabel = DEFAULT_COLOR_LABELS[themeColor] ?? capitalize(themeColor); + + const query = searchValue.trim().toLowerCase(); + + const filteredColorNames = isNonEmptyString(query) + ? MAIN_COLOR_NAMES.filter( + (colorName) => + colorName.toLowerCase().includes(query) || + (DEFAULT_COLOR_LABELS[colorName] ?? '').toLowerCase().includes(query), + ) + : MAIN_COLOR_NAMES; + + const handleSelectColor = (selectedColor: ThemeColor) => { + updateNavigationMenuItemInDraft(navigationMenuItemId, { + color: selectedColor, + }); + closeDropdown(NAVIGATION_MENU_ITEM_COLOR_DROPDOWN_ID); + }; + + return ( + {colorLabel}} + dropdownComponents={ + + setSearchValue(event.target.value)} + /> + + + {filteredColorNames.map((colorName) => ( + handleSelectColor(colorName)} + color={colorName} + selected={colorName === themeColor} + colorLabels={DEFAULT_COLOR_LABELS} + /> + ))} + + + } + /> + ); +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditFolderPickerSubView.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditFolderPickerSubView.tsx index 56578e72bf..a52113c43e 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditFolderPickerSubView.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditFolderPickerSubView.tsx @@ -1,12 +1,15 @@ -import { useState } from 'react'; import { useLingui } from '@lingui/react/macro'; -import { IconFolderPlus } from 'twenty-ui/display'; +import { useState } from 'react'; +import { useIcons } from 'twenty-ui/display'; import { CommandGroup } from '@/command-menu/components/CommandGroup'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; import { CommandMenuList } from '@/command-menu/components/CommandMenuList'; import { CommandMenuSubViewWithSearch } from '@/command-menu/components/CommandMenuSubViewWithSearch'; import { useFolderPickerSelectionData } from '@/command-menu/pages/navigation-menu-item/hooks/useFolderPickerSelectionData'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; +import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault'; +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { filterBySearchQuery } from '~/utils/filterBySearchQuery'; @@ -18,6 +21,7 @@ export const CommandMenuEditFolderPickerSubView = ({ onBack, }: CommandMenuEditFolderPickerSubViewProps) => { const { t } = useLingui(); + const { getIcon } = useIcons(); const [searchValue, setSearchValue] = useState(''); const { foldersToShow, includeNoFolderOption, handleSelectFolder } = useFolderPickerSelectionData({ onCloseSubView: onBack }); @@ -64,20 +68,30 @@ export const CommandMenuEditFolderPickerSubView = ({ /> )} - {filteredFolders.map((folder) => ( - handleSelectFolder(folder.id)} - > - handleSelectFolder(folder.id)} - /> - - ))} + {filteredFolders.map((folder) => { + const FolderIcon = getIcon(folder.icon ?? FOLDER_ICON_DEFAULT); + const folderColor = + folder.color ?? DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER; + return ( + handleSelectFolder(folder.id)} + > + + } + label={folder.name} + id={folder.id} + onClick={() => handleSelectFolder(folder.id)} + /> + + ); + })} diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditLinkItemView.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditLinkItemView.tsx index 75f2d56910..8508bae905 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditLinkItemView.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuEditLinkItemView.tsx @@ -5,13 +5,15 @@ import { getAbsoluteUrl } from 'twenty-shared/utils'; import { CommandGroup } from '@/command-menu/components/CommandGroup'; import { CommandMenuList } from '@/command-menu/components/CommandMenuList'; +import { CommandMenuEditColorOption } from '@/command-menu/pages/navigation-menu-item/components/CommandMenuEditColorOption'; import { type OrganizeActionsProps, CommandMenuEditOrganizeActions, } from '@/command-menu/pages/navigation-menu-item/components/CommandMenuEditOrganizeActions'; -import { getOrganizeActionsSelectableItemIds } from '@/command-menu/pages/navigation-menu-item/utils/getOrganizeActionsSelectableItemIds'; import { CommandMenuEditOwnerSection } from '@/command-menu/pages/navigation-menu-item/components/CommandMenuEditOwnerSection'; +import { getOrganizeActionsSelectableItemIds } from '@/command-menu/pages/navigation-menu-item/utils/getOrganizeActionsSelectableItemIds'; import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/types/processed-navigation-menu-item'; +import { parseThemeColor } from '@/navigation-menu-item/utils/parseThemeColor'; import { TextInput } from '@/ui/input/components/TextInput'; type CommandMenuEditLinkItemViewProps = OrganizeActionsProps & { @@ -40,6 +42,10 @@ export const CommandMenuEditLinkItemView = ({ return ( + void; + showColorOption?: boolean; }; export const CommandMenuEditObjectViewBase = ({ @@ -18,11 +24,22 @@ export const CommandMenuEditObjectViewBase = ({ onRemove, onAddBefore, onAddAfter, + showColorOption = false, }: CommandMenuEditObjectViewBaseProps) => { + const { t } = useLingui(); + const { selectedItem } = useSelectedNavigationMenuItemEditItem(); const selectableItemIds = getOrganizeActionsSelectableItemIds(true); return ( + {showColorOption && selectedItem && ( + + + + )} theme.font.color.tertiary}; @@ -40,7 +51,7 @@ export const CommandMenuNavigationMenuItemEditPage = () => { const selectedItemType = selectedItem?.itemType ?? null; const [isFolderPickerOpen, setIsFolderPickerOpen] = useState(false); - const setFolderPicker = () => setIsFolderPickerOpen(true); + const openFolderPicker = () => setIsFolderPickerOpen(true); const { canMoveUp, @@ -53,6 +64,25 @@ export const CommandMenuNavigationMenuItemEditPage = () => { } = useNavigationMenuItemEditOrganizeActions(); const { updateLinkInDraft } = useUpdateLinkInDraft(); + const { openAddItemToFolderPage } = useOpenAddItemToFolderPage(); + const { workspaceNavigationMenuItems } = useNavigationMenuItemsDraftState(); + + const handleAddItemToFolder = () => { + if ( + !selectedItem || + selectedItem.itemType !== NavigationMenuItemType.FOLDER + ) { + return; + } + const folderItemCount = workspaceNavigationMenuItems.filter( + (item) => (item.folderId ?? null) === selectedItem.id, + ).length; + openAddItemToFolderPage({ + targetFolderId: selectedItem.id, + targetIndex: folderItemCount, + resetNavigationStack: false, + }); + }; if (!selectedNavigationMenuItemInEditMode || !selectedItemLabel) { return ( @@ -77,7 +107,7 @@ export const CommandMenuNavigationMenuItemEditPage = () => { if (!selectedItemObjectMetadata) return null; return ( { onRemove={onRemove} onAddBefore={onAddBefore} onAddAfter={onAddAfter} + showColorOption={ + selectedItem && + 'viewKey' in selectedItem && + selectedItem.viewKey === ViewKey.Index + } /> ); case NavigationMenuItemType.LINK: @@ -97,7 +132,7 @@ export const CommandMenuNavigationMenuItemEditPage = () => { key={selectedItem.id} selectedItem={selectedItem} onUpdateLink={(linkId, link) => updateLinkInDraft(linkId, { link })} - onOpenFolderPicker={setFolderPicker} + onOpenFolderPicker={openFolderPicker} canMoveUp={canMoveUp} canMoveDown={canMoveDown} onMoveUp={onMoveUp} @@ -113,8 +148,31 @@ export const CommandMenuNavigationMenuItemEditPage = () => { return ( + {selectedItem && ( + + + + + + + )} { commandGroups={[]} selectableItemIds={getOrganizeActionsSelectableItemIds(true)} > + {selectedItem && ( + + + + )} { onAddBefore={onAddBefore} onAddAfter={onAddAfter} showMoveToFolder - onMoveToFolder={setFolderPicker} + onMoveToFolder={openFolderPicker} /> ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemMainMenu.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemMainMenu.tsx index 3b1c8704d3..c95d9b2106 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemMainMenu.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemMainMenu.tsx @@ -1,10 +1,11 @@ +import { useTheme } from '@emotion/react'; import { useLingui } from '@lingui/react/macro'; import { - IconAddressBook, - IconCube, + Avatar, + IconBuildingSkyscraper, IconFolder, IconLink, - IconList, + IconTable, } from 'twenty-ui/display'; import { CommandGroup } from '@/command-menu/components/CommandGroup'; @@ -15,8 +16,9 @@ import { CommandMenuItemWithAddToNavigationDrag } from '@/command-menu/component import { CommandMenuList } from '@/command-menu/components/CommandMenuList'; import { useAddFolderToNavigationMenu } from '@/command-menu/pages/navigation-menu-item/hooks/useAddFolderToNavigationMenu'; import { useAddLinkToNavigationMenu } from '@/command-menu/pages/navigation-menu-item/hooks/useAddLinkToNavigationMenu'; -import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; type CommandMenuNewSidebarItemMainMenuProps = { onSelectObject: () => void; @@ -30,6 +32,7 @@ export const CommandMenuNewSidebarItemMainMenu = ({ onSelectRecord, }: CommandMenuNewSidebarItemMainMenuProps) => { const { t } = useLingui(); + const theme = useTheme(); const { handleAddFolder } = useAddFolderToNavigationMenu(); const { handleAddLink } = useAddLinkToNavigationMenu(); @@ -46,7 +49,12 @@ export const CommandMenuNewSidebarItemMainMenu = ({ ( + + )} label={t`Object`} id="object" hasSubMenu={true} @@ -57,7 +65,12 @@ export const CommandMenuNewSidebarItemMainMenu = ({ ( + + )} label={t`View`} id="view" hasSubMenu={true} @@ -68,7 +81,13 @@ export const CommandMenuNewSidebarItemMainMenu = ({ ( + + )} label={t`Record`} id="record" hasSubMenu={true} diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemObjectFlow.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemObjectFlow.tsx index 7d02f51510..6a63f23191 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemObjectFlow.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemObjectFlow.tsx @@ -6,6 +6,7 @@ import { CommandMenuObjectPickerSubView } from '@/command-menu/pages/navigation- import { CommandMenuSystemObjectPickerSubView } from '@/command-menu/pages/navigation-menu-item/components/CommandMenuSystemObjectPickerSubView'; import { getAvailableObjectMetadataForNewSidebarItem } from '@/command-menu/pages/navigation-menu-item/utils/getAvailableObjectMetadataForNewSidebarItem'; import { useAddObjectToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddObjectToNavigationMenuDraft'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems'; import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft'; import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState'; @@ -40,8 +41,11 @@ export const CommandMenuNewSidebarItemObjectFlow = ({ const setAddMenuItemInsertionContext = useSetAtomState( addMenuItemInsertionContextState, ); - const { views, objectMetadataIdsWithIndexView } = - useNavigationMenuObjectMetadataFromDraft(currentDraft); + const { + views, + objectMetadataIdsWithIndexView, + objectMetadataIdsInWorkspace, + } = useNavigationMenuObjectMetadataFromDraft(currentDraft); const objectMetadataIdsWithDisplayableViews = new Set( views @@ -61,12 +65,16 @@ export const CommandMenuNewSidebarItemObjectFlow = ({ objectMetadataItem: ObjectMetadataItem, defaultViewId: string, ) => { + if (objectMetadataIdsInWorkspace.has(objectMetadataItem.id)) { + return; + } addObjectToDraft( objectMetadataItem.id, defaultViewId, currentDraft, addMenuItemInsertionContext?.targetFolderId, addMenuItemInsertionContext?.targetIndex, + getStandardObjectIconColor(objectMetadataItem.nameSingular), ); setAddMenuItemInsertionContext(null); closeCommandMenu(); diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewObjectPickerSubView.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewObjectPickerSubView.tsx index 7d08c7b796..719aceec35 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewObjectPickerSubView.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewObjectPickerSubView.tsx @@ -1,4 +1,3 @@ -import { useTheme } from '@emotion/react'; import { useLingui } from '@lingui/react/macro'; import { IconSettings, useIcons } from 'twenty-ui/display'; @@ -7,8 +6,8 @@ import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; import { CommandMenuList } from '@/command-menu/components/CommandMenuList'; import { CommandMenuSubViewWithSearch } from '@/command-menu/components/CommandMenuSubViewWithSearch'; import { useFilteredPickerItems } from '@/command-menu/hooks/useFilteredPickerItems'; -import { IconWithBackground } from '@/navigation-menu-item/components/IconWithBackground'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -32,9 +31,7 @@ export const CommandMenuNewSidebarItemViewObjectPickerSubView = ({ showSystemObjectsOption = true, }: CommandMenuNewSidebarItemViewObjectPickerSubViewProps) => { const { t } = useLingui(); - const theme = useTheme(); const { getIcon } = useIcons(); - const iconColors = getNavigationMenuItemIconColors(theme); const { filteredItems, selectableItemIds, isEmpty, hasSearchQuery } = useFilteredPickerItems({ items: objects, @@ -68,12 +65,12 @@ export const CommandMenuNewSidebarItemViewObjectPickerSubView = ({ onEnter={() => onSelectObject(objectMetadataItem)} > ( - ( + )} label={objectMetadataItem.labelPlural} @@ -86,7 +83,7 @@ export const CommandMenuNewSidebarItemViewObjectPickerSubView = ({ {showSystemObjectsOption && ( } label={t`System objects`} id="system" hasSubMenu={true} diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewPickerSubView.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewPickerSubView.tsx index 7848a6e196..9abb0bf0a2 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewPickerSubView.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewPickerSubView.tsx @@ -1,7 +1,3 @@ -import { useLingui } from '@lingui/react/macro'; -import { useState } from 'react'; -import { useIcons } from 'twenty-ui/display'; - import { CommandGroup } from '@/command-menu/components/CommandGroup'; import { CommandMenuAddToNavDroppable } from '@/command-menu/components/CommandMenuAddToNavDroppable'; import { CommandMenuItemWithAddToNavigationDrag } from '@/command-menu/components/CommandMenuItemWithAddToNavigationDrag'; @@ -9,18 +5,23 @@ import { CommandMenuList } from '@/command-menu/components/CommandMenuList'; import { CommandMenuSubViewWithSearch } from '@/command-menu/components/CommandMenuSubViewWithSearch'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { useFilteredPickerItems } from '@/command-menu/hooks/useFilteredPickerItems'; -import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems'; import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/components/ObjectIconWithViewOverlay'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; import { useAddViewToNavigationMenuDraft } from '@/navigation-menu-item/hooks/useAddViewToNavigationMenuDraft'; +import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems'; import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft'; import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; +import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; import { type View } from '@/views/types/View'; import { ViewKey } from '@/views/types/ViewKey'; +import { useLingui } from '@lingui/react/macro'; +import { useState } from 'react'; +import { isDefined } from 'twenty-shared/utils'; +import { useIcons } from 'twenty-ui/display'; type CommandMenuNewSidebarItemViewPickerSubViewProps = { selectedObjectMetadataIdForView: string; @@ -80,6 +81,9 @@ export const CommandMenuNewSidebarItemViewPickerSubView = ({ currentDraft, addMenuItemInsertionContext?.targetFolderId ?? null, addMenuItemInsertionContext?.targetIndex, + isDefined(selectedObjectMetadataItem) + ? getStandardObjectIconColor(selectedObjectMetadataItem.nameSingular) + : undefined, ); setAddMenuItemInsertionContext(null); closeCommandMenu(); @@ -118,6 +122,9 @@ export const CommandMenuNewSidebarItemViewPickerSubView = ({ selectedObjectMetadataItem.icon, )} ViewIcon={getIcon(view.icon)} + objectColor={getStandardObjectIconColor( + selectedObjectMetadataItem.nameSingular, + )} /> ) : undefined } diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewSystemSubView.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewSystemSubView.tsx index 22f12177a2..1c81adacfb 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewSystemSubView.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuNewSidebarItemViewSystemSubView.tsx @@ -1,4 +1,3 @@ -import { useTheme } from '@emotion/react'; import { useLingui } from '@lingui/react/macro'; import { useIcons } from 'twenty-ui/display'; @@ -6,9 +5,9 @@ import { CommandGroup } from '@/command-menu/components/CommandGroup'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; import { CommandMenuList } from '@/command-menu/components/CommandMenuList'; import { CommandMenuSubViewWithSearch } from '@/command-menu/components/CommandMenuSubViewWithSearch'; -import { IconWithBackground } from '@/navigation-menu-item/components/IconWithBackground'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; import { useFilteredPickerItems } from '@/command-menu/hooks/useFilteredPickerItems'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -28,9 +27,7 @@ export const CommandMenuNewSidebarItemViewSystemSubView = ({ onSelectObject, }: CommandMenuNewSidebarItemViewSystemSubViewProps) => { const { t } = useLingui(); - const theme = useTheme(); const { getIcon } = useIcons(); - const iconColors = getNavigationMenuItemIconColors(theme); const { filteredItems: filteredSystemObjectMetadataItems, selectableItemIds, @@ -67,12 +64,12 @@ export const CommandMenuNewSidebarItemViewSystemSubView = ({ onEnter={() => onSelectObject(objectMetadataItem)} > ( - ( + )} label={objectMetadataItem.labelPlural} diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectMenuItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectMenuItem.tsx index 7a896ee039..ea7465f5ad 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectMenuItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectMenuItem.tsx @@ -1,12 +1,14 @@ -import { useTheme } from '@emotion/react'; +import { useLingui } from '@lingui/react/macro'; import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; import { CommandMenuItemWithAddToNavigationDrag } from '@/command-menu/components/CommandMenuItemWithAddToNavigationDrag'; -import { IconWithBackground } from '@/navigation-menu-item/components/IconWithBackground'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; +import { useDraftNavigationMenuItems } from '@/navigation-menu-item/hooks/useDraftNavigationMenuItems'; +import { useNavigationMenuObjectMetadataFromDraft } from '@/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue'; @@ -28,28 +30,38 @@ export const CommandMenuObjectMenuItem = ({ variant, dragIndex, }: CommandMenuObjectMenuItemProps) => { - const theme = useTheme(); + const { t } = useLingui(); const { getIcon } = useIcons(); - const iconColors = getNavigationMenuItemIconColors(theme); + const { currentDraft } = useDraftNavigationMenuItems(); + const { objectMetadataIdsInWorkspace } = + useNavigationMenuObjectMetadataFromDraft(currentDraft); + const isAlreadyInNavbar = objectMetadataIdsInWorkspace.has( + objectMetadataItem.id, + ); const defaultViewId = useAtomFamilySelectorValue( coreIndexViewIdFromObjectMetadataItemFamilySelector, { objectMetadataItemId: objectMetadataItem.id }, ); const Icon = getIcon(objectMetadataItem.icon); - const isDisabled = !isDefined(defaultViewId); + const iconColor = getStandardObjectIconColor(objectMetadataItem.nameSingular); + const isDisabled = isAlreadyInNavbar || !isDefined(defaultViewId); const handleClick = () => { - if (!defaultViewId) { + if (isDisabled || !defaultViewId) { return; } onSelect(objectMetadataItem, defaultViewId); }; + const styledIcon = () => ( + + ); + return ( {variant === 'add' && !isDisabled ? ( ) : ( ( - - )} + Icon={styledIcon} label={objectMetadataItem.labelPlural} id={objectMetadataItem.id} onClick={handleClick} disabled={isDisabled} + description={isAlreadyInNavbar ? t`Already in navbar` : undefined} /> )} diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectPickerItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectPickerItem.tsx index a6b767a459..27ab2d1b18 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectPickerItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectPickerItem.tsx @@ -1,11 +1,10 @@ -import { useTheme } from '@emotion/react'; import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem'; import { CommandMenuObjectMenuItem } from '@/command-menu/pages/navigation-menu-item/components/CommandMenuObjectMenuItem'; -import { IconWithBackground } from '@/navigation-menu-item/components/IconWithBackground'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -29,9 +28,7 @@ export const CommandMenuObjectPickerItem = ({ objectMenuItemVariant = 'edit', dragIndex, }: CommandMenuObjectPickerItemProps) => { - const theme = useTheme(); const { getIcon } = useIcons(); - const iconColors = getNavigationMenuItemIconColors(theme); if (isViewItem && isDefined(onSelectObjectForViewEdit)) { return ( @@ -40,12 +37,12 @@ export const CommandMenuObjectPickerItem = ({ onEnter={() => onSelectObjectForViewEdit(objectMetadataItem)} > ( - ( + )} label={objectMetadataItem.labelPlural} diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectPickerSubView.tsx b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectPickerSubView.tsx index 94a985a495..0940ad8a52 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectPickerSubView.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/components/CommandMenuObjectPickerSubView.tsx @@ -9,6 +9,7 @@ import { CommandMenuList } from '@/command-menu/components/CommandMenuList'; import { CommandMenuSubViewWithSearch } from '@/command-menu/components/CommandMenuSubViewWithSearch'; import { useFilteredPickerItems } from '@/command-menu/hooks/useFilteredPickerItems'; import { CommandMenuObjectPickerItem } from '@/command-menu/pages/navigation-menu-item/components/CommandMenuObjectPickerItem'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -72,10 +73,10 @@ export const CommandMenuObjectPickerSubView = ({ } label={t`System objects`} id="system" - hasSubMenu={true} + hasSubMenu onClick={onOpenSystemPicker} /> @@ -83,10 +84,10 @@ export const CommandMenuObjectPickerSubView = ({ ) : ( } label={t`System objects`} id="system" - hasSubMenu={true} + hasSubMenu onClick={onOpenSystemPicker} /> diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useFolderPickerSelectionData.ts b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useFolderPickerSelectionData.ts index 2f4cdb0c91..a16f47de97 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useFolderPickerSelectionData.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useFolderPickerSelectionData.ts @@ -12,7 +12,7 @@ import { NavigationMenuItemType } from '@/navigation-menu-item/constants/Navigat type FolderOption = { id: string; name: string; - folderId?: string; + folderId?: string | null; }; const getDescendantFolderIds = ( diff --git a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions.ts b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions.ts index 9f7ff88868..082c9c79fa 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/navigation-menu-item/hooks/useNavigationMenuItemEditOrganizeActions.ts @@ -1,6 +1,6 @@ import { useLingui } from '@lingui/react/macro'; import { isDefined } from 'twenty-shared/utils'; -import { IconPlus } from 'twenty-ui/display'; +import { IconColumnInsertRight } from 'twenty-ui/display'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; @@ -119,7 +119,7 @@ export const useNavigationMenuItemEditOrganizeActions = navigateCommandMenu({ page: CommandMenuPages.NavigationMenuAddItem, pageTitle: t`New sidebar item`, - pageIcon: IconPlus, + pageIcon: IconColumnInsertRight, resetNavigationStack: true, }); }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/AddToNavigationDragHandle.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/AddToNavigationDragHandle.tsx index 38eef0d73f..f7790807e6 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/AddToNavigationDragHandle.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/AddToNavigationDragHandle.tsx @@ -1,12 +1,15 @@ import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { isNonEmptyString } from '@sniptt/guards'; import type { ReactNode } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { IconGripVertical, type IconComponent } from 'twenty-ui/display'; -import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/components/NavigationMenuItemIconContainer'; +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder'; +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorLink'; +import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; import type { AddToNavigationDragPayload } from '@/navigation-menu-item/types/add-to-navigation-drag-payload'; -import { getIconBackgroundColorForPayload } from '@/navigation-menu-item/utils/getIconBackgroundColorForPayload'; const StyledIconSlot = styled.div<{ $hasFixedSize: boolean }>` align-items: center; @@ -18,8 +21,8 @@ const StyledIconSlot = styled.div<{ $hasFixedSize: boolean }>` ${({ theme, $hasFixedSize }) => $hasFixedSize && css` - height: ${theme.spacing(4.5)}; - width: ${theme.spacing(4.5)}; + height: ${theme.spacing(4)}; + width: ${theme.spacing(4)}; `} &:active { @@ -30,11 +33,13 @@ const StyledIconSlot = styled.div<{ $hasFixedSize: boolean }>` type AddToNavigationDragHandleIconProps = { icon?: IconComponent; customIconContent?: ReactNode; + iconColor?: string; }; const AddToNavigationDragHandleIcon = ({ icon, customIconContent, + iconColor, }: AddToNavigationDragHandleIconProps) => { const theme = useTheme(); const iconSize = theme.icon.size.md; @@ -47,7 +52,11 @@ const AddToNavigationDragHandleIcon = ({ if (isDefined(icon)) { const Icon = icon; return ( - + ); } }; @@ -66,37 +75,34 @@ export const AddToNavigationDragHandle = ({ isHovered, }: AddToNavigationDragHandleProps) => { const theme = useTheme(); - const iconBackgroundColor = getIconBackgroundColorForPayload(payload, theme); - const hasBackgroundColor = !!iconBackgroundColor && !isHovered; - const payloadHasBackgroundColor = !!iconBackgroundColor; - const iconSize = theme.icon.size.md; - const iconStroke = theme.icon.stroke.sm; - + const effectiveColor = + payload.type === 'object' && isNonEmptyString(payload.iconColor) + ? payload.iconColor + : payload.type === 'folder' + ? DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER + : payload.type === 'link' + ? DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK + : undefined; + const hasBackgroundColor = + payload.type !== NavigationMenuItemType.RECORD && + isDefined(effectiveColor) && + !isHovered; const showCustomContentWithoutWrapper = isDefined(customIconContent); return ( {isHovered ? ( ) : showCustomContentWithoutWrapper ? ( customIconContent - ) : hasBackgroundColor ? ( - - - + ) : hasBackgroundColor && icon ? ( + ) : ( { const { t } = useLingui(); - const theme = useTheme(); - const iconColors = getNavigationMenuItemIconColors(theme); const objectMetadataItems = useAtomStateValue(objectMetadataItemsState); const location = useLocation(); const navigate = useNavigate(); @@ -235,7 +233,7 @@ export const CurrentWorkspaceMemberNavigationMenuItems = ({ } /> diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberOrphanNavigationMenuItems.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberOrphanNavigationMenuItems.tsx index 400f7d6ff2..f395bf5bab 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberOrphanNavigationMenuItems.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/CurrentWorkspaceMemberOrphanNavigationMenuItems.tsx @@ -5,13 +5,14 @@ import { IconHeartOff } from 'twenty-ui/display'; import { LightIconButton } from 'twenty-ui/input'; import { NavigationItemDropTarget } from '@/navigation-menu-item/components/NavigationItemDropTarget'; -import { NavigationSections } from '@/navigation-menu-item/constants/NavigationSections.constants'; import { NavigationMenuItemDroppable } from '@/navigation-menu-item/components/NavigationMenuItemDroppable'; import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/NavigationMenuItemIcon'; import { NavigationMenuItemDroppableIds } from '@/navigation-menu-item/constants/NavigationMenuItemDroppableIds'; +import { NavigationSections } from '@/navigation-menu-item/constants/NavigationSections.constants'; import { NavigationMenuItemDragContext } from '@/navigation-menu-item/contexts/NavigationMenuItemDragContext'; import { useDeleteNavigationMenuItem } from '@/navigation-menu-item/hooks/useDeleteNavigationMenuItem'; import { useSortedNavigationMenuItems } from '@/navigation-menu-item/hooks/useSortedNavigationMenuItems'; +import { getEffectiveNavigationMenuItemColor } from '@/navigation-menu-item/utils/getEffectiveNavigationMenuItemColor'; import { getNavigationMenuItemSecondaryLabel } from '@/navigation-menu-item/utils/getNavigationMenuItemSecondaryLabel'; import { isLocationMatchingNavigationMenuItem } from '@/navigation-menu-item/utils/isLocationMatchingNavigationMenuItem'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; @@ -71,6 +72,9 @@ export const CurrentWorkspaceMemberOrphanNavigationMenuItems = () => { navigationMenuItem={navigationMenuItem} /> )} + iconColor={getEffectiveNavigationMenuItemColor( + navigationMenuItem, + )} active={isLocationMatchingNavigationMenuItem( currentPath, currentViewPath, diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/IconWithBackground.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/IconWithBackground.tsx deleted file mode 100644 index 112c4d829a..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/IconWithBackground.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { useTheme } from '@emotion/react'; -import type { IconComponent } from 'twenty-ui/display'; - -import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/components/NavigationMenuItemIconContainer'; - -type IconWithBackgroundProps = { - Icon: IconComponent; - backgroundColor: string; - size?: number | string; - stroke?: number | string; -}; - -export const IconWithBackground = ({ - Icon, - backgroundColor, - size, - stroke, -}: IconWithBackgroundProps) => { - const theme = useTheme(); - return ( - - - - ); -}; 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 741bc86c85..b43c31960e 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 @@ -1,4 +1,5 @@ import { useTheme } from '@emotion/react'; +import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; import { Avatar, useIcons } from 'twenty-ui/display'; import { FeatureFlagKey } from '~/generated-metadata/graphql'; @@ -6,13 +7,15 @@ import { FeatureFlagKey } from '~/generated-metadata/graphql'; import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/components/NavigationMenuItemIconContainer'; import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/components/ObjectIconWithViewOverlay'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; +import { useObjectNavItemColor } from '@/navigation-menu-item/hooks/useObjectNavItemColor'; +import { getNavigationMenuItemIconStyleFromColor } from '@/navigation-menu-item/utils/get-navigation-menu-item-icon-style-from-color'; +import { getEffectiveNavigationMenuItemColor } from '@/navigation-menu-item/utils/getEffectiveNavigationMenuItemColor'; import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems'; import { useGetStandardObjectIcon } from '@/object-metadata/hooks/useGetStandardObjectIcon'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { ViewKey } from '@/views/types/ViewKey'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; export const NavigationMenuItemIcon = ({ navigationMenuItem, @@ -26,82 +29,80 @@ export const NavigationMenuItemIcon = ({ ); const objectMetadataItems = useAtomStateValue(objectMetadataItemsState); const { Icon: StandardIcon, IconColor } = useGetStandardObjectIcon( - navigationMenuItem.objectNameSingular || '', + navigationMenuItem.objectNameSingular ?? '', ); const isRecord = navigationMenuItem.itemType === NavigationMenuItemType.RECORD; - const isLink = navigationMenuItem.itemType === NavigationMenuItemType.LINK; - const isObjectIndexView = - navigationMenuItem.itemType === NavigationMenuItemType.VIEW && - navigationMenuItem.viewKey === ViewKey.Index; const isViewWithOverlay = navigationMenuItem.itemType === NavigationMenuItemType.VIEW && - !isObjectIndexView; + navigationMenuItem.viewKey !== ViewKey.Index; const objectMetadataItem = objectMetadataItems.find( (item) => item.nameSingular === navigationMenuItem.objectNameSingular, ); - const ObjectIconForView = + const objectNavItemColor = useObjectNavItemColor( + navigationMenuItem.objectNameSingular ?? '', + ); + const objectIconForView = objectMetadataItem?.icon != null ? getIcon(objectMetadataItem.icon) : StandardIcon; - if ( + const canShowViewOverlay = isViewWithOverlay && - isDefined(ObjectIconForView) && - isDefined(navigationMenuItem.Icon) - ) { - const ViewIcon = getIcon(navigationMenuItem.Icon); + isDefined(objectIconForView) && + isDefined(navigationMenuItem.Icon); + + if (canShowViewOverlay) { return ( ); } - const IconToUse = - StandardIcon || + const iconToUse = + StandardIcon ?? (navigationMenuItem.Icon ? getIcon(navigationMenuItem.Icon) : undefined); + const effectiveColor = + getEffectiveNavigationMenuItemColor(navigationMenuItem); + const useStyledIcon = + isNavigationMenuItemEditingEnabled && + !isRecord && + isNonEmptyString(effectiveColor); + const iconStyle = useStyledIcon + ? getNavigationMenuItemIconStyleFromColor(theme, effectiveColor) + : null; - const placeholderColorSeed = navigationMenuItem.targetRecordId ?? undefined; - const iconColors = getNavigationMenuItemIconColors(theme); - const iconBackgroundColor = isNavigationMenuItemEditingEnabled - ? isRecord - ? undefined - : isLink - ? iconColors.link - : isViewWithOverlay - ? iconColors.view - : iconColors.object - : undefined; - - const iconColorToUse = iconBackgroundColor - ? theme.grayScale.gray1 + const iconColorToUse = iconStyle + ? iconStyle.iconColor : StandardIcon ? IconColor : theme.font.color.secondary; const avatar = ( ); - if (!iconBackgroundColor) { + if (!iconStyle) { return avatar; } return ( {avatar} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemIconContainer.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemIconContainer.tsx index af565eacc9..a73e8ac832 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemIconContainer.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemIconContainer.tsx @@ -2,15 +2,19 @@ import styled from '@emotion/styled'; export const StyledNavigationMenuItemIconContainer = styled.div<{ $backgroundColor?: string; + $borderColor?: string; }>` align-items: center; - border-radius: ${({ theme }) => theme.border.radius.xs}; + border-radius: 4px; + box-sizing: border-box; display: flex; flex-shrink: 0; - height: ${({ theme }) => theme.spacing(4.5)}; + height: ${({ theme }) => theme.spacing(4)}; justify-content: center; - width: ${({ theme }) => theme.spacing(4.5)}; + width: ${({ theme }) => theme.spacing(4)}; ${({ $backgroundColor }) => $backgroundColor ? `background-color: ${$backgroundColor};` : ''} + ${({ $borderColor }) => + $borderColor ? `border: 1px solid ${$borderColor};` : ''} `; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemStyleIcon.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemStyleIcon.tsx new file mode 100644 index 0000000000..978b715478 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/NavigationMenuItemStyleIcon.tsx @@ -0,0 +1,30 @@ +import { useTheme } from '@emotion/react'; +import type { IconComponent } from 'twenty-ui/display'; + +import { StyledNavigationMenuItemIconContainer } from '@/navigation-menu-item/components/NavigationMenuItemIconContainer'; +import { getNavigationMenuItemIconStyleFromColor } from '@/navigation-menu-item/utils/get-navigation-menu-item-icon-style-from-color'; + +export type NavigationMenuItemStyleIconProps = { + Icon: IconComponent; + color?: string | null; +}; + +export const NavigationMenuItemStyleIcon = ({ + Icon, + color, +}: NavigationMenuItemStyleIconProps) => { + const theme = useTheme(); + const style = getNavigationMenuItemIconStyleFromColor(theme, color); + return ( + + + + ); +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/ObjectIconWithViewOverlay.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/ObjectIconWithViewOverlay.tsx index 32cbf51277..8a8bec1bf8 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/ObjectIconWithViewOverlay.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/ObjectIconWithViewOverlay.tsx @@ -2,34 +2,40 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import type { IconComponent } from 'twenty-ui/display'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; +import { getNavigationMenuItemIconStyleFromColor } from '@/navigation-menu-item/utils/get-navigation-menu-item-icon-style-from-color'; const StyledCompositeContainer = styled.div` align-items: center; - border-radius: ${({ theme }) => theme.border.radius.xs}; + border-radius: 4px; + box-sizing: border-box; display: flex; flex-shrink: 0; - height: ${({ theme }) => theme.spacing(4.5)}; + height: 16px; justify-content: center; position: relative; - width: ${({ theme }) => theme.spacing(4.5)}; + width: 16px; `; -const StyledObjectIconWrapper = styled.div<{ $backgroundColor: string }>` +const StyledObjectIconWrapper = styled.div<{ + $backgroundColor: string; + $borderColor?: string; +}>` position: absolute; inset: 0; - border-radius: ${({ theme }) => theme.border.radius.xs}; + border-radius: 4px; + box-sizing: border-box; background-color: ${({ $backgroundColor }) => $backgroundColor}; display: flex; align-items: center; justify-content: center; + ${({ $borderColor }) => + $borderColor ? `border: 1px solid ${$borderColor};` : ''} `; const StyledViewOverlay = styled.div<{ $backgroundColor: string }>` align-items: center; background-color: ${({ $backgroundColor }) => $backgroundColor}; - border: 1px solid ${({ theme }) => theme.background.primary}; - border-radius: ${({ theme }) => theme.border.radius.xs}; + border-radius: 4px; bottom: -7px; display: flex; height: ${({ theme }) => theme.spacing(3.5)}; @@ -42,29 +48,37 @@ const StyledViewOverlay = styled.div<{ $backgroundColor: string }>` export type ObjectIconWithViewOverlayProps = { ObjectIcon: IconComponent; ViewIcon: IconComponent; + objectColor?: string | null; }; export const ObjectIconWithViewOverlay = ({ ObjectIcon, ViewIcon, + objectColor, }: ObjectIconWithViewOverlayProps) => { const theme = useTheme(); - const iconColors = getNavigationMenuItemIconColors(theme); + const objectStyle = getNavigationMenuItemIconStyleFromColor( + theme, + objectColor, + ); return ( - + - + diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx index acb2a67536..c9d07a64ae 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItems.tsx @@ -1,7 +1,12 @@ import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; -import { useNavigate } from 'react-router-dom'; -import { IconLink, IconPlus, IconTool, useIcons } from 'twenty-ui/display'; +import { + IconColumnInsertRight, + IconLink, + IconPlus, + IconTool, + useIcons, +} from 'twenty-ui/display'; import { LightIconButton } from 'twenty-ui/input'; import { FeatureFlagKey } from '~/generated-metadata/graphql'; @@ -26,9 +31,8 @@ import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavi import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; -import { useStore } from 'jotai'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { isNonEmptyString } from '@sniptt/guards'; +import { useStore } from 'jotai'; import { CommandMenuPages } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; @@ -64,7 +68,6 @@ export const WorkspaceNavigationMenuItems = () => { const setOpenNavigationMenuItemFolderIds = useSetAtomState( openNavigationMenuItemFolderIdsState, ); - const navigate = useNavigate(); const { navigateCommandMenu } = useNavigateCommandMenu(); const { openNavigationMenuItemInCommandMenu } = useOpenNavigationMenuItemInCommandMenu(); @@ -100,14 +103,14 @@ export const WorkspaceNavigationMenuItems = () => { pageIcon: IconLink, }); } else if (isDefined(objectMetadataItem)) { + const pageTitle = + item.itemType === NavigationMenuItemType.VIEW + ? item.labelIdentifier + : objectMetadataItem.labelSingular; openNavigationMenuItemInCommandMenu({ - pageTitle: objectMetadataItem.labelPlural, + pageTitle, pageIcon: getIcon(objectMetadataItem.icon), }); - - if (isNonEmptyString(item.link)) { - navigate(item.link); - } } }; @@ -118,7 +121,7 @@ export const WorkspaceNavigationMenuItems = () => { enterEditMode(); setSelectedNavigationMenuItemInEditMode(navigationMenuItemId); openNavigationMenuItemInCommandMenu({ - pageTitle: objectMetadataItem.labelPlural, + pageTitle: objectMetadataItem.labelSingular, pageIcon: getIcon(objectMetadataItem.icon), }); }; @@ -128,7 +131,7 @@ export const WorkspaceNavigationMenuItems = () => { navigateCommandMenu({ page: CommandMenuPages.NavigationMenuAddItem, pageTitle: t`New sidebar item`, - pageIcon: IconPlus, + pageIcon: IconColumnInsertRight, resetNavigationStack: true, }); }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItemsFolder.tsx b/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItemsFolder.tsx index 6a74c92e7b..5362d5e8b0 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItemsFolder.tsx +++ b/packages/twenty-front/src/modules/navigation-menu-item/components/WorkspaceNavigationMenuItemsFolder.tsx @@ -1,13 +1,20 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { Droppable } from '@hello-pangea/dnd'; +import { useLingui } from '@lingui/react/macro'; import { isNonEmptyString } from '@sniptt/guards'; import { useContext } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; import { isDefined } from 'twenty-shared/utils'; +import { + IconChevronDown, + IconChevronRight, + IconPlus, + useIcons, +} from 'twenty-ui/display'; import { useIsDropDisabledForSection } from '@/navigation-menu-item/hooks/useIsDropDisabledForSection'; -import { useLocation, useNavigate } from 'react-router-dom'; -import { IconChevronDown, IconChevronRight, useIcons } from 'twenty-ui/display'; +import { useOpenAddItemToFolderPage } from '@/navigation-menu-item/hooks/useOpenAddItemToFolderPage'; import { AnimatedExpandableContainer } from 'twenty-ui/layout'; import { useIsMobile } from 'twenty-ui/utilities'; @@ -15,18 +22,17 @@ import { NavigationMenuItemDroppable } from '@/navigation-menu-item/components/N import { NavigationMenuItemIcon } from '@/navigation-menu-item/components/NavigationMenuItemIcon'; import { WorkspaceNavigationMenuItemFolderDragClone } from '@/navigation-menu-item/components/WorkspaceNavigationMenuItemFolderDragClone'; import { FOLDER_ICON_DEFAULT } from '@/navigation-menu-item/constants/FolderIconDefault'; +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder'; import { NavigationMenuItemDroppableIds } from '@/navigation-menu-item/constants/NavigationMenuItemDroppableIds'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; import { NavigationMenuItemDragContext } from '@/navigation-menu-item/contexts/NavigationMenuItemDragContext'; import { type NavigationMenuItemClickParams } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems'; import { openNavigationMenuItemFolderIdsState } from '@/navigation-menu-item/states/openNavigationMenuItemFolderIdsState'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; import { getNavigationMenuItemSecondaryLabel } from '@/navigation-menu-item/utils/getNavigationMenuItemSecondaryLabel'; import { getObjectMetadataForNavigationMenuItem } from '@/navigation-menu-item/utils/getObjectMetadataForNavigationMenuItem'; import { isLocationMatchingNavigationMenuItem } from '@/navigation-menu-item/utils/isLocationMatchingNavigationMenuItem'; import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem'; import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; import { NavigationDrawerItemsCollapsableContainer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemsCollapsableContainer'; @@ -34,12 +40,11 @@ import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/compo import { currentNavigationMenuItemFolderIdState } from '@/ui/navigation/navigation-drawer/states/currentNavigationMenuItemFolderIdState'; import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; import { coreViewsState } from '@/views/states/coreViewState'; import { ViewKey } from '@/views/types/ViewKey'; import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { FeatureFlagKey } from '~/generated-metadata/graphql'; const StyledFolderContainer = styled.div<{ $isSelectedInEditMode: boolean }>` border: ${({ theme, $isSelectedInEditMode }) => @@ -54,7 +59,6 @@ const StyledFolderDroppableContent = styled.div<{ }>` display: flex; flex-direction: column; - gap: ${({ theme }) => theme.betweenSiblingsGap}; padding-bottom: ${({ theme, $compact }) => ($compact ? 0 : theme.spacing(2))}; `; @@ -68,6 +72,7 @@ type WorkspaceNavigationMenuItemsFolderProps = { folderId: string; folderName: string; folderIconKey?: string | null; + folderColor?: string | null; navigationMenuItems: ProcessedNavigationMenuItem[]; isGroup: boolean; isEditMode?: boolean; @@ -82,6 +87,7 @@ export const WorkspaceNavigationMenuItemsFolder = ({ folderId, folderName, folderIconKey, + folderColor, navigationMenuItems, isGroup, isEditMode = false, @@ -93,10 +99,6 @@ export const WorkspaceNavigationMenuItemsFolder = ({ }: WorkspaceNavigationMenuItemsFolderProps) => { const theme = useTheme(); const { getIcon } = useIcons(); - const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED, - ); - const iconColors = getNavigationMenuItemIconColors(theme); const FolderIcon = getIcon(folderIconKey ?? FOLDER_ICON_DEFAULT); const objectMetadataItems = useAtomStateValue(objectMetadataItemsState); const coreViews = useAtomStateValue(coreViewsState); @@ -107,15 +109,30 @@ export const WorkspaceNavigationMenuItemsFolder = ({ const currentViewPath = location.pathname + location.search; const isMobile = useIsMobile(); + const { t } = useLingui(); const [openNavigationMenuItemFolderIds, setOpenNavigationMenuItemFolderIds] = useAtomState(openNavigationMenuItemFolderIdsState); const setCurrentNavigationMenuItemFolderId = useSetAtomState( currentNavigationMenuItemFolderIdState, ); + const { openAddItemToFolderPage } = useOpenAddItemToFolderPage(); const isOpen = openNavigationMenuItemFolderIds.includes(folderId); + const folderContentLengthForTree = + isEditMode && isSelectedInEditMode + ? navigationMenuItems.length + 1 + : navigationMenuItems.length; + + const handleAddMenuItemToFolder = () => { + openAddItemToFolderPage({ + targetFolderId: folderId, + targetIndex: navigationMenuItems.length, + resetNavigationStack: true, + }); + }; + const handleToggle = () => { if (isMobile) { setCurrentNavigationMenuItemFolderId((prev) => @@ -165,8 +182,10 @@ export const WorkspaceNavigationMenuItemsFolder = ({ )} + {isEditMode && isSelectedInEditMode && ( + + )} diff --git a/packages/twenty-front/src/modules/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder.ts b/packages/twenty-front/src/modules/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder.ts new file mode 100644 index 0000000000..ba6173a441 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder.ts @@ -0,0 +1 @@ +export const DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER = 'orange'; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/constants/NavigationMenuItemDefaultColorLink.ts b/packages/twenty-front/src/modules/navigation-menu-item/constants/NavigationMenuItemDefaultColorLink.ts new file mode 100644 index 0000000000..5d0542bb55 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/constants/NavigationMenuItemDefaultColorLink.ts @@ -0,0 +1 @@ +export const DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK = 'red'; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/constants/NavigationMenuItemDefaultIconColor.constant.ts b/packages/twenty-front/src/modules/navigation-menu-item/constants/NavigationMenuItemDefaultIconColor.constant.ts new file mode 100644 index 0000000000..d420bcb653 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/constants/NavigationMenuItemDefaultIconColor.constant.ts @@ -0,0 +1,3 @@ +import type { ThemeColor } from 'twenty-ui/theme'; + +export const DEFAULT_NAV_ITEM_ICON_COLOR: ThemeColor = 'gray'; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/graphql/fragments/navigationMenuItemFragment.ts b/packages/twenty-front/src/modules/navigation-menu-item/graphql/fragments/navigationMenuItemFragment.ts index 5e30e2a577..f1a435bfc7 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/graphql/fragments/navigationMenuItemFragment.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/graphql/fragments/navigationMenuItemFragment.ts @@ -11,6 +11,7 @@ export const NAVIGATION_MENU_ITEM_FRAGMENT = gql` name link icon + color position applicationId createdAt diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddFolderToNavigationMenuDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddFolderToNavigationMenuDraft.ts index 133ec4b9b9..5e8ed48a1f 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddFolderToNavigationMenuDraft.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddFolderToNavigationMenuDraft.ts @@ -1,10 +1,11 @@ -import { v4 } from 'uuid'; import { isDefined } from 'twenty-shared/utils'; +import { v4 } from 'uuid'; import type { NavigationMenuItem } from '~/generated-metadata/graphql'; +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder'; import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState'; -import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; import { computeInsertIndexAndPosition } from '@/navigation-menu-item/utils/computeInsertIndexAndPosition'; +import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; export const useAddFolderToNavigationMenuDraft = () => { const setNavigationMenuItemsDraft = useSetAtomState( @@ -43,6 +44,7 @@ export const useAddFolderToNavigationMenuDraft = () => { position, userWorkspaceId: undefined, name: name.trim(), + color: DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER, applicationId: undefined, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddLinkToNavigationMenuDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddLinkToNavigationMenuDraft.ts index bf706098bf..9ed79c2985 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddLinkToNavigationMenuDraft.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddLinkToNavigationMenuDraft.ts @@ -2,10 +2,11 @@ import { isDefined } from 'twenty-shared/utils'; import { v4 } from 'uuid'; import type { NavigationMenuItem } from '~/generated-metadata/graphql'; +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorLink'; import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState'; -import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; import { computeInsertIndexAndPosition } from '@/navigation-menu-item/utils/computeInsertIndexAndPosition'; import { normalizeUrl } from '@/navigation-menu-item/utils/normalizeUrl'; +import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; export const useAddLinkToNavigationMenuDraft = () => { const setNavigationMenuItemsDraft = useSetAtomState( @@ -47,6 +48,7 @@ export const useAddLinkToNavigationMenuDraft = () => { userWorkspaceId: undefined, name: label.trim() || 'Link', link: normalizedUrl, + color: DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK, applicationId: undefined, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddObjectToNavigationMenuDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddObjectToNavigationMenuDraft.ts index 1454235cbb..ff431361af 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddObjectToNavigationMenuDraft.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddObjectToNavigationMenuDraft.ts @@ -1,10 +1,10 @@ -import { v4 } from 'uuid'; import { isDefined } from 'twenty-shared/utils'; +import { v4 } from 'uuid'; import type { NavigationMenuItem } from '~/generated-metadata/graphql'; import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState'; -import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; import { computeInsertIndexAndPosition } from '@/navigation-menu-item/utils/computeInsertIndexAndPosition'; +import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; export const useAddObjectToNavigationMenuDraft = () => { const setNavigationMenuItemsDraft = useSetAtomState( @@ -17,6 +17,7 @@ export const useAddObjectToNavigationMenuDraft = () => { currentDraft: NavigationMenuItem[], targetFolderId?: string | null, targetIndex?: number, + color?: string | null, ): string => { const folderId = targetFolderId ?? null; @@ -45,6 +46,7 @@ export const useAddObjectToNavigationMenuDraft = () => { folderId: folderId ?? undefined, name: undefined, applicationId: undefined, + color, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddViewToNavigationMenuDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddViewToNavigationMenuDraft.ts index 0792f58e66..7c9db8294b 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddViewToNavigationMenuDraft.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useAddViewToNavigationMenuDraft.ts @@ -1,10 +1,10 @@ -import { v4 } from 'uuid'; import { isDefined } from 'twenty-shared/utils'; +import { v4 } from 'uuid'; import type { NavigationMenuItem } from '~/generated-metadata/graphql'; import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState'; -import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; import { computeInsertIndexAndPosition } from '@/navigation-menu-item/utils/computeInsertIndexAndPosition'; +import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; export const useAddViewToNavigationMenuDraft = () => { const setNavigationMenuItemsDraft = useSetAtomState( @@ -16,6 +16,7 @@ export const useAddViewToNavigationMenuDraft = () => { currentDraft: NavigationMenuItem[], targetFolderId?: string | null, targetIndex?: number, + color?: string | null, ): string => { const folderId = targetFolderId ?? null; @@ -44,6 +45,7 @@ export const useAddViewToNavigationMenuDraft = () => { folderId: folderId ?? undefined, name: undefined, applicationId: undefined, + color, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useDraftNavigationMenuItemsAllFolders.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useDraftNavigationMenuItemsAllFolders.ts index 9a3637d5bf..f12389dd9d 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useDraftNavigationMenuItemsAllFolders.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useDraftNavigationMenuItemsAllFolders.ts @@ -8,7 +8,9 @@ export const useDraftNavigationMenuItemsAllFolders = () => { currentDraft?.filter(isNavigationMenuItemFolder).map((item) => ({ id: item.id, name: item.name ?? 'Folder', - folderId: item.folderId ?? undefined, + folderId: item.folderId, + icon: item.icon, + color: item.color, })) ?? []; return { allFolders }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useDraftNavigationMenuItemsWorkspaceFolders.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useDraftNavigationMenuItemsWorkspaceFolders.ts index c7a691d91e..33cbfe4c66 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useDraftNavigationMenuItemsWorkspaceFolders.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useDraftNavigationMenuItemsWorkspaceFolders.ts @@ -8,6 +8,8 @@ export const useDraftNavigationMenuItemsWorkspaceFolders = () => { (folder) => ({ id: folder.id, name: folder.folderName, + icon: folder.icon, + color: folder.color, }), ); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useHandleAddToNavigationDrop.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useHandleAddToNavigationDrop.ts index 977923322e..003550c40f 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useHandleAddToNavigationDrop.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useHandleAddToNavigationDrop.ts @@ -18,11 +18,13 @@ import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/states/i import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/navigationMenuItemsDraftState'; import { openNavigationMenuItemFolderIdsState } from '@/navigation-menu-item/states/openNavigationMenuItemFolderIdsState'; import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/states/selectedNavigationMenuItemInEditModeState'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; -import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; +import { getObjectMetadataIdsInDraft } from '@/navigation-menu-item/utils/getObjectMetadataIdsInDraft'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; import { isWorkspaceDroppableId } from '@/navigation-menu-item/utils/isWorkspaceDroppableId'; import { validateAndExtractWorkspaceFolderId } from '@/navigation-menu-item/utils/validateAndExtractWorkspaceFolderId'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; +import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; import { coreViewsState } from '@/views/states/coreViewState'; import { convertCoreViewToView } from '@/views/utils/convertCoreViewToView'; import { useStore } from 'jotai'; @@ -128,15 +130,27 @@ export const useHandleAddToNavigationDrop = () => { return; } case NavigationMenuItemType.OBJECT: { + const views = coreViews.map(convertCoreViewToView); + const objectMetadataIdsInWorkspace = getObjectMetadataIdsInDraft( + currentDraft, + views, + ); + if (objectMetadataIdsInWorkspace.has(payload.objectMetadataId)) { + return; + } + const objectMetadataItem = objectMetadataItems.find( + (item) => item.id === payload.objectMetadataId, + ); const newItemId = addObjectToDraft( payload.objectMetadataId, payload.defaultViewId, currentDraft, folderId, index, - ); - const objectMetadataItem = objectMetadataItems.find( - (item) => item.id === payload.objectMetadataId, + payload.iconColor ?? + (objectMetadataItem + ? getStandardObjectIconColor(objectMetadataItem.nameSingular) + : undefined), ); openEditForNewNavItem(newItemId, { pageTitle: objectMetadataItem?.labelPlural ?? payload.label, @@ -147,14 +161,22 @@ export const useHandleAddToNavigationDrop = () => { return; } case NavigationMenuItemType.VIEW: { + const views = coreViews.map(convertCoreViewToView); + const view = views.find((v) => v.id === payload.viewId); + const viewObjectMetadataItem = view + ? objectMetadataItems.find( + (item) => item.id === view.objectMetadataId, + ) + : undefined; const newItemId = addViewToDraft( payload.viewId, currentDraft, folderId, index, + viewObjectMetadataItem + ? getStandardObjectIconColor(viewObjectMetadataItem.nameSingular) + : undefined, ); - const views = coreViews.map(convertCoreViewToView); - const view = views.find((v) => v.id === payload.viewId); openEditForNewNavItem(newItemId, { pageTitle: view?.name ?? payload.label, pageIcon: view ? getIcon(view.icon) : IconFolder, diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuItemsByFolder.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuItemsByFolder.ts index 51cbadb8d7..361e020677 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuItemsByFolder.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuItemsByFolder.ts @@ -15,9 +15,16 @@ import { usePrefetchedNavigationMenuItemsData } from './usePrefetchedNavigationM type NavigationMenuItemFolder = { id: string; folderName: string; + icon?: string | null; + color?: string | null; navigationMenuItems: ReturnType[number][]; }; +type NavigationMenuItemFolderEntry = Pick< + NavigationMenuItemFolder, + 'id' | 'folderName' | 'icon' | 'color' +>; + export const useNavigationMenuItemsByFolder = () => { const coreViews = useAtomStateValue(coreViewsState); const objectMetadataItems = useAtomStateValue(objectMetadataItemsState); @@ -34,13 +41,18 @@ export const useNavigationMenuItemsByFolder = () => { const { workspaceFolders, userFolders, itemsByFolderId } = allNavigationMenuItems.reduce<{ - workspaceFolders: Array<{ id: string; name: string }>; - userFolders: Array<{ id: string; name: string }>; + workspaceFolders: NavigationMenuItemFolderEntry[]; + userFolders: NavigationMenuItemFolderEntry[]; itemsByFolderId: Map; }>( (acc, item) => { if (isNavigationMenuItemFolder(item)) { - const folderEntry = { id: item.id, name: item.name || 'Folder' }; + const folderEntry: NavigationMenuItemFolderEntry = { + id: item.id, + folderName: item.name || 'Folder', + icon: item.icon ?? undefined, + color: item.color ?? undefined, + }; if (isDefined(item.userWorkspaceId)) { acc.userFolders.push(folderEntry); } else { @@ -64,7 +76,7 @@ export const useNavigationMenuItemsByFolder = () => { }, ); - const buildFoldersList = (folders: Array<{ id: string; name: string }>) => { + const buildFoldersList = (folders: NavigationMenuItemFolderEntry[]) => { const sortedFolders = [...folders].sort((a, b) => { const folderA = allNavigationMenuItems.find((item) => item.id === a.id); const folderB = allNavigationMenuItems.find((item) => item.id === b.id); @@ -117,7 +129,9 @@ export const useNavigationMenuItemsByFolder = () => { acc.push({ id: folder.id, - folderName: folder.name, + folderName: folder.folderName, + icon: folder.icon, + color: folder.color, navigationMenuItems: sortedItems, }); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft.ts index 93324647b4..771793896c 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useNavigationMenuObjectMetadataFromDraft.ts @@ -1,5 +1,6 @@ import { isDefined } from 'twenty-shared/utils'; +import { getObjectMetadataIdsInDraft } from '@/navigation-menu-item/utils/getObjectMetadataIdsInDraft'; import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { coreViewsState } from '@/views/states/coreViewState'; import { ViewKey } from '@/views/types/ViewKey'; @@ -17,20 +18,9 @@ export const useNavigationMenuObjectMetadataFromDraft = ( const coreViews = useAtomStateValue(coreViewsState); const views = coreViews.map(convertCoreViewToView); - const objectMetadataIdsInWorkspace = currentDraft.reduce>( - (ids, item) => { - const view = isDefined(item.viewId) - ? views.find((view) => view.id === item.viewId) - : undefined; - if (isDefined(view)) { - ids.add(view.objectMetadataId); - } - if (isDefined(item.targetObjectMetadataId)) { - ids.add(item.targetObjectMetadataId); - } - return ids; - }, - new Set(), + const objectMetadataIdsInWorkspace = getObjectMetadataIdsInDraft( + currentDraft, + views, ); const objectMetadataIdsWithIndexView = new Set( diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useObjectNavItemColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useObjectNavItemColor.ts new file mode 100644 index 0000000000..b989d7c97e --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useObjectNavItemColor.ts @@ -0,0 +1,17 @@ +import { useWorkspaceSectionItems } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; +import { ViewKey } from '@/views/types/ViewKey'; + +export const useObjectNavItemColor = (objectNameSingular: string): string => { + const items = useWorkspaceSectionItems(); + const objectNavItem = items.find( + (item) => + 'viewKey' in item && + item.viewKey === ViewKey.Index && + item.objectNameSingular === objectNameSingular, + ); + return ( + (objectNavItem && 'color' in objectNavItem ? objectNavItem.color : null) ?? + getStandardObjectIconColor(objectNameSingular) + ); +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useOpenAddItemToFolderPage.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useOpenAddItemToFolderPage.ts new file mode 100644 index 0000000000..95b28eb60b --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useOpenAddItemToFolderPage.ts @@ -0,0 +1,39 @@ +import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; +import { addMenuItemInsertionContextState } from '@/navigation-menu-item/states/addMenuItemInsertionContextState'; +import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; +import { useLingui } from '@lingui/react/macro'; +import { CommandMenuPages } from 'twenty-shared/types'; +import { IconColumnInsertRight } from 'twenty-ui/display'; + +type OpenAddItemToFolderPageParams = { + targetFolderId: string; + targetIndex: number; + resetNavigationStack?: boolean; +}; + +export const useOpenAddItemToFolderPage = () => { + const { t } = useLingui(); + const { navigateCommandMenu } = useNavigateCommandMenu(); + const setAddMenuItemInsertionContext = useSetAtomState( + addMenuItemInsertionContextState, + ); + + const openAddItemToFolderPage = ({ + targetFolderId, + targetIndex, + resetNavigationStack = true, + }: OpenAddItemToFolderPageParams) => { + setAddMenuItemInsertionContext({ + targetFolderId, + targetIndex, + }); + navigateCommandMenu({ + page: CommandMenuPages.NavigationMenuAddItem, + pageTitle: t`New sidebar item`, + pageIcon: IconColumnInsertRight, + resetNavigationStack, + }); + }; + + return { openAddItemToFolderPage }; +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSaveNavigationMenuItemsDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSaveNavigationMenuItemsDraft.ts index a1520a6d4c..5e14c3ef1f 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSaveNavigationMenuItemsDraft.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSaveNavigationMenuItemsDraft.ts @@ -12,8 +12,8 @@ import { navigationMenuItemsDraftState } from '@/navigation-menu-item/states/nav import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/utils/filterWorkspaceNavigationMenuItems'; import { isNavigationMenuItemFolder } from '@/navigation-menu-item/utils/isNavigationMenuItemFolder'; import { isNavigationMenuItemLink } from '@/navigation-menu-item/utils/isNavigationMenuItemLink'; -import { useStore } from 'jotai'; import { prefetchNavigationMenuItemsState } from '@/prefetch/states/prefetchNavigationMenuItemsState'; +import { useStore } from 'jotai'; export const useSaveNavigationMenuItemsDraft = () => { const { updateNavigationMenuItem } = useUpdateNavigationMenuItem(); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel.ts index be6a5d7e2b..94fb6ef5b8 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemLabel.ts @@ -1,6 +1,23 @@ +import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; import { useSelectedNavigationMenuItemEditItem } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItem'; import { useSelectedNavigationMenuItemEditItemObjectMetadata } from '@/navigation-menu-item/hooks/useSelectedNavigationMenuItemEditItemObjectMetadata'; -import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; + +const getLabelForItemType = ( + itemType: NavigationMenuItemType, + item: { name?: string | null; labelIdentifier?: string | null }, + objectLabelSingular?: string | null, +): string => { + switch (itemType) { + case NavigationMenuItemType.FOLDER: + return item.name ?? 'Folder'; + case NavigationMenuItemType.LINK: + return item.name ?? 'Link'; + case NavigationMenuItemType.VIEW: + return item.labelIdentifier ?? objectLabelSingular ?? ''; + default: + return objectLabelSingular ?? ''; + } +}; export const useSelectedNavigationMenuItemEditItemLabel = () => { const { selectedItem } = useSelectedNavigationMenuItemEditItem(); @@ -8,11 +25,11 @@ export const useSelectedNavigationMenuItemEditItemLabel = () => { useSelectedNavigationMenuItemEditItemObjectMetadata(); const selectedItemLabel = selectedItem - ? selectedItem.itemType === NavigationMenuItemType.FOLDER - ? (selectedItem.name ?? 'Folder') - : selectedItem.itemType === NavigationMenuItemType.LINK - ? (selectedItem.name ?? 'Link') - : (selectedItemObjectMetadata?.labelPlural ?? '') + ? getLabelForItemType( + selectedItem.itemType, + selectedItem, + selectedItemObjectMetadata?.labelSingular, + ) : null; return { selectedItemLabel }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useUpdateNavigationMenuItemInDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useUpdateNavigationMenuItemInDraft.ts index ac6e746af6..993f339b49 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/hooks/useUpdateNavigationMenuItemInDraft.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/hooks/useUpdateNavigationMenuItemInDraft.ts @@ -20,10 +20,9 @@ export const useUpdateNavigationMenuItemInDraft = () => { setNavigationMenuItemsDraft((draft) => { if (!draft) return draft; - return draft.map((item) => { - if (item.id !== navigationMenuItemId) return item; - return { ...item, ...updates }; - }); + return draft.map((item) => + item.id === navigationMenuItemId ? { ...item, ...updates } : item, + ); }); }; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/types/add-to-navigation-drag-payload.ts b/packages/twenty-front/src/modules/navigation-menu-item/types/add-to-navigation-drag-payload.ts index 77a973253f..a44559a00b 100644 --- a/packages/twenty-front/src/modules/navigation-menu-item/types/add-to-navigation-drag-payload.ts +++ b/packages/twenty-front/src/modules/navigation-menu-item/types/add-to-navigation-drag-payload.ts @@ -3,6 +3,7 @@ export type AddToNavigationDragPayloadObject = { objectMetadataId: string; defaultViewId: string; label: string; + iconColor?: string; }; export type AddToNavigationDragPayloadView = { diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/NavigationMenuItemIconColorShadeBackground.constant.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/NavigationMenuItemIconColorShadeBackground.constant.ts new file mode 100644 index 0000000000..13bf6ae5a1 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/NavigationMenuItemIconColorShadeBackground.constant.ts @@ -0,0 +1 @@ +export const COLOR_SHADE_BACKGROUND = 5; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/NavigationMenuItemIconColorShadeBorder.constant.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/NavigationMenuItemIconColorShadeBorder.constant.ts new file mode 100644 index 0000000000..8167e2df31 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/NavigationMenuItemIconColorShadeBorder.constant.ts @@ -0,0 +1 @@ +export const COLOR_SHADE_BORDER = 6; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/NavigationMenuItemIconColorShadeIcon.constant.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/NavigationMenuItemIconColorShadeIcon.constant.ts new file mode 100644 index 0000000000..fabc31d4e9 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/NavigationMenuItemIconColorShadeIcon.constant.ts @@ -0,0 +1 @@ +export const COLOR_SHADE_ICON = 11; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/get-color-from-theme.util.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/get-color-from-theme.util.ts new file mode 100644 index 0000000000..9ecfa585e1 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/get-color-from-theme.util.ts @@ -0,0 +1,12 @@ +import type { Theme } from '@emotion/react'; +import type { ThemeColor } from 'twenty-ui/theme'; + +export const getColorFromTheme = ( + theme: Theme, + themeColor: ThemeColor, + shade: number, +): string => { + const colorMap = theme.color as unknown as Record; + const key = `${themeColor}${shade}`; + return colorMap[key] ?? theme.tag.text[themeColor]; +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/get-navigation-menu-item-icon-border-color.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/get-navigation-menu-item-icon-border-color.ts new file mode 100644 index 0000000000..75e76cbf68 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/get-navigation-menu-item-icon-border-color.ts @@ -0,0 +1,10 @@ +import type { Theme } from '@emotion/react'; +import type { ThemeColor } from 'twenty-ui/theme'; + +import { getColorFromTheme } from '@/navigation-menu-item/utils/get-color-from-theme.util'; +import { COLOR_SHADE_BORDER } from '@/navigation-menu-item/utils/NavigationMenuItemIconColorShadeBorder.constant'; + +export const getNavigationMenuItemIconBorderColor = ( + theme: Theme, + themeColor: ThemeColor, +): string => getColorFromTheme(theme, themeColor, COLOR_SHADE_BORDER); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/get-navigation-menu-item-icon-style-from-color.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/get-navigation-menu-item-icon-style-from-color.ts new file mode 100644 index 0000000000..d21f33c406 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/get-navigation-menu-item-icon-style-from-color.ts @@ -0,0 +1,25 @@ +import type { Theme } from '@emotion/react'; + +import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultIconColor.constant'; +import { getColorFromTheme } from '@/navigation-menu-item/utils/get-color-from-theme.util'; +import { COLOR_SHADE_BACKGROUND } from '@/navigation-menu-item/utils/NavigationMenuItemIconColorShadeBackground.constant'; +import { COLOR_SHADE_BORDER } from '@/navigation-menu-item/utils/NavigationMenuItemIconColorShadeBorder.constant'; +import { COLOR_SHADE_ICON } from '@/navigation-menu-item/utils/NavigationMenuItemIconColorShadeIcon.constant'; +import { type NavigationMenuItemIconStyle } from '@/navigation-menu-item/utils/navigation-menu-item-icon-style.type'; +import { parseThemeColor } from '@/navigation-menu-item/utils/parseThemeColor'; + +export const getNavigationMenuItemIconStyleFromColor = ( + theme: Theme, + color: string | null | undefined, +): NavigationMenuItemIconStyle => { + const themeColor = parseThemeColor(color ?? DEFAULT_NAV_ITEM_ICON_COLOR); + return { + backgroundColor: getColorFromTheme( + theme, + themeColor, + COLOR_SHADE_BACKGROUND, + ), + iconColor: getColorFromTheme(theme, themeColor, COLOR_SHADE_ICON), + borderColor: getColorFromTheme(theme, themeColor, COLOR_SHADE_BORDER), + }; +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/getEffectiveNavigationMenuItemColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/getEffectiveNavigationMenuItemColor.ts new file mode 100644 index 0000000000..22475ebbcc --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/getEffectiveNavigationMenuItemColor.ts @@ -0,0 +1,21 @@ +import { isNonEmptyString } from '@sniptt/guards'; + +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorFolder'; +import { DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultColorLink'; +import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; +import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/types/processed-navigation-menu-item'; + +export const getEffectiveNavigationMenuItemColor = ( + navigationMenuItem: ProcessedNavigationMenuItem, +): string | undefined => { + if (isNonEmptyString(navigationMenuItem.color)) { + return navigationMenuItem.color; + } + if (navigationMenuItem.itemType === NavigationMenuItemType.FOLDER) { + return DEFAULT_NAVIGATION_MENU_ITEM_COLOR_FOLDER; + } + if (navigationMenuItem.itemType === NavigationMenuItemType.LINK) { + return DEFAULT_NAVIGATION_MENU_ITEM_COLOR_LINK; + } + return undefined; +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/getIconBackgroundColorForPayload.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/getIconBackgroundColorForPayload.ts deleted file mode 100644 index 2bc17e4fba..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/utils/getIconBackgroundColorForPayload.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Theme } from '@emotion/react'; - -import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; -import type { AddToNavigationDragPayload } from '@/navigation-menu-item/types/add-to-navigation-drag-payload'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; - -export const getIconBackgroundColorForPayload = ( - payload: AddToNavigationDragPayload, - theme: Theme, -): string | undefined => { - const colors = getNavigationMenuItemIconColors(theme); - switch (payload.type) { - case NavigationMenuItemType.OBJECT: - return colors.object; - case NavigationMenuItemType.VIEW: - return colors.view; - case NavigationMenuItemType.FOLDER: - return colors.folder; - case NavigationMenuItemType.LINK: - return colors.link; - case NavigationMenuItemType.RECORD: - return undefined; - default: - return undefined; - } -}; 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 deleted file mode 100644 index ce983aa6d9..0000000000 --- a/packages/twenty-front/src/modules/navigation-menu-item/utils/getNavigationMenuItemIconColors.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { Theme } from '@emotion/react'; - -export const getNavigationMenuItemIconColors = (theme: Theme) => ({ - folder: theme.color.orange, - link: theme.color.red, - view: theme.grayScale.gray8, - object: theme.color.blue, -}); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/getObjectMetadataIdsInDraft.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/getObjectMetadataIdsInDraft.ts new file mode 100644 index 0000000000..18e62f4e21 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/getObjectMetadataIdsInDraft.ts @@ -0,0 +1,28 @@ +import { isDefined } from 'twenty-shared/utils'; + +type NavigationMenuItemDraftForObjectIds = { + viewId?: string | null; + targetObjectMetadataId?: string | null; +}; + +type ViewForObjectIds = { + id: string; + objectMetadataId: string; +}; + +export const getObjectMetadataIdsInDraft = ( + draft: NavigationMenuItemDraftForObjectIds[], + views: ViewForObjectIds[], +): Set => + draft.reduce>((ids, item) => { + const view = isDefined(item.viewId) + ? views.find((view) => view.id === item.viewId) + : undefined; + if (isDefined(view)) { + ids.add(view.objectMetadataId); + } + if (isDefined(item.targetObjectMetadataId)) { + ids.add(item.targetObjectMetadataId); + } + return ids; + }, new Set()); diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/getStandardObjectIconColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/getStandardObjectIconColor.ts new file mode 100644 index 0000000000..b399cb24b7 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/getStandardObjectIconColor.ts @@ -0,0 +1,38 @@ +import { type ThemeColor, MAIN_COLOR_NAMES } from 'twenty-ui/theme'; + +import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultIconColor.constant'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; + +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/utils/navigation-menu-item-icon-style.type.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/navigation-menu-item-icon-style.type.ts new file mode 100644 index 0000000000..5fbf0b7ea8 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/navigation-menu-item-icon-style.type.ts @@ -0,0 +1,5 @@ +export type NavigationMenuItemIconStyle = { + backgroundColor: string; + iconColor: string; + borderColor: string; +}; diff --git a/packages/twenty-front/src/modules/navigation-menu-item/utils/parseThemeColor.ts b/packages/twenty-front/src/modules/navigation-menu-item/utils/parseThemeColor.ts new file mode 100644 index 0000000000..30e6783e16 --- /dev/null +++ b/packages/twenty-front/src/modules/navigation-menu-item/utils/parseThemeColor.ts @@ -0,0 +1,10 @@ +import { themeColorSchema, type ThemeColor } from 'twenty-ui/theme'; + +import { DEFAULT_NAV_ITEM_ICON_COLOR } from '@/navigation-menu-item/constants/NavigationMenuItemDefaultIconColor.constant'; + +export const parseThemeColor = ( + color: string | null | undefined, +): ThemeColor => { + const result = themeColorSchema.safeParse(color ?? ''); + return result.success ? result.data : DEFAULT_NAV_ITEM_ICON_COLOR; +}; 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 c47bbf2edc..84d89ed76c 100644 --- a/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx +++ b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx @@ -2,7 +2,8 @@ import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainCo import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; import { ObjectIconWithViewOverlay } from '@/navigation-menu-item/components/ObjectIconWithViewOverlay'; import { NavigationMenuItemType } from '@/navigation-menu-item/constants/NavigationMenuItemType'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; +import { useObjectNavItemColor } from '@/navigation-menu-item/hooks/useObjectNavItemColor'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems'; import { lastVisitedViewPerObjectMetadataItemState } from '@/navigation/states/lastVisitedViewPerObjectMetadataItemState'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; @@ -13,11 +14,10 @@ import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/compo import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment'; import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue'; import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue'; +import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { coreViewsFromObjectMetadataItemFamilySelector } from '@/views/states/selectors/coreViewsFromObjectMetadataItemFamilySelector'; import { ViewKey } from '@/views/types/ViewKey'; -import { useTheme } from '@emotion/react'; import { useLocation } from 'react-router-dom'; -import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; import { AppPath } from 'twenty-shared/types'; import { getAppPath, isDefined } from 'twenty-shared/utils'; import { Avatar, useIcons } from 'twenty-ui/display'; @@ -45,11 +45,9 @@ export const NavigationDrawerItemForObjectMetadataItem = ({ onActiveItemClickWhenNotInEditMode, isDragging = false, }: NavigationDrawerItemForObjectMetadataItemProps) => { - const theme = useTheme(); const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled( FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED, ); - const iconColors = getNavigationMenuItemIconColors(theme); const lastVisitedViewPerObjectMetadataItem = useAtomStateValue( lastVisitedViewPerObjectMetadataItemState, ); @@ -68,6 +66,9 @@ export const NavigationDrawerItemForObjectMetadataItem = ({ lastVisitedViewPerObjectMetadataItem?.[objectMetadataItem.id]; const { getIcon } = useIcons(); + const objectNavItemColor = useObjectNavItemColor( + objectMetadataItem.nameSingular, + ); const location = useLocation(); const currentPath = location.pathname; const currentPathWithSearch = `${location.pathname}${location.search}`; @@ -141,13 +142,17 @@ export const NavigationDrawerItemForObjectMetadataItem = ({ ) : getIcon(objectMetadataItem.icon); - const iconBackgroundColor = - isNavigationMenuItemEditingEnabled && !isRecord && !isViewWithCustomName - ? iconColors.object + const iconThemeColor = + isNavigationMenuItemEditingEnabled && !isRecord + ? isDefined(navigationMenuItem?.color) + ? navigationMenuItem.color + : (getStandardObjectIconColor(objectMetadataItem.nameSingular) ?? + 'gray') : undefined; const secondaryLabel = @@ -222,7 +227,7 @@ export const NavigationDrawerItemForObjectMetadataItem = ({ } onClick={handleClick} Icon={Icon} - iconBackgroundColor={iconBackgroundColor} + iconColor={iconThemeColor} active={isActive} isSelectedInEditMode={isSelectedInEditMode} isDragging={isDragging} diff --git a/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForWorkspaceItems.tsx b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForWorkspaceItems.tsx index ae7bc999b0..de33480684 100644 --- a/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForWorkspaceItems.tsx +++ b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerSectionForWorkspaceItems.tsx @@ -1,5 +1,4 @@ import { NavigationDropTargetContext } from '@/navigation-menu-item/contexts/NavigationDropTargetContext'; -import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { Droppable } from '@hello-pangea/dnd'; import { useLingui } from '@lingui/react/macro'; @@ -18,7 +17,6 @@ import { type FlatWorkspaceItem, type NavigationMenuItemClickParams, } from '@/navigation-menu-item/hooks/useWorkspaceSectionItems'; -import { getNavigationMenuItemIconColors } from '@/navigation-menu-item/utils/getNavigationMenuItemIconColors'; import { getObjectMetadataForNavigationMenuItem } from '@/navigation-menu-item/utils/getObjectMetadataForNavigationMenuItem'; import { type ProcessedNavigationMenuItem } from '@/navigation-menu-item/utils/sortNavigationMenuItems'; import { NavigationDrawerItemForObjectMetadataItem } from '@/object-metadata/components/NavigationDrawerItemForObjectMetadataItem'; @@ -67,7 +65,6 @@ export const NavigationDrawerSectionForWorkspaceItems = ({ onActiveObjectMetadataItemClick, }: NavigationDrawerSectionForWorkspaceItemsProps) => { const { t } = useLingui(); - const theme = useTheme(); const workspaceDropDisabled = useIsDropDisabledForSection(true); const { toggleNavigationSection, isNavigationSectionOpen } = useNavigationSection('Workspace'); @@ -206,6 +203,9 @@ export const NavigationDrawerSectionForWorkspaceItems = ({ folderId={item.id} folderName={item.name ?? 'Folder'} folderIconKey={item.Icon} + folderColor={ + 'color' in item ? item.color : undefined + } navigationMenuItems={ folderChildrenById.get(item.id) ?? [] } @@ -231,7 +231,6 @@ export const NavigationDrawerSectionForWorkspaceItems = ({ if (type === 'link') { const linkItem = item as ProcessedNavigationMenuItem; - const iconColors = getNavigationMenuItemIconColors(theme); return ( { const objectMetadataItem = findObjectMetadataItemByNamePlural(objectNamePlural); - const { getIcon } = useIcons(); - const Icon = getIcon(objectMetadataItem?.icon); - const label = objectMetadataItem?.labelPlural ?? objectNamePlural; const pageHeaderTitle = @@ -65,7 +62,12 @@ export const RecordIndexPageHeader = () => { ); return ( - + ( + + )} + > {isDefined(contextStoreCurrentViewId) && ( <> 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 new file mode 100644 index 0000000000..154cbc5325 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeaderIcon.tsx @@ -0,0 +1,59 @@ +import { isNonEmptyString } from '@sniptt/guards'; + +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; +import { usePrefetchedNavigationMenuItemsData } from '@/navigation-menu-item/hooks/usePrefetchedNavigationMenuItemsData'; +import { getStandardObjectIconColor } from '@/navigation-menu-item/utils/getStandardObjectIconColor'; +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue'; +import { coreIndexViewIdFromObjectMetadataItemFamilySelector } from '@/views/states/selectors/coreIndexViewIdFromObjectMetadataItemFamilySelector'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; +import { useTheme } from '@emotion/react'; +import { isDefined } from 'twenty-shared/utils'; +import { useIcons } from 'twenty-ui/display'; +import { FeatureFlagKey } from '~/generated-metadata/graphql'; + +export const RecordIndexPageHeaderIcon = ({ + objectMetadataItem, +}: { + objectMetadataItem?: ObjectMetadataItem; +}) => { + const theme = useTheme(); + const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled( + FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED, + ); + const { workspaceNavigationMenuItems } = + usePrefetchedNavigationMenuItemsData(); + const coreIndexViewId = useAtomFamilySelectorValue( + coreIndexViewIdFromObjectMetadataItemFamilySelector, + { objectMetadataItemId: objectMetadataItem?.id ?? '' }, + ); + const { getIcon } = useIcons(); + const ObjectIcon = getIcon(objectMetadataItem?.icon); + + if (!ObjectIcon) { + return null; + } + + if (!isNavigationMenuItemEditingEnabled) { + return ; + } + + const navItem = isDefined(coreIndexViewId) + ? workspaceNavigationMenuItems.find( + (item) => item.viewId === coreIndexViewId, + ) + : undefined; + const navigationMenuItemColor = isNonEmptyString(navItem?.color) + ? navItem.color + : undefined; + const iconColor = + navigationMenuItemColor ?? + getStandardObjectIconColor(objectMetadataItem?.nameSingular ?? ''); + + return ( + + ); +}; 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 22bf852e32..05087d8824 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,3 +1,4 @@ +import { NavigationMenuItemStyleIcon } from '@/navigation-menu-item/components/NavigationMenuItemStyleIcon'; 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'; @@ -7,10 +8,12 @@ import { type NavigationDrawerSubItemState } from '@/ui/navigation/navigation-dr import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; 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 { isNonEmptyString } from '@sniptt/guards'; import { type ReactNode } from 'react'; import { Link } from 'react-router-dom'; import { isDefined } from 'twenty-shared/utils'; @@ -29,6 +32,7 @@ import { type TriggerEventType, useMouseDownNavigation, } from 'twenty-ui/utilities'; +import { FeatureFlagKey } from '~/generated-metadata/graphql'; const DEFAULT_INDENTATION_LEVEL = 1; @@ -43,7 +47,7 @@ export type NavigationDrawerItemProps = { to?: string; onClick?: () => void; Icon?: IconComponent | ((props: TablerIconsProps) => JSX.Element); - iconBackgroundColor?: string; + iconColor?: string | null; active?: boolean; danger?: boolean; soon?: boolean; @@ -217,7 +221,10 @@ const StyledSpacer = styled.span` flex-grow: 1; `; -const StyledIcon = styled.div<{ $backgroundColor?: string }>` +const StyledIcon = styled.div<{ + $backgroundColor?: string; + $borderColor?: string; +}>` align-items: center; display: flex; flex-grow: 0; @@ -225,13 +232,15 @@ const StyledIcon = styled.div<{ $backgroundColor?: string }>` justify-content: center; margin-right: ${({ theme }) => theme.spacing(2)}; - ${({ theme, $backgroundColor }) => + ${({ theme, $backgroundColor, $borderColor }) => $backgroundColor && css` background-color: ${$backgroundColor}; - border-radius: ${theme.border.radius.xs}; - height: ${theme.spacing(4.5)}; - width: ${theme.spacing(4.5)}; + border-radius: 4px; + box-sizing: border-box; + height: ${theme.spacing(4)}; + width: ${theme.spacing(4)}; + ${$borderColor ? `border: 1px solid ${$borderColor};` : ''} `} `; @@ -286,7 +295,7 @@ export const NavigationDrawerItem = ({ secondaryLabel, indentationLevel = DEFAULT_INDENTATION_LEVEL, Icon, - iconBackgroundColor, + iconColor, to, onClick, active, @@ -308,6 +317,9 @@ export const NavigationDrawerItem = ({ const theme = useTheme(); const isMobile = useIsMobile(); const isSettingsPage = useIsSettingsPage(); + const isNavigationMenuItemEditingEnabled = useIsFeatureEnabled( + FeatureFlagKey.IS_NAVIGATION_MENU_ITEM_EDITING_ENABLED, + ); const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] = useAtomState(isNavigationDrawerExpandedState); @@ -379,30 +391,30 @@ export const NavigationDrawerItem = ({ )} - {Icon && ( - - + + + ) : ( + + - - )} + } + /> + + ))} { + await queryRunner.query( + `ALTER TABLE "core"."navigationMenuItem" ADD "color" text`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."navigationMenuItem" DROP COLUMN "color"`, + ); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/application/utils/from-navigation-menu-item-manifest-to-universal-flat-navigation-menu-item.util.ts b/packages/twenty-server/src/engine/core-modules/application/utils/from-navigation-menu-item-manifest-to-universal-flat-navigation-menu-item.util.ts index 39c2cabcb8..2fdc0aa594 100644 --- a/packages/twenty-server/src/engine/core-modules/application/utils/from-navigation-menu-item-manifest-to-universal-flat-navigation-menu-item.util.ts +++ b/packages/twenty-server/src/engine/core-modules/application/utils/from-navigation-menu-item-manifest-to-universal-flat-navigation-menu-item.util.ts @@ -17,6 +17,7 @@ export const fromNavigationMenuItemManifestToUniversalFlatNavigationMenuItem = applicationUniversalIdentifier, name: navigationMenuItemManifest.name ?? null, icon: navigationMenuItemManifest.icon ?? null, + color: navigationMenuItemManifest.color ?? null, position: navigationMenuItemManifest.position, viewUniversalIdentifier: navigationMenuItemManifest.viewUniversalIdentifier ?? null, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap index 6a74e1be83..3f787164d5 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/__tests__/__snapshots__/all-universal-flat-entity-properties-to-compare-and-stringify.constant.spec.ts.snap @@ -106,6 +106,7 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma "name", "link", "icon", + "color", ], "propertiesToStringify": [], }, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts index d4c59e526d..95ca7bb8d2 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant.ts @@ -1020,6 +1020,11 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = { name: { toCompare: true, toStringify: false, universalProperty: undefined }, link: { toCompare: true, toStringify: false, universalProperty: undefined }, icon: { toCompare: true, toStringify: false, universalProperty: undefined }, + color: { + toCompare: true, + toStringify: false, + universalProperty: undefined, + }, createdAt: { toCompare: false, toStringify: false, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/constants/flat-navigation-menu-item-editable-properties.constant.ts b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/constants/flat-navigation-menu-item-editable-properties.constant.ts index 00bea27ab1..27c3d47d7e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/constants/flat-navigation-menu-item-editable-properties.constant.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/constants/flat-navigation-menu-item-editable-properties.constant.ts @@ -6,4 +6,5 @@ export const FLAT_NAVIGATION_MENU_ITEM_EDITABLE_PROPERTIES = [ 'name', 'link', 'icon', + 'color', ] as const satisfies MetadataEntityPropertyName<'navigationMenuItem'>[]; diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-create-navigation-menu-item-input-to-flat-navigation-menu-item-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-create-navigation-menu-item-input-to-flat-navigation-menu-item-to-create.util.ts index 831eb3e844..26ce55f893 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-create-navigation-menu-item-input-to-flat-navigation-menu-item-to-create.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-create-navigation-menu-item-input-to-flat-navigation-menu-item-to-create.util.ts @@ -82,6 +82,7 @@ export const fromCreateNavigationMenuItemInputToFlatNavigationMenuItemToCreate = name: createNavigationMenuItemInput.name ?? null, link: createNavigationMenuItemInput.link ?? null, icon: createNavigationMenuItemInput.icon ?? null, + color: createNavigationMenuItemInput.color ?? null, position, workspaceId, applicationId: flatApplication.id, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-flat-navigation-menu-item-to-navigation-menu-item-dto.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-flat-navigation-menu-item-to-navigation-menu-item-dto.util.ts index bc4c74468d..f942e26642 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-flat-navigation-menu-item-to-navigation-menu-item-dto.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-flat-navigation-menu-item-to-navigation-menu-item-dto.util.ts @@ -14,6 +14,7 @@ export const fromFlatNavigationMenuItemToNavigationMenuItemDto = ( name: flatNavigationMenuItem.name ?? undefined, link: flatNavigationMenuItem.link ?? undefined, icon: flatNavigationMenuItem.icon ?? undefined, + color: flatNavigationMenuItem.color ?? undefined, position: flatNavigationMenuItem.position, workspaceId: flatNavigationMenuItem.workspaceId, applicationId: flatNavigationMenuItem.applicationId ?? undefined, diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-navigation-menu-item-entity-to-flat-navigation-menu-item.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-navigation-menu-item-entity-to-flat-navigation-menu-item.util.ts index 00679ca296..cd62156a9b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-navigation-menu-item-entity-to-flat-navigation-menu-item.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-navigation-menu-item/utils/from-navigation-menu-item-entity-to-flat-navigation-menu-item.util.ts @@ -83,6 +83,7 @@ export const fromNavigationMenuItemEntityToFlatNavigationMenuItem = ({ name: navigationMenuItemEntity.name, link: navigationMenuItemEntity.link, icon: navigationMenuItemEntity.icon, + color: navigationMenuItemEntity.color, position: navigationMenuItemEntity.position, workspaceId: navigationMenuItemEntity.workspaceId, universalIdentifier: navigationMenuItemEntity.universalIdentifier, diff --git a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/create-navigation-menu-item.input.ts b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/create-navigation-menu-item.input.ts index 138b050fb1..efe8041483 100644 --- a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/create-navigation-menu-item.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/create-navigation-menu-item.input.ts @@ -41,6 +41,11 @@ export class CreateNavigationMenuItemInput { @Field(() => String, { nullable: true }) icon?: string | null; + @IsOptional() + @IsString() + @Field(() => String, { nullable: true }) + color?: string | null; + @IsUUID() @IsOptional() @Field(() => UUIDScalarType, { nullable: true }) diff --git a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/navigation-menu-item.dto.ts b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/navigation-menu-item.dto.ts index 67c8055c07..240d820136 100644 --- a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/navigation-menu-item.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/navigation-menu-item.dto.ts @@ -51,6 +51,10 @@ export class NavigationMenuItemDTO { @Field(() => String, { nullable: true }) icon?: string | null; + @IsOptional() + @Field(() => String, { nullable: true }) + color?: string | null; + @IsUUID() @IsOptional() @Field(() => UUIDScalarType, { nullable: true }) diff --git a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/update-navigation-menu-item.input.ts b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/update-navigation-menu-item.input.ts index 4ec27e8639..3967a1e4b9 100644 --- a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/update-navigation-menu-item.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/dtos/update-navigation-menu-item.input.ts @@ -38,6 +38,11 @@ export class UpdateNavigationMenuItemInput { @IsString() @Field(() => String, { nullable: true }) icon?: string | null; + + @IsOptional() + @IsString() + @Field(() => String, { nullable: true }) + color?: string | null; } @InputType() diff --git a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity.ts b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity.ts index 0270f7d45a..7a1822f405 100644 --- a/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/navigation-menu-item/entities/navigation-menu-item.entity.ts @@ -87,6 +87,9 @@ export class NavigationMenuItemEntity @Column({ nullable: true, type: 'text' }) icon: string | null; + @Column({ nullable: true, type: 'text' }) + color: string | null; + @ManyToOne(() => NavigationMenuItemEntity, { onDelete: 'CASCADE', nullable: true, diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts index a10ee65927..0f5d93f77c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts @@ -896,6 +896,7 @@ export class ObjectMetadataService extends TypeOrmQueryService +> = { + allCompanies: 'blue', + allPeople: 'blue', + allTasks: 'turquoise', + allNotes: 'turquoise', + allOpportunities: 'red', + workflowsFolder: 'orange', + allDashboards: 'gray', + workflowsFolderAllWorkflows: 'gray', + workflowsFolderAllWorkflowRuns: 'gray', + workflowsFolderAllWorkflowVersions: 'gray', +}; diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/navigation-menu-item/create-standard-navigation-menu-item-flat-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/navigation-menu-item/create-standard-navigation-menu-item-flat-metadata.util.ts index 08fd0c1002..943b335086 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/navigation-menu-item/create-standard-navigation-menu-item-flat-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/navigation-menu-item/create-standard-navigation-menu-item-flat-metadata.util.ts @@ -1,11 +1,14 @@ import { isDefined } from 'twenty-shared/utils'; import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type'; -import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications'; import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util'; import { type FlatNavigationMenuItem } from 'src/engine/metadata-modules/flat-navigation-menu-item/types/flat-navigation-menu-item.type'; import { type FlatView } from 'src/engine/metadata-modules/flat-view/types/flat-view.type'; -import { STANDARD_NAVIGATION_MENU_ITEMS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-navigation-menu-item.constant'; +import { + STANDARD_NAVIGATION_MENU_ITEM_DEFAULT_COLORS, + STANDARD_NAVIGATION_MENU_ITEMS, +} from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-navigation-menu-item.constant'; +import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications'; export const createStandardNavigationMenuItemFlatMetadata = ({ workspaceId, @@ -66,6 +69,9 @@ export const createStandardNavigationMenuItemFlatMetadata = ({ name: null, link: null, icon: null, + color: + STANDARD_NAVIGATION_MENU_ITEM_DEFAULT_COLORS[navigationMenuItemName] ?? + null, position, createdAt: now, updatedAt: now, diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/navigation-menu-item/create-standard-navigation-menu-item-folder-flat-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/navigation-menu-item/create-standard-navigation-menu-item-folder-flat-metadata.util.ts index 949bfc7668..9a57a0b52b 100644 --- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/navigation-menu-item/create-standard-navigation-menu-item-folder-flat-metadata.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/navigation-menu-item/create-standard-navigation-menu-item-folder-flat-metadata.util.ts @@ -40,6 +40,7 @@ export const createStandardNavigationMenuItemFolderFlatMetadata = ({ name, link: null, icon: icon ?? null, + color: 'orange', position, createdAt: now, updatedAt: now, @@ -99,6 +100,7 @@ export const createStandardNavigationMenuItemFolderItemFlatMetadata = ({ name: null, link: null, icon: null, + color: 'gray', position, createdAt: now, updatedAt: now, diff --git a/packages/twenty-shared/src/application/navigationMenuItemManifestType.ts b/packages/twenty-shared/src/application/navigationMenuItemManifestType.ts index dca715c537..3038d8af00 100644 --- a/packages/twenty-shared/src/application/navigationMenuItemManifestType.ts +++ b/packages/twenty-shared/src/application/navigationMenuItemManifestType.ts @@ -3,6 +3,7 @@ import { type SyncableEntityOptions } from '@/application/syncableEntityOptionsT export type NavigationMenuItemManifest = SyncableEntityOptions & { name?: string; icon?: string; + color?: string; position: number; viewUniversalIdentifier?: string; link?: string; diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts index f423511e43..74a0489827 100644 --- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts +++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts @@ -91,6 +91,7 @@ export { IconCodeCircle, IconCoins, IconColorSwatch, + IconColumnInsertRight, IconColumns, IconCommand, IconMessageCircle as IconComment, @@ -268,8 +269,8 @@ export { IconMoodSmile, IconMoon, IconMouse2, - IconNorthStar, IconNewSection, + IconNorthStar, IconNoteOff, IconNotes, IconNumber, diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts index 9c4e254a5c..4df76bb99a 100644 --- a/packages/twenty-ui/src/display/index.ts +++ b/packages/twenty-ui/src/display/index.ts @@ -166,6 +166,7 @@ export { IconCodeCircle, IconCoins, IconColorSwatch, + IconColumnInsertRight, IconColumns, IconCommand, IconComment, @@ -343,8 +344,8 @@ export { IconMoodSmile, IconMoon, IconMouse2, - IconNorthStar, IconNewSection, + IconNorthStar, IconNoteOff, IconNotes, IconNumber, diff --git a/packages/twenty-ui/src/navigation/index.ts b/packages/twenty-ui/src/navigation/index.ts index dd3380d1b8..52f8997f2b 100644 --- a/packages/twenty-ui/src/navigation/index.ts +++ b/packages/twenty-ui/src/navigation/index.ts @@ -41,13 +41,14 @@ export { MenuItemSelect, } from './menu/menu-item/components/MenuItemSelect'; export { MenuItemSelectAvatar } from './menu/menu-item/components/MenuItemSelectAvatar'; -export type { ColorLabels } from './menu/menu-item/components/MenuItemSelectColor'; export { MenuItemSelectColor } from './menu/menu-item/components/MenuItemSelectColor'; export { MenuItemSelectTag } from './menu/menu-item/components/MenuItemSelectTag'; export type { MenuItemSuggestionProps } from './menu/menu-item/components/MenuItemSuggestion'; export { MenuItemSuggestion } from './menu/menu-item/components/MenuItemSuggestion'; export type { MenuItemToggleProps } from './menu/menu-item/components/MenuItemToggle'; export { MenuItemToggle } from './menu/menu-item/components/MenuItemToggle'; +export type { ColorLabels } from './menu/menu-item/constants/DefaultColorLabels'; +export { DEFAULT_COLOR_LABELS } from './menu/menu-item/constants/DefaultColorLabels'; export type { MenuItemIconProps } from './menu/menu-item/internals/components/MenuItemIcon'; export { MenuItemIcon } from './menu/menu-item/internals/components/MenuItemIcon'; export { diff --git a/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItemSelectColor.tsx b/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItemSelectColor.tsx index 741e980349..dc0871b850 100644 --- a/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItemSelectColor.tsx +++ b/packages/twenty-ui/src/navigation/menu/menu-item/components/MenuItemSelectColor.tsx @@ -8,38 +8,12 @@ import { import { ColorSample, type ColorSampleVariant } from '@ui/display'; import { type ThemeColor } from '@ui/theme'; +import { + DEFAULT_COLOR_LABELS, + type ColorLabels, +} from '../constants/DefaultColorLabels'; import { StyledMenuItemSelect } from './MenuItemSelect'; -export type ColorLabels = Record; - -const DEFAULT_COLOR_LABELS: ColorLabels = { - gray: 'Gray', - tomato: 'Tomato', - red: 'Red', - ruby: 'Ruby', - crimson: 'Crimson', - pink: 'Pink', - plum: 'Plum', - purple: 'Purple', - violet: 'Violet', - iris: 'Iris', - cyan: 'Cyan', - turquoise: 'Turquoise', - sky: 'Sky', - blue: 'Blue', - jade: 'Jade', - green: 'Green', - grass: 'Grass', - mint: 'Mint', - lime: 'Lime', - bronze: 'Bronze', - gold: 'Gold', - brown: 'Brown', - orange: 'Orange', - amber: 'Amber', - yellow: 'Yellow', -}; - type MenuItemSelectColorProps = { selected: boolean; className?: string; diff --git a/packages/twenty-ui/src/navigation/menu/menu-item/constants/DefaultColorLabels.ts b/packages/twenty-ui/src/navigation/menu/menu-item/constants/DefaultColorLabels.ts new file mode 100644 index 0000000000..daa8524ff0 --- /dev/null +++ b/packages/twenty-ui/src/navigation/menu/menu-item/constants/DefaultColorLabels.ts @@ -0,0 +1,31 @@ +import { type ThemeColor } from '@ui/theme'; + +export type ColorLabels = Record; + +export const DEFAULT_COLOR_LABELS: ColorLabels = { + gray: 'Gray', + tomato: 'Tomato', + red: 'Red', + ruby: 'Ruby', + crimson: 'Crimson', + pink: 'Pink', + plum: 'Plum', + purple: 'Purple', + violet: 'Violet', + iris: 'Iris', + cyan: 'Cyan', + turquoise: 'Turquoise', + sky: 'Sky', + blue: 'Blue', + jade: 'Jade', + green: 'Green', + grass: 'Grass', + mint: 'Mint', + lime: 'Lime', + bronze: 'Bronze', + gold: 'Gold', + brown: 'Brown', + orange: 'Orange', + amber: 'Amber', + yellow: 'Yellow', +};