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
)
}
NewGCodeFile.propTypes = {
reset: PropTypes.bool.isRequired,
onOk: PropTypes.func.isRequired
}
export default NewGCodeFile