import React, { useState, useEffect } from 'react' import { useLocation } from 'react-router-dom' import axios from 'axios' import { Descriptions, Spin, Space, Button, message, Badge, Form, Typography, Flex, Input, Card, Collapse, Dropdown, Popover, Checkbox } from 'antd' import { LoadingOutlined, CaretLeftOutlined } from '@ant-design/icons' import IdText from '../../common/IdText.jsx' import { capitalizeFirstLetter } from '../../utils/Utils.js' import FilamentSelect from '../../common/FilamentSelect' import useCollapseState from '../../hooks/useCollapseState' import FilamentIcon from '../../../Icons/FilamentIcon' import TimeDisplay from '../../common/TimeDisplay.jsx' import ReloadIcon from '../../../Icons/ReloadIcon' import EditIcon from '../../../Icons/EditIcon.jsx' import XMarkIcon from '../../../Icons/XMarkIcon.jsx' import CheckIcon from '../../../Icons/CheckIcon.jsx' import config from '../../../../config.js' import AuditLogTable from '../../common/AuditLogTable.jsx' import DashboardNotes from '../../common/DashboardNotes.jsx' import BinIcon from '../../../Icons/BinIcon.jsx' import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx' import GCodeFileIcon from '../../../Icons/GCodeFileIcon.jsx' import NoteIcon from '../../../Icons/NoteIcon.jsx' import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx' const { Title, Text } = Typography const GCodeFileInfo = () => { const [gcodeFileData, setGCodeFileData] = useState(null) const [editLoading, setLoading] = useState(false) const [error, setError] = useState(null) const location = useLocation() const [messageApi, contextHolder] = message.useMessage() const gcodeFileId = new URLSearchParams(location.search).get('gcodeFileId') const [isEditing, setIsEditing] = useState(false) const [form] = Form.useForm() const [fetchLoading, setFetchLoading] = useState(true) const [collapseState, updateCollapseState] = useCollapseState( 'GCodeFileInfo', { info: true, preview: true } ) useEffect(() => { if (gcodeFileId) { fetchGCodeFileDetails() } }, [gcodeFileId]) useEffect(() => { if (gcodeFileData) { form.setFieldsValue({ name: gcodeFileData.name || '', filament: gcodeFileData.filament || { id: null, name: '' } }) } }, [gcodeFileData, form]) const fetchGCodeFileDetails = async () => { try { setFetchLoading(true) const response = await axios.get( `${config.backendUrl}/gcodefiles/${gcodeFileId}`, { headers: { Accept: 'application/json' }, withCredentials: true } ) setGCodeFileData(response.data) setError(null) } catch (err) { setError('Failed to fetch GCodeFile details') messageApi.error('Failed to fetch GCodeFile details') } finally { setFetchLoading(false) } } const startEditing = () => { setIsEditing(true) updateCollapseState('info', true) } const cancelEditing = () => { form.setFieldsValue({ name: gcodeFileData?.name || '', filament: gcodeFileData?.filament || { id: null, name: '' } }) setIsEditing(false) } const updateGCodeFileInfo = async () => { try { const values = await form.validateFields() setLoading(true) await axios.put( `${config.backendUrl}/gcodefiles/${gcodeFileId}`, values, { headers: { 'Content-Type': 'application/json' }, withCredentials: true } ) setGCodeFileData({ ...gcodeFileData, ...values }) setIsEditing(false) messageApi.success('GCode File information updated successfully') } catch (err) { if (err.errorFields) { return } console.error('Failed to update gcode file information:', err) messageApi.error('Failed to update gcode file information') } finally { fetchGCodeFileDetails() setLoading(false) } } const actionItems = { items: [ { label: 'Edit GCode File', key: 'edit', icon: }, { label: 'Delete GCode File', key: 'delete', icon: , danger: true }, { type: 'divider' }, { label: 'Reload GCode File', key: 'reload', icon: } ], onClick: ({ key }) => { if (key === 'reload') { fetchGCodeFileDetails() } } } const getViewDropdownItems = () => { const sections = [ { key: 'info', label: 'GCode File Information' }, { key: 'preview', label: 'GCode File Preview' }, { key: 'notes', label: 'Notes' }, { key: 'auditLogs', label: 'Audit Logs' } ] return ( {sections.map((section) => ( { updateCollapseState(section.key, e.target.checked) }} > {section.label} ))} ) } return ( <> {contextHolder} Actions View {isEditing ? ( <> } type='primary' onClick={updateGCodeFileInfo} loading={editLoading} disabled={editLoading} /> } onClick={cancelEditing} disabled={editLoading} /> > ) : ( } onClick={startEditing} /> )} {error ? ( {error || 'Print job not found'} } onClick={fetchGCodeFileDetails}> Retry ) : ( updateCollapseState('info', keys.length > 0) } expandIcon={({ isActive }) => ( )} className='no-h-padding-collapse no-t-padding-collapse' > GCode File Information } key='info' > } > {gcodeFileData?._id ? ( ) : ( n/a )} {gcodeFileData?.createdAt ? ( ) : ( n/a )} {isEditing ? ( ) : gcodeFileData?.name ? ( {gcodeFileData.name} ) : ( n/a )} {gcodeFileData?.updatedAt ? ( ) : ( n/a )} {isEditing ? ( ) : gcodeFileData?.filament ? ( ) : ( n/a )} {gcodeFileData?.filament ? ( ) : ( n/a )} {gcodeFileData?.gcodeFileInfo ?.estimatedPrintingTimeNormalMode ? ( { gcodeFileData.gcodeFileInfo .estimatedPrintingTimeNormalMode } ) : ( n/a )} {gcodeFileData?.cost ? ( {'£' + gcodeFileData.cost.toFixed(2)} ) : ( n/a )} {gcodeFileData?.gcodeFileInfo?.sparseInfillDensity ? ( {gcodeFileData.gcodeFileInfo.sparseInfillDensity} ) : ( n/a )} {gcodeFileData?.gcodeFileInfo?.sparseInfillPattern ? ( {capitalizeFirstLetter( gcodeFileData.gcodeFileInfo.sparseInfillPattern )} ) : ( n/a )} {gcodeFileData?.gcodeFileInfo?.filamentUsedMm ? ( {gcodeFileData.gcodeFileInfo.filamentUsedMm}mm ) : ( n/a )} {gcodeFileData?.gcodeFileInfo?.filamentUsedG ? ( {gcodeFileData.gcodeFileInfo.filamentUsedG}g ) : ( n/a )} {gcodeFileData?.gcodeFileInfo?.nozzleTemperature ? ( {gcodeFileData.gcodeFileInfo.nozzleTemperature}° ) : ( n/a )} {gcodeFileData?.gcodeFileInfo?.hotPlateTemp ? ( {gcodeFileData.gcodeFileInfo.hotPlateTemp}° ) : ( n/a )} {gcodeFileData?.gcodeFileInfo?.filamentSettingsId ? ( {gcodeFileData.gcodeFileInfo.filamentSettingsId.replaceAll( '"', '' )} ) : ( n/a )} {gcodeFileData?.gcodeFileInfo?.printSettingsId ? ( {gcodeFileData.gcodeFileInfo.printSettingsId.replaceAll( '"', '' )} ) : ( n/a )} updateCollapseState('preview', keys.length > 0) } expandIcon={({ isActive }) => ( )} className='no-h-padding-collapse' > GCode File Preview } key='preview' > }> {gcodeFileData?.gcodeFileInfo?.thumbnail ? ( ) : ( n/a )} updateCollapseState('notes', keys.length > 0) } expandIcon={({ isActive }) => ( )} className='no-h-padding-collapse' > Notes } key='notes' > updateCollapseState('auditLogs', keys.length > 0) } expandIcon={({ isActive }) => ( )} className='no-h-padding-collapse' > Audit Log } key='auditLogs' > )} > ) } export default GCodeFileInfo
{error || 'Print job not found'}