Electron fixes and new menu icon.

This commit is contained in:
Tom Butcher 2025-08-23 19:20:59 +01:00
parent 7276633f3e
commit b1484bf127
8 changed files with 125 additions and 48 deletions

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"> <svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1.51748,0,0,1.51748,-16.5594,-16.5594)"> <g transform="matrix(1.51748,0,0,1.51748,-16.5594,-18.5104)">
<g transform="matrix(1,0,0,1,0,-1.28566)"> <g transform="matrix(0.722087,0,0,0.722087,-7.06688,4.8622)">
<g transform="matrix(0.583164,0,0,0.583164,0.722015,18.47)"> <path d="M30.756,42.557L77.418,42.557C79.178,42.557 80.569,41.138 80.569,39.379C80.569,37.627 79.168,36.169 77.418,36.169L30.756,36.169C29.016,36.169 27.637,37.647 27.637,39.379C27.637,41.118 29.006,42.557 30.756,42.557Z" style="fill-rule:nonzero;"/>
<path d="M23.983,10.233L83.255,10.233C85.015,10.233 86.406,8.815 86.406,7.055C86.406,5.304 85.005,3.845 83.255,3.845L23.983,3.845C22.244,3.845 20.864,5.324 20.864,7.055C20.864,8.795 22.234,10.233 23.983,10.233Z" style="fill-rule:nonzero;"/>
</g> </g>
<g transform="matrix(0.583164,0,0,0.583164,0.722015,21.0413)"> <g transform="matrix(1,0,0,1,0,0.343066)">
<path d="M23.983,42.557L83.255,42.557C85.015,42.557 86.406,41.138 86.406,39.379C86.406,37.627 85.005,36.169 83.255,36.169L23.983,36.169C22.244,36.169 20.864,37.647 20.864,39.379C20.864,41.118 22.234,42.557 23.983,42.557Z" style="fill-rule:nonzero;"/> <g transform="matrix(0.722087,0,0,0.722087,-7.06688,19.0175)">
<path d="M30.756,42.557L77.418,42.557C79.178,42.557 80.569,41.138 80.569,39.379C80.569,37.627 79.168,36.169 77.418,36.169L30.756,36.169C29.016,36.169 27.637,37.647 27.637,39.379C27.637,41.118 29.006,42.557 30.756,42.557Z" style="fill-rule:nonzero;"/>
</g> </g>
<g transform="matrix(0.722087,0,0,0.722087,-7.06688,-9.97925)">
<path d="M30.756,42.557L77.418,42.557C79.178,42.557 80.569,41.138 80.569,39.379C80.569,37.627 79.168,36.169 77.418,36.169L30.756,36.169C29.016,36.169 27.637,37.647 27.637,39.379C27.637,41.118 29.006,42.557 30.756,42.557Z" style="fill-rule:nonzero;"/>
</g> </g>
<g transform="matrix(0.583164,0,0,0.583164,0.722015,18.47)">
<path d="M23.983,26.395L83.255,26.395C85.015,26.395 86.406,24.966 86.406,23.207C86.406,21.456 85.005,20.007 83.255,20.007L23.983,20.007C22.244,20.007 20.864,21.485 20.864,23.207C20.864,24.947 22.234,26.395 23.983,26.395Z" style="fill-rule:nonzero;"/>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -63,10 +63,13 @@
line-height: 40px; line-height: 40px;
} }
.electron-sidebar .ant-menu-item { .electron-navigation .ant-menu-overflow-item-rest {
height: 32.5px; padding-inline: 10px;
line-height: 32.5px; }
padding-inline: calc(50% - 8px - 4px);
.electron-sidebar .ant-menu-item, .electron-sidebar .ant-menu-submenu-title {
height: 32.5px !important;
line-height: 32.5px !important;
} }
.electron-sider.ant-layout-sider-collapsed { .electron-sider.ant-layout-sider-collapsed {

Binary file not shown.

View File

@ -9,12 +9,12 @@ import {
Button, Button,
Tooltip, Tooltip,
Badge, Badge,
Divider Divider,
Typography
} from 'antd' } from 'antd'
import { import {
LogoutOutlined, LogoutOutlined,
MailOutlined, MailOutlined,
MenuOutlined,
LoadingOutlined LoadingOutlined
} from '@ant-design/icons' } from '@ant-design/icons'
import { AuthContext } from '../context/AuthContext' import { AuthContext } from '../context/AuthContext'
@ -27,6 +27,7 @@ import KeyboardShortcut from './KeyboardShortcut'
import FarmControlLogo from '../../Logos/FarmControlLogo' import FarmControlLogo from '../../Logos/FarmControlLogo'
import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall' import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall'
import MenuIcon from '../../Icons/MenuIcon'
import ProductionIcon from '../../Icons/ProductionIcon' import ProductionIcon from '../../Icons/ProductionIcon'
import InventoryIcon from '../../Icons/InventoryIcon' import InventoryIcon from '../../Icons/InventoryIcon'
import PersonIcon from '../../Icons/PersonIcon' import PersonIcon from '../../Icons/PersonIcon'
@ -38,6 +39,8 @@ import DeveloperIcon from '../../Icons/DeveloperIcon'
import { ElectronContext } from '../context/ElectronContext' import { ElectronContext } from '../context/ElectronContext'
import DashboardWindowButtons from './DashboardWindowButtons' import DashboardWindowButtons from './DashboardWindowButtons'
const { Text } = Typography
const DashboardNavigation = () => { const DashboardNavigation = () => {
const { logout, userProfile } = useContext(AuthContext) const { logout, userProfile } = useContext(AuthContext)
const { showSpotlight } = useContext(SpotlightContext) const { showSpotlight } = useContext(SpotlightContext)
@ -47,27 +50,14 @@ 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 { platform, isElectron, isFullScreen } = useContext(ElectronContext) const { platform, isElectron, isFullScreen } = useContext(ElectronContext)
useEffect(() => {
const pathParts = location.pathname.split('/').filter(Boolean)
if (pathParts.length > 2) {
setSelectedKey(pathParts[1]) // Return the section (production/management)
}
}, [location.pathname])
useEffect(() => {
if (connecting == true) {
setApiServerState('connecting')
} else if (connected == true) {
setApiServerState('connected')
} else {
setApiServerState('disconnected')
}
console.log('Connecting/connected', connecting, connected)
}, [connecting, connected])
const mainMenuItems = [ const mainMenuItems = [
{ {
key: 'production', key: 'production',
@ -115,6 +105,27 @@ const DashboardNavigation = () => {
} }
} }
useEffect(() => {
const pathParts = location.pathname.split('/').filter(Boolean)
if (pathParts.length > 2) {
setSelectedMenuItem(
mainMenuItems.filter((item) => item.key == pathParts[1])[0]
)
setSelectedKey(pathParts[1]) // Return the section (production/management)
}
}, [location.pathname])
useEffect(() => {
if (connecting == true) {
setApiServerState('connecting')
} else if (connected == true) {
setApiServerState('connected')
} else {
setApiServerState('disconnected')
}
console.log('Connecting/connected', connecting, connected)
}, [connecting, connected])
const handleMainMenuClick = ({ key }) => { const handleMainMenuClick = ({ key }) => {
if (key === 'production') { if (key === 'production') {
navigate('/dashboard/production/overview') navigate('/dashboard/production/overview')
@ -125,35 +136,91 @@ const DashboardNavigation = () => {
} }
} }
const showAppLogo =
(isElectron && platform == 'darwin' && isFullScreen == true) ||
(isElectron && platform != 'darwin')
const isMacOSApp = isElectron && platform == 'darwin'
const isOtherApp = isElectron && platform != 'darwin'
const showDesktopLogo = !isElectron && !isMobile
const showMobileLogo = !isElectron && isMobile
const navigationContents = ( const navigationContents = (
<Flex style={{ width: '100%' }} align='center'> <Flex style={{ width: '100%' }} align='center'>
{isElectron && platform == 'darwin' ? <DashboardWindowButtons /> : null} {isMacOSApp ? <DashboardWindowButtons /> : null}
{(!isElectron || (platform == 'darwin' && isFullScreen == true)) && {showAppLogo == true && (
!isMobile ? ( <FarmControlLogoSmall
style={{
fontSize: '46px',
height: '16px',
marginLeft: '15px',
marginRight: '5px'
}}
/>
)}
{showDesktopLogo == true ? (
<FarmControlLogo <FarmControlLogo
style={{ style={{
fontSize: '200px', fontSize: '200px',
height: '18px', height: '18px',
marginLeft: marginRight: '15px'
platform == 'darwin' && isFullScreen == true ? '8px' : 'unset' }}
/>
) : showMobileLogo == true ? (
<FarmControlLogoSmall
style={{
fontSize: '48px',
marginRight: isElectron ? '20px' : '25px'
}} }}
/> />
) : !isElectron && isMobile ? (
<FarmControlLogoSmall style={{ fontSize: '48px' }} />
) : null} ) : null}
{isMobile && (
<Flex
gap={'small'}
align='center'
style={{
marginLeft: '16px',
minWidth: 0, // allow children to shrink
maxWidth: '100%' // responsive
}}
>
{selectedMenuItem.icon}
<Text
ellipsis
style={{
minWidth: 0, // CRUCIAL for flex children
maxWidth: '100%', // Responsive
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
flex: 1 // Take available space, shrink as needed
}}
>
{selectedMenuItem.label}
</Text>
</Flex>
)}
<Menu <Menu
mode='horizontal' mode='horizontal'
className={isElectron ? 'electron-navigation' : null} className={isElectron ? 'electron-navigation' : null}
items={mainMenuItems} items={mainMenuItems}
style={{ style={{
flexWrap: 'wrap', flexWrap: 'wrap',
flexGrow: 1, flexGrow: isMobile ? 0 : 1,
border: 0 border: 0,
width: isMobile ? '64px' : 'unset'
}} }}
onClick={handleMainMenuClick} onClick={handleMainMenuClick}
selectedKeys={[selectedKey]} selectedKeys={[selectedKey]}
overflowedIndicator={<Button type='text' icon={<MenuOutlined />} />} overflowedIndicator={
<Button
type='text'
icon={<MenuIcon />}
style={{ marginBottom: '4px' }}
/> />
}
/>
{isMobile && <div style={{ flexGrow: 1 }} />}
<Flex gap={'middle'} align='center'> <Flex gap={'middle'} align='center'>
<Space> <Space>
<KeyboardShortcut <KeyboardShortcut
@ -235,6 +302,7 @@ const DashboardNavigation = () => {
</Space> </Space>
) : null} ) : null}
</Flex> </Flex>
{isOtherApp ? <DashboardWindowButtons /> : null}
</Flex> </Flex>
) )
@ -243,7 +311,7 @@ const DashboardNavigation = () => {
{isElectron ? ( {isElectron ? (
<Flex <Flex
className='ant-menu-horizontal ant-menu-light electron-navigation-wrapper' className='ant-menu-horizontal ant-menu-light electron-navigation-wrapper'
style={{ lineHeight: '40px', padding: '0 8px 0 4px' }} style={{ lineHeight: '40px', padding: '0 10px 0 4px' }}
> >
{navigationContents} {navigationContents}
</Flex> </Flex>

View File

@ -36,7 +36,7 @@ const DashboardWindowButtons = () => {
<Flex align='center'> <Flex align='center'>
{platform == 'darwin' ? ( {platform == 'darwin' ? (
isFullScreen == false ? ( isFullScreen == false ? (
<div style={{ width: '60px' }} /> <div style={{ width: '65px' }} />
) : null ) : null
) : ( ) : (
<> <>

View File

@ -7,6 +7,8 @@ const Flag = ({
hasDropShadow = false, hasDropShadow = false,
borderRadius = 0 borderRadius = 0
}) => { }) => {
const useRelativePath =
typeof window !== 'undefined' && window.location.href.includes('index.html')
var flagContainerStyle = {} var flagContainerStyle = {}
var flagStyle = {} var flagStyle = {}
if (hasBorder == true) { if (hasBorder == true) {
@ -31,7 +33,10 @@ const Flag = ({
} }
return ( return (
<Flex style={flagContainerStyle}> <Flex style={flagContainerStyle}>
<img src={`/flags/${size}/${code}.svg`} style={flagStyle} /> <img
src={`${useRelativePath ? '.' : ''}/flags/${size}/${code}.svg`}
style={flagStyle}
/>
</Flex> </Flex>
) )
} }

View File

@ -105,6 +105,7 @@ const AuthProvider = ({ children }) => {
} }
}, },
[ [
redirectType,
messageApi, messageApi,
openExternalUrl, openExternalUrl,
isElectron, isElectron,

View File

@ -5,9 +5,9 @@ import svgr from 'vite-plugin-svgr'
import svgo from 'vite-plugin-svgo' import svgo from 'vite-plugin-svgo'
export default defineConfig({ export default defineConfig({
base: './',
plugins: [react(), svgo(), svgr(), eslintPlugin()], plugins: [react(), svgo(), svgr(), eslintPlugin()],
build: { build: {
// to output your build into build dir the same as Webpack
outDir: 'build' outDir: 'build'
}, },
server: { server: {