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
+ />
+ }
+ onClick={handleRefresh}
+ size='large'
+ />
+ >
)}
{launchSuccess && (
🗃️,
- path: '/dashboard/developer/sessionstorage'
- },
- {
- key: 'authcontextdebug',
- label: 'Auth Debug',
- icon: 🔐,
- path: '/dashboard/developer/authcontextdebug'
- },
- {
- key: 'apicontextdebug',
- label: 'API Debug',
- icon: 🌐,
- path: '/dashboard/developer/apicontextdebug'
- }
-]
-
-const routeKeyMap = {
- '/dashboard/developer/sessionstorage': 'sessionstorage',
- '/dashboard/developer/authcontextdebug': 'authcontextdebug',
- '/dashboard/developer/apicontextdebug': 'apicontextdebug'
-}
+import { getSidebarItems, getSidebarSelectedKey } from '../../../database/Sidebars'
const DeveloperSidebar = (props) => {
const location = useLocation()
- const selectedKey = (() => {
- const match = Object.keys(routeKeyMap).find((path) => {
- const pathSplit = path.split('/')
- const locationPathSplit = location.pathname.split('/')
- if (pathSplit.length > locationPathSplit.length) return false
- for (let i = 0; i < pathSplit.length; i++) {
- if (pathSplit[i] !== locationPathSplit[i]) return false
- }
- return true
- })
- return match ? routeKeyMap[match] : 'sessionstorage'
- })()
+ const includeDev = import.meta.env.MODE === 'development'
+ const items = getSidebarItems('developer', { includeDev })
+ const selectedKey = getSidebarSelectedKey('developer', location.pathname, {
+ includeDev
+ })
return
}
diff --git a/src/components/Dashboard/Finance/FinanceSidebar.jsx b/src/components/Dashboard/Finance/FinanceSidebar.jsx
index 1c4e032..81e212a 100644
--- a/src/components/Dashboard/Finance/FinanceSidebar.jsx
+++ b/src/components/Dashboard/Finance/FinanceSidebar.jsx
@@ -1,51 +1,14 @@
import { useLocation } from 'react-router-dom'
import DashboardSidebar from '../common/DashboardSidebar'
-import InvoiceIcon from '../../Icons/InvoiceIcon'
-import PaymentIcon from '../../Icons/PaymentIcon'
-import FinanceIcon from '../../Icons/FinanceIcon'
-
-const items = [
- {
- key: 'overview',
- label: 'Overview',
- icon: ,
- path: '/dashboard/finance/overview'
- },
- { type: 'divider' },
- {
- key: 'invoices',
- label: 'Invoices',
- icon: ,
- path: '/dashboard/finance/invoices'
- },
- {
- key: 'payments',
- label: 'Payments',
- icon: ,
- path: '/dashboard/finance/payments'
- }
-]
-
-const routeKeyMap = {
- '/dashboard/finance/overview': 'overview',
- '/dashboard/finance/invoices': 'invoices',
- '/dashboard/finance/payments': 'payments'
-}
+import { getSidebarItems, getSidebarSelectedKey } from '../../../database/Sidebars'
const FinanceSidebar = (props) => {
const location = useLocation()
- const selectedKey = (() => {
- const match = Object.keys(routeKeyMap).find((path) => {
- const pathSplit = path.split('/')
- const locationPathSplit = location.pathname.split('/')
- if (pathSplit.length > locationPathSplit.length) return false
- for (let i = 0; i < pathSplit.length; i++) {
- if (pathSplit[i] !== locationPathSplit[i]) return false
- }
- return true
- })
- return match ? routeKeyMap[match] : 'overview'
- })()
+ const includeDev = import.meta.env.MODE === 'development'
+ const items = getSidebarItems('finance', { includeDev })
+ const selectedKey = getSidebarSelectedKey('finance', location.pathname, {
+ includeDev
+ })
return
}
diff --git a/src/components/Dashboard/Inventory/InventorySidebar.jsx b/src/components/Dashboard/Inventory/InventorySidebar.jsx
index 06370c4..9d3682b 100644
--- a/src/components/Dashboard/Inventory/InventorySidebar.jsx
+++ b/src/components/Dashboard/Inventory/InventorySidebar.jsx
@@ -1,118 +1,14 @@
import { useLocation } from 'react-router-dom'
import DashboardSidebar from '../common/DashboardSidebar'
-import FilamentStockIcon from '../../Icons/FilamentStockIcon'
-import PartStockIcon from '../../Icons/PartStockIcon'
-import ProductStockIcon from '../../Icons/ProductStockIcon'
-import StockEventIcon from '../../Icons/StockEventIcon'
-import StockAuditIcon from '../../Icons/StockAuditIcon'
-import PurchaseOrderIcon from '../../Icons/PurchaseOrderIcon'
-import ShipmentIcon from '../../Icons/ShipmentIcon'
-import OrderItemIcon from '../../Icons/OrderItemIcon'
-import InventoryIcon from '../../Icons/InventoryIcon'
-import StockLocationIcon from '../../Icons/StockLocationIcon'
-import StockTransferIcon from '../../Icons/StockTransferIcon'
-
-const items = [
- {
- key: 'overview',
- label: 'Overview',
- icon: ,
- path: '/dashboard/inventory/overview'
- },
- { type: 'divider' },
- {
- key: 'filamentstocks',
- label: 'Filament Stocks',
- icon: ,
- path: '/dashboard/inventory/filamentstocks'
- },
- {
- key: 'partstocks',
- label: 'Part Stocks',
- icon: ,
- path: '/dashboard/inventory/partstocks'
- },
- {
- key: 'productstocks',
- label: 'Product Stocks',
- icon: ,
- path: '/dashboard/inventory/productstocks'
- },
- { type: 'divider' },
- {
- key: 'purchaseorders',
- label: 'Purchase Orders',
- icon: ,
- path: '/dashboard/inventory/purchaseorders'
- },
- { type: 'divider' },
- {
- key: 'orderitems',
- label: 'Order Items',
- icon: ,
- path: '/dashboard/inventory/orderitems'
- },
- {
- key: 'shipments',
- label: 'Shipments',
- icon: ,
- path: '/dashboard/inventory/shipments'
- },
- { type: 'divider' },
- {
- key: 'stocklocations',
- label: 'Stock Locations',
- icon: ,
- path: '/dashboard/inventory/stocklocations'
- },
- {
- key: 'stockevents',
- label: 'Stock Events',
- icon: ,
- path: '/dashboard/inventory/stockevents'
- },
- {
- key: 'stockaudits',
- label: 'Stock Audits',
- icon: ,
- path: '/dashboard/inventory/stockaudits'
- },
- {
- key: 'stocktransfers',
- label: 'Stock Transfers',
- icon: ,
- path: '/dashboard/inventory/stocktransfers'
- }
-]
-
-const routeKeyMap = {
- '/dashboard/inventory/overview': 'overview',
- '/dashboard/inventory/filamentstocks': 'filamentstocks',
- '/dashboard/inventory/partstocks': 'partstocks',
- '/dashboard/inventory/productstocks': 'productstocks',
- '/dashboard/inventory/stocklocations': 'stocklocations',
- '/dashboard/inventory/stocktransfers': 'stocktransfers',
- '/dashboard/inventory/stockevents': 'stockevents',
- '/dashboard/inventory/stockaudits': 'stockaudits',
- '/dashboard/inventory/purchaseorders': 'purchaseorders',
- '/dashboard/inventory/orderitems': 'orderitems',
- '/dashboard/inventory/shipments': 'shipments'
-}
+import { getSidebarItems, getSidebarSelectedKey } from '../../../database/Sidebars'
const InventorySidebar = (props) => {
const location = useLocation()
- const selectedKey = (() => {
- const match = Object.keys(routeKeyMap).find((path) => {
- const pathSplit = path.split('/')
- const locationPathSplit = location.pathname.split('/')
- if (pathSplit.length > locationPathSplit.length) return false
- for (let i = 0; i < pathSplit.length; i++) {
- if (pathSplit[i] !== locationPathSplit[i]) return false
- }
- return true
- })
- return match ? routeKeyMap[match] : 'overview'
- })()
+ const includeDev = import.meta.env.MODE === 'development'
+ const items = getSidebarItems('inventory', { includeDev })
+ const selectedKey = getSidebarSelectedKey('inventory', location.pathname, {
+ includeDev
+ })
return
}
diff --git a/src/components/Dashboard/Management/ManagementSidebar.jsx b/src/components/Dashboard/Management/ManagementSidebar.jsx
index 6881be2..d4895bf 100644
--- a/src/components/Dashboard/Management/ManagementSidebar.jsx
+++ b/src/components/Dashboard/Management/ManagementSidebar.jsx
@@ -1,244 +1,14 @@
import { useLocation } from 'react-router-dom'
import DashboardSidebar from '../common/DashboardSidebar'
-import FilamentIcon from '../../Icons/FilamentIcon'
-import FilamentSkuIcon from '../../Icons/FilamentSkuIcon'
-import PartIcon from '../../Icons/PartIcon'
-import PartSkuIcon from '../../Icons/PartSkuIcon'
-import ProductIcon from '../../Icons/ProductIcon'
-import ProductCategoryIcon from '../../Icons/ProductCategoryIcon'
-import ProductSkuIcon from '../../Icons/ProductSkuIcon'
-import VendorIcon from '../../Icons/VendorIcon'
-import MaterialIcon from '../../Icons/MaterialIcon'
-import NoteTypeIcon from '../../Icons/NoteTypeIcon'
-import SettingsIcon from '../../Icons/SettingsIcon'
-import AuditLogIcon from '../../Icons/AuditLogIcon'
-import DeveloperIcon from '../../Icons/DeveloperIcon'
-import PersonIcon from '../../Icons/PersonIcon'
-import HostIcon from '../../Icons/HostIcon'
-import DocumentPrinterIcon from '../../Icons/DocumentPrinterIcon'
-import DocumentTemplateIcon from '../../Icons/DocumentTemplateIcon'
-import DocumentIcon from '../../Icons/DocumentIcon'
-import DocumentSizeIcon from '../../Icons/DocumentSizeIcon'
-import DocumentJobIcon from '../../Icons/DocumentJobIcon'
-import FileIcon from '../../Icons/FileIcon'
-import CourierIcon from '../../Icons/CourierIcon'
-import CourierServiceIcon from '../../Icons/CourierServiceIcon'
-import TaxRateIcon from '../../Icons/TaxRateIcon'
-import TaxRecordIcon from '../../Icons/TaxRecordIcon'
-import AppPasswordIcon from '../../Icons/AppPasswordIcon'
-
-const items = [
- {
- key: 'filaments',
- icon: ,
- label: 'Filaments',
- path: '/dashboard/management/filaments'
- },
- {
- key: 'filamentSkus',
- icon: ,
- label: 'Filament SKUs',
- path: '/dashboard/management/filamentskus'
- },
- {
- key: 'parts',
- icon: ,
- label: 'Parts',
- path: '/dashboard/management/parts'
- },
- {
- key: 'partSkus',
- icon: ,
- label: 'Part SKUs',
- path: '/dashboard/management/partskus'
- },
- {
- key: 'products',
- icon: ,
- label: 'Products',
- path: '/dashboard/management/products'
- },
- {
- key: 'productCategories',
- icon: ,
- label: 'Product Categories',
- path: '/dashboard/management/productcategories'
- },
- {
- key: 'productSkus',
- icon: ,
- label: 'Product SKUs',
- path: '/dashboard/management/productskus'
- },
- {
- key: 'vendors',
- icon: ,
- label: 'Vendors',
- path: '/dashboard/management/vendors'
- },
- {
- key: 'materials',
- icon: ,
- label: 'Materials',
- path: '/dashboard/management/materials'
- },
- { type: 'divider' },
- {
- key: 'couriers',
- icon: ,
- label: 'Couriers',
- path: '/dashboard/management/couriers'
- },
- {
- key: 'courierServices',
- icon: ,
- label: 'Courier Services',
- path: '/dashboard/management/courierservices'
- },
- { type: 'divider' },
- {
- key: 'taxRates',
- icon: ,
- label: 'Tax Rates',
- path: '/dashboard/management/taxrates'
- },
- {
- key: 'taxRecords',
- icon: ,
- label: 'Tax Records',
- path: '/dashboard/management/taxrecords'
- },
- { type: 'divider' },
- {
- key: 'noteTypes',
- icon: ,
- label: 'Note Types',
- path: '/dashboard/management/notetypes'
- },
- {
- key: 'documents',
- icon: ,
- label: 'Documents',
- children: [
- {
- key: 'documentPrinters',
- icon: ,
- label: 'Document Printers',
- path: '/dashboard/management/documentprinters'
- },
- {
- key: 'documentJobs',
- icon: ,
- label: 'Document Jobs',
- path: '/dashboard/management/documentjobs'
- },
- {
- key: 'documentTemplates',
- icon: ,
- label: 'Document Templates',
- path: '/dashboard/management/documenttemplates'
- },
- {
- key: 'documentSizes',
- icon: ,
- label: 'Document Sizes',
- path: '/dashboard/management/documentsizes'
- }
- ]
- },
- { type: 'divider' },
- {
- key: 'hosts',
- icon: ,
- label: 'Hosts',
- path: '/dashboard/management/hosts'
- },
- { type: 'divider' },
- {
- key: 'users',
- icon: ,
- label: 'Users',
- path: '/dashboard/management/users'
- },
- {
- key: 'appPasswords',
- icon: ,
- label: 'App Passwords',
- path: '/dashboard/management/apppasswords'
- },
- {
- key: 'settings',
- icon: ,
- label: 'Settings',
- path: '/dashboard/management/settings'
- },
- {
- key: 'files',
- icon: ,
- label: 'Files',
- path: '/dashboard/management/files'
- },
- {
- key: 'auditLogs',
- icon: ,
- label: 'Audit Logs',
- path: '/dashboard/management/auditlogs'
- }
-]
-
-if (import.meta.env.MODE === 'development') {
- items.push(
- { type: 'divider' },
- {
- key: 'developer',
- icon: ,
- label: 'Developer',
- path: '/dashboard/developer/sessionstorage'
- }
- )
-}
-
-const routeKeyMap = {
- '/dashboard/management/filaments': 'filaments',
- '/dashboard/management/filamentskus': 'filamentSkus',
- '/dashboard/management/parts': 'parts',
- '/dashboard/management/partskus': 'partSkus',
- '/dashboard/management/users': 'users',
- '/dashboard/management/apppasswords': 'appPasswords',
- '/dashboard/management/products': 'products',
- '/dashboard/management/productcategories': 'productCategories',
- '/dashboard/management/productskus': 'productSkus',
- '/dashboard/management/vendors': 'vendors',
- '/dashboard/management/couriers': 'couriers',
- '/dashboard/management/courierservices': 'courierServices',
- '/dashboard/management/taxrates': 'taxRates',
- '/dashboard/management/taxrecords': 'taxRecords',
- '/dashboard/management/materials': 'materials',
- '/dashboard/management/notetypes': 'noteTypes',
- '/dashboard/management/settings': 'settings',
- '/dashboard/management/auditlogs': 'auditLogs',
- '/dashboard/management/files': 'files',
- '/dashboard/management/hosts': 'hosts',
- '/dashboard/management/documentsizes': 'documentSizes',
- '/dashboard/management/documentprinters': 'documentPrinters',
- '/dashboard/management/documenttemplates': 'documentTemplates',
- '/dashboard/management/documentjobs': 'documentJobs'
-}
+import { getSidebarItems, getSidebarSelectedKey } from '../../../database/Sidebars'
const ManagementSidebar = (props) => {
const location = useLocation()
- const selectedKey = (() => {
- const match = Object.keys(routeKeyMap).find((path) => {
- const pathSplit = path.split('/')
- const locationPathSplit = location.pathname.split('/')
- if (pathSplit.length > locationPathSplit.length) return false
- for (let i = 0; i < pathSplit.length; i++) {
- if (pathSplit[i] !== locationPathSplit[i]) return false
- }
- return true
- })
- return match ? routeKeyMap[match] : 'filaments'
- })()
+ const includeDev = import.meta.env.MODE === 'development'
+ const items = getSidebarItems('management', { includeDev })
+ const selectedKey = getSidebarSelectedKey('management', location.pathname, {
+ includeDev
+ })
return
}
diff --git a/src/components/Dashboard/Production/ProductionSidebar.jsx b/src/components/Dashboard/Production/ProductionSidebar.jsx
index 871b0ee..005ad3a 100644
--- a/src/components/Dashboard/Production/ProductionSidebar.jsx
+++ b/src/components/Dashboard/Production/ProductionSidebar.jsx
@@ -1,67 +1,14 @@
import { useLocation } from 'react-router-dom'
import DashboardSidebar from '../common/DashboardSidebar'
-import ProductionIcon from '../../Icons/ProductionIcon'
-import PrinterIcon from '../../Icons/PrinterIcon'
-import JobIcon from '../../Icons/JobIcon'
-import GCodeFileIcon from '../../Icons/GCodeFileIcon'
-import SubJobIcon from '../../Icons/SubJobIcon'
-
-const items = [
- {
- key: 'overview',
- icon: ,
- label: 'Overview',
- path: '/dashboard/production/overview'
- },
- { type: 'divider' },
- {
- key: 'printers',
- icon: ,
- label: 'Printers',
- path: '/dashboard/production/printers'
- },
- {
- key: 'jobs',
- icon: ,
- label: 'Jobs',
- path: '/dashboard/production/jobs'
- },
- {
- key: 'subJobs',
- icon: ,
- label: 'Sub Jobs',
- path: '/dashboard/production/subjobs'
- },
- {
- key: 'gcodeFiles',
- icon: ,
- label: 'GCode Files',
- path: '/dashboard/production/gcodefiles'
- }
-]
-
-const routeKeyMap = {
- '/dashboard/production/overview': 'overview',
- '/dashboard/production/printers': 'printers',
- '/dashboard/production/jobs': 'jobs',
- '/dashboard/production/subjobs': 'subJobs',
- '/dashboard/production/gcodefiles': 'gcodeFiles'
-}
+import { getSidebarItems, getSidebarSelectedKey } from '../../../database/Sidebars'
const ProductionSidebar = (props) => {
const location = useLocation()
- const selectedKey = (() => {
- const match = Object.keys(routeKeyMap).find((path) => {
- const pathSplit = path.split('/')
- const locationPathSplit = location.pathname.split('/')
- if (pathSplit.length > locationPathSplit.length) return false
- for (let i = 0; i < pathSplit.length; i++) {
- if (pathSplit[i] !== locationPathSplit[i]) return false
- }
- return true
- })
- return match ? routeKeyMap[match] : 'overview'
- })()
+ const includeDev = import.meta.env.MODE === 'development'
+ const items = getSidebarItems('production', { includeDev })
+ const selectedKey = getSidebarSelectedKey('production', location.pathname, {
+ includeDev
+ })
return
}
diff --git a/src/components/Dashboard/Sales/SalesSidebar.jsx b/src/components/Dashboard/Sales/SalesSidebar.jsx
index b0ef781..fe13911 100644
--- a/src/components/Dashboard/Sales/SalesSidebar.jsx
+++ b/src/components/Dashboard/Sales/SalesSidebar.jsx
@@ -1,68 +1,14 @@
import { useLocation } from 'react-router-dom'
import DashboardSidebar from '../common/DashboardSidebar'
-import ClientIcon from '../../Icons/ClientIcon'
-import SalesIcon from '../../Icons/SalesIcon'
-import SalesOrderIcon from '../../Icons/SalesOrderIcon'
-import MarketplaceIcon from '../../Icons/MarketplaceIcon'
-import ListingIcon from '../../Icons/ListingIcon'
-
-const items = [
- {
- key: 'overview',
- label: 'Overview',
- icon: ,
- path: '/dashboard/sales/overview'
- },
- { type: 'divider' },
- {
- key: 'clients',
- label: 'Clients',
- icon: ,
- path: '/dashboard/sales/clients'
- },
- {
- key: 'salesorders',
- label: 'Sales Orders',
- icon: ,
- path: '/dashboard/sales/salesorders'
- },
- {
- key: 'marketplaces',
- label: 'Marketplaces',
- icon: ,
- path: '/dashboard/sales/marketplaces'
- },
- {
- key: 'listings',
- label: 'Listings',
- icon: ,
- path: '/dashboard/sales/listings'
- }
-]
-
-const routeKeyMap = {
- '/dashboard/sales/overview': 'overview',
- '/dashboard/sales/clients': 'clients',
- '/dashboard/sales/salesorders': 'salesorders',
- '/dashboard/sales/marketplaces': 'marketplaces',
- '/dashboard/sales/listings': 'listings',
- '/dashboard/sales/listingvarients': 'listings'
-}
+import { getSidebarItems, getSidebarSelectedKey } from '../../../database/Sidebars'
const SalesSidebar = (props) => {
const location = useLocation()
- const selectedKey = (() => {
- const match = Object.keys(routeKeyMap).find((path) => {
- const pathSplit = path.split('/')
- const locationPathSplit = location.pathname.split('/')
- if (pathSplit.length > locationPathSplit.length) return false
- for (let i = 0; i < pathSplit.length; i++) {
- if (pathSplit[i] !== locationPathSplit[i]) return false
- }
- return true
- })
- return match ? routeKeyMap[match] : 'overview'
- })()
+ const includeDev = import.meta.env.MODE === 'development'
+ const items = getSidebarItems('sales', { includeDev })
+ const selectedKey = getSidebarSelectedKey('sales', location.pathname, {
+ includeDev
+ })
return
}
diff --git a/src/components/Dashboard/common/DashboardNavigation.jsx b/src/components/Dashboard/common/DashboardNavigation.jsx
index aa4021e..79a4e4a 100644
--- a/src/components/Dashboard/common/DashboardNavigation.jsx
+++ b/src/components/Dashboard/common/DashboardNavigation.jsx
@@ -38,6 +38,10 @@ import SettingsIcon from '../../Icons/SettingsIcon'
import DeveloperIcon from '../../Icons/DeveloperIcon'
import { ElectronContext } from '../context/ElectronContext'
import DashboardWindowButtons from './DashboardWindowButtons'
+import {
+ getSidebarDefaultPath,
+ getSidebarMenuSections
+} from '../../../database/Sidebars'
const { Text } = Typography
@@ -57,7 +61,8 @@ const DashboardNavigation = () => {
icon:
})
const isMobile = useMediaQuery({ maxWidth: 768 })
- const { platform, isElectron, isFullScreen } = useContext(ElectronContext)
+ const { platform, isElectron, isFullScreen, setSidebarViewMenu } =
+ useContext(ElectronContext)
const mainMenuItems = useMemo(
() => [
@@ -117,19 +122,16 @@ const DashboardNavigation = () => {
}, [connecting, connected])
const handleMainMenuClick = ({ key }) => {
- if (key === 'production') {
- navigate('/dashboard/production/overview')
- } else if (key === 'inventory') {
- navigate('/dashboard/inventory/overview')
- } else if (key === 'finance') {
- navigate('/dashboard/finance/overview')
- } else if (key === 'sales') {
- navigate('/dashboard/sales/overview')
- } else if (key === 'management') {
- navigate('/dashboard/management/filaments')
- }
+ navigate(getSidebarDefaultPath(key, { includeDev: import.meta.env.DEV }))
}
+ useEffect(() => {
+ if (!isElectron || !setSidebarViewMenu) return
+ const includeDev = import.meta.env.DEV
+ const sections = getSidebarMenuSections({ includeDev })
+ setSidebarViewMenu(sections)
+ }, [isElectron, setSidebarViewMenu])
+
const showAppLogo =
(isElectron && platform == 'darwin' && isFullScreen == true) ||
(isElectron && platform != 'darwin')
diff --git a/src/components/Dashboard/common/DashboardSidebar.jsx b/src/components/Dashboard/common/DashboardSidebar.jsx
index b4f40eb..0ebf0a6 100644
--- a/src/components/Dashboard/common/DashboardSidebar.jsx
+++ b/src/components/Dashboard/common/DashboardSidebar.jsx
@@ -7,6 +7,7 @@ import { useMediaQuery } from 'react-responsive'
import { useNavigate } from 'react-router-dom'
import PropTypes from 'prop-types'
import { ElectronContext } from '../context/ElectronContext'
+import { getSidebarIconNode } from '../../Icons/sidebarIconMap'
const { Sider } = Layout
const DashboardSidebar = ({
@@ -47,7 +48,7 @@ const DashboardSidebar = ({
const mappedItem = {
key: item.key,
- icon: item.icon,
+ icon: item.icon || getSidebarIconNode(item.iconKey),
label: item.label
}
diff --git a/src/components/Dashboard/context/ElectronContext.jsx b/src/components/Dashboard/context/ElectronContext.jsx
index 95b0afa..c58328b 100644
--- a/src/components/Dashboard/context/ElectronContext.jsx
+++ b/src/components/Dashboard/context/ElectronContext.jsx
@@ -1,4 +1,4 @@
-import { createContext, useEffect, useState } from 'react'
+import { createContext, useCallback, useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import { useNavigate } from 'react-router-dom'
@@ -144,6 +144,14 @@ const ElectronProvider = ({ children }) => {
}
}
+ const setSidebarViewMenu = useCallback(
+ async (sections) => {
+ if (!electronAvailable || !ipcRenderer) return false
+ return await ipcRenderer.invoke('set-sidebar-view-menu', sections)
+ },
+ [electronAvailable]
+ )
+
return (
{
clearAuthSession,
getToken,
setToken,
- resizeSpotlightWindow
+ resizeSpotlightWindow,
+ setSidebarViewMenu
}}
>
{children}
diff --git a/src/components/Icons/sidebarIconMap.jsx b/src/components/Icons/sidebarIconMap.jsx
new file mode 100644
index 0000000..a641cef
--- /dev/null
+++ b/src/components/Icons/sidebarIconMap.jsx
@@ -0,0 +1,113 @@
+import ProductionIcon from './ProductionIcon'
+import PrinterIcon from './PrinterIcon'
+import JobIcon from './JobIcon'
+import GCodeFileIcon from './GCodeFileIcon'
+import SubJobIcon from './SubJobIcon'
+import InventoryIcon from './InventoryIcon'
+import FilamentStockIcon from './FilamentStockIcon'
+import PartStockIcon from './PartStockIcon'
+import ProductStockIcon from './ProductStockIcon'
+import StockEventIcon from './StockEventIcon'
+import StockAuditIcon from './StockAuditIcon'
+import PurchaseOrderIcon from './PurchaseOrderIcon'
+import ShipmentIcon from './ShipmentIcon'
+import OrderItemIcon from './OrderItemIcon'
+import StockLocationIcon from './StockLocationIcon'
+import StockTransferIcon from './StockTransferIcon'
+import SalesIcon from './SalesIcon'
+import ClientIcon from './ClientIcon'
+import SalesOrderIcon from './SalesOrderIcon'
+import MarketplaceIcon from './MarketplaceIcon'
+import ListingIcon from './ListingIcon'
+import FinanceIcon from './FinanceIcon'
+import InvoiceIcon from './InvoiceIcon'
+import PaymentIcon from './PaymentIcon'
+import FilamentIcon from './FilamentIcon'
+import FilamentSkuIcon from './FilamentSkuIcon'
+import PartIcon from './PartIcon'
+import PartSkuIcon from './PartSkuIcon'
+import ProductIcon from './ProductIcon'
+import ProductCategoryIcon from './ProductCategoryIcon'
+import ProductSkuIcon from './ProductSkuIcon'
+import VendorIcon from './VendorIcon'
+import MaterialIcon from './MaterialIcon'
+import NoteTypeIcon from './NoteTypeIcon'
+import SettingsIcon from './SettingsIcon'
+import AuditLogIcon from './AuditLogIcon'
+import DeveloperIcon from './DeveloperIcon'
+import PersonIcon from './PersonIcon'
+import HostIcon from './HostIcon'
+import DocumentPrinterIcon from './DocumentPrinterIcon'
+import DocumentTemplateIcon from './DocumentTemplateIcon'
+import DocumentIcon from './DocumentIcon'
+import DocumentSizeIcon from './DocumentSizeIcon'
+import DocumentJobIcon from './DocumentJobIcon'
+import FileIcon from './FileIcon'
+import CourierIcon from './CourierIcon'
+import CourierServiceIcon from './CourierServiceIcon'
+import TaxRateIcon from './TaxRateIcon'
+import TaxRecordIcon from './TaxRecordIcon'
+import AppPasswordIcon from './AppPasswordIcon'
+
+const toEmoji = (emoji) => {emoji}
+
+const sidebarIconMap = {
+ production: ,
+ printer: ,
+ job: ,
+ subJob: ,
+ gcodeFile: ,
+ inventory: ,
+ filamentStock: ,
+ partStock: ,
+ productStock: ,
+ stockEvent: ,
+ stockAudit: ,
+ purchaseOrder: ,
+ orderItem: ,
+ shipment: ,
+ stockLocation: ,
+ stockTransfer: ,
+ sales: ,
+ client: ,
+ salesOrder: ,
+ marketplace: ,
+ listing: ,
+ finance: ,
+ invoice: ,
+ payment: ,
+ filament: ,
+ filamentSku: ,
+ part: ,
+ partSku: ,
+ product: ,
+ productCategory: ,
+ productSku: ,
+ vendor: ,
+ material: ,
+ noteType: ,
+ settings: ,
+ auditLog: ,
+ developer: ,
+ person: ,
+ host: ,
+ documentPrinter: ,
+ documentTemplate: ,
+ document: ,
+ documentSize: ,
+ documentJob: ,
+ file: ,
+ courier: ,
+ courierService: ,
+ taxRate: ,
+ taxRecord: ,
+ appPassword: ,
+ sessionStorage: toEmoji('🗃️'),
+ authDebug: toEmoji('🔐'),
+ apiDebug: toEmoji('🌐')
+}
+
+export const getSidebarIconNode = (iconKey) => {
+ if (!iconKey) return null
+ return sidebarIconMap[iconKey] || null
+}
diff --git a/src/database/Sidebars.js b/src/database/Sidebars.js
new file mode 100644
index 0000000..7360fdb
--- /dev/null
+++ b/src/database/Sidebars.js
@@ -0,0 +1,160 @@
+import productionSidebarItems from './sidebars/production'
+import inventorySidebarItems from './sidebars/inventory'
+import salesSidebarItems from './sidebars/sales'
+import financeSidebarItems from './sidebars/finance'
+import managementSidebarItems from './sidebars/management'
+import developerSidebarItems from './sidebars/developer'
+
+const SIDEBARS = {
+ production: {
+ key: 'production',
+ label: 'Production',
+ iconKey: 'production',
+ items: productionSidebarItems
+ },
+ inventory: {
+ key: 'inventory',
+ label: 'Inventory',
+ iconKey: 'inventory',
+ items: inventorySidebarItems
+ },
+ sales: {
+ key: 'sales',
+ label: 'Sales',
+ iconKey: 'sales',
+ items: salesSidebarItems,
+ routeAliases: {
+ '/dashboard/sales/listingvarients': 'listings'
+ }
+ },
+ finance: {
+ key: 'finance',
+ label: 'Finance',
+ iconKey: 'finance',
+ items: financeSidebarItems
+ },
+ management: {
+ key: 'management',
+ label: 'Management',
+ iconKey: 'settings',
+ items: managementSidebarItems
+ },
+ developer: {
+ key: 'developer',
+ label: 'Developer',
+ iconKey: 'developer',
+ items: developerSidebarItems
+ }
+}
+
+const splitPath = (path) => path.split('/').filter(Boolean)
+
+const isPathPrefixMatch = (candidatePath, pathname) => {
+ const candidateParts = splitPath(candidatePath)
+ const pathParts = splitPath(pathname)
+ if (candidateParts.length > pathParts.length) return false
+ for (let i = 0; i < candidateParts.length; i++) {
+ if (candidateParts[i] !== pathParts[i]) return false
+ }
+ return true
+}
+
+const getFilteredItems = (items = [], includeDev = false) =>
+ items
+ .filter((item) => item?.devOnly !== true || includeDev)
+ .map((item) => {
+ if (item?.children && Array.isArray(item.children)) {
+ return { ...item, children: getFilteredItems(item.children, includeDev) }
+ }
+ return { ...item }
+ })
+
+const flattenPathEntries = (items = [], acc = []) => {
+ items.forEach((item) => {
+ if (item?.type === 'divider') return
+ if (item?.path) {
+ acc.push({ path: item.path, key: item.key })
+ }
+ if (item?.children && Array.isArray(item.children)) {
+ flattenPathEntries(item.children, acc)
+ }
+ })
+ return acc
+}
+
+const findDefaultKey = (items = []) => {
+ for (const item of items) {
+ if (item?.type === 'divider') continue
+ if (item?.children && item.children.length > 0) {
+ const childKey = findDefaultKey(item.children)
+ if (childKey) return childKey
+ }
+ if (item?.key) return item.key
+ }
+ return ''
+}
+
+const toMenuItems = (items = []) =>
+ items.map((item) => {
+ if (item?.type === 'divider') return { type: 'divider' }
+
+ const menuItem = {
+ key: item.key,
+ label: item.label,
+ iconKey: item.iconKey
+ }
+
+ if (item?.path) {
+ menuItem.path = item.path
+ }
+
+ if (item?.children && Array.isArray(item.children)) {
+ menuItem.children = toMenuItems(item.children)
+ }
+
+ return menuItem
+ })
+
+export const getSidebar = (sidebarKey) => SIDEBARS[sidebarKey] || null
+
+export const getSidebarItems = (sidebarKey, options = {}) => {
+ const sidebar = getSidebar(sidebarKey)
+ if (!sidebar) return []
+ const { includeDev = false } = options
+ return getFilteredItems(sidebar.items, includeDev)
+}
+
+export const getSidebarSelectedKey = (sidebarKey, pathname, options = {}) => {
+ const sidebar = getSidebar(sidebarKey)
+ if (!sidebar) return ''
+
+ const items = getSidebarItems(sidebarKey, options)
+ const pathEntries = flattenPathEntries(items)
+ const aliasEntries = Object.entries(sidebar.routeAliases || {}).map(
+ ([path, key]) => ({ path, key })
+ )
+
+ const match = [...pathEntries, ...aliasEntries]
+ .filter((entry) => isPathPrefixMatch(entry.path, pathname))
+ .sort((a, b) => splitPath(b.path).length - splitPath(a.path).length)[0]
+
+ return match?.key || findDefaultKey(items)
+}
+
+export const getSidebarMenuSections = (options = {}) => {
+ const { includeDev = false } = options
+ return Object.values(SIDEBARS)
+ .filter((sidebar) => includeDev || sidebar.key !== 'developer')
+ .map((sidebar) => ({
+ key: sidebar.key,
+ label: sidebar.label,
+ iconKey: sidebar.iconKey,
+ items: toMenuItems(getSidebarItems(sidebar.key, { includeDev }))
+ }))
+}
+
+export const getSidebarDefaultPath = (sidebarKey, options = {}) => {
+ const items = getSidebarItems(sidebarKey, options)
+ const first = flattenPathEntries(items)[0]
+ return first?.path || '/dashboard/production/overview'
+}
diff --git a/src/database/sidebars/developer.js b/src/database/sidebars/developer.js
new file mode 100644
index 0000000..cea2a37
--- /dev/null
+++ b/src/database/sidebars/developer.js
@@ -0,0 +1,22 @@
+const developerSidebarItems = [
+ {
+ key: 'sessionstorage',
+ label: 'Session Storage',
+ iconKey: 'sessionStorage',
+ path: '/dashboard/developer/sessionstorage'
+ },
+ {
+ key: 'authcontextdebug',
+ label: 'Auth Debug',
+ iconKey: 'authDebug',
+ path: '/dashboard/developer/authcontextdebug'
+ },
+ {
+ key: 'apicontextdebug',
+ label: 'API Debug',
+ iconKey: 'apiDebug',
+ path: '/dashboard/developer/apicontextdebug'
+ }
+]
+
+export default developerSidebarItems
diff --git a/src/database/sidebars/finance.js b/src/database/sidebars/finance.js
new file mode 100644
index 0000000..ac00605
--- /dev/null
+++ b/src/database/sidebars/finance.js
@@ -0,0 +1,23 @@
+const financeSidebarItems = [
+ {
+ key: 'overview',
+ label: 'Overview',
+ iconKey: 'finance',
+ path: '/dashboard/finance/overview'
+ },
+ { type: 'divider' },
+ {
+ key: 'invoices',
+ label: 'Invoices',
+ iconKey: 'invoice',
+ path: '/dashboard/finance/invoices'
+ },
+ {
+ key: 'payments',
+ label: 'Payments',
+ iconKey: 'payment',
+ path: '/dashboard/finance/payments'
+ }
+]
+
+export default financeSidebarItems
diff --git a/src/database/sidebars/inventory.js b/src/database/sidebars/inventory.js
new file mode 100644
index 0000000..0a5e14c
--- /dev/null
+++ b/src/database/sidebars/inventory.js
@@ -0,0 +1,74 @@
+const inventorySidebarItems = [
+ {
+ key: 'overview',
+ label: 'Overview',
+ iconKey: 'inventory',
+ path: '/dashboard/inventory/overview'
+ },
+ { type: 'divider' },
+ {
+ key: 'filamentstocks',
+ label: 'Filament Stocks',
+ iconKey: 'filamentStock',
+ path: '/dashboard/inventory/filamentstocks'
+ },
+ {
+ key: 'partstocks',
+ label: 'Part Stocks',
+ iconKey: 'partStock',
+ path: '/dashboard/inventory/partstocks'
+ },
+ {
+ key: 'productstocks',
+ label: 'Product Stocks',
+ iconKey: 'productStock',
+ path: '/dashboard/inventory/productstocks'
+ },
+ { type: 'divider' },
+ {
+ key: 'purchaseorders',
+ label: 'Purchase Orders',
+ iconKey: 'purchaseOrder',
+ path: '/dashboard/inventory/purchaseorders'
+ },
+ { type: 'divider' },
+ {
+ key: 'orderitems',
+ label: 'Order Items',
+ iconKey: 'orderItem',
+ path: '/dashboard/inventory/orderitems'
+ },
+ {
+ key: 'shipments',
+ label: 'Shipments',
+ iconKey: 'shipment',
+ path: '/dashboard/inventory/shipments'
+ },
+ { type: 'divider' },
+ {
+ key: 'stocklocations',
+ label: 'Stock Locations',
+ iconKey: 'stockLocation',
+ path: '/dashboard/inventory/stocklocations'
+ },
+ {
+ key: 'stockevents',
+ label: 'Stock Events',
+ iconKey: 'stockEvent',
+ path: '/dashboard/inventory/stockevents'
+ },
+ {
+ key: 'stockaudits',
+ label: 'Stock Audits',
+ iconKey: 'stockAudit',
+ path: '/dashboard/inventory/stockaudits'
+ },
+ {
+ key: 'stocktransfers',
+ label: 'Stock Transfers',
+ iconKey: 'stockTransfer',
+ path: '/dashboard/inventory/stocktransfers'
+ }
+]
+
+export default inventorySidebarItems
diff --git a/src/database/sidebars/management.js b/src/database/sidebars/management.js
new file mode 100644
index 0000000..c2c0b77
--- /dev/null
+++ b/src/database/sidebars/management.js
@@ -0,0 +1,168 @@
+const managementSidebarItems = [
+ {
+ key: 'filaments',
+ iconKey: 'filament',
+ label: 'Filaments',
+ path: '/dashboard/management/filaments'
+ },
+ {
+ key: 'filamentSkus',
+ iconKey: 'filamentSku',
+ label: 'Filament SKUs',
+ path: '/dashboard/management/filamentskus'
+ },
+ {
+ key: 'parts',
+ iconKey: 'part',
+ label: 'Parts',
+ path: '/dashboard/management/parts'
+ },
+ {
+ key: 'partSkus',
+ iconKey: 'partSku',
+ label: 'Part SKUs',
+ path: '/dashboard/management/partskus'
+ },
+ {
+ key: 'products',
+ iconKey: 'product',
+ label: 'Products',
+ path: '/dashboard/management/products'
+ },
+ {
+ key: 'productCategories',
+ iconKey: 'productCategory',
+ label: 'Product Categories',
+ path: '/dashboard/management/productcategories'
+ },
+ {
+ key: 'productSkus',
+ iconKey: 'productSku',
+ label: 'Product SKUs',
+ path: '/dashboard/management/productskus'
+ },
+ {
+ key: 'vendors',
+ iconKey: 'vendor',
+ label: 'Vendors',
+ path: '/dashboard/management/vendors'
+ },
+ {
+ key: 'materials',
+ iconKey: 'material',
+ label: 'Materials',
+ path: '/dashboard/management/materials'
+ },
+ { type: 'divider' },
+ {
+ key: 'couriers',
+ iconKey: 'courier',
+ label: 'Couriers',
+ path: '/dashboard/management/couriers'
+ },
+ {
+ key: 'courierServices',
+ iconKey: 'courierService',
+ label: 'Courier Services',
+ path: '/dashboard/management/courierservices'
+ },
+ { type: 'divider' },
+ {
+ key: 'taxRates',
+ iconKey: 'taxRate',
+ label: 'Tax Rates',
+ path: '/dashboard/management/taxrates'
+ },
+ {
+ key: 'taxRecords',
+ iconKey: 'taxRecord',
+ label: 'Tax Records',
+ path: '/dashboard/management/taxrecords'
+ },
+ { type: 'divider' },
+ {
+ key: 'noteTypes',
+ iconKey: 'noteType',
+ label: 'Note Types',
+ path: '/dashboard/management/notetypes'
+ },
+ {
+ key: 'documents',
+ iconKey: 'document',
+ label: 'Documents',
+ children: [
+ {
+ key: 'documentPrinters',
+ iconKey: 'documentPrinter',
+ label: 'Document Printers',
+ path: '/dashboard/management/documentprinters'
+ },
+ {
+ key: 'documentJobs',
+ iconKey: 'documentJob',
+ label: 'Document Jobs',
+ path: '/dashboard/management/documentjobs'
+ },
+ {
+ key: 'documentTemplates',
+ iconKey: 'documentTemplate',
+ label: 'Document Templates',
+ path: '/dashboard/management/documenttemplates'
+ },
+ {
+ key: 'documentSizes',
+ iconKey: 'documentSize',
+ label: 'Document Sizes',
+ path: '/dashboard/management/documentsizes'
+ }
+ ]
+ },
+ { type: 'divider' },
+ {
+ key: 'hosts',
+ iconKey: 'host',
+ label: 'Hosts',
+ path: '/dashboard/management/hosts'
+ },
+ { type: 'divider' },
+ {
+ key: 'users',
+ iconKey: 'person',
+ label: 'Users',
+ path: '/dashboard/management/users'
+ },
+ {
+ key: 'appPasswords',
+ iconKey: 'appPassword',
+ label: 'App Passwords',
+ path: '/dashboard/management/apppasswords'
+ },
+ {
+ key: 'settings',
+ iconKey: 'settings',
+ label: 'Settings',
+ path: '/dashboard/management/settings'
+ },
+ {
+ key: 'files',
+ iconKey: 'file',
+ label: 'Files',
+ path: '/dashboard/management/files'
+ },
+ {
+ key: 'auditLogs',
+ iconKey: 'auditLog',
+ label: 'Audit Logs',
+ path: '/dashboard/management/auditlogs'
+ },
+ { type: 'divider' },
+ {
+ key: 'developer',
+ iconKey: 'developer',
+ label: 'Developer',
+ path: '/dashboard/developer/sessionstorage',
+ devOnly: true
+ }
+]
+
+export default managementSidebarItems
diff --git a/src/database/sidebars/production.js b/src/database/sidebars/production.js
new file mode 100644
index 0000000..7dd6d4f
--- /dev/null
+++ b/src/database/sidebars/production.js
@@ -0,0 +1,35 @@
+const productionSidebarItems = [
+ {
+ key: 'overview',
+ iconKey: 'production',
+ label: 'Overview',
+ path: '/dashboard/production/overview'
+ },
+ { type: 'divider' },
+ {
+ key: 'printers',
+ iconKey: 'printer',
+ label: 'Printers',
+ path: '/dashboard/production/printers'
+ },
+ {
+ key: 'jobs',
+ iconKey: 'job',
+ label: 'Jobs',
+ path: '/dashboard/production/jobs'
+ },
+ {
+ key: 'subJobs',
+ iconKey: 'subJob',
+ label: 'Sub Jobs',
+ path: '/dashboard/production/subjobs'
+ },
+ {
+ key: 'gcodeFiles',
+ iconKey: 'gcodeFile',
+ label: 'GCode Files',
+ path: '/dashboard/production/gcodefiles'
+ }
+]
+
+export default productionSidebarItems
diff --git a/src/database/sidebars/sales.js b/src/database/sidebars/sales.js
new file mode 100644
index 0000000..9df640a
--- /dev/null
+++ b/src/database/sidebars/sales.js
@@ -0,0 +1,35 @@
+const salesSidebarItems = [
+ {
+ key: 'overview',
+ label: 'Overview',
+ iconKey: 'sales',
+ path: '/dashboard/sales/overview'
+ },
+ { type: 'divider' },
+ {
+ key: 'clients',
+ label: 'Clients',
+ iconKey: 'client',
+ path: '/dashboard/sales/clients'
+ },
+ {
+ key: 'salesorders',
+ label: 'Sales Orders',
+ iconKey: 'salesOrder',
+ path: '/dashboard/sales/salesorders'
+ },
+ {
+ key: 'marketplaces',
+ label: 'Marketplaces',
+ iconKey: 'marketplace',
+ path: '/dashboard/sales/marketplaces'
+ },
+ {
+ key: 'listings',
+ label: 'Listings',
+ iconKey: 'listing',
+ path: '/dashboard/sales/listings'
+ }
+]
+
+export default salesSidebarItems