Implement Mobile Footer Navigation and Enhance Dashboard Layout
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Added MobileFooterNavigation component for improved navigation on mobile devices, featuring segmented navigation for key sections. - Updated DashboardLayout to conditionally render MobileFooterNavigation based on screen size. - Enhanced DashboardNavigation to dynamically adjust menu items based on mobile view, improving user experience. - Refactored CSS styles for mobile footer navigation to ensure proper layout and responsiveness. - Adjusted ObjectTable component to account for new mobile layout, ensuring consistent display across devices.
This commit is contained in:
parent
968dc0667a
commit
2fa5c288d2
@ -3356,3 +3356,96 @@ body.objectKanbanColumnResizing * {
|
|||||||
.ant-steps-item-content {
|
.ant-steps-item-content {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation.ant-layout-footer {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
line-height: normal;
|
||||||
|
padding: 8px 8px calc(8px + env(safe-area-inset-bottom, 0px));
|
||||||
|
background: var(--color-mobile-footer-bg);
|
||||||
|
color: var(--color-text);
|
||||||
|
border-top: 1px solid var(--color-mobile-footer-item-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation-tabs.segmented-nav {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-item {
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
border: none;
|
||||||
|
color: var(--color-text);
|
||||||
|
transition:
|
||||||
|
background 0.2s ease,
|
||||||
|
color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-item-icon {
|
||||||
|
padding-inline: 0;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-item-icon-contents {
|
||||||
|
min-height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-item-icon-contents .anticon {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-thumb {
|
||||||
|
background: var(--color-mobile-footer-item-bg);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-item-selected,
|
||||||
|
.mobile-footer-navigation
|
||||||
|
.segmented-nav-item:not(.segmented-nav-item-disabled):hover {
|
||||||
|
background: var(--color-mobile-footer-item-bg);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-item-selected {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-item-selected .anticon,
|
||||||
|
.mobile-footer-navigation .segmented-nav-item-selected svg {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-footer-navigation .segmented-nav-item-label {
|
||||||
|
min-height: 0;
|
||||||
|
line-height: 1.2;
|
||||||
|
padding-inline: 0;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-layout.is-mobile {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-menu.ant-menu-horizontal {
|
||||||
|
max-width: 32px;
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-menu.ant-menu-horizontal .ant-menu-submenu {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -11,14 +11,17 @@ import DashboardNavigation from './common/DashboardNavigation'
|
|||||||
import DashboardBreadcrumb from './common/DashboardBreadcrumb'
|
import DashboardBreadcrumb from './common/DashboardBreadcrumb'
|
||||||
import DeveloperSidebar from './Developer/DeveloperSidebar'
|
import DeveloperSidebar from './Developer/DeveloperSidebar'
|
||||||
import DashboardSidebarSplitter from './common/DashboardSidebarSplitter'
|
import DashboardSidebarSplitter from './common/DashboardSidebarSplitter'
|
||||||
|
import MobileFooterNavigation from './common/MobileFooterNavigation'
|
||||||
import { useThemeContext } from './context/ThemeContext'
|
import { useThemeContext } from './context/ThemeContext'
|
||||||
import { MessageProvider } from './context/MessageContext'
|
import { MessageProvider } from './context/MessageContext'
|
||||||
import { useDashboardObjectToolsContext } from './context/DashboardObjectToolsContext'
|
import { useDashboardObjectToolsContext } from './context/DashboardObjectToolsContext'
|
||||||
|
import { useMediaQuery } from 'react-responsive'
|
||||||
|
|
||||||
const { Content } = Layout
|
const { Content } = Layout
|
||||||
|
|
||||||
const DashboardLayout = ({ children }) => {
|
const DashboardLayout = ({ children }) => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
const isProduction = location.pathname.startsWith('/dashboard/production')
|
const isProduction = location.pathname.startsWith('/dashboard/production')
|
||||||
const isInventory = location.pathname.startsWith('/dashboard/inventory')
|
const isInventory = location.pathname.startsWith('/dashboard/inventory')
|
||||||
const isFinance = location.pathname.startsWith('/dashboard/finance')
|
const isFinance = location.pathname.startsWith('/dashboard/finance')
|
||||||
@ -49,7 +52,7 @@ const DashboardLayout = ({ children }) => {
|
|||||||
<MessageProvider>
|
<MessageProvider>
|
||||||
<Layout
|
<Layout
|
||||||
style={{ height: 'var(--unit-100vh)' }}
|
style={{ height: 'var(--unit-100vh)' }}
|
||||||
className={`${isDarkMode ? 'dark-mode' : 'light-mode'} main-layout`}
|
className={`${isDarkMode ? 'dark-mode' : 'light-mode'} main-layout${isMobile ? ' is-mobile' : ''}`}
|
||||||
>
|
>
|
||||||
<DashboardNavigation />
|
<DashboardNavigation />
|
||||||
<DashboardSidebarSplitter sidebar={sidebar}>
|
<DashboardSidebarSplitter sidebar={sidebar}>
|
||||||
@ -68,6 +71,7 @@ const DashboardLayout = ({ children }) => {
|
|||||||
</Content>
|
</Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
</DashboardSidebarSplitter>
|
</DashboardSidebarSplitter>
|
||||||
|
{isMobile ? <MobileFooterNavigation /> : null}
|
||||||
</Layout>
|
</Layout>
|
||||||
</MessageProvider>
|
</MessageProvider>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -22,7 +22,6 @@ import { Header } from 'antd/es/layout/layout'
|
|||||||
import { useMediaQuery } from 'react-responsive'
|
import { useMediaQuery } from 'react-responsive'
|
||||||
import KeyboardShortcut from './KeyboardShortcut'
|
import KeyboardShortcut from './KeyboardShortcut'
|
||||||
import UserProfilePopover from './UserProfilePopover'
|
import UserProfilePopover from './UserProfilePopover'
|
||||||
import ElipsisText from './ElipsisText'
|
|
||||||
|
|
||||||
import FarmControlLogo from '../../Logos/FarmControlLogo'
|
import FarmControlLogo from '../../Logos/FarmControlLogo'
|
||||||
import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall'
|
import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall'
|
||||||
@ -42,10 +41,14 @@ import DashboardWindowButtons from './DashboardWindowButtons'
|
|||||||
import WindowAppMenu from './WindowAppMenu'
|
import WindowAppMenu from './WindowAppMenu'
|
||||||
import WebAppSwitcher from './WebAppSwitcher'
|
import WebAppSwitcher from './WebAppSwitcher'
|
||||||
import {
|
import {
|
||||||
|
filterSidebarItemsByListPermission,
|
||||||
getSidebarDefaultPath,
|
getSidebarDefaultPath,
|
||||||
|
getSidebarItems,
|
||||||
getSidebarMenuSections,
|
getSidebarMenuSections,
|
||||||
|
getSidebarSelectedKey,
|
||||||
isSidebarSectionVisible
|
isSidebarSectionVisible
|
||||||
} from '../../../database/Sidebars'
|
} from '../../../database/Sidebars'
|
||||||
|
import { getSidebarIconNode } from '../../Icons/sidebarIconMap'
|
||||||
|
|
||||||
import { useAppUpdateContext } from '../context/AppUpdateContext'
|
import { useAppUpdateContext } from '../context/AppUpdateContext'
|
||||||
import { useThemeContext } from '../context/ThemeContext'
|
import { useThemeContext } from '../context/ThemeContext'
|
||||||
@ -53,6 +56,29 @@ import { getEffectiveAppearance } from '../../../database/Settings'
|
|||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const mapSidebarItemsToMenu = (items, navigate) =>
|
||||||
|
items.map((item) => {
|
||||||
|
if (item?.type === 'divider') {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
const mappedItem = {
|
||||||
|
key: item.key,
|
||||||
|
icon: item.icon || getSidebarIconNode(item.iconKey),
|
||||||
|
label: item.label
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.path) {
|
||||||
|
mappedItem.onClick = () => navigate(item.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item?.children && Array.isArray(item.children)) {
|
||||||
|
mappedItem.children = mapSidebarItemsToMenu(item.children, navigate)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedItem
|
||||||
|
})
|
||||||
|
|
||||||
const DashboardNavigation = () => {
|
const DashboardNavigation = () => {
|
||||||
const { userProfile } = useContext(AuthContext)
|
const { userProfile } = useContext(AuthContext)
|
||||||
const { showSpotlight } = useContext(SpotlightContext)
|
const { showSpotlight } = useContext(SpotlightContext)
|
||||||
@ -71,11 +97,6 @@ const DashboardNavigation = () => {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const [selectedKey, setSelectedKey] = useState('production')
|
const [selectedKey, setSelectedKey] = useState('production')
|
||||||
const [selectedMenuItem, setSelectedMenuItem] = useState({
|
|
||||||
key: 'production',
|
|
||||||
label: 'Production',
|
|
||||||
icon: <ProductionIcon />
|
|
||||||
})
|
|
||||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
const {
|
const {
|
||||||
platform,
|
platform,
|
||||||
@ -116,6 +137,7 @@ const DashboardNavigation = () => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
const includeDev = import.meta.env.DEV
|
const includeDev = import.meta.env.DEV
|
||||||
|
const includeDevItems = import.meta.env.MODE === 'development'
|
||||||
const mainMenuItems = useMemo(() => {
|
const mainMenuItems = useMemo(() => {
|
||||||
const iconStyles = {
|
const iconStyles = {
|
||||||
fontSize: !showNavigationLabels ? '16px' : undefined,
|
fontSize: !showNavigationLabels ? '16px' : undefined,
|
||||||
@ -135,7 +157,10 @@ const DashboardNavigation = () => {
|
|||||||
key: 'production',
|
key: 'production',
|
||||||
label: 'Production',
|
label: 'Production',
|
||||||
className: 'electrobun-webkit-app-region-no-drag',
|
className: 'electrobun-webkit-app-region-no-drag',
|
||||||
icon: navigationIcon(<ProductionIcon style={iconStyles} />, 'Production')
|
icon: navigationIcon(
|
||||||
|
<ProductionIcon style={iconStyles} />,
|
||||||
|
'Production'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'inventory',
|
key: 'inventory',
|
||||||
@ -166,6 +191,29 @@ const DashboardNavigation = () => {
|
|||||||
)
|
)
|
||||||
}, [includeDev, showNavigationLabels, userProfile])
|
}, [includeDev, showNavigationLabels, userProfile])
|
||||||
|
|
||||||
|
const sidebarMenuItems = useMemo(() => {
|
||||||
|
const items = filterSidebarItemsByListPermission(
|
||||||
|
getSidebarItems(selectedKey, { includeDev: includeDevItems }),
|
||||||
|
userProfile
|
||||||
|
)
|
||||||
|
return mapSidebarItemsToMenu(items, navigate)
|
||||||
|
}, [includeDevItems, navigate, selectedKey, userProfile])
|
||||||
|
|
||||||
|
const selectedSidebarItemKey = useMemo(
|
||||||
|
() =>
|
||||||
|
getSidebarSelectedKey(selectedKey, location.pathname, {
|
||||||
|
includeDev: includeDevItems,
|
||||||
|
userProfile
|
||||||
|
}),
|
||||||
|
[includeDevItems, location.pathname, selectedKey, userProfile]
|
||||||
|
)
|
||||||
|
|
||||||
|
const headerMenuItems = isMobile
|
||||||
|
? sidebarMenuItems
|
||||||
|
: showNavigationLabels
|
||||||
|
? mainMenuItems
|
||||||
|
: mainMenuItems.map((item) => ({ ...item, label: undefined }))
|
||||||
|
|
||||||
const [userPopoverOpen, setUserPopoverOpen] = useState(false)
|
const [userPopoverOpen, setUserPopoverOpen] = useState(false)
|
||||||
|
|
||||||
const userPopoverContent = (
|
const userPopoverContent = (
|
||||||
@ -174,15 +222,10 @@ const DashboardNavigation = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const pathParts = location.pathname.split('/').filter(Boolean)
|
const pathParts = location.pathname.split('/').filter(Boolean)
|
||||||
if (pathParts.length > 2) {
|
const sectionKey = pathParts[1]
|
||||||
const nextItem =
|
if (!sectionKey) return
|
||||||
mainMenuItems.find((item) => item.key == pathParts[1]) ||
|
const nextItem = mainMenuItems.find((item) => item.key == sectionKey)
|
||||||
mainMenuItems[0]
|
setSelectedKey(nextItem?.key || sectionKey)
|
||||||
if (nextItem) {
|
|
||||||
setSelectedMenuItem(nextItem)
|
|
||||||
setSelectedKey(nextItem.key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [location.pathname, mainMenuItems])
|
}, [location.pathname, mainMenuItems])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -213,6 +256,29 @@ const DashboardNavigation = () => {
|
|||||||
|
|
||||||
const showControls = !isElectron || authenticated
|
const showControls = !isElectron || authenticated
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu
|
||||||
|
mode='horizontal'
|
||||||
|
className={`${isElectron ? 'electron-navigation' : null} ${!showNavigationLabels && !isMobile ? 'no-navigation-labels' : ''}${isMobile ? 'mobile-menu' : ''}`}
|
||||||
|
items={headerMenuItems}
|
||||||
|
style={{
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
flexGrow: 1,
|
||||||
|
border: 0,
|
||||||
|
minWidth: 0
|
||||||
|
}}
|
||||||
|
onClick={isMobile ? undefined : handleMainMenuClick}
|
||||||
|
selectedKeys={[isMobile ? selectedSidebarItemKey : selectedKey]}
|
||||||
|
overflowedIndicator={
|
||||||
|
<Button
|
||||||
|
type='text'
|
||||||
|
icon={<MenuIcon />}
|
||||||
|
style={{ marginBottom: '4px' }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
const navigationContents = (
|
const navigationContents = (
|
||||||
<Flex style={{ width: '100%' }} align='center'>
|
<Flex style={{ width: '100%' }} align='center'>
|
||||||
{isMacOSApp ? <DashboardWindowButtons /> : null}
|
{isMacOSApp ? <DashboardWindowButtons /> : null}
|
||||||
@ -225,6 +291,7 @@ const DashboardNavigation = () => {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
{showControls && isMobile && menu}
|
||||||
{showDesktopLogo == true ? (
|
{showDesktopLogo == true ? (
|
||||||
<FarmControlLogo
|
<FarmControlLogo
|
||||||
style={{
|
style={{
|
||||||
@ -241,57 +308,8 @@ const DashboardNavigation = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{isMobile && (
|
|
||||||
<Flex
|
|
||||||
gap={'small'}
|
|
||||||
align='center'
|
|
||||||
style={{
|
|
||||||
marginLeft: '16px',
|
|
||||||
minWidth: 0, // allow children to shrink
|
|
||||||
maxWidth: '100%' // responsive
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{selectedMenuItem?.icon}
|
|
||||||
<ElipsisText
|
|
||||||
style={{
|
|
||||||
minWidth: 0,
|
|
||||||
maxWidth: '100%',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
overflow: 'hidden',
|
|
||||||
flex: 1
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{selectedMenuItem?.label}
|
|
||||||
</ElipsisText>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
<div style={{ flexGrow: 1 }}>
|
<div style={{ flexGrow: 1 }}>
|
||||||
{showControls && (
|
{showControls && !isMobile && menu}
|
||||||
<Menu
|
|
||||||
mode='horizontal'
|
|
||||||
className={`${isElectron ? 'electron-navigation' : null} ${!showNavigationLabels ? 'no-navigation-labels' : ''}`}
|
|
||||||
items={
|
|
||||||
showNavigationLabels
|
|
||||||
? mainMenuItems
|
|
||||||
: mainMenuItems.map((item) => ({ ...item, label: undefined }))
|
|
||||||
}
|
|
||||||
style={{
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
flexGrow: isMobile ? 0 : 1,
|
|
||||||
border: 0,
|
|
||||||
width: isMobile ? '64px' : 'unset'
|
|
||||||
}}
|
|
||||||
onClick={handleMainMenuClick}
|
|
||||||
selectedKeys={[selectedKey]}
|
|
||||||
overflowedIndicator={
|
|
||||||
<Button
|
|
||||||
type='text'
|
|
||||||
icon={<MenuIcon />}
|
|
||||||
style={{ marginBottom: '4px' }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!showControls && (
|
{!showControls && (
|
||||||
<Text
|
<Text
|
||||||
type='secondary'
|
type='secondary'
|
||||||
@ -306,7 +324,6 @@ const DashboardNavigation = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isMobile && <div style={{ flexGrow: 1 }} />}
|
|
||||||
|
|
||||||
<div className='electrobun-webkit-app-region-no-drag'>
|
<div className='electrobun-webkit-app-region-no-drag'>
|
||||||
<Flex
|
<Flex
|
||||||
@ -449,7 +466,7 @@ const DashboardNavigation = () => {
|
|||||||
align='center'
|
align='center'
|
||||||
className='ant-menu-light'
|
className='ant-menu-light'
|
||||||
style={{
|
style={{
|
||||||
padding: '0 26px',
|
padding: isMobile ? '0 12px' : '0 26px',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
borderBottom: '1px solid rgba(5, 5, 5, 0.00)'
|
borderBottom: '1px solid rgba(5, 5, 5, 0.00)'
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import {
|
|||||||
useContext
|
useContext
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { Layout, Menu, Flex, Button, Divider } from 'antd'
|
import { Layout, Menu, Flex, Button, Divider } from 'antd'
|
||||||
import { CaretDownFilled } from '@ant-design/icons'
|
|
||||||
import CollapseSidebarIcon from '../../Icons/CollapseSidebarIcon'
|
import CollapseSidebarIcon from '../../Icons/CollapseSidebarIcon'
|
||||||
import ExpandSidebarIcon from '../../Icons/ExpandSidebarIcon'
|
import ExpandSidebarIcon from '../../Icons/ExpandSidebarIcon'
|
||||||
import { useMediaQuery } from 'react-responsive'
|
import { useMediaQuery } from 'react-responsive'
|
||||||
@ -82,8 +81,6 @@ const DashboardSidebar = ({
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(item)
|
|
||||||
|
|
||||||
const icon = item.icon || getSidebarIconNode(item.iconKey)
|
const icon = item.icon || getSidebarIconNode(item.iconKey)
|
||||||
const mappedItem = {
|
const mappedItem = {
|
||||||
key: item.key,
|
key: item.key,
|
||||||
@ -114,16 +111,7 @@ const DashboardSidebar = ({
|
|||||||
const _items = mapItemsRecursively(allowedItems)
|
const _items = mapItemsRecursively(allowedItems)
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return null
|
||||||
<Menu
|
|
||||||
mode='horizontal'
|
|
||||||
selectedKeys={[selectedKey]}
|
|
||||||
items={_items}
|
|
||||||
_internalDisableMenuItemTitleTooltip
|
|
||||||
style={{ lineHeight: '40px' }}
|
|
||||||
overflowedIndicator={<Button type='text' icon={<CaretDownFilled />} />}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -158,8 +158,7 @@ const DashboardSidebarSplitter = ({ sidebar, children }) => {
|
|||||||
|
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout style={{ flex: 1, minHeight: 0, overflow: 'hidden' }}>
|
||||||
{sidebarNode}
|
|
||||||
{children}
|
{children}
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
|
|||||||
98
src/components/Dashboard/common/MobileFooterNavigation.jsx
Normal file
98
src/components/Dashboard/common/MobileFooterNavigation.jsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { useContext, useMemo } from 'react'
|
||||||
|
import { Layout } from 'antd'
|
||||||
|
import { useMediaQuery } from 'react-responsive'
|
||||||
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
import { AuthContext } from '../context/AuthContext'
|
||||||
|
import { ElectronContext } from '../context/ElectronContext'
|
||||||
|
import {
|
||||||
|
getSidebarDefaultPath,
|
||||||
|
isSidebarSectionVisible
|
||||||
|
} from '../../../database/Sidebars'
|
||||||
|
import { getSidebarIconNode } from '../../Icons/sidebarIconMap'
|
||||||
|
import SegmentedNav from './SegmentedNav'
|
||||||
|
|
||||||
|
const { Footer } = Layout
|
||||||
|
|
||||||
|
const MobileFooterNavigation = () => {
|
||||||
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
|
const { userProfile, authenticated } = useContext(AuthContext)
|
||||||
|
const { isElectron } = useContext(ElectronContext)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const location = useLocation()
|
||||||
|
const includeDev = import.meta.env.DEV
|
||||||
|
|
||||||
|
const sectionItems = useMemo(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: 'production',
|
||||||
|
label: 'Production',
|
||||||
|
iconKey: 'production'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'inventory',
|
||||||
|
label: 'Inventory',
|
||||||
|
iconKey: 'inventory'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'sales',
|
||||||
|
label: 'Sales',
|
||||||
|
iconKey: 'sales'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'finance',
|
||||||
|
label: 'Finance',
|
||||||
|
iconKey: 'finance'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'management',
|
||||||
|
label: 'Management',
|
||||||
|
iconKey: 'settings'
|
||||||
|
}
|
||||||
|
].filter((item) =>
|
||||||
|
isSidebarSectionVisible(item.key, { includeDev, userProfile })
|
||||||
|
),
|
||||||
|
[includeDev, userProfile]
|
||||||
|
)
|
||||||
|
|
||||||
|
const selectedKey = useMemo(() => {
|
||||||
|
const pathParts = location.pathname.split('/').filter(Boolean)
|
||||||
|
const sectionKey = pathParts[1]
|
||||||
|
return (
|
||||||
|
sectionItems.find((item) => item.key === sectionKey)?.key ||
|
||||||
|
sectionItems[0]?.key
|
||||||
|
)
|
||||||
|
}, [location.pathname, sectionItems])
|
||||||
|
|
||||||
|
const options = useMemo(
|
||||||
|
() =>
|
||||||
|
sectionItems.map((item) => ({
|
||||||
|
value: item.key,
|
||||||
|
label: item.label,
|
||||||
|
icon: getSidebarIconNode(item.iconKey)
|
||||||
|
})),
|
||||||
|
[sectionItems]
|
||||||
|
)
|
||||||
|
|
||||||
|
const showControls = !isElectron || authenticated
|
||||||
|
|
||||||
|
if (!isMobile || !showControls || options.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Footer className='mobile-footer-navigation ant-menu-light'>
|
||||||
|
<SegmentedNav
|
||||||
|
className='mobile-footer-navigation-tabs'
|
||||||
|
value={selectedKey}
|
||||||
|
options={options}
|
||||||
|
animated
|
||||||
|
onChange={(key) =>
|
||||||
|
navigate(getSidebarDefaultPath(key, { includeDev, userProfile }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Footer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MobileFooterNavigation
|
||||||
@ -388,7 +388,7 @@ const ObjectTable = forwardRef(
|
|||||||
const { getViewFromUrl } = useTableState()
|
const { getViewFromUrl } = useTableState()
|
||||||
var adjustedScrollHeight = scrollHeight
|
var adjustedScrollHeight = scrollHeight
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
adjustedScrollHeight = 'calc(var(--unit-100vh) - 298px)'
|
adjustedScrollHeight = 'calc(var(--unit-100vh) - 348px)'
|
||||||
}
|
}
|
||||||
if (isCards || isKanban || isTimeline) {
|
if (isCards || isKanban || isTimeline) {
|
||||||
adjustedScrollHeight = 'calc(var(--unit-100vh) - 210px)'
|
adjustedScrollHeight = 'calc(var(--unit-100vh) - 210px)'
|
||||||
|
|||||||
@ -204,6 +204,18 @@ export const ThemeProvider = ({ children }) => {
|
|||||||
'--color-text-placeholder',
|
'--color-text-placeholder',
|
||||||
isDarkMode ? '#4F4F4F' : '#BFBFBF'
|
isDarkMode ? '#4F4F4F' : '#BFBFBF'
|
||||||
)
|
)
|
||||||
|
root.style.setProperty(
|
||||||
|
'--color-mobile-footer-bg',
|
||||||
|
isDarkMode ? '#141414' : '#ffffff'
|
||||||
|
)
|
||||||
|
root.style.setProperty(
|
||||||
|
'--color-mobile-footer-item-bg',
|
||||||
|
isDarkMode ? '#1d1d1d' : '#f5f5f5'
|
||||||
|
)
|
||||||
|
root.style.setProperty(
|
||||||
|
'--color-mobile-footer-item-border',
|
||||||
|
isDarkMode ? '#303030' : '#f0f0f0'
|
||||||
|
)
|
||||||
}, [isDarkMode, primaryColorOverride])
|
}, [isDarkMode, primaryColorOverride])
|
||||||
|
|
||||||
const themeConfig = {
|
const themeConfig = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user