diff --git a/src/components/Dashboard/Developer/ApiContextDebug.jsx b/src/components/Dashboard/Developer/ApiContextDebug.jsx index fe18e2c..75e95a6 100644 --- a/src/components/Dashboard/Developer/ApiContextDebug.jsx +++ b/src/components/Dashboard/Developer/ApiContextDebug.jsx @@ -31,16 +31,16 @@ const ApiContextDebug = () => { connecting, connected, fetchLoading, - lockObject, - unlockObject, - fetchObjectLock, + setObjectActivity, + clearObjectActivity, + fetchObjectActivities, updateObject, createObject, deleteObject, subscribeToObjectUpdates, subscribeToObjectEvent, subscribeToObjectTypeUpdates, - subscribeToObjectLock, + subscribeToObjectActivity, fetchObject, fetchObjects, fetchObjectsByProperty, @@ -169,40 +169,40 @@ const ApiContextDebug = () => { } } - const testLockObject = () => { + const testSetObjectActivity = () => { try { - lockObject(testInputs.objectId, testInputs.objectType) - msgApi.success('Lock command sent') + setObjectActivity(testInputs.objectId, testInputs.objectType, 'viewing') + msgApi.success('setActivity command sent') } catch (err) { - msgApi.error('Lock command failed') - logger.error('Lock error:', err) + msgApi.error('setActivity command failed') + logger.error('setActivity error:', err) } } - const testUnlockObject = () => { + const testClearObjectActivity = () => { try { - unlockObject(testInputs.objectId, testInputs.objectType) - msgApi.success('Unlock command sent') + clearObjectActivity(testInputs.objectId, testInputs.objectType) + msgApi.success('clearActivity command sent') } catch (err) { - msgApi.error('Unlock command failed') - logger.error('Unlock error:', err) + msgApi.error('clearActivity command failed') + logger.error('clearActivity error:', err) } } - const testFetchObjectLock = async () => { + const testFetchObjectActivities = async () => { try { - msgApi.loading('Testing fetchObjectLock...', 0) - const result = await fetchObjectLock( + msgApi.loading('Testing fetchObjectActivities...', 0) + const result = await fetchObjectActivities( testInputs.objectId, testInputs.objectType ) msgApi.destroy() - msgApi.success('fetchObjectLock test completed') - logger.debug('fetchObjectLock test completed', result) + msgApi.success('fetchObjectActivities test completed') + logger.debug('fetchObjectActivities test completed', result) } catch (err) { msgApi.destroy() - msgApi.error('fetchObjectLock test failed') - logger.error('fetchObjectLock error:', err) + msgApi.error('fetchObjectActivities test failed') + logger.error('fetchObjectActivities error:', err) } } @@ -477,28 +477,27 @@ const ApiContextDebug = () => { } } - const testSubscribeToObjectLock = () => { + const testSubscribeToObjectActivity = () => { try { - const callback = (lockData) => { - logger.debug('Object lock update received:', lockData) + const callback = (activity) => { + logger.debug('Object activity update received:', activity) } - const unsubscribe = subscribeToObjectLock( + const unsubscribe = subscribeToObjectActivity( testInputs.objectId, testInputs.objectType, callback ) - msgApi.success('Subscribed to object lock updates') - logger.debug('Subscribed to object lock updates for test-id') + msgApi.success('Subscribed to object activity updates') + logger.debug('Subscribed to object activity updates for test-id') - // Store unsubscribe function for cleanup setTimeout(() => { if (unsubscribe) { unsubscribe() - logger.debug('Unsubscribed from object lock updates') + logger.debug('Unsubscribed from object activity updates') } - }, 10000) // Auto-unsubscribe after 10 seconds + }, 10000) } catch (err) { - msgApi.error('Subscribe to object lock updates failed') + msgApi.error('Subscribe to object activity updates failed') logger.error('Subscribe error:', err) } } @@ -719,8 +718,8 @@ const ApiContextDebug = () => { - - - diff --git a/src/components/Dashboard/Finance/Invoices/InvoiceInfo.jsx b/src/components/Dashboard/Finance/Invoices/InvoiceInfo.jsx index 4d4484a..2fed8b5 100644 --- a/src/components/Dashboard/Finance/Invoices/InvoiceInfo.jsx +++ b/src/components/Dashboard/Finance/Invoices/InvoiceInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -16,7 +16,7 @@ import PaymentIcon from '../../../Icons/PaymentIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -110,7 +110,7 @@ const InvoiceInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} disabled={ - objectFormState.lock?.locked || + objectFormState.beingEditedByOther || objectFormState.loading || editDisabled } diff --git a/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx b/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx index 0dbbf84..55c8546 100644 --- a/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx +++ b/src/components/Dashboard/Finance/Payments/PaymentInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -106,7 +106,7 @@ const PaymentInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} disabled={ - objectFormState.lock?.locked || + objectFormState.beingEditedByOther || objectFormState.loading || editDisabled } diff --git a/src/components/Dashboard/Finance/TaxRecords/TaxRecordInfo.jsx b/src/components/Dashboard/Finance/TaxRecords/TaxRecordInfo.jsx index 32f4de0..7f9439e 100644 --- a/src/components/Dashboard/Finance/TaxRecords/TaxRecordInfo.jsx +++ b/src/components/Dashboard/Finance/TaxRecords/TaxRecordInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config' import useCollapseState from '../../hooks/useCollapseState' @@ -13,7 +13,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -74,7 +74,7 @@ const TaxRecordInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx b/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx index d5d6274..59e404f 100644 --- a/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx +++ b/src/components/Dashboard/Inventory/FilamentStocks/FilamentStockInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import useCollapseState from '../../hooks/useCollapseState' import ObjectForm from '../../common/ObjectForm' import ObjectInfo from '../../common/ObjectInfo' @@ -11,7 +11,7 @@ import EditButtons from '../../common/EditButtons' import ActionHandler from '../../common/ActionHandler' import InfoCollapse from '../../common/InfoCollapse' import NotesPanel from '../../common/NotesPanel' -import LockIndicator from '../../common/LockIndicator' +import ActivityIndicator from '../../common/ActivityIndicator' import InfoCircleIcon from '../../../Icons/InfoCircleIcon' import FilamentStockIcon from '../../../Icons/FilamentStockIcon' import NoteIcon from '../../../Icons/NoteIcon' @@ -69,7 +69,7 @@ const FilamentStockInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Inventory/PartStocks/PartStockInfo.jsx b/src/components/Dashboard/Inventory/PartStocks/PartStockInfo.jsx index 10b4f35..5bc7e94 100644 --- a/src/components/Dashboard/Inventory/PartStocks/PartStockInfo.jsx +++ b/src/components/Dashboard/Inventory/PartStocks/PartStockInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -45,7 +45,7 @@ const PartStockInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -76,7 +76,7 @@ const PartStockInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Inventory/ProductStocks/ProductStockInfo.jsx b/src/components/Dashboard/Inventory/ProductStocks/ProductStockInfo.jsx index d73089d..3f7bff7 100644 --- a/src/components/Dashboard/Inventory/ProductStocks/ProductStockInfo.jsx +++ b/src/components/Dashboard/Inventory/ProductStocks/ProductStockInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -21,7 +21,7 @@ import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import PartStockIcon from '../../../Icons/PartStockIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -55,7 +55,7 @@ const ProductStockInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -99,7 +99,7 @@ const ProductStockInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} disabled={ - objectFormState.lock?.locked || + objectFormState.beingEditedByOther || objectFormState.loading || editDisabled } diff --git a/src/components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo.jsx b/src/components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo.jsx index e7bc459..513729f 100644 --- a/src/components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo.jsx +++ b/src/components/Dashboard/Inventory/PurchaseOrders/PurchaseOrderInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -136,7 +136,7 @@ const PurchaseOrderInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} disabled={ - objectFormState.lock?.locked || + objectFormState.beingEditedByOther || objectFormState.loading || editDisabled } diff --git a/src/components/Dashboard/Inventory/Shipments/PurchaseOrderInfo.jsx b/src/components/Dashboard/Inventory/Shipments/PurchaseOrderInfo.jsx index 901c8b6..c48592c 100644 --- a/src/components/Dashboard/Inventory/Shipments/PurchaseOrderInfo.jsx +++ b/src/components/Dashboard/Inventory/Shipments/PurchaseOrderInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -112,7 +112,7 @@ const PurchaseOrderInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Inventory/Shipments/ShipmentInfo.jsx b/src/components/Dashboard/Inventory/Shipments/ShipmentInfo.jsx index 43fcf0a..ccdb5fe 100644 --- a/src/components/Dashboard/Inventory/Shipments/ShipmentInfo.jsx +++ b/src/components/Dashboard/Inventory/Shipments/ShipmentInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -98,7 +98,7 @@ const ShipmentInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx b/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx index 82b5c31..66ab3ac 100644 --- a/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx +++ b/src/components/Dashboard/Inventory/StockAudits/StockAuditInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -45,7 +45,7 @@ const StockAuditInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -76,7 +76,7 @@ const StockAuditInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Inventory/StockLocations/StockLocationInfo.jsx b/src/components/Dashboard/Inventory/StockLocations/StockLocationInfo.jsx index b2af4b1..b787993 100644 --- a/src/components/Dashboard/Inventory/StockLocations/StockLocationInfo.jsx +++ b/src/components/Dashboard/Inventory/StockLocations/StockLocationInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -46,7 +46,7 @@ const StockLocationInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -77,7 +77,7 @@ const StockLocationInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Inventory/StockTransfers/StockTransferInfo.jsx b/src/components/Dashboard/Inventory/StockTransfers/StockTransferInfo.jsx index ee1c65a..5939401 100644 --- a/src/components/Dashboard/Inventory/StockTransfers/StockTransferInfo.jsx +++ b/src/components/Dashboard/Inventory/StockTransfers/StockTransferInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import useCollapseState from '../../hooks/useCollapseState.jsx' import NotesPanel from '../../common/NotesPanel.jsx' @@ -19,7 +19,7 @@ import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import StockTransferIcon from '../../../Icons/StockTransferIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -50,7 +50,7 @@ const StockTransferInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -94,7 +94,7 @@ const StockTransferInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} disabled={ - objectFormState.lock?.locked || + objectFormState.beingEditedByOther || objectFormState.loading || editDisabled } diff --git a/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx b/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx index 199a4c9..66c1544 100644 --- a/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx +++ b/src/components/Dashboard/Management/AppPasswords/AppPasswordInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState, useContext } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Modal } from 'antd' +import { Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import useCollapseState from '../../hooks/useCollapseState' import InfoCollapse from '../../common/InfoCollapse' @@ -10,7 +10,7 @@ import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -78,7 +78,7 @@ const AppPasswordInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} disabled={ - objectFormState.lock?.locked || + objectFormState.beingEditedByOther || objectFormState.loading || editDisabled } diff --git a/src/components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx b/src/components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx index 31ffcc3..173981b 100644 --- a/src/components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx +++ b/src/components/Dashboard/Management/CourierServices/CourierServiceInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config' import useCollapseState from '../../hooks/useCollapseState' @@ -13,7 +13,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -76,7 +76,7 @@ const CourierServiceInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Couriers/CourierInfo.jsx b/src/components/Dashboard/Management/Couriers/CourierInfo.jsx index 70197f0..879cbf0 100644 --- a/src/components/Dashboard/Management/Couriers/CourierInfo.jsx +++ b/src/components/Dashboard/Management/Couriers/CourierInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config' import useCollapseState from '../../hooks/useCollapseState' @@ -13,7 +13,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -72,7 +72,7 @@ const CourierInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/DocumentJobs/DocumentJobInfo.jsx b/src/components/Dashboard/Management/DocumentJobs/DocumentJobInfo.jsx index a38f6b8..4457168 100644 --- a/src/components/Dashboard/Management/DocumentJobs/DocumentJobInfo.jsx +++ b/src/components/Dashboard/Management/DocumentJobs/DocumentJobInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -73,7 +73,7 @@ const DocumentJobInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo.jsx b/src/components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo.jsx index dd2d169..669710a 100644 --- a/src/components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo.jsx +++ b/src/components/Dashboard/Management/DocumentPrinters/DocumentPrinterInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -47,7 +47,7 @@ const DocumentPrinterInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -78,7 +78,7 @@ const DocumentPrinterInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/DocumentSizes/DocumentSizeInfo.jsx b/src/components/Dashboard/Management/DocumentSizes/DocumentSizeInfo.jsx index a19bad8..ae2b340 100644 --- a/src/components/Dashboard/Management/DocumentSizes/DocumentSizeInfo.jsx +++ b/src/components/Dashboard/Management/DocumentSizes/DocumentSizeInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -73,7 +73,7 @@ const DocumentSizeInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx b/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx index ddc7b09..38ddce3 100644 --- a/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx +++ b/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateDesign.jsx @@ -10,7 +10,7 @@ import ViewButton from '../../common/ViewButton.jsx' import NoteIcon from '../../../Icons/NoteIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ScrollBox from '../../common/ScrollBox.jsx' @@ -41,7 +41,7 @@ const DocumentTemplateDesign = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -97,7 +97,7 @@ const DocumentTemplateDesign = () => { updateVisibleState={updateCollapseState} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateInfo.jsx b/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateInfo.jsx index ad6ec25..2c6e401 100644 --- a/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateInfo.jsx +++ b/src/components/Dashboard/Management/DocumentTemplates/DocumentTemplateInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -47,7 +47,7 @@ const DocumentTemplateInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -78,7 +78,7 @@ const DocumentTemplateInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/FilamentSkus/FilamentSkuInfo.jsx b/src/components/Dashboard/Management/FilamentSkus/FilamentSkuInfo.jsx index 849d23d..aaba220 100644 --- a/src/components/Dashboard/Management/FilamentSkus/FilamentSkuInfo.jsx +++ b/src/components/Dashboard/Management/FilamentSkus/FilamentSkuInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import useCollapseState from '../../hooks/useCollapseState' import NotesPanel from '../../common/NotesPanel' import InfoCollapse from '../../common/InfoCollapse' @@ -8,7 +8,7 @@ import ObjectInfo from '../../common/ObjectInfo' import ViewButton from '../../common/ViewButton' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx' import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' @@ -71,7 +71,7 @@ const FilamentSkuInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx b/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx index 8e7bfdf..87654b2 100644 --- a/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx +++ b/src/components/Dashboard/Management/Filaments/FilamentInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -90,7 +90,7 @@ const FilamentInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Files/FileInfo.jsx b/src/components/Dashboard/Management/Files/FileInfo.jsx index a71765e..e43facf 100644 --- a/src/components/Dashboard/Management/Files/FileInfo.jsx +++ b/src/components/Dashboard/Management/Files/FileInfo.jsx @@ -1,6 +1,6 @@ import { useContext, useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config.js' import useCollapseState from '../../hooks/useCollapseState.jsx' @@ -13,7 +13,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -82,7 +82,7 @@ const FileInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Hosts/HostInfo.jsx b/src/components/Dashboard/Management/Hosts/HostInfo.jsx index 3bf999e..86b671c 100644 --- a/src/components/Dashboard/Management/Hosts/HostInfo.jsx +++ b/src/components/Dashboard/Management/Hosts/HostInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -47,7 +47,7 @@ const HostInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -86,7 +86,7 @@ const HostInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Materials/MaterialInfo.jsx b/src/components/Dashboard/Management/Materials/MaterialInfo.jsx index 9545b22..baf447d 100644 --- a/src/components/Dashboard/Management/Materials/MaterialInfo.jsx +++ b/src/components/Dashboard/Management/Materials/MaterialInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config' import useCollapseState from '../../hooks/useCollapseState' @@ -13,7 +13,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -74,7 +74,7 @@ const MaterialInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx b/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx index 31eb8d9..8642fed 100644 --- a/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx +++ b/src/components/Dashboard/Management/NoteTypes/NoteTypeInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex } from 'antd' +import { Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import useCollapseState from '../../hooks/useCollapseState' import InfoCollapse from '../../common/InfoCollapse' @@ -10,7 +10,7 @@ import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -63,7 +63,7 @@ const NoteTypeInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Notes/NoteInfo.jsx b/src/components/Dashboard/Management/Notes/NoteInfo.jsx index 456aa79..40ced6b 100644 --- a/src/components/Dashboard/Management/Notes/NoteInfo.jsx +++ b/src/components/Dashboard/Management/Notes/NoteInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -16,7 +16,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -76,7 +76,7 @@ const NoteInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/PartSkus/PartSkuInfo.jsx b/src/components/Dashboard/Management/PartSkus/PartSkuInfo.jsx index b6a2698..8d738a0 100644 --- a/src/components/Dashboard/Management/PartSkus/PartSkuInfo.jsx +++ b/src/components/Dashboard/Management/PartSkus/PartSkuInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import useCollapseState from '../../hooks/useCollapseState' import NotesPanel from '../../common/NotesPanel' import InfoCollapse from '../../common/InfoCollapse' @@ -8,7 +8,7 @@ import ObjectInfo from '../../common/ObjectInfo' import ViewButton from '../../common/ViewButton' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx' import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' @@ -66,7 +66,7 @@ const PartSkuInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Parts/PartInfo.jsx b/src/components/Dashboard/Management/Parts/PartInfo.jsx index a70b1e6..b2ec82f 100644 --- a/src/components/Dashboard/Management/Parts/PartInfo.jsx +++ b/src/components/Dashboard/Management/Parts/PartInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import useCollapseState from '../../hooks/useCollapseState' import NotesPanel from '../../common/NotesPanel' import InfoCollapse from '../../common/InfoCollapse' @@ -8,7 +8,7 @@ import ObjectInfo from '../../common/ObjectInfo' import ViewButton from '../../common/ViewButton' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx' import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' @@ -71,7 +71,7 @@ const PartInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/ProductCategories/ProductCategoryInfo.jsx b/src/components/Dashboard/Management/ProductCategories/ProductCategoryInfo.jsx index 2237d38..9583138 100644 --- a/src/components/Dashboard/Management/ProductCategories/ProductCategoryInfo.jsx +++ b/src/components/Dashboard/Management/ProductCategories/ProductCategoryInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import useCollapseState from '../../hooks/useCollapseState' import NotesPanel from '../../common/NotesPanel' import InfoCollapse from '../../common/InfoCollapse' @@ -11,7 +11,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -71,7 +71,7 @@ const ProductCategoryInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/ProductSkus/ProductSkuInfo.jsx b/src/components/Dashboard/Management/ProductSkus/ProductSkuInfo.jsx index 2aef157..b7b33e2 100644 --- a/src/components/Dashboard/Management/ProductSkus/ProductSkuInfo.jsx +++ b/src/components/Dashboard/Management/ProductSkus/ProductSkuInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import useCollapseState from '../../hooks/useCollapseState' import NotesPanel from '../../common/NotesPanel' import InfoCollapse from '../../common/InfoCollapse' @@ -8,7 +8,7 @@ import ObjectInfo from '../../common/ObjectInfo' import ViewButton from '../../common/ViewButton' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx' import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' @@ -73,7 +73,7 @@ const ProductSkuInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Products/ProductInfo.jsx b/src/components/Dashboard/Management/Products/ProductInfo.jsx index 0e64ea2..976929a 100644 --- a/src/components/Dashboard/Management/Products/ProductInfo.jsx +++ b/src/components/Dashboard/Management/Products/ProductInfo.jsx @@ -1,7 +1,6 @@ import { useRef, useState } from 'react' -import { Modal } from 'antd' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import useCollapseState from '../../hooks/useCollapseState' import NotesPanel from '../../common/NotesPanel' import InfoCollapse from '../../common/InfoCollapse' @@ -9,7 +8,7 @@ import ObjectInfo from '../../common/ObjectInfo' import ViewButton from '../../common/ViewButton' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx' import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' @@ -72,7 +71,7 @@ const ProductInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx b/src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx index 5329193..500469e 100644 --- a/src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx +++ b/src/components/Dashboard/Management/TaxRates/TaxRateInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config' import useCollapseState from '../../hooks/useCollapseState' @@ -13,7 +13,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -71,7 +71,7 @@ const TaxRateInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Users/UserInfo.jsx b/src/components/Dashboard/Management/Users/UserInfo.jsx index 0ef9c6a..e11124c 100644 --- a/src/components/Dashboard/Management/Users/UserInfo.jsx +++ b/src/components/Dashboard/Management/Users/UserInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import useCollapseState from '../../hooks/useCollapseState' import NotesPanel from '../../common/NotesPanel' @@ -13,7 +13,7 @@ import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import AppPasswordIcon from '../../../Icons/AppPasswordIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -76,7 +76,7 @@ const UserInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Management/Vendors/VendorInfo.jsx b/src/components/Dashboard/Management/Vendors/VendorInfo.jsx index b4787c8..f70e074 100644 --- a/src/components/Dashboard/Management/Vendors/VendorInfo.jsx +++ b/src/components/Dashboard/Management/Vendors/VendorInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config' import useCollapseState from '../../hooks/useCollapseState' @@ -13,7 +13,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -71,7 +71,7 @@ const VendorInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo.jsx b/src/components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo.jsx index 4e2900d..0581ee3 100644 --- a/src/components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo.jsx +++ b/src/components/Dashboard/Production/FilamentProfiles/FilamentProfileInfo.jsx @@ -12,7 +12,7 @@ import HotEndIcon from '../../../Icons/HotEndIcon' import InfoCircleIcon from '../../../Icons/InfoCircleIcon' import InfoCollapse from '../../common/InfoCollapse' import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder' -import LockIndicator from '../../common/LockIndicator' +import ActivityIndicator from '../../common/ActivityIndicator' import MultiMotionIcon from '../../../Icons/MultiMotionIcon' import NoteIcon from '../../../Icons/NoteIcon' import NotesPanel from '../../common/NotesPanel' @@ -108,7 +108,7 @@ const FilamentProfileInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { startEditing={() => actionHandlerRef.current.callAction('edit')} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx b/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx index 1ebd9b7..2489b91 100644 --- a/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx +++ b/src/components/Dashboard/Production/GCodeFiles/GCodeFileInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -15,7 +15,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -48,7 +48,7 @@ const GCodeFileInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -79,7 +79,7 @@ const GCodeFileInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Production/GCodeFiles/GCodeFilePreview.jsx b/src/components/Dashboard/Production/GCodeFiles/GCodeFilePreview.jsx index f87ad35..ee84453 100644 --- a/src/components/Dashboard/Production/GCodeFiles/GCodeFilePreview.jsx +++ b/src/components/Dashboard/Production/GCodeFiles/GCodeFilePreview.jsx @@ -10,7 +10,7 @@ import ViewButton from '../../common/ViewButton.jsx' import NoteIcon from '../../../Icons/NoteIcon.jsx' import EyeIcon from '../../../Icons/EyeIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ScrollBox from '../../common/ScrollBox.jsx' import FilePreview from '../../common/FilePreview.jsx' @@ -79,7 +79,7 @@ const GCodeFilePreview = () => { disabled={objectFormState.loading} /> - + diff --git a/src/components/Dashboard/Production/Jobs/JobInfo.jsx b/src/components/Dashboard/Production/Jobs/JobInfo.jsx index f551891..1341116 100644 --- a/src/components/Dashboard/Production/Jobs/JobInfo.jsx +++ b/src/components/Dashboard/Production/Jobs/JobInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' import config from '../../../../config.js' @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -47,7 +47,7 @@ const JobInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: { _id: jobId @@ -89,7 +89,7 @@ const JobInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} disabled={ - objectFormState.lock?.locked || + objectFormState.beingEditedByOther || objectFormState.loading || editDisabled } diff --git a/src/components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo.jsx b/src/components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo.jsx index c5b282d..0526a45 100644 --- a/src/components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo.jsx +++ b/src/components/Dashboard/Production/PrinterProfiles/PrinterProfileInfo.jsx @@ -13,7 +13,7 @@ import PictureIcon from '../../../Icons/PictureIcon' import RulerIcon from '../../../Icons/RulerIcon' import InfoCollapse from '../../common/InfoCollapse' import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder' -import LockIndicator from '../../common/LockIndicator' +import ActivityIndicator from '../../common/ActivityIndicator' import NoteIcon from '../../../Icons/NoteIcon' import NotesPanel from '../../common/NotesPanel' import ObjectActions from '../../common/ObjectActions' @@ -87,7 +87,7 @@ const PrinterProfileInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { startEditing={() => actionHandlerRef.current.callAction('edit')} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Production/Printers/ControlPrinter.jsx b/src/components/Dashboard/Production/Printers/ControlPrinter.jsx index 1bfe61f..d38d69b 100644 --- a/src/components/Dashboard/Production/Printers/ControlPrinter.jsx +++ b/src/components/Dashboard/Production/Printers/ControlPrinter.jsx @@ -79,7 +79,7 @@ const ControlPrinter = ({ slicerIntegration = false }) => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) diff --git a/src/components/Dashboard/Production/Printers/PrinterInfo.jsx b/src/components/Dashboard/Production/Printers/PrinterInfo.jsx index f050ec9..e97c3e5 100644 --- a/src/components/Dashboard/Production/Printers/PrinterInfo.jsx +++ b/src/components/Dashboard/Production/Printers/PrinterInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import NewPrinterProfile from '../PrinterProfiles/NewPrinterProfile' import { LoadingOutlined } from '@ant-design/icons' import loglevel from 'loglevel' @@ -15,7 +15,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -45,7 +45,7 @@ const PrinterInfo = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: {} }) @@ -81,7 +81,7 @@ const PrinterInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Production/Printers/SlicerIntegration.jsx b/src/components/Dashboard/Production/Printers/SlicerIntegration.jsx index bdc0a1e..d90d537 100644 --- a/src/components/Dashboard/Production/Printers/SlicerIntegration.jsx +++ b/src/components/Dashboard/Production/Printers/SlicerIntegration.jsx @@ -34,7 +34,7 @@ const SlicerIntegration = () => { isEditing: false, editLoading: false, formValid: false, - locked: false, + activities: [], loading: false, objectData: { pendingSlicerUploads: [] diff --git a/src/components/Dashboard/Production/SubJobs/SubJobInfo.jsx b/src/components/Dashboard/Production/SubJobs/SubJobInfo.jsx index f7d82e2..e0efee4 100644 --- a/src/components/Dashboard/Production/SubJobs/SubJobInfo.jsx +++ b/src/components/Dashboard/Production/SubJobs/SubJobInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Card } from 'antd' +import { Card, Flex, Space } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import useCollapseState from '../../hooks/useCollapseState' import NotesPanel from '../../common/NotesPanel' @@ -12,7 +12,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -64,7 +64,7 @@ const SubJobInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Sales/ListingVarients/ListingVarientInfo.jsx b/src/components/Dashboard/Sales/ListingVarients/ListingVarientInfo.jsx index 177e2c1..e66c95f 100644 --- a/src/components/Dashboard/Sales/ListingVarients/ListingVarientInfo.jsx +++ b/src/components/Dashboard/Sales/ListingVarients/ListingVarientInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config' import useCollapseState from '../../hooks/useCollapseState' @@ -13,7 +13,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -23,7 +23,6 @@ import UserNotifierToggle from '../../common/UserNotifierToggle.jsx' import ScrollBox from '../../common/ScrollBox.jsx' import PublishListingVarient from './PublishListingVarient.jsx' import UnpublishListingVarient from './UnpublishListingVarient.jsx' -import { Card } from 'antd' const log = loglevel.getLogger('ListingVarientInfo') log.setLevel(config.logLevel) @@ -89,7 +88,7 @@ const ListingVarientInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Sales/Listings/ListingInfo.jsx b/src/components/Dashboard/Sales/Listings/ListingInfo.jsx index bd56e03..d8c6a34 100644 --- a/src/components/Dashboard/Sales/Listings/ListingInfo.jsx +++ b/src/components/Dashboard/Sales/Listings/ListingInfo.jsx @@ -1,6 +1,6 @@ import { useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Modal } from 'antd' +import { Card, Flex, Modal, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config' import useCollapseState from '../../hooks/useCollapseState' @@ -14,7 +14,7 @@ import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ListingVarientIcon from '../../../Icons/ListingVarientIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -25,7 +25,6 @@ import ScrollBox from '../../common/ScrollBox.jsx' import NewListingVarient from '../ListingVarients/NewListingVarient.jsx' import PublishListing from './PublishListing.jsx' import UnpublishListing from './UnpublishListing.jsx' -import { Card } from 'antd' const log = loglevel.getLogger('ListingInfo') log.setLevel(config.logLevel) @@ -93,7 +92,7 @@ const ListingInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Sales/Marketplaces/MarketplaceInfo.jsx b/src/components/Dashboard/Sales/Marketplaces/MarketplaceInfo.jsx index 4e37bac..56b957d 100644 --- a/src/components/Dashboard/Sales/Marketplaces/MarketplaceInfo.jsx +++ b/src/components/Dashboard/Sales/Marketplaces/MarketplaceInfo.jsx @@ -1,6 +1,6 @@ import { useContext, useRef, useState } from 'react' import { useLocation } from 'react-router-dom' -import { Space, Flex, Modal, message } from 'antd' +import { Card, Flex, message, Modal, Space } from 'antd' import loglevel from 'loglevel' import config from '../../../../config' import useCollapseState from '../../hooks/useCollapseState' @@ -13,7 +13,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm' import EditButtons from '../../common/EditButtons' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -21,7 +21,6 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx' import DocumentPrintButton from '../../common/DocumentPrintButton.jsx' import UserNotifierToggle from '../../common/UserNotifierToggle.jsx' import ScrollBox from '../../common/ScrollBox.jsx' -import { Card } from 'antd' import SyncListings from './SyncListings.jsx' import SyncOrders from './SyncOrders.jsx' import ConfigureMarketplace from './ConfigureMarketplace.jsx' @@ -162,7 +161,7 @@ const MarketplaceInfo = () => { style={{ maxHeight: '100%', minHeight: 0 }} > - + { disabled={objectFormState.loading} /> - + { }} editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} - disabled={objectFormState.lock?.locked || objectFormState.loading} + disabled={objectFormState.beingEditedByOther || objectFormState.loading} loading={objectFormState.editLoading} /> diff --git a/src/components/Dashboard/Sales/SalesOrders/SalesOrderInfo.jsx b/src/components/Dashboard/Sales/SalesOrders/SalesOrderInfo.jsx index c74ad84..a626a2f 100644 --- a/src/components/Dashboard/Sales/SalesOrders/SalesOrderInfo.jsx +++ b/src/components/Dashboard/Sales/SalesOrders/SalesOrderInfo.jsx @@ -14,7 +14,7 @@ import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' import ObjectForm from '../../common/ObjectForm.jsx' import EditButtons from '../../common/EditButtons.jsx' -import LockIndicator from '../../common/LockIndicator.jsx' +import ActivityIndicator from '../../common/ActivityIndicator.jsx' import ActionHandler from '../../common/ActionHandler.jsx' import ObjectActions from '../../common/ObjectActions.jsx' import ObjectTable from '../../common/ObjectTable.jsx' @@ -133,7 +133,7 @@ const SalesOrderInfo = () => { }} > - + { disabled={objectFormState.loading} /> - + { editLoading={objectFormState.editLoading} formValid={objectFormState.formValid} disabled={ - objectFormState.lock?.locked || + objectFormState.beingEditedByOther || objectFormState.loading || editDisabled } diff --git a/src/components/Dashboard/common/ActivityIndicator.jsx b/src/components/Dashboard/common/ActivityIndicator.jsx new file mode 100644 index 0000000..a6c7ecd --- /dev/null +++ b/src/components/Dashboard/common/ActivityIndicator.jsx @@ -0,0 +1,255 @@ +import { useState, useEffect, useContext, useRef, memo } from 'react' +import { Flex, Card, Popover, Divider } from 'antd' +import { UserOutlined } from '@ant-design/icons' +import PropTypes from 'prop-types' +import EditIcon from '../../Icons/EditIcon' +import { ApiServerContext } from '../context/ApiServerContext' +import { AuthContext } from '../context/AuthContext' +import SpotlightTooltip from './SpotlightTooltip' +import { useNavigate } from 'react-router-dom' +import { getModelByName } from '../../../database/ObjectModels' + +const getUserId = (user) => user?._id || user + +const getProfileImageId = (user) => { + const profileImage = user?.profileImage + return ( + profileImage?._id || + (typeof profileImage === 'string' ? profileImage : null) + ) +} + +const getDisplayName = (user) => + user?.name || + [user?.firstName, user?.lastName].filter(Boolean).join(' ') || + user?.username || + 'User' + +const avatarImageStyle = { + width: 32, + height: 32, + borderRadius: '12px', + objectFit: 'cover', + border: '1px solid rgb(128, 128, 128, 0.4)' +} + +const avatarCardStyle = { borderRadius: '12px', width: 32, height: 32 } + +const UserProfileImage = memo(function UserProfileImage({ + profileImageId, + displayName +}) { + const { fetchFileContent } = useContext(ApiServerContext) + const fetchFileContentRef = useRef(fetchFileContent) + fetchFileContentRef.current = fetchFileContent + + const [profileImageUrl, setProfileImageUrl] = useState(null) + const profileImageUrlRef = useRef(null) + const loadedImageIdRef = useRef(null) + + useEffect(() => { + if (!profileImageId) { + if (profileImageUrlRef.current) { + URL.revokeObjectURL(profileImageUrlRef.current) + profileImageUrlRef.current = null + } + loadedImageIdRef.current = null + setProfileImageUrl(null) + return + } + + if ( + loadedImageIdRef.current === profileImageId && + profileImageUrlRef.current + ) { + return + } + + let cancelled = false + const file = { _id: profileImageId, name: '', extension: '' } + + const loadProfileImage = async () => { + try { + const fileURL = await fetchFileContentRef.current(file, false) + if (!cancelled) { + if (profileImageUrlRef.current) { + URL.revokeObjectURL(profileImageUrlRef.current) + } + profileImageUrlRef.current = fileURL || null + loadedImageIdRef.current = profileImageId + setProfileImageUrl(fileURL || null) + } else if (fileURL) { + URL.revokeObjectURL(fileURL) + } + } catch { + if (!cancelled) { + setProfileImageUrl(null) + } + } + } + + loadProfileImage() + + return () => { + cancelled = true + } + }, [profileImageId]) + + useEffect(() => { + return () => { + if (profileImageUrlRef.current) { + URL.revokeObjectURL(profileImageUrlRef.current) + profileImageUrlRef.current = null + } + loadedImageIdRef.current = null + } + }, []) + + if (profileImageUrl) { + return ( + {displayName} + ) + } + + return ( + + + + + + ) +}) + +UserProfileImage.propTypes = { + profileImageId: PropTypes.string, + displayName: PropTypes.string.isRequired +} + +const UserActivityAvatar = memo( + function UserActivityAvatar({ user, mode }) { + const profileImageId = getProfileImageId(user) + const displayName = getDisplayName(user) + + const navigate = useNavigate() + + const model = getModelByName('user') + + // Get hyperlink URL from model's default actions + var hyperlink = null + const defaultModelActions = + model.actions?.filter((action) => action.default == true) || [] + + if (defaultModelActions.length >= 1 && getUserId(user)) { + hyperlink = defaultModelActions[0].url(getUserId(user)) + } + + return ( + } + placement='bottomLeft' + arrow={false} + style={{ padding: 0 }} + > +
{ + navigate(hyperlink) + }} + > + + {mode === 'editing' && ( +
+ +
+ )} +
+
+ ) + }, + (prevProps, nextProps) => { + return ( + prevProps.mode === nextProps.mode && + getUserId(prevProps.user) === getUserId(nextProps.user) && + getProfileImageId(prevProps.user) === getProfileImageId(nextProps.user) + ) + } +) + +UserActivityAvatar.propTypes = { + user: PropTypes.object, + mode: PropTypes.string +} + +const ActivityIndicator = ({ + activities = [], + showStartingDivider = false +}) => { + const { userProfile } = useContext(AuthContext) + const currentUserId = userProfile?._id + + const otherActivities = activities.filter((activity) => { + if (!activity?.mode) return false + const userId = getUserId(activity.user) + return userId && userId !== currentUserId + }) + + if (otherActivities.length === 0) { + return null + } + + return ( + + {showStartingDivider && otherActivities.length >= 1 && ( + + )} + {otherActivities.map((activity) => { + const userId = getUserId(activity.user) + return ( + + ) + })} + + ) +} + +ActivityIndicator.propTypes = { + activities: PropTypes.array, + showStartingDivider: PropTypes.bool +} + +export default ActivityIndicator diff --git a/src/components/Dashboard/common/LockIndicator.jsx b/src/components/Dashboard/common/LockIndicator.jsx deleted file mode 100644 index b793530..0000000 --- a/src/components/Dashboard/common/LockIndicator.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Flex, Tag } from 'antd' -import IdDisplay from './IdDisplay' -import LockIcon from '../../Icons/LockIcon' -import PropTypes from 'prop-types' - -const LockIndicator = ({ lock }) => { - if (!lock?.locked || lock?.locked == false) { - return - } - return ( - - } style={{ margin: 0 }} color={'orange'} /> - - - ) -} - -LockIndicator.propTypes = { - lock: PropTypes.object -} - -export default LockIndicator diff --git a/src/components/Dashboard/common/ObjectForm.jsx b/src/components/Dashboard/common/ObjectForm.jsx index e3750a6..15be0c2 100644 --- a/src/components/Dashboard/common/ObjectForm.jsx +++ b/src/components/Dashboard/common/ObjectForm.jsx @@ -11,13 +11,13 @@ import { Form } from 'antd' import { ApiServerContext } from '../context/ApiServerContext' import { AuthContext } from '../context/AuthContext' import { useMessageContext } from '../context/MessageContext' -import PropTypes from 'prop-types' import DeleteObjectModal from './DeleteObjectModal' import merge from 'lodash/merge' import mergeWith from 'lodash/mergeWith' import set from 'lodash/set' import { getModelByName } from '../../../database/ObjectModels' -import { useNavigate } from 'react-router-dom' +import { useLocation, useNavigate } from 'react-router-dom' +import PropTypes from 'prop-types' const arrayReplaceCustomizer = (objValue, srcValue) => { if (Array.isArray(srcValue)) { @@ -25,6 +25,45 @@ const arrayReplaceCustomizer = (objValue, srcValue) => { } } +const getUserId = (user) => user?._id || user + +const getBeingEditedByOther = (activities, currentUserId) => + activities.some( + (activity) => + activity?.mode === 'editing' && + getUserId(activity.user) && + getUserId(activity.user) !== currentUserId + ) + +const isUserEditing = (activities, currentUserId) => + activities.some( + (activity) => + activity?.mode === 'editing' && getUserId(activity.user) === currentUserId + ) + +const applyActivityUpdate = (activities, activity) => { + const userId = getUserId(activity?.user) + if (!userId) { + return activities + } + + if (!activity?.mode) { + return activities.filter((item) => getUserId(item.user) !== userId) + } + + const existingIndex = activities.findIndex( + (item) => getUserId(item.user) === userId + ) + + if (existingIndex >= 0) { + const next = [...activities] + next[existingIndex] = { ...activities[existingIndex], ...activity } + return next + } + + return [...activities, activity] +} + const buildObjectFromEntries = (entries = []) => { return entries.reduce((acc, entry) => { const { namePath, value } = entry || {} @@ -55,7 +94,7 @@ const ObjectForm = forwardRef( const onStateChangeRef = useRef(onStateChange) const [fetchLoading, setFetchLoading] = useState(true) const [editLoading, setEditLoading] = useState(false) - const [lock, setLock] = useState({}) + const [activities, setActivities] = useState([]) const [initialized, setInitialized] = useState(false) const [isEditing, setIsEditing] = useState(false) const isEditingRef = useRef(false) @@ -70,20 +109,49 @@ const ObjectForm = forwardRef( fetchObject, updateObject, deleteObject, - lockObject, - unlockObject, - fetchObjectLock, + setObjectActivity, + clearObjectActivity, + fetchObjectActivities, showError, connected, subscribeToObjectUpdates, - subscribeToObjectLock, + subscribeToObjectActivity, flushFile } = useContext(ApiServerContext) - const { token } = useContext(AuthContext) + const { token, userProfile } = useContext(AuthContext) + const location = useLocation() const navigate = useNavigate() + + const clearAction = useCallback( + (actionParam = 'action') => { + const searchParams = new URLSearchParams(location.search) + searchParams.delete(actionParam) + const newSearch = searchParams.toString() + + console.log('newSearch', newSearch) + console.log('location.pathname', location.pathname) + const newPath = location.pathname + (newSearch ? `?${newSearch}` : '') + navigate(newPath, { replace: true }) + }, + [location.pathname, location.search, navigate] + ) + // Get the model definition for this object type const model = getModelByName(type) + const notifyActivityState = useCallback( + (nextActivities) => { + onStateChangeRef.current({ + activities: nextActivities, + beingEditedByOther: getBeingEditedByOther( + nextActivities, + userProfile?._id + ) + }) + }, + [userProfile?._id] + ) + // Check if the model has properties with type 'file' or 'fileList' const hasFileProperties = useCallback(() => { if (!model || !model.properties) return false @@ -122,7 +190,8 @@ const ObjectForm = forwardRef( const currentIdRef = useRef(id) const currentTypeRef = useRef(type) const currentIsEditingRef = useRef(isEditing) - const currentUnlockObjectRef = useRef(unlockObject) + const currentSetObjectActivityRef = useRef(setObjectActivity) + const currentClearObjectActivityRef = useRef(clearObjectActivity) const currentHasFilePropertiesRef = useRef(hasFileProperties) const currentFlushOrphanFilesRef = useRef(flushOrphanFiles) @@ -131,7 +200,8 @@ const ObjectForm = forwardRef( currentIdRef.current = id currentTypeRef.current = type currentIsEditingRef.current = isEditing - currentUnlockObjectRef.current = unlockObject + currentSetObjectActivityRef.current = setObjectActivity + currentClearObjectActivityRef.current = clearObjectActivity currentHasFilePropertiesRef.current = hasFileProperties currentFlushOrphanFilesRef.current = flushOrphanFiles }) @@ -233,6 +303,67 @@ const ObjectForm = forwardRef( [] ) + const releaseEditingState = useCallback( + (options = {}) => { + const { notifyParent = true } = options + + if (serverObjectData.current) { + const computedEntries = calculateComputedValues( + serverObjectData.current, + model + ) + const computedValuesObject = buildObjectFromEntries(computedEntries) + const resetFormData = merge( + {}, + serverObjectData.current, + computedValuesObject + ) + setIsEditing(false) + isEditingRef.current = false + form.setFieldsValue(resetFormData) + setObjectData({ ...resetFormData, _isEditing: false }) + } else { + setIsEditing(false) + isEditingRef.current = false + } + + if (notifyParent) { + onStateChangeRef.current({ isEditing: false }) + } + }, + [calculateComputedValues, form, model] + ) + + const handleEditingConflict = useCallback( + (nextActivities) => { + const currentUserId = userProfile?._id + if ( + !isEditingRef.current || + !getBeingEditedByOther(nextActivities, currentUserId) + ) { + return + } + + if (isUserEditing(nextActivities, currentUserId)) { + setObjectActivity(id, type, 'editing') + return + } + + releaseEditingState() + clearAction() + showMessageError('Another user is already editing this item') + }, + [ + id, + type, + userProfile?._id, + setObjectActivity, + releaseEditingState, + clearAction, + showMessageError + ] + ) + // Validate form on change (debounced to avoid heavy work on every keystroke) useEffect(() => { const timeoutId = setTimeout(() => { @@ -268,7 +399,7 @@ const ObjectForm = forwardRef( useEffect(() => { return () => { if (currentIdRef.current) { - currentUnlockObjectRef.current( + currentClearObjectActivityRef.current( currentIdRef.current, currentTypeRef.current ) @@ -289,9 +420,16 @@ const ObjectForm = forwardRef( setFetchLoading(true) onStateChangeRef.current({ loading: true }) const data = await fetchObject(id, type) - const lockEvent = await fetchObjectLock(id, type) - setLock(lockEvent) - onStateChangeRef.current({ lock: lockEvent }) + const initialActivities = await fetchObjectActivities(id, type) + setActivities(initialActivities) + notifyActivityState(initialActivities) + + if ( + isEditingRef.current && + getBeingEditedByOther(initialActivities, userProfile?._id) + ) { + releaseEditingState() + } serverObjectData.current = data @@ -313,14 +451,17 @@ const ObjectForm = forwardRef( } }, [ fetchObject, - fetchObjectLock, + fetchObjectActivities, + notifyActivityState, id, type, form, showMessageError, showError, calculateComputedValues, - model + model, + userProfile?._id, + releaseEditingState ]) // Update event handler @@ -331,12 +472,19 @@ const ObjectForm = forwardRef( }, []) // Update event handler - const updateLockEventHandler = useCallback((value) => { - setLock((prev) => { - onStateChangeRef.current({ lock: { ...prev, ...value } }) - return { ...prev, ...value } - }) - }, []) + const updateActivityEventHandler = useCallback( + (activity, activities) => { + setActivities((prev) => { + const next = Array.isArray(activities) + ? activities + : applyActivityUpdate(prev, activity) + notifyActivityState(next) + handleEditingConflict(next) + return next + }) + }, + [notifyActivityState, handleEditingConflict] + ) useEffect(() => { if (connected == true && initialized == false && id && token != null) { @@ -347,29 +495,37 @@ const ObjectForm = forwardRef( useEffect(() => { if (id && connected) { + currentSetObjectActivityRef.current( + id, + type, + isEditingRef.current == true ? 'editing' : 'viewing' + ) + const objectUpdatesUnsubscribe = subscribeToObjectUpdates( id, type, updateObjectEventHandler ) - const lockUnsubscribe = subscribeToObjectLock( + const activityUnsubscribe = subscribeToObjectActivity( id, type, - updateLockEventHandler + updateActivityEventHandler ) + return () => { if (objectUpdatesUnsubscribe) objectUpdatesUnsubscribe() - if (lockUnsubscribe) lockUnsubscribe() + if (activityUnsubscribe) activityUnsubscribe() + currentClearObjectActivityRef.current(id, type) } } }, [ id, type, - subscribeToObjectUpdates, - subscribeToObjectLock, - updateObjectEventHandler, connected, - updateLockEventHandler + subscribeToObjectUpdates, + subscribeToObjectActivity, + updateObjectEventHandler, + updateActivityEventHandler ]) // Debounce objectData updates sent to parent to limit re-renders @@ -380,51 +536,52 @@ const ObjectForm = forwardRef( return () => clearTimeout(timeoutId) }, [objectData]) - const startEditing = () => { - setIsEditing(true) - isEditingRef.current = true - const computedEntries = calculateComputedValues(objectData, model) - const computedValuesObject = buildObjectFromEntries(computedEntries) - setObjectData((prev) => ({ - ...prev, - ...computedValuesObject, - _isEditing: isEditingRef.current - })) - onStateChangeRef.current({ - isEditing: true, - objectData: { - ...objectData, - ...computedValuesObject, - _isEditing: isEditingRef.current + const startEditing = async () => { + try { + const latestActivities = await fetchObjectActivities(id, type) + setActivities(latestActivities) + notifyActivityState(latestActivities) + + if (getBeingEditedByOther(latestActivities, userProfile?._id)) { + clearAction() + showMessageError('Another user is already editing this item') + return } - }) - lockObject(id, type) + + const activityResult = await setObjectActivity(id, type, 'editing') + if (activityResult?.success === false) { + clearAction() + showMessageError('Another user is already editing this item') + return + } + + isEditingRef.current = true + setIsEditing(true) + + const computedEntries = calculateComputedValues(objectData, model) + const computedValuesObject = buildObjectFromEntries(computedEntries) + setObjectData((prev) => ({ + ...prev, + ...computedValuesObject, + _isEditing: true + })) + onStateChangeRef.current({ + isEditing: true, + objectData: { + ...objectData, + ...computedValuesObject, + _isEditing: true + } + }) + } catch (err) { + console.error(err) + showMessageError('Failed to start editing') + } } const cancelEditing = () => { - console.log('cancelEditing') - - if (serverObjectData.current) { - // Recalculate computed values when canceling - const computedEntries = calculateComputedValues( - serverObjectData.current, - model - ) - const computedValuesObject = buildObjectFromEntries(computedEntries) - const resetFormData = merge( - {}, - serverObjectData.current, - computedValuesObject - ) - setIsEditing(false) - isEditingRef.current = false - form.setFieldsValue(resetFormData) - console.log('resetFormData', resetFormData) - setObjectData({ ...resetFormData, _isEditing: isEditingRef.current }) - } - - onStateChangeRef.current({ isEditing: isEditingRef.current }) - unlockObject(id, type) + releaseEditingState() + setObjectActivity(id, type, 'viewing') } const handleUpdate = async () => { @@ -446,6 +603,7 @@ const ObjectForm = forwardRef( ...currentFormData, _isEditing: isEditingRef.current }) + setObjectActivity(id, type, 'viewing') showSuccess('Information updated successfully') } catch (err) { console.error(err) @@ -499,7 +657,7 @@ const ObjectForm = forwardRef( fetchLoading, isEditing, objectData, - lock + activities })) return ( @@ -587,7 +745,7 @@ const ObjectForm = forwardRef( setIsEditing, setObjectData, editLoading, - lock, + activities, handleFetchObject, handleDelete })} diff --git a/src/components/Dashboard/common/ObjectTable.jsx b/src/components/Dashboard/common/ObjectTable.jsx index dbaa79f..9649ff5 100644 --- a/src/components/Dashboard/common/ObjectTable.jsx +++ b/src/components/Dashboard/common/ObjectTable.jsx @@ -125,8 +125,8 @@ const ObjectTable = forwardRef( subscribeToObjectUpdates, subscribeToObjectTypeUpdates, updateMultipleObjects, - lockObject, - unlockObject + setObjectActivity, + clearObjectActivity } = useContext(ApiServerContext) const isMobile = useMediaQuery({ maxWidth: 768 }) const navigate = useNavigate() @@ -532,19 +532,19 @@ const ObjectTable = forwardRef( setIsEditing(true) tableData.forEach((item) => { if (!item.isSkeleton) { - lockObject(item._id, type) + setObjectActivity(item._id, type, 'editing') } }) - }, [tableData, lockObject, type]) + }, [tableData, setObjectActivity, type]) const cancelEditing = useCallback(() => { setIsEditing(false) tableData.forEach((item) => { if (!item.isSkeleton) { - unlockObject(item._id, type) + clearObjectActivity(item._id, type) } }) - }, [tableData, unlockObject, type]) + }, [tableData, clearObjectActivity, type]) const handleUpdate = useCallback(async () => { setEditLoading(true) @@ -560,7 +560,7 @@ const ObjectTable = forwardRef( reload() tableData.forEach((item) => { if (!item.isSkeleton) { - unlockObject(item._id, type) + clearObjectActivity(item._id, type) } }) } catch (err) { @@ -568,7 +568,7 @@ const ObjectTable = forwardRef( } finally { setEditLoading(false) } - }, [type, updateMultipleObjects, reload, tableData, unlockObject]) + }, [type, updateMultipleObjects, reload, tableData, clearObjectActivity]) // Update event handler for real-time updates const updateEventHandler = useCallback((id, updatedData) => { diff --git a/src/components/Dashboard/context/ApiServerContext.jsx b/src/components/Dashboard/context/ApiServerContext.jsx index f460e11..793d171 100644 --- a/src/components/Dashboard/context/ApiServerContext.jsx +++ b/src/components/Dashboard/context/ApiServerContext.jsx @@ -104,45 +104,44 @@ const ApiServerProvider = ({ children }) => { const [userSettings, setUserSettings] = useState(createEmptyUserSettings) const [userSettingsLoaded, setUserSettingsLoaded] = useState(false) const subscribedCallbacksRef = useRef(new Map()) - const subscribedLockCallbacksRef = useRef(new Map()) + const subscribedActivityCallbacksRef = useRef(new Map()) const notificationListenersRef = useRef(new Set()) const completedLaunchSessionsRef = useRef(new Set()) - const handleLockUpdate = useCallback( - async (lockData) => { - logger.debug('Notifying lock update:', lockData) - const objectId = lockData._id || lockData.id + const handleActivityUpdate = useCallback(async (data) => { + logger.debug('Notifying activity update:', data) + const objectId = data._id || data.id + const objectType = data.objectType + const activity = data.activity + const activities = data.activities + const callbacksRefKey = `${objectType}:${objectId}` - if ( - objectId && - subscribedLockCallbacksRef.current.has(objectId) && - lockData.user != userProfile?._id - ) { - const callbacks = subscribedLockCallbacksRef.current.get(objectId) - logger.debug( - `Calling ${callbacks.length} lock callbacks for object:`, - objectId - ) - callbacks.forEach((callback) => { - try { - callback(lockData) - } catch (error) { - logger.error('Error in lock update callback:', error) - } - }) - } else { - logger.debug( - `No lock callbacks found for object: ${objectId}, subscribed lock callbacks:`, - Array.from(subscribedLockCallbacksRef.current.keys()) - ) - } - }, - [userProfile?._id] - ) + if ( + objectId && + objectType && + activity && + subscribedActivityCallbacksRef.current.has(callbacksRefKey) + ) { + const callbacks = subscribedActivityCallbacksRef.current.get( + callbacksRefKey + ) + logger.debug( + `Calling ${callbacks.length} activity callbacks for object:`, + objectId + ) + callbacks.forEach((callback) => { + try { + callback(activity, activities) + } catch (error) { + logger.error('Error in activity update callback:', error) + } + }) + } + }, []) const clearSubscriptions = useCallback(() => { subscribedCallbacksRef.current.clear() - subscribedLockCallbacksRef.current.clear() + subscribedActivityCallbacksRef.current.clear() notificationListenersRef.current.clear() }, []) @@ -255,7 +254,7 @@ const ApiServerProvider = ({ children }) => { newSocket.on('objectEvent', handleObjectEvent) newSocket.on('objectNew', handleObjectNew) newSocket.on('objectDelete', handleObjectDelete) - newSocket.on('lockUpdate', handleLockUpdate) + newSocket.on('activityUpdate', handleActivityUpdate) newSocket.on('modelStats', handleModelStats) newSocket.on('notification', (data) => { let notification @@ -306,7 +305,7 @@ const ApiServerProvider = ({ children }) => { token, authenticated, messageApi, - handleLockUpdate, + handleActivityUpdate, clearSubscriptions, getUserSettings ]) @@ -332,44 +331,49 @@ const ApiServerProvider = ({ children }) => { } }, [token, authenticated, connectToServer]) - const lockObject = (id, type) => { - logger.debug('Locking ' + id) + const setObjectActivity = (id, type, mode) => { + logger.debug('Setting activity for', id, mode) if (socketRef.current && socketRef.current.connected) { - socketRef.current.emit('lock', { _id: id, type: type }) - logger.debug('Sent lock command for object:', id) - } - } - - const unlockObject = (id, type) => { - logger.debug('Unlocking ' + id) - if (socketRef.current && socketRef.current.connected == true) { - socketRef.current.emit('unlock', { _id: id, type: type }) - logger.debug('Sent unlock command for object:', id) - } - } - - const fetchObjectLock = async (id, type) => { - if (socketRef.current && socketRef.current.connected == true) { - logger.debug('Fetching lock status for ' + id) return new Promise((resolve) => { socketRef.current.emit( - 'getLock', + 'setActivity', + { _id: id, type, mode }, + (result) => { + logger.debug('Received setActivity result for object:', id, result) + resolve(result || { success: true }) + } + ) + }) + } + return Promise.resolve({ success: false, error: 'not_connected' }) + } + + const clearObjectActivity = (id, type) => { + logger.debug('Clearing activity for', id) + if (socketRef.current && socketRef.current.connected == true) { + socketRef.current.emit('clearActivity', { _id: id, type }) + logger.debug('Sent clearActivity command for object:', id) + } + } + + const fetchObjectActivities = async (id, type) => { + if (socketRef.current && socketRef.current.connected == true) { + logger.debug('Fetching activities for ' + id) + return new Promise((resolve) => { + socketRef.current.emit( + 'getActivities', { _id: id, type: type }, - (lockEvent) => { - logger.debug('Received lock status for object:', id, lockEvent) - if (lockEvent.user != userProfile?._id) { - resolve(lockEvent) - } else { - resolve(null) - } + (result) => { + logger.debug('Received activities for object:', id, result) + resolve(result?.activities || []) } ) - logger.debug('Sent fetch lock command for object:', id) }) } + return [] } const handleObjectUpdate = async (data) => { @@ -677,21 +681,25 @@ const ApiServerProvider = ({ children }) => { [offObjectTypeUpdatesEvent] ) - const offLockEvent = useCallback((id, callback) => { + const offActivityEvent = useCallback((id, objectType, callback) => { if (socketRef.current && socketRef.current.connected == true) { - // Remove callback from the subscribed lock callbacks map - if (subscribedLockCallbacksRef.current.has(id)) { - const callbacks = subscribedLockCallbacksRef.current - .get(id) + const callbacksRefKey = `${objectType}:${id}` + if (subscribedActivityCallbacksRef.current.has(callbacksRefKey)) { + const callbacks = subscribedActivityCallbacksRef.current + .get(callbacksRefKey) .filter((cb) => cb !== callback) if (callbacks.length === 0) { - subscribedLockCallbacksRef.current.delete(id) + subscribedActivityCallbacksRef.current.delete(callbacksRefKey) + socketRef.current.emit('unsubscribeObjectActivity', { + _id: id, + objectType + }) } else { - subscribedLockCallbacksRef.current.set(id, callbacks) + subscribedActivityCallbacksRef.current.set(callbacksRefKey, callbacks) } } - logger.debug('Removed lock event listener for object:', id) + logger.debug('Removed activity event listener for object:', id) } }, []) @@ -835,27 +843,41 @@ const ApiServerProvider = ({ children }) => { [offModelStats] ) - const subscribeToObjectLock = useCallback( + const subscribeToObjectActivity = useCallback( (id, type, callback) => { - logger.debug('Subscribing to lock for object:', id, 'type:', type) + logger.debug('Subscribing to activity for object:', id, 'type:', type) if (socketRef.current && socketRef.current.connected == true) { - // Add callback to the subscribed lock callbacks map immediately - if (!subscribedLockCallbacksRef.current.has(id)) { - subscribedLockCallbacksRef.current.set(id, []) + const callbacksRefKey = `${type}:${id}` + if (!subscribedActivityCallbacksRef.current.has(callbacksRefKey)) { + subscribedActivityCallbacksRef.current.set(callbacksRefKey, []) } - subscribedLockCallbacksRef.current.get(id).push(callback) - logger.debug( - `Added lock callback for object ${id}, total lock callbacks: ${subscribedLockCallbacksRef.current.get(id).length}` - ) + const callbacksLength = + subscribedActivityCallbacksRef.current.get(callbacksRefKey).length - socketRef.current.emit('subscribe_lock', { _id: id, objectType: type }) - logger.debug('Registered lock event listener for object:', id) + subscribedActivityCallbacksRef.current + .get(callbacksRefKey) + .push(callback) - // Return cleanup function - return () => offLockEvent(id, callback) + if (callbacksLength <= 0) { + socketRef.current.emit( + 'subscribeToObjectActivity', + { _id: id, objectType: type }, + (result) => { + if (result?.success && Array.isArray(result.activities)) { + result.activities.forEach((activity) => { + callback(activity) + }) + } + } + ) + } + + logger.debug('Registered activity event listener for object:', id) + + return () => offActivityEvent(id, type, callback) } }, - [offLockEvent] + [offActivityEvent] ) const showError = useCallback( @@ -1971,9 +1993,9 @@ const ApiServerProvider = ({ children }) => { userSettingsLoaded, getUserSettings, updateUserSettings, - lockObject, - unlockObject, - fetchObjectLock, + setObjectActivity, + clearObjectActivity, + fetchObjectActivities, updateObject, updateMultipleObjects, createObject, @@ -1983,7 +2005,7 @@ const ApiServerProvider = ({ children }) => { subscribeToObjectUpdates, subscribeToObjectEvent, subscribeToObjectTypeUpdates, - subscribeToObjectLock, + subscribeToObjectActivity, subscribeToModelStats, fetchObject, fetchObjects,