mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	[UI] Icons in navbar (#9346)
* Add Icons to main header * Add user configurability
This commit is contained in:
		| @@ -183,6 +183,12 @@ USER_SETTINGS: dict[str, InvenTreeSettingsKeyType] = { | |||||||
|         'default': False, |         'default': False, | ||||||
|         'validator': bool, |         'validator': bool, | ||||||
|     }, |     }, | ||||||
|  |     'ICONS_IN_NAVBAR': { | ||||||
|  |         'name': _('Navigation Icons'), | ||||||
|  |         'description': _('Display icons in the navigation bar'), | ||||||
|  |         'default': False, | ||||||
|  |         'validator': bool, | ||||||
|  |     }, | ||||||
|     'DATE_DISPLAY_FORMAT': { |     'DATE_DISPLAY_FORMAT': { | ||||||
|         'name': _('Date Format'), |         'name': _('Date Format'), | ||||||
|         'description': _('Preferred format for displaying dates'), |         'description': _('Preferred format for displaying dates'), | ||||||
|   | |||||||
| @@ -13,13 +13,16 @@ import { type ReactNode, useEffect, useMemo, useState } from 'react'; | |||||||
| import { useMatch, useNavigate } from 'react-router-dom'; | import { useMatch, useNavigate } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { api } from '../../App'; | import { api } from '../../App'; | ||||||
| import { navTabs as mainNavTabs } from '../../defaults/links'; | import { getNavTabs } from '../../defaults/links'; | ||||||
| import { ApiEndpoints } from '../../enums/ApiEndpoints'; | import { ApiEndpoints } from '../../enums/ApiEndpoints'; | ||||||
| import { navigateToLink } from '../../functions/navigation'; | import { navigateToLink } from '../../functions/navigation'; | ||||||
| import * as classes from '../../main.css'; | import * as classes from '../../main.css'; | ||||||
| import { apiUrl, useServerApiState } from '../../states/ApiState'; | import { apiUrl, useServerApiState } from '../../states/ApiState'; | ||||||
| import { useLocalState } from '../../states/LocalState'; | import { useLocalState } from '../../states/LocalState'; | ||||||
| import { useGlobalSettingsState } from '../../states/SettingsState'; | import { | ||||||
|  |   useGlobalSettingsState, | ||||||
|  |   useUserSettingsState | ||||||
|  | } from '../../states/SettingsState'; | ||||||
| import { useUserState } from '../../states/UserState'; | import { useUserState } from '../../states/UserState'; | ||||||
| import { ScanButton } from '../buttons/ScanButton'; | import { ScanButton } from '../buttons/ScanButton'; | ||||||
| import { SpotlightButton } from '../buttons/SpotlightButton'; | import { SpotlightButton } from '../buttons/SpotlightButton'; | ||||||
| @@ -160,30 +163,45 @@ function NavTabs() { | |||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|   const match = useMatch(':tabName/*'); |   const match = useMatch(':tabName/*'); | ||||||
|   const tabValue = match?.params.tabName; |   const tabValue = match?.params.tabName; | ||||||
|  |   const navTabs = getNavTabs(user); | ||||||
|  |   const userSettings = useUserSettingsState(); | ||||||
|  |  | ||||||
|  |   const withIcons: boolean = useMemo( | ||||||
|  |     () => userSettings.isSet('ICONS_IN_NAVBAR', false), | ||||||
|  |     [userSettings] | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   const tabs: ReactNode[] = useMemo(() => { |   const tabs: ReactNode[] = useMemo(() => { | ||||||
|     const _tabs: ReactNode[] = []; |     const _tabs: ReactNode[] = []; | ||||||
|  |  | ||||||
|     mainNavTabs.forEach((tab) => { |     navTabs.forEach((tab) => { | ||||||
|       if (tab.role && !user.hasViewRole(tab.role)) { |       if (tab.role && !user.hasViewRole(tab.role)) { | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  |       // TODO: Hide icons if user does not wish to display them! | ||||||
|  |  | ||||||
|       _tabs.push( |       _tabs.push( | ||||||
|         <Tabs.Tab |         <Tabs.Tab | ||||||
|           value={tab.name} |           value={tab.name} | ||||||
|           key={tab.name} |           key={tab.name} | ||||||
|  |           leftSection={ | ||||||
|  |             withIcons && | ||||||
|  |             tab.icon && ( | ||||||
|  |               <ActionIcon variant='transparent'>{tab.icon}</ActionIcon> | ||||||
|  |             ) | ||||||
|  |           } | ||||||
|           onClick={(event: any) => |           onClick={(event: any) => | ||||||
|             navigateToLink(`/${tab.name}`, navigate, event) |             navigateToLink(`/${tab.name}`, navigate, event) | ||||||
|           } |           } | ||||||
|         > |         > | ||||||
|           {tab.text} |           {tab.title} | ||||||
|         </Tabs.Tab> |         </Tabs.Tab> | ||||||
|       ); |       ); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     return _tabs; |     return _tabs; | ||||||
|   }, [mainNavTabs, user]); |   }, [navTabs, user, withIcons]); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Tabs |     <Tabs | ||||||
|   | |||||||
| @@ -1,28 +1,72 @@ | |||||||
| import { Trans, t } from '@lingui/macro'; | import { Trans, t } from '@lingui/macro'; | ||||||
| import { openContextModal } from '@mantine/modals'; | import { openContextModal } from '@mantine/modals'; | ||||||
|  |  | ||||||
|  | import { | ||||||
|  |   IconBox, | ||||||
|  |   IconBuildingFactory2, | ||||||
|  |   IconDashboard, | ||||||
|  |   IconPackages, | ||||||
|  |   IconShoppingCart, | ||||||
|  |   IconTruckDelivery | ||||||
|  | } from '@tabler/icons-react'; | ||||||
|  | import type { ReactNode } from 'react'; | ||||||
| import type { MenuLinkItem } from '../components/items/MenuLinks'; | import type { MenuLinkItem } from '../components/items/MenuLinks'; | ||||||
| import { StylishText } from '../components/items/StylishText'; | import { StylishText } from '../components/items/StylishText'; | ||||||
| import { UserRoles } from '../enums/Roles'; | import { UserRoles } from '../enums/Roles'; | ||||||
| import type { SettingsStateProps } from '../states/SettingsState'; | import type { SettingsStateProps } from '../states/SettingsState'; | ||||||
| import type { UserStateProps } from '../states/UserState'; | import type { UserStateProps } from '../states/UserState'; | ||||||
|  |  | ||||||
| export const navTabs = [ | type NavTab = { | ||||||
|   { text: <Trans>Dashboard</Trans>, name: 'home' }, |   name: string; | ||||||
|   { text: <Trans>Parts</Trans>, name: 'part', role: UserRoles.part }, |   title: string; | ||||||
|   { text: <Trans>Stock</Trans>, name: 'stock', role: UserRoles.stock }, |   icon: ReactNode; | ||||||
|   { |   role?: UserRoles; | ||||||
|     text: <Trans>Manufacturing</Trans>, | }; | ||||||
|     name: 'manufacturing', |  | ||||||
|     role: UserRoles.build | export function getNavTabs(user: UserStateProps): NavTab[] { | ||||||
|   }, |   const navTabs: NavTab[] = [ | ||||||
|   { |     { | ||||||
|     text: <Trans>Purchasing</Trans>, |       name: 'home', | ||||||
|     name: 'purchasing', |       title: t`Dashboard`, | ||||||
|     role: UserRoles.purchase_order |       icon: <IconDashboard /> | ||||||
|   }, |     }, | ||||||
|   { text: <Trans>Sales</Trans>, name: 'sales', role: UserRoles.sales_order } |     { | ||||||
| ]; |       name: 'part', | ||||||
|  |       title: t`Parts`, | ||||||
|  |       icon: <IconBox />, | ||||||
|  |       role: UserRoles.part | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'stock', | ||||||
|  |       title: t`Stock`, | ||||||
|  |       icon: <IconPackages />, | ||||||
|  |       role: UserRoles.stock | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'manufacturing', | ||||||
|  |       title: t`Manufacturing`, | ||||||
|  |       icon: <IconBuildingFactory2 />, | ||||||
|  |       role: UserRoles.build | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'purchasing', | ||||||
|  |       title: t`Purchasing`, | ||||||
|  |       icon: <IconShoppingCart />, | ||||||
|  |       role: UserRoles.purchase_order | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       name: 'sales', | ||||||
|  |       title: t`Sales`, | ||||||
|  |       icon: <IconTruckDelivery />, | ||||||
|  |       role: UserRoles.sales_order | ||||||
|  |     } | ||||||
|  |   ]; | ||||||
|  |  | ||||||
|  |   return navTabs.filter((tab) => { | ||||||
|  |     if (!tab.role) return true; | ||||||
|  |     return user.hasViewRole(tab.role); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
| export const docLinks = { | export const docLinks = { | ||||||
|   app: 'https://docs.inventree.org/app/', |   app: 'https://docs.inventree.org/app/', | ||||||
|   | |||||||
| @@ -49,6 +49,7 @@ export default function UserSettings() { | |||||||
|         content: ( |         content: ( | ||||||
|           <UserSettingList |           <UserSettingList | ||||||
|             keys={[ |             keys={[ | ||||||
|  |               'ICONS_IN_NAVBAR', | ||||||
|               'STICKY_HEADER', |               'STICKY_HEADER', | ||||||
|               'DATE_DISPLAY_FORMAT', |               'DATE_DISPLAY_FORMAT', | ||||||
|               'FORMS_CLOSE_USING_ESCAPE', |               'FORMS_CLOSE_USING_ESCAPE', | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user