Compare commits
No commits in common. "cd83679232e5b398773a015aedfb7fe667a2192a" and "2a18f3d697a7b5ca32ee0e5d6982e214b727351a" have entirely different histories.
cd83679232
...
2a18f3d697
@ -1,68 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { Modal, Button, Space, Typography } from 'antd'
|
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
|
||||||
import BinIcon from '../../Icons/BinIcon'
|
|
||||||
|
|
||||||
const { Text } = Typography
|
|
||||||
|
|
||||||
const DeleteObjectModal = ({
|
|
||||||
open,
|
|
||||||
onOk,
|
|
||||||
onCancel,
|
|
||||||
loading,
|
|
||||||
objectType,
|
|
||||||
objectName
|
|
||||||
}) => {
|
|
||||||
const model = getModelByName(objectType)
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
open={open}
|
|
||||||
title={
|
|
||||||
<Space size={'middle'}>
|
|
||||||
<BinIcon />
|
|
||||||
{`Confirm Delete ${model.label}`}
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
onOk={onOk}
|
|
||||||
onCancel={onCancel}
|
|
||||||
okText='Delete'
|
|
||||||
cancelText='Cancel'
|
|
||||||
okType='danger'
|
|
||||||
closable={false}
|
|
||||||
centered
|
|
||||||
maskClosable={false}
|
|
||||||
footer={[
|
|
||||||
<Button key='cancel' onClick={onCancel} disabled={loading}>
|
|
||||||
Cancel
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
key='delete'
|
|
||||||
type='primary'
|
|
||||||
danger
|
|
||||||
onClick={onOk}
|
|
||||||
loading={loading}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
Are you sure you want to delete this {model.label.toLowerCase()}
|
|
||||||
{objectName ? ` "${objectName}"` : ''}?
|
|
||||||
</Text>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteObjectModal.propTypes = {
|
|
||||||
open: PropTypes.bool.isRequired,
|
|
||||||
onOk: PropTypes.func.isRequired,
|
|
||||||
onCancel: PropTypes.func.isRequired,
|
|
||||||
loading: PropTypes.bool,
|
|
||||||
objectType: PropTypes.string.isRequired,
|
|
||||||
objectName: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DeleteObjectModal
|
|
||||||
@ -49,7 +49,7 @@ const IdDisplay = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex align={'end'} className='iddisplay'>
|
<Flex align={'center'} className='iddisplay'>
|
||||||
{(() => {
|
{(() => {
|
||||||
const content = (
|
const content = (
|
||||||
<Flex gap={4}>
|
<Flex gap={4}>
|
||||||
|
|||||||
@ -12,33 +12,33 @@ const InfoCollapse = ({
|
|||||||
active,
|
active,
|
||||||
onToggle,
|
onToggle,
|
||||||
className = '',
|
className = '',
|
||||||
collapseKey = 'default'
|
key = 'default'
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Collapse
|
<Collapse
|
||||||
ghost
|
ghost
|
||||||
expandIconPosition='end'
|
expandIconPosition='end'
|
||||||
activeKey={active ? [collapseKey] : []}
|
activeKey={active ? [key] : []}
|
||||||
onChange={(keys) => onToggle(keys.length > 0)}
|
onChange={(keys) => onToggle(keys.length > 0)}
|
||||||
expandIcon={({ isActive }) => (
|
expandIcon={({ isActive }) => (
|
||||||
<CaretLeftOutlined rotate={isActive ? -90 : 0} />
|
<CaretLeftOutlined rotate={isActive ? -90 : 0} />
|
||||||
)}
|
)}
|
||||||
className={`no-h-padding-collapse ${className}`}
|
className={`no-h-padding-collapse ${className}`}
|
||||||
items={[
|
>
|
||||||
{
|
<Collapse.Panel
|
||||||
key: collapseKey,
|
header={
|
||||||
children: children,
|
<Flex align='center' gap={'middle'}>
|
||||||
label: (
|
{icon}
|
||||||
<Flex align='center' gap={'middle'}>
|
<Title level={5} style={{ margin: 0 }}>
|
||||||
{icon}
|
{title}
|
||||||
<Title level={5} style={{ margin: 0 }}>
|
</Title>
|
||||||
{title}
|
</Flex>
|
||||||
</Title>
|
|
||||||
</Flex>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
]}
|
key={key}
|
||||||
/>
|
>
|
||||||
|
{children}
|
||||||
|
</Collapse.Panel>
|
||||||
|
</Collapse>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ InfoCollapse.propTypes = {
|
|||||||
active: PropTypes.bool.isRequired,
|
active: PropTypes.bool.isRequired,
|
||||||
onToggle: PropTypes.func.isRequired,
|
onToggle: PropTypes.func.isRequired,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
collapseKey: PropTypes.string
|
key: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default InfoCollapse
|
export default InfoCollapse
|
||||||
|
|||||||
@ -26,7 +26,6 @@ import MarkdownDisplay from './MarkdownDisplay'
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
|
||||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
||||||
import NoteTypeSelect from './NoteTypeSelect'
|
import NoteTypeSelect from './NoteTypeSelect'
|
||||||
import IdDisplay from './IdDisplay'
|
import IdDisplay from './IdDisplay'
|
||||||
@ -260,7 +259,7 @@ NoteItem.propTypes = {
|
|||||||
onChildNoteAdded: PropTypes.func
|
onChildNoteAdded: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
const NotesPanel = ({ _id, onNewNote, type }) => {
|
const NotesPanel = ({ _id, onNewNote }) => {
|
||||||
const [newNoteOpen, setNewNoteOpen] = useState(false)
|
const [newNoteOpen, setNewNoteOpen] = useState(false)
|
||||||
const [showMarkdown, setShowMarkdown] = useState(false)
|
const [showMarkdown, setShowMarkdown] = useState(false)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
@ -274,7 +273,6 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
|
|||||||
const [expandedNotes, setExpandedNotes] = useState({})
|
const [expandedNotes, setExpandedNotes] = useState({})
|
||||||
const [newNoteForm] = Form.useForm()
|
const [newNoteForm] = Form.useForm()
|
||||||
const [selectedParentId, setSelectedParentId] = useState(null)
|
const [selectedParentId, setSelectedParentId] = useState(null)
|
||||||
const [selectedParentType, setSelectedParentType] = useState(null)
|
|
||||||
const [childNoteCallbacks, setChildNoteCallbacks] = useState({})
|
const [childNoteCallbacks, setChildNoteCallbacks] = useState({})
|
||||||
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false)
|
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false)
|
||||||
const [noteToDelete, setNoteToDelete] = useState(null)
|
const [noteToDelete, setNoteToDelete] = useState(null)
|
||||||
@ -291,33 +289,30 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
|
|||||||
}, [newNoteForm, newNoteFormUpdateValues])
|
}, [newNoteForm, newNoteFormUpdateValues])
|
||||||
|
|
||||||
const { authenticated, userProfile } = useContext(AuthContext)
|
const { authenticated, userProfile } = useContext(AuthContext)
|
||||||
const { fetchNotes } = useContext(ApiServerContext)
|
|
||||||
|
|
||||||
const fetchData = useCallback(
|
const fetchData = useCallback(async (id) => {
|
||||||
async (id) => {
|
try {
|
||||||
try {
|
const response = await axios.get(`${config.backendUrl}/notes`, {
|
||||||
const newData = await fetchNotes(id)
|
params: {
|
||||||
setLoading(false)
|
parent: id,
|
||||||
return newData
|
sort: 'createdAt',
|
||||||
} catch (error) {
|
order: 'ascend'
|
||||||
setNotes([])
|
},
|
||||||
setError(error)
|
headers: {
|
||||||
setLoading(false)
|
Accept: 'application/json'
|
||||||
}
|
},
|
||||||
},
|
withCredentials: true
|
||||||
[fetchNotes]
|
})
|
||||||
)
|
|
||||||
|
|
||||||
const handleNewChildNote = useCallback(
|
const newData = response.data
|
||||||
(parentId) => {
|
setLoading(false)
|
||||||
setSelectedParentId(parentId)
|
return newData
|
||||||
setSelectedParentType('note')
|
} catch (error) {
|
||||||
setNewNoteOpen(true)
|
setNotes([])
|
||||||
newNoteForm.resetFields()
|
setError(error)
|
||||||
setNewNoteFormValues({})
|
setLoading(false)
|
||||||
},
|
}
|
||||||
[newNoteForm]
|
}, [])
|
||||||
)
|
|
||||||
|
|
||||||
const generateNotes = useCallback(
|
const generateNotes = useCallback(
|
||||||
async (id) => {
|
async (id) => {
|
||||||
@ -350,7 +345,7 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
|
|||||||
expandedNotes={expandedNotes}
|
expandedNotes={expandedNotes}
|
||||||
setExpandedNotes={setExpandedNotes}
|
setExpandedNotes={setExpandedNotes}
|
||||||
fetchData={fetchData}
|
fetchData={fetchData}
|
||||||
onNewNote={handleNewChildNote}
|
onNewNote={handleNewNoteFromDropdown}
|
||||||
onDeleteNote={handleDeleteNote}
|
onDeleteNote={handleDeleteNote}
|
||||||
userProfile={userProfile}
|
userProfile={userProfile}
|
||||||
onChildNoteAdded={(noteId, callback) => {
|
onChildNoteAdded={(noteId, callback) => {
|
||||||
@ -362,7 +357,7 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
|
|||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
[loading, fetchData, expandedNotes, userProfile, handleNewChildNote]
|
[loading, fetchData, expandedNotes, userProfile]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleNewNote = async () => {
|
const handleNewNote = async () => {
|
||||||
@ -370,11 +365,7 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
|
|||||||
try {
|
try {
|
||||||
await axios.post(
|
await axios.post(
|
||||||
`${config.backendUrl}/notes`,
|
`${config.backendUrl}/notes`,
|
||||||
{
|
{ ...newNoteFormValues, parent: selectedParentId || _id },
|
||||||
...newNoteFormValues,
|
|
||||||
parent: selectedParentId,
|
|
||||||
parentType: selectedParentType
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
withCredentials: true
|
withCredentials: true
|
||||||
}
|
}
|
||||||
@ -411,6 +402,13 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleNewNoteFromDropdown = (parentId) => {
|
||||||
|
setSelectedParentId(parentId)
|
||||||
|
setNewNoteOpen(true)
|
||||||
|
newNoteForm.resetFields()
|
||||||
|
setNewNoteFormValues({})
|
||||||
|
}
|
||||||
|
|
||||||
const handleDeleteNote = async (noteId) => {
|
const handleDeleteNote = async (noteId) => {
|
||||||
setNoteToDelete(noteId)
|
setNoteToDelete(noteId)
|
||||||
setDeleteConfirmOpen(true)
|
setDeleteConfirmOpen(true)
|
||||||
@ -501,8 +499,7 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
handleReloadData()
|
handleReloadData()
|
||||||
} else if (key === 'newNote') {
|
} else if (key === 'newNote') {
|
||||||
setSelectedParentId(_id)
|
setSelectedParentId(null)
|
||||||
setSelectedParentType(type)
|
|
||||||
setNewNoteOpen(true)
|
setNewNoteOpen(true)
|
||||||
newNoteForm.resetFields()
|
newNoteForm.resetFields()
|
||||||
setNewNoteFormValues({})
|
setNewNoteFormValues({})
|
||||||
@ -525,8 +522,7 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
|
|||||||
icon={<PlusIcon />}
|
icon={<PlusIcon />}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedParentId(_id)
|
setSelectedParentId(null)
|
||||||
setSelectedParentType(type)
|
|
||||||
setNewNoteOpen(true)
|
setNewNoteOpen(true)
|
||||||
newNoteForm.resetFields()
|
newNoteForm.resetFields()
|
||||||
setNewNoteFormValues({})
|
setNewNoteFormValues({})
|
||||||
@ -692,7 +688,6 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
|
|||||||
|
|
||||||
NotesPanel.propTypes = {
|
NotesPanel.propTypes = {
|
||||||
_id: PropTypes.string.isRequired,
|
_id: PropTypes.string.isRequired,
|
||||||
type: PropTypes.string.isRequired,
|
|
||||||
onNewNote: PropTypes.func
|
onNewNote: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,6 @@ function mapActionsToMenuItems(actions, currentUrlWithActions, id) {
|
|||||||
const item = {
|
const item = {
|
||||||
key: action.key || action.name,
|
key: action.key || action.name,
|
||||||
label: action.label,
|
label: action.label,
|
||||||
danger: action?.danger || false,
|
|
||||||
icon: action.icon ? React.createElement(action.icon) : undefined,
|
icon: action.icon ? React.createElement(action.icon) : undefined,
|
||||||
disabled: actionUrl && actionUrl === currentUrlWithActions
|
disabled: actionUrl && actionUrl === currentUrlWithActions
|
||||||
}
|
}
|
||||||
@ -54,10 +53,11 @@ const ObjectActions = ({
|
|||||||
location.search
|
location.search
|
||||||
)
|
)
|
||||||
|
|
||||||
|
console.log('curr url', currentUrlWithoutActions)
|
||||||
|
|
||||||
|
// Filter out actions whose url matches currentUrl
|
||||||
const filteredActions = actions.filter(
|
const filteredActions = actions.filter(
|
||||||
(action) =>
|
(action) => !(action.url(id) && action.url(id) === currentUrlWithoutActions)
|
||||||
typeof action.url !== 'function' ||
|
|
||||||
action.url(id) !== currentUrlWithoutActions
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const currentUrlWithActions = location.pathname + location.search
|
const currentUrlWithActions = location.pathname + location.search
|
||||||
|
|||||||
@ -13,7 +13,6 @@ const ObjectInfo = ({
|
|||||||
properties = [],
|
properties = [],
|
||||||
required = undefined,
|
required = undefined,
|
||||||
visibleProperties = {},
|
visibleProperties = {},
|
||||||
objectPropertyProps = {},
|
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const allItems = getModelProperties(type)
|
const allItems = getModelProperties(type)
|
||||||
@ -56,7 +55,6 @@ const ObjectInfo = ({
|
|||||||
children: (
|
children: (
|
||||||
<ObjectProperty
|
<ObjectProperty
|
||||||
{...item}
|
{...item}
|
||||||
{...objectPropertyProps}
|
|
||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
@ -93,8 +91,7 @@ ObjectInfo.propTypes = {
|
|||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
objectData: PropTypes.object,
|
objectData: PropTypes.object,
|
||||||
required: PropTypes.bool,
|
required: PropTypes.bool,
|
||||||
visibleProperties: PropTypes.object,
|
visibleProperties: PropTypes.object
|
||||||
objectPropertyProps: PropTypes.object
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectInfo
|
export default ObjectInfo
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { List, Typography, Flex } from 'antd'
|
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
|
||||||
import IdDisplay from './IdDisplay'
|
|
||||||
|
|
||||||
const { Text } = Typography
|
|
||||||
|
|
||||||
const ObjectList = ({ value, objectType, bordered = true }) => {
|
|
||||||
if (!value || !Array.isArray(value) || value.length === 0) {
|
|
||||||
return <Text type='secondary'>n/a</Text>
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<List
|
|
||||||
size='small'
|
|
||||||
bordered={bordered}
|
|
||||||
dataSource={value}
|
|
||||||
renderItem={(item) => {
|
|
||||||
const model = getModelByName(objectType)
|
|
||||||
const Icon = model.icon
|
|
||||||
return (
|
|
||||||
<List.Item>
|
|
||||||
<Flex gap={'small'} align='center'>
|
|
||||||
<Icon />
|
|
||||||
{item?.name ? <Text ellipsis>{item.name}</Text> : null}
|
|
||||||
{item?._id ? (
|
|
||||||
<IdDisplay
|
|
||||||
id={item?._id}
|
|
||||||
longId={false}
|
|
||||||
type={objectType}
|
|
||||||
showHyperlink={true}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Flex>
|
|
||||||
</List.Item>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectList.propTypes = {
|
|
||||||
value: PropTypes.array,
|
|
||||||
bordered: PropTypes.bool,
|
|
||||||
objectType: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ObjectList
|
|
||||||
@ -37,10 +37,6 @@ import { getPropertyValue } from '../../../database/ObjectModels'
|
|||||||
import PropertyChanges from './PropertyChanges'
|
import PropertyChanges from './PropertyChanges'
|
||||||
import NetGrossDisplay from './NetGrossDisplay'
|
import NetGrossDisplay from './NetGrossDisplay'
|
||||||
import NetGrossInput from './NetGrossInput'
|
import NetGrossInput from './NetGrossInput'
|
||||||
import ObjectList from './ObjectList'
|
|
||||||
import VarianceDisplay from './VarianceDisplay'
|
|
||||||
import OperationDisplay from './OperationDisplay'
|
|
||||||
import MarkdownDisplay from './MarkdownDisplay'
|
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
@ -75,7 +71,7 @@ const ObjectProperty = ({
|
|||||||
initial = false,
|
initial = false,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
if (value && typeof value == 'function' && objectData) {
|
if (typeof value == 'function' && objectData) {
|
||||||
value = value(objectData)
|
value = value(objectData)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,14 +87,6 @@ const ObjectProperty = ({
|
|||||||
difference = difference(objectData)
|
difference = difference(objectData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prefix && typeof prefix == 'function' && objectData) {
|
|
||||||
prefix = prefix(objectData)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (suffix && typeof suffix == 'function' && objectData) {
|
|
||||||
suffix = suffix(objectData)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
value = getPropertyValue(objectData, name)
|
value = getPropertyValue(objectData, name)
|
||||||
}
|
}
|
||||||
@ -230,19 +218,6 @@ const ObjectProperty = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'variance': {
|
|
||||||
if (value != null) {
|
|
||||||
return (
|
|
||||||
<VarianceDisplay value={value} prefix={prefix} suffix={suffix} />
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Text type='secondary' {...textParams}>
|
|
||||||
n/a
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'text':
|
case 'text':
|
||||||
if (value != null && value != '') {
|
if (value != null && value != '') {
|
||||||
return (
|
return (
|
||||||
@ -259,16 +234,6 @@ const ObjectProperty = ({
|
|||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case 'markdown':
|
|
||||||
if (value != null && value != '') {
|
|
||||||
return <MarkdownDisplay content={value} />
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Text type='secondary' {...textParams}>
|
|
||||||
n/a
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
case 'email':
|
case 'email':
|
||||||
if (value != null && value != '') {
|
if (value != null && value != '') {
|
||||||
return <EmailDisplay email={value} />
|
return <EmailDisplay email={value} />
|
||||||
@ -300,9 +265,6 @@ const ObjectProperty = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'objectList': {
|
|
||||||
return <ObjectList value={value} objectType={objectType} />
|
|
||||||
}
|
|
||||||
case 'state': {
|
case 'state': {
|
||||||
if (value && value?.type) {
|
if (value && value?.type) {
|
||||||
switch (objectType) {
|
switch (objectType) {
|
||||||
@ -384,17 +346,6 @@ const ObjectProperty = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'operation': {
|
|
||||||
if (value != null) {
|
|
||||||
return <OperationDisplay operation={value} />
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Text type='secondary' {...textParams}>
|
|
||||||
n/a
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'propertyChanges': {
|
case 'propertyChanges': {
|
||||||
return <PropertyChanges type={objectType} value={value} />
|
return <PropertyChanges type={objectType} value={value} />
|
||||||
}
|
}
|
||||||
@ -604,7 +555,7 @@ const ObjectProperty = ({
|
|||||||
<PrinterSelect placeholder={label} />
|
<PrinterSelect placeholder={label} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
case 'gcodeFile':
|
case 'gcodefile':
|
||||||
return (
|
return (
|
||||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||||
<GCodeFileSelect placeholder={label} />
|
<GCodeFileSelect placeholder={label} />
|
||||||
@ -652,22 +603,20 @@ const ObjectProperty = ({
|
|||||||
|
|
||||||
ObjectProperty.propTypes = {
|
ObjectProperty.propTypes = {
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
value: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
value: PropTypes.any,
|
||||||
isEditing: PropTypes.bool,
|
isEditing: PropTypes.bool,
|
||||||
formItemProps: PropTypes.object,
|
formItemProps: PropTypes.object,
|
||||||
required: PropTypes.bool,
|
required: PropTypes.bool,
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
prefix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
prefix: PropTypes.string,
|
||||||
suffix: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
suffix: PropTypes.string,
|
||||||
min: PropTypes.number,
|
min: PropTypes.number,
|
||||||
max: PropTypes.number,
|
max: PropTypes.number,
|
||||||
step: PropTypes.number,
|
step: PropTypes.number,
|
||||||
showLabel: PropTypes.bool,
|
showLabel: PropTypes.bool,
|
||||||
objectType: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
objectType: PropTypes.string,
|
||||||
readOnly: PropTypes.bool,
|
readOnly: PropTypes.bool,
|
||||||
disabled: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
|
disabled: PropTypes.bool
|
||||||
difference: PropTypes.oneOfType([PropTypes.any, PropTypes.func]),
|
|
||||||
objectData: PropTypes.object
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectProperty
|
export default ObjectProperty
|
||||||
|
|||||||
@ -1,57 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import { Space, Tag } from 'antd'
|
|
||||||
import QuestionCircleIcon from '../../Icons/QuestionCircleIcon'
|
|
||||||
import PlusIcon from '../../Icons/PlusIcon'
|
|
||||||
import BinIcon from '../../Icons/BinIcon'
|
|
||||||
import EditIcon from '../../Icons/EditIcon'
|
|
||||||
|
|
||||||
const OperationDisplay = ({
|
|
||||||
operation,
|
|
||||||
showIcon = true,
|
|
||||||
showText = true,
|
|
||||||
showColor = true
|
|
||||||
}) => {
|
|
||||||
var tagText = 'False'
|
|
||||||
var tagIcon = <QuestionCircleIcon />
|
|
||||||
var tagColor = 'default'
|
|
||||||
switch (operation) {
|
|
||||||
case 'new':
|
|
||||||
tagText = 'New'
|
|
||||||
tagIcon = <PlusIcon />
|
|
||||||
tagColor = 'success'
|
|
||||||
break
|
|
||||||
case 'delete':
|
|
||||||
tagText = 'Delete'
|
|
||||||
tagIcon = <BinIcon />
|
|
||||||
tagColor = 'error'
|
|
||||||
break
|
|
||||||
case 'edit':
|
|
||||||
tagText = 'Edit'
|
|
||||||
tagIcon = <EditIcon />
|
|
||||||
tagColor = 'blue'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Space>
|
|
||||||
<Tag
|
|
||||||
style={{ margin: 0 }}
|
|
||||||
color={showColor ? tagColor : 'default'}
|
|
||||||
icon={showIcon ? tagIcon : undefined}
|
|
||||||
>
|
|
||||||
{showText ? tagText : null}
|
|
||||||
</Tag>
|
|
||||||
</Space>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
OperationDisplay.propTypes = {
|
|
||||||
operation: PropTypes.bool.isRequired,
|
|
||||||
showIcon: PropTypes.bool,
|
|
||||||
showText: PropTypes.bool,
|
|
||||||
showColor: PropTypes.bool
|
|
||||||
}
|
|
||||||
|
|
||||||
export default OperationDisplay
|
|
||||||
@ -8,7 +8,7 @@ import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
|||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
const PropertyChanges = ({ type, value }) => {
|
const PropertyChanges = ({ type, value }) => {
|
||||||
if (!value || (!value.new && !value.old)) {
|
if (!value || !value.new) {
|
||||||
return <Text type='secondary'>n/a</Text>
|
return <Text type='secondary'>n/a</Text>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,15 +18,7 @@ const PropertyChanges = ({ type, value }) => {
|
|||||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||||
const val = obj[key]
|
const val = obj[key]
|
||||||
const newKey = prefix ? `${prefix}.${key}` : key
|
const newKey = prefix ? `${prefix}.${key}` : key
|
||||||
|
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
||||||
// Don't flatten keys that are "state" or "netGross"
|
|
||||||
if (
|
|
||||||
val &&
|
|
||||||
typeof val === 'object' &&
|
|
||||||
!Array.isArray(val) &&
|
|
||||||
key !== 'state' &&
|
|
||||||
key !== 'netGross'
|
|
||||||
) {
|
|
||||||
flattenObject(val, newKey, res)
|
flattenObject(val, newKey, res)
|
||||||
} else {
|
} else {
|
||||||
res[newKey] = val
|
res[newKey] = val
|
||||||
@ -42,6 +34,7 @@ const PropertyChanges = ({ type, value }) => {
|
|||||||
...flatOld,
|
...flatOld,
|
||||||
...flatNew
|
...flatNew
|
||||||
}
|
}
|
||||||
|
console.log('combined', combinedChanges)
|
||||||
return (
|
return (
|
||||||
<Descriptions size='small' column={1}>
|
<Descriptions size='small' column={1}>
|
||||||
{Object.keys(combinedChanges).map((key) => {
|
{Object.keys(combinedChanges).map((key) => {
|
||||||
@ -53,25 +46,19 @@ const PropertyChanges = ({ type, value }) => {
|
|||||||
return (
|
return (
|
||||||
<Descriptions.Item key={key} label={changeProperty.label}>
|
<Descriptions.Item key={key} label={changeProperty.label}>
|
||||||
<Space>
|
<Space>
|
||||||
{value?.old ? (
|
<ObjectProperty
|
||||||
<ObjectProperty
|
{...changeProperty}
|
||||||
{...changeProperty}
|
longId={false}
|
||||||
longId={false}
|
objectData={value?.old}
|
||||||
objectData={value?.old}
|
/>
|
||||||
/>
|
<Text type='secondary'>
|
||||||
) : null}
|
<ArrowRightIcon />
|
||||||
{value?.old && value?.new ? (
|
</Text>
|
||||||
<Text type='secondary'>
|
<ObjectProperty
|
||||||
<ArrowRightIcon />
|
{...changeProperty}
|
||||||
</Text>
|
longId={false}
|
||||||
) : null}
|
objectData={value?.new}
|
||||||
{value?.new ? (
|
/>
|
||||||
<ObjectProperty
|
|
||||||
{...changeProperty}
|
|
||||||
longId={false}
|
|
||||||
objectData={value?.new}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Space>
|
</Space>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Typography } from 'antd'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
const { Text } = Typography
|
|
||||||
|
|
||||||
const VarianceDisplay = ({ value, prefix, suffix }) => {
|
|
||||||
if (value === null || value === undefined) {
|
|
||||||
return <Text type='secondary'>n/a</Text>
|
|
||||||
}
|
|
||||||
|
|
||||||
const isPositive = value > 0
|
|
||||||
const isNegative = value < 0
|
|
||||||
const displayValue = Math.abs(value).toFixed(2)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Text type={isPositive ? 'success' : isNegative ? 'danger' : undefined}>
|
|
||||||
{prefix || ''}
|
|
||||||
{isPositive ? '+' : isNegative ? '-' : ''}
|
|
||||||
{displayValue}
|
|
||||||
{suffix || ''}
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
VarianceDisplay.propTypes = {
|
|
||||||
value: PropTypes.number,
|
|
||||||
prefix: PropTypes.string,
|
|
||||||
suffix: PropTypes.string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default VarianceDisplay
|
|
||||||
@ -4,8 +4,7 @@ import React, {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
useContext,
|
useContext,
|
||||||
useRef,
|
useRef
|
||||||
useCallback
|
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import io from 'socket.io-client'
|
import io from 'socket.io-client'
|
||||||
import { message, notification, Modal, Space, Button } from 'antd'
|
import { message, notification, Modal, Space, Button } from 'antd'
|
||||||
@ -23,9 +22,8 @@ logger.setLevel(config.logLevel)
|
|||||||
const ApiServerContext = createContext()
|
const ApiServerContext = createContext()
|
||||||
|
|
||||||
const ApiServerProvider = ({ children }) => {
|
const ApiServerProvider = ({ children }) => {
|
||||||
const { token, userProfile, authenticated } = useContext(AuthContext)
|
const { token, userProfile } = useContext(AuthContext)
|
||||||
const socketRef = useRef(null)
|
const socketRef = useRef(null)
|
||||||
const [connected, setConnected] = useState(false)
|
|
||||||
const [connecting, setConnecting] = useState(false)
|
const [connecting, setConnecting] = useState(false)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [messageApi, contextHolder] = message.useMessage()
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
@ -34,43 +32,9 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
const [showErrorModal, setShowErrorModal] = useState(false)
|
const [showErrorModal, setShowErrorModal] = useState(false)
|
||||||
const [errorModalContent, setErrorModalContent] = useState('')
|
const [errorModalContent, setErrorModalContent] = useState('')
|
||||||
const [retryCallback, setRetryCallback] = useState(null)
|
const [retryCallback, setRetryCallback] = useState(null)
|
||||||
const subscribedCallbacksRef = useRef(new Map())
|
|
||||||
const subscribedLockCallbacksRef = useRef(new Map())
|
|
||||||
|
|
||||||
const notifyLockUpdate = useCallback(
|
useEffect(() => {
|
||||||
async (lockData) => {
|
if (token) {
|
||||||
logger.debug('Notifying lock update:', lockData)
|
|
||||||
const objectId = lockData._id || lockData.id
|
|
||||||
|
|
||||||
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]
|
|
||||||
)
|
|
||||||
|
|
||||||
const connectToServer = useCallback(() => {
|
|
||||||
if (token && authenticated == true) {
|
|
||||||
logger.debug('Token is available, connecting to api server...')
|
logger.debug('Token is available, connecting to api server...')
|
||||||
|
|
||||||
const newSocket = io(config.apiServerUrl, {
|
const newSocket = io(config.apiServerUrl, {
|
||||||
@ -84,25 +48,18 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
newSocket.on('connect', () => {
|
newSocket.on('connect', () => {
|
||||||
logger.debug('Api Server connected')
|
logger.debug('Api Server connected')
|
||||||
setConnecting(false)
|
setConnecting(false)
|
||||||
setConnected(true)
|
|
||||||
setError(null)
|
setError(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
newSocket.on('notify_object_update', notifyObjectUpdate)
|
|
||||||
newSocket.on('notify_object_new', notifyObjectNew)
|
|
||||||
newSocket.on('notify_lock_update', notifyLockUpdate)
|
|
||||||
|
|
||||||
newSocket.on('disconnect', () => {
|
newSocket.on('disconnect', () => {
|
||||||
logger.debug('Api Server disconnected')
|
logger.debug('Api Server disconnected')
|
||||||
setError('Api Server disconnected')
|
setError('Api Server disconnected')
|
||||||
setConnected(false)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
newSocket.on('connect_error', (err) => {
|
newSocket.on('connect_error', (err) => {
|
||||||
logger.error('Api Server connection error:', err)
|
logger.error('Api Server connection error:', err)
|
||||||
messageApi.error('Api Server connection error: ' + err.message)
|
messageApi.error('Api Server connection error: ' + err.message)
|
||||||
setError('Api Server connection error')
|
setError('Api Server connection error')
|
||||||
setConnected(false)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
newSocket.on('bridge.notification', (data) => {
|
newSocket.on('bridge.notification', (data) => {
|
||||||
@ -118,27 +75,21 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
socketRef.current = newSocket
|
socketRef.current = newSocket
|
||||||
}
|
|
||||||
}, [token, authenticated, messageApi, notificationApi, notifyLockUpdate])
|
|
||||||
|
|
||||||
useEffect(() => {
|
// Clean up function
|
||||||
if (token && authenticated == true) {
|
return () => {
|
||||||
connectToServer()
|
if (socketRef.current) {
|
||||||
|
logger.debug('Cleaning up api server connection...')
|
||||||
|
socketRef.current.disconnect()
|
||||||
|
socketRef.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (!token && socketRef.current) {
|
} else if (!token && socketRef.current) {
|
||||||
logger.debug('Token not available, disconnecting api server...')
|
logger.debug('Token not available, disconnecting api server...')
|
||||||
socketRef.current.disconnect()
|
socketRef.current.disconnect()
|
||||||
socketRef.current = null
|
socketRef.current = null
|
||||||
}
|
}
|
||||||
|
}, [token, messageApi])
|
||||||
// Clean up function
|
|
||||||
return () => {
|
|
||||||
if (socketRef.current) {
|
|
||||||
logger.debug('Cleaning up api server connection...')
|
|
||||||
socketRef.current.disconnect()
|
|
||||||
socketRef.current = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [token, authenticated, connectToServer])
|
|
||||||
|
|
||||||
const lockObject = (id, type) => {
|
const lockObject = (id, type) => {
|
||||||
logger.debug('Locking ' + id)
|
logger.debug('Locking ' + id)
|
||||||
@ -167,12 +118,8 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
type: type
|
type: type
|
||||||
},
|
},
|
||||||
(lockEvent) => {
|
(lockEvent) => {
|
||||||
logger.debug('Received lock status for object:', id, lockEvent)
|
logger.debug('Received lock event for object:', id, lockEvent)
|
||||||
if (lockEvent.user != userProfile?._id) {
|
resolve(lockEvent)
|
||||||
resolve(lockEvent)
|
|
||||||
} else {
|
|
||||||
resolve(null)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
logger.debug('Sent fetch lock command for object:', id)
|
logger.debug('Sent fetch lock command for object:', id)
|
||||||
@ -180,186 +127,65 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyObjectUpdate = async (object) => {
|
const onLockEvent = (id, callback) => {
|
||||||
logger.debug('Notifying object update:', object)
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
const objectId = object._id || object.id
|
const eventHandler = (data) => {
|
||||||
|
if (data._id === id && data?.user !== userProfile._id) {
|
||||||
if (objectId && subscribedCallbacksRef.current.has(objectId)) {
|
logger.debug(
|
||||||
const callbacks = subscribedCallbacksRef.current.get(objectId)
|
'Lock update received for object:',
|
||||||
logger.debug(
|
id,
|
||||||
`Calling ${callbacks.length} callbacks for object:`,
|
'locked:',
|
||||||
objectId
|
data.locked
|
||||||
)
|
)
|
||||||
callbacks.forEach((callback) => {
|
callback(data)
|
||||||
try {
|
|
||||||
callback(object)
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error in object update callback:', error)
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
} else {
|
|
||||||
logger.debug(
|
socketRef.current.on('notify_lock_update', eventHandler)
|
||||||
`No callbacks found for object: ${objectId}, subscribed callbacks:`,
|
logger.debug('Registered lock event listener for object:', id)
|
||||||
Array.from(subscribedCallbacksRef.current.keys())
|
|
||||||
)
|
// Return cleanup function
|
||||||
|
return () => offLockEvent(id, eventHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyObjectNew = async (object) => {
|
const offLockEvent = (id, eventHandler) => {
|
||||||
logger.debug('Notifying object new:', object)
|
|
||||||
const objectType = object.type || 'unknown'
|
|
||||||
|
|
||||||
if (objectType && subscribedCallbacksRef.current.has(objectType)) {
|
|
||||||
const callbacks = subscribedCallbacksRef.current.get(objectType)
|
|
||||||
logger.debug(
|
|
||||||
`Calling ${callbacks.length} callbacks for type:`,
|
|
||||||
objectType
|
|
||||||
)
|
|
||||||
callbacks.forEach((callback) => {
|
|
||||||
try {
|
|
||||||
callback(object)
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error in object new callback:', error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
logger.debug(
|
|
||||||
`No callbacks found for object: ${objectType}, subscribed callbacks:`,
|
|
||||||
Array.from(subscribedCallbacksRef.current.keys())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const offUpdateEvent = useCallback((id, type, callback) => {
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
// Remove callback from the subscribed callbacks map
|
socketRef.current.off('notify_lock_update', eventHandler)
|
||||||
if (subscribedCallbacksRef.current.has(id)) {
|
|
||||||
const callbacks = subscribedCallbacksRef.current
|
|
||||||
.get(id)
|
|
||||||
.filter((cb) => cb !== callback)
|
|
||||||
if (callbacks.length === 0) {
|
|
||||||
subscribedCallbacksRef.current.delete(id)
|
|
||||||
socketRef.current.emit('unsubscribe', { id: id, type: type })
|
|
||||||
} else {
|
|
||||||
subscribedCallbacksRef.current.set(id, callbacks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug('Removed update event listener for object:', id)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const offTypeEvent = useCallback((type, callback) => {
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
|
||||||
// Remove callback from the subscribed callbacks map
|
|
||||||
if (subscribedCallbacksRef.current.has(type)) {
|
|
||||||
const callbacks = subscribedCallbacksRef.current
|
|
||||||
.get(type)
|
|
||||||
.filter((cb) => cb !== callback)
|
|
||||||
if (callbacks.length === 0) {
|
|
||||||
subscribedCallbacksRef.current.delete(type)
|
|
||||||
socketRef.current.emit('unsubscribe', { type: type })
|
|
||||||
} else {
|
|
||||||
subscribedCallbacksRef.current.set(type, callbacks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug('Removed new event listener for type:', type)
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const subscribeToObject = useCallback(
|
|
||||||
(id, type, callback) => {
|
|
||||||
logger.debug('Subscribing to object:', id, 'type:', type)
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
|
||||||
// Add callback to the subscribed callbacks map immediately
|
|
||||||
if (!subscribedCallbacksRef.current.has(id)) {
|
|
||||||
subscribedCallbacksRef.current.set(id, [])
|
|
||||||
}
|
|
||||||
subscribedCallbacksRef.current.get(id).push(callback)
|
|
||||||
logger.debug(
|
|
||||||
`Added callback for object ${id}, total callbacks: ${subscribedCallbacksRef.current.get(id).length}`
|
|
||||||
)
|
|
||||||
|
|
||||||
socketRef.current.emit('subscribe', { id: id, type: type })
|
|
||||||
logger.debug('Registered update event listener for object:', id)
|
|
||||||
|
|
||||||
// Return cleanup function
|
|
||||||
return () => offUpdateEvent(id, type, callback)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[offUpdateEvent]
|
|
||||||
)
|
|
||||||
|
|
||||||
const subscribeToType = useCallback(
|
|
||||||
(type, callback) => {
|
|
||||||
logger.debug('Subscribing to type:', type)
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
|
||||||
// Add callback to the subscribed callbacks map immediately
|
|
||||||
if (!subscribedCallbacksRef.current.has(type)) {
|
|
||||||
subscribedCallbacksRef.current.set(type, [])
|
|
||||||
}
|
|
||||||
subscribedCallbacksRef.current.get(type).push(callback)
|
|
||||||
logger.debug(
|
|
||||||
`Added callback for type ${type}, total callbacks: ${subscribedCallbacksRef.current.get(type).length}`
|
|
||||||
)
|
|
||||||
|
|
||||||
socketRef.current.emit('subscribe', { type: type })
|
|
||||||
logger.debug('Registered update event listener for object:', type)
|
|
||||||
|
|
||||||
// Return cleanup function
|
|
||||||
return () => offTypeEvent(type, callback)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[offTypeEvent]
|
|
||||||
)
|
|
||||||
|
|
||||||
const offLockEvent = useCallback((id, 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)
|
|
||||||
.filter((cb) => cb !== callback)
|
|
||||||
if (callbacks.length === 0) {
|
|
||||||
subscribedLockCallbacksRef.current.delete(id)
|
|
||||||
} else {
|
|
||||||
subscribedLockCallbacksRef.current.set(id, callbacks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug('Removed lock event listener for object:', id)
|
logger.debug('Removed lock event listener for object:', id)
|
||||||
}
|
}
|
||||||
}, [])
|
}
|
||||||
|
|
||||||
const subscribeToLock = useCallback(
|
const onUpdateEvent = (id, callback) => {
|
||||||
(id, type, callback) => {
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
logger.debug('Subscribing to lock for object:', id, 'type:', type)
|
const eventHandler = (data) => {
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
if (data._id === id && data?.user !== userProfile._id) {
|
||||||
// Add callback to the subscribed lock callbacks map immediately
|
logger.debug(
|
||||||
if (!subscribedLockCallbacksRef.current.has(id)) {
|
'Update event received for object:',
|
||||||
subscribedLockCallbacksRef.current.set(id, [])
|
id,
|
||||||
|
'updatedAt:',
|
||||||
|
data.updatedAt
|
||||||
|
)
|
||||||
|
callback(data)
|
||||||
}
|
}
|
||||||
subscribedLockCallbacksRef.current.get(id).push(callback)
|
|
||||||
logger.debug(
|
|
||||||
`Added lock callback for object ${id}, total lock callbacks: ${subscribedLockCallbacksRef.current.get(id).length}`
|
|
||||||
)
|
|
||||||
|
|
||||||
socketRef.current.emit('subscribe_lock', { id: id, type: type })
|
|
||||||
logger.debug('Registered lock event listener for object:', id)
|
|
||||||
|
|
||||||
// Return cleanup function
|
|
||||||
return () => offLockEvent(id, callback)
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
[offLockEvent]
|
|
||||||
)
|
|
||||||
|
|
||||||
const showError = (error, callback = null) => {
|
socketRef.current.on('notify_object_update', eventHandler)
|
||||||
var content = `Error ${error.code} (${error.status}): ${error.message}`
|
logger.debug('Registered update event listener for object:', id)
|
||||||
if (error.response?.data?.error) {
|
|
||||||
content = `${error.response?.data?.error} (${error.status})`
|
// Return cleanup function
|
||||||
|
return () => offUpdateEvent(id, eventHandler)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const offUpdateEvent = (id, eventHandler) => {
|
||||||
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
socketRef.current.off('notify_update', eventHandler)
|
||||||
|
logger.debug('Removed update event listener for object:', id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const showError = (content, callback = null) => {
|
||||||
setErrorModalContent(content)
|
setErrorModalContent(content)
|
||||||
setRetryCallback(() => callback)
|
setRetryCallback(() => callback)
|
||||||
setShowErrorModal(true)
|
setShowErrorModal(true)
|
||||||
@ -374,8 +200,8 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
setRetryCallback(null)
|
setRetryCallback(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generalized fetchObject function
|
// Generalized fetchObjectInfo function
|
||||||
const fetchObject = async (id, type) => {
|
const fetchObjectInfo = async (id, type) => {
|
||||||
const fetchUrl = `${config.backendUrl}/${type}s/${id}`
|
const fetchUrl = `${config.backendUrl}/${type}s/${id}`
|
||||||
setFetchLoading(true)
|
setFetchLoading(true)
|
||||||
logger.debug('Fetching from ' + fetchUrl)
|
logger.debug('Fetching from ' + fetchUrl)
|
||||||
@ -388,17 +214,62 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
})
|
})
|
||||||
return response.data
|
return response.data
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showError(err, () => {
|
logger.error('Failed to fetch object information:', err)
|
||||||
fetchObject(id, type)
|
// Don't automatically show error - let the component handle it
|
||||||
})
|
throw err
|
||||||
return {}
|
|
||||||
} finally {
|
} finally {
|
||||||
setFetchLoading(false)
|
setFetchLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update filament information
|
||||||
|
const updateObjectInfo = async (id, type, value) => {
|
||||||
|
const updateUrl = `${config.backendUrl}/${type.toLowerCase()}s/${id}`
|
||||||
|
logger.debug('Updating info for ' + id)
|
||||||
|
try {
|
||||||
|
const response = await axios.put(updateUrl, value, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true
|
||||||
|
})
|
||||||
|
logger.debug('Filament updated successfully')
|
||||||
|
if (socketRef.current && socketRef.current.connected == true) {
|
||||||
|
await socketRef.current.emit('update', {
|
||||||
|
_id: id,
|
||||||
|
type: type,
|
||||||
|
updatedAt: response.data.updatedAt
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return response.data
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Failed to update filament information:', err)
|
||||||
|
// Don't automatically show error - let the component handle it
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update filament information
|
||||||
|
const createObject = async (type, value) => {
|
||||||
|
const createUrl = `${config.backendUrl}/${type.toLowerCase()}s`
|
||||||
|
logger.debug('Creating object...')
|
||||||
|
try {
|
||||||
|
const response = await axios.post(createUrl, value, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
withCredentials: true
|
||||||
|
})
|
||||||
|
return response.data
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Failed to update filament information:', err)
|
||||||
|
// Don't automatically show error - let the component handle it
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch table data with pagination, filtering, and sorting
|
// Fetch table data with pagination, filtering, and sorting
|
||||||
const fetchObjects = async (type, params = {}) => {
|
const fetchTableData = async (type, params = {}) => {
|
||||||
const {
|
const {
|
||||||
page = 1,
|
page = 1,
|
||||||
limit = 25,
|
limit = 25,
|
||||||
@ -448,122 +319,9 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
hasMore,
|
hasMore,
|
||||||
page
|
page
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
showError(err, () => {
|
logger.error('Failed to fetch table data:', error)
|
||||||
fetchObjects(type, params)
|
throw error
|
||||||
})
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch table data with pagination, filtering, and sorting
|
|
||||||
const fetchObjectsByProperty = async (type, params = {}) => {
|
|
||||||
const { filter = {}, properties = [] } = params
|
|
||||||
|
|
||||||
logger.debug('Fetching property object data from:', type, {
|
|
||||||
properties,
|
|
||||||
filter
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await axios.get(
|
|
||||||
`${config.backendUrl}/${type.toLowerCase()}s/properties`,
|
|
||||||
{
|
|
||||||
params: {
|
|
||||||
...filter,
|
|
||||||
properties: properties.join(',') // Convert array to comma-separated string
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json'
|
|
||||||
},
|
|
||||||
withCredentials: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const newData = response.data
|
|
||||||
|
|
||||||
return newData
|
|
||||||
} catch (err) {
|
|
||||||
showError(err, () => {
|
|
||||||
fetchObjectsByProperty(type, params)
|
|
||||||
})
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update filament information
|
|
||||||
const updateObject = async (id, type, value) => {
|
|
||||||
const updateUrl = `${config.backendUrl}/${type.toLowerCase()}s/${id}`
|
|
||||||
logger.debug('Updating info for ' + id)
|
|
||||||
try {
|
|
||||||
const response = await axios.put(updateUrl, value, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
withCredentials: true
|
|
||||||
})
|
|
||||||
logger.debug('Object updated successfully')
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
|
||||||
await socketRef.current.emit('update', {
|
|
||||||
_id: id,
|
|
||||||
type: type,
|
|
||||||
updatedAt: response.data.updatedAt
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return response.data
|
|
||||||
} catch (err) {
|
|
||||||
setError(err, () => {
|
|
||||||
updateObject(id, type, value)
|
|
||||||
})
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update filament information
|
|
||||||
const deleteObject = async (id, type) => {
|
|
||||||
const deleteUrl = `${config.backendUrl}/${type.toLowerCase()}s/${id}`
|
|
||||||
logger.debug('Deleting object ID: ' + id)
|
|
||||||
try {
|
|
||||||
const response = await axios.delete(deleteUrl, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
withCredentials: true
|
|
||||||
})
|
|
||||||
logger.debug('Object deleted successfully')
|
|
||||||
if (socketRef.current && socketRef.current.connected == true) {
|
|
||||||
await socketRef.current.emit('update', {
|
|
||||||
_id: id,
|
|
||||||
type: type,
|
|
||||||
updatedAt: response.data.updatedAt
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return response.data
|
|
||||||
} catch (err) {
|
|
||||||
showError(err, () => {
|
|
||||||
deleteObject(id, type)
|
|
||||||
})
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update filament information
|
|
||||||
const createObject = async (type, value) => {
|
|
||||||
const createUrl = `${config.backendUrl}/${type.toLowerCase()}s`
|
|
||||||
logger.debug('Creating object...')
|
|
||||||
try {
|
|
||||||
const response = await axios.post(createUrl, value, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
withCredentials: true
|
|
||||||
})
|
|
||||||
return response.data
|
|
||||||
} catch (err) {
|
|
||||||
showError(err, () => {
|
|
||||||
createObject(type, value)
|
|
||||||
})
|
|
||||||
return {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,35 +348,27 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
document.body.appendChild(fileLink)
|
document.body.appendChild(fileLink)
|
||||||
fileLink.click()
|
fileLink.click()
|
||||||
fileLink.parentNode.removeChild(fileLink)
|
fileLink.parentNode.removeChild(fileLink)
|
||||||
} catch (err) {
|
|
||||||
showError(err, () => {
|
|
||||||
fetchObjectContent(id, type, fileName)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch notes for a specific parent
|
|
||||||
const fetchNotes = async (parentId) => {
|
|
||||||
logger.debug('Fetching notes for parent:', parentId)
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${config.backendUrl}/notes`, {
|
|
||||||
params: {
|
|
||||||
parent: parentId,
|
|
||||||
sort: 'createdAt',
|
|
||||||
order: 'ascend'
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json'
|
|
||||||
},
|
|
||||||
withCredentials: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const notesData = response.data
|
|
||||||
logger.debug('Fetched notes:', notesData.length)
|
|
||||||
return notesData
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to fetch notes:', error)
|
logger.error('Failed to download GCode file content:', error)
|
||||||
throw error
|
|
||||||
|
if (error.response) {
|
||||||
|
if (error.response.status === 404) {
|
||||||
|
showError(
|
||||||
|
`The ${type} file "${fileName}" was not found on the server. It may have been deleted or moved.`,
|
||||||
|
() => fetchObjectContent(id, type, fileName)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
showError(
|
||||||
|
`Error downloading ${type} file: ${error.response.status} - ${error.response.statusText}`,
|
||||||
|
() => fetchObjectContent(id, type, fileName)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showError(
|
||||||
|
'An unexpected error occurred while downloading. Please check your connection and try again.',
|
||||||
|
() => fetchObjectContent(id, type, fileName)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,24 +378,19 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
apiServer: socketRef.current,
|
apiServer: socketRef.current,
|
||||||
error,
|
error,
|
||||||
connecting,
|
connecting,
|
||||||
connected,
|
|
||||||
lockObject,
|
lockObject,
|
||||||
unlockObject,
|
unlockObject,
|
||||||
fetchObjectLock,
|
fetchObjectLock,
|
||||||
updateObject,
|
updateObjectInfo,
|
||||||
createObject,
|
createObject,
|
||||||
deleteObject,
|
onLockEvent,
|
||||||
subscribeToObject,
|
onUpdateEvent,
|
||||||
subscribeToType,
|
|
||||||
subscribeToLock,
|
|
||||||
offUpdateEvent,
|
offUpdateEvent,
|
||||||
fetchObject,
|
fetchObjectInfo,
|
||||||
fetchObjects,
|
fetchTableData,
|
||||||
fetchObjectsByProperty,
|
|
||||||
fetchLoading,
|
fetchLoading,
|
||||||
showError,
|
showError,
|
||||||
fetchObjectContent,
|
fetchObjectContent
|
||||||
fetchNotes
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
|
|||||||
@ -53,7 +53,8 @@ const AuthProvider = ({ children }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.status === 200 && response.data) {
|
if (response.status === 200 && response.data) {
|
||||||
logger.debug('Got auth token!')
|
logger.debug('User is authenticated!')
|
||||||
|
setAuthenticated(true)
|
||||||
setToken(response.data.access_token)
|
setToken(response.data.access_token)
|
||||||
setExpiresAt(response.data.expires_at)
|
setExpiresAt(response.data.expires_at)
|
||||||
setUserProfile(response.data)
|
setUserProfile(response.data)
|
||||||
@ -88,6 +89,75 @@ const AuthProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const showTokenExpirationMessage = useCallback(
|
||||||
|
(expiresAt) => {
|
||||||
|
const now = new Date()
|
||||||
|
const expirationDate = new Date(expiresAt)
|
||||||
|
const timeRemaining = expirationDate - now
|
||||||
|
|
||||||
|
if (timeRemaining <= 0) {
|
||||||
|
if (authenticated) {
|
||||||
|
setShowSessionExpiredModal(true)
|
||||||
|
setAuthenticated(false)
|
||||||
|
notificationApi.destroy('token-expiration')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const minutes = Math.floor(timeRemaining / 60000)
|
||||||
|
const seconds = Math.floor((timeRemaining % 60000) / 1000)
|
||||||
|
|
||||||
|
// Only show notification in the final minute
|
||||||
|
if (minutes === 0) {
|
||||||
|
const totalSeconds = 60
|
||||||
|
const remainingSeconds = totalSeconds - seconds
|
||||||
|
const progress = (remainingSeconds / totalSeconds) * 100
|
||||||
|
|
||||||
|
notificationApi.info({
|
||||||
|
message: 'Session Expiring Soon',
|
||||||
|
description: (
|
||||||
|
<div>
|
||||||
|
<div style={{ marginBottom: 8 }}>
|
||||||
|
Your session will expire in {seconds} seconds
|
||||||
|
</div>
|
||||||
|
<Progress
|
||||||
|
percent={progress}
|
||||||
|
size='small'
|
||||||
|
status='active'
|
||||||
|
showInfo={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
duration: 0,
|
||||||
|
key: 'token-expiration',
|
||||||
|
icon: null,
|
||||||
|
placement: 'bottomRight',
|
||||||
|
style: {
|
||||||
|
width: 360
|
||||||
|
},
|
||||||
|
className: 'token-expiration-notification',
|
||||||
|
closeIcon: null,
|
||||||
|
onClose: () => {},
|
||||||
|
btn: (
|
||||||
|
<Button
|
||||||
|
type='primary'
|
||||||
|
size='small'
|
||||||
|
onClick={() => {
|
||||||
|
notificationApi.destroy('token-expiration')
|
||||||
|
refreshToken()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reload Session
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else if (minutes === 1) {
|
||||||
|
// Clear any existing notification when we enter the final minute
|
||||||
|
notificationApi.destroy('token-expiration')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[authenticated, notificationApi]
|
||||||
|
)
|
||||||
|
|
||||||
const handleSessionExpiredModalOk = () => {
|
const handleSessionExpiredModalOk = () => {
|
||||||
setShowSessionExpiredModal(false)
|
setShowSessionExpiredModal(false)
|
||||||
loginWithSSO()
|
loginWithSSO()
|
||||||
@ -97,89 +167,22 @@ const AuthProvider = ({ children }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let intervalId
|
let intervalId
|
||||||
|
|
||||||
const tokenRefresh = () => {
|
const tokenRefreshInterval = () => {
|
||||||
if (expiresAt) {
|
if (expiresAt) {
|
||||||
const now = new Date()
|
showTokenExpirationMessage(expiresAt)
|
||||||
const expirationDate = new Date(expiresAt)
|
|
||||||
const timeRemaining = expirationDate - now
|
|
||||||
|
|
||||||
if (timeRemaining <= 0) {
|
|
||||||
if (authenticated == true) {
|
|
||||||
setAuthenticated(false)
|
|
||||||
}
|
|
||||||
setShowSessionExpiredModal(true)
|
|
||||||
notificationApi.destroy('token-expiration')
|
|
||||||
} else {
|
|
||||||
if (authenticated == false) {
|
|
||||||
setAuthenticated(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const minutes = Math.floor(timeRemaining / 60000)
|
|
||||||
const seconds = Math.floor((timeRemaining % 60000) / 1000)
|
|
||||||
|
|
||||||
// Only show notification in the final minute
|
|
||||||
if (minutes === 0) {
|
|
||||||
const totalSeconds = 60
|
|
||||||
const remainingSeconds = totalSeconds - seconds
|
|
||||||
const progress = (remainingSeconds / totalSeconds) * 100
|
|
||||||
|
|
||||||
notificationApi.info({
|
|
||||||
message: 'Session Expiring Soon',
|
|
||||||
description: (
|
|
||||||
<div>
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
Your session will expire in {seconds} seconds
|
|
||||||
</div>
|
|
||||||
<Progress
|
|
||||||
percent={progress}
|
|
||||||
size='small'
|
|
||||||
status='active'
|
|
||||||
showInfo={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
duration: 0,
|
|
||||||
key: 'token-expiration',
|
|
||||||
icon: null,
|
|
||||||
placement: 'bottomRight',
|
|
||||||
style: {
|
|
||||||
width: 360
|
|
||||||
},
|
|
||||||
className: 'token-expiration-notification',
|
|
||||||
closeIcon: null,
|
|
||||||
onClose: () => {},
|
|
||||||
btn: (
|
|
||||||
<Button
|
|
||||||
type='primary'
|
|
||||||
size='small'
|
|
||||||
onClick={() => {
|
|
||||||
notificationApi.destroy('token-expiration')
|
|
||||||
refreshToken()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Reload Session
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
} else if (minutes === 1) {
|
|
||||||
// Clear any existing notification when we enter the final minute
|
|
||||||
notificationApi.destroy('token-expiration')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
intervalId = setInterval(tokenRefresh, 1000)
|
if (authenticated) {
|
||||||
|
intervalId = setInterval(tokenRefreshInterval, 1000)
|
||||||
console.log('fresh', authenticated)
|
}
|
||||||
tokenRefresh()
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (intervalId) {
|
if (intervalId) {
|
||||||
clearInterval(intervalId)
|
clearInterval(intervalId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [expiresAt, authenticated, notificationApi, refreshToken])
|
}, [expiresAt, authenticated, showTokenExpirationMessage])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkAuthStatus()
|
checkAuthStatus()
|
||||||
|
|||||||
@ -461,9 +461,16 @@ const SpotlightProvider = ({ children }) => {
|
|||||||
align='center'
|
align='center'
|
||||||
justify='space-between'
|
justify='space-between'
|
||||||
>
|
>
|
||||||
<Flex gap={'small'} align='center'>
|
<Flex
|
||||||
{Icon ? <Icon style={{ fontSize: '20px' }} /> : null}
|
gap={'small'}
|
||||||
|
style={{ marginBottom: '2px' }}
|
||||||
|
align='center'
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
{Icon ? (
|
||||||
|
<Icon style={{ fontSize: '20px' }} />
|
||||||
|
) : null}
|
||||||
|
</Text>
|
||||||
{item.name ? (
|
{item.name ? (
|
||||||
<Text ellipsis style={{ maxWidth: 170 }}>
|
<Text ellipsis style={{ maxWidth: 170 }}>
|
||||||
{item.name}
|
{item.name}
|
||||||
|
|||||||
@ -6,16 +6,8 @@ export const AuditLog = {
|
|||||||
prefix: 'ADL',
|
prefix: 'ADL',
|
||||||
icon: AuditLogIcon,
|
icon: AuditLogIcon,
|
||||||
actions: [],
|
actions: [],
|
||||||
columns: [
|
columns: ['_id', 'owner', 'owner._id', 'parent._id', 'changes', 'createdAt'],
|
||||||
'_id',
|
filters: ['_id', 'owner._id', 'parent._id'],
|
||||||
'owner',
|
|
||||||
'owner._id',
|
|
||||||
'parent._id',
|
|
||||||
'operation',
|
|
||||||
'changes',
|
|
||||||
'createdAt'
|
|
||||||
],
|
|
||||||
filters: ['_id', 'owner._id', 'parent._id', 'operation'],
|
|
||||||
sorters: ['createdAt'],
|
sorters: ['createdAt'],
|
||||||
properties: [
|
properties: [
|
||||||
{
|
{
|
||||||
@ -83,12 +75,6 @@ export const AuditLog = {
|
|||||||
showHyperlink: true,
|
showHyperlink: true,
|
||||||
showCopy: true
|
showCopy: true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'operation',
|
|
||||||
label: 'Operation',
|
|
||||||
columnWidth: 120,
|
|
||||||
type: 'operation'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'changes',
|
name: 'changes',
|
||||||
label: 'Changes',
|
label: 'Changes',
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import JobIcon from '../../components/Icons/JobIcon'
|
import JobIcon from '../../components/Icons/JobIcon'
|
||||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
|
|
||||||
export const Job = {
|
export const Job = {
|
||||||
name: 'job',
|
name: 'job',
|
||||||
@ -21,6 +22,13 @@ export const Job = {
|
|||||||
label: 'Reload',
|
label: 'Reload',
|
||||||
icon: ReloadIcon,
|
icon: ReloadIcon,
|
||||||
url: (_id) => `/dashboard/production/jobs/info?jobId=${_id}&action=reload`
|
url: (_id) => `/dashboard/production/jobs/info?jobId=${_id}&action=reload`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
label: 'Edit',
|
||||||
|
row: true,
|
||||||
|
icon: EditIcon,
|
||||||
|
url: (_id) => `/dashboard/production/jobs/info?jobId=${_id}&action=edit`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
columns: [
|
columns: [
|
||||||
@ -60,7 +68,7 @@ export const Job = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
columnFixed: 'left',
|
columnFixed: 'left',
|
||||||
objectType: 'gcodeFile',
|
objectType: 'gcodeFile',
|
||||||
required: true
|
readOnly: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'gcodeFile._id',
|
name: 'gcodeFile._id',
|
||||||
@ -90,10 +98,9 @@ export const Job = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'printers',
|
name: 'printers',
|
||||||
label: 'Printers',
|
label: 'Assigned Printers',
|
||||||
type: 'objectList',
|
type: 'number',
|
||||||
objectType: 'printer',
|
readOnly: true
|
||||||
required: true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,58 +16,5 @@ export const Note = {
|
|||||||
url: (_id) => `/dashboard/management/notes/info?noteId=${_id}`
|
url: (_id) => `/dashboard/management/notes/info?noteId=${_id}`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
properties: [
|
url: () => `#`
|
||||||
{
|
|
||||||
name: '_id',
|
|
||||||
label: 'ID',
|
|
||||||
type: 'id',
|
|
||||||
objectType: 'note',
|
|
||||||
showCopy: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'createdAt',
|
|
||||||
label: 'Created At',
|
|
||||||
type: 'dateTime',
|
|
||||||
readOnly: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'noteType',
|
|
||||||
label: 'Note Type',
|
|
||||||
type: 'object',
|
|
||||||
objectType: 'noteType',
|
|
||||||
showHyperlink: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'parent._id',
|
|
||||||
label: 'Parent ID',
|
|
||||||
type: 'id',
|
|
||||||
objectType: (objectData) => {
|
|
||||||
return objectData.parentType
|
|
||||||
},
|
|
||||||
showHyperlink: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'noteType._id',
|
|
||||||
label: 'Note Type ID',
|
|
||||||
type: 'id',
|
|
||||||
objectType: 'noteType'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'user._id',
|
|
||||||
label: 'User ID',
|
|
||||||
type: 'id',
|
|
||||||
objectType: 'user'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'content',
|
|
||||||
label: 'Content',
|
|
||||||
type: 'markdown'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updatedAt',
|
|
||||||
label: 'Updated At',
|
|
||||||
type: 'dateTime',
|
|
||||||
readOnly: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,7 +60,7 @@ export const StockEvent = {
|
|||||||
label: 'Parent',
|
label: 'Parent',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
objectType: (objectData) => {
|
objectType: (objectData) => {
|
||||||
return objectData?.parentType
|
return objectData.parentType
|
||||||
},
|
},
|
||||||
value: null,
|
value: null,
|
||||||
showCopy: true
|
showCopy: true
|
||||||
@ -78,12 +78,9 @@ export const StockEvent = {
|
|||||||
{
|
{
|
||||||
name: 'value',
|
name: 'value',
|
||||||
label: 'Value',
|
label: 'Value',
|
||||||
columnWidth: 120,
|
columnWidth: 100,
|
||||||
type: 'variance',
|
type: 'number',
|
||||||
showCopy: true,
|
showCopy: true
|
||||||
suffix: (objectData) => {
|
|
||||||
return objectData.unit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user