Add filament stock management to ControlPrinter component with modal integration for loading and unloading filament. Implement new actions for pausing, resuming, and canceling jobs, enhancing printer control functionality.

This commit is contained in:
Tom Butcher 2025-11-24 03:34:28 +00:00
parent afe25c1e09
commit 91e7121fd5

View File

@ -1,6 +1,6 @@
import { useState, useRef, useEffect, useContext } from 'react'
import { useLocation } from 'react-router-dom'
import { Space, Flex, Card, Splitter, Divider } from 'antd'
import { Space, Flex, Card, Splitter, Divider, Modal } from 'antd'
import loglevel from 'loglevel'
import config from '../../../../config.js'
import useCollapseState from '../../hooks/useCollapseState.js'
@ -28,6 +28,9 @@ import { useMediaQuery } from 'react-responsive'
import AlertsDisplay from '../../common/AlertsDisplay.jsx'
import { ApiServerContext } from '../../context/ApiServerContext.jsx'
import LoadFilamentStock from '../../Inventory/FilamentStocks/LoadFilamentStock.jsx'
import UnloadFilamentStock from '../../Inventory/FilamentStocks/UnloadFilamentStock.jsx'
const log = loglevel.getLogger('ControlPrinter')
log.setLevel(config.logLevel)
@ -57,6 +60,9 @@ const ControlPrinter = () => {
collapseState.movement
)
const [loadFilamentStockOpen, setLoadFilamentStockOpen] = useState(false)
const [unloadFilamentStockOpen, setUnloadFilamentStockOpen] = useState(false)
useEffect(() => {
setSideBarVisible(
collapseState.temperature ||
@ -122,6 +128,39 @@ const ControlPrinter = () => {
})
}
return true
},
pauseJob: () => {
if (connected == true) {
sendObjectAction(printerId, 'printer', {
type: 'pauseJob'
})
}
return true
},
resumeJob: () => {
if (connected == true) {
sendObjectAction(printerId, 'printer', {
type: 'resumeJob'
})
}
return true
},
cancelJob: () => {
if (connected == true) {
sendObjectAction(printerId, 'printer', {
type: 'cancelJob'
})
}
return true
},
loadFilamentStock: () => {
setLoadFilamentStockOpen(true)
return true
},
unloadFilamentStock: () => {
setUnloadFilamentStockOpen(true)
return true
}
}
@ -146,298 +185,338 @@ const ControlPrinter = () => {
)
return (
<Flex
gap='large'
vertical='true'
style={{
maxHeight: '100%',
minHeight: 0
}}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='printer'
id={printerId}
disabled={objectFormState.loading}
visibleActions={{ edit: false }}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{
key: 'printer',
label: 'Printer'
},
{
key: 'job',
label: 'Job'
},
{
key: 'subJob',
label: 'Sub Job'
},
{
key: 'filamentStock',
label: 'Filament Stock'
},
{
key: 'temperature',
label: 'Temperature'
},
{
key: 'position',
label: 'Position'
},
{
key: 'movement',
label: 'Movement'
},
{ key: 'notes', label: 'Notes' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<AlertsDisplay alerts={objectFormState.objectData?.alerts} />
<>
<Flex
gap='large'
vertical='true'
style={{
maxHeight: '100%',
minHeight: 0
}}
>
<Flex justify={'space-between'}>
<Space size='middle'>
<Space size='small'>
<ObjectActions
type='printer'
id={printerId}
disabled={objectFormState.loading}
visibleActions={{ edit: false }}
objectData={objectFormState.objectData}
/>
<ViewButton
disabled={objectFormState.loading}
items={[
{
key: 'printer',
label: 'Printer'
},
{
key: 'job',
label: 'Job'
},
{
key: 'subJob',
label: 'Sub Job'
},
{
key: 'filamentStock',
label: 'Filament Stock'
},
{
key: 'temperature',
label: 'Temperature'
},
{
key: 'position',
label: 'Position'
},
{
key: 'movement',
label: 'Movement'
},
{ key: 'notes', label: 'Notes' }
]}
visibleState={collapseState}
updateVisibleState={updateCollapseState}
/>
<AlertsDisplay alerts={objectFormState.objectData?.alerts} />
</Space>
</Space>
<Space>
<EditButtons
isEditing={objectFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={objectFormState.editLoading}
formValid={objectFormState.formValid}
disabled={objectFormState.lock?.locked || objectFormState.loading}
loading={objectFormState.editLoading}
/>
</Space>
</Space>
<Space>
<EditButtons
isEditing={objectFormState.isEditing}
handleUpdate={() => {
actionHandlerRef.current.callAction('finishEdit')
}}
cancelEditing={() => {
actionHandlerRef.current.callAction('cancelEdit')
}}
startEditing={() => {
actionHandlerRef.current.callAction('edit')
}}
editLoading={objectFormState.editLoading}
formValid={objectFormState.formValid}
disabled={objectFormState.lock?.locked || objectFormState.loading}
loading={objectFormState.editLoading}
/>
</Space>
</Flex>
<div style={{ height: '100%', overflowY: 'scroll' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={objectFormState.loading}
ref={actionHandlerRef}
>
<Flex vertical>
<Splitter className={'farmcontrol-splitter'}>
<Splitter.Panel>
<Flex vertical gap={'large'}>
<InfoCollapse
title={'Printer'}
icon={<PrinterIcon />}
collapseKey='printer'
active={collapseState.printer}
onToggle={(expanded) =>
updateCollapseState('printer', expanded)
}
>
<ObjectForm
id={printerId}
type='printer'
ref={objectFormRef}
onStateChange={(state) => {
console.log('Got edit form state change', state)
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({
loading: printerObjectLoading,
objectData: printerObjectData
}) => {
return (
<ObjectInfo
loading={printerObjectLoading}
column={sideBarVisible ? 1 : undefined}
visibleProperties={{
connectedAt: false,
vendor: false,
'vendor._id': false,
host: false,
'host._id': false,
'moonraker.port': false,
'moonraker.apiKey': false,
'moonraker.protocol': false,
'moonraker.host': false,
tags: false,
firmware: false,
alerts: false
}}
objectData={printerObjectData}
type='printer'
/>
)
}}
</ObjectForm>
</InfoCollapse>
<InfoCollapse
title={'Job'}
icon={<JobIcon />}
collapseKey='job'
active={collapseState.job}
onToggle={(expanded) =>
updateCollapseState('job', expanded)
}
>
{objectFormState.objectData?.currentJob?._id ? (
<ObjectForm
id={objectFormState.objectData.currentJob._id}
type='job'
onStateChange={() => {}}
>
{({
loading: jobObjectLoading,
objectData: jobObjectData
}) => {
return (
<ObjectInfo
loading={jobObjectLoading}
column={sideBarVisible ? 1 : undefined}
visibleProperties={{
printers: false,
createdAt: false
}}
objectData={jobObjectData}
type='job'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No job.'}
hasBackground={false}
/>
)}
</InfoCollapse>
<InfoCollapse
title={'Sub Job'}
icon={<SubJobIcon />}
collapseKey='subJob'
active={collapseState.subJob}
onToggle={(expanded) =>
updateCollapseState('subJob', expanded)
}
>
{objectFormState.objectData?.currentSubJob?._id ? (
<ObjectForm
id={objectFormState.objectData.currentSubJob._id}
type='subjob'
onStateChange={() => {}}
>
{({
loading: subJobObjectLoading,
objectData: subJobObjectData
}) => {
return (
<ObjectInfo
loading={subJobObjectLoading}
column={sideBarVisible ? 1 : undefined}
visibleProperties={{
printers: false,
createdAt: false
}}
objectData={subJobObjectData}
type='subJob'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No sub job.'}
hasBackground={false}
/>
)}
</InfoCollapse>
<InfoCollapse
title={'Filament Stock'}
icon={<FilamentStockIcon />}
collapseKey='filamentStock'
active={collapseState.filamentStock}
onToggle={(expanded) =>
updateCollapseState('filamentStock', expanded)
}
>
{objectFormState.objectData?.currentFilamentStock?._id ? (
<ObjectForm
id={
objectFormState.objectData.currentFilamentStock._id
}
type='filamentStock'
onStateChange={() => {}}
>
{({
loading: filamentStockObjectLoading,
objectData: filamentStockObjectData
}) => {
return (
<ObjectInfo
loading={filamentStockObjectLoading}
column={sideBarVisible ? 1 : undefined}
showHyperlink={true}
visibleProperties={{
updatedAt: false,
createdAt: false
}}
objectData={filamentStockObjectData}
type='filamentStock'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No filament stock.'}
hasBackground={false}
/>
)}
</InfoCollapse>
</Flex>
</Splitter.Panel>
{sideBarVisible && !isMobile ? (
<Splitter.Panel
style={{ minWidth: '325px' }}
defaultSize='20%'
max='35%'
>
{sideBarItems}
</Splitter.Panel>
) : null}
</Splitter>
{isMobile ? (
<>
<Divider />
{sideBarItems}
</>
) : null}
</Flex>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={printerId} type='printer' />
</Card>
</InfoCollapse>
</Flex>
</div>
</Flex>
<div style={{ height: '100%', overflowY: 'scroll' }}>
<Flex vertical gap={'large'}>
<ActionHandler
actions={actions}
loading={objectFormState.loading}
ref={actionHandlerRef}
>
<Flex vertical>
<Splitter className={'farmcontrol-splitter'}>
<Splitter.Panel>
<Flex vertical gap={'large'}>
<InfoCollapse
title={'Printer'}
icon={<PrinterIcon />}
collapseKey='printer'
active={collapseState.printer}
onToggle={(expanded) =>
updateCollapseState('printer', expanded)
}
>
<ObjectForm
id={printerId}
type='printer'
ref={objectFormRef}
onStateChange={(state) => {
console.log('Got edit form state change', state)
setEditFormState((prev) => ({ ...prev, ...state }))
}}
>
{({
loading: printerObjectLoading,
objectData: printerObjectData
}) => {
return (
<ObjectInfo
loading={printerObjectLoading}
column={sideBarVisible ? 1 : undefined}
visibleProperties={{
connectedAt: false,
vendor: false,
'vendor._id': false,
host: false,
'host._id': false,
'moonraker.port': false,
'moonraker.apiKey': false,
'moonraker.protocol': false,
'moonraker.host': false,
tags: false,
firmware: false,
alerts: false,
online: false,
active: false,
currentFilamentStock: false,
'currentFilamentStock._id': false,
currentJob: false,
'currentJob._id': false,
currentSubJob: false,
'currentSubJob._id': false
}}
objectData={printerObjectData}
type='printer'
/>
)
}}
</ObjectForm>
</InfoCollapse>
<InfoCollapse
title={'Job'}
icon={<JobIcon />}
collapseKey='job'
active={collapseState.job}
onToggle={(expanded) =>
updateCollapseState('job', expanded)
}
>
{objectFormState.objectData?.currentJob?._id ? (
<ObjectForm
id={objectFormState.objectData.currentJob._id}
type='job'
onStateChange={() => {}}
>
{({
loading: jobObjectLoading,
objectData: jobObjectData
}) => {
return (
<ObjectInfo
loading={jobObjectLoading}
column={sideBarVisible ? 1 : undefined}
visibleProperties={{
printers: false,
createdAt: false
}}
objectData={jobObjectData}
type='job'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No job.'}
hasBackground={false}
/>
)}
</InfoCollapse>
<InfoCollapse
title={'Sub Job'}
icon={<SubJobIcon />}
collapseKey='subJob'
active={collapseState.subJob}
onToggle={(expanded) =>
updateCollapseState('subJob', expanded)
}
>
{objectFormState.objectData?.currentSubJob?._id ? (
<ObjectForm
id={objectFormState.objectData.currentSubJob._id}
type='subJob'
onStateChange={() => {}}
>
{({
loading: subJobObjectLoading,
objectData: subJobObjectData
}) => {
return (
<ObjectInfo
loading={subJobObjectLoading}
column={sideBarVisible ? 1 : undefined}
visibleProperties={{
printers: false,
createdAt: false
}}
objectData={subJobObjectData}
type='subJob'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No sub job.'}
hasBackground={false}
/>
)}
</InfoCollapse>
<InfoCollapse
title={'Filament Stock'}
icon={<FilamentStockIcon />}
collapseKey='filamentStock'
active={collapseState.filamentStock}
onToggle={(expanded) =>
updateCollapseState('filamentStock', expanded)
}
>
{objectFormState.objectData?.currentFilamentStock
?._id ? (
<ObjectForm
id={
objectFormState.objectData.currentFilamentStock
._id
}
type='filamentStock'
onStateChange={() => {}}
>
{({
loading: filamentStockObjectLoading,
objectData: filamentStockObjectData
}) => {
return (
<ObjectInfo
loading={filamentStockObjectLoading}
column={sideBarVisible ? 1 : undefined}
showHyperlink={true}
visibleProperties={{
updatedAt: false,
createdAt: false
}}
objectData={filamentStockObjectData}
type='filamentStock'
/>
)
}}
</ObjectForm>
) : (
<MissingPlaceholder
message={'No filament stock.'}
hasBackground={false}
/>
)}
</InfoCollapse>
</Flex>
</Splitter.Panel>
{sideBarVisible && !isMobile ? (
<Splitter.Panel
style={{ minWidth: '325px' }}
defaultSize='20%'
max='35%'
>
{sideBarItems}
</Splitter.Panel>
) : null}
</Splitter>
{isMobile ? (
<>
<Divider />
{sideBarItems}
</>
) : null}
</Flex>
</ActionHandler>
<InfoCollapse
title='Notes'
icon={<NoteIcon />}
active={collapseState.notes}
onToggle={(expanded) => updateCollapseState('notes', expanded)}
collapseKey='notes'
>
<Card>
<NotesPanel _id={printerId} type='printer' />
</Card>
</InfoCollapse>
</Flex>
</div>
</Flex>
<Modal
open={loadFilamentStockOpen}
onCancel={() => setLoadFilamentStockOpen(false)}
footer={null}
width='700px'
destroyOnHidden={true}
>
<LoadFilamentStock
printer={objectFormState.objectData}
onOk={() => setLoadFilamentStockOpen(false)}
reset={false}
filamentStockLoaded={false}
/>
</Modal>
<Modal
open={unloadFilamentStockOpen}
onCancel={() => setUnloadFilamentStockOpen(false)}
footer={null}
width='700px'
destroyOnHidden={true}
>
<UnloadFilamentStock
printer={objectFormState.objectData}
onOk={() => setUnloadFilamentStockOpen(false)}
reset={false}
filamentStockLoaded={false}
/>
</Modal>
</>
)
}