import PropTypes from 'prop-types' import React, { useState, useContext, useEffect } from 'react' import axios from 'axios' import { capitalizeFirstLetter, timeStringToMinutes } from '../../utils/Utils.js' import { Form, Input, Button, message, Typography, Flex, Steps, Divider, Upload, Descriptions, Checkbox, Spin, InputNumber, Badge } from 'antd' import { LoadingOutlined } from '@ant-design/icons' import { AuthContext } from '../../context/AuthContext.js' import GCodeFileIcon from '../../../Icons/GCodeFileIcon' import FilamentSelect from '../../common/FilamentSelect' import config from '../../../../config.js' const { Dragger } = Upload const { Title } = Typography const initialNewGCodeFileForm = { gcodeFileInfo: {}, name: '', printTimeMins: 0, cost: 0, file: null, filament: null } //const chunkSize = 5000 const NewGCodeFile = ({ onOk, reset }) => { const [messageApi] = message.useMessage() const [newGCodeFileLoading, setNewGCodeFileLoading] = useState(false) const [gcodeParsing, setGcodeParsing] = useState(false) const [filamentSelectFilter, setFilamentSelectFilter] = useState(null) const [useFilamentSelectFilter, setUseFilamentSelectFilter] = useState(true) const [currentStep, setCurrentStep] = useState(0) const [nextEnabled, setNextEnabled] = useState(false) const [nextLoading, setNextLoading] = useState(false) const [newGCodeFileForm] = Form.useForm() const [newGCodeFileFormValues, setNewGCodeFileFormValues] = useState( initialNewGCodeFileForm ) const [gcodeFile, setGCodeFile] = useState(null) const newGCodeFileFormUpdateValues = Form.useWatch([], newGCodeFileForm) const { token, authenticated } = useContext(AuthContext) // eslint-disable-next-line const fetchFilamentDetails = async () => { if (!authenticated) { return } if ( newGCodeFileFormValues.filament && newGCodeFileFormValues.gcodeFileInfo ) { try { setNextLoading(true) const response = await axios.get( `${config.backendUrl}/filaments/${newGCodeFileFormValues.filament}`, { headers: { Accept: 'application/json' }, withCredentials: true // Important for including cookies } ) setNextLoading(false) const price = (response.data.price / 1000) * newGCodeFileFormValues.gcodeFileInfo.filament_used_g // convert kg to g and multiply const printTimeMins = timeStringToMinutes( newGCodeFileFormValues.gcodeFileInfo .estimated_printing_time_normal_mode ) setNewGCodeFileFormValues({ ...newGCodeFileFormValues, price, printTimeMins }) } catch (error) { if (error.response) { messageApi.error( 'Error fetching filament data:', error.response.status ) } else { messageApi.error( 'An unexpected error occurred. Please try again later.' ) } } } } React.useEffect(() => { newGCodeFileForm .validateFields({ validateOnly: true }) .then(() => setNextEnabled(true)) .catch(() => setNextEnabled(false)) }, [newGCodeFileForm, newGCodeFileFormUpdateValues]) const summaryItems = [ { key: 'name', label: 'Name', children: newGCodeFileFormValues?.name }, { key: 'filament', label: 'Filament', children: newGCodeFileFormValues?.filament != null ?? (<> {newGCodeFileFormValues.filament} )('n/a') }, { key: 'cost', label: 'Cost', children: '£' + newGCodeFileFormValues?.cost }, { key: 'sparse_infill_density', label: 'Infill Density', children: newGCodeFileFormValues?.gcodeFileInfo?.sparseInfillDensity }, { key: 'sparse_infill_pattern', label: 'Infill Pattern', children: capitalizeFirstLetter( newGCodeFileFormValues?.gcodeFileInfo?.sparseInfillPattern ) }, { key: 'layer_height', label: 'Layer Height', children: newGCodeFileFormValues?.gcodeFileInfo?.layerHeight + 'mm' }, { key: 'filamentType', label: 'Filament Material', children: newGCodeFileFormValues?.gcodeFileInfo?.filamentType }, { key: 'filamentUsedG', label: 'Filament Used (g)', children: newGCodeFileFormValues?.gcodeFileInfo?.filamentUsedG + 'g' }, { key: 'filamentVendor', label: 'Filament Brand', children: newGCodeFileFormValues?.gcodeFileInfo?.filamentVendor }, { key: 'hotendTemperature', label: 'Hotend Temperature', children: newGCodeFileFormValues?.gcodeFileInfo?.nozzleTemperature + '°' }, { key: 'bedTemperature', label: 'Bed Temperature', children: newGCodeFileFormValues?.gcodeFileInfo?.hotPlateTemp + '°' }, { key: 'estimated_printing_time_normal_mode', label: 'Est. Print Time', children: newGCodeFileFormValues?.gcodeFileInfo?.estimatedPrintingTimeNormalMode } ] React.useEffect(() => { if (reset) { setCurrentStep(0) newGCodeFileForm.resetFields() } }, [reset, newGCodeFileForm]) useEffect(() => { const filamentCost = newGCodeFileFormValues?.filament?.cost const gcodeFilamentUsed = newGCodeFileFormValues?.gcodeFileInfo?.filamentUsedG if (filamentCost && gcodeFilamentUsed) { const cost = (filamentCost / 1000) * gcodeFilamentUsed console.log('Setting cost') setNewGCodeFileFormValues((prev) => ({ ...prev, cost: cost.toFixed(2) })) newGCodeFileForm.setFieldValue('cost', cost.toFixed(2)) } }, [ newGCodeFileForm, newGCodeFileFormValues?.filament?.cost, newGCodeFileFormValues?.gcodeFileInfo?.filamentUsedG ]) const handleNewGCodeFileUpload = async (id) => { setNewGCodeFileLoading(true) const formData = new FormData() formData.append('gcodeFile', gcodeFile) try { await axios.post( `${config.backendUrl}/gcodefiles/${id}/content`, formData, { headers: { 'Content-Type': 'multipart/form-data', Authorization: `Bearer ${token}` } } ) resetForm() onOk() } catch (error) { messageApi.error('Error creating new gcode file: ' + error.message) } finally { setNewGCodeFileLoading(false) } } const handleNewGCodeFile = async () => { setNewGCodeFileLoading(true) try { const request = await axios.post( `${config.backendUrl}/gcodefiles`, newGCodeFileFormValues, { headers: { Authorization: `Bearer ${token}` } } ) messageApi.info('New G Code file created successfully. Uploading...') handleNewGCodeFileUpload(request.data._id) } catch (error) { messageApi.error('Error creating new gcode file: ' + error.message) } finally { setNewGCodeFileLoading(false) } } const handleGetGCodeFileInfo = async (file) => { try { setGcodeParsing(true) // Create a FormData object to send the file const formData = new FormData() formData.append('gcodeFile', file) // Call the API to extract and parse the config block const request = await axios.post( `${config.backendUrl}/gcodefiles/content`, formData, { withCredentials: true // Important for including cookies }, { headers: { Accept: 'application/json' } } ) // Parse the API response const parsedConfig = await request.data // Update state with the parsed config from API setNewGCodeFileFormValues({ ...newGCodeFileFormValues, gcodeFileInfo: parsedConfig }) console.log(parsedConfig) // Update filter settings if filament info is available if (parsedConfig.filament_type && parsedConfig.filament_diameter) { setFilamentSelectFilter({ type: parsedConfig.filament_type, diameter: parsedConfig.filament_diameter }) } const fileName = file.name.replace(/\.[^/.]+$/, '') newGCodeFileForm.setFieldValue('name', fileName) setNewGCodeFileFormValues((prev) => ({ ...prev, name: fileName })) setGCodeFile(file) setGcodeParsing(false) setCurrentStep(currentStep + 1) } catch (error) { console.error('Error getting G-code file info:', error) } } const resetForm = () => { newGCodeFileForm.setFieldsValue(initialNewGCodeFileForm) setNewGCodeFileFormValues(initialNewGCodeFileForm) setGCodeFile(null) setGcodeParsing(false) setCurrentStep(0) } const steps = [ { title: 'Upload', key: 'upload', content: ( <> (Array.isArray(e) ? e : e && e.fileList)} > { handleGetGCodeFileInfo(file) setTimeout(() => { onSuccess('ok') }, 0) }} > {gcodeParsing == true ? ( } /> ) : ( <>

Click or drag gcode file here.

Supported file extentions: .gcode, .gco, .g

)}
) }, { title: 'Details', key: 'details', content: ( <> { setUseFilamentSelectFilter(e.target.checked) }} > Filter ) }, { title: 'Summary', key: 'done', content: ( <> ) } ] return (
New G Code File
setNewGCodeFileFormValues((prevValues) => ({ ...prevValues, ...changedValues })) } initialValues={initialNewGCodeFileForm} >
{steps[currentStep].content}
{currentStep < steps.length - 1 && ( )} {currentStep === steps.length - 1 && ( )}
) } NewGCodeFile.propTypes = { reset: PropTypes.bool.isRequired, onOk: PropTypes.func.isRequired } export default NewGCodeFile