Enhance GCodeFile, DeployJob, NewJob, and ControlPrinter components with slicer integration support

- Updated NewGCodeFile and NewJob components to include slicerIntegration, currentStep, and onStepChange props for improved workflow management.
- Modified DeployJob to handle printer online status and added slicerIntegration support for conditional rendering of actions and steps.
- Enhanced ControlPrinter to manage visibility of actions based on slicerIntegration, improving user experience in printer control.
- Refactored form handling to pass result objects to onOk callbacks, ensuring better data handling upon submission.
This commit is contained in:
Tom Butcher 2026-07-20 02:11:01 +01:00
parent def9b9aefb
commit d7ad2208d4
4 changed files with 202 additions and 80 deletions

View File

@ -1,35 +1,49 @@
import { useMemo } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import ObjectInfo from '../../common/ObjectInfo' import ObjectInfo from '../../common/ObjectInfo'
import NewObjectForm from '../../common/NewObjectForm' import NewObjectForm from '../../common/NewObjectForm'
import WizardView from '../../common/WizardView' import WizardView from '../../common/WizardView'
const NewGCodeFile = ({ onOk, defaultValues }) => { const NewGCodeFile = ({
onOk,
defaultValues,
slicerIntegration = false,
shouldPrint = false,
currentStep,
onStepChange
}) => {
const initialValues = useMemo(
() => ({
state: { type: 'draft' },
...defaultValues
}),
[defaultValues]
)
return ( return (
<NewObjectForm <NewObjectForm type={'gcodeFile'} defaultValues={initialValues}>
type={'gcodeFile'}
defaultValues={{
state: { type: 'draft' },
...defaultValues
}}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => { {({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [ const steps = [
{ ...(!slicerIntegration
title: 'Upload', ? [
key: 'upload', {
content: ( title: 'Upload',
<ObjectInfo key: 'upload',
type='gcodeFile' content: (
column={1} <ObjectInfo
bordered={false} type='gcodeFile'
isEditing={true} column={1}
required={true} bordered={false}
objectData={objectData} isEditing={true}
visibleProperties={{ file: true }} required={true}
showLabels={false} objectData={objectData}
/> visibleProperties={{ file: true }}
) showLabels={false}
}, />
)
}
]
: []),
{ {
title: 'Required', title: 'Required',
key: 'required', key: 'required',
@ -73,10 +87,13 @@ const NewGCodeFile = ({ onOk, defaultValues }) => {
loading={submitLoading} loading={submitLoading}
formValid={formValid} formValid={formValid}
title='New GCode File' title='New GCode File'
showSteps={!slicerIntegration || !shouldPrint}
currentStep={currentStep}
onStepChange={onStepChange}
onSubmit={async () => { onSubmit={async () => {
const result = await handleSubmit() const result = await handleSubmit()
if (result) { if (result) {
onOk() onOk(result)
} }
}} }}
/> />
@ -89,7 +106,11 @@ const NewGCodeFile = ({ onOk, defaultValues }) => {
NewGCodeFile.propTypes = { NewGCodeFile.propTypes = {
onOk: PropTypes.func.isRequired, onOk: PropTypes.func.isRequired,
reset: PropTypes.bool, reset: PropTypes.bool,
defaultValues: PropTypes.object defaultValues: PropTypes.object,
slicerIntegration: PropTypes.bool,
currentStep: PropTypes.number,
onStepChange: PropTypes.func,
shouldPrint: PropTypes.bool
} }
export default NewGCodeFile export default NewGCodeFile

View File

@ -2,17 +2,20 @@ import { useState, useContext, useEffect, useCallback } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import WizardView from '../../common/WizardView' import WizardView from '../../common/WizardView'
import { ApiServerContext } from '../../context/ApiServerContext' import { ApiServerContext } from '../../context/ApiServerContext'
import { Flex, Typography } from 'antd' import { Descriptions, Flex, Typography } from 'antd'
import ObjectTable from '../../common/ObjectTable' import ObjectTable from '../../common/ObjectTable'
import ObjectSelect from '../../common/ObjectSelect' import ObjectProperty from '../../common/ObjectProperty'
const { Text } = Typography const { Text } = Typography
const DeployJob = ({
const DeployJob = ({ onOk, objectData = undefined }) => { onOk,
objectData = undefined,
slicerIntegration = false
}) => {
const [deployLoading, setDeployLoading] = useState(false) const [deployLoading, setDeployLoading] = useState(false)
const [job, setJob] = useState(objectData) const [job, setJob] = useState(objectData)
const [deployedSubJobsCount, setDeployedSubJobsCount] = useState(0) const [deployedSubJobsCount, setDeployedSubJobsCount] = useState(0)
const [subJobsCount, setSubJobsCount] = useState(999) const [subJobsCount, setSubJobsCount] = useState(999)
const [allPrintersOnline, setAllPrintersOnline] = useState(false)
const { sendObjectAction, fetchObjects } = useContext(ApiServerContext) const { sendObjectAction, fetchObjects } = useContext(ApiServerContext)
const handleDeploy = useCallback(async () => { const handleDeploy = useCallback(async () => {
@ -51,21 +54,94 @@ const DeployJob = ({ onOk, objectData = undefined }) => {
} }
}, [deployedSubJobsCount, subJobsCount, deployLoading, onOk]) }, [deployedSubJobsCount, subJobsCount, deployLoading, onOk])
useEffect(() => {
if (objectData?.printers) {
if (objectData.printers.every((printer) => printer.online)) {
setAllPrintersOnline(true)
} else {
setAllPrintersOnline(false)
}
} else {
setAllPrintersOnline(false)
}
}, [objectData])
console.log(objectData)
const steps = [ const steps = [
{ {
title: 'Confirm', title: 'Confirm',
key: 'confirm', key: 'confirm',
content: ( content: (
<Flex vertical gap={'middle'} style={{ width: '100%' }}> <Flex vertical gap={'middle'} style={{ width: '100%' }}>
<Flex gap={'small'} align='center' style={{ width: '100%' }}> <Flex gap={'middle'} align='center' style={{ width: '100%' }}>
<Text type='secondary'>Job:</Text> <Descriptions
<ObjectSelect column={1}
type={'job'} items={[
style={{ flexGrow: 1 }} {
value={objectData} label: (
onChange={(newJob) => { <Flex
setJob(newJob) align='center'
}} gap={'small'}
style={{ height: '100%' }}
>
<Text type='secondary'>Job</Text>
</Flex>
),
children: (
<div
style={{
flexGrow: 1,
marginLeft: '3px',
marginBottom: '1px'
}}
>
<ObjectProperty
type={'object'}
name={'job'}
objectData={{ job: objectData }}
value={(object) => object.job}
readOnly={true}
isEditing={true}
objectType={'job'}
onChange={(newJob) => {
setJob(newJob)
}}
/>
</div>
)
},
{
label: (
<Flex
align='center'
gap={'small'}
style={{ height: '100%' }}
>
<Text type='secondary'>Printers</Text>
</Flex>
),
children: (
<div
style={{
flexGrow: 1,
marginLeft: '3px',
marginBottom: '1px'
}}
>
<ObjectProperty
type={'objectList'}
name={'printers'}
objectData={{ printers: objectData.printers }}
value={(object) => object.printers}
readOnly={true}
isEditing={true}
objectType={'printer'}
/>
</div>
)
}
]}
/> />
</Flex> </Flex>
@ -74,11 +150,11 @@ const DeployJob = ({ onOk, objectData = undefined }) => {
scrollHeight={'200px'} scrollHeight={'200px'}
visibleColumns={{ visibleColumns={{
printer: false, printer: false,
'job._id': false, job: false
'printer._id': false
}} }}
masterFilter={{ 'job._id': job?._id }} masterFilter={{ 'job._id': job?._id }}
size={'small'} size={'small'}
showActions={!slicerIntegration}
/> />
</Flex> </Flex>
) )
@ -89,9 +165,13 @@ const DeployJob = ({ onOk, objectData = undefined }) => {
<WizardView <WizardView
steps={steps} steps={steps}
loading={deployLoading} loading={deployLoading}
formValid={objectData != undefined} formValid={objectData != undefined && allPrintersOnline}
title='Deploy Job' title='Deploy Job'
showSteps={false} showSteps={false}
showSkipButton={slicerIntegration}
onSkip={() => {
onOk()
}}
submitText='Deploy' submitText='Deploy'
progress={(deployedSubJobsCount / subJobsCount) * 100} progress={(deployedSubJobsCount / subJobsCount) * 100}
onSubmit={() => { onSubmit={() => {
@ -104,7 +184,8 @@ const DeployJob = ({ onOk, objectData = undefined }) => {
DeployJob.propTypes = { DeployJob.propTypes = {
onOk: PropTypes.func.isRequired, onOk: PropTypes.func.isRequired,
objectData: PropTypes.object, objectData: PropTypes.object,
reset: PropTypes.bool reset: PropTypes.bool,
slicerIntegration: PropTypes.bool
} }
export default DeployJob export default DeployJob

View File

@ -1,17 +1,26 @@
import { useMemo } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import ObjectInfo from '../../common/ObjectInfo' import ObjectInfo from '../../common/ObjectInfo'
import NewObjectForm from '../../common/NewObjectForm' import NewObjectForm from '../../common/NewObjectForm'
import WizardView from '../../common/WizardView' import WizardView from '../../common/WizardView'
const NewJob = ({ onOk, defaultValues }) => { const NewJob = ({
onOk,
defaultValues,
slicerIntegration = false,
currentStep,
onStepChange
}) => {
const initialValues = useMemo(
() => ({
state: { type: 'draft' },
...defaultValues
}),
[defaultValues]
)
return ( return (
<NewObjectForm <NewObjectForm type={'job'} defaultValues={initialValues}>
type={'job'}
defaultValues={{
state: { type: 'draft' },
...defaultValues
}}
>
{({ handleSubmit, submitLoading, objectData, formValid }) => { {({ handleSubmit, submitLoading, objectData, formValid }) => {
const steps = [ const steps = [
{ {
@ -59,10 +68,17 @@ const NewJob = ({ onOk, defaultValues }) => {
loading={submitLoading} loading={submitLoading}
formValid={formValid} formValid={formValid}
title='New Job' title='New Job'
showSteps={!slicerIntegration}
currentStep={currentStep}
onStepChange={onStepChange}
showSkipButton={slicerIntegration}
onSkip={() => {
onOk(null)
}}
onSubmit={async () => { onSubmit={async () => {
const result = await handleSubmit() const result = await handleSubmit()
if (result) { if (result) {
onOk() onOk(result)
} }
}} }}
/> />
@ -75,7 +91,10 @@ const NewJob = ({ onOk, defaultValues }) => {
NewJob.propTypes = { NewJob.propTypes = {
onOk: PropTypes.func.isRequired, onOk: PropTypes.func.isRequired,
reset: PropTypes.bool, reset: PropTypes.bool,
defaultValues: PropTypes.object defaultValues: PropTypes.object,
slicerIntegration: PropTypes.bool,
currentStep: PropTypes.number,
onStepChange: PropTypes.func
} }
export default NewJob export default NewJob

View File

@ -9,9 +9,9 @@ import InfoCollapse from '../../common/InfoCollapse.jsx'
import ViewButton from '../../common/ViewButton.jsx' import ViewButton from '../../common/ViewButton.jsx'
import NoteIcon from '../../../Icons/NoteIcon.jsx' import NoteIcon from '../../../Icons/NoteIcon.jsx'
import ObjectForm from '../../common/ObjectForm.jsx' import ObjectForm from '../../common/ObjectForm.jsx'
import EditButtons from '../../common/EditButtons.jsx'
import ActionHandler from '../../common/ActionHandler.jsx' import ActionHandler from '../../common/ActionHandler.jsx'
import ObjectActions from '../../common/ObjectActions.jsx' import ObjectActions from '../../common/ObjectActions.jsx'
import PropTypes from 'prop-types'
import ObjectInfo from '../../common/ObjectInfo.jsx' import ObjectInfo from '../../common/ObjectInfo.jsx'
import PrinterIcon from '../../../Icons/PrinterIcon.jsx' import PrinterIcon from '../../../Icons/PrinterIcon.jsx'
@ -35,7 +35,7 @@ import ScrollBox from '../../common/ScrollBox.jsx'
const log = loglevel.getLogger('ControlPrinter') const log = loglevel.getLogger('ControlPrinter')
log.setLevel(config.logLevel) log.setLevel(config.logLevel)
const ControlPrinter = () => { const ControlPrinter = ({ slicerIntegration = false }) => {
const location = useLocation() const location = useLocation()
const objectFormRef = useRef(null) const objectFormRef = useRef(null)
const actionHandlerRef = useRef(null) const actionHandlerRef = useRef(null)
@ -226,7 +226,11 @@ const ControlPrinter = () => {
type='printer' type='printer'
id={printerId} id={printerId}
disabled={objectFormState.loading} disabled={objectFormState.loading}
visibleActions={{ edit: false }} visibleActions={{
edit: false,
info: !slicerIntegration,
control: !slicerIntegration
}}
objectData={objectFormState.objectData} objectData={objectFormState.objectData}
/> />
<ViewButton <ViewButton
@ -275,24 +279,6 @@ const ControlPrinter = () => {
/> />
</Space> </Space>
</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> </Flex>
<ScrollBox> <ScrollBox>
@ -353,11 +339,16 @@ const ControlPrinter = () => {
currentSubJob: false, currentSubJob: false,
'currentSubJob._id': false, 'currentSubJob._id': false,
createdAt: false, createdAt: false,
updatedAt: false updatedAt: false,
state: !slicerIntegration,
name: !slicerIntegration
}}
objectPropertyProps={{
showHyperlink: !slicerIntegration
}} }}
objectData={printerObjectData} objectData={printerObjectData}
type='printer' type='printer'
labelWidth='100px' labelWidth='120px'
/> />
) )
}} }}
@ -386,12 +377,15 @@ const ControlPrinter = () => {
<ObjectInfo <ObjectInfo
loading={jobObjectLoading} loading={jobObjectLoading}
column={sideBarVisible ? 1 : undefined} column={sideBarVisible ? 1 : undefined}
showHyperlink={true} showHyperlink={!slicerIntegration}
visibleProperties={{ visibleProperties={{
printers: false, printers: false,
createdAt: false, createdAt: false,
finishedAt: false finishedAt: false
}} }}
objectPropertyProps={{
showHyperlink: !slicerIntegration
}}
objectData={jobObjectData} objectData={jobObjectData}
type='job' type='job'
/> />
@ -428,12 +422,15 @@ const ControlPrinter = () => {
<ObjectInfo <ObjectInfo
loading={subJobObjectLoading} loading={subJobObjectLoading}
column={sideBarVisible ? 1 : undefined} column={sideBarVisible ? 1 : undefined}
showHyperlink={true} showHyperlink={!slicerIntegration}
visibleProperties={{ visibleProperties={{
printers: false, printers: false,
createdAt: false, createdAt: false,
finishedAt: false finishedAt: false
}} }}
objectPropertyProps={{
showHyperlink: !slicerIntegration
}}
objectData={subJobObjectData} objectData={subJobObjectData}
type='subJob' type='subJob'
/> />
@ -474,7 +471,7 @@ const ControlPrinter = () => {
<ObjectInfo <ObjectInfo
loading={filamentStockObjectLoading} loading={filamentStockObjectLoading}
column={sideBarVisible ? 1 : undefined} column={sideBarVisible ? 1 : undefined}
showHyperlink={true} showHyperlink={!slicerIntegration}
visibleProperties={{ visibleProperties={{
updatedAt: false, updatedAt: false,
createdAt: false createdAt: false
@ -558,4 +555,8 @@ const ControlPrinter = () => {
) )
} }
ControlPrinter.propTypes = {
slicerIntegration: PropTypes.bool
}
export default ControlPrinter export default ControlPrinter