Fixed warnings and info pages.

This commit is contained in:
Tom Butcher 2025-08-23 00:53:47 +01:00
parent bb047651ae
commit 5680b067a8
18 changed files with 1567 additions and 1675 deletions

View File

@ -1,3 +1,4 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import useCollapseState from '../../hooks/useCollapseState'
@ -19,6 +20,8 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder'
const FilamentStockInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const filamentStockId = new URLSearchParams(location.search).get(
'filamentStockId'
)
@ -31,161 +34,166 @@ const FilamentStockInfo = () => {
auditLogs: true
}
)
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current?.fetchObject?.()
return true
},
edit: () => {
editFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
editFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
editFormRef?.current?.handleUpdate?.()
return true
}
}
return (
<EditObjectForm
id={filamentStockId}
type='filamentStock'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
{({
loading,
isEditing,
formValid,
objectData,
editLoading,
lock,
fetchObject
}) => {
const actions = {
reload: () => {
fetchObject()
return true
}
}
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{ height: '100%', minHeight: 0 }}
<>
<Flex
gap='large'
vertical='true'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='filamentStock'
id={filamentStockId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'Filament Stock Information' },
{ key: 'events', label: 'Filament Stock Events' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='Filament Stock Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='filamentStock'
id={filamentStockId}
disabled={loading}
/>
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'Filament Stock Information' },
{ key: 'events', label: 'Filament Stock Events' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
<EditObjectForm
id={filamentStockId}
type='filamentStock'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={true}
loading={editLoading}
type='filamentStock'
objectData={objectData}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='Filament Stock Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='filamentStock'
objectData={objectData}
/>
</InfoCollapse>
<InfoCollapse
title='Filament Stock Events'
icon={<FilamentStockIcon />}
active={collapseState.events}
onToggle={(expanded) =>
updateCollapseState('events', expanded)
}
collapseKey='events'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='stockEvent'
masterFilter={{ 'parent._id': filamentStockId }}
visibleColumns={{ 'parent._id': false }}
/>
)}
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) =>
updateCollapseState('notes', expanded)
}
collapseKey='notes'
>
<Card>
<NotesPanel
_id={filamentStockId}
type='filamentStock'
/>
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': filamentStockId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
)}
</EditObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Filament Stock Events'
icon={<FilamentStockIcon />}
active={collapseState.events}
onToggle={(expanded) => updateCollapseState('events', expanded)}
collapseKey='events'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='stockEvent'
masterFilter={{ 'parent._id': filamentStockId }}
visibleColumns={{ 'parent._id': false }}
/>
)}
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={filamentStockId} type='filamentStock' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': filamentStockId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -14,9 +14,7 @@ const DocumentSizes = () => {
const [messageApi, contextHolder] = message.useMessage()
const [newDocumentSizeOpen, setNewDocumentSizeOpen] = useState(false)
const tableRef = useRef()
const [viewMode, setViewMode] = useViewMode('documentSize')
const [columnVisibility, setColumnVisibility] =
useColumnVisibility('documentSize')
@ -85,7 +83,7 @@ const DocumentSizes = () => {
<NewDocumentSize
onOk={() => {
setNewDocumentSizeOpen(false)
messageApi.success('New note type created successfully.')
messageApi.success('New document size created successfully.')
tableRef.current?.reload()
}}
reset={!newDocumentSizeOpen}

View File

@ -1,3 +1,4 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
@ -24,6 +25,8 @@ log.setLevel(config.logLevel)
const DocumentSizeInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const documentSizeId = new URLSearchParams(location.search).get(
'documentSizeId'
)
@ -35,159 +38,149 @@ const DocumentSizeInfo = () => {
auditLogs: true
}
)
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current?.fetchObject?.()
return true
},
edit: () => {
editFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
editFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
editFormRef?.current?.handleUpdate?.()
return true
}
}
return (
<EditObjectForm
id={documentSizeId}
type='documentSize'
style={{ height: '100%' }}
>
{({
loading,
isEditing,
startEditing,
cancelEditing,
handleUpdate,
formValid,
objectData,
editLoading,
lock,
fetchObject
}) => {
// Define actions for ActionHandler
const actions = {
reload: () => {
fetchObject()
return true
},
edit: () => {
startEditing()
return false
},
cancelEdit: () => {
cancelEditing()
return true
},
finishEdit: () => {
handleUpdate()
return true
}
}
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{
height: 'calc(var(--unit-100vh) - 155px)',
minHeight: 0
}}
<>
<Flex
gap='large'
vertical='true'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='documentSize'
id={documentSizeId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'Document Size Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='Document Size Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='documentSize'
id={documentSizeId}
disabled={loading}
/>
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'DocumentSize Information' },
{ key: 'stocks', label: 'DocumentSize Stocks' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
<EditObjectForm
id={documentSizeId}
type='documentSize'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={lock?.locked || loading}
loading={editLoading}
type='documentSize'
objectData={objectData}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflowY: 'scroll' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='Document Size Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='documentSize'
objectData={objectData}
/>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) =>
updateCollapseState('notes', expanded)
}
collapseKey='notes'
>
<Card>
<NotesPanel _id={documentSizeId} type='documentSize' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': documentSizeId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
)}
</EditObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={documentSizeId} type='documentSize' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': documentSizeId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -1,3 +1,4 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
@ -25,6 +26,8 @@ log.setLevel(config.logLevel)
const FilamentInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const filamentId = new URLSearchParams(location.search).get('filamentId')
const [collapseState, updateCollapseState] = useCollapseState(
'FilamentInfo',
@ -36,104 +39,110 @@ const FilamentInfo = () => {
}
)
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current?.fetchObject?.()
return true
},
edit: () => {
editFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
editFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
editFormRef?.current?.handleUpdate?.()
return true
}
}
return (
<EditObjectForm id={filamentId} type='filament' style={{ height: '100%' }}>
{({
loading,
isEditing,
startEditing,
cancelEditing,
handleUpdate,
formValid,
objectData,
editLoading,
lock,
fetchObject
}) => {
// Define actions for ActionHandler
const actions = {
reload: () => {
fetchObject()
return true
},
edit: () => {
startEditing()
return false
},
cancelEdit: () => {
cancelEditing()
return true
},
finishEdit: () => {
handleUpdate()
return true
}
}
<>
<Flex
gap='large'
vertical='true'
style={{
height: 'calc(var(--unit-100vh) - 155px)',
minHeight: 0
}}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='filament'
id={filamentId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'Filament Information' },
{ key: 'stocks', label: 'Filament Stocks' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{
height: 'calc(var(--unit-100vh) - 155px)',
minHeight: 0
}}
<div style={{ height: '100%', overflowY: 'scroll' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='Filament Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='filament'
id={filamentId}
disabled={loading}
/>
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'Filament Information' },
{ key: 'stocks', label: 'Filament Stocks' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={lock?.locked || loading}
loading={editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflowY: 'scroll' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='Filament Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<EditObjectForm
id={filamentId}
type='filament'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => {
return (
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
@ -141,73 +150,69 @@ const FilamentInfo = () => {
type='filament'
objectData={objectData}
/>
</InfoCollapse>
)
}}
</EditObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Filament Stocks'
icon={<FilamentIcon />}
active={collapseState.stocks}
onToggle={(expanded) =>
updateCollapseState('stocks', expanded)
}
collapseKey='stocks'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='filamentStock'
masterFilter={{ 'filament._id': filamentId }}
visibleColumns={{
filament: false,
'filament._id': false,
startingWeight: false
}}
/>
)}
</InfoCollapse>
<InfoCollapse
title='Filament Stocks'
icon={<FilamentIcon />}
active={collapseState.stocks}
onToggle={(expanded) => updateCollapseState('stocks', expanded)}
collapseKey='stocks'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='filamentStock'
masterFilter={{ 'filament._id': filamentId }}
visibleColumns={{
filament: false,
'filament._id': false,
startingWeight: false
}}
/>
)}
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) =>
updateCollapseState('notes', expanded)
}
collapseKey='notes'
>
<Card>
<NotesPanel _id={filamentId} type='filament' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={filamentId} type='filament' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': filamentId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': filamentId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -1,3 +1,4 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
@ -17,6 +18,8 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
const NoteTypeInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const noteTypeId = new URLSearchParams(location.search).get('noteTypeId')
const [collapseState, updateCollapseState] = useCollapseState(
'NoteTypeInfo',
@ -25,140 +28,137 @@ const NoteTypeInfo = () => {
auditLogs: true
}
)
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current?.fetchObject?.()
return true
},
edit: () => {
editFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
editFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
editFormRef?.current?.handleUpdate?.()
return true
}
}
return (
<EditObjectForm
id={noteTypeId}
type='noteType'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
{({
loading,
isEditing,
startEditing,
cancelEditing,
handleUpdate,
formValid,
objectData,
editLoading,
lock,
fetchObject
}) => {
// Define actions for ActionHandler
const actions = {
reload: () => {
fetchObject()
return true
},
edit: () => {
startEditing()
return false
},
cancelEdit: () => {
cancelEditing()
return true
},
finishEdit: () => {
handleUpdate()
return true
}
}
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{ height: '100%', minHeight: 0 }}
<>
<Flex
gap='large'
vertical='true'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='noteType'
id={noteTypeId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'Note Type Information' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='Note Type Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='noteType'
id={noteTypeId}
disabled={loading}
/>
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'Note Type Information' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
<EditObjectForm
id={noteTypeId}
type='noteType'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={lock?.locked || loading}
loading={editLoading}
type='noteType'
objectData={objectData}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='Note Type Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='noteType'
objectData={objectData}
/>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': noteTypeId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
)}
</EditObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': noteTypeId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -1,4 +1,4 @@
import { useContext } from 'react'
import { useRef, useState, useContext } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import useCollapseState from '../../hooks/useCollapseState'
@ -20,168 +20,165 @@ import { ApiServerContext } from '../../context/ApiServerContext'
const PartInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const partId = new URLSearchParams(location.search).get('partId')
const { fetchObjectContent } = useContext(ApiServerContext)
const [collapseState, updateCollapseState] = useCollapseState('PartInfo', {
info: true,
parts: true,
notes: true,
auditLogs: true
})
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current?.fetchObject?.()
return true
},
edit: () => {
editFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
editFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
editFormRef?.current?.handleUpdate?.()
return true
},
download: () => {
if (partId && editFormRef?.current?.getObjectData) {
const objectData = editFormRef.current.getObjectData()
fetchObjectContent(partId, 'part', `${objectData?.name || 'part'}.stl`)
return true
}
}
}
return (
<EditObjectForm
id={partId}
type='part'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
{({
loading,
isEditing,
startEditing,
cancelEditing,
handleUpdate,
formValid,
objectData,
editLoading,
lock,
fetchObject
}) => {
const actions = {
reload: () => {
fetchObject()
return true
},
edit: () => {
startEditing()
return false
},
cancelEdit: () => {
cancelEditing()
return true
},
finishEdit: () => {
handleUpdate()
return true
},
download: () => {
if (partId) {
fetchObjectContent(partId, 'part', `${objectData.name}.stl`)
return true
}
}
}
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{ height: '100%', minHeight: 0 }}
<>
<Flex
gap='large'
vertical='true'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='part'
id={partId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'Part Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='Part Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='part'
id={partId}
disabled={loading}
/>
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'Part Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
<EditObjectForm
id={partId}
type='part'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={lock?.locked || loading}
loading={editLoading}
type='part'
objectData={objectData}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='Part Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='part'
objectData={objectData}
/>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) =>
updateCollapseState('notes', expanded)
}
collapseKey='notes'
>
<Card>
<NotesPanel _id={partId} type='part' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': partId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
)}
</EditObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={partId} type='part' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': partId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -1,3 +1,4 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import useCollapseState from '../../hooks/useCollapseState'
@ -19,6 +20,8 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
const ProductInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const productId = new URLSearchParams(location.search).get('productId')
const [collapseState, updateCollapseState] = useCollapseState('ProductInfo', {
info: true,
@ -26,172 +29,165 @@ const ProductInfo = () => {
notes: true,
auditLogs: true
})
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current?.fetchObject?.()
return true
},
edit: () => {
editFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
editFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
editFormRef?.current?.handleUpdate?.()
return true
}
}
return (
<EditObjectForm
id={productId}
type='product'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
{({
loading,
isEditing,
startEditing,
cancelEditing,
handleUpdate,
formValid,
objectData,
editLoading,
lock,
fetchObject
}) => {
const actions = {
reload: () => {
fetchObject()
return true
},
edit: () => {
startEditing()
return false
},
cancelEdit: () => {
cancelEditing()
return true
},
finishEdit: () => {
handleUpdate()
return true
}
}
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{ height: '100%', minHeight: 0 }}
<>
<Flex
gap='large'
vertical='true'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='product'
id={productId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'Product Information' },
{ key: 'parts', label: 'Product Parts' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='Product Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='product'
id={productId}
disabled={loading}
/>
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'Product Information' },
{ key: 'parts', label: 'Product Parts' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
<EditObjectForm
id={productId}
type='product'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={lock?.locked || loading}
loading={editLoading}
type='product'
objectData={objectData}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='Product Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='product'
objectData={objectData}
/>
</InfoCollapse>
<InfoCollapse
title='Product Parts'
icon={<ProductIcon />}
active={collapseState.parts}
onToggle={(expanded) =>
updateCollapseState('parts', expanded)
}
collapseKey='parts'
>
<ObjectTable
type='part'
visibleColumns={{
product: false,
'product._id': false
}}
masterFilter={{ 'product._id': productId }}
/>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) =>
updateCollapseState('notes', expanded)
}
collapseKey='notes'
>
<Card>
<NotesPanel _id={productId} type='product' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': productId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
)}
</EditObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Product Parts'
icon={<ProductIcon />}
active={collapseState.parts}
onToggle={(expanded) => updateCollapseState('parts', expanded)}
collapseKey='parts'
>
<ObjectTable
type='part'
visibleColumns={{
product: false,
'product._id': false
}}
masterFilter={{ 'product._id': productId }}
/>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={productId} type='product' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': productId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -1,3 +1,4 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
@ -19,161 +20,157 @@ import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
const UserInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const userId = new URLSearchParams(location.search).get('userId')
const [collapseState, updateCollapseState] = useCollapseState('UserInfo', {
info: true,
notes: true,
auditLogs: true
})
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current?.fetchObject?.()
return true
},
edit: () => {
editFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
editFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
editFormRef?.current?.handleUpdate?.()
return true
}
}
return (
<EditObjectForm
id={userId}
type='user'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
{({
loading,
isEditing,
startEditing,
cancelEditing,
handleUpdate,
formValid,
objectData,
editLoading,
lock,
fetchObject
}) => {
// Define actions for ActionHandler
const actions = {
reload: () => {
fetchObject()
return true
},
edit: () => {
startEditing()
return false
},
cancelEdit: () => {
cancelEditing()
return true
},
finishEdit: () => {
handleUpdate()
return true
}
}
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{ height: '100%', minHeight: 0 }}
<>
<Flex
gap='large'
vertical='true'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='user'
id={userId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'User Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='User Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='user'
id={userId}
disabled={loading}
/>
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'User Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
<EditObjectForm
id={userId}
type='user'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={lock?.locked || loading || true}
loading={editLoading}
type='user'
objectData={objectData}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='User Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='user'
objectData={objectData}
/>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) =>
updateCollapseState('notes', expanded)
}
collapseKey='notes'
>
<Card>
<NotesPanel _id={userId} type='user' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': userId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
)}
</EditObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={userId} type='user' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': userId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -1,3 +1,4 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import loglevel from 'loglevel'
@ -23,165 +24,160 @@ log.setLevel(config.logLevel)
const VendorInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const vendorId = new URLSearchParams(location.search).get('vendorId')
const [collapseState, updateCollapseState] = useCollapseState('VendorInfo', {
info: true,
notes: true,
auditLogs: true
})
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
lock: null,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current?.handleFetchObject?.()
return true
},
edit: () => {
editFormRef?.current?.startEditing?.()
return false
},
cancelEdit: () => {
editFormRef?.current?.cancelEditing?.()
return true
},
finishEdit: () => {
editFormRef?.current?.handleUpdate?.()
return true
},
delete: () => {
editFormRef?.current?.handleDelete?.()
return true
}
}
return (
<EditObjectForm
id={vendorId}
type='vendor'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
{({
loading,
isEditing,
startEditing,
cancelEditing,
handleFetchObject,
handleUpdate,
handleDelete,
formValid,
objectData,
editLoading,
lock
}) => {
// Define actions for ActionHandler
const actions = {
reload: () => {
handleFetchObject()
return true
},
edit: () => {
startEditing()
return false
},
cancelEdit: () => {
cancelEditing()
return true
},
finishEdit: () => {
handleUpdate()
return true
},
delete: () => {
handleDelete()
return true
}
}
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{ height: '100%', minHeight: 0 }}
<>
<Flex
gap='large'
vertical='true'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='vendor'
id={vendorId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'Vendor Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='Vendor Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='vendor'
id={vendorId}
disabled={loading}
/>
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'Vendor Information' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
<EditObjectForm
id={vendorId}
type='vendor'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={lock?.locked || loading}
loading={editLoading}
type='vendor'
objectData={objectData}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='Vendor Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
isEditing={isEditing}
type='vendor'
objectData={objectData}
/>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) =>
updateCollapseState('notes', expanded)
}
collapseKey='notes'
>
<Card>
<NotesPanel _id={vendorId} type='vendor' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': vendorId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
)}
</EditObjectForm>
</InfoCollapse>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={vendorId} type='vendor' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': vendorId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -1,223 +1,225 @@
import { useContext } from 'react'
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card, Typography } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import useCollapseState from '../../hooks/useCollapseState'
import NotesPanel from '../../common/NotesPanel'
import InfoCollapse from '../../common/InfoCollapse'
import ObjectInfo from '../../common/ObjectInfo'
import ViewButton from '../../common/ViewButton'
import EditObjectForm from '../../common/EditObjectForm'
import EditButtons from '../../common/EditButtons'
import LockIndicator from '../../common/LockIndicator.jsx'
import ActionHandler from '../../common/ActionHandler'
import loglevel from 'loglevel'
import config from '../../../../config.js'
import useCollapseState from '../../hooks/useCollapseState.js'
import NotesPanel from '../../common/NotesPanel.jsx'
import InfoCollapse from '../../common/InfoCollapse.jsx'
import ObjectInfo from '../../common/ObjectInfo.jsx'
import ViewButton from '../../common/ViewButton.jsx'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx'
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
import GCodeFileIcon from '../../../Icons/GCodeFileIcon.jsx'
import { ApiServerContext } from '../../context/ApiServerContext'
import EditObjectForm from '../../common/EditObjectForm.jsx'
import EditButtons from '../../common/EditButtons.jsx'
import LockIndicator from '../../common/LockIndicator.jsx'
import ActionHandler from '../../common/ActionHandler.jsx'
import ObjectActions from '../../common/ObjectActions.jsx'
import ObjectTable from '../../common/ObjectTable.jsx'
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import EyeIcon from '../../../Icons/EyeIcon.jsx'
const { Text } = Typography
const log = loglevel.getLogger('GCodeFileInfo')
log.setLevel(config.logLevel)
const GCodeFileInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const gcodeFileId = new URLSearchParams(location.search).get('gcodeFileId')
const { fetchObjectContent } = useContext(ApiServerContext)
const [collapseState, updateCollapseState] = useCollapseState(
'gcodeFileInfo',
'GCodeFileInfo',
{
info: true,
preview: true,
stocks: true,
notes: true,
auditLogs: true
}
)
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
locked: false,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current.handleFetchObject()
return true
},
edit: () => {
editFormRef?.current.startEditing()
return false
},
cancelEdit: () => {
editFormRef?.current.cancelEditing()
return true
},
finishEdit: () => {
editFormRef?.current.handleUpdate()
return true
}
}
return (
<EditObjectForm
id={gcodeFileId}
type='gcodefile'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
{({
loading,
isEditing,
startEditing,
cancelEditing,
handleUpdate,
formValid,
objectData,
editLoading,
lock,
fetchObject
}) => {
// Define actions that can be triggered via URL, now with access to startEditing
const actions = {
reload: () => {
fetchObject()
return true
},
edit: () => {
startEditing()
return false
},
cancelEdit: () => {
cancelEditing()
return true
},
finishEdit: () => {
handleUpdate()
return true
},
download: () => {
if (gcodeFileId) {
fetchObjectContent(
gcodeFileId,
'gcodeFile',
`${objectData.name}.gcode`
)
return true
}
}
}
<>
<Flex
gap='large'
vertical='true'
style={{
height: 'calc(var(--unit-100vh) - 155px)',
minHeight: 0
}}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='gcodeFile'
id={gcodeFileId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'GCode File Information' },
{ key: 'preview', label: 'GCode File Preview' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{ height: '100%', minHeight: 0 }}
<div style={{ height: '100%', overflowY: 'scroll' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<EditObjectForm
id={gcodeFileId}
type='gcodeFile'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
console.log('Got edit form state change', state)
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='gcodeFile'
id={gcodeFileId}
disabled={loading}
/>
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'GCode File Information' },
{ key: 'preview', label: 'GCode File Preview' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={lock?.locked || loading}
loading={editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='GCode File Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
objectData={objectData}
type='gcodeFile'
/>
</InfoCollapse>
<InfoCollapse
title='GCode File Preview'
icon={<GCodeFileIcon />}
active={collapseState.preview}
onToggle={(expanded) =>
updateCollapseState('preview', expanded)
}
collapseKey='preview'
>
<Card>
{objectData?.gcodeFileInfo?.thumbnail ? (
<img
src={`data:image/png;base64,${objectData.gcodeFileInfo.thumbnail.data}`}
alt='GCodeFile'
style={{ maxWidth: '100%' }}
/>
) : (
<Text>n/a</Text>
)}
</Card>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) =>
updateCollapseState('notes', expanded)
}
collapseKey='notes'
>
<Card>
<NotesPanel _id={gcodeFileId} type='gcodeFile' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': gcodeFileId }}
visibleColumns={{ _id: false, 'parent._id': false }}
{({ loading, isEditing, objectData }) => {
return (
<Flex vertical gap={'large'}>
<InfoCollapse
title='GCode File Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='gcodeFile'
objectData={objectData}
visibleProperties={{}}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
</InfoCollapse>
<InfoCollapse
title='GCode File Preview'
icon={<EyeIcon />}
active={collapseState.preview}
onToggle={(expanded) =>
updateCollapseState('preview', expanded)
}
collapseKey='preview'
>
<Card>
{objectData?.gcodeFileInfo?.thumbnail ? (
<img
src={`data:image/png;base64,${objectData.gcodeFileInfo.thumbnail.data}`}
alt='GCodeFile'
style={{ maxWidth: '100%' }}
/>
) : (
<Text>n/a</Text>
)}
</Card>
</InfoCollapse>
</Flex>
)
}}
</EditObjectForm>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={gcodeFileId} type='gcodeFile' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': gcodeFileId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -1,25 +1,33 @@
import { useRef, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import useCollapseState from '../../hooks/useCollapseState'
import NotesPanel from '../../common/NotesPanel'
import InfoCollapse from '../../common/InfoCollapse'
import ObjectInfo from '../../common/ObjectInfo'
import ViewButton from '../../common/ViewButton'
import EditObjectForm from '../../common/EditObjectForm'
import EditButtons from '../../common/EditButtons'
import loglevel from 'loglevel'
import config from '../../../../config.js'
import useCollapseState from '../../hooks/useCollapseState.js'
import NotesPanel from '../../common/NotesPanel.jsx'
import InfoCollapse from '../../common/InfoCollapse.jsx'
import ObjectInfo from '../../common/ObjectInfo.jsx'
import ViewButton from '../../common/ViewButton.jsx'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx'
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
import EditObjectForm from '../../common/EditObjectForm.jsx'
import EditButtons from '../../common/EditButtons.jsx'
import LockIndicator from '../../common/LockIndicator.jsx'
import ActionHandler from '../../common/ActionHandler'
import InfoCircleIcon from '../../../Icons/InfoCircleIcon'
import JobIcon from '../../../Icons/JobIcon'
import AuditLogIcon from '../../../Icons/AuditLogIcon'
import NoteIcon from '../../../Icons/NoteIcon'
import ActionHandler from '../../common/ActionHandler.jsx'
import ObjectActions from '../../common/ObjectActions.jsx'
import ObjectTable from '../../common/ObjectTable.jsx'
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
import JobIcon from '../../../Icons/JobIcon.jsx'
const log = loglevel.getLogger('JobInfo')
log.setLevel(config.logLevel)
const JobInfo = () => {
const location = useLocation()
const editFormRef = useRef(null)
const actionHandlerRef = useRef(null)
const jobId = new URLSearchParams(location.search).get('jobId')
const [collapseState, updateCollapseState] = useCollapseState('JobInfo', {
info: true,
@ -28,152 +36,170 @@ const JobInfo = () => {
auditLogs: true
})
const [editFormState, setEditFormState] = useState({
isEditing: false,
editLoading: false,
formValid: false,
locked: false,
loading: false
})
const actions = {
reload: () => {
editFormRef?.current.handleFetchObject()
return true
},
edit: () => {
editFormRef?.current.startEditing()
return false
},
cancelEdit: () => {
editFormRef?.current.cancelEditing()
return true
},
finishEdit: () => {
editFormRef?.current.handleUpdate()
return true
}
}
return (
<EditObjectForm
id={jobId}
type='job'
style={{ height: 'calc(var(--unit-100vh) - 155px)', minHeight: 0 }}
>
{({
loading,
isEditing,
formValid,
objectData,
editLoading,
lock,
fetchObject
}) => {
// Define actions that can be triggered via URL, now with access to startEditing
const actions = {
reload: () => {
fetchObject()
return true
}
}
<>
<Flex
gap='large'
vertical='true'
style={{
height: 'calc(var(--unit-100vh) - 155px)',
minHeight: 0
}}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='job'
id={jobId}
disabled={editFormState.loading}
/>
<ViewButton
disabled={editFormState.loading}
items={[
{ key: 'info', label: 'Job Information' },
{ key: 'subJobs', label: 'Sub Jobs' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={editFormState.lock} />
</Space>
<Space>
<EditButtons
isEditing={editFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={editFormState.editLoading}
formValid={editFormState.formValid}
disabled={editFormState.lock?.locked || editFormState.loading}
loading={editFormState.editLoading}
/>
</Space>
</Flex>
return (
<ActionHandler actions={actions} loading={loading}>
{({ callAction }) => (
<Flex
gap='large'
vertical='true'
style={{ height: '100%', minHeight: 0 }}
<div style={{ height: '100%', overflowY: 'scroll' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={editFormState.loading}
ref={actionHandlerRef}
>
<InfoCollapse
title='Job Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) => updateCollapseState('info', expanded)}
collapseKey='info'
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions type='job' id={jobId} disabled={loading} />
<ViewButton
disabled={loading}
items={[
{ key: 'info', label: 'Job Information' },
{ key: 'subJobs', label: 'Sub Jobs' },
{ key: 'notes', label: 'Notes' },
{ key: 'auditLogs', label: 'Audit Logs' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
</Space>
<LockIndicator lock={lock} />
</Space>
<Space>
<EditButtons
<EditObjectForm
id={jobId}
type='job'
style={{ height: '100%' }}
ref={editFormRef}
onStateChange={(state) => {
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({ loading, isEditing, objectData }) => (
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
handleUpdate={() => {
callAction('finishEdit')
}}
cancelEditing={() => {
callAction('cancelEdit')
}}
startEditing={() => {
callAction('edit')
}}
editLoading={editLoading}
formValid={formValid}
disabled={true}
loading={editLoading}
type='job'
objectData={objectData}
/>
</Space>
</Flex>
)}
</EditObjectForm>
</InfoCollapse>
</ActionHandler>
<div style={{ height: '100%', overflow: 'auto' }}>
<Flex vertical gap={'large'}>
<InfoCollapse
title='Job Information'
icon={<InfoCircleIcon />}
active={collapseState.info}
onToggle={(expanded) =>
updateCollapseState('info', expanded)
}
collapseKey='info'
>
<ObjectInfo
loading={loading}
indicator={<LoadingOutlined />}
isEditing={isEditing}
type='job'
objectData={objectData}
/>
</InfoCollapse>
<InfoCollapse
title='Sub Jobs'
icon={<JobIcon />}
active={collapseState.subJobs}
onToggle={(expanded) => updateCollapseState('subJobs', expanded)}
collapseKey='subJobs'
>
<ObjectTable
type='subJob'
masterFilter={{ 'job._id': jobId }}
visibleColumns={{ 'job._id': false }}
/>
</InfoCollapse>
<InfoCollapse
title='Sub Jobs'
icon={<JobIcon />}
active={collapseState.subJobs}
onToggle={(expanded) =>
updateCollapseState('subJobs', expanded)
}
collapseKey='subJobs'
>
<ObjectTable
type='subJob'
masterFilter={{ 'job._id': jobId }}
visibleColumns={{ 'job._id': false }}
/>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={jobId} type='job' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) =>
updateCollapseState('notes', expanded)
}
collapseKey='notes'
>
<Card>
<NotesPanel _id={jobId} type='job' />
</Card>
</InfoCollapse>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': jobId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
)}
</ActionHandler>
)
}}
</EditObjectForm>
<InfoCollapse
title='Audit Logs'
icon={<AuditLogIcon />}
active={collapseState.auditLogs}
onToggle={(expanded) =>
updateCollapseState('auditLogs', expanded)
}
collapseKey='auditLogs'
>
{editFormState.loading ? (
<InfoCollapsePlaceholder />
) : (
<ObjectTable
type='auditLog'
masterFilter={{ 'parent._id': jobId }}
visibleColumns={{ _id: false, 'parent._id': false }}
/>
)}
</InfoCollapse>
</Flex>
</div>
</Flex>
</>
)
}

View File

@ -83,7 +83,7 @@ const ActionHandler = forwardRef(
ActionHandler.displayName = 'ActionHandler'
ActionHandler.propTypes = {
children: PropTypes.func,
children: PropTypes.oneOf([PropTypes.func, PropTypes.object]),
actions: PropTypes.objectOf(PropTypes.func),
actionParam: PropTypes.string,
clearAfterExecute: PropTypes.bool,

View File

@ -1,92 +0,0 @@
// NoteTypeSelect.js
import PropTypes from 'prop-types'
import { message, Tag, Select, Space } from 'antd'
import { useEffect, useState, useContext, useCallback } from 'react'
import axios from 'axios'
import { AuthContext } from '../context/AuthContext'
import config from '../../../config'
const NoteTypeSelect = ({ onChange, disabled, value = null }) => {
const [noteTypeOptions, setNoteTypeOptions] = useState([])
const [loading, setLoading] = useState(true)
const [messageApi] = message.useMessage()
const { authenticated } = useContext(AuthContext)
const fetchNoteTypesData = useCallback(async () => {
if (!authenticated) {
return
}
setLoading(true)
try {
const response = await axios.get(
`${config.backendUrl}/notetypes?active=true`,
{
headers: {
Accept: 'application/json'
},
withCredentials: true // Important for including cookies
}
)
const data = response.data
setNoteTypeOptions(() => {
var options = []
data.map((noteType) => {
var newNoteTypeOption = {
label: (
<Space>
<Tag color={noteType?.color}>{noteType.name}</Tag>
</Space>
),
value: noteType._id
}
options.push(newNoteTypeOption)
})
return options
})
setLoading(false)
} catch (error) {
if (error.response) {
// For other errors, show a message
messageApi.error(
'Error fetching note types data:',
error.response.status
)
} else {
messageApi.error(
'An unexpected error occurred. Please try again later.'
)
}
}
}, [authenticated, messageApi])
useEffect(() => {
if (authenticated) {
fetchNoteTypesData()
}
}, [authenticated, fetchNoteTypesData])
return (
<Select
options={noteTypeOptions}
onChange={onChange}
loading={loading}
disabled={disabled}
placeholder='Select note type'
style={{ width: '100%' }}
value={value}
/>
)
}
NoteTypeSelect.propTypes = {
onChange: PropTypes.func,
disabled: PropTypes.bool,
checkable: PropTypes.bool,
value: PropTypes.object
}
export default NoteTypeSelect

View File

@ -28,10 +28,10 @@ import config from '../../../config'
import { AuthContext } from '../context/AuthContext'
import { ApiServerContext } from '../context/ApiServerContext'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import NoteTypeSelect from './NoteTypeSelect'
import IdDisplay from './IdDisplay'
import ReloadIcon from '../../Icons/ReloadIcon'
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
import ObjectProperty from './ObjectProperty'
const { Text, Title } = Typography
const { TextArea } = Input
@ -621,7 +621,11 @@ const NotesPanel = ({ _id, onNewNote, type }) => {
{ required: true, message: 'Please select a note type' }
]}
>
<NoteTypeSelect />
<ObjectProperty
type='object'
objectType='noteType'
isEditing={true}
/>
</Form.Item>
</Flex>
</Form>

View File

@ -47,7 +47,7 @@ const OperationDisplay = ({
}
OperationDisplay.propTypes = {
operation: PropTypes.bool.isRequired,
operation: PropTypes.string.isRequired,
showIcon: PropTypes.bool,
showText: PropTypes.bool,
showColor: PropTypes.bool

View File

@ -38,7 +38,7 @@ const ApiServerProvider = ({ children }) => {
const subscribedCallbacksRef = useRef(new Map())
const subscribedLockCallbacksRef = useRef(new Map())
const notifyLockUpdate = useCallback(
const handleLockUpdate = useCallback(
async (lockData) => {
logger.debug('Notifying lock update:', lockData)
const objectId = lockData._id || lockData.id
@ -94,7 +94,7 @@ const ApiServerProvider = ({ children }) => {
newSocket.on('objectUpdate', handleObjectUpdate)
newSocket.on('objectNew', handleObjectNew)
newSocket.on('notify_lock_update', notifyLockUpdate)
newSocket.on('lockUpdate', handleLockUpdate)
newSocket.on('disconnect', () => {
logger.debug('Api Server disconnected')
@ -123,7 +123,7 @@ const ApiServerProvider = ({ children }) => {
socketRef.current = newSocket
}
}, [token, authenticated, messageApi, notificationApi, notifyLockUpdate])
}, [token, authenticated, messageApi, notificationApi, handleLockUpdate])
useEffect(() => {
if (token && authenticated == true) {

View File

@ -1,6 +1,5 @@
// src/contexts/PrintServerContext.js
import { createContext, useEffect, useState, useContext, useRef } from 'react'
import io from 'socket.io-client'
import { message, notification } from 'antd'
import PropTypes from 'prop-types'
import { AuthContext } from './AuthContext'
@ -21,47 +20,9 @@ const PrintServerProvider = ({ children }) => {
useEffect(() => {
if (token) {
setConnecting(false)
setError(null)
log.debug('Token is available, connecting to print server...')
const newSocket = io(config.printServerUrl, {
reconnectionAttempts: 3,
timeout: 3000,
auth: { token: token }
})
setConnecting(true)
newSocket.on('connect', () => {
log.debug('Print server connected')
setConnecting(false)
setError(null)
})
newSocket.on('disconnect', () => {
log.debug('Print server disconnected')
setError('Print server disconnected')
})
newSocket.on('connect_error', (err) => {
log.error('Print server connection error:', err)
messageApi.error('Print server connection error: ' + err.message)
setError('Print server connection error')
})
newSocket.on('bridge.notification', (data) => {
notificationApi[data.type]({
title: data.title,
message: data.message
})
})
newSocket.on('error', (err) => {
log.error('Print server error:', err)
setError('Print server error')
})
socketRef.current = newSocket
// Clean up function
return () => {
if (socketRef.current) {

View File

@ -29,6 +29,7 @@ export const FilamentStock = {
],
filters: ['_id'],
sorters: ['createdAt', 'updatedAt'],
group: ['filament'],
properties: [
{
name: '_id',