From 15ce7123a251297036ad515a75352a0177c11f17 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sat, 20 Jun 2026 02:32:52 +0100 Subject: [PATCH] Implemented electron view menu. --- public/mainWindow.js | 105 ++++++-- src/components/App/AppLaunch.jsx | 26 +- .../Dashboard/Developer/DeveloperSidebar.jsx | 48 +--- .../Dashboard/Finance/FinanceSidebar.jsx | 49 +--- .../Dashboard/Inventory/InventorySidebar.jsx | 116 +-------- .../Management/ManagementSidebar.jsx | 242 +----------------- .../Production/ProductionSidebar.jsx | 65 +---- .../Dashboard/Sales/SalesSidebar.jsx | 66 +---- .../Dashboard/common/DashboardNavigation.jsx | 26 +- .../Dashboard/common/DashboardSidebar.jsx | 3 +- .../Dashboard/context/ElectronContext.jsx | 13 +- src/components/Icons/sidebarIconMap.jsx | 113 ++++++++ src/database/Sidebars.js | 160 ++++++++++++ src/database/sidebars/developer.js | 22 ++ src/database/sidebars/finance.js | 23 ++ src/database/sidebars/inventory.js | 74 ++++++ src/database/sidebars/management.js | 168 ++++++++++++ src/database/sidebars/production.js | 35 +++ src/database/sidebars/sales.js | 35 +++ 19 files changed, 794 insertions(+), 595 deletions(-) create mode 100644 src/components/Icons/sidebarIconMap.jsx create mode 100644 src/database/Sidebars.js create mode 100644 src/database/sidebars/developer.js create mode 100644 src/database/sidebars/finance.js create mode 100644 src/database/sidebars/inventory.js create mode 100644 src/database/sidebars/management.js create mode 100644 src/database/sidebars/production.js create mode 100644 src/database/sidebars/sales.js diff --git a/public/mainWindow.js b/public/mainWindow.js index 4ffa913..28e5929 100644 --- a/public/mainWindow.js +++ b/public/mainWindow.js @@ -6,6 +6,7 @@ const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) let win +let sidebarViewMenuSections = [] const PROTOCOL_PREFIX = 'farmcontrol://' @@ -40,6 +41,76 @@ function sendNavigateToRenderer(redirectPath) { deliver() } +function toElectronSidebarMenuItems(items = []) { + return items + .map((item) => { + if (item?.type === 'divider') { + return { type: 'separator' } + } + + const menuItem = { + label: item.label + } + + if (item?.children && Array.isArray(item.children) && item.children.length) { + menuItem.submenu = toElectronSidebarMenuItems(item.children) + } else if (item?.path) { + menuItem.click = () => sendNavigateToRenderer(item.path) + } else { + menuItem.enabled = false + } + + return menuItem + }) + .filter(Boolean) +} + +function buildApplicationMenuTemplate() { + const env = (process.env.NODE_ENV || 'development').trim() + const viewSubmenu = sidebarViewMenuSections.map((section) => ({ + label: section.label, + submenu: toElectronSidebarMenuItems(section.items || []) + })) + + if (viewSubmenu.length === 0) { + viewSubmenu.push({ label: 'No sidebar items available', enabled: false }) + } + + if (env === 'development') { + viewSubmenu.push( + { type: 'separator' }, + { + label: 'Toggle Developer Tools', + accelerator: + process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', + click: () => { + if (win && !win.isDestroyed()) { + win.webContents.toggleDevTools() + } + } + } + ) + } + + const template = [ + { role: 'fileMenu' }, + { role: 'editMenu' }, + { label: 'View', submenu: viewSubmenu }, + { role: 'windowMenu' } + ] + + if (process.platform === 'darwin') { + template.unshift({ role: 'appMenu' }) + } + + return template +} + +function applyApplicationMenu() { + const menu = Menu.buildFromTemplate(buildApplicationMenuTemplate()) + Menu.setApplicationMenu(menu) +} + export function handleDeepLink(url) { if (!url?.startsWith(`${PROTOCOL_PREFIX}app`)) return const redirectPath = url.replace(`${PROTOCOL_PREFIX}app`, '') || '/' @@ -118,29 +189,7 @@ export function createWindow() { } }) - // Set up custom menu bar - const env = (process.env.NODE_ENV || 'development').trim() - if (env === 'development') { - const devMenu = [ - { - label: 'Developer', - submenu: [ - { - label: 'Toggle Developer Tools', - accelerator: - process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', - click: () => { - win.webContents.toggleDevTools() - } - } - ] - } - ] - const menu = Menu.buildFromTemplate(devMenu) - Menu.setApplicationMenu(menu) - } else { - Menu.setApplicationMenu(null) - } + applyApplicationMenu() // For development, load from localhost; for production, load the built index.html if (process.env.ELECTRON_START_URL) { @@ -208,6 +257,16 @@ export function setupMainWindowIPC() { } return true }) + + ipcMain.handle('set-sidebar-view-menu', (event, sidebarSections) => { + if (!Array.isArray(sidebarSections)) { + return false + } + + sidebarViewMenuSections = sidebarSections + applyApplicationMenu() + return true + }) } export function setupMainWindowAppEvents(app) { diff --git a/src/components/App/AppLaunch.jsx b/src/components/App/AppLaunch.jsx index c95b0a3..850954e 100644 --- a/src/components/App/AppLaunch.jsx +++ b/src/components/App/AppLaunch.jsx @@ -1,12 +1,13 @@ import { useContext, useEffect, useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Flex, Card, Alert } from 'antd' +import { Flex, Card, Alert, Button } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import { customAlphabet } from 'nanoid' import AuthParticles from './AppParticles' import FarmControlLogo from '../Logos/FarmControlLogo' import ExclamationOctagonIcon from '../Icons/ExclamationOctagonIcon' import CheckIcon from '../Icons/CheckIcon' +import ReloadIcon from '../Icons/ReloadIcon' import { ApiServerContext } from '../Dashboard/context/ApiServerContext' const createLaunchSession = customAlphabet( @@ -24,6 +25,10 @@ const AuthLaunch = () => { const [launchErrorMessage, setLaunchErrorMessage] = useState('') const [launchSuccess, setLaunchSuccess] = useState(false) + const handleRefresh = () => { + window.location.reload() + } + useEffect(() => { let cancelled = false const redirect = new URLSearchParams(location.search).get('redirect') @@ -168,12 +173,19 @@ const AuthLaunch = () => { /> )} {launchError && ( - } - type='error' - showIcon - /> + <> + } + type='error' + showIcon + /> +