From ecf73c13c007bdf5a2a5e3e691535be552f439f3 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sun, 23 Nov 2025 13:26:46 +0000 Subject: [PATCH] Refactor Load and Unload Filament Stock components to utilize WizardView for improved user experience, integrate API server context for real-time temperature and filament sensor updates, and streamline form validation logic. --- .../FilamentStocks/LoadFilamentStock.jsx | 257 +++++++----------- .../FilamentStocks/UnloadFilamentStock.jsx | 179 +++++------- 2 files changed, 163 insertions(+), 273 deletions(-) diff --git a/src/components/Dashboard/Inventory/FilamentStocks/LoadFilamentStock.jsx b/src/components/Dashboard/Inventory/FilamentStocks/LoadFilamentStock.jsx index a003ba2..72e69dd 100644 --- a/src/components/Dashboard/Inventory/FilamentStocks/LoadFilamentStock.jsx +++ b/src/components/Dashboard/Inventory/FilamentStocks/LoadFilamentStock.jsx @@ -1,27 +1,14 @@ import { useState, useContext, useEffect } from 'react' -import { - Form, - Button, - Typography, - Flex, - Steps, - Divider, - Descriptions, - Alert -} from 'antd' -import { useMediaQuery } from 'react-responsive' +import { Form, Flex, Descriptions, Alert } from 'antd' import PropTypes from 'prop-types' -import { PrintServerContext } from '../../context/PrintServerContext' -import FilamentStockSelect from '../../common/FilamentStockSelect' -import PrinterSelect from '../../common/PrinterSelect' -import FilamentStockDisplay from '../../common/FilamentStockDisplay' import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel' import { LoadingOutlined } from '@ant-design/icons' -import PrinterState from '../../common/StateDisplay' - -const { Title } = Typography +import ObjectSelect from '../../common/ObjectSelect' +import ObjectDisplay from '../../common/ObjectDisplay' +import WizardView from '../../common/WizardView' +import { ApiServerContext } from '../../context/ApiServerContext' const LoadFilamentStock = ({ onOk, @@ -29,7 +16,8 @@ const LoadFilamentStock = ({ printer = null, filamentStockLoaded = false }) => { - const isMobile = useMediaQuery({ maxWidth: 768 }) + const { connected, subscribeToObjectEvent, sendObjectAction } = + useContext(ApiServerContext) LoadFilamentStock.propTypes = { onOk: PropTypes.func.isRequired, @@ -38,8 +26,6 @@ const LoadFilamentStock = ({ filamentStockLoaded: PropTypes.bool } - const { printServer } = useContext(PrintServerContext) - const initialLoadFilamentStockForm = { printer: printer, filamentStock: null @@ -47,8 +33,7 @@ const LoadFilamentStock = ({ const [loadFilamentStockLoading, setLoadFilamentStockLoading] = useState(false) - const [currentStep, setCurrentStep] = useState(0) - const [nextEnabled, setNextEnabled] = useState(false) + const [formValid, setFormValid] = useState(false) const [currentTemperature, setCurrentTemperature] = useState(-1) const [targetTemperature, setTargetTemperature] = useState(0) const [filamentSensorDetected, setFilamentSensorDetected] = @@ -62,77 +47,69 @@ const LoadFilamentStock = ({ loadFilamentStockForm ) - // Add websocket temperature monitoring useEffect(() => { - if (loadFilamentStockFormValues.printer) { - const params = { - printerId: loadFilamentStockFormValues.printer._id, - objects: { - extruder: null, - 'filament_switch_sensor fsensor': null + if (printer?._id && connected) { + const temperatureEventUnsubscribe = subscribeToObjectEvent( + printer._id, + 'printer', + 'temperature', + (event) => { + if (event.data?.extruder?.current) { + setCurrentTemperature(event.data.extruder.current) + } + if (event.data?.extruder?.target) { + setTargetTemperature(event.data.extruder.target) + } } - } - - const notifyStatusUpdate = (statusUpdate) => { - if (statusUpdate?.extruder?.temperature !== undefined) { - setCurrentTemperature(statusUpdate.extruder.temperature) + ) + const filamentStockEventUnsubscribe = subscribeToObjectEvent( + printer._id, + 'printer', + 'filamentSensor', + (event) => { + console.log('filamentSensor', event.data) + setFilamentSensorDetected(event.data.detected) } - if (statusUpdate?.extruder?.target !== undefined) { - setTargetTemperature(statusUpdate.extruder.target) - } - if ( - statusUpdate?.['filament_switch_sensor fsensor'] - ?.filament_detected !== undefined - ) { - setFilamentSensorDetected( - Boolean( - statusUpdate['filament_switch_sensor fsensor'].filament_detected - ) - ) - } - } - - printServer.emit('printer.objects.subscribe', params) - printServer.emit('printer.objects.query', params) - printServer.on('notify_status_update', notifyStatusUpdate) - + ) return () => { - printServer.off('notify_status_update', notifyStatusUpdate) - printServer.emit('printer.objects.unsubscribe', params) + if (temperatureEventUnsubscribe) temperatureEventUnsubscribe() + if (filamentStockEventUnsubscribe) filamentStockEventUnsubscribe() } } - }, [printServer, loadFilamentStockFormValues.printer]) + }, [printer?._id, connected]) useEffect(() => { + // Validate form fields loadFilamentStockForm .validateFields({ validateOnly: true }) - .then(() => setNextEnabled(filamentSensorDetected)) - .catch(() => setNextEnabled(false)) + .then(() => { + const hasPrinter = Boolean(loadFilamentStockFormValues.printer) + const hasFilamentStock = Boolean( + loadFilamentStockFormValues.filamentStock + ) + + // Step 0 (Preheat): needs printer + filament detected + (temp reached or no temp set) + const preheatReady = + hasPrinter && + filamentSensorDetected && + (targetTemperature === 0 || currentTemperature >= targetTemperature) + + // Step 1+ (Required/Summary): needs filamentStock selected + // Form is valid if preheat is ready (for step 0) OR filamentStock is selected (for step 1+) + // This allows progression: step 0 can proceed when preheat is ready, + // and step 1+ can proceed when filamentStock is selected + setFormValid(preheatReady || (hasPrinter && hasFilamentStock)) + }) + .catch(() => setFormValid(false)) }, [ loadFilamentStockForm, loadFilamentStockFormUpdateValues, - filamentSensorDetected - ]) - - useEffect(() => { - if ( - filamentSensorDetected == true && - currentTemperature >= targetTemperature - ) { - setNextEnabled(filamentSensorDetected) - if (currentStep == 0) { - setCurrentStep(1) - } - } else if (filamentSensorDetected == false) { - setCurrentStep(0) - } - }, [ + loadFilamentStockFormValues, filamentSensorDetected, - targetTemperature, currentTemperature, - currentStep + targetTemperature ]) const summaryItems = [ @@ -140,8 +117,9 @@ const LoadFilamentStock = ({ key: 'filamentStock', label: 'Stock', children: loadFilamentStockFormValues.filamentStock ? ( - ) : ( 'n/a' @@ -151,9 +129,12 @@ const LoadFilamentStock = ({ key: 'printer', label: 'Printer', children: loadFilamentStockFormValues.printer ? ( - + ) : ( - 'n/a>' + 'n/a' ) } ] @@ -169,10 +150,16 @@ const LoadFilamentStock = ({ try { // Set the extruder temperature - await printServer.emit('printer.filamentstock.load', { - printerId: loadFilamentStockFormValues.printer._id, - filamentStockId: loadFilamentStockFormValues.filamentStock._id - }) + await sendObjectAction( + loadFilamentStockFormValues.printer._id, + 'printer', + { + type: 'loadFilamentStock', + data: { + filamentStock: loadFilamentStockFormValues.filamentStock + } + } + ) onOk() } finally { setLoadFilamentStockLoading(false) @@ -196,7 +183,7 @@ const LoadFilamentStock = ({ } ]} > - + {targetTemperature == 0 ? ( ) : null} @@ -250,7 +236,7 @@ const LoadFilamentStock = ({ } ]} > - + ) @@ -267,75 +253,28 @@ const LoadFilamentStock = ({ ] return ( - - {!isMobile && ( -
- -
- )} - - {!isMobile && } - - - - Load Filament Stock - -
- setLoadFilamentStockFormValues((prevValues) => ({ - ...prevValues, - ...changedValues - })) - } - initialValues={initialLoadFilamentStockForm} - > -
{steps[currentStep].content}
- - - - {currentStep < steps.length - 1 && ( - - )} - {currentStep === steps.length - 1 && ( - - )} - -
-
-
+
+ setLoadFilamentStockFormValues((prevValues) => ({ + ...prevValues, + ...changedValues + })) + } + initialValues={initialLoadFilamentStockForm} + > + loadFilamentStockForm.submit()} + formValid={formValid} + loading={loadFilamentStockLoading} + submitText='Done' + /> + ) } diff --git a/src/components/Dashboard/Inventory/FilamentStocks/UnloadFilamentStock.jsx b/src/components/Dashboard/Inventory/FilamentStocks/UnloadFilamentStock.jsx index 8b4b9c2..910769e 100644 --- a/src/components/Dashboard/Inventory/FilamentStocks/UnloadFilamentStock.jsx +++ b/src/components/Dashboard/Inventory/FilamentStocks/UnloadFilamentStock.jsx @@ -1,34 +1,30 @@ import { useState, useContext, useEffect } from 'react' -import { Form, Button, Typography, Flex, Steps, Divider, Alert } from 'antd' -import { useMediaQuery } from 'react-responsive' +import { Form, Flex, Alert } from 'antd' import PropTypes from 'prop-types' -import { PrintServerContext } from '../../context/PrintServerContext' -import PrinterSelect from '../../common/PrinterSelect' +import ObjectSelect from '../../common/ObjectSelect' import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel' +import WizardView from '../../common/WizardView' +import { ApiServerContext } from '../../context/ApiServerContext' import { LoadingOutlined } from '@ant-design/icons' -const { Title } = Typography - const UnloadFilamentStock = ({ onOk, reset, printer = null }) => { + const { connected, subscribeToObjectEvent, sendObjectAction } = + useContext(ApiServerContext) UnloadFilamentStock.propTypes = { onOk: PropTypes.func.isRequired, reset: PropTypes.bool.isRequired, printer: PropTypes.object } - const { printServer } = useContext(PrintServerContext) - const isMobile = useMediaQuery({ maxWidth: 768 }) - const initialUnloadFilamentStockForm = { printer: printer } const [unloadFilamentStockLoading, setUnloadFilamentStockLoading] = useState(false) - const [currentStep, setCurrentStep] = useState(0) - const [nextEnabled, setNextEnabled] = useState(false) + const [formValid, setFormValid] = useState(false) const [currentTemperature, setCurrentTemperature] = useState(-1) const [targetTemperature, setTargetTemperature] = useState(0) const [filamentSensorDetected, setFilamentSensorDetected] = useState(true) @@ -36,46 +32,35 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => { const [unloadFilamentStockFormValues, setUnloadFilamentStockFormValues] = useState(initialUnloadFilamentStockForm) - // Add websocket temperature monitoring useEffect(() => { - if (unloadFilamentStockFormValues.printer) { - const params = { - printerId: unloadFilamentStockFormValues.printer._id, - objects: { - extruder: null, - 'filament_switch_sensor fsensor': null + if (printer?._id && connected) { + const temperatureEventUnsubscribe = subscribeToObjectEvent( + printer._id, + 'printer', + 'temperature', + (event) => { + if (event.data?.extruder?.current) { + setCurrentTemperature(event.data.extruder.current) + } + if (event.data?.extruder?.target) { + setTargetTemperature(event.data.extruder.target) + } } - } - - const notifyStatusUpdate = (statusUpdate) => { - if (statusUpdate?.extruder?.temperature !== undefined) { - setCurrentTemperature(statusUpdate.extruder.temperature) + ) + const filamentStockEventUnsubscribe = subscribeToObjectEvent( + printer._id, + 'printer', + 'filamentSensor', + (event) => { + setFilamentSensorDetected(event.data.detected) } - if (statusUpdate?.extruder?.target !== undefined) { - setTargetTemperature(statusUpdate.extruder.target) - } - if ( - statusUpdate?.['filament_switch_sensor fsensor'] - ?.filament_detected !== undefined - ) { - setFilamentSensorDetected( - Boolean( - statusUpdate['filament_switch_sensor fsensor'].filament_detected - ) - ) - } - } - - printServer.emit('printer.objects.subscribe', params) - printServer.emit('printer.objects.query', params) - printServer.on('notify_status_update', notifyStatusUpdate) - + ) return () => { - printServer.off('notify_status_update', notifyStatusUpdate) - printServer.emit('printer.objects.unsubscribe', params) + if (temperatureEventUnsubscribe) temperatureEventUnsubscribe() + if (filamentStockEventUnsubscribe) filamentStockEventUnsubscribe() } } - }, [printServer, unloadFilamentStockFormValues.printer]) + }, [printer?._id, connected]) useEffect(() => { if (reset) { @@ -90,14 +75,14 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => { }) .then(() => { // Only enable next if we have a printer selected, we're not loading, and we've reached target temperature - setNextEnabled( + setFormValid( Boolean(unloadFilamentStockFormValues.printer) && !unloadFilamentStockLoading && currentTemperature + 1 > targetTemperature && targetTemperature != 0 ) }) - .catch(() => setNextEnabled(false)) + .catch(() => setFormValid(false)) }, [ unloadFilamentStockForm, unloadFilamentStockFormValues, @@ -109,10 +94,13 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => { const handleUnloadFilamentStock = async () => { setUnloadFilamentStockLoading(true) // Send G-code to retract the filament - await printServer.emit('printer.gcode.script', { - printerId: unloadFilamentStockFormValues.printer._id, - script: `_CLIENT_LINEAR_MOVE E=-200 F=1000` - }) + await sendObjectAction( + unloadFilamentStockFormValues.printer._id, + 'printer', + { + type: 'unloadFilamentStock' + } + ) //setUnloadFilamentStockLoading(false) } @@ -140,7 +128,7 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => { } ]} > - + {unloadFilamentStockLoading == false ? ( @@ -182,11 +170,11 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => { /> )} - {unloadFilamentStockFormValues.printer ? ( + {unloadFilamentStockFormValues.printer?._id ? ( ) : null} @@ -195,65 +183,28 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => { ] return ( - - {!isMobile && ( -
- -
- )} - - {!isMobile && } - - - - Unload Filament Stock - -
- setUnloadFilamentStockFormValues((prevValues) => ({ - ...prevValues, - ...changedValues - })) - } - initialValues={initialUnloadFilamentStockForm} - > -
{steps[currentStep].content}
- - - - {currentStep === steps.length - 1 && ( - - )} - -
-
-
+
+ setUnloadFilamentStockFormValues((prevValues) => ({ + ...prevValues, + ...changedValues + })) + } + initialValues={initialUnloadFilamentStockForm} + > + unloadFilamentStockForm.submit()} + formValid={formValid} + loading={unloadFilamentStockLoading} + submitText={unloadFilamentStockLoading ? 'Unloading...' : 'Unload'} + /> + ) }