From 24c06c25d3d84b4cc0fd4f168be3ebb5e7c2e39b Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Thu, 20 Aug 2026 19:35:09 +0100 Subject: [PATCH] Refactor JavaScript Language Support and Enhance Autocomplete Functionality - Consolidated JavaScript completion sources into a new module, improving organization and maintainability. - Updated the `fcTemplateLang` to utilize the new `javascriptCompletionSources` for enhanced autocomplete capabilities. - Removed redundant imports and streamlined the code in various components, including Invoices, Payments, TaxRecords, and Inventory sections, by replacing dropdown actions with a unified `ObjectActions` component. - Improved the handling of object actions across multiple inventory and finance components, enhancing user experience and code consistency. --- src/codemirror/fcTemplateLang/index.js | 9 +- src/codemirror/javascriptLang/index.js | 60 ++++++ src/codemirror/javascriptLang/nodeScope.js | 200 ++++++++++++++++++ src/components/Dashboard/Finance/Invoices.jsx | 57 +---- .../Finance/Invoices/InvoiceInfo.jsx | 1 + src/components/Dashboard/Finance/Payments.jsx | 57 +---- .../Finance/Payments/PaymentInfo.jsx | 1 + .../Dashboard/Finance/TaxRecords.jsx | 54 +---- .../Finance/TaxRecords/TaxRecordInfo.jsx | 1 + .../Dashboard/Inventory/FilamentStocks.jsx | 57 +---- .../FilamentStocks/FilamentStockInfo.jsx | 1 + .../Dashboard/Inventory/OrderItems.jsx | 57 +---- .../Inventory/OrderItems/OrderItemInfo.jsx | 1 + .../Dashboard/Inventory/PartStocks.jsx | 57 +---- .../Inventory/PartStocks/PartStockInfo.jsx | 1 + .../Dashboard/Inventory/ProductStocks.jsx | 57 +---- .../ProductStocks/ProductStockInfo.jsx | 1 + .../Dashboard/Inventory/PurchaseOrders.jsx | 57 +---- .../PurchaseOrders/PurchaseOrderInfo.jsx | 1 + .../Dashboard/Inventory/Shipments.jsx | 57 +---- .../Inventory/Shipments/PurchaseOrderInfo.jsx | 1 + .../Inventory/Shipments/ShipmentInfo.jsx | 1 + .../Dashboard/Inventory/StockAudits.jsx | 57 +---- .../Inventory/StockAudits/StockAuditInfo.jsx | 1 + .../Dashboard/Inventory/StockEvents.jsx | 26 +-- .../Dashboard/Inventory/StockLocations.jsx | 57 +---- .../StockLocations/StockLocationInfo.jsx | 1 + .../Dashboard/Inventory/StockTransfers.jsx | 57 +---- .../StockTransfers/StockTransferInfo.jsx | 1 + .../Dashboard/Management/AppPasswords.jsx | 55 +---- .../AppPasswords/AppPasswordInfo.jsx | 1 + .../Dashboard/Management/AuditLogs.jsx | 26 +-- .../Dashboard/Management/CourierServices.jsx | 54 +---- .../CourierServices/CourierServiceInfo.jsx | 1 + .../Dashboard/Management/Couriers.jsx | 54 +---- .../Management/Couriers/CourierInfo.jsx | 1 + .../Dashboard/Management/DocumentJobs.jsx | 54 +---- .../DocumentJobs/DocumentJobInfo.jsx | 1 + .../Dashboard/Management/DocumentPrinters.jsx | 54 +---- .../DocumentPrinters/DocumentPrinterInfo.jsx | 1 + .../Dashboard/Management/DocumentSizes.jsx | 54 +---- .../DocumentSizes/DocumentSizeInfo.jsx | 1 + .../Management/DocumentTemplates.jsx | 54 +---- .../DocumentTemplateDesign.jsx | 1 + .../DocumentTemplateInfo.jsx | 1 + .../Dashboard/Management/FilamentSkus.jsx | 57 +---- .../FilamentSkus/FilamentSkuInfo.jsx | 1 + .../Dashboard/Management/Filaments.jsx | 55 +---- .../Management/Filaments/FilamentInfo.jsx | 1 + src/components/Dashboard/Management/Files.jsx | 26 +-- .../Dashboard/Management/Files/FileInfo.jsx | 1 + src/components/Dashboard/Management/Hosts.jsx | 56 +---- .../Dashboard/Management/Hosts/HostInfo.jsx | 1 + .../Dashboard/Management/Materials.jsx | 55 +---- .../Management/Materials/MaterialInfo.jsx | 1 + .../Dashboard/Management/NoteTypes.jsx | 54 +---- .../Management/NoteTypes/NoteTypeInfo.jsx | 1 + .../Dashboard/Management/Notes/NoteInfo.jsx | 1 + .../Dashboard/Management/PartSkus.jsx | 57 +---- .../Management/PartSkus/PartSkuInfo.jsx | 1 + src/components/Dashboard/Management/Parts.jsx | 56 +---- .../Dashboard/Management/Parts/PartInfo.jsx | 1 + .../Management/PermissionSettings.jsx | 55 +---- .../PermissionSettingInfo.jsx | 1 + .../Management/ProductCategories.jsx | 55 +---- .../ProductCategories/ProductCategoryInfo.jsx | 1 + .../Dashboard/Management/ProductSkus.jsx | 57 +---- .../Management/ProductSkus/ProductSkuInfo.jsx | 1 + .../Dashboard/Management/Products.jsx | 55 +---- .../Management/Products/ProductInfo.jsx | 1 + .../Dashboard/Management/TaxRates.jsx | 54 +---- .../Management/TaxRates/TaxRateInfo.jsx | 1 + .../Dashboard/Management/UserGroups.jsx | 54 +---- .../Management/UserGroups/UserGroupInfo.jsx | 1 + src/components/Dashboard/Management/Users.jsx | 26 +-- .../Dashboard/Management/Users/UserInfo.jsx | 1 + .../Dashboard/Management/Vendors.jsx | 54 +---- .../Management/Vendors/VendorInfo.jsx | 1 + .../Dashboard/Production/FilamentProfiles.jsx | 56 +---- .../FilamentProfiles/FilamentProfileInfo.jsx | 1 + .../Dashboard/Production/GCodeFiles.jsx | 56 +---- .../Production/GCodeFiles/GCodeFileInfo.jsx | 1 + .../GCodeFiles/GCodeFilePreview.jsx | 1 + src/components/Dashboard/Production/Jobs.jsx | 59 +----- .../Dashboard/Production/Jobs/JobInfo.jsx | 1 + .../Dashboard/Production/PrinterProfiles.jsx | 56 +---- .../PrinterProfiles/PrinterProfileInfo.jsx | 1 + .../Dashboard/Production/Printers.jsx | 56 +---- .../Production/Printers/ControlPrinter.jsx | 1 + .../Production/Printers/PrinterInfo.jsx | 1 + .../Dashboard/Production/SubJobs.jsx | 26 +-- .../Production/SubJobs/SubJobInfo.jsx | 1 + src/components/Dashboard/Sales/Clients.jsx | 54 +---- .../Dashboard/Sales/Clients/ClientInfo.jsx | 1 + .../ListingVarients/ListingVarientInfo.jsx | 1 + src/components/Dashboard/Sales/Listings.jsx | 57 +---- .../Dashboard/Sales/Listings/ListingInfo.jsx | 1 + .../Dashboard/Sales/Marketplaces.jsx | 57 +---- .../Sales/Marketplaces/MarketplaceInfo.jsx | 1 + .../Dashboard/Sales/SalesOrders.jsx | 57 +---- .../Sales/SalesOrders/SalesOrderInfo.jsx | 1 + .../Dashboard/common/CodeBlockEditor.jsx | 6 +- .../Dashboard/common/EditButtons.jsx | 28 ++- .../Dashboard/common/ObjectActions.jsx | 124 +++++++---- .../Dashboard/common/ObjectTable.jsx | 17 +- .../Dashboard/common/PermissionsMatrix.jsx | 13 +- .../Dashboard/common/UserProfilePopover.jsx | 1 + .../Dashboard/context/ActionsContext.jsx | 37 +++- .../Dashboard/context/ActionsModalContext.jsx | 74 +++++-- .../Dashboard/context/ApiServerContext.jsx | 6 +- .../Dashboard/context/AuthContext.jsx | 140 +++++++----- src/database/ObjectModels.js | 10 +- src/database/models/AppPassword.js | 15 ++ src/database/models/Client.js | 15 ++ src/database/models/Courier.js | 15 ++ src/database/models/CourierService.js | 15 ++ src/database/models/DocumentJob.js | 15 ++ src/database/models/DocumentPrinter.js | 15 ++ src/database/models/DocumentSize.js | 15 ++ src/database/models/DocumentTemplate.js | 15 ++ src/database/models/Filament.js | 28 ++- src/database/models/FilamentProfile.js | 15 ++ src/database/models/FilamentSku.js | 15 ++ src/database/models/FilamentStock.js | 15 ++ src/database/models/GCodeFile.js | 15 ++ src/database/models/Host.js | 15 ++ src/database/models/Invoice.js | 40 ++-- src/database/models/Job.js | 15 ++ src/database/models/Listing.js | 33 +-- src/database/models/ListingVarient.js | 15 ++ src/database/models/Marketplace.js | 15 ++ src/database/models/Material.js | 15 ++ src/database/models/NoteType.js | 15 ++ src/database/models/OrderItem.js | 15 ++ src/database/models/Part.js | 28 ++- src/database/models/PartSku.js | 15 ++ src/database/models/PartStock.js | 15 ++ src/database/models/Payment.js | 15 ++ src/database/models/PermissionSetting.js | 15 ++ src/database/models/Printer.js | 33 +-- src/database/models/PrinterProfile.js | 15 ++ src/database/models/Product.js | 28 ++- src/database/models/ProductCategory.js | 15 ++ src/database/models/ProductSku.js | 15 ++ src/database/models/ProductStock.js | 15 ++ src/database/models/PurchaseOrder.js | 79 +++---- src/database/models/SalesOrder.js | 82 +++---- src/database/models/Shipment.js | 15 ++ src/database/models/StockAudit.js | 15 ++ src/database/models/StockLocation.js | 15 ++ src/database/models/StockTransfer.js | 15 ++ src/database/models/TaxRate.js | 15 ++ src/database/models/TaxRecord.js | 15 ++ src/database/models/User.js | 21 +- src/database/models/UserGroup.js | 15 ++ src/database/models/Vendor.js | 15 ++ src/database/permissions.js | 31 +++ src/utils/cookies.js | 176 +++++++-------- src/utils/modelActions.js | 47 ++++ 159 files changed, 1877 insertions(+), 2522 deletions(-) create mode 100644 src/codemirror/javascriptLang/index.js create mode 100644 src/codemirror/javascriptLang/nodeScope.js diff --git a/src/codemirror/fcTemplateLang/index.js b/src/codemirror/fcTemplateLang/index.js index d397375e..f79b6741 100644 --- a/src/codemirror/fcTemplateLang/index.js +++ b/src/codemirror/fcTemplateLang/index.js @@ -8,11 +8,8 @@ import { } from '@codemirror/language' import { styleTags, tags as t } from '@lezer/highlight' import { parseMixed } from '@lezer/common' -import { - javascriptLanguage, - scopeCompletionSource, - localCompletionSource -} from '@codemirror/lang-javascript' +import { javascriptLanguage } from '@codemirror/lang-javascript' +import { javascriptCompletionSources } from '../javascriptLang' import { xmlLanguage, completeFromSchema, @@ -109,7 +106,7 @@ export function fcTemplateLang(options = {}) { }), autoCloseTags, javascriptLanguage.data.of({ - autocomplete: [localCompletionSource, scopeCompletionSource(jsScope)] + autocomplete: javascriptCompletionSources(jsScope) }) ]) } diff --git a/src/codemirror/javascriptLang/index.js b/src/codemirror/javascriptLang/index.js new file mode 100644 index 00000000..89980e57 --- /dev/null +++ b/src/codemirror/javascriptLang/index.js @@ -0,0 +1,60 @@ +import { completeFromList } from '@codemirror/autocomplete' +import { + javascript, + javascriptLanguage, + localCompletionSource, + scopeCompletionSource, + snippets +} from '@codemirror/lang-javascript' +import { nodeJsScope } from './nodeScope.js' + +function extraScope(autoCompleteObject) { + let data = autoCompleteObject + if (typeof data === 'string') { + try { + data = JSON.parse(data) + } catch { + data = {} + } + } + if (!data || typeof data !== 'object' || Array.isArray(data)) { + return {} + } + return data +} + +export function nodeScopeCompletionSource(autoCompleteObject) { + return scopeCompletionSource({ + ...nodeJsScope, + ...extraScope(autoCompleteObject) + }) +} + +/** + * Completions for mixed languages that mount `javascriptLanguage` + * without `javascript()` (snippets + locals + Node globals). + */ +export function javascriptCompletionSources(autoCompleteObject) { + return [ + completeFromList(snippets), + localCompletionSource, + nodeScopeCompletionSource(autoCompleteObject) + ] +} + +/** + * JavaScript language support with Node.js globals, snippets, and + * local-variable completion. + * @param {{ autoCompleteObject?: object|string }} [options] + */ +export function javascriptLang(options = {}) { + const { autoCompleteObject } = options + return [ + javascript(), + javascriptLanguage.data.of({ + autocomplete: nodeScopeCompletionSource(autoCompleteObject) + }) + ] +} + +export { nodeJsScope } diff --git a/src/codemirror/javascriptLang/nodeScope.js b/src/codemirror/javascriptLang/nodeScope.js new file mode 100644 index 00000000..fca44de0 --- /dev/null +++ b/src/codemirror/javascriptLang/nodeScope.js @@ -0,0 +1,200 @@ +/** No-op used so Node APIs enumerate as functions in completions. */ +function fn() {} + +const streamLike = { + write: fn, + end: fn, + cork: fn, + uncork: fn, + on: fn, + once: fn, + off: fn, + emit: fn, + pipe: fn, + isTTY: false +} + +const nodeProcess = { + env: {}, + argv: ['node'], + argv0: 'node', + execArgv: [], + execPath: '', + cwd: fn, + chdir: fn, + exit: fn, + exitCode: 0, + nextTick: fn, + pid: 0, + ppid: 0, + platform: 'linux', + arch: 'x64', + version: 'v20.0.0', + versions: { node: '20.0.0' }, + title: 'node', + stdout: streamLike, + stderr: { ...streamLike }, + stdin: { on: fn, once: fn, read: fn, isTTY: false }, + hrtime: Object.assign(fn, { bigint: fn }), + uptime: fn, + memoryUsage: fn, + cpuUsage: fn, + resourceUsage: fn, + abort: fn, + kill: fn, + getuid: fn, + getgid: fn, + setuid: fn, + setgid: fn, + umask: fn, + binding: fn, + dlopen: fn, + features: {}, + config: {}, + release: { name: 'node' } +} + +const BufferStub = Object.assign(fn, { + from: fn, + alloc: fn, + allocUnsafe: fn, + allocUnsafeSlow: fn, + concat: fn, + isBuffer: fn, + isEncoding: fn, + byteLength: fn, + compare: fn, + prototype: {} +}) + +const requireFn = Object.assign(fn, { + resolve: Object.assign(fn, { paths: fn }), + cache: {}, + extensions: {}, + main: {} +}) + +/** + * Scope object for CodeMirror `scopeCompletionSource`. + * Browser-only globals (`window`, `document`, …) are omitted. + */ +export function createNodeJsScope() { + const scope = { + process: nodeProcess, + Buffer: BufferStub, + require: requireFn, + module: { + exports: {}, + require: requireFn, + id: '.', + filename: '', + dirname: '', + paths: [], + loaded: false, + children: [], + parent: null + }, + exports: {}, + __dirname: '', + __filename: '', + console, + setTimeout, + clearTimeout, + setInterval, + clearInterval, + setImmediate: fn, + clearImmediate: fn, + queueMicrotask, + fetch, + URL, + URLSearchParams, + TextEncoder, + TextDecoder, + AbortController, + AbortSignal, + Event, + EventTarget, + Request, + Response, + Headers, + FormData, + Blob, + structuredClone, + performance, + crypto, + atob, + btoa, + WebAssembly, + Promise, + Map, + Set, + WeakMap, + WeakSet, + Array, + Object, + String, + Number, + Boolean, + Symbol, + BigInt, + Date, + RegExp, + Error, + TypeError, + RangeError, + SyntaxError, + URIError, + EvalError, + ReferenceError, + JSON, + Math, + Intl, + Proxy, + Reflect, + ArrayBuffer, + DataView, + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + BigInt64Array, + BigUint64Array, + Infinity, + NaN, + parseInt, + parseFloat, + isNaN, + isFinite, + encodeURI, + decodeURI, + encodeURIComponent, + decodeURIComponent, + eval, + Function + } + + if (typeof File !== 'undefined') scope.File = File + if (typeof globalThis.WeakRef !== 'undefined') { + scope.WeakRef = globalThis.WeakRef + } + if (typeof globalThis.FinalizationRegistry !== 'undefined') { + scope.FinalizationRegistry = globalThis.FinalizationRegistry + } + if (typeof globalThis.AggregateError !== 'undefined') { + scope.AggregateError = globalThis.AggregateError + } + if (typeof SharedArrayBuffer !== 'undefined') { + scope.SharedArrayBuffer = SharedArrayBuffer + } + + scope.global = scope + scope.globalThis = scope + return scope +} + +export const nodeJsScope = createNodeJsScope() diff --git a/src/components/Dashboard/Finance/Invoices.jsx b/src/components/Dashboard/Finance/Invoices.jsx index d16a8553..908d1b34 100644 --- a/src/components/Dashboard/Finance/Invoices.jsx +++ b/src/components/Dashboard/Finance/Invoices.jsx @@ -1,9 +1,7 @@ -import { useState, useRef } from 'react' -import { Button, Flex, Space, Dropdown, Modal } from 'antd' -import NewInvoice from './Invoices/NewInvoice' +import { useRef } from 'react' +import { Flex, Space } from 'antd' import ObjectTable from '../common/ObjectTable' -import PlusIcon from '../../Icons/PlusIcon' -import ReloadIcon from '../../Icons/ReloadIcon' +import ObjectActions from '../common/ObjectActions' import useColumnVisibility from '../hooks/useColumnVisibility' import ObjectTableViewButton from '../common/ObjectTableViewButton' import FilterSidebarButton from '../common/FilterSidebarButton' @@ -15,7 +13,6 @@ import ColumnViewButton from '../common/ColumnViewButton' import ExportListButton from '../common/ExportListButton' const Invoices = () => { - const [newInvoiceOpen, setNewInvoiceOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('invoices') @@ -29,37 +26,17 @@ const Invoices = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Invoices') - const actionItems = { - items: [ - { - label: 'New Invoice', - key: 'newInvoice', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newInvoice') { - setNewInvoiceOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewInvoiceOpen(false) - }} - destroyOnHidden={true} - > - { - setNewInvoiceOpen(false) - tableRef.current?.reload() - }} - reset={newInvoiceOpen} - /> - ) } diff --git a/src/components/Dashboard/Finance/Invoices/InvoiceInfo.jsx b/src/components/Dashboard/Finance/Invoices/InvoiceInfo.jsx index 14757a46..e7011b8f 100644 --- a/src/components/Dashboard/Finance/Invoices/InvoiceInfo.jsx +++ b/src/components/Dashboard/Finance/Invoices/InvoiceInfo.jsx @@ -98,6 +98,7 @@ const InvoiceInfo = () => { { - const [newPaymentOpen, setNewPaymentOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('payments') @@ -29,37 +26,17 @@ const Payments = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Payments') - const actionItems = { - items: [ - { - label: 'New Payment', - key: 'newPayment', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newPayment') { - setNewPaymentOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewPaymentOpen(false) - }} - destroyOnHidden={true} - > - { - setNewPaymentOpen(false) - tableRef.current?.reload() - }} - reset={newPaymentOpen} - /> - ) } diff --git a/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx b/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx index 8164eced..6b3e5398 100644 --- a/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx +++ b/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx @@ -91,6 +91,7 @@ const PaymentInfo = () => { { - const [newTaxRecordOpen, setNewTaxRecordOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('taxRecord') @@ -29,37 +26,17 @@ const TaxRecords = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('TaxRecords') - const actionItems = { - items: [ - { - label: 'New Tax Record', - key: 'newTaxRecord', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newTaxRecord') { - setNewTaxRecordOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewTaxRecordOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewTaxRecordOpen(false) - tableRef.current?.reload() - }} - reset={!newTaxRecordOpen} - /> - ) } diff --git a/src/components/Dashboard/Finance/TaxRecords/TaxRecordInfo.jsx b/src/components/Dashboard/Finance/TaxRecords/TaxRecordInfo.jsx index 6cf21942..924826c8 100644 --- a/src/components/Dashboard/Finance/TaxRecords/TaxRecordInfo.jsx +++ b/src/components/Dashboard/Finance/TaxRecords/TaxRecordInfo.jsx @@ -81,6 +81,7 @@ const TaxRecordInfo = () => { { const tableRef = useRef() - const [newFilamentStockOpen, setNewFilamentStockOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('filamentStocks') @@ -34,37 +31,17 @@ const FilamentStocks = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('FilamentStocks') - const actionItems = { - items: [ - { - label: 'New Filament Stock', - key: 'newFilamentStock', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newFilamentStock') { - setNewFilamentStockOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewFilamentStockOpen(false) - }} - destroyOnHidden={true} - > - { - setNewFilamentStockOpen(false) - tableRef.current?.reload() - }} - reset={newFilamentStockOpen} - /> - ) } diff --git a/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx b/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx index 99b4746d..dc8301b5 100644 --- a/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx +++ b/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx @@ -79,6 +79,7 @@ const FilamentStockInfo = () => { { - const [newOrderItemOpen, setNewOrderItemOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('orderItems') @@ -29,37 +26,17 @@ const OrderItems = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('OrderItems') - const actionItems = { - items: [ - { - label: 'New Order Item', - key: 'newOrderItem', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newOrderItem') { - setNewOrderItemOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewOrderItemOpen(false) - }} - destroyOnHidden={true} - > - { - setNewOrderItemOpen(false) - tableRef.current?.reload() - }} - reset={newOrderItemOpen} - /> - ) } diff --git a/src/components/Dashboard/Inventory/OrderItems/OrderItemInfo.jsx b/src/components/Dashboard/Inventory/OrderItems/OrderItemInfo.jsx index a4b763ea..852d86ea 100644 --- a/src/components/Dashboard/Inventory/OrderItems/OrderItemInfo.jsx +++ b/src/components/Dashboard/Inventory/OrderItems/OrderItemInfo.jsx @@ -85,6 +85,7 @@ const OrderItemInfo = () => { { const tableRef = useRef() - const [newPartStockOpen, setNewPartStockOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('partStocks') @@ -34,37 +31,17 @@ const PartStocks = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('PartStocks') - const actionItems = { - items: [ - { - label: 'New Part Stock', - key: 'newPartStock', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newPartStock') { - setNewPartStockOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewPartStockOpen(false) - }} - destroyOnHidden={true} - > - { - setNewPartStockOpen(false) - tableRef.current?.reload() - }} - reset={newPartStockOpen} - /> - ) } diff --git a/src/components/Dashboard/Inventory/PartStocks/PartStockInfo.jsx b/src/components/Dashboard/Inventory/PartStocks/PartStockInfo.jsx index 0cff01a1..2156e3b2 100644 --- a/src/components/Dashboard/Inventory/PartStocks/PartStockInfo.jsx +++ b/src/components/Dashboard/Inventory/PartStocks/PartStockInfo.jsx @@ -87,6 +87,7 @@ const PartStockInfo = () => { { const tableRef = useRef() - const [newProductStockOpen, setNewProductStockOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('productStocks') @@ -35,37 +32,17 @@ const ProductStocks = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('ProductStocks') - const actionItems = { - items: [ - { - label: 'New Product Stock', - key: 'newProductStock', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newProductStock') { - setNewProductStockOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewProductStockOpen(false) - }} - destroyOnHidden={true} - > - { - setNewProductStockOpen(false) - tableRef.current?.reload() - }} - reset={newProductStockOpen} - /> - ) } diff --git a/src/components/Dashboard/Inventory/ProductStocks/ProductStockInfo.jsx b/src/components/Dashboard/Inventory/ProductStocks/ProductStockInfo.jsx index dbdd3c71..131ccd98 100644 --- a/src/components/Dashboard/Inventory/ProductStocks/ProductStockInfo.jsx +++ b/src/components/Dashboard/Inventory/ProductStocks/ProductStockInfo.jsx @@ -104,6 +104,7 @@ const ProductStockInfo = () => { { - const [newPurchaseOrderOpen, setNewPurchaseOrderOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('purchaseOrders') @@ -29,37 +26,17 @@ const PurchaseOrders = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('PurchaseOrders') - const actionItems = { - items: [ - { - label: 'New Purchase Order', - key: 'newPurchaseOrder', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newPurchaseOrder') { - setNewPurchaseOrderOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewPurchaseOrderOpen(false) - }} - destroyOnHidden={true} - > - { - setNewPurchaseOrderOpen(false) - tableRef.current?.reload() - }} - reset={newPurchaseOrderOpen} - /> - ) } diff --git a/src/components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo.jsx b/src/components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo.jsx index 38d1a309..bfaf5b60 100644 --- a/src/components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo.jsx +++ b/src/components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo.jsx @@ -109,6 +109,7 @@ const PurchaseOrderInfo = () => { { - const [newShipmentOpen, setNewShipmentOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('shipments') @@ -29,37 +26,17 @@ const Shipments = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Shipments') - const actionItems = { - items: [ - { - label: 'New Shipment', - key: 'newShipment', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newShipment') { - setNewShipmentOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewShipmentOpen(false) - }} - destroyOnHidden={true} - > - { - setNewShipmentOpen(false) - tableRef.current?.reload() - }} - reset={newShipmentOpen} - /> - ) } diff --git a/src/components/Dashboard/Inventory/Shipments/PurchaseOrderInfo.jsx b/src/components/Dashboard/Inventory/Shipments/PurchaseOrderInfo.jsx index bcc8cbd1..801c469e 100644 --- a/src/components/Dashboard/Inventory/Shipments/PurchaseOrderInfo.jsx +++ b/src/components/Dashboard/Inventory/Shipments/PurchaseOrderInfo.jsx @@ -119,6 +119,7 @@ const PurchaseOrderInfo = () => { { { const tableRef = useRef() - const [newStockAuditOpen, setNewStockAuditOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('stockAudits') @@ -34,37 +31,17 @@ const StockAudits = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('StockAudits') - const actionItems = { - items: [ - { - label: 'New Stock audit', - key: 'newStockAudit', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newStockAudit') { - setNewStockAuditOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewStockAuditOpen(false) - }} - destroyOnHidden={true} - > - { - setNewStockAuditOpen(false) - tableRef.current?.reload() - }} - reset={newStockAuditOpen} - /> - ) } diff --git a/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx b/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx index d86ffd8c..ab7f0a78 100644 --- a/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx +++ b/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx @@ -83,6 +83,7 @@ const StockAuditInfo = () => { { const tableRef = useRef() @@ -25,29 +25,17 @@ const StockEvents = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('StockEvents') - const actionItems = { - items: [ - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { const tableRef = useRef() - const [newOpen, setNewOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('stockLocations') @@ -32,37 +29,17 @@ const StockLocations = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('StockLocations') - const actionItems = { - items: [ - { - label: 'New Stock Location', - key: 'new', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'new') { - setNewOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewOpen(false) - }} - destroyOnHidden={true} - > - { - setNewOpen(false) - tableRef.current?.reload() - }} - reset={newOpen} - /> - ) } diff --git a/src/components/Dashboard/Inventory/StockLocations/StockLocationInfo.jsx b/src/components/Dashboard/Inventory/StockLocations/StockLocationInfo.jsx index 659ebc91..08a319ed 100644 --- a/src/components/Dashboard/Inventory/StockLocations/StockLocationInfo.jsx +++ b/src/components/Dashboard/Inventory/StockLocations/StockLocationInfo.jsx @@ -84,6 +84,7 @@ const StockLocationInfo = () => { { const tableRef = useRef() - const [newOpen, setNewOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('stockTransfers') @@ -32,37 +29,17 @@ const StockTransfers = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('StockTransfers') - const actionItems = { - items: [ - { - label: 'New Stock Transfer', - key: 'new', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'new') { - setNewOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewOpen(false) - }} - destroyOnHidden={true} - > - { - setNewOpen(false) - tableRef.current?.reload() - }} - reset={newOpen} - /> - ) } diff --git a/src/components/Dashboard/Inventory/StockTransfers/StockTransferInfo.jsx b/src/components/Dashboard/Inventory/StockTransfers/StockTransferInfo.jsx index 05c792de..3a0ae2d5 100644 --- a/src/components/Dashboard/Inventory/StockTransfers/StockTransferInfo.jsx +++ b/src/components/Dashboard/Inventory/StockTransfers/StockTransferInfo.jsx @@ -94,6 +94,7 @@ const StockTransferInfo = () => { { - const [newAppPasswordOpen, setNewAppPasswordOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('appPassword') @@ -28,37 +25,17 @@ const AppPasswords = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('AppPasswords') - const actionItems = { - items: [ - { - label: 'New App Password', - key: 'newAppPassword', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newAppPassword') { - setNewAppPasswordOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewAppPasswordOpen(false) - }} - > - { - setNewAppPasswordOpen(false) - tableRef.current?.reload() - }} - reset={newAppPasswordOpen} - /> - ) diff --git a/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx b/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx index f9e95fb5..57941bab 100644 --- a/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx +++ b/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx @@ -80,6 +80,7 @@ const AppPasswordInfo = () => { { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('AuditLogs') - const actionItems = { - items: [ - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { - const [newCourierServiceOpen, setNewCourierServiceOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('courierService') @@ -29,37 +26,17 @@ const CourierServices = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('CourierServices') - const actionItems = { - items: [ - { - label: 'New Courier Service', - key: 'newCourierService', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newCourierService') { - setNewCourierServiceOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewCourierServiceOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewCourierServiceOpen(false) - tableRef.current?.reload() - }} - reset={!newCourierServiceOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx b/src/components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx index 08430416..e4499b8c 100644 --- a/src/components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx +++ b/src/components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx @@ -83,6 +83,7 @@ const CourierServiceInfo = () => { { - const [newCourierOpen, setNewCourierOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('courier') @@ -28,37 +25,17 @@ const Couriers = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Couriers') - const actionItems = { - items: [ - { - label: 'New Courier', - key: 'newCourier', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newCourier') { - setNewCourierOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewCourierOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewCourierOpen(false) - tableRef.current?.reload() - }} - reset={!newCourierOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/Couriers/CourierInfo.jsx b/src/components/Dashboard/Management/Couriers/CourierInfo.jsx index 3d0efac9..29d89a25 100644 --- a/src/components/Dashboard/Management/Couriers/CourierInfo.jsx +++ b/src/components/Dashboard/Management/Couriers/CourierInfo.jsx @@ -79,6 +79,7 @@ const CourierInfo = () => { { - const [newDocumentJobOpen, setNewDocumentJobOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('documentJob') @@ -29,37 +26,17 @@ const DocumentJobs = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('DocumentJobs') - const actionItems = { - items: [ - { - label: 'New Document Job', - key: 'newDocumentJob', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newDocumentJob') { - setNewDocumentJobOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewDocumentJobOpen(false)} - footer={null} - destroyOnHidden={true} - width={900} - > - { - setNewDocumentJobOpen(false) - tableRef.current?.reload() - }} - reset={!newDocumentJobOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/DocumentJobs/DocumentJobInfo.jsx b/src/components/Dashboard/Management/DocumentJobs/DocumentJobInfo.jsx index 1048ab3f..52ac06bf 100644 --- a/src/components/Dashboard/Management/DocumentJobs/DocumentJobInfo.jsx +++ b/src/components/Dashboard/Management/DocumentJobs/DocumentJobInfo.jsx @@ -80,6 +80,7 @@ const DocumentJobInfo = () => { { const tableRef = useRef() - const [newDocumentPrinterOpen, setNewDocumentPrinterOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('documentPrinter') const [columnVisibility, setColumnVisibility] = @@ -28,37 +25,17 @@ const DocumentPrinters = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('DocumentPrinters') - const actionItems = { - items: [ - { - label: 'New Document Printer', - key: 'newDocumentPrinter', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newDocumentPrinter') { - setNewDocumentPrinterOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewDocumentPrinterOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewDocumentPrinterOpen(false) - tableRef.current?.reload() - }} - reset={!newDocumentPrinterOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo.jsx b/src/components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo.jsx index 7f4c12ca..abffb8c0 100644 --- a/src/components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo.jsx +++ b/src/components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo.jsx @@ -85,6 +85,7 @@ const DocumentPrinterInfo = () => { { - const [newDocumentSizeOpen, setNewDocumentSizeOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('documentSize') const [columnVisibility, setColumnVisibility] = @@ -27,37 +24,17 @@ const DocumentSizes = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('DocumentSizes') - const actionItems = { - items: [ - { - label: 'New Document Size', - key: 'newDocumentSize', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newDocumentSize') { - setNewDocumentSizeOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewDocumentSizeOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewDocumentSizeOpen(false) - tableRef.current?.reload() - }} - reset={!newDocumentSizeOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/DocumentSizes/DocumentSizeInfo.jsx b/src/components/Dashboard/Management/DocumentSizes/DocumentSizeInfo.jsx index 0bcc856a..36410685 100644 --- a/src/components/Dashboard/Management/DocumentSizes/DocumentSizeInfo.jsx +++ b/src/components/Dashboard/Management/DocumentSizes/DocumentSizeInfo.jsx @@ -80,6 +80,7 @@ const DocumentSizeInfo = () => { { - const [newDocumentTemplateOpen, setNewDocumentTemplateOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('documentTemplate') @@ -29,37 +26,17 @@ const DocumentTemplates = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('DocumentTemplates') - const actionItems = { - items: [ - { - label: 'New Document Template', - key: 'newDocumentTemplate', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newDocumentTemplate') { - setNewDocumentTemplateOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewDocumentTemplateOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewDocumentTemplateOpen(false) - tableRef.current?.reload() - }} - reset={!newDocumentTemplateOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx b/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx index 87568335..ccfb2093 100644 --- a/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx +++ b/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx @@ -75,6 +75,7 @@ const DocumentTemplateDesign = () => { { { const tableRef = useRef() - const [newFilamentSkuOpen, setNewFilamentSkuOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('filamentSkus') @@ -32,37 +29,17 @@ const FilamentSkus = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('FilamentSkus') - const actionItems = { - items: [ - { - label: 'New Filament SKU', - key: 'newFilamentSku', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newFilamentSku') { - setNewFilamentSkuOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewFilamentSkuOpen(false) - }} - destroyOnHidden={true} - > - { - setNewFilamentSkuOpen(false) - tableRef.current?.reload() - }} - reset={newFilamentSkuOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/FilamentSkus/FilamentSkuInfo.jsx b/src/components/Dashboard/Management/FilamentSkus/FilamentSkuInfo.jsx index 02229089..305f96e4 100644 --- a/src/components/Dashboard/Management/FilamentSkus/FilamentSkuInfo.jsx +++ b/src/components/Dashboard/Management/FilamentSkus/FilamentSkuInfo.jsx @@ -78,6 +78,7 @@ const FilamentSkuInfo = () => { { - const [newFilamentOpen, setNewFilamentOpen] = useState(false) const tableRef = useRef() // View mode state (cards/list), persisted in sessionStorage via custom hook @@ -34,37 +31,17 @@ const Filaments = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Filaments') - const actionItems = { - items: [ - { - label: 'New Filament', - key: 'newFilament', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newFilament') { - setNewFilamentOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewFilamentOpen(false) - }} - > - { - setNewFilamentOpen(false) - tableRef.current?.reload() - }} - reset={newFilamentOpen} - /> - ) diff --git a/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx b/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx index d9461371..7ee2e650 100644 --- a/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx +++ b/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx @@ -99,6 +99,7 @@ const FilamentInfo = () => { { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Files') - const actionItems = { - items: [ - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { { - const [newHostOpen, setNewHostOpen] = useState(false) const tableRef = useRef() // View mode state (cards/list), persisted in sessionStorage via custom hook @@ -33,37 +30,17 @@ const Hosts = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Hosts') - const actionItems = { - items: [ - { - label: 'New Host', - key: 'newHost', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newHost') { - setNewHostOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewHostOpen(false) - }} - > - { - setNewHostOpen(false) - tableRef.current?.reload() - }} - reset={newHostOpen} - /> - ) diff --git a/src/components/Dashboard/Management/Hosts/HostInfo.jsx b/src/components/Dashboard/Management/Hosts/HostInfo.jsx index 39bce0f6..c217fc15 100644 --- a/src/components/Dashboard/Management/Hosts/HostInfo.jsx +++ b/src/components/Dashboard/Management/Hosts/HostInfo.jsx @@ -94,6 +94,7 @@ const HostInfo = () => { { - const [newMaterialOpen, setNewMaterialOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('material') @@ -31,37 +28,17 @@ const Materials = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Materials') - const actionItems = { - items: [ - { - label: 'New Material', - key: 'newMaterial', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newMaterial') { - setNewMaterialOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewMaterialOpen(false) - }} - > - { - setNewMaterialOpen(false) - tableRef.current?.reload() - }} - reset={newMaterialOpen} - /> - ) diff --git a/src/components/Dashboard/Management/Materials/MaterialInfo.jsx b/src/components/Dashboard/Management/Materials/MaterialInfo.jsx index 580a886b..f4e48288 100644 --- a/src/components/Dashboard/Management/Materials/MaterialInfo.jsx +++ b/src/components/Dashboard/Management/Materials/MaterialInfo.jsx @@ -81,6 +81,7 @@ const MaterialInfo = () => { { - const [newNoteTypeOpen, setNewNoteTypeOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('noteType') @@ -29,37 +26,17 @@ const NoteTypes = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('NoteTypes') - const actionItems = { - items: [ - { - label: 'New Note Type', - key: 'newNoteType', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newNoteType') { - setNewNoteTypeOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewNoteTypeOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewNoteTypeOpen(false) - tableRef.current?.reload() - }} - reset={!newNoteTypeOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx b/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx index 71ccabbb..011eee0b 100644 --- a/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx +++ b/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx @@ -70,6 +70,7 @@ const NoteTypeInfo = () => { { { const tableRef = useRef() - const [newPartSkuOpen, setNewPartSkuOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('partSkus') @@ -31,37 +28,17 @@ const PartSkus = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('PartSkus') - const actionItems = { - items: [ - { - label: 'New Part SKU', - key: 'newPartSku', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newPartSku') { - setNewPartSkuOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewPartSkuOpen(false) - }} - destroyOnHidden={true} - > - { - setNewPartSkuOpen(false) - tableRef.current?.reload() - }} - reset={newPartSkuOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/PartSkus/PartSkuInfo.jsx b/src/components/Dashboard/Management/PartSkus/PartSkuInfo.jsx index d0e3a2fa..f949a991 100644 --- a/src/components/Dashboard/Management/PartSkus/PartSkuInfo.jsx +++ b/src/components/Dashboard/Management/PartSkus/PartSkuInfo.jsx @@ -73,6 +73,7 @@ const PartSkuInfo = () => { { - const [newPartOpen, setNewPartOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('part') const [columnVisibility, setColumnVisibility] = useColumnVisibility('part') @@ -33,37 +30,17 @@ const Parts = (filter) => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Parts') - const actionItems = { - items: [ - { - label: 'New Part', - key: 'newPart', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newPart') { - setNewPartOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewPartOpen(false) - }} - destroyOnHidden={true} - > - { - setNewPartOpen(false) - tableRef.current?.reload() - }} - reset={newPartOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/Parts/PartInfo.jsx b/src/components/Dashboard/Management/Parts/PartInfo.jsx index c17e500e..04723b02 100644 --- a/src/components/Dashboard/Management/Parts/PartInfo.jsx +++ b/src/components/Dashboard/Management/Parts/PartInfo.jsx @@ -80,6 +80,7 @@ const PartInfo = () => { { - const [newPermissionSettingsOpen, setNewPermissionSettingsOpen] = - useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('permissionSetting') @@ -30,37 +26,17 @@ const PermissionSettingsList = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('PermissionSettings') - const actionItems = { - items: [ - { - label: 'New Permission Settings', - key: 'newPermissionSettings', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newPermissionSettings') { - setNewPermissionSettingsOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewPermissionSettingsOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewPermissionSettingsOpen(false) - tableRef.current?.reload() - }} - reset={!newPermissionSettingsOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/PermissionSettings/PermissionSettingInfo.jsx b/src/components/Dashboard/Management/PermissionSettings/PermissionSettingInfo.jsx index 7d22b591..2dd8be0f 100644 --- a/src/components/Dashboard/Management/PermissionSettings/PermissionSettingInfo.jsx +++ b/src/components/Dashboard/Management/PermissionSettings/PermissionSettingInfo.jsx @@ -85,6 +85,7 @@ const PermissionSettingInfo = () => { { - const [newProductCategoryOpen, setNewProductCategoryOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('productCategory') @@ -31,37 +28,17 @@ const ProductCategories = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('ProductCategories') - const actionItems = { - items: [ - { - label: 'New Product Category', - key: 'newProductCategory', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newProductCategory') { - setNewProductCategoryOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewProductCategoryOpen(false) - }} - > - { - setNewProductCategoryOpen(false) - tableRef.current?.reload() - }} - reset={newProductCategoryOpen} - /> - ) diff --git a/src/components/Dashboard/Management/ProductCategories/ProductCategoryInfo.jsx b/src/components/Dashboard/Management/ProductCategories/ProductCategoryInfo.jsx index 5e84b0fd..60f6e0b1 100644 --- a/src/components/Dashboard/Management/ProductCategories/ProductCategoryInfo.jsx +++ b/src/components/Dashboard/Management/ProductCategories/ProductCategoryInfo.jsx @@ -78,6 +78,7 @@ const ProductCategoryInfo = () => { { const tableRef = useRef() - const [newProductSkuOpen, setNewProductSkuOpen] = useState(false) const [viewMode, setViewMode] = useViewMode('productSkus') @@ -32,37 +29,17 @@ const ProductSkus = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('ProductSkus') - const actionItems = { - items: [ - { - label: 'New Product SKU', - key: 'newProductSku', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newProductSku') { - setNewProductSkuOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewProductSkuOpen(false) - }} - destroyOnHidden={true} - > - { - setNewProductSkuOpen(false) - tableRef.current?.reload() - }} - reset={newProductSkuOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/ProductSkus/ProductSkuInfo.jsx b/src/components/Dashboard/Management/ProductSkus/ProductSkuInfo.jsx index 2a124b83..00b5799b 100644 --- a/src/components/Dashboard/Management/ProductSkus/ProductSkuInfo.jsx +++ b/src/components/Dashboard/Management/ProductSkus/ProductSkuInfo.jsx @@ -79,6 +79,7 @@ const ProductSkuInfo = () => { { const navigate = useNavigate() - const [newProductOpen, setNewProductOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('Products') @@ -244,28 +240,6 @@ const Products = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Products') - const actionItems = { - items: [ - { - label: 'New Product', - key: 'newProduct', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newProduct') { - setNewProductOpen(true) - } - } - } const getFilterDropdown = ({ setSelectedKeys, @@ -332,9 +306,11 @@ const Products = () => { - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewProductOpen(false) - }} - destroyOnHidden={true} - > - { - setNewProductOpen(false) - tableRef.current?.reload() - }} - reset={newProductOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/Products/ProductInfo.jsx b/src/components/Dashboard/Management/Products/ProductInfo.jsx index 43ecd564..3ffc1773 100644 --- a/src/components/Dashboard/Management/Products/ProductInfo.jsx +++ b/src/components/Dashboard/Management/Products/ProductInfo.jsx @@ -80,6 +80,7 @@ const ProductInfo = () => { { - const [newTaxRateOpen, setNewTaxRateOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('taxRate') @@ -28,37 +25,17 @@ const TaxRates = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('TaxRates') - const actionItems = { - items: [ - { - label: 'New Tax Rate', - key: 'newTaxRate', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newTaxRate') { - setNewTaxRateOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewTaxRateOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewTaxRateOpen(false) - tableRef.current?.reload() - }} - reset={!newTaxRateOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx b/src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx index b9f79f62..7952968e 100644 --- a/src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx +++ b/src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx @@ -78,6 +78,7 @@ const TaxRateInfo = () => { { - const [newUserGroupOpen, setNewUserGroupOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('userGroup') @@ -28,37 +25,17 @@ const UserGroups = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('UserGroups') - const actionItems = { - items: [ - { - label: 'New User Group', - key: 'newUserGroup', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newUserGroup') { - setNewUserGroupOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewUserGroupOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewUserGroupOpen(false) - tableRef.current?.reload() - }} - reset={!newUserGroupOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/UserGroups/UserGroupInfo.jsx b/src/components/Dashboard/Management/UserGroups/UserGroupInfo.jsx index 7a8a5c40..21f9d4ce 100644 --- a/src/components/Dashboard/Management/UserGroups/UserGroupInfo.jsx +++ b/src/components/Dashboard/Management/UserGroups/UserGroupInfo.jsx @@ -84,6 +84,7 @@ const UserGroupInfo = () => { { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Users') - const actionItems = { - items: [ - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } - } - } return ( - - - + tableRef.current?.reload()} + /> { { - const [newVendorOpen, setNewVendorOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('vendor') @@ -28,37 +25,17 @@ const Vendors = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Vendors') - const actionItems = { - items: [ - { - label: 'New Vendor', - key: 'newVendor', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newVendor') { - setNewVendorOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewVendorOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewVendorOpen(false) - tableRef.current?.reload() - }} - reset={!newVendorOpen} - /> - ) } diff --git a/src/components/Dashboard/Management/Vendors/VendorInfo.jsx b/src/components/Dashboard/Management/Vendors/VendorInfo.jsx index 318ad71f..63ca861f 100644 --- a/src/components/Dashboard/Management/Vendors/VendorInfo.jsx +++ b/src/components/Dashboard/Management/Vendors/VendorInfo.jsx @@ -78,6 +78,7 @@ const VendorInfo = () => { { - const [newProfileOpen, setNewProfileOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('FilamentProfiles') const [columnVisibility, setColumnVisibility] = @@ -26,37 +23,17 @@ const FilamentProfiles = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('FilamentProfiles') - const actionItems = { - items: [ - { - label: 'New Filament Profile', - key: 'newFilamentProfile', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newFilamentProfile') { - setNewProfileOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - - setNewProfileOpen(false)} - destroyOnHidden - > - {newProfileOpen && ( - { - setNewProfileOpen(false) - tableRef.current?.reload() - }} - /> - )} - ) } diff --git a/src/components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo.jsx b/src/components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo.jsx index 65ba07b1..47f04da6 100644 --- a/src/components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo.jsx +++ b/src/components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo.jsx @@ -115,6 +115,7 @@ const FilamentProfileInfo = () => { { - const [newGCodeFileOpen, setNewGCodeFileOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('gcodeFile') @@ -31,37 +28,17 @@ const GCodeFiles = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('GCodeFiles') - const actionItems = { - items: [ - { - label: 'New GCodeFile', - key: 'newGCodeFile', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newGCodeFile') { - setNewGCodeFileOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewGCodeFileOpen(false) - }} - destroyOnHidden={true} - > - { - setNewGCodeFileOpen(false) - tableRef.current?.reload() - }} - reset={newGCodeFileOpen} - /> - ) } diff --git a/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx b/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx index e3ff1e55..83df2575 100644 --- a/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx +++ b/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx @@ -85,6 +85,7 @@ const GCodeFileInfo = () => { { { - const [newJobOpen, setNewJobOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('job') @@ -28,41 +25,18 @@ const Jobs = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Jobs') - const actionItems = { - items: [ - { - label: 'New Print Job', - key: 'newJob', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'newJob') { - showNewJobModal() - } else if (key === 'reloadList') { - tableRef.current?.reload() - } - } - } - const showNewJobModal = () => { - setNewJobOpen(true) - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewJobOpen(false) - }} - > - { - setNewJobOpen(false) - tableRef.current?.reload() - }} - reset={newJobOpen} - /> - ) } diff --git a/src/components/Dashboard/Production/Jobs/JobInfo.jsx b/src/components/Dashboard/Production/Jobs/JobInfo.jsx index 5666fc69..0bfce988 100644 --- a/src/components/Dashboard/Production/Jobs/JobInfo.jsx +++ b/src/components/Dashboard/Production/Jobs/JobInfo.jsx @@ -91,6 +91,7 @@ const JobInfo = () => { { - const [newProfileOpen, setNewProfileOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('PrinterProfiles') const [columnVisibility, setColumnVisibility] = @@ -25,37 +22,17 @@ const PrinterProfiles = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('PrinterProfiles') - const actionItems = { - items: [ - { - label: 'New Printer Profile', - key: 'newPrinterProfile', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newPrinterProfile') { - setNewProfileOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - - setNewProfileOpen(false)} - destroyOnHidden - > - {newProfileOpen && ( - { - setNewProfileOpen(false) - tableRef.current?.reload() - }} - /> - )} - ) } diff --git a/src/components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo.jsx b/src/components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo.jsx index 325a6e38..741651d6 100644 --- a/src/components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo.jsx +++ b/src/components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo.jsx @@ -93,6 +93,7 @@ const PrinterProfileInfo = () => { { - const [newPrinterOpen, setNewPrinterOpen] = useState(false) const tableRef = useRef() // View mode state (cards/list), persisted in sessionStorage via custom hook @@ -34,37 +31,17 @@ const Printers = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Printers') - const actionItems = { - items: [ - { - label: 'New Printer', - key: 'newPrinter', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newPrinter') { - setNewPrinterOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewPrinterOpen(false) - }} - destroyOnHidden - > - { - setNewPrinterOpen(false) - tableRef.current?.reload() - }} - reset={newPrinterOpen} - /> - ) diff --git a/src/components/Dashboard/Production/Printers/ControlPrinter.jsx b/src/components/Dashboard/Production/Printers/ControlPrinter.jsx index b495fd85..224d6ff8 100644 --- a/src/components/Dashboard/Production/Printers/ControlPrinter.jsx +++ b/src/components/Dashboard/Production/Printers/ControlPrinter.jsx @@ -256,6 +256,7 @@ const ControlPrinter = ({ slicerIntegration = false }) => { { { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('SubJobs') - const actionItems = { - items: [ - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { { - const [newClientOpen, setNewClientOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('client') @@ -28,37 +25,17 @@ const Clients = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Clients') - const actionItems = { - items: [ - { - label: 'New Client', - key: 'newClient', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newClient') { - setNewClientOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - setNewClientOpen(false)} - footer={null} - destroyOnHidden={true} - width={700} - > - { - setNewClientOpen(false) - tableRef.current?.reload() - }} - reset={!newClientOpen} - /> - ) } diff --git a/src/components/Dashboard/Sales/Clients/ClientInfo.jsx b/src/components/Dashboard/Sales/Clients/ClientInfo.jsx index 9f3bd754..98688ad4 100644 --- a/src/components/Dashboard/Sales/Clients/ClientInfo.jsx +++ b/src/components/Dashboard/Sales/Clients/ClientInfo.jsx @@ -78,6 +78,7 @@ const ClientInfo = () => { { { - const [newListingOpen, setNewListingOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('listings') @@ -29,37 +26,17 @@ const Listings = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Listings') - const actionItems = { - items: [ - { - label: 'New Listing', - key: 'newListing', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newListing') { - setNewListingOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewListingOpen(false) - }} - destroyOnHidden={true} - > - { - setNewListingOpen(false) - tableRef.current?.reload() - }} - reset={newListingOpen} - /> - ) } diff --git a/src/components/Dashboard/Sales/Listings/ListingInfo.jsx b/src/components/Dashboard/Sales/Listings/ListingInfo.jsx index 35c3c6ab..018faf34 100644 --- a/src/components/Dashboard/Sales/Listings/ListingInfo.jsx +++ b/src/components/Dashboard/Sales/Listings/ListingInfo.jsx @@ -89,6 +89,7 @@ const ListingInfo = () => { { - const [newMarketplaceOpen, setNewMarketplaceOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('marketplaces') @@ -29,37 +26,17 @@ const Marketplaces = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('Marketplaces') - const actionItems = { - items: [ - { - label: 'New Marketplace', - key: 'newMarketplace', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newMarketplace') { - setNewMarketplaceOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewMarketplaceOpen(false) - }} - destroyOnHidden={true} - > - { - setNewMarketplaceOpen(false) - tableRef.current?.reload() - }} - reset={newMarketplaceOpen} - /> - ) } diff --git a/src/components/Dashboard/Sales/Marketplaces/MarketplaceInfo.jsx b/src/components/Dashboard/Sales/Marketplaces/MarketplaceInfo.jsx index c91d703e..abdeb792 100644 --- a/src/components/Dashboard/Sales/Marketplaces/MarketplaceInfo.jsx +++ b/src/components/Dashboard/Sales/Marketplaces/MarketplaceInfo.jsx @@ -158,6 +158,7 @@ const MarketplaceInfo = () => { { - const [newSalesOrderOpen, setNewSalesOrderOpen] = useState(false) const tableRef = useRef() const [viewMode, setViewMode] = useViewMode('salesOrders') @@ -29,37 +26,17 @@ const SalesOrders = () => { const [showSortSidebar, setShowSortSidebar] = useSortSidebarVisibility('SalesOrders') - const actionItems = { - items: [ - { - label: 'New Sales Order', - key: 'newSalesOrder', - icon: - }, - { type: 'divider' }, - { - label: 'Reload List', - key: 'reloadList', - icon: - } - ], - onClick: ({ key }) => { - if (key === 'reloadList') { - tableRef.current?.reload() - } else if (key === 'newSalesOrder') { - setNewSalesOrderOpen(true) - } - } - } return ( <> - - - + tableRef.current?.reload()} + /> { useSortInUrl={true} /> - { - setNewSalesOrderOpen(false) - }} - destroyOnHidden={true} - > - { - setNewSalesOrderOpen(false) - tableRef.current?.reload() - }} - reset={newSalesOrderOpen} - /> - ) } diff --git a/src/components/Dashboard/Sales/SalesOrders/SalesOrderInfo.jsx b/src/components/Dashboard/Sales/SalesOrders/SalesOrderInfo.jsx index ea2087cb..f4579d5c 100644 --- a/src/components/Dashboard/Sales/SalesOrders/SalesOrderInfo.jsx +++ b/src/components/Dashboard/Sales/SalesOrders/SalesOrderInfo.jsx @@ -107,6 +107,7 @@ const SalesOrderInfo = () => { { + const { userProfile } = useContext(AuthContext) + const { currentObjectType } = useActions() + const modelType = type || currentObjectType + const model = modelType ? getModelByName(modelType) : null + const denied = model + ? !hasActionPermission(userProfile, model, 'edit') + : false + const editDisabled = disabled || denied + return isEditing ? ( { - if (!disabled && !loading && formValid) { + if (!editDisabled && !loading && formValid) { handleUpdate() } }} @@ -30,7 +45,7 @@ const EditButtons = ({ type='primary' onClick={handleUpdate} loading={loading} - disabled={loading || !formValid || disabled} + disabled={loading || !formValid || editDisabled} /> { - if (!disabled && !loading) { + if (!editDisabled && !loading) { startEditing() } }} @@ -63,7 +78,7 @@ const EditButtons = ({ icon={} onClick={startEditing} loading={loading} - disabled={disabled || loading} + disabled={editDisabled || loading} /> ) @@ -76,7 +91,8 @@ EditButtons.propTypes = { startEditing: PropTypes.func.isRequired, formValid: PropTypes.bool.isRequired, disabled: PropTypes.any, - loading: PropTypes.bool.isRequired + loading: PropTypes.bool.isRequired, + type: PropTypes.string } export default EditButtons diff --git a/src/components/Dashboard/common/ObjectActions.jsx b/src/components/Dashboard/common/ObjectActions.jsx index 00c629de..3022a609 100644 --- a/src/components/Dashboard/common/ObjectActions.jsx +++ b/src/components/Dashboard/common/ObjectActions.jsx @@ -1,36 +1,38 @@ -import { createElement, useContext } from 'react' +import { createElement, useContext, useEffect, useRef } from 'react' import { Dropdown, Button } from 'antd' import { getModelByName } from '../../../database/ObjectModels' import PropTypes from 'prop-types' import { useNavigate, useLocation } from 'react-router-dom' import { useActionsModal } from '../context/ActionsModalContext' +import { useActions } from '../context/ActionsContext' import KeyboardShortcut from './KeyboardShortcut' import { AuthContext } from '../context/AuthContext' import { + actionVisibleOnPage, buildActionUrl, + getActionPermissionTarget, + resolveAction, stripPageActionParams } from '../../../utils/modelActions' +import { hasActionPermission } from '../../../database/permissions' -// Recursively filter actions based on visibleActions function filterActionsByVisibility(actions, visibleActions) { if (!visibleActions) return actions return actions.filter((action) => { if (action.type === 'divider') { - return true // Always show dividers + return true } const actionKey = action.key || action.name const isVisible = visibleActions[actionKey] !== false - // If this action has children, filter them recursively if (action.children && Array.isArray(action.children)) { const filteredChildren = filterActionsByVisibility( action.children, visibleActions ) action.children = filteredChildren - // Show parent if it has visible children or if it's explicitly visible return ( isVisible && (filteredChildren.length > 0 || visibleActions[actionKey] === true) @@ -74,18 +76,21 @@ function cleanDividers(items) { } function isSameActionUrl(action, actionUrl, currentUrl, pathname, search) { - if (action.type === 'modal') { + if (action.type === 'modal' || action.type === 'alias') { return actionUrl === currentUrl } - // Compare the action URL to the current location with its action param - // stripped. Default page actions (e.g. Info) match and stay hidden when - // already on that page. Actions that set their own action param (edit, - // cancelEdit, finishEdit, etc.) do not match and remain available — - // visibility is controlled by action.visible (e.g. _isEditing). return actionUrl === stripPageActionParams(pathname, search) } -// Recursively map actions to AntD Dropdown items +function actionDenied(userProfile, model, action, parentDenied = false) { + if (parentDenied) return true + const { model: permissionModel, actionName } = getActionPermissionTarget( + action, + model + ) + return !hasActionPermission(userProfile, permissionModel, actionName) +} + function mapActionsToMenuItems( actions, currentUrlWithActions, @@ -94,29 +99,36 @@ function mapActionsToMenuItems( userProfile, model, pathname, - search + search, + parentDenied = false ) { return cleanDividers( actions.map((action) => { if (action.type === 'divider') { return { type: 'divider' } } + const displayAction = resolveAction(action) const actionUrl = buildActionUrl(model, action, id, pathname, search) + const denied = actionDenied(userProfile, model, action, parentDenied) - var disabled = isSameActionUrl( - action, - actionUrl, - currentUrlWithActions, - pathname, - search - ) + var disabled = + denied || + isSameActionUrl( + action, + actionUrl, + currentUrlWithActions, + pathname, + search + ) var visible = true if (action.disabled) { if (typeof action.disabled === 'function') { - disabled = action.disabled({ ...objectData, _user: userProfile }) + disabled = + denied || + action.disabled({ ...objectData, _user: userProfile }) } else { - disabled = action.disabled + disabled = denied || action.disabled } } @@ -134,9 +146,11 @@ function mapActionsToMenuItems( const item = { key: action.key || action.name, - label: action.label, + label: displayAction.label, danger: action?.danger || false, - icon: action.icon ? createElement(action.icon) : undefined, + icon: displayAction.icon + ? createElement(displayAction.icon) + : undefined, disabled } if (action.children && Array.isArray(action.children)) { @@ -148,7 +162,8 @@ function mapActionsToMenuItems( userProfile, model, pathname, - search + search, + denied ) } return item @@ -159,7 +174,9 @@ function mapActionsToMenuItems( const ObjectActions = ({ type, id, - objectData, + objectData = {}, + pageName = 'info', + onReload, disabled = false, buttonProps = {}, visibleActions = {}, @@ -170,11 +187,23 @@ const ObjectActions = ({ const navigate = useNavigate() const location = useLocation() const { showActionsModal } = useActionsModal() + const { setOnModalOk } = useActions() const { userProfile } = useContext(AuthContext) + const onReloadRef = useRef(onReload) + onReloadRef.current = onReload + + useEffect(() => { + if (pageName !== 'list') return + setOnModalOk(() => () => onReloadRef.current?.()) + return () => setOnModalOk(null) + }, [pageName, setOnModalOk]) + + const pageActions = actions.filter((action) => + actionVisibleOnPage(action, pageName) + ) - // First filter by visibility, then by current URL const visibilityFilteredActions = filterActionsByVisibility( - actions, + pageActions, visibleActions ) @@ -200,7 +229,9 @@ const ObjectActions = ({ const currentUrlWithActions = location.pathname + location.search - // Compose AntD Dropdown menu items + const openActionsModal = () => + showActionsModal(id, type, objectData, pageName) + const menu = { items: mapActionsToMenuItems( filteredActions, @@ -213,36 +244,35 @@ const ObjectActions = ({ location.search ), onClick: (info) => { - // Find the action by key - const findAction = (acts, key) => { + const findMenuAction = (acts, key, parentDenied = false) => { for (const act of acts) { - if ((act.key || act.name) === key) return act + if (act.type === 'divider') continue + const denied = actionDenied(userProfile, model, act, parentDenied) + if ((act.key || act.name) === key) { + return denied ? null : act + } if (act.children) { - const found = findAction(act.children, key) + const found = findMenuAction(act.children, key, denied) if (found) return found } } return null } - const action = findAction(filteredActions, info.key) - if (action) { - navigate( - buildActionUrl(model, action, id, location.pathname, location.search) - ) - } + const action = findMenuAction(filteredActions, info.key) + if (!action) return + navigate( + buildActionUrl(model, action, id, location.pathname, location.search) + ) } } return ( - showActionsModal(id, type, objectData)} - > + @@ -253,8 +283,10 @@ const ObjectActions = ({ ObjectActions.propTypes = { type: PropTypes.string.isRequired, - objectData: PropTypes.object.isRequired, - id: PropTypes.string.isRequired, + objectData: PropTypes.object, + id: PropTypes.string, + pageName: PropTypes.string, + onReload: PropTypes.func, disabled: PropTypes.bool, buttonProps: PropTypes.object, buttonLabel: PropTypes.string, diff --git a/src/components/Dashboard/common/ObjectTable.jsx b/src/components/Dashboard/common/ObjectTable.jsx index c50243ae..e7dc4c71 100644 --- a/src/components/Dashboard/common/ObjectTable.jsx +++ b/src/components/Dashboard/common/ObjectTable.jsx @@ -55,6 +55,7 @@ import { getActiveFilterValues, useTableStatePersistence } from '../context/TableStateContext' +import { hasActionPermission } from '../../../database/permissions' const logger = loglevel.getLogger('DasboardTable') logger.setLevel(config.logLevel) @@ -432,15 +433,18 @@ const ObjectTable = forwardRef( return ( {rowActions.map((action, index) => { - var disabled = false + const denied = !hasActionPermission(userProfile, model, action.name) + var disabled = denied if (action.disabled) { if (typeof action.disabled === 'function') { - disabled = action.disabled({ - ...objectData, - _user: userProfile - }) + disabled = + denied || + action.disabled({ + ...objectData, + _user: userProfile + }) } else { - disabled = action.disabled + disabled = denied || action.disabled } } return ( @@ -457,6 +461,7 @@ const ObjectTable = forwardRef( type={'text'} size={'small'} onClick={() => { + if (denied) return if ( onRowAction && onRowAction(action, objectData) !== false diff --git a/src/components/Dashboard/common/PermissionsMatrix.jsx b/src/components/Dashboard/common/PermissionsMatrix.jsx index 20eb676f..9353a8b1 100644 --- a/src/components/Dashboard/common/PermissionsMatrix.jsx +++ b/src/components/Dashboard/common/PermissionsMatrix.jsx @@ -173,7 +173,10 @@ const getModelPaths = (model) => { } addPath(model?.url) - ;(model?.actions || []).forEach((action) => addPath(action?.url)) + ;(model?.actions || []).forEach((action) => { + if (action?.type === 'alias' || action?.type === 'callback') return + addPath(action?.url) + }) return [...new Set(paths.filter(Boolean))] } @@ -310,7 +313,13 @@ const PermissionsMatrix = ({ const { isElectron } = useContext(ElectronContext) const isMobile = useMediaQuery({ maxWidth: 768 }) const models = useMemo(() => getPermissionMatrixModels(), []) - const actions = useMemo(() => getPermissionMatrixActions(), []) + const actions = useMemo( + () => + getPermissionMatrixActions().filter( + (action) => action.type !== 'alias' && action.type !== 'callback' + ), + [] + ) const permissions = useMemo( () => (value && typeof value === 'object' ? value : {}), [value] diff --git a/src/components/Dashboard/common/UserProfilePopover.jsx b/src/components/Dashboard/common/UserProfilePopover.jsx index 9afc5bc7..70743364 100644 --- a/src/components/Dashboard/common/UserProfilePopover.jsx +++ b/src/components/Dashboard/common/UserProfilePopover.jsx @@ -140,6 +140,7 @@ const UserProfilePopover = ({ onClose }) => { {userProfile?._id && ( { if (actionName) { const action = findAction(model, actionName) - if (action?.type === 'modal') return + if (action?.type === 'modal' || action?.type === 'alias') return } const params = new URLSearchParams(location.search) @@ -112,7 +113,7 @@ const ActionsProvider = ({ children }) => { const action = findAction(model, actionName) if (!action) return - if (action.type === 'modal') { + if (action.type === 'modal' || action.type === 'alias') { lastHandledAction.current = actionKey setModalAction(action) return @@ -154,19 +155,31 @@ const ActionsProvider = ({ children }) => { ? { ...currentObject, _user: userProfile } : { _user: userProfile } + const resolvedModalAction = + modalAction?.type === 'alias' ? resolveAction(modalAction) : modalAction + const [modelWidth, setModelWidth] = useState(520) useEffect(() => { - if (modalAction?.modalWidth) { - setModelWidth(modalAction.modalWidth) + if (resolvedModalAction?.modalWidth) { + setModelWidth(resolvedModalAction.modalWidth) } - }, [modalAction?.modalWidth]) + }, [resolvedModalAction?.modalWidth]) const [modalCentered, setModalCentered] = useState(false) useEffect(() => { - if (modalAction?.modalCentered !== undefined) { - setModalCentered(modalAction.modalCentered) + if (resolvedModalAction?.modalCentered !== undefined) { + setModalCentered(resolvedModalAction.modalCentered) } - }, [modalAction?.modalCentered]) + }, [resolvedModalAction?.modalCentered]) + + const modalContentData = + modalAction?.type === 'alias' + ? typeof modalAction.objectData === 'function' + ? modalAction.objectData(modalObjectData) + : (modalAction.objectData ?? {}) + : resolvedModalAction?.name === 'new' + ? {} + : modalObjectData return ( { }} > { } > - {modalAction?.content && modalObjectData - ? modalAction.content(modalObjectData, { onOk: handleModalOk }) + {resolvedModalAction?.content + ? resolvedModalAction.content(modalContentData, { + onOk: handleModalOk + }) : null} diff --git a/src/components/Dashboard/context/ActionsModalContext.jsx b/src/components/Dashboard/context/ActionsModalContext.jsx index 3788a27d..5dc72caa 100644 --- a/src/components/Dashboard/context/ActionsModalContext.jsx +++ b/src/components/Dashboard/context/ActionsModalContext.jsx @@ -11,9 +11,13 @@ import PropTypes from 'prop-types' import { useLocation, useNavigate } from 'react-router-dom' import { getModelByName } from '../../../database/ObjectModels' +import { hasActionPermission } from '../../../database/permissions' import { AuthContext } from './AuthContext' import { + actionVisibleOnPage, buildActionUrl, + getActionPermissionTarget, + resolveAction, stripActionParams } from '../../../utils/modelActions' import ElipsisText from '../common/ElipsisText' @@ -24,7 +28,14 @@ const ActionsModalContext = createContext() const stripActionParam = stripActionParams // Flatten nested actions (including children) into a single list -const flattenActions = (actions, parentLabel = '') => { +const flattenActions = ( + actions, + parentLabel = '', + userProfile, + model, + parentDenied = false, + pageName = 'info' +) => { if (!Array.isArray(actions)) return [] const flat = [] @@ -34,29 +45,52 @@ const flattenActions = (actions, parentLabel = '') => { return } + if (!actionVisibleOnPage(action, pageName)) { + return + } + + const { model: permissionModel, actionName } = getActionPermissionTarget( + action, + model + ) + const denied = + parentDenied || + !hasActionPermission(userProfile, permissionModel, actionName) + + const displayAction = resolveAction(action) const hasUrl = typeof action.url === 'function' || action.type === 'page' || - action.type === 'modal' + action.type === 'modal' || + action.type === 'alias' const hasChildren = Array.isArray(action.children) && action.children.length > 0 - const currentLabel = action.label || action.name || '' + const currentLabel = displayAction.label || action.name || '' const fullLabel = parentLabel ? `${parentLabel} / ${currentLabel}` : currentLabel - // Only push actions that are actually runnable if (hasUrl) { flat.push({ - ...action, + ...displayAction, key: action.key || action.name || fullLabel, - fullLabel + fullLabel, + permissionDenied: denied }) } if (hasChildren) { - flat.push(...flattenActions(action.children, fullLabel)) + flat.push( + ...flattenActions( + action.children, + fullLabel, + userProfile, + model, + denied, + pageName + ) + ) } }) @@ -74,13 +108,14 @@ const ActionsModalProvider = ({ children }) => { const [context, setContext] = useState({ id: null, type: null, - objectData: null + objectData: null, + pageName: 'info' }) const inputRef = useRef(null) - const showActionsModal = (id, type, objectData = null) => { - setContext({ id, type, objectData }) + const showActionsModal = (id, type, objectData = null, pageName = 'info') => { + setContext({ id, type, objectData, pageName }) setQuery('') setVisible(true) } @@ -110,10 +145,17 @@ const ActionsModalProvider = ({ children }) => { const ModelIcon = model?.icon || null const modelLabel = model?.label || model?.name || '' - const flattenedActions = useMemo( - () => flattenActions(model?.actions || []), - [model] - ) + const flattenedActions = useMemo(() => { + const pageName = context.pageName || 'info' + return flattenActions( + model?.actions || [], + '', + userProfile, + model, + false, + pageName + ) + }, [model, userProfile, context.pageName]) const currentUrlWithoutActions = stripActionParam( location.pathname, @@ -146,6 +188,10 @@ const ActionsModalProvider = ({ children }) => { } } + if (action.permissionDenied) { + disabled = true + } + return disabled } diff --git a/src/components/Dashboard/context/ApiServerContext.jsx b/src/components/Dashboard/context/ApiServerContext.jsx index b664d07e..e9ba4a74 100644 --- a/src/components/Dashboard/context/ApiServerContext.jsx +++ b/src/components/Dashboard/context/ApiServerContext.jsx @@ -1032,7 +1032,9 @@ const ApiServerProvider = ({ children }) => { console.error(error.response.data.method) const code = error.response.data.code || 'UNKNOWN' if (code == 'UNAUTHORIZED') { - setUnauthenticated() + if (token) { + setUnauthenticated() + } return } if (code == 'FORBIDDEN') { @@ -1056,7 +1058,7 @@ const ApiServerProvider = ({ children }) => { setRetryCallback(() => callback) setShowErrorModal(true) }, - [setUnauthenticated] + [setUnauthenticated, token] ) const handleRetry = () => { diff --git a/src/components/Dashboard/context/AuthContext.jsx b/src/components/Dashboard/context/AuthContext.jsx index f7cb529c..8fb38610 100644 --- a/src/components/Dashboard/context/AuthContext.jsx +++ b/src/components/Dashboard/context/AuthContext.jsx @@ -23,7 +23,7 @@ import ExclamationOctogonIcon from '../../Icons/ExclamationOctagonIcon' import InfoCircleIcon from '../../Icons/InfoCircleIcon' import config from '../../../config' import loglevel from 'loglevel' -import { ElectronContext } from './ElectronContext' +import { ElectronContext, isElectron as detectIsElectron } from './ElectronContext' import { useLocation, useNavigate } from 'react-router-dom' import { getAuthCookies, @@ -59,14 +59,43 @@ const AuthProvider = ({ children }) => { const [messageApi, contextHolder] = message.useMessage() const [notificationApi, notificationContextHolder] = notification.useNotification() - const [authenticated, setAuthenticated] = useState(false) + const [initialWebAuth] = useState(() => { + if (detectIsElectron()) { + return { retrieved: false } + } + try { + if (validateAuthCookies()) { + const session = getAuthCookies() + return { + retrieved: true, + token: session.token, + expiresAt: session.expiresAt, + user: session.user, + authenticated: Boolean(session.token) + } + } + } catch (error) { + console.error('Error reading initial auth session:', error) + } + return { + retrieved: true, + token: null, + expiresAt: null, + user: null, + authenticated: false + } + }) + const [authenticated, setAuthenticated] = useState( + Boolean(initialWebAuth.authenticated) + ) const [initialized, setInitialized] = useState(false) - const [retreivedTokenFromCookies, setRetreivedTokenFromCookies] = - useState(false) + const [retreivedTokenFromCookies, setRetreivedTokenFromCookies] = useState( + Boolean(initialWebAuth.retrieved) + ) const [loading, setLoading] = useState(false) - const [token, setToken] = useState(null) - const [expiresAt, setExpiresAt] = useState(null) - const [userProfile, setUserProfile] = useState(null) + const [token, setToken] = useState(initialWebAuth.token ?? null) + const [expiresAt, setExpiresAt] = useState(initialWebAuth.expiresAt ?? null) + const [userProfile, setUserProfile] = useState(initialWebAuth.user ?? null) const [profileImageUrl, setProfileImageUrl] = useState(null) const profileImageUrlRef = useRef(null) const processedAuthCodeRef = useRef(null) @@ -83,6 +112,13 @@ const AuthProvider = ({ children }) => { } = useContext(ElectronContext) const location = useLocation() const navigate = useNavigate() + const sessionRef = useRef({ + token, + expiresAt, + userProfile, + authenticated + }) + sessionRef.current = { token, expiresAt, userProfile, authenticated } var redirectType = 'web' @@ -158,7 +194,7 @@ const AuthProvider = ({ children }) => { if (isElectron) return if (!areCookiesEnabled()) { messageApi.warning( - 'Cookies are disabled. Login state may not persist between tabs.' + 'Browser storage is disabled. Login state may not persist between tabs.' ) } }, [messageApi, isElectron]) @@ -192,35 +228,31 @@ const AuthProvider = ({ children }) => { setUserProfile(null) setShowUnauthorizedModal(true) } - } else { - // First validate existing cookies to clean up expired ones - if (validateAuthCookies()) { - const { - token: storedToken, - expiresAt: storedExpiresAt, - user: storedUser - } = getAuthCookies() + } else if (validateAuthCookies()) { + const { + token: storedToken, + expiresAt: storedExpiresAt, + user: storedUser + } = getAuthCookies() - if (!cancelled) { - setToken(storedToken) - setUserProfile(storedUser) - setExpiresAt(storedExpiresAt) - setAuthenticated(true) + if (!cancelled) { + setToken(storedToken) + setUserProfile(storedUser) + setExpiresAt(storedExpiresAt) + setAuthenticated(true) - if (storedToken && storedUser) { - getUserInfo(storedToken, () => cancelled) - } + if (storedToken) { + getUserInfo(storedToken, () => cancelled) } - } else if (!cancelled) { - setAuthenticated(false) - setUserProfile(null) - setShowUnauthorizedModal(true) } + } else if (!cancelled && !sessionRef.current.token) { + setAuthenticated(false) + setUserProfile(null) + setShowUnauthorizedModal(true) } } catch (error) { console.error('Error loading persisted auth session:', error) - await clearPersistedSession() - if (!cancelled) { + if (!cancelled && !sessionRef.current.token) { setAuthenticated(false) setUserProfile(null) setShowUnauthorizedModal(true) @@ -243,11 +275,11 @@ const AuthProvider = ({ children }) => { // eslint-disable-next-line react-hooks/exhaustive-deps -- run only on mount to load persisted session; deps are stable in behavior }, []) - // Set up cookie synchronization between tabs + // Sync auth from other tabs. Only treat a missing token as logout — never + // drop a valid in-memory session because another tab is still booting. useEffect(() => { if (isElectron) return const cleanupCookieSync = setupCookieSync(() => { - // When cookies change in another tab, re-validate and update state try { if (validateAuthCookies()) { const { @@ -255,27 +287,34 @@ const AuthProvider = ({ children }) => { expiresAt: newExpiresAt, user: newUser } = getAuthCookies() + const current = sessionRef.current + const expiresChanged = + String(newExpiresAt ?? '') !== String(current.expiresAt ?? '') + const userChanged = + newUser != null && + JSON.stringify(newUser) !== JSON.stringify(current.userProfile) if ( - newToken !== token || - newExpiresAt !== expiresAt || - JSON.stringify(newUser) !== JSON.stringify(userProfile) + newToken !== current.token || + expiresChanged || + userChanged ) { setToken(newToken) setExpiresAt(newExpiresAt) - setUserProfile(newUser) + if (newUser) setUserProfile(newUser) setAuthenticated(true) + setShowUnauthorizedModal(false) logger.debug('Auth state synchronized from another tab') } } else { - // Cookies are invalid, clear state - setToken(null) - setExpiresAt(null) - setUserProfile(null) - setAuthenticated(false) - setShowUnauthorizedModal(true) - logger.debug( - 'Auth state cleared due to invalid cookies from another tab' - ) + const { token: storedToken } = getAuthCookies() + if (!storedToken && sessionRef.current.token) { + setToken(null) + setExpiresAt(null) + setUserProfile(null) + setAuthenticated(false) + setShowUnauthorizedModal(true) + logger.debug('Auth state cleared due to logout in another tab') + } } } catch (error) { console.error('Error syncing auth state:', error) @@ -283,7 +322,7 @@ const AuthProvider = ({ children }) => { }) return cleanupCookieSync - }, [token, expiresAt, userProfile, isElectron]) + }, [isElectron]) // Persist userProfile changes to cookies/electron storage so updates (e.g. from // WebSocket or profile edits) are saved for session restoration @@ -535,16 +574,19 @@ const AuthProvider = ({ children }) => { } }, [token, messageApi, persistSession]) - const setUnauthenticated = () => { + const setUnauthenticated = useCallback(() => { + const hadSession = Boolean(sessionRef.current.token) setToken(null) setExpiresAt(null) setUserProfile(null) - clearPersistedSession() setAuthenticated(false) + if (hadSession) { + clearPersistedSession() + } if (showSessionExpiredModal == false) { setShowUnauthorizedModal(true) } - } + }, [clearPersistedSession, showSessionExpiredModal]) const refreshToken = useCallback(async () => { try { diff --git a/src/database/ObjectModels.js b/src/database/ObjectModels.js index b4a74897..f772d26b 100644 --- a/src/database/ObjectModels.js +++ b/src/database/ObjectModels.js @@ -245,10 +245,12 @@ export function getPermissionMatrixActions() { ;(model.actions || []).forEach((action) => { if (!action?.name || action.type === 'divider') return if (HIDDEN_PERMISSION_ACTIONS.has(action.name)) return + if (action.type === 'alias' || action.type === 'callback') return if (!seen.has(action.name)) { seen.set(action.name, { name: action.name, - label: action.label || action.name + type: action.type, + label: action.name === 'new' ? 'New' : action.label || action.name }) } }) @@ -258,7 +260,11 @@ export function getPermissionMatrixActions() { export function modelHasPermissionAction(model, actionName) { return (model?.actions || []).some( - (action) => action?.name === actionName && action.type !== 'divider' + (action) => + action?.name === actionName && + action.type !== 'divider' && + action.type !== 'alias' && + action.type !== 'callback' ) } diff --git a/src/database/models/AppPassword.js b/src/database/models/AppPassword.js index 4657e6ea..d0361ab2 100644 --- a/src/database/models/AppPassword.js +++ b/src/database/models/AppPassword.js @@ -4,11 +4,15 @@ const AppPasswordInfo = lazy( () => import('../../components/Dashboard/Management/AppPasswords/AppPasswordInfo') ) +const NewAppPassword = lazy( + () => import('../../components/Dashboard/Management/AppPasswords/NewAppPassword') +) const RegenerateAppPasswordSecret = lazy( () => import('../../components/Dashboard/Management/AppPasswords/RegenerateAppPasswordSecret') ) import AppPasswordIcon from '../../components/Icons/AppPasswordIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -23,6 +27,17 @@ export const AppPassword = { prefix: 'APP', icon: AppPasswordIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New App Password', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewAppPassword, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Client.js b/src/database/models/Client.js index 17ea3099..1c380cd3 100644 --- a/src/database/models/Client.js +++ b/src/database/models/Client.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const ClientInfo = lazy( () => import('../../components/Dashboard/Sales/Clients/ClientInfo') ) +const NewClient = lazy( + () => import('../../components/Dashboard/Sales/Clients/NewClient') +) import ClientIcon from '../../components/Icons/ClientIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const Client = { prefix: 'CLI', icon: ClientIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Client', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewClient, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Courier.js b/src/database/models/Courier.js index 3e8cc113..0a8c4d13 100644 --- a/src/database/models/Courier.js +++ b/src/database/models/Courier.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const CourierInfo = lazy( () => import('../../components/Dashboard/Management/Couriers/CourierInfo') ) +const NewCourier = lazy( + () => import('../../components/Dashboard/Management/Couriers/NewCourier') +) import CourierIcon from '../../components/Icons/CourierIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const Courier = { prefix: 'COR', icon: CourierIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Courier', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewCourier, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/CourierService.js b/src/database/models/CourierService.js index e5b186d5..9de98067 100644 --- a/src/database/models/CourierService.js +++ b/src/database/models/CourierService.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const CourierServiceInfo = lazy( () => import('../../components/Dashboard/Management/CourierServices/CourierServiceInfo') ) +const NewCourierService = lazy( + () => import('../../components/Dashboard/Management/CourierServices/NewCourierService') +) import CourierServiceIcon from '../../components/Icons/CourierServiceIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const CourierService = { prefix: 'COS', icon: CourierServiceIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Courier Service', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewCourierService, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/DocumentJob.js b/src/database/models/DocumentJob.js index 35c864ff..ddeb5ede 100644 --- a/src/database/models/DocumentJob.js +++ b/src/database/models/DocumentJob.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const DocumentJobInfo = lazy( () => import('../../components/Dashboard/Management/DocumentJobs/DocumentJobInfo') ) +const NewDocumentJob = lazy( + () => import('../../components/Dashboard/Management/DocumentJobs/NewDocumentJob') +) import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' @@ -18,6 +22,17 @@ export const DocumentJob = { prefix: 'DJB', icon: DocumentJobIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 900, + label: 'New Document Job', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewDocumentJob, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/DocumentPrinter.js b/src/database/models/DocumentPrinter.js index bcdcda95..7554efc2 100644 --- a/src/database/models/DocumentPrinter.js +++ b/src/database/models/DocumentPrinter.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const DocumentPrinterInfo = lazy( () => import('../../components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo') ) +const NewDocumentPrinter = lazy( + () => import('../../components/Dashboard/Management/DocumentPrinters/NewDocumentPrinter') +) import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' @@ -17,6 +21,17 @@ export const DocumentPrinter = { prefix: 'DPR', icon: DocumentPrinterIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Document Printer', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewDocumentPrinter, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/DocumentSize.js b/src/database/models/DocumentSize.js index f1d74656..ca4745a2 100644 --- a/src/database/models/DocumentSize.js +++ b/src/database/models/DocumentSize.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const DocumentSizeInfo = lazy( () => import('../../components/Dashboard/Management/DocumentSizes/DocumentSizeInfo') ) +const NewDocumentSize = lazy( + () => import('../../components/Dashboard/Management/DocumentSizes/NewDocumentSize') +) import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' @@ -17,6 +21,17 @@ export const DocumentSize = { prefix: 'DSZ', icon: DocumentSizeIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Document Size', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewDocumentSize, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/DocumentTemplate.js b/src/database/models/DocumentTemplate.js index 0c339da6..bd2a5a07 100644 --- a/src/database/models/DocumentTemplate.js +++ b/src/database/models/DocumentTemplate.js @@ -4,7 +4,11 @@ const DocumentTemplateInfo = lazy( () => import('../../components/Dashboard/Management/DocumentTemplates/DocumentTemplateInfo') ) +const NewDocumentTemplate = lazy( + () => import('../../components/Dashboard/Management/DocumentTemplates/NewDocumentTemplate') +) import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' @@ -19,6 +23,17 @@ export const DocumentTemplate = { prefix: 'DTP', icon: DocumentTemplateIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Document Template', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewDocumentTemplate, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'design', label: 'Design', diff --git a/src/database/models/Filament.js b/src/database/models/Filament.js index dff1d7eb..35eebbc6 100644 --- a/src/database/models/Filament.js +++ b/src/database/models/Filament.js @@ -3,15 +3,15 @@ import { createElement, lazy } from 'react' const FilamentInfo = lazy( () => import('../../components/Dashboard/Management/Filaments/FilamentInfo') ) -const NewFilamentSku = lazy( - () => import('../../components/Dashboard/Management/FilamentSkus/NewFilamentSku') +const NewFilament = lazy( + () => import('../../components/Dashboard/Management/Filaments/NewFilament') ) import EditIcon from '../../components/Icons/EditIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' import FilamentIcon from '../../components/Icons/FilamentIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' -import PlusIcon from '../../components/Icons/PlusIcon' import BinIcon from '../../components/Icons/BinIcon' export const Filament = { @@ -22,6 +22,17 @@ export const Filament = { prefix: 'FIL', icon: FilamentIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Filament', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewFilament, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', @@ -65,16 +76,13 @@ export const Filament = { { type: 'divider' }, { name: 'newFilamentSku', - type: 'modal', - modalWidth: 700, - label: 'New Filament SKU', - icon: PlusIcon, + type: 'alias', + objectType: 'filamentSku', + aliasAction: 'new', visible: (objectData) => { return !(objectData?._isEditing && objectData?._isEditing == true) }, - content: (objectData, { onOk } = {}) => { - return createElement(NewFilamentSku, { defaultValues: { filament: objectData }, onOk, reset: true }) - } + objectData: (objectData) => ({ filament: objectData }) }, { type: 'divider' }, { diff --git a/src/database/models/FilamentProfile.js b/src/database/models/FilamentProfile.js index 477ce5eb..41712544 100644 --- a/src/database/models/FilamentProfile.js +++ b/src/database/models/FilamentProfile.js @@ -4,7 +4,11 @@ const FilamentProfileInfo = lazy( () => import('../../components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo') ) +const NewFilamentProfile = lazy( + () => import('../../components/Dashboard/Production/FilamentProfiles/NewFilamentProfile') +) import FilamentProfileIcon from '../../components/Icons/FilamentProfileIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -19,6 +23,17 @@ export const FilamentProfile = { prefix: 'FPF', icon: FilamentProfileIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Filament Profile', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewFilamentProfile, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/FilamentSku.js b/src/database/models/FilamentSku.js index 3aced3df..1e9fa6b7 100644 --- a/src/database/models/FilamentSku.js +++ b/src/database/models/FilamentSku.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const FilamentSkuInfo = lazy( () => import('../../components/Dashboard/Management/FilamentSkus/FilamentSkuInfo') ) +const NewFilamentSku = lazy( + () => import('../../components/Dashboard/Management/FilamentSkus/NewFilamentSku') +) import FilamentSkuIcon from '../../components/Icons/FilamentSkuIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const FilamentSku = { prefix: 'FSU', icon: FilamentSkuIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Filament SKU', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewFilamentSku, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/FilamentStock.js b/src/database/models/FilamentStock.js index 2358474d..8add1289 100644 --- a/src/database/models/FilamentStock.js +++ b/src/database/models/FilamentStock.js @@ -4,7 +4,11 @@ const FilamentStockInfo = lazy( () => import('../../components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo') ) +const NewFilamentStock = lazy( + () => import('../../components/Dashboard/Inventory/FilamentStocks/NewFilamentStock') +) import FilamentStockIcon from '../../components/Icons/FilamentStockIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' export const FilamentStock = { @@ -16,6 +20,17 @@ export const FilamentStock = { readOnly: true, icon: FilamentStockIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Filament Stock', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewFilamentStock, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/GCodeFile.js b/src/database/models/GCodeFile.js index e1c861b8..4ef683af 100644 --- a/src/database/models/GCodeFile.js +++ b/src/database/models/GCodeFile.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const GCodeFileInfo = lazy( () => import('../../components/Dashboard/Production/GCodeFiles/GCodeFileInfo') ) +const NewGCodeFile = lazy( + () => import('../../components/Dashboard/Production/GCodeFiles/NewGCodeFile') +) import DownloadIcon from '../../components/Icons/DownloadIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' @@ -19,6 +23,17 @@ export const GCodeFile = { prefix: 'GCF', icon: GCodeFileIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New G-Code File', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewGCodeFile, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'preview', label: 'Preview', diff --git a/src/database/models/Host.js b/src/database/models/Host.js index 2fa6d591..49182249 100644 --- a/src/database/models/Host.js +++ b/src/database/models/Host.js @@ -3,10 +3,14 @@ import { createElement, lazy } from 'react' const HostInfo = lazy( () => import('../../components/Dashboard/Management/Hosts/HostInfo') ) +const NewHost = lazy( + () => import('../../components/Dashboard/Management/Hosts/NewHost') +) const HostOTP = lazy( () => import('../../components/Dashboard/Management/Hosts/HostOtp') ) import HostIcon from '../../components/Icons/HostIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -22,6 +26,17 @@ export const Host = { prefix: 'HST', icon: HostIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Host', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewHost, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Invoice.js b/src/database/models/Invoice.js index e9cb406f..1239e3cd 100644 --- a/src/database/models/Invoice.js +++ b/src/database/models/Invoice.js @@ -3,22 +3,22 @@ import { createElement, lazy } from 'react' const InvoiceInfo = lazy( () => import('../../components/Dashboard/Finance/Invoices/InvoiceInfo') ) +const NewInvoice = lazy( + () => import('../../components/Dashboard/Finance/Invoices/NewInvoice') +) const PostInvoice = lazy( () => import('../../components/Dashboard/Finance/Invoices/PostInvoice') ) const AcknowledgeInvoice = lazy( () => import('../../components/Dashboard/Finance/Invoices/AcknowledgeInvoice') ) -const NewPayment = lazy( - () => import('../../components/Dashboard/Finance/Payments/NewPayment') -) import InvoiceIcon from '../../components/Icons/InvoiceIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import CheckIcon from '../../components/Icons/CheckIcon' import EditIcon from '../../components/Icons/EditIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' import BinIcon from '../../components/Icons/BinIcon' -import PlusIcon from '../../components/Icons/PlusIcon' export const Invoice = { name: 'invoice', @@ -28,6 +28,17 @@ export const Invoice = { prefix: 'INV', icon: InvoiceIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Invoice', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewInvoice, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', @@ -88,24 +99,17 @@ export const Invoice = { { type: 'divider' }, { name: 'newPayment', - type: 'modal', - modalWidth: 700, - label: 'New Payment', - icon: PlusIcon, + type: 'alias', + objectType: 'payment', + aliasAction: 'new', disabled: (objectData) => { const allowedStates = ['acknowledged', 'partiallyPaid', 'overdue'] return !allowedStates.includes(objectData?.state?.type) }, - content: (objectData, { onOk } = {}) => { - return createElement(NewPayment, { - defaultValues: { - invoice: objectData, - amount: objectData?.grandTotalAmount - }, - onOk, - reset: true - }) - } + objectData: (objectData) => ({ + invoice: objectData, + amount: objectData?.grandTotalAmount + }) }, { type: 'divider' }, { diff --git a/src/database/models/Job.js b/src/database/models/Job.js index a12121c1..73f63754 100644 --- a/src/database/models/Job.js +++ b/src/database/models/Job.js @@ -3,10 +3,14 @@ import { createElement, lazy } from 'react' const JobInfo = lazy( () => import('../../components/Dashboard/Production/Jobs/JobInfo') ) +const NewJob = lazy( + () => import('../../components/Dashboard/Production/Jobs/NewJob') +) const DeployJob = lazy( () => import('../../components/Dashboard/Production/Jobs/DeployJob') ) import JobIcon from '../../components/Icons/JobIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' @@ -22,6 +26,17 @@ export const Job = { prefix: 'JOB', icon: JobIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Job', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewJob, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Listing.js b/src/database/models/Listing.js index badb5e76..3021eb6e 100644 --- a/src/database/models/Listing.js +++ b/src/database/models/Listing.js @@ -3,9 +3,8 @@ import { createElement, lazy } from 'react' const ListingInfo = lazy( () => import('../../components/Dashboard/Sales/Listings/ListingInfo') ) -const NewListingVarient = lazy( - () => - import('../../components/Dashboard/Sales/ListingVarients/NewListingVarient') +const NewListing = lazy( + () => import('../../components/Dashboard/Sales/Listings/NewListing') ) const PublishListing = lazy( () => import('../../components/Dashboard/Sales/Listings/PublishListing') @@ -14,12 +13,12 @@ const UnpublishListing = lazy( () => import('../../components/Dashboard/Sales/Listings/UnpublishListing') ) import ListingIcon from '../../components/Icons/ListingIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' import BinIcon from '../../components/Icons/BinIcon' -import PlusIcon from '../../components/Icons/PlusIcon' export const Listing = { name: 'listing', @@ -29,6 +28,17 @@ export const Listing = { prefix: 'LST', icon: ListingIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Listing', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewListing, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', @@ -72,17 +82,10 @@ export const Listing = { { type: 'divider' }, { name: 'newListingVarient', - type: 'modal', - modalWidth: 700, - label: 'New Listing Varient', - icon: PlusIcon, - content: (objectData, { onOk } = {}) => { - return createElement(NewListingVarient, { - defaultValues: { listing: objectData }, - onOk, - reset: true - }) - } + type: 'alias', + objectType: 'listingVarient', + aliasAction: 'new', + objectData: (objectData) => ({ listing: objectData }) }, { type: 'divider' }, { diff --git a/src/database/models/ListingVarient.js b/src/database/models/ListingVarient.js index ed3973af..fd8bfd65 100644 --- a/src/database/models/ListingVarient.js +++ b/src/database/models/ListingVarient.js @@ -3,6 +3,9 @@ import { createElement, lazy } from 'react' const ListingVarientInfo = lazy( () => import('../../components/Dashboard/Sales/ListingVarients/ListingVarientInfo') ) +const NewListingVarient = lazy( + () => import('../../components/Dashboard/Sales/ListingVarients/NewListingVarient') +) const PublishListingVarient = lazy( () => import('../../components/Dashboard/Sales/ListingVarients/PublishListingVarient') ) @@ -10,6 +13,7 @@ const UnpublishListingVarient = lazy( () => import('../../components/Dashboard/Sales/ListingVarients/UnpublishListingVarient') ) import ListingVarientIcon from '../../components/Icons/ListingVarientIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -24,6 +28,17 @@ export const ListingVarient = { prefix: 'LVR', icon: ListingVarientIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Listing Varient', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewListingVarient, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Marketplace.js b/src/database/models/Marketplace.js index d83d0827..b217a5f7 100644 --- a/src/database/models/Marketplace.js +++ b/src/database/models/Marketplace.js @@ -3,6 +3,9 @@ import { createElement, lazy } from 'react' const MarketplaceInfo = lazy( () => import('../../components/Dashboard/Sales/Marketplaces/MarketplaceInfo') ) +const NewMarketplace = lazy( + () => import('../../components/Dashboard/Sales/Marketplaces/NewMarketplace') +) const SyncListings = lazy( () => import('../../components/Dashboard/Sales/Marketplaces/SyncListings') ) @@ -10,6 +13,7 @@ const SyncOrders = lazy( () => import('../../components/Dashboard/Sales/Marketplaces/SyncOrders') ) import MarketplaceIcon from '../../components/Icons/MarketplaceIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -26,6 +30,17 @@ export const Marketplace = { prefix: 'MKT', icon: MarketplaceIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Marketplace', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewMarketplace, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Material.js b/src/database/models/Material.js index 7f35e6fc..95febbe8 100644 --- a/src/database/models/Material.js +++ b/src/database/models/Material.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const MaterialInfo = lazy( () => import('../../components/Dashboard/Management/Materials/MaterialInfo') ) +const NewMaterial = lazy( + () => import('../../components/Dashboard/Management/Materials/NewMaterial') +) import MaterialIcon from '../../components/Icons/MaterialIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const Material = { prefix: 'MAT', icon: MaterialIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Material', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewMaterial, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/NoteType.js b/src/database/models/NoteType.js index 66c3d227..d4721e9c 100644 --- a/src/database/models/NoteType.js +++ b/src/database/models/NoteType.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const NoteTypeInfo = lazy( () => import('../../components/Dashboard/Management/NoteTypes/NoteTypeInfo') ) +const NewNoteType = lazy( + () => import('../../components/Dashboard/Management/NoteTypes/NewNoteType') +) import NoteTypeIcon from '../../components/Icons/NoteTypeIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -17,6 +21,17 @@ export const NoteType = { prefix: 'NTY', icon: NoteTypeIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Note Type', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewNoteType, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/OrderItem.js b/src/database/models/OrderItem.js index f4c616ec..b8df8101 100644 --- a/src/database/models/OrderItem.js +++ b/src/database/models/OrderItem.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const OrderItemInfo = lazy( () => import('../../components/Dashboard/Inventory/OrderItems/OrderItemInfo') ) +const NewOrderItem = lazy( + () => import('../../components/Dashboard/Inventory/OrderItems/NewOrderItem') +) import OrderItemIcon from '../../components/Icons/OrderItemIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const OrderItem = { prefix: 'ODI', icon: OrderItemIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Order Item', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewOrderItem, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Part.js b/src/database/models/Part.js index 7e89a318..a8255ad5 100644 --- a/src/database/models/Part.js +++ b/src/database/models/Part.js @@ -3,15 +3,15 @@ import { createElement, lazy } from 'react' const PartInfo = lazy( () => import('../../components/Dashboard/Management/Parts/PartInfo') ) -const NewPartSku = lazy( - () => import('../../components/Dashboard/Management/PartSkus/NewPartSku') +const NewPart = lazy( + () => import('../../components/Dashboard/Management/Parts/NewPart') ) import EditIcon from '../../components/Icons/EditIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import PartIcon from '../../components/Icons/PartIcon' -import PlusIcon from '../../components/Icons/PlusIcon' export const Part = { name: 'part', @@ -21,6 +21,17 @@ export const Part = { prefix: 'PRT', icon: PartIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Part', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewPart, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', @@ -64,16 +75,13 @@ export const Part = { { type: 'divider' }, { name: 'newPartSku', - type: 'modal', - modalWidth: 700, - label: 'New Part SKU', - icon: PlusIcon, + type: 'alias', + objectType: 'partSku', + aliasAction: 'new', visible: (objectData) => { return !(objectData?._isEditing && objectData?._isEditing == true) }, - content: (objectData, { onOk } = {}) => { - return createElement(NewPartSku, { defaultValues: { part: objectData }, onOk, reset: true }) - } + objectData: (objectData) => ({ part: objectData }) } ], pages: [ diff --git a/src/database/models/PartSku.js b/src/database/models/PartSku.js index 0d34cf33..9bdd1fa7 100644 --- a/src/database/models/PartSku.js +++ b/src/database/models/PartSku.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const PartSkuInfo = lazy( () => import('../../components/Dashboard/Management/PartSkus/PartSkuInfo') ) +const NewPartSku = lazy( + () => import('../../components/Dashboard/Management/PartSkus/NewPartSku') +) import PartSkuIcon from '../../components/Icons/PartSkuIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const PartSku = { prefix: 'PSU', icon: PartSkuIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Part SKU', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewPartSku, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/PartStock.js b/src/database/models/PartStock.js index baf15d96..129b9384 100644 --- a/src/database/models/PartStock.js +++ b/src/database/models/PartStock.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const PartStockInfo = lazy( () => import('../../components/Dashboard/Inventory/PartStocks/PartStockInfo') ) +const NewPartStock = lazy( + () => import('../../components/Dashboard/Inventory/PartStocks/NewPartStock') +) import PartStockIcon from '../../components/Icons/PartStockIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' export const PartStock = { @@ -15,6 +19,17 @@ export const PartStock = { readOnly: true, icon: PartStockIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Part Stock', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewPartStock, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Payment.js b/src/database/models/Payment.js index 6f736644..e986657c 100644 --- a/src/database/models/Payment.js +++ b/src/database/models/Payment.js @@ -3,6 +3,9 @@ import { createElement, lazy } from 'react' const PaymentInfo = lazy( () => import('../../components/Dashboard/Finance/Payments/PaymentInfo') ) +const NewPayment = lazy( + () => import('../../components/Dashboard/Finance/Payments/NewPayment') +) const PostPayment = lazy( () => import('../../components/Dashboard/Finance/Payments/PostPayment') ) @@ -16,6 +19,7 @@ const CancelPayment = lazy( () => import('../../components/Dashboard/Finance/Payments/CancelPayment') ) import PaymentIcon from '../../components/Icons/PaymentIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import CheckIcon from '../../components/Icons/CheckIcon' import EditIcon from '../../components/Icons/EditIcon' @@ -30,6 +34,17 @@ export const Payment = { prefix: 'PAY', icon: PaymentIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Payment', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewPayment, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/PermissionSetting.js b/src/database/models/PermissionSetting.js index b38510da..c7015e08 100644 --- a/src/database/models/PermissionSetting.js +++ b/src/database/models/PermissionSetting.js @@ -4,7 +4,11 @@ const PermissionSettingInfo = lazy( () => import('../../components/Dashboard/Management/PermissionSettings/PermissionSettingInfo') ) +const NewPermissionSetting = lazy( + () => import('../../components/Dashboard/Management/PermissionSettings/NewPermissionSetting') +) import PermissionSettingIcon from '../../components/Icons/PermissionSettingIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -20,6 +24,17 @@ export const PermissionSetting = { endpoint: 'permissionsettings', icon: PermissionSettingIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Permission Setting', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewPermissionSetting, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Printer.js b/src/database/models/Printer.js index fa8fc48a..de9d6fae 100644 --- a/src/database/models/Printer.js +++ b/src/database/models/Printer.js @@ -3,9 +3,8 @@ import { createElement, lazy } from 'react' const PrinterInfo = lazy( () => import('../../components/Dashboard/Production/Printers/PrinterInfo') ) -const NewPrinterProfile = lazy( - () => - import('../../components/Dashboard/Production/PrinterProfiles/NewPrinterProfile') +const NewPrinter = lazy( + () => import('../../components/Dashboard/Production/Printers/NewPrinter') ) const LoadFilamentStock = lazy( () => @@ -27,6 +26,7 @@ const RestartMoonraker = lazy( import('../../components/Dashboard/Production/Printers/RestartMoonraker') ) import PrinterIcon from '../../components/Icons/PrinterIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import ReloadIcon from '../../components/Icons/ReloadIcon' import EditIcon from '../../components/Icons/EditIcon' @@ -38,7 +38,6 @@ import StopCircleIcon from '../../components/Icons/StopCircleIcon' import FilamentStockIcon from '../../components/Icons/FilamentStockIcon' import ControlIcon from '../../components/Icons/ControlIcon' import JobIcon from '../../components/Icons/JobIcon' -import PlusIcon from '../../components/Icons/PlusIcon' export const Printer = { name: 'printer', @@ -48,6 +47,17 @@ export const Printer = { prefix: 'PRN', icon: PrinterIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Printer', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewPrinter, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', @@ -98,17 +108,10 @@ export const Printer = { { type: 'divider' }, { name: 'newPrinterProfile', - type: 'modal', - modalWidth: 700, - label: 'New Printer Profile', - icon: PlusIcon, - content: (objectData, { onOk } = {}) => { - return createElement(NewPrinterProfile, { - defaultValues: { printer: objectData }, - onOk, - reset: true - }) - } + type: 'alias', + objectType: 'printerProfile', + aliasAction: 'new', + objectData: (objectData) => ({ printer: objectData }) }, { type: 'divider' }, { diff --git a/src/database/models/PrinterProfile.js b/src/database/models/PrinterProfile.js index cdbd7041..fc4ac001 100644 --- a/src/database/models/PrinterProfile.js +++ b/src/database/models/PrinterProfile.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const PrinterProfileInfo = lazy( () => import('../../components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo') ) +const NewPrinterProfile = lazy( + () => import('../../components/Dashboard/Production/PrinterProfiles/NewPrinterProfile') +) import PrinterProfileIcon from '../../components/Icons/PrinterProfileIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -283,6 +287,17 @@ export const PrinterProfile = { prefix: 'PPF', icon: PrinterProfileIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Printer Profile', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewPrinterProfile, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Product.js b/src/database/models/Product.js index 80a5eccd..413f4100 100644 --- a/src/database/models/Product.js +++ b/src/database/models/Product.js @@ -3,15 +3,15 @@ import { createElement, lazy } from 'react' const ProductInfo = lazy( () => import('../../components/Dashboard/Management/Products/ProductInfo') ) -const NewProductSku = lazy( - () => import('../../components/Dashboard/Management/ProductSkus/NewProductSku') +const NewProduct = lazy( + () => import('../../components/Dashboard/Management/Products/NewProduct') ) import ProductIcon from '../../components/Icons/ProductIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' -import PlusIcon from '../../components/Icons/PlusIcon' export const Product = { name: 'product', @@ -21,6 +21,17 @@ export const Product = { prefix: 'PRD', icon: ProductIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Product', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewProduct, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', @@ -66,16 +77,13 @@ export const Product = { }, { name: 'newProductSku', - type: 'modal', - modalWidth: 700, - label: 'New Product SKU', - icon: PlusIcon, + type: 'alias', + objectType: 'productSku', + aliasAction: 'new', visible: (objectData) => { return !(objectData?._isEditing && objectData?._isEditing == true) }, - content: (objectData, { onOk } = {}) => { - return createElement(NewProductSku, { defaultValues: { product: objectData }, onOk, reset: true }) - } + objectData: (objectData) => ({ product: objectData }) } ], pages: [ diff --git a/src/database/models/ProductCategory.js b/src/database/models/ProductCategory.js index 069623a6..4de32e4f 100644 --- a/src/database/models/ProductCategory.js +++ b/src/database/models/ProductCategory.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const ProductCategoryInfo = lazy( () => import('../../components/Dashboard/Management/ProductCategories/ProductCategoryInfo') ) +const NewProductCategory = lazy( + () => import('../../components/Dashboard/Management/ProductCategories/NewProductCategory') +) import ProductCategoryIcon from '../../components/Icons/ProductCategoryIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -19,6 +23,17 @@ export const ProductCategory = { endpoint: 'productcategories', icon: ProductCategoryIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Product Category', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewProductCategory, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/ProductSku.js b/src/database/models/ProductSku.js index 7e23b5c0..0c2ab10e 100644 --- a/src/database/models/ProductSku.js +++ b/src/database/models/ProductSku.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const ProductSkuInfo = lazy( () => import('../../components/Dashboard/Management/ProductSkus/ProductSkuInfo') ) +const NewProductSku = lazy( + () => import('../../components/Dashboard/Management/ProductSkus/NewProductSku') +) import ProductSkuIcon from '../../components/Icons/ProductSkuIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const ProductSku = { prefix: 'SKU', icon: ProductSkuIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Product SKU', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewProductSku, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/ProductStock.js b/src/database/models/ProductStock.js index 83c8727c..0d1069b1 100644 --- a/src/database/models/ProductStock.js +++ b/src/database/models/ProductStock.js @@ -3,10 +3,14 @@ import { createElement, lazy } from 'react' const ProductStockInfo = lazy( () => import('../../components/Dashboard/Inventory/ProductStocks/ProductStockInfo') ) +const NewProductStock = lazy( + () => import('../../components/Dashboard/Inventory/ProductStocks/NewProductStock') +) const PostProductStock = lazy( () => import('../../components/Dashboard/Inventory/ProductStocks/PostProductStock') ) import ProductStockIcon from '../../components/Icons/ProductStockIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import { getModelByName } from '../ObjectModels.js' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' @@ -22,6 +26,17 @@ export const ProductStock = { prefix: 'PDS', icon: ProductStockIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Product Stock', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewProductStock, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/PurchaseOrder.js b/src/database/models/PurchaseOrder.js index f9a721d8..124121f6 100644 --- a/src/database/models/PurchaseOrder.js +++ b/src/database/models/PurchaseOrder.js @@ -3,14 +3,8 @@ import { createElement, lazy } from 'react' const PurchaseOrderInfo = lazy( () => import('../../components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo') ) -const NewOrderItem = lazy( - () => import('../../components/Dashboard/Inventory/OrderItems/NewOrderItem') -) -const NewShipment = lazy( - () => import('../../components/Dashboard/Inventory/Shipments/NewShipment') -) -const NewInvoice = lazy( - () => import('../../components/Dashboard/Finance/Invoices/NewInvoice') +const NewPurchaseOrder = lazy( + () => import('../../components/Dashboard/Inventory/PurchaseOrders/NewPurchaseOrder') ) const PostPurchaseOrder = lazy( () => import('../../components/Dashboard/Inventory/PurchaseOrders/PostPurchaseOrder') @@ -22,8 +16,8 @@ const CancelPurchaseOrder = lazy( () => import('../../components/Dashboard/Inventory/PurchaseOrders/CancelPurchaseOrder') ) import PurchaseOrderIcon from '../../components/Icons/PurchaseOrderIcon' -import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import PlusIcon from '../../components/Icons/PlusIcon' +import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import CheckIcon from '../../components/Icons/CheckIcon' import EditIcon from '../../components/Icons/EditIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' @@ -37,6 +31,17 @@ export const PurchaseOrder = { prefix: 'POR', icon: PurchaseOrderIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Purchase Order', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewPurchaseOrder, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', @@ -97,48 +102,36 @@ export const PurchaseOrder = { { type: 'divider' }, { name: 'newOrderItem', - type: 'modal', - modalWidth: 700, - label: 'New Order Item', - icon: PlusIcon, + type: 'alias', + objectType: 'orderItem', + aliasAction: 'new', disabled: (objectData) => { return objectData?.state?.type != 'draft' }, - content: (objectData, { onOk } = {}) => { - return createElement(NewOrderItem, { - defaultValues: { - order: { _id: objectData._id }, - orderType: 'purchaseOrder', - syncAmount: 'itemCost' - }, - onOk, - reset: true - }) - } + objectData: (objectData) => ({ + order: { _id: objectData._id }, + orderType: 'purchaseOrder', + syncAmount: 'itemCost' + }) }, { name: 'newShipment', - type: 'modal', - modalWidth: 700, - label: 'New Shipment', - icon: PlusIcon, + type: 'alias', + objectType: 'shipment', + aliasAction: 'new', disabled: (objectData) => { return objectData?.state?.type != 'draft' }, - content: (objectData, { onOk } = {}) => { - return createElement(NewShipment, { - defaultValues: { orderType: 'purchaseOrder', order: { _id: objectData._id } }, - onOk, - reset: true - }) - } + objectData: (objectData) => ({ + orderType: 'purchaseOrder', + order: { _id: objectData._id } + }) }, { name: 'newInvoice', - type: 'modal', - modalWidth: 700, - label: 'New Invoice', - icon: PlusIcon, + type: 'alias', + objectType: 'invoice', + aliasAction: 'new', disabled: (objectData) => { const allowedStates = [ 'received', @@ -148,13 +141,7 @@ export const PurchaseOrder = { ] return !allowedStates.includes(objectData?.state?.type) }, - content: (objectData, { onOk } = {}) => { - return createElement(NewInvoice, { - defaultValues: { orderType: 'purchaseOrder', order: objectData }, - onOk, - reset: true - }) - } + objectData: (objectData) => ({ orderType: 'purchaseOrder', order: objectData }) }, { type: 'divider' diff --git a/src/database/models/SalesOrder.js b/src/database/models/SalesOrder.js index 8e803913..55d4518c 100644 --- a/src/database/models/SalesOrder.js +++ b/src/database/models/SalesOrder.js @@ -3,14 +3,8 @@ import { createElement, lazy } from 'react' const SalesOrderInfo = lazy( () => import('../../components/Dashboard/Sales/SalesOrders/SalesOrderInfo') ) -const NewOrderItem = lazy( - () => import('../../components/Dashboard/Inventory/OrderItems/NewOrderItem') -) -const NewShipment = lazy( - () => import('../../components/Dashboard/Inventory/Shipments/NewShipment') -) -const NewInvoice = lazy( - () => import('../../components/Dashboard/Finance/Invoices/NewInvoice') +const NewSalesOrder = lazy( + () => import('../../components/Dashboard/Sales/SalesOrders/NewSalesOrder') ) const PostSalesOrder = lazy( () => import('../../components/Dashboard/Sales/SalesOrders/PostSalesOrder') @@ -22,8 +16,8 @@ const CancelSalesOrder = lazy( () => import('../../components/Dashboard/Sales/SalesOrders/CancelSalesOrder') ) import SalesOrderIcon from '../../components/Icons/SalesOrderIcon' -import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import PlusIcon from '../../components/Icons/PlusIcon' +import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import CheckIcon from '../../components/Icons/CheckIcon' import EditIcon from '../../components/Icons/EditIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' @@ -37,6 +31,17 @@ export const SalesOrder = { prefix: 'SOR', icon: SalesOrderIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Sales Order', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewSalesOrder, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', @@ -97,53 +102,38 @@ export const SalesOrder = { { type: 'divider' }, { name: 'newOrderItem', - type: 'modal', - modalWidth: 700, - label: 'New Order Item', - icon: PlusIcon, + type: 'alias', + objectType: 'orderItem', + aliasAction: 'new', disabled: (objectData) => { return objectData?.state?.type != 'draft' }, - content: (objectData, { onOk } = {}) => { - return createElement(NewOrderItem, { - defaultValues: { - order: { _id: objectData._id }, - orderType: 'salesOrder', - syncAmount: 'itemPrice' - }, - onOk, - reset: true - }) - } + objectData: (objectData) => ({ + order: { _id: objectData._id }, + orderType: 'salesOrder', + syncAmount: 'itemPrice' + }) }, { name: 'newShipment', - type: 'modal', - modalWidth: 700, + type: 'alias', + objectType: 'shipment', + aliasAction: 'new', modalCentered: false, - label: 'New Shipment', - icon: PlusIcon, disabled: (objectData) => { return objectData?.state?.type != 'draft' }, - content: (objectData, { onOk } = {}) => { - return createElement(NewShipment, { - defaultValues: { - orderType: 'salesOrder', - order: { _id: objectData._id } - }, - onOk, - reset: true - }) - } + objectData: (objectData) => ({ + orderType: 'salesOrder', + order: { _id: objectData._id } + }) }, { name: 'newInvoice', - type: 'modal', - modalWidth: 700, + type: 'alias', + objectType: 'invoice', + aliasAction: 'new', modalCentered: false, - label: 'New Invoice', - icon: PlusIcon, disabled: (objectData) => { const allowedStates = [ 'delivered', @@ -155,13 +145,7 @@ export const SalesOrder = { ] return !allowedStates.includes(objectData?.state?.type) }, - content: (objectData, { onOk } = {}) => { - return createElement(NewInvoice, { - defaultValues: { orderType: 'salesOrder', order: objectData }, - onOk, - reset: true - }) - } + objectData: (objectData) => ({ orderType: 'salesOrder', order: objectData }) }, { type: 'divider' }, { diff --git a/src/database/models/Shipment.js b/src/database/models/Shipment.js index 49b42d31..dac7f0dc 100644 --- a/src/database/models/Shipment.js +++ b/src/database/models/Shipment.js @@ -3,6 +3,9 @@ import { createElement, lazy } from 'react' const ShipmentInfo = lazy( () => import('../../components/Dashboard/Inventory/Shipments/ShipmentInfo') ) +const NewShipment = lazy( + () => import('../../components/Dashboard/Inventory/Shipments/NewShipment') +) const ShipShipment = lazy( () => import('../../components/Dashboard/Inventory/Shipments/ShipShipment') ) @@ -13,6 +16,7 @@ const CancelShipment = lazy( () => import('../../components/Dashboard/Inventory/Shipments/CancelShipment') ) import ShipmentIcon from '../../components/Icons/ShipmentIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import BinIcon from '../../components/Icons/BinIcon' @@ -27,6 +31,17 @@ export const Shipment = { prefix: 'SHP', icon: ShipmentIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Shipment', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewShipment, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/StockAudit.js b/src/database/models/StockAudit.js index 9e54c586..7a7f9cc0 100644 --- a/src/database/models/StockAudit.js +++ b/src/database/models/StockAudit.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const StockAuditInfo = lazy( () => import('../../components/Dashboard/Inventory/StockAudits/StockAuditInfo') ) +const NewStockAudit = lazy( + () => import('../../components/Dashboard/Inventory/StockAudits/NewStockAudit') +) import StockAuditIcon from '../../components/Icons/StockAuditIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -17,6 +21,17 @@ export const StockAudit = { prefix: 'SAU', icon: StockAuditIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 800, + label: 'New Stock Audit', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewStockAudit, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/StockLocation.js b/src/database/models/StockLocation.js index 0fe6d8c7..600ed04a 100644 --- a/src/database/models/StockLocation.js +++ b/src/database/models/StockLocation.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const StockLocationInfo = lazy( () => import('../../components/Dashboard/Inventory/StockLocations/StockLocationInfo') ) +const NewStockLocation = lazy( + () => import('../../components/Dashboard/Inventory/StockLocations/NewStockLocation') +) import StockLocationIcon from '../../components/Icons/StockLocationIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -17,6 +21,17 @@ export const StockLocation = { prefix: 'SLN', icon: StockLocationIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 640, + label: 'New Stock Location', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewStockLocation, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/StockTransfer.js b/src/database/models/StockTransfer.js index 6137cfc2..313a72fd 100644 --- a/src/database/models/StockTransfer.js +++ b/src/database/models/StockTransfer.js @@ -3,10 +3,14 @@ import { createElement, lazy } from 'react' const StockTransferInfo = lazy( () => import('../../components/Dashboard/Inventory/StockTransfers/StockTransferInfo') ) +const NewStockTransfer = lazy( + () => import('../../components/Dashboard/Inventory/StockTransfers/NewStockTransfer') +) const PostStockTransfer = lazy( () => import('../../components/Dashboard/Inventory/StockTransfers/PostStockTransfer') ) import StockTransferIcon from '../../components/Icons/StockTransferIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -21,6 +25,17 @@ export const StockTransfer = { prefix: 'STT', icon: StockTransferIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 740, + label: 'New Stock Transfer', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewStockTransfer, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/TaxRate.js b/src/database/models/TaxRate.js index 5c62c3ac..85f37f13 100644 --- a/src/database/models/TaxRate.js +++ b/src/database/models/TaxRate.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const TaxRateInfo = lazy( () => import('../../components/Dashboard/Management/TaxRates/TaxRateInfo') ) +const NewTaxRate = lazy( + () => import('../../components/Dashboard/Management/TaxRates/NewTaxRate') +) import TaxRateIcon from '../../components/Icons/TaxRateIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const TaxRate = { prefix: 'TXR', icon: TaxRateIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Tax Rate', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewTaxRate, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/TaxRecord.js b/src/database/models/TaxRecord.js index b0f17173..ea592bbd 100644 --- a/src/database/models/TaxRecord.js +++ b/src/database/models/TaxRecord.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const TaxRecordInfo = lazy( () => import('../../components/Dashboard/Finance/TaxRecords/TaxRecordInfo') ) +const NewTaxRecord = lazy( + () => import('../../components/Dashboard/Finance/TaxRecords/NewTaxRecord') +) import TaxRecordIcon from '../../components/Icons/TaxRecordIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const TaxRecord = { prefix: 'TXR', icon: TaxRecordIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Tax Record', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewTaxRecord, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/User.js b/src/database/models/User.js index 61ac11fe..2efc1fe8 100644 --- a/src/database/models/User.js +++ b/src/database/models/User.js @@ -3,16 +3,11 @@ import { createElement, lazy } from 'react' const UserInfo = lazy( () => import('../../components/Dashboard/Management/Users/UserInfo') ) -const NewAppPassword = lazy( - () => - import('../../components/Dashboard/Management/AppPasswords/NewAppPassword') -) import PersonIcon from '../../components/Icons/PersonIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' import XMarkIcon from '../../components/Icons/XMarkIcon' -import PlusIcon from '../../components/Icons/PlusIcon' import { calculateUserPermissions } from '../permissions.js' export const User = { @@ -65,21 +60,13 @@ export const User = { }, { name: 'newAppPassword', - type: 'modal', - modalWidth: 680, - label: 'New App Password', - icon: PlusIcon, + type: 'alias', + objectType: 'appPassword', + aliasAction: 'new', disabled: (objectData) => { return objectData?._user?._id != objectData?._id }, - content: (objectData, { onOk } = {}) => { - console.log('newAppPassword objectData', objectData) - return createElement(NewAppPassword, { - defaultValues: { user: objectData._user }, - onOk, - reset: true - }) - } + objectData: (objectData) => ({ user: objectData }) } ], pages: [ diff --git a/src/database/models/UserGroup.js b/src/database/models/UserGroup.js index 81ab6708..189fecd0 100644 --- a/src/database/models/UserGroup.js +++ b/src/database/models/UserGroup.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const UserGroupInfo = lazy( () => import('../../components/Dashboard/Management/UserGroups/UserGroupInfo') ) +const NewUserGroup = lazy( + () => import('../../components/Dashboard/Management/UserGroups/NewUserGroup') +) import PersonGroupIcon from '../../components/Icons/PersonGroupIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -19,6 +23,17 @@ export const UserGroup = { prefix: 'UGP', icon: PersonGroupIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New User Group', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewUserGroup, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/models/Vendor.js b/src/database/models/Vendor.js index 3475eee1..27363b3c 100644 --- a/src/database/models/Vendor.js +++ b/src/database/models/Vendor.js @@ -3,7 +3,11 @@ import { createElement, lazy } from 'react' const VendorInfo = lazy( () => import('../../components/Dashboard/Management/Vendors/VendorInfo') ) +const NewVendor = lazy( + () => import('../../components/Dashboard/Management/Vendors/NewVendor') +) import VendorIcon from '../../components/Icons/VendorIcon' +import PlusIcon from '../../components/Icons/PlusIcon' import InfoCircleIcon from '../../components/Icons/InfoCircleIcon' import EditIcon from '../../components/Icons/EditIcon' import CheckIcon from '../../components/Icons/CheckIcon' @@ -18,6 +22,17 @@ export const Vendor = { prefix: 'VEN', icon: VendorIcon, actions: [ + { + name: 'new', + type: 'modal', + pageName: 'list', + modalWidth: 700, + label: 'New Vendor', + icon: PlusIcon, + content: (objectData, { onOk } = {}) => { + return createElement(NewVendor, { defaultValues: objectData, onOk, reset: true }) + } + }, { name: 'info', type: 'page', diff --git a/src/database/permissions.js b/src/database/permissions.js index 52cbf461..7698c0ca 100644 --- a/src/database/permissions.js +++ b/src/database/permissions.js @@ -36,3 +36,34 @@ export const calculateUserPermissions = (objectData = {}) => export const calculateUserGroupPermissions = (objectData = {}) => applyPermissionSettingsList(objectData.permissionSettings) + +const PERMISSION_ACTION_ALIASES = { + cancelEdit: 'edit', + finishEdit: 'edit' +} + +export const getPermissionActionName = (actionName) => + PERMISSION_ACTION_ALIASES[actionName] || actionName + +const isTopLevelModelAction = (model, actionName) => + (model?.actions || []).some( + (action) => + action?.name === actionName && + action.type !== 'divider' && + action.type !== 'alias' && + action.type !== 'callback' + ) + +export const hasActionPermission = (userProfile, model, actionName) => { + if (!actionName) return true + + const permissionAction = getPermissionActionName(actionName) + const isControlled = + Boolean(PERMISSION_ACTION_ALIASES[actionName]) || + isTopLevelModelAction(model, actionName) + + if (!isControlled) return true + + const modelName = typeof model === 'string' ? model : model?.name + return userProfile?.permissions?.[modelName]?.[permissionAction] === true +} diff --git a/src/utils/cookies.js b/src/utils/cookies.js index 74403756..1e63f2d1 100644 --- a/src/utils/cookies.js +++ b/src/utils/cookies.js @@ -6,16 +6,62 @@ const COOKIE_OPTIONS = { maxAge: 7 * 24 * 60 * 60 // 7 days in seconds } +const AUTH_STORAGE_KEY = 'farmcontrol.auth' +const LEGACY_AUTH_COOKIE_NAMES = ['authToken', 'authExpiresAt', 'user'] + const getAuthExpiryTime = (expiresAt) => { const numericExpiry = Number(expiresAt) if (Number.isFinite(numericExpiry) && numericExpiry > 0) return numericExpiry return new Date(expiresAt).getTime() } -const getAuthCookieMaxAge = (expiresAt) => { - const expiryTime = getAuthExpiryTime(expiresAt) - if (!Number.isFinite(expiryTime)) return COOKIE_OPTIONS.maxAge - return Math.max(Math.floor((expiryTime - Date.now()) / 1000), 1) +const toStoredUser = (user) => { + if (!user || typeof user !== 'object') return user ?? null + const stored = { ...user } + delete stored.access_token + delete stored.refresh_token + delete stored.id_token + return stored +} + +const parseStoredSession = (raw) => { + if (!raw) return { token: null, expiresAt: null, user: null } + const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw + return { + token: parsed.access_token || parsed.token || null, + expiresAt: parsed.expires_at || parsed.expiresAt || null, + user: parsed.user || null + } +} + +const getLegacyAuthCookies = () => { + const userCookie = getCookie('user') + let user = null + if (userCookie) { + try { + user = JSON.parse(userCookie) + } catch (error) { + console.error('Error parsing legacy auth user cookie:', error) + } + } + return { + token: getCookie('authToken'), + expiresAt: getCookie('authExpiresAt'), + user + } +} + +const clearLegacyAuthCookies = () => { + LEGACY_AUTH_COOKIE_NAMES.forEach((name) => removeCookie(name)) +} + +const serializeAuthSession = (authData, fallbackUser = null) => { + const payload = { + access_token: authData.access_token, + expires_at: getAuthExpiryTime(authData.expires_at), + user: toStoredUser(authData.user) ?? fallbackUser ?? null + } + return JSON.stringify(payload) } /** @@ -126,6 +172,16 @@ export const removeCookie = (name, options = {}) => { * @returns {boolean} - True if cookies are enabled */ export const areCookiesEnabled = () => { + try { + const key = '__farmcontrol_auth_test__' + window.localStorage.setItem(key, '1') + const enabled = window.localStorage.getItem(key) === '1' + window.localStorage.removeItem(key) + if (enabled) return true + } catch (e) { + console.error('Error checking if localStorage is enabled:', e) + } + try { setCookie('test', 'test') const enabled = getCookie('test') === 'test' @@ -143,9 +199,9 @@ export const areCookiesEnabled = () => { */ export const validateAuthCookies = () => { try { - const { token, expiresAt, user } = getAuthCookies() + const { token, expiresAt } = getAuthCookies() - if (!token || !expiresAt || !user) { + if (!token || !expiresAt) { return false } @@ -153,15 +209,13 @@ export const validateAuthCookies = () => { const expirationTime = getAuthExpiryTime(expiresAt) if (!Number.isFinite(expirationTime) || expirationTime <= now) { - // Cookies are expired, clean them up clearAuthCookies() return false } return true } catch (error) { - console.error('Error validating auth cookies:', error) - clearAuthCookies() + console.error('Error validating auth session:', error) return false } } @@ -173,9 +227,9 @@ export const validateAuthCookies = () => { */ export const checkAuthCookiesExpiry = (minutesBeforeExpiry = 5) => { try { - const { token, expiresAt, user } = getAuthCookies() + const { token, expiresAt } = getAuthCookies() - if (!token || !expiresAt || !user) { + if (!token || !expiresAt) { return { isExpiringSoon: false, timeRemaining: 0 } } @@ -200,40 +254,19 @@ export const checkAuthCookiesExpiry = (minutesBeforeExpiry = 5) => { */ export const setupCookieSync = (onAuthChange) => { const handleStorageChange = (event) => { - // Check if auth-related cookies changed if ( - event.key === 'authToken' || - event.key === 'authExpiresAt' || - event.key === 'user' + event.key === AUTH_STORAGE_KEY || + event.key === null || + LEGACY_AUTH_COOKIE_NAMES.includes(event.key) ) { - // Small delay to ensure cookies are updated - setTimeout(() => { - onAuthChange() - }, 100) + onAuthChange() } } - // Listen for storage events (for cross-tab communication) window.addEventListener('storage', handleStorageChange) - // Also listen for cookie changes using a polling mechanism - let lastCookieState = document.cookie - const cookieCheckInterval = setInterval(() => { - const currentCookieState = document.cookie - if (currentCookieState !== lastCookieState) { - lastCookieState = currentCookieState - // Check if auth cookies changed - const authCookies = getAuthCookies() - if (authCookies.token || authCookies.expiresAt || authCookies.user) { - onAuthChange() - } - } - }, 1000) - - // Return cleanup function return () => { window.removeEventListener('storage', handleStorageChange) - clearInterval(cookieCheckInterval) } } @@ -244,53 +277,22 @@ export const setupCookieSync = (onAuthChange) => { */ export const setAuthCookies = (authData) => { try { - if (!authData) { + if (!authData?.access_token || !authData?.expires_at) { console.warn('Auth data is required') return false } - let success = true - const maxAge = getAuthCookieMaxAge(authData.expires_at) - - if (authData.access_token) { - success = - success && - setCookie('authToken', authData.access_token, { - maxAge - }) + const existing = getAuthCookies() + const serialized = serializeAuthSession(authData, existing.user) + if (window.localStorage.getItem(AUTH_STORAGE_KEY) === serialized) { + return true } - if (authData.expires_at) { - success = - success && - setCookie('authExpiresAt', authData.expires_at, { - maxAge - }) - } - - if (authData.user) { - const userObject = { - ...authData.user, - access_token: undefined, - refresh_token: undefined, - id_token: undefined - } - success = - success && - setCookie('user', JSON.stringify(userObject), { - maxAge - }) - } - - if (!success) { - console.warn('Some cookies failed to set, clearing all auth cookies') - clearAuthCookies() - } - - return success + window.localStorage.setItem(AUTH_STORAGE_KEY, serialized) + clearLegacyAuthCookies() + return true } catch (error) { - console.error('Error setting auth cookies:', error) - clearAuthCookies() + console.error('Error setting auth session:', error) return false } } @@ -300,18 +302,26 @@ export const setAuthCookies = (authData) => { * @returns {Object} - Object containing auth data from cookies */ export const getAuthCookies = () => { - return { - token: getCookie('authToken'), - expiresAt: getCookie('authExpiresAt'), - user: getCookie('user') ? JSON.parse(getCookie('user')) : null + try { + const raw = window.localStorage.getItem(AUTH_STORAGE_KEY) + if (raw) { + return parseStoredSession(raw) + } + } catch (error) { + console.error('Error reading auth session from localStorage:', error) } + + return getLegacyAuthCookies() } /** * Clear authentication cookies */ export const clearAuthCookies = () => { - removeCookie('authToken') - removeCookie('authExpiresAt') - removeCookie('user') + try { + window.localStorage.removeItem(AUTH_STORAGE_KEY) + } catch (error) { + console.error('Error clearing auth session from localStorage:', error) + } + clearLegacyAuthCookies() } diff --git a/src/utils/modelActions.js b/src/utils/modelActions.js index 866787c5..57ad7cfa 100644 --- a/src/utils/modelActions.js +++ b/src/utils/modelActions.js @@ -1,7 +1,54 @@ +import { getModelByName } from '../database/ObjectModels' + export function getObjectIdParamName(modelName) { return `${modelName}Id` } +export function actionVisibleOnPage(action, pageName) { + if (!action) return false + if (action.type === 'divider') return true + if (Array.isArray(action.pages) && action.pages.length > 0) { + return action.pages.includes(pageName) + } + if (action.type !== 'page' && action.pageName) { + return action.pageName === pageName + } + if (action.name === 'new') return pageName === 'list' + return pageName !== 'list' +} + +export function resolveAction(action) { + if (!action || action.type !== 'alias') return action + const targetModel = getModelByName(action.objectType) + const target = findAction(targetModel, action.aliasAction) + if (!target) return action + return { + ...target, + ...action, + type: 'alias', + label: action.label || target.label, + icon: action.icon || target.icon, + modalWidth: action.modalWidth ?? target.modalWidth, + modalCentered: action.modalCentered ?? target.modalCentered, + content: target.content + } +} + +export function getActionPermissionTarget(action, sourceModel) { + if (action?.type === 'alias') { + const targetModel = getModelByName(action.objectType) + const resolved = resolveAction(action) + return { + model: targetModel || action.objectType, + actionName: action.aliasAction || resolved?.name + } + } + return { + model: sourceModel, + actionName: action?.name + } +} + export function getObjectIdFromSearch(modelName, search) { const params = new URLSearchParams(search) return params.get(getObjectIdParamName(modelName))