Compare commits
No commits in common. "c6c99bb02cb6c21eb59979153745a94e1e65aafc" and "c2f55a596784d6f210526e9be0709ec8ba1a998d" have entirely different histories.
c6c99bb02c
...
c2f55a5967
@ -43,7 +43,7 @@
|
|||||||
vertical-align: middle !important;
|
vertical-align: middle !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-typography-ellipsis-single-line > code {
|
.ant-typography-ellipsis-single-line >code {
|
||||||
vertical-align: top !important;
|
vertical-align: top !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,9 +55,7 @@
|
|||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
.electron-navigation-wrapper li,
|
.electron-navigation-wrapper li, .electron-navigation-wrapper button, .electron-navigation-wrapper .ant-tag{
|
||||||
.electron-navigation-wrapper button,
|
|
||||||
.electron-navigation-wrapper .ant-tag {
|
|
||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,8 +67,7 @@
|
|||||||
padding-inline: 10px;
|
padding-inline: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.electron-sidebar .ant-menu-item,
|
.electron-sidebar .ant-menu-item, .electron-sidebar .ant-menu-submenu-title {
|
||||||
.electron-sidebar .ant-menu-submenu-title {
|
|
||||||
height: 32.5px !important;
|
height: 32.5px !important;
|
||||||
line-height: 32.5px !important;
|
line-height: 32.5px !important;
|
||||||
}
|
}
|
||||||
@ -80,10 +77,11 @@
|
|||||||
min-width: 55px !important;
|
min-width: 55px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-modal .ant-modal-footer {
|
.loading-modal .ant-modal-footer {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--unit-100vh: 100vh;
|
--unit-100vh: 100vh;
|
||||||
}
|
}
|
||||||
@ -93,8 +91,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-cards-header .ant-table-tbody {
|
.dashboard-cards-header .ant-table-tbody {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu-overflow-item-rest::after {
|
.ant-menu-overflow-item-rest::after {
|
||||||
@ -227,21 +225,19 @@ body {
|
|||||||
}
|
}
|
||||||
/* --- End of src/components/Dashboard/common/DashboardSidebar.css --- */
|
/* --- End of src/components/Dashboard/common/DashboardSidebar.css --- */
|
||||||
|
|
||||||
.objectTableDescritions > .ant-descriptions-view > table {
|
.objectTableDescritions >.ant-descriptions-view > table {
|
||||||
table-layout: fixed !important;
|
table-layout: fixed !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.objectTableDescritions
|
.objectTableDescritions >.ant-descriptions-view .ant-descriptions-row >.ant-descriptions-item-label {
|
||||||
> .ant-descriptions-view
|
|
||||||
.ant-descriptions-row
|
|
||||||
> .ant-descriptions-item-label {
|
|
||||||
width: 35%;
|
width: 35%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.farmcontrol-splitter > .ant-splitter-bar {
|
.farmcontrol-splitter > .ant-splitter-bar {
|
||||||
margin: 8px;
|
margin: 8px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.dark-mode {
|
.dark-mode {
|
||||||
--sb-track-color: #1d1d1d;
|
--sb-track-color: #1d1d1d;
|
||||||
--sb-thumb-color: #848484;
|
--sb-thumb-color: #848484;
|
||||||
@ -253,31 +249,32 @@ body {
|
|||||||
--sb-size: 8px;
|
--sb-size: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar:vertical {
|
::-webkit-scrollbar:vertical {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar:horizontal {
|
::-webkit-scrollbar:horizontal {
|
||||||
height: 8px;
|
height: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Track */
|
/* Track */
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle */
|
/* Handle */
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: #8888881a;
|
background: #8888881a;
|
||||||
border: 2px solid rgba(0, 0, 0, 0);
|
border: 2px solid rgba(0, 0, 0, 0);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
transition: all 1s;
|
transition: all 1s;
|
||||||
-moz-transition: all 1s;
|
-moz-transition: all 1s;
|
||||||
-webkit-transition: all 1s;
|
-webkit-transition: all 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-corner {
|
::-webkit-scrollbar-corner {
|
||||||
@ -294,9 +291,9 @@ body {
|
|||||||
|
|
||||||
/* Handle on hover */
|
/* Handle on hover */
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: #5555551f;
|
background: #5555551f;
|
||||||
border: 2px solid rgba(0, 0, 0, 0);
|
border: 2px solid rgba(0, 0, 0, 0);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:vertical:hover {
|
::-webkit-scrollbar-thumb:vertical:hover {
|
||||||
@ -309,8 +306,4 @@ body {
|
|||||||
|
|
||||||
.ant-table-body {
|
.ant-table-body {
|
||||||
scrollbar-color: auto;
|
scrollbar-color: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-select-selection-item .ant-tag {
|
|
||||||
margin-left: 1px !important;
|
|
||||||
}
|
|
||||||
855
package-lock.json
generated
855
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,27 @@
|
|||||||
import { useState, useContext, useEffect } from 'react'
|
import { useState, useContext, useEffect } from 'react'
|
||||||
import { Form, Flex, Descriptions, Alert } from 'antd'
|
import {
|
||||||
|
Form,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
Flex,
|
||||||
|
Steps,
|
||||||
|
Divider,
|
||||||
|
Descriptions,
|
||||||
|
Alert
|
||||||
|
} from 'antd'
|
||||||
|
import { useMediaQuery } from 'react-responsive'
|
||||||
import PropTypes from 'prop-types'
|
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 PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
||||||
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import ObjectSelect from '../../common/ObjectSelect'
|
import PrinterState from '../../common/StateDisplay'
|
||||||
import ObjectDisplay from '../../common/ObjectDisplay'
|
|
||||||
import WizardView from '../../common/WizardView'
|
const { Title } = Typography
|
||||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
|
||||||
|
|
||||||
const LoadFilamentStock = ({
|
const LoadFilamentStock = ({
|
||||||
onOk,
|
onOk,
|
||||||
@ -16,8 +29,7 @@ const LoadFilamentStock = ({
|
|||||||
printer = null,
|
printer = null,
|
||||||
filamentStockLoaded = false
|
filamentStockLoaded = false
|
||||||
}) => {
|
}) => {
|
||||||
const { connected, subscribeToObjectEvent, sendObjectAction } =
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
useContext(ApiServerContext)
|
|
||||||
|
|
||||||
LoadFilamentStock.propTypes = {
|
LoadFilamentStock.propTypes = {
|
||||||
onOk: PropTypes.func.isRequired,
|
onOk: PropTypes.func.isRequired,
|
||||||
@ -26,6 +38,8 @@ const LoadFilamentStock = ({
|
|||||||
filamentStockLoaded: PropTypes.bool
|
filamentStockLoaded: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { printServer } = useContext(PrintServerContext)
|
||||||
|
|
||||||
const initialLoadFilamentStockForm = {
|
const initialLoadFilamentStockForm = {
|
||||||
printer: printer,
|
printer: printer,
|
||||||
filamentStock: null
|
filamentStock: null
|
||||||
@ -33,7 +47,8 @@ const LoadFilamentStock = ({
|
|||||||
|
|
||||||
const [loadFilamentStockLoading, setLoadFilamentStockLoading] =
|
const [loadFilamentStockLoading, setLoadFilamentStockLoading] =
|
||||||
useState(false)
|
useState(false)
|
||||||
const [formValid, setFormValid] = useState(false)
|
const [currentStep, setCurrentStep] = useState(0)
|
||||||
|
const [nextEnabled, setNextEnabled] = useState(false)
|
||||||
const [currentTemperature, setCurrentTemperature] = useState(-1)
|
const [currentTemperature, setCurrentTemperature] = useState(-1)
|
||||||
const [targetTemperature, setTargetTemperature] = useState(0)
|
const [targetTemperature, setTargetTemperature] = useState(0)
|
||||||
const [filamentSensorDetected, setFilamentSensorDetected] =
|
const [filamentSensorDetected, setFilamentSensorDetected] =
|
||||||
@ -47,69 +62,77 @@ const LoadFilamentStock = ({
|
|||||||
loadFilamentStockForm
|
loadFilamentStockForm
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Add websocket temperature monitoring
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (printer?._id && connected) {
|
if (loadFilamentStockFormValues.printer) {
|
||||||
const temperatureEventUnsubscribe = subscribeToObjectEvent(
|
const params = {
|
||||||
printer._id,
|
printerId: loadFilamentStockFormValues.printer._id,
|
||||||
'printer',
|
objects: {
|
||||||
'temperature',
|
extruder: null,
|
||||||
(event) => {
|
'filament_switch_sensor fsensor': null
|
||||||
if (event.data?.extruder?.current) {
|
|
||||||
setCurrentTemperature(event.data.extruder.current)
|
|
||||||
}
|
|
||||||
if (event.data?.extruder?.target) {
|
|
||||||
setTargetTemperature(event.data.extruder.target)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
const filamentStockEventUnsubscribe = subscribeToObjectEvent(
|
|
||||||
printer._id,
|
const notifyStatusUpdate = (statusUpdate) => {
|
||||||
'printer',
|
if (statusUpdate?.extruder?.temperature !== undefined) {
|
||||||
'filamentSensor',
|
setCurrentTemperature(statusUpdate.extruder.temperature)
|
||||||
(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 () => {
|
return () => {
|
||||||
if (temperatureEventUnsubscribe) temperatureEventUnsubscribe()
|
printServer.off('notify_status_update', notifyStatusUpdate)
|
||||||
if (filamentStockEventUnsubscribe) filamentStockEventUnsubscribe()
|
printServer.emit('printer.objects.unsubscribe', params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [printer?._id, connected])
|
}, [printServer, loadFilamentStockFormValues.printer])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Validate form fields
|
|
||||||
loadFilamentStockForm
|
loadFilamentStockForm
|
||||||
.validateFields({
|
.validateFields({
|
||||||
validateOnly: true
|
validateOnly: true
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => setNextEnabled(filamentSensorDetected))
|
||||||
const hasPrinter = Boolean(loadFilamentStockFormValues.printer)
|
.catch(() => setNextEnabled(false))
|
||||||
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,
|
loadFilamentStockForm,
|
||||||
loadFilamentStockFormUpdateValues,
|
loadFilamentStockFormUpdateValues,
|
||||||
loadFilamentStockFormValues,
|
filamentSensorDetected
|
||||||
|
])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
filamentSensorDetected == true &&
|
||||||
|
currentTemperature >= targetTemperature
|
||||||
|
) {
|
||||||
|
setNextEnabled(filamentSensorDetected)
|
||||||
|
if (currentStep == 0) {
|
||||||
|
setCurrentStep(1)
|
||||||
|
}
|
||||||
|
} else if (filamentSensorDetected == false) {
|
||||||
|
setCurrentStep(0)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
filamentSensorDetected,
|
filamentSensorDetected,
|
||||||
|
targetTemperature,
|
||||||
currentTemperature,
|
currentTemperature,
|
||||||
targetTemperature
|
currentStep
|
||||||
])
|
])
|
||||||
|
|
||||||
const summaryItems = [
|
const summaryItems = [
|
||||||
@ -117,9 +140,8 @@ const LoadFilamentStock = ({
|
|||||||
key: 'filamentStock',
|
key: 'filamentStock',
|
||||||
label: 'Stock',
|
label: 'Stock',
|
||||||
children: loadFilamentStockFormValues.filamentStock ? (
|
children: loadFilamentStockFormValues.filamentStock ? (
|
||||||
<ObjectDisplay
|
<FilamentStockDisplay
|
||||||
objectType='filamentStock'
|
filamentStock={loadFilamentStockFormValues.filamentStock}
|
||||||
object={loadFilamentStockFormValues.filamentStock}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
'n/a'
|
'n/a'
|
||||||
@ -129,12 +151,9 @@ const LoadFilamentStock = ({
|
|||||||
key: 'printer',
|
key: 'printer',
|
||||||
label: 'Printer',
|
label: 'Printer',
|
||||||
children: loadFilamentStockFormValues.printer ? (
|
children: loadFilamentStockFormValues.printer ? (
|
||||||
<ObjectDisplay
|
<PrinterState printer={loadFilamentStockFormValues.printer} />
|
||||||
objectType='printer'
|
|
||||||
object={loadFilamentStockFormValues.printer}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
'n/a'
|
'n/a>'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -150,16 +169,10 @@ const LoadFilamentStock = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Set the extruder temperature
|
// Set the extruder temperature
|
||||||
await sendObjectAction(
|
await printServer.emit('printer.filamentstock.load', {
|
||||||
loadFilamentStockFormValues.printer._id,
|
printerId: loadFilamentStockFormValues.printer._id,
|
||||||
'printer',
|
filamentStockId: loadFilamentStockFormValues.filamentStock._id
|
||||||
{
|
})
|
||||||
type: 'loadFilamentStock',
|
|
||||||
data: {
|
|
||||||
filamentStock: loadFilamentStockFormValues.filamentStock
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
onOk()
|
onOk()
|
||||||
} finally {
|
} finally {
|
||||||
setLoadFilamentStockLoading(false)
|
setLoadFilamentStockLoading(false)
|
||||||
@ -183,7 +196,7 @@ const LoadFilamentStock = ({
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<ObjectSelect type='printer' checkable={false} />
|
<PrinterSelect checkable={false} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{targetTemperature == 0 ? (
|
{targetTemperature == 0 ? (
|
||||||
<Alert
|
<Alert
|
||||||
@ -213,9 +226,10 @@ const LoadFilamentStock = ({
|
|||||||
|
|
||||||
{loadFilamentStockFormValues.printer ? (
|
{loadFilamentStockFormValues.printer ? (
|
||||||
<PrinterTemperaturePanel
|
<PrinterTemperaturePanel
|
||||||
showBed={false}
|
showHeatedBed={false}
|
||||||
showMoreInfo={false}
|
showMoreInfo={false}
|
||||||
id={loadFilamentStockFormValues.printer._id}
|
printerId={loadFilamentStockFormValues.printer._id}
|
||||||
|
shouldUnsubscribe={false}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -236,7 +250,7 @@ const LoadFilamentStock = ({
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<ObjectSelect type='filamentStock' />
|
<FilamentStockSelect />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@ -253,28 +267,75 @@ const LoadFilamentStock = ({
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Flex gap={'middle'}>
|
||||||
name='loadFilamentStock'
|
{!isMobile && (
|
||||||
autoComplete='off'
|
<div style={{ minWidth: '160px' }}>
|
||||||
form={loadFilamentStockForm}
|
<Steps
|
||||||
onFinish={handleLoadFilamentStock}
|
current={currentStep}
|
||||||
onValuesChange={(changedValues) =>
|
items={steps}
|
||||||
setLoadFilamentStockFormValues((prevValues) => ({
|
direction='vertical'
|
||||||
...prevValues,
|
style={{ width: 'fit-content' }}
|
||||||
...changedValues
|
/>
|
||||||
}))
|
</div>
|
||||||
}
|
)}
|
||||||
initialValues={initialLoadFilamentStockForm}
|
|
||||||
>
|
{!isMobile && <Divider type={'vertical'} style={{ height: 'unset' }} />}
|
||||||
<WizardView
|
|
||||||
title='Load Filament Stock'
|
<Flex vertical={'true'} style={{ flexGrow: 1 }} gap='middle'>
|
||||||
steps={steps}
|
<Title level={2} style={{ marginTop: 0, marginBottom: 4 }}>
|
||||||
onSubmit={() => loadFilamentStockForm.submit()}
|
Load Filament Stock
|
||||||
formValid={formValid}
|
</Title>
|
||||||
loading={loadFilamentStockLoading}
|
<Form
|
||||||
submitText='Done'
|
name='basic'
|
||||||
/>
|
autoComplete='off'
|
||||||
</Form>
|
form={loadFilamentStockForm}
|
||||||
|
onFinish={handleLoadFilamentStock}
|
||||||
|
onValuesChange={(changedValues) =>
|
||||||
|
setLoadFilamentStockFormValues((prevValues) => ({
|
||||||
|
...prevValues,
|
||||||
|
...changedValues
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
initialValues={initialLoadFilamentStockForm}
|
||||||
|
>
|
||||||
|
<div style={{ minHeight: '260px' }}>{steps[currentStep].content}</div>
|
||||||
|
|
||||||
|
<Flex justify={'end'}>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
margin: '0 8px'
|
||||||
|
}}
|
||||||
|
onClick={() => setCurrentStep(currentStep - 1)}
|
||||||
|
disabled={!(currentStep > 0)}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
|
{currentStep < steps.length - 1 && (
|
||||||
|
<Button
|
||||||
|
type='primary'
|
||||||
|
disabled={!nextEnabled}
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentStep(currentStep + 1)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{currentStep === steps.length - 1 && (
|
||||||
|
<Button
|
||||||
|
type='primary'
|
||||||
|
loading={loadFilamentStockLoading}
|
||||||
|
onClick={() => {
|
||||||
|
loadFilamentStockForm.submit()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Done
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Form>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,30 +1,34 @@
|
|||||||
import { useState, useContext, useEffect } from 'react'
|
import { useState, useContext, useEffect } from 'react'
|
||||||
import { Form, Flex, Alert } from 'antd'
|
import { Form, Button, Typography, Flex, Steps, Divider, Alert } from 'antd'
|
||||||
|
import { useMediaQuery } from 'react-responsive'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import { PrintServerContext } from '../../context/PrintServerContext'
|
||||||
|
|
||||||
import ObjectSelect from '../../common/ObjectSelect'
|
import PrinterSelect from '../../common/PrinterSelect'
|
||||||
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
import PrinterTemperaturePanel from '../../common/PrinterTemperaturePanel'
|
||||||
import WizardView from '../../common/WizardView'
|
|
||||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
|
||||||
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
|
const { Title } = Typography
|
||||||
|
|
||||||
const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
||||||
const { connected, subscribeToObjectEvent, sendObjectAction } =
|
|
||||||
useContext(ApiServerContext)
|
|
||||||
UnloadFilamentStock.propTypes = {
|
UnloadFilamentStock.propTypes = {
|
||||||
onOk: PropTypes.func.isRequired,
|
onOk: PropTypes.func.isRequired,
|
||||||
reset: PropTypes.bool.isRequired,
|
reset: PropTypes.bool.isRequired,
|
||||||
printer: PropTypes.object
|
printer: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { printServer } = useContext(PrintServerContext)
|
||||||
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
|
|
||||||
const initialUnloadFilamentStockForm = {
|
const initialUnloadFilamentStockForm = {
|
||||||
printer: printer
|
printer: printer
|
||||||
}
|
}
|
||||||
|
|
||||||
const [unloadFilamentStockLoading, setUnloadFilamentStockLoading] =
|
const [unloadFilamentStockLoading, setUnloadFilamentStockLoading] =
|
||||||
useState(false)
|
useState(false)
|
||||||
const [formValid, setFormValid] = useState(false)
|
const [currentStep, setCurrentStep] = useState(0)
|
||||||
|
const [nextEnabled, setNextEnabled] = useState(false)
|
||||||
const [currentTemperature, setCurrentTemperature] = useState(-1)
|
const [currentTemperature, setCurrentTemperature] = useState(-1)
|
||||||
const [targetTemperature, setTargetTemperature] = useState(0)
|
const [targetTemperature, setTargetTemperature] = useState(0)
|
||||||
const [filamentSensorDetected, setFilamentSensorDetected] = useState(true)
|
const [filamentSensorDetected, setFilamentSensorDetected] = useState(true)
|
||||||
@ -32,35 +36,46 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
|||||||
const [unloadFilamentStockFormValues, setUnloadFilamentStockFormValues] =
|
const [unloadFilamentStockFormValues, setUnloadFilamentStockFormValues] =
|
||||||
useState(initialUnloadFilamentStockForm)
|
useState(initialUnloadFilamentStockForm)
|
||||||
|
|
||||||
|
// Add websocket temperature monitoring
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (printer?._id && connected) {
|
if (unloadFilamentStockFormValues.printer) {
|
||||||
const temperatureEventUnsubscribe = subscribeToObjectEvent(
|
const params = {
|
||||||
printer._id,
|
printerId: unloadFilamentStockFormValues.printer._id,
|
||||||
'printer',
|
objects: {
|
||||||
'temperature',
|
extruder: null,
|
||||||
(event) => {
|
'filament_switch_sensor fsensor': null
|
||||||
if (event.data?.extruder?.current) {
|
|
||||||
setCurrentTemperature(event.data.extruder.current)
|
|
||||||
}
|
|
||||||
if (event.data?.extruder?.target) {
|
|
||||||
setTargetTemperature(event.data.extruder.target)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
const filamentStockEventUnsubscribe = subscribeToObjectEvent(
|
|
||||||
printer._id,
|
const notifyStatusUpdate = (statusUpdate) => {
|
||||||
'printer',
|
if (statusUpdate?.extruder?.temperature !== undefined) {
|
||||||
'filamentSensor',
|
setCurrentTemperature(statusUpdate.extruder.temperature)
|
||||||
(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 () => {
|
return () => {
|
||||||
if (temperatureEventUnsubscribe) temperatureEventUnsubscribe()
|
printServer.off('notify_status_update', notifyStatusUpdate)
|
||||||
if (filamentStockEventUnsubscribe) filamentStockEventUnsubscribe()
|
printServer.emit('printer.objects.unsubscribe', params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [printer?._id, connected])
|
}, [printServer, unloadFilamentStockFormValues.printer])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (reset) {
|
if (reset) {
|
||||||
@ -75,14 +90,14 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Only enable next if we have a printer selected, we're not loading, and we've reached target temperature
|
// Only enable next if we have a printer selected, we're not loading, and we've reached target temperature
|
||||||
setFormValid(
|
setNextEnabled(
|
||||||
Boolean(unloadFilamentStockFormValues.printer) &&
|
Boolean(unloadFilamentStockFormValues.printer) &&
|
||||||
!unloadFilamentStockLoading &&
|
!unloadFilamentStockLoading &&
|
||||||
currentTemperature + 1 > targetTemperature &&
|
currentTemperature + 1 > targetTemperature &&
|
||||||
targetTemperature != 0
|
targetTemperature != 0
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.catch(() => setFormValid(false))
|
.catch(() => setNextEnabled(false))
|
||||||
}, [
|
}, [
|
||||||
unloadFilamentStockForm,
|
unloadFilamentStockForm,
|
||||||
unloadFilamentStockFormValues,
|
unloadFilamentStockFormValues,
|
||||||
@ -94,13 +109,10 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
|||||||
const handleUnloadFilamentStock = async () => {
|
const handleUnloadFilamentStock = async () => {
|
||||||
setUnloadFilamentStockLoading(true)
|
setUnloadFilamentStockLoading(true)
|
||||||
// Send G-code to retract the filament
|
// Send G-code to retract the filament
|
||||||
await sendObjectAction(
|
await printServer.emit('printer.gcode.script', {
|
||||||
unloadFilamentStockFormValues.printer._id,
|
printerId: unloadFilamentStockFormValues.printer._id,
|
||||||
'printer',
|
script: `_CLIENT_LINEAR_MOVE E=-200 F=1000`
|
||||||
{
|
})
|
||||||
type: 'unloadFilamentStock'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
//setUnloadFilamentStockLoading(false)
|
//setUnloadFilamentStockLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +140,7 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<ObjectSelect type='printer' checkable={false} />
|
<PrinterSelect checkable={false} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{unloadFilamentStockLoading == false ? (
|
{unloadFilamentStockLoading == false ? (
|
||||||
@ -170,11 +182,11 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{unloadFilamentStockFormValues.printer?._id ? (
|
{unloadFilamentStockFormValues.printer ? (
|
||||||
<PrinterTemperaturePanel
|
<PrinterTemperaturePanel
|
||||||
showBed={false}
|
showHeatedBed={false}
|
||||||
showMoreInfo={false}
|
showMoreInfo={false}
|
||||||
id={unloadFilamentStockFormValues.printer._id}
|
printerId={unloadFilamentStockFormValues.printer._id}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Flex>
|
||||||
@ -183,28 +195,65 @@ const UnloadFilamentStock = ({ onOk, reset, printer = null }) => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Flex gap={'middle'}>
|
||||||
name='unloadFilamentStock'
|
{!isMobile && (
|
||||||
autoComplete='off'
|
<div style={{ minWidth: '160px' }}>
|
||||||
form={unloadFilamentStockForm}
|
<Steps
|
||||||
onFinish={handleUnloadFilamentStock}
|
current={currentStep}
|
||||||
onValuesChange={(changedValues) =>
|
items={steps}
|
||||||
setUnloadFilamentStockFormValues((prevValues) => ({
|
direction='vertical'
|
||||||
...prevValues,
|
style={{ width: 'fit-content' }}
|
||||||
...changedValues
|
/>
|
||||||
}))
|
</div>
|
||||||
}
|
)}
|
||||||
initialValues={initialUnloadFilamentStockForm}
|
|
||||||
>
|
{!isMobile && <Divider type={'vertical'} style={{ height: 'unset' }} />}
|
||||||
<WizardView
|
|
||||||
title='Unload Filament Stock'
|
<Flex vertical={'true'} style={{ flexGrow: 1 }} gap='middle'>
|
||||||
steps={steps}
|
<Title level={2} style={{ marginTop: 0, marginBottom: 4 }}>
|
||||||
onSubmit={() => unloadFilamentStockForm.submit()}
|
Unload Filament Stock
|
||||||
formValid={formValid}
|
</Title>
|
||||||
loading={unloadFilamentStockLoading}
|
<Form
|
||||||
submitText={unloadFilamentStockLoading ? 'Unloading...' : 'Unload'}
|
name='unloadFilamentStock'
|
||||||
/>
|
autoComplete='off'
|
||||||
</Form>
|
form={unloadFilamentStockForm}
|
||||||
|
onFinish={handleUnloadFilamentStock}
|
||||||
|
onValuesChange={(changedValues) =>
|
||||||
|
setUnloadFilamentStockFormValues((prevValues) => ({
|
||||||
|
...prevValues,
|
||||||
|
...changedValues
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
initialValues={initialUnloadFilamentStockForm}
|
||||||
|
>
|
||||||
|
<div style={{ minHeight: '260px' }}>{steps[currentStep].content}</div>
|
||||||
|
|
||||||
|
<Flex justify={'end'}>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
margin: '0 8px'
|
||||||
|
}}
|
||||||
|
onClick={() => setCurrentStep(currentStep - 1)}
|
||||||
|
disabled={!(currentStep > 0)}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
|
{currentStep === steps.length - 1 && (
|
||||||
|
<Button
|
||||||
|
type='primary'
|
||||||
|
loading={unloadFilamentStockLoading}
|
||||||
|
disabled={!nextEnabled}
|
||||||
|
onClick={() => {
|
||||||
|
unloadFilamentStockForm.submit()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{unloadFilamentStockLoading ? 'Unloading...' : 'Unload'}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</Form>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,26 +3,12 @@ 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'
|
||||||
import TemplatePreview from '../../common/TemplatePreview'
|
import TemplatePreview from '../../common/TemplatePreview'
|
||||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
|
||||||
import { useContext, useRef, useState } from 'react'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
|
|
||||||
const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
|
const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
|
||||||
const { sendObjectAction, downloadTemplatePDF, formatFileName } =
|
|
||||||
useContext(ApiServerContext)
|
|
||||||
const [downloading, setDownloading] = useState(false)
|
|
||||||
|
|
||||||
// Capture initial default values so later prop changes don't re-initialize the form
|
|
||||||
const defaultValuesRef = useRef({
|
|
||||||
objectType: 'documentJob',
|
|
||||||
...defaultValues,
|
|
||||||
saveToFile: false
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NewObjectForm
|
<NewObjectForm
|
||||||
type={'documentJob'}
|
type={'documentJob'}
|
||||||
defaultValues={defaultValuesRef.current}
|
defaultValues={{ objectType: 'documentJob', ...defaultValues }}
|
||||||
>
|
>
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
||||||
const steps = [
|
const steps = [
|
||||||
@ -42,10 +28,6 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
const fileName =
|
|
||||||
formatFileName(
|
|
||||||
objectData?.name + ' ' + dayjs().format('YYYY-MM-DD HH:mm:ss')
|
|
||||||
) || 'document'
|
|
||||||
return (
|
return (
|
||||||
<WizardView
|
<WizardView
|
||||||
steps={steps}
|
steps={steps}
|
||||||
@ -53,10 +35,8 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
|
|||||||
title={'Print Document'}
|
title={'Print Document'}
|
||||||
formValid={formValid}
|
formValid={formValid}
|
||||||
loading={submitLoading}
|
loading={submitLoading}
|
||||||
disabled={downloading}
|
|
||||||
sideBarGrow={true}
|
|
||||||
sideBar={
|
sideBar={
|
||||||
<div style={{ minHeight: '500px', flexGrow: 1 }}>
|
<div style={{ minWidth: '400px', minHeight: '500px' }}>
|
||||||
<TemplatePreview
|
<TemplatePreview
|
||||||
objectData={objectData?.object}
|
objectData={objectData?.object}
|
||||||
documentTemplate={objectData?.documentTemplate}
|
documentTemplate={objectData?.documentTemplate}
|
||||||
@ -66,52 +46,10 @@ const NewDocumentJob = ({ onOk, defaultValues = {} }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
onSubmit={async () => {
|
onSubmit={() => {
|
||||||
const newDocumentJob = await handleSubmit()
|
handleSubmit()
|
||||||
if (newDocumentJob.sendToFile == true) {
|
onOk()
|
||||||
sendObjectAction(newDocumentJob._id, 'documentJob', {
|
|
||||||
type: 'print',
|
|
||||||
data: newDocumentJob
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (onOk) {
|
|
||||||
onOk()
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
actions={[
|
|
||||||
{
|
|
||||||
label: 'Download',
|
|
||||||
steps: ['required'],
|
|
||||||
loading: downloading == true,
|
|
||||||
disabled: downloading == true || submitLoading == true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: 'PDF',
|
|
||||||
key: 'pdf',
|
|
||||||
onClick: () => {
|
|
||||||
setDownloading(true)
|
|
||||||
downloadTemplatePDF(
|
|
||||||
objectData.documentTemplate._id,
|
|
||||||
objectData.documentTemplate.content,
|
|
||||||
objectData.object,
|
|
||||||
fileName,
|
|
||||||
() => {
|
|
||||||
setDownloading(false)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'PNG',
|
|
||||||
key: 'png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'JPEG',
|
|
||||||
key: 'jpeg'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { useRef, useState } from 'react'
|
import { useRef } from 'react'
|
||||||
import { Button, Flex, Space, Dropdown, message, Modal } from 'antd'
|
import { Button, Flex, Space, Dropdown } from 'antd'
|
||||||
import PlusIcon from '../../Icons/PlusIcon'
|
|
||||||
import ObjectTable from '../common/ObjectTable'
|
import ObjectTable from '../common/ObjectTable'
|
||||||
import ReloadIcon from '../../Icons/ReloadIcon'
|
import ReloadIcon from '../../Icons/ReloadIcon'
|
||||||
import useColumnVisibility from '../hooks/useColumnVisibility'
|
import useColumnVisibility from '../hooks/useColumnVisibility'
|
||||||
@ -8,12 +7,10 @@ import GridIcon from '../../Icons/GridIcon'
|
|||||||
import ListIcon from '../../Icons/ListIcon'
|
import ListIcon from '../../Icons/ListIcon'
|
||||||
import useViewMode from '../hooks/useViewMode'
|
import useViewMode from '../hooks/useViewMode'
|
||||||
import ColumnViewButton from '../common/ColumnViewButton'
|
import ColumnViewButton from '../common/ColumnViewButton'
|
||||||
import NewDocumentPrinter from './DocumentPrinters/NewDocumentPrinter'
|
|
||||||
|
|
||||||
const DocumentPrinters = () => {
|
const DocumentPrinters = () => {
|
||||||
const [messageApi, contextHolder] = message.useMessage()
|
|
||||||
const tableRef = useRef()
|
const tableRef = useRef()
|
||||||
const [newDocumentPrinterOpen, setNewDocumentPrinterOpen] = useState(false)
|
|
||||||
const [viewMode, setViewMode] = useViewMode('documentPrinter')
|
const [viewMode, setViewMode] = useViewMode('documentPrinter')
|
||||||
|
|
||||||
const [columnVisibility, setColumnVisibility] =
|
const [columnVisibility, setColumnVisibility] =
|
||||||
@ -21,12 +18,6 @@ const DocumentPrinters = () => {
|
|||||||
|
|
||||||
const actionItems = {
|
const actionItems = {
|
||||||
items: [
|
items: [
|
||||||
{
|
|
||||||
label: 'New Document Printer',
|
|
||||||
key: 'newDocumentPrinter',
|
|
||||||
icon: <PlusIcon />
|
|
||||||
},
|
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
{
|
||||||
label: 'Reload List',
|
label: 'Reload List',
|
||||||
key: 'reloadList',
|
key: 'reloadList',
|
||||||
@ -36,8 +27,6 @@ const DocumentPrinters = () => {
|
|||||||
onClick: ({ key }) => {
|
onClick: ({ key }) => {
|
||||||
if (key === 'reloadList') {
|
if (key === 'reloadList') {
|
||||||
tableRef.current?.reload()
|
tableRef.current?.reload()
|
||||||
} else if (key === 'newDocumentPrinter') {
|
|
||||||
setNewDocumentPrinterOpen(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +34,6 @@ const DocumentPrinters = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex vertical={'true'} gap='large'>
|
<Flex vertical={'true'} gap='large'>
|
||||||
{contextHolder}
|
|
||||||
<Flex justify={'space-between'}>
|
<Flex justify={'space-between'}>
|
||||||
<Space size='small'>
|
<Space size='small'>
|
||||||
<Dropdown menu={actionItems}>
|
<Dropdown menu={actionItems}>
|
||||||
@ -74,22 +62,6 @@ const DocumentPrinters = () => {
|
|||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Modal
|
|
||||||
open={newDocumentPrinterOpen}
|
|
||||||
onCancel={() => setNewDocumentPrinterOpen(false)}
|
|
||||||
footer={null}
|
|
||||||
destroyOnHidden={true}
|
|
||||||
width={700}
|
|
||||||
>
|
|
||||||
<NewDocumentPrinter
|
|
||||||
onOk={() => {
|
|
||||||
setNewDocumentPrinterOpen(false)
|
|
||||||
messageApi.success('New note type created successfully.')
|
|
||||||
tableRef.current?.reload()
|
|
||||||
}}
|
|
||||||
reset={!newDocumentPrinterOpen}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useRef, useState } from 'react'
|
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
import { Space, Flex, Card } from 'antd'
|
import { Space, Flex, Card } from 'antd'
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
@ -26,8 +25,6 @@ log.setLevel(config.logLevel)
|
|||||||
|
|
||||||
const DocumentPrinterInfo = () => {
|
const DocumentPrinterInfo = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const objectFormRef = useRef(null)
|
|
||||||
const actionHandlerRef = useRef(null)
|
|
||||||
const documentPrinterId = new URLSearchParams(location.search).get(
|
const documentPrinterId = new URLSearchParams(location.search).get(
|
||||||
'documentPrinterId'
|
'documentPrinterId'
|
||||||
)
|
)
|
||||||
@ -35,125 +32,122 @@ const DocumentPrinterInfo = () => {
|
|||||||
'DocumentPrinterInfo',
|
'DocumentPrinterInfo',
|
||||||
{
|
{
|
||||||
info: true,
|
info: true,
|
||||||
stocks: true,
|
|
||||||
notes: true,
|
notes: true,
|
||||||
auditLogs: true
|
auditLogs: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const [objectFormState, setEditFormState] = useState({
|
|
||||||
isEditing: false,
|
|
||||||
editLoading: false,
|
|
||||||
formValid: false,
|
|
||||||
locked: false,
|
|
||||||
loading: false,
|
|
||||||
objectData: {}
|
|
||||||
})
|
|
||||||
|
|
||||||
const actions = {
|
|
||||||
reload: () => {
|
|
||||||
objectFormRef?.current.handleFetchObject()
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
edit: () => {
|
|
||||||
objectFormRef?.current.startEditing()
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
cancelEdit: () => {
|
|
||||||
objectFormRef?.current.cancelEditing()
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
finishEdit: () => {
|
|
||||||
objectFormRef?.current.handleUpdate()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<ObjectForm
|
||||||
<Flex
|
id={documentPrinterId}
|
||||||
gap='large'
|
type='documentPrinter'
|
||||||
vertical='true'
|
style={{ height: '100%' }}
|
||||||
style={{
|
>
|
||||||
maxHeight: '100%',
|
{({
|
||||||
minHeight: 0
|
loading,
|
||||||
}}
|
isEditing,
|
||||||
>
|
startEditing,
|
||||||
<Flex justify={'space-between'}>
|
cancelEditing,
|
||||||
<Space size='middle'>
|
handleUpdate,
|
||||||
<Space size='small'>
|
formValid,
|
||||||
<ObjectActions
|
objectData,
|
||||||
type='documentPrinter'
|
editLoading,
|
||||||
id={documentPrinterId}
|
lock,
|
||||||
disabled={objectFormState.loading}
|
fetchObject
|
||||||
objectData={objectFormState.objectData}
|
}) => {
|
||||||
/>
|
// Define actions for ActionHandler
|
||||||
<ViewButton
|
const actions = {
|
||||||
disabled={objectFormState.loading}
|
reload: () => {
|
||||||
items={[
|
fetchObject()
|
||||||
{
|
return true
|
||||||
key: 'info',
|
},
|
||||||
label: 'DocumentPrinter Information'
|
edit: () => {
|
||||||
},
|
startEditing()
|
||||||
{ key: 'notes', label: 'Notes' },
|
return false
|
||||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
},
|
||||||
]}
|
cancelEdit: () => {
|
||||||
visibleState={collapseState}
|
cancelEditing()
|
||||||
updateVisibleState={updateCollapseState}
|
return true
|
||||||
/>
|
},
|
||||||
<DocumentPrintButton
|
finishEdit: () => {
|
||||||
type='documentPrinter'
|
handleUpdate()
|
||||||
objectData={objectFormState.objectData}
|
return true
|
||||||
disabled={objectFormState.loading}
|
}
|
||||||
/>
|
}
|
||||||
</Space>
|
|
||||||
<LockIndicator lock={objectFormState.lock} />
|
|
||||||
</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>
|
|
||||||
|
|
||||||
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
return (
|
||||||
<Flex vertical gap={'large'}>
|
<ActionHandler actions={actions} loading={loading}>
|
||||||
<ActionHandler
|
{({ callAction }) => (
|
||||||
actions={actions}
|
<Flex
|
||||||
loading={objectFormState.loading}
|
gap='large'
|
||||||
ref={actionHandlerRef}
|
vertical='true'
|
||||||
>
|
style={{
|
||||||
<InfoCollapse
|
maxHeight: '100%',
|
||||||
title='Document Printer Information'
|
minHeight: 0
|
||||||
icon={<InfoCircleIcon />}
|
}}
|
||||||
active={collapseState.info}
|
|
||||||
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
|
||||||
collapseKey='info'
|
|
||||||
>
|
>
|
||||||
<ObjectForm
|
<Flex justify={'space-between'}>
|
||||||
id={documentPrinterId}
|
<Space size='middle'>
|
||||||
type='documentPrinter'
|
<Space size='small'>
|
||||||
style={{ height: '100%' }}
|
<ObjectActions
|
||||||
ref={objectFormRef}
|
type='documentPrinter'
|
||||||
onStateChange={(state) => {
|
id={documentPrinterId}
|
||||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
disabled={loading}
|
||||||
}}
|
objectData={objectData}
|
||||||
>
|
/>
|
||||||
{({ loading, isEditing, objectData }) => {
|
<ViewButton
|
||||||
return (
|
disabled={loading}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'info',
|
||||||
|
label: 'DocumentPrinter Information'
|
||||||
|
},
|
||||||
|
{ key: 'stocks', label: 'DocumentPrinter Stocks' },
|
||||||
|
{ key: 'notes', label: 'Notes' },
|
||||||
|
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
<DocumentPrintButton
|
||||||
|
type='documentPrinter'
|
||||||
|
objectData={objectData}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
<LockIndicator lock={lock} />
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<EditButtons
|
||||||
|
isEditing={isEditing}
|
||||||
|
handleUpdate={() => {
|
||||||
|
callAction('finishEdit')
|
||||||
|
}}
|
||||||
|
cancelEditing={() => {
|
||||||
|
callAction('cancelEdit')
|
||||||
|
}}
|
||||||
|
startEditing={() => {
|
||||||
|
callAction('edit')
|
||||||
|
}}
|
||||||
|
editLoading={editLoading}
|
||||||
|
formValid={formValid}
|
||||||
|
disabled={lock?.locked || loading}
|
||||||
|
loading={editLoading}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
||||||
|
<Flex vertical gap={'large'}>
|
||||||
|
<InfoCollapse
|
||||||
|
title='Document Printer Information'
|
||||||
|
icon={<InfoCircleIcon />}
|
||||||
|
active={collapseState.info}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('info', expanded)
|
||||||
|
}
|
||||||
|
collapseKey='info'
|
||||||
|
>
|
||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
loading={loading}
|
loading={loading}
|
||||||
indicator={<LoadingOutlined />}
|
indicator={<LoadingOutlined />}
|
||||||
@ -161,47 +155,52 @@ const DocumentPrinterInfo = () => {
|
|||||||
type='documentPrinter'
|
type='documentPrinter'
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
</InfoCollapse>
|
||||||
}}
|
|
||||||
</ObjectForm>
|
|
||||||
</InfoCollapse>
|
|
||||||
</ActionHandler>
|
|
||||||
|
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Notes'
|
title='Notes'
|
||||||
icon={<NoteIcon />}
|
icon={<NoteIcon />}
|
||||||
active={collapseState.notes}
|
active={collapseState.notes}
|
||||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
onToggle={(expanded) =>
|
||||||
collapseKey='notes'
|
updateCollapseState('notes', expanded)
|
||||||
>
|
}
|
||||||
<Card>
|
collapseKey='notes'
|
||||||
<NotesPanel _id={documentPrinterId} type='documentPrinter' />
|
>
|
||||||
</Card>
|
<Card>
|
||||||
</InfoCollapse>
|
<NotesPanel
|
||||||
|
_id={documentPrinterId}
|
||||||
|
type='documentPrinter'
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</InfoCollapse>
|
||||||
|
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Audit Logs'
|
title='Audit Logs'
|
||||||
icon={<AuditLogIcon />}
|
icon={<AuditLogIcon />}
|
||||||
active={collapseState.auditLogs}
|
active={collapseState.auditLogs}
|
||||||
onToggle={(expanded) =>
|
onToggle={(expanded) =>
|
||||||
updateCollapseState('auditLogs', expanded)
|
updateCollapseState('auditLogs', expanded)
|
||||||
}
|
}
|
||||||
collapseKey='auditLogs'
|
collapseKey='auditLogs'
|
||||||
>
|
>
|
||||||
{objectFormState.loading ? (
|
{loading ? (
|
||||||
<InfoCollapsePlaceholder />
|
<InfoCollapsePlaceholder />
|
||||||
) : (
|
) : (
|
||||||
<ObjectTable
|
<ObjectTable
|
||||||
type='auditLog'
|
type='auditLog'
|
||||||
masterFilter={{ 'parent._id': documentPrinterId }}
|
masterFilter={{ 'parent._id': documentPrinterId }}
|
||||||
visibleColumns={{ _id: false, 'parent._id': false }}
|
visibleColumns={{ _id: false, 'parent._id': false }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
</Flex>
|
</Flex>
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
)}
|
||||||
|
</ActionHandler>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</ObjectForm>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,85 +0,0 @@
|
|||||||
import PropTypes from 'prop-types'
|
|
||||||
import ObjectInfo from '../../common/ObjectInfo'
|
|
||||||
import NewObjectForm from '../../common/NewObjectForm'
|
|
||||||
import WizardView from '../../common/WizardView'
|
|
||||||
|
|
||||||
const NewDocumentPrinter = ({ onOk }) => {
|
|
||||||
return (
|
|
||||||
<NewObjectForm
|
|
||||||
type={'documentPrinter'}
|
|
||||||
defaultValues={{ active: true, global: false }}
|
|
||||||
>
|
|
||||||
{({ handleSubmit, submitLoading, objectData, formValid }) => {
|
|
||||||
const steps = [
|
|
||||||
{
|
|
||||||
title: 'Required',
|
|
||||||
key: 'required',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='documentPrinter'
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing={true}
|
|
||||||
required={true}
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Optional',
|
|
||||||
key: 'optional',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='documentPrinter'
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
isEditing={true}
|
|
||||||
required={false}
|
|
||||||
visibleProperties={{ content: false, testObject: false }}
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Summary',
|
|
||||||
key: 'summary',
|
|
||||||
content: (
|
|
||||||
<ObjectInfo
|
|
||||||
type='documentPrinter'
|
|
||||||
column={1}
|
|
||||||
bordered={false}
|
|
||||||
visibleProperties={{
|
|
||||||
_id: false,
|
|
||||||
createdAt: false,
|
|
||||||
updatedAt: false
|
|
||||||
}}
|
|
||||||
isEditing={false}
|
|
||||||
objectData={objectData}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WizardView
|
|
||||||
steps={steps}
|
|
||||||
loading={submitLoading}
|
|
||||||
formValid={formValid}
|
|
||||||
title='New Document Printer'
|
|
||||||
onSubmit={() => {
|
|
||||||
handleSubmit()
|
|
||||||
onOk()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</NewObjectForm>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
NewDocumentPrinter.propTypes = {
|
|
||||||
onOk: PropTypes.func.isRequired,
|
|
||||||
reset: PropTypes.bool
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NewDocumentPrinter
|
|
||||||
@ -1,9 +1,19 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useMediaQuery } from 'react-responsive'
|
||||||
|
import { Typography, Flex, Steps, Divider } from 'antd'
|
||||||
|
|
||||||
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 NewObjectButtons from '../../common/NewObjectButtons'
|
||||||
|
|
||||||
|
const { Title } = Typography
|
||||||
|
|
||||||
const NewDocumentTemplate = ({ onOk }) => {
|
const NewDocumentTemplate = ({ onOk }) => {
|
||||||
|
const [currentStep, setCurrentStep] = useState(0)
|
||||||
|
|
||||||
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NewObjectForm
|
<NewObjectForm
|
||||||
type={'documentTemplate'}
|
type={'documentTemplate'}
|
||||||
@ -59,18 +69,44 @@ const NewDocumentTemplate = ({ onOk }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WizardView
|
<Flex gap='middle'>
|
||||||
steps={steps}
|
{!isMobile && (
|
||||||
loading={submitLoading}
|
<div style={{ minWidth: '160px' }}>
|
||||||
formValid={formValid}
|
<Steps
|
||||||
title='New Document Template'
|
current={currentStep}
|
||||||
onSubmit={() => {
|
items={steps}
|
||||||
handleSubmit()
|
direction='vertical'
|
||||||
onOk()
|
style={{ width: 'fit-content' }}
|
||||||
}}
|
/>
|
||||||
/>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isMobile && (
|
||||||
|
<Divider type='vertical' style={{ height: 'unset' }} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Flex vertical gap='middle' style={{ flexGrow: 1 }}>
|
||||||
|
<Title level={2} style={{ margin: 0 }}>
|
||||||
|
New Document Template
|
||||||
|
</Title>
|
||||||
|
<div style={{ minHeight: '260px', marginBottom: 8 }}>
|
||||||
|
{steps[currentStep].content}
|
||||||
|
</div>
|
||||||
|
<NewObjectButtons
|
||||||
|
currentStep={currentStep}
|
||||||
|
totalSteps={steps.length}
|
||||||
|
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
||||||
|
onNext={() => setCurrentStep((prev) => prev + 1)}
|
||||||
|
onSubmit={() => {
|
||||||
|
handleSubmit()
|
||||||
|
onOk()
|
||||||
|
}}
|
||||||
|
formValid={formValid}
|
||||||
|
submitLoading={submitLoading}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</NewObjectForm>
|
</NewObjectForm>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useState, useRef, useEffect, useContext } from 'react'
|
import { useState, useRef, useEffect, useContext } from 'react'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
import { Space, Flex, Card, Splitter, Divider, Modal } from 'antd'
|
import { Space, Flex, Card, Splitter, Divider } from 'antd'
|
||||||
import loglevel from 'loglevel'
|
import loglevel from 'loglevel'
|
||||||
import config from '../../../../config.js'
|
import config from '../../../../config.js'
|
||||||
import useCollapseState from '../../hooks/useCollapseState.js'
|
import useCollapseState from '../../hooks/useCollapseState.js'
|
||||||
@ -28,9 +28,6 @@ import { useMediaQuery } from 'react-responsive'
|
|||||||
import AlertsDisplay from '../../common/AlertsDisplay.jsx'
|
import AlertsDisplay from '../../common/AlertsDisplay.jsx'
|
||||||
import { ApiServerContext } from '../../context/ApiServerContext.jsx'
|
import { ApiServerContext } from '../../context/ApiServerContext.jsx'
|
||||||
|
|
||||||
import LoadFilamentStock from '../../Inventory/FilamentStocks/LoadFilamentStock.jsx'
|
|
||||||
import UnloadFilamentStock from '../../Inventory/FilamentStocks/UnloadFilamentStock.jsx'
|
|
||||||
|
|
||||||
const log = loglevel.getLogger('ControlPrinter')
|
const log = loglevel.getLogger('ControlPrinter')
|
||||||
log.setLevel(config.logLevel)
|
log.setLevel(config.logLevel)
|
||||||
|
|
||||||
@ -60,9 +57,6 @@ const ControlPrinter = () => {
|
|||||||
collapseState.movement
|
collapseState.movement
|
||||||
)
|
)
|
||||||
|
|
||||||
const [loadFilamentStockOpen, setLoadFilamentStockOpen] = useState(false)
|
|
||||||
const [unloadFilamentStockOpen, setUnloadFilamentStockOpen] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSideBarVisible(
|
setSideBarVisible(
|
||||||
collapseState.temperature ||
|
collapseState.temperature ||
|
||||||
@ -128,39 +122,6 @@ const ControlPrinter = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
},
|
|
||||||
pauseJob: () => {
|
|
||||||
if (connected == true) {
|
|
||||||
sendObjectAction(printerId, 'printer', {
|
|
||||||
type: 'pauseJob'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
|
|
||||||
resumeJob: () => {
|
|
||||||
if (connected == true) {
|
|
||||||
sendObjectAction(printerId, 'printer', {
|
|
||||||
type: 'resumeJob'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
cancelJob: () => {
|
|
||||||
if (connected == true) {
|
|
||||||
sendObjectAction(printerId, 'printer', {
|
|
||||||
type: 'cancelJob'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
loadFilamentStock: () => {
|
|
||||||
setLoadFilamentStockOpen(true)
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
unloadFilamentStock: () => {
|
|
||||||
setUnloadFilamentStockOpen(true)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,338 +146,298 @@ const ControlPrinter = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Flex
|
||||||
<Flex
|
gap='large'
|
||||||
gap='large'
|
vertical='true'
|
||||||
vertical='true'
|
style={{
|
||||||
style={{
|
maxHeight: '100%',
|
||||||
maxHeight: '100%',
|
minHeight: 0
|
||||||
minHeight: 0
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<Flex justify={'space-between'}>
|
||||||
<Flex justify={'space-between'}>
|
<Space size='middle'>
|
||||||
<Space size='middle'>
|
<Space size='small'>
|
||||||
<Space size='small'>
|
<ObjectActions
|
||||||
<ObjectActions
|
type='printer'
|
||||||
type='printer'
|
id={printerId}
|
||||||
id={printerId}
|
disabled={objectFormState.loading}
|
||||||
disabled={objectFormState.loading}
|
visibleActions={{ edit: false }}
|
||||||
visibleActions={{ edit: false }}
|
objectData={objectFormState.objectData}
|
||||||
objectData={objectFormState.objectData}
|
|
||||||
/>
|
|
||||||
<ViewButton
|
|
||||||
disabled={objectFormState.loading}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
key: 'printer',
|
|
||||||
label: 'Printer'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'job',
|
|
||||||
label: 'Job'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'subJob',
|
|
||||||
label: 'Sub Job'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'filamentStock',
|
|
||||||
label: 'Filament Stock'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'temperature',
|
|
||||||
label: 'Temperature'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'position',
|
|
||||||
label: 'Position'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'movement',
|
|
||||||
label: 'Movement'
|
|
||||||
},
|
|
||||||
{ key: 'notes', label: 'Notes' }
|
|
||||||
]}
|
|
||||||
visibleState={collapseState}
|
|
||||||
updateVisibleState={updateCollapseState}
|
|
||||||
/>
|
|
||||||
<AlertsDisplay alerts={objectFormState.objectData?.alerts} />
|
|
||||||
</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}
|
|
||||||
/>
|
/>
|
||||||
|
<ViewButton
|
||||||
|
disabled={objectFormState.loading}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'printer',
|
||||||
|
label: 'Printer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'job',
|
||||||
|
label: 'Job'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'subJob',
|
||||||
|
label: 'Sub Job'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'filamentStock',
|
||||||
|
label: 'Filament Stock'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'temperature',
|
||||||
|
label: 'Temperature'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'position',
|
||||||
|
label: 'Position'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'movement',
|
||||||
|
label: 'Movement'
|
||||||
|
},
|
||||||
|
{ key: 'notes', label: 'Notes' }
|
||||||
|
]}
|
||||||
|
visibleState={collapseState}
|
||||||
|
updateVisibleState={updateCollapseState}
|
||||||
|
/>
|
||||||
|
<AlertsDisplay alerts={objectFormState.objectData?.alerts} />
|
||||||
</Space>
|
</Space>
|
||||||
</Flex>
|
</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>
|
||||||
|
|
||||||
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
<div style={{ height: '100%', overflowY: 'scroll' }}>
|
||||||
<Flex vertical gap={'large'}>
|
<Flex vertical gap={'large'}>
|
||||||
<ActionHandler
|
<ActionHandler
|
||||||
actions={actions}
|
actions={actions}
|
||||||
loading={objectFormState.loading}
|
loading={objectFormState.loading}
|
||||||
ref={actionHandlerRef}
|
ref={actionHandlerRef}
|
||||||
>
|
>
|
||||||
<Flex vertical>
|
<Flex vertical>
|
||||||
<Splitter className={'farmcontrol-splitter'}>
|
<Splitter className={'farmcontrol-splitter'}>
|
||||||
<Splitter.Panel>
|
<Splitter.Panel>
|
||||||
<Flex vertical gap={'large'}>
|
<Flex vertical gap={'large'}>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title={'Printer'}
|
title={'Printer'}
|
||||||
icon={<PrinterIcon />}
|
icon={<PrinterIcon />}
|
||||||
collapseKey='printer'
|
collapseKey='printer'
|
||||||
active={collapseState.printer}
|
active={collapseState.printer}
|
||||||
onToggle={(expanded) =>
|
onToggle={(expanded) =>
|
||||||
updateCollapseState('printer', expanded)
|
updateCollapseState('printer', expanded)
|
||||||
}
|
}
|
||||||
|
>
|
||||||
|
<ObjectForm
|
||||||
|
id={printerId}
|
||||||
|
type='printer'
|
||||||
|
ref={objectFormRef}
|
||||||
|
onStateChange={(state) => {
|
||||||
|
console.log('Got edit form state change', state)
|
||||||
|
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
|
{({
|
||||||
|
loading: printerObjectLoading,
|
||||||
|
objectData: printerObjectData
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ObjectInfo
|
||||||
|
loading={printerObjectLoading}
|
||||||
|
column={sideBarVisible ? 1 : undefined}
|
||||||
|
visibleProperties={{
|
||||||
|
connectedAt: false,
|
||||||
|
vendor: false,
|
||||||
|
'vendor._id': false,
|
||||||
|
host: false,
|
||||||
|
'host._id': false,
|
||||||
|
'moonraker.port': false,
|
||||||
|
'moonraker.apiKey': false,
|
||||||
|
'moonraker.protocol': false,
|
||||||
|
'moonraker.host': false,
|
||||||
|
tags: false,
|
||||||
|
firmware: false,
|
||||||
|
alerts: false
|
||||||
|
}}
|
||||||
|
objectData={printerObjectData}
|
||||||
|
type='printer'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</ObjectForm>
|
||||||
|
</InfoCollapse>
|
||||||
|
<InfoCollapse
|
||||||
|
title={'Job'}
|
||||||
|
icon={<JobIcon />}
|
||||||
|
collapseKey='job'
|
||||||
|
active={collapseState.job}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('job', expanded)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{objectFormState.objectData?.currentJob?._id ? (
|
||||||
<ObjectForm
|
<ObjectForm
|
||||||
id={printerId}
|
id={objectFormState.objectData.currentJob._id}
|
||||||
type='printer'
|
type='job'
|
||||||
ref={objectFormRef}
|
onStateChange={() => {}}
|
||||||
onStateChange={(state) => {
|
|
||||||
console.log('Got edit form state change', state)
|
|
||||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{({
|
{({
|
||||||
loading: printerObjectLoading,
|
loading: jobObjectLoading,
|
||||||
objectData: printerObjectData
|
objectData: jobObjectData
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
loading={printerObjectLoading}
|
loading={jobObjectLoading}
|
||||||
column={sideBarVisible ? 1 : undefined}
|
column={sideBarVisible ? 1 : undefined}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
connectedAt: false,
|
printers: false,
|
||||||
vendor: false,
|
createdAt: false
|
||||||
'vendor._id': false,
|
|
||||||
host: false,
|
|
||||||
'host._id': false,
|
|
||||||
'moonraker.port': false,
|
|
||||||
'moonraker.apiKey': false,
|
|
||||||
'moonraker.protocol': false,
|
|
||||||
'moonraker.host': false,
|
|
||||||
tags: false,
|
|
||||||
firmware: false,
|
|
||||||
alerts: false,
|
|
||||||
online: false,
|
|
||||||
active: false,
|
|
||||||
currentFilamentStock: false,
|
|
||||||
'currentFilamentStock._id': false,
|
|
||||||
currentJob: false,
|
|
||||||
'currentJob._id': false,
|
|
||||||
currentSubJob: false,
|
|
||||||
'currentSubJob._id': false
|
|
||||||
}}
|
}}
|
||||||
objectData={printerObjectData}
|
objectData={jobObjectData}
|
||||||
type='printer'
|
type='job'
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</ObjectForm>
|
</ObjectForm>
|
||||||
</InfoCollapse>
|
) : (
|
||||||
<InfoCollapse
|
<MissingPlaceholder
|
||||||
title={'Job'}
|
message={'No job.'}
|
||||||
icon={<JobIcon />}
|
hasBackground={false}
|
||||||
collapseKey='job'
|
/>
|
||||||
active={collapseState.job}
|
)}
|
||||||
onToggle={(expanded) =>
|
</InfoCollapse>
|
||||||
updateCollapseState('job', expanded)
|
<InfoCollapse
|
||||||
}
|
title={'Sub Job'}
|
||||||
>
|
icon={<SubJobIcon />}
|
||||||
{objectFormState.objectData?.currentJob?._id ? (
|
collapseKey='subJob'
|
||||||
<ObjectForm
|
active={collapseState.subJob}
|
||||||
id={objectFormState.objectData.currentJob._id}
|
onToggle={(expanded) =>
|
||||||
type='job'
|
updateCollapseState('subJob', expanded)
|
||||||
onStateChange={() => {}}
|
}
|
||||||
>
|
|
||||||
{({
|
|
||||||
loading: jobObjectLoading,
|
|
||||||
objectData: jobObjectData
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<ObjectInfo
|
|
||||||
loading={jobObjectLoading}
|
|
||||||
column={sideBarVisible ? 1 : undefined}
|
|
||||||
visibleProperties={{
|
|
||||||
printers: false,
|
|
||||||
createdAt: false
|
|
||||||
}}
|
|
||||||
objectData={jobObjectData}
|
|
||||||
type='job'
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</ObjectForm>
|
|
||||||
) : (
|
|
||||||
<MissingPlaceholder
|
|
||||||
message={'No job.'}
|
|
||||||
hasBackground={false}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title={'Sub Job'}
|
|
||||||
icon={<SubJobIcon />}
|
|
||||||
collapseKey='subJob'
|
|
||||||
active={collapseState.subJob}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('subJob', expanded)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{objectFormState.objectData?.currentSubJob?._id ? (
|
|
||||||
<ObjectForm
|
|
||||||
id={objectFormState.objectData.currentSubJob._id}
|
|
||||||
type='subJob'
|
|
||||||
onStateChange={() => {}}
|
|
||||||
>
|
|
||||||
{({
|
|
||||||
loading: subJobObjectLoading,
|
|
||||||
objectData: subJobObjectData
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<ObjectInfo
|
|
||||||
loading={subJobObjectLoading}
|
|
||||||
column={sideBarVisible ? 1 : undefined}
|
|
||||||
visibleProperties={{
|
|
||||||
printers: false,
|
|
||||||
createdAt: false
|
|
||||||
}}
|
|
||||||
objectData={subJobObjectData}
|
|
||||||
type='subJob'
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</ObjectForm>
|
|
||||||
) : (
|
|
||||||
<MissingPlaceholder
|
|
||||||
message={'No sub job.'}
|
|
||||||
hasBackground={false}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</InfoCollapse>
|
|
||||||
<InfoCollapse
|
|
||||||
title={'Filament Stock'}
|
|
||||||
icon={<FilamentStockIcon />}
|
|
||||||
collapseKey='filamentStock'
|
|
||||||
active={collapseState.filamentStock}
|
|
||||||
onToggle={(expanded) =>
|
|
||||||
updateCollapseState('filamentStock', expanded)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{objectFormState.objectData?.currentFilamentStock
|
|
||||||
?._id ? (
|
|
||||||
<ObjectForm
|
|
||||||
id={
|
|
||||||
objectFormState.objectData.currentFilamentStock
|
|
||||||
._id
|
|
||||||
}
|
|
||||||
type='filamentStock'
|
|
||||||
onStateChange={() => {}}
|
|
||||||
>
|
|
||||||
{({
|
|
||||||
loading: filamentStockObjectLoading,
|
|
||||||
objectData: filamentStockObjectData
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<ObjectInfo
|
|
||||||
loading={filamentStockObjectLoading}
|
|
||||||
column={sideBarVisible ? 1 : undefined}
|
|
||||||
showHyperlink={true}
|
|
||||||
visibleProperties={{
|
|
||||||
updatedAt: false,
|
|
||||||
createdAt: false
|
|
||||||
}}
|
|
||||||
objectData={filamentStockObjectData}
|
|
||||||
type='filamentStock'
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</ObjectForm>
|
|
||||||
) : (
|
|
||||||
<MissingPlaceholder
|
|
||||||
message={'No filament stock.'}
|
|
||||||
hasBackground={false}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</InfoCollapse>
|
|
||||||
</Flex>
|
|
||||||
</Splitter.Panel>
|
|
||||||
{sideBarVisible && !isMobile ? (
|
|
||||||
<Splitter.Panel
|
|
||||||
style={{ minWidth: '325px' }}
|
|
||||||
defaultSize='20%'
|
|
||||||
max='35%'
|
|
||||||
>
|
>
|
||||||
{sideBarItems}
|
{objectFormState.objectData?.currentSubJob?._id ? (
|
||||||
</Splitter.Panel>
|
<ObjectForm
|
||||||
) : null}
|
id={objectFormState.objectData.currentSubJob._id}
|
||||||
</Splitter>
|
type='subjob'
|
||||||
{isMobile ? (
|
onStateChange={() => {}}
|
||||||
<>
|
>
|
||||||
<Divider />
|
{({
|
||||||
|
loading: subJobObjectLoading,
|
||||||
|
objectData: subJobObjectData
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ObjectInfo
|
||||||
|
loading={subJobObjectLoading}
|
||||||
|
column={sideBarVisible ? 1 : undefined}
|
||||||
|
visibleProperties={{
|
||||||
|
printers: false,
|
||||||
|
createdAt: false
|
||||||
|
}}
|
||||||
|
objectData={subJobObjectData}
|
||||||
|
type='subJob'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</ObjectForm>
|
||||||
|
) : (
|
||||||
|
<MissingPlaceholder
|
||||||
|
message={'No sub job.'}
|
||||||
|
hasBackground={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
<InfoCollapse
|
||||||
|
title={'Filament Stock'}
|
||||||
|
icon={<FilamentStockIcon />}
|
||||||
|
collapseKey='filamentStock'
|
||||||
|
active={collapseState.filamentStock}
|
||||||
|
onToggle={(expanded) =>
|
||||||
|
updateCollapseState('filamentStock', expanded)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{objectFormState.objectData?.currentFilamentStock?._id ? (
|
||||||
|
<ObjectForm
|
||||||
|
id={
|
||||||
|
objectFormState.objectData.currentFilamentStock._id
|
||||||
|
}
|
||||||
|
type='filamentStock'
|
||||||
|
onStateChange={() => {}}
|
||||||
|
>
|
||||||
|
{({
|
||||||
|
loading: filamentStockObjectLoading,
|
||||||
|
objectData: filamentStockObjectData
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ObjectInfo
|
||||||
|
loading={filamentStockObjectLoading}
|
||||||
|
column={sideBarVisible ? 1 : undefined}
|
||||||
|
showHyperlink={true}
|
||||||
|
visibleProperties={{
|
||||||
|
updatedAt: false,
|
||||||
|
createdAt: false
|
||||||
|
}}
|
||||||
|
objectData={filamentStockObjectData}
|
||||||
|
type='filamentStock'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</ObjectForm>
|
||||||
|
) : (
|
||||||
|
<MissingPlaceholder
|
||||||
|
message={'No filament stock.'}
|
||||||
|
hasBackground={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</InfoCollapse>
|
||||||
|
</Flex>
|
||||||
|
</Splitter.Panel>
|
||||||
|
{sideBarVisible && !isMobile ? (
|
||||||
|
<Splitter.Panel
|
||||||
|
style={{ minWidth: '325px' }}
|
||||||
|
defaultSize='20%'
|
||||||
|
max='35%'
|
||||||
|
>
|
||||||
{sideBarItems}
|
{sideBarItems}
|
||||||
</>
|
</Splitter.Panel>
|
||||||
) : null}
|
) : null}
|
||||||
</Flex>
|
</Splitter>
|
||||||
</ActionHandler>
|
{isMobile ? (
|
||||||
<InfoCollapse
|
<>
|
||||||
title='Notes'
|
<Divider />
|
||||||
icon={<NoteIcon />}
|
{sideBarItems}
|
||||||
active={collapseState.notes}
|
</>
|
||||||
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
) : null}
|
||||||
collapseKey='notes'
|
</Flex>
|
||||||
>
|
</ActionHandler>
|
||||||
<Card>
|
<InfoCollapse
|
||||||
<NotesPanel _id={printerId} type='printer' />
|
title='Notes'
|
||||||
</Card>
|
icon={<NoteIcon />}
|
||||||
</InfoCollapse>
|
active={collapseState.notes}
|
||||||
</Flex>
|
onToggle={(expanded) => updateCollapseState('notes', expanded)}
|
||||||
</div>
|
collapseKey='notes'
|
||||||
</Flex>
|
>
|
||||||
<Modal
|
<Card>
|
||||||
open={loadFilamentStockOpen}
|
<NotesPanel _id={printerId} type='printer' />
|
||||||
onCancel={() => setLoadFilamentStockOpen(false)}
|
</Card>
|
||||||
footer={null}
|
</InfoCollapse>
|
||||||
width='700px'
|
</Flex>
|
||||||
destroyOnHidden={true}
|
</div>
|
||||||
>
|
</Flex>
|
||||||
<LoadFilamentStock
|
|
||||||
printer={objectFormState.objectData}
|
|
||||||
onOk={() => setLoadFilamentStockOpen(false)}
|
|
||||||
reset={false}
|
|
||||||
filamentStockLoaded={false}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
<Modal
|
|
||||||
open={unloadFilamentStockOpen}
|
|
||||||
onCancel={() => setUnloadFilamentStockOpen(false)}
|
|
||||||
footer={null}
|
|
||||||
width='700px'
|
|
||||||
destroyOnHidden={true}
|
|
||||||
>
|
|
||||||
<UnloadFilamentStock
|
|
||||||
printer={objectFormState.objectData}
|
|
||||||
onOk={() => setUnloadFilamentStockOpen(false)}
|
|
||||||
reset={false}
|
|
||||||
filamentStockLoaded={false}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -108,14 +108,7 @@ const DocumentPrintButton = ({
|
|||||||
onCancel={() => setNewDocumentJobOpen(false)}
|
onCancel={() => setNewDocumentJobOpen(false)}
|
||||||
footer={null}
|
footer={null}
|
||||||
destroyOnHidden={true}
|
destroyOnHidden={true}
|
||||||
width={{
|
width={900}
|
||||||
xs: '100%',
|
|
||||||
sm: '100%',
|
|
||||||
md: '100%',
|
|
||||||
lg: '90%',
|
|
||||||
xl: '80%',
|
|
||||||
xxl: '80%'
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<NewDocumentJob
|
<NewDocumentJob
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
|
|||||||
@ -9,8 +9,7 @@ const NewObjectButtons = ({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
formValid,
|
formValid,
|
||||||
submitLoading,
|
submitLoading,
|
||||||
submitText = 'Done',
|
submitText = 'Done'
|
||||||
disabled = false
|
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Flex justify='end'>
|
<Flex justify='end'>
|
||||||
@ -25,18 +24,14 @@ const NewObjectButtons = ({
|
|||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{currentStep < totalSteps - 1 ? (
|
{currentStep < totalSteps - 1 ? (
|
||||||
<Button
|
<Button type='primary' disabled={!formValid} onClick={onNext}>
|
||||||
type='primary'
|
|
||||||
disabled={!formValid || disabled}
|
|
||||||
onClick={onNext}
|
|
||||||
>
|
|
||||||
Next
|
Next
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
type='primary'
|
type='primary'
|
||||||
loading={submitLoading}
|
loading={submitLoading}
|
||||||
disabled={!formValid || disabled}
|
disabled={!formValid}
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
>
|
>
|
||||||
{submitText}
|
{submitText}
|
||||||
@ -54,8 +49,7 @@ NewObjectButtons.propTypes = {
|
|||||||
onSubmit: PropTypes.func.isRequired,
|
onSubmit: PropTypes.func.isRequired,
|
||||||
formValid: PropTypes.bool.isRequired,
|
formValid: PropTypes.bool.isRequired,
|
||||||
submitLoading: PropTypes.bool,
|
submitLoading: PropTypes.bool,
|
||||||
submitText: PropTypes.string,
|
submitText: PropTypes.string
|
||||||
disabled: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NewObjectButtons
|
export default NewObjectButtons
|
||||||
|
|||||||
@ -138,29 +138,24 @@ const ObjectForm = forwardRef(
|
|||||||
return computedValues
|
return computedValues
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// Validate form on change (debounced to avoid heavy work on every keystroke)
|
// Validate form on change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timeoutId = setTimeout(() => {
|
form
|
||||||
form
|
.validateFields({ validateOnly: true })
|
||||||
.validateFields({ validateOnly: true })
|
.then(() => {
|
||||||
.then(() => {
|
setFormValid(true)
|
||||||
setFormValid(true)
|
onStateChange({
|
||||||
onStateChange({
|
formValid: true,
|
||||||
formValid: true,
|
objectData: { ...serverObjectData, ...form.getFieldsValue() }
|
||||||
objectData: { ...serverObjectData, ...form.getFieldsValue() }
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
})
|
||||||
setFormValid(false)
|
.catch(() => {
|
||||||
onStateChange({
|
onStateChange({
|
||||||
formValid: false,
|
formValid: true,
|
||||||
objectData: { ...serverObjectData, ...form.getFieldsValue() }
|
objectData: { ...serverObjectData, ...form.getFieldsValue() }
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}, 150)
|
})
|
||||||
|
}, [form, formUpdateValues])
|
||||||
return () => clearTimeout(timeoutId)
|
|
||||||
}, [form, formUpdateValues, onStateChange, serverObjectData])
|
|
||||||
|
|
||||||
// Cleanup on unmount
|
// Cleanup on unmount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -257,14 +252,9 @@ const ObjectForm = forwardRef(
|
|||||||
updateLockEventHandler
|
updateLockEventHandler
|
||||||
])
|
])
|
||||||
|
|
||||||
// Debounce objectData updates sent to parent to limit re-renders
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timeoutId = setTimeout(() => {
|
onStateChange({ objectData })
|
||||||
onStateChange({ objectData })
|
}, [objectData])
|
||||||
}, 150)
|
|
||||||
|
|
||||||
return () => clearTimeout(timeoutId)
|
|
||||||
}, [objectData, onStateChange])
|
|
||||||
|
|
||||||
const startEditing = () => {
|
const startEditing = () => {
|
||||||
setIsEditing(true)
|
setIsEditing(true)
|
||||||
@ -376,18 +366,9 @@ const ObjectForm = forwardRef(
|
|||||||
model
|
model
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update form with computed values if any were calculated and they changed
|
// Update form with computed values if any were calculated
|
||||||
if (Object.keys(computedValues).length > 0) {
|
if (Object.keys(computedValues).length > 0) {
|
||||||
const currentComputedValues = form.getFieldsValue(
|
form.setFieldsValue(computedValues)
|
||||||
Object.keys(computedValues)
|
|
||||||
)
|
|
||||||
const hasDiff = Object.keys(computedValues).some(
|
|
||||||
(key) => currentComputedValues[key] !== computedValues[key]
|
|
||||||
)
|
|
||||||
|
|
||||||
if (hasDiff) {
|
|
||||||
form.setFieldsValue(computedValues)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge all values (user input + computed values)
|
// Merge all values (user input + computed values)
|
||||||
|
|||||||
@ -81,8 +81,6 @@ const ObjectProperty = ({
|
|||||||
minimal = false,
|
minimal = false,
|
||||||
previewOpen = false,
|
previewOpen = false,
|
||||||
showPreview = true,
|
showPreview = true,
|
||||||
options = [],
|
|
||||||
roundNumber = false,
|
|
||||||
showHyperlink,
|
showHyperlink,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
@ -168,18 +166,6 @@ const ObjectProperty = ({
|
|||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case 'select': {
|
|
||||||
const selectValue = options.find((option) => option.value === value)
|
|
||||||
if (selectValue) {
|
|
||||||
return <Text {...textParams}>{selectValue.label}</Text>
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Text type='secondary' {...textParams}>
|
|
||||||
n/a
|
|
||||||
</Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'priceMode':
|
case 'priceMode':
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 'margin':
|
case 'margin':
|
||||||
@ -248,15 +234,10 @@ const ObjectProperty = ({
|
|||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
var roundedValue = value
|
|
||||||
if (roundNumber != false) {
|
|
||||||
roundedValue = value.toFixed(roundNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text {...textParams}>
|
<Text {...textParams}>
|
||||||
{prefix}
|
{prefix}
|
||||||
{typeof value === 'number' ? roundedValue : value}
|
{typeof value === 'number' ? value.toFixed(2) : value}
|
||||||
{suffix}
|
{suffix}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
@ -572,17 +553,6 @@ const ObjectProperty = ({
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)
|
||||||
case 'select':
|
|
||||||
return (
|
|
||||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
|
||||||
<Select
|
|
||||||
defaultValue={value}
|
|
||||||
placeholder={'Select a ' + label.toLowerCase() + '...'}
|
|
||||||
disabled={disabled}
|
|
||||||
options={options}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
)
|
|
||||||
case 'priceMode':
|
case 'priceMode':
|
||||||
return (
|
return (
|
||||||
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
<Form.Item name={formItemName} {...mergedFormItemProps}>
|
||||||
@ -803,8 +773,7 @@ ObjectProperty.propTypes = {
|
|||||||
height: PropTypes.string,
|
height: PropTypes.string,
|
||||||
previewOpen: PropTypes.bool,
|
previewOpen: PropTypes.bool,
|
||||||
showPreview: PropTypes.bool,
|
showPreview: PropTypes.bool,
|
||||||
showHyperlink: PropTypes.bool,
|
showHyperlink: PropTypes.bool
|
||||||
options: PropTypes.array
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ObjectProperty
|
export default ObjectProperty
|
||||||
|
|||||||
@ -144,7 +144,6 @@ const ObjectSelect = ({
|
|||||||
parentKeys: parentKeys.concat(key || '-'),
|
parentKeys: parentKeys.concat(key || '-'),
|
||||||
filterPath: newFilterPath,
|
filterPath: newFilterPath,
|
||||||
selectable: false,
|
selectable: false,
|
||||||
|
|
||||||
children: buildTreeData(
|
children: buildTreeData(
|
||||||
value,
|
value,
|
||||||
pIdx + 1,
|
pIdx + 1,
|
||||||
@ -279,11 +278,8 @@ const ObjectSelect = ({
|
|||||||
handleFetchObjectsProperties()
|
handleFetchObjectsProperties()
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
}
|
}
|
||||||
if (value == null) {
|
|
||||||
setTreeSelectValue(null)
|
|
||||||
setInitialized(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleValue()
|
handleValue()
|
||||||
}, [
|
}, [
|
||||||
value,
|
value,
|
||||||
@ -307,13 +303,8 @@ const ObjectSelect = ({
|
|||||||
|
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
setObjectPropertiesTree({})
|
setObjectPropertiesTree({})
|
||||||
setObjectList([])
|
|
||||||
setTreeData([])
|
setTreeData([])
|
||||||
setInitialized(false)
|
setInitialized(false)
|
||||||
onTreeSelectChange(null)
|
|
||||||
setTreeSelectValue(null)
|
|
||||||
setInitialLoading(true)
|
|
||||||
setError(false)
|
|
||||||
prevValuesRef.current = { type, masterFilter }
|
prevValuesRef.current = { type, masterFilter }
|
||||||
}
|
}
|
||||||
}, [type, masterFilter])
|
}, [type, masterFilter])
|
||||||
|
|||||||
@ -227,7 +227,7 @@ const ObjectTable = forwardRef(
|
|||||||
const loadNextPage = useCallback(() => {
|
const loadNextPage = useCallback(() => {
|
||||||
const highestPage = Math.max(...pages.map((p) => p.pageNum))
|
const highestPage = Math.max(...pages.map((p) => p.pageNum))
|
||||||
const nextPage = highestPage + 1
|
const nextPage = highestPage + 1
|
||||||
if (hasMore && lazyLoading == false) {
|
if (hasMore) {
|
||||||
setPages((prev) => {
|
setPages((prev) => {
|
||||||
const filteredPages = prev.map((page) => ({
|
const filteredPages = prev.map((page) => ({
|
||||||
...page,
|
...page,
|
||||||
@ -244,13 +244,13 @@ const ObjectTable = forwardRef(
|
|||||||
})
|
})
|
||||||
fetchData(nextPage)
|
fetchData(nextPage)
|
||||||
}
|
}
|
||||||
}, [pages, createSkeletonData, fetchData, hasMore, lazyLoading])
|
}, [pages, createSkeletonData, fetchData, hasMore])
|
||||||
|
|
||||||
const loadPreviousPage = useCallback(() => {
|
const loadPreviousPage = useCallback(() => {
|
||||||
const lowestPage = Math.min(...pages.map((p) => p.pageNum))
|
const lowestPage = Math.min(...pages.map((p) => p.pageNum))
|
||||||
const prevPage = lowestPage - 1
|
const prevPage = lowestPage - 1
|
||||||
|
|
||||||
if (prevPage > 0 && lazyLoading == false) {
|
if (prevPage > 0) {
|
||||||
setPages((prev) => {
|
setPages((prev) => {
|
||||||
const filteredPages = prev.map((page) => ({
|
const filteredPages = prev.map((page) => ({
|
||||||
...page,
|
...page,
|
||||||
@ -267,7 +267,7 @@ const ObjectTable = forwardRef(
|
|||||||
})
|
})
|
||||||
fetchData(prevPage)
|
fetchData(prevPage)
|
||||||
}
|
}
|
||||||
}, [pages, createSkeletonData, fetchData, lazyLoading])
|
}, [pages, createSkeletonData, fetchData])
|
||||||
|
|
||||||
const handleScroll = useCallback(
|
const handleScroll = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
@ -600,7 +600,7 @@ const ObjectTable = forwardRef(
|
|||||||
title: prop.label,
|
title: prop.label,
|
||||||
dataIndex: prop.name,
|
dataIndex: prop.name,
|
||||||
width: prop.columnWidth || width,
|
width: prop.columnWidth || width,
|
||||||
fixed: isMobile ? undefined : fixed,
|
fixed: fixed,
|
||||||
key: prop.name,
|
key: prop.name,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
if (record?.isSkeleton) {
|
if (record?.isSkeleton) {
|
||||||
@ -651,7 +651,7 @@ const ObjectTable = forwardRef(
|
|||||||
),
|
),
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
width: 20 + rowActions.length * 30, // Adjust width based on number of actions
|
width: 80 + rowActions.length * 40, // Adjust width based on number of actions
|
||||||
render: (record) => {
|
render: (record) => {
|
||||||
return renderActions(record)
|
return renderActions(record)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,8 +17,7 @@ const ObjectTypeSelect = ({
|
|||||||
.sort((a, b) => a.label.localeCompare(b.label))
|
.sort((a, b) => a.label.localeCompare(b.label))
|
||||||
.map((model) => ({
|
.map((model) => ({
|
||||||
value: model.name,
|
value: model.name,
|
||||||
label: <ObjectTypeDisplay objectType={model.name} />,
|
label: <ObjectTypeDisplay objectType={model.name} />
|
||||||
searchText: model.label?.toLowerCase() || ''
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -32,7 +31,9 @@ const ObjectTypeSelect = ({
|
|||||||
allowClear={allowClear}
|
allowClear={allowClear}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
filterOption={(input, option) =>
|
filterOption={(input, option) =>
|
||||||
option.searchText?.includes(input.toLowerCase()) ?? false
|
option.label.props.children[1].props.children
|
||||||
|
.toLowerCase()
|
||||||
|
.indexOf(input.toLowerCase()) >= 0
|
||||||
}
|
}
|
||||||
options={options}
|
options={options}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -4,13 +4,6 @@ import { Progress, Flex, Space } from 'antd'
|
|||||||
import StateTag from './StateTag'
|
import StateTag from './StateTag'
|
||||||
|
|
||||||
const StateDisplay = ({ state, showProgress = true, showState = true }) => {
|
const StateDisplay = ({ state, showProgress = true, showState = true }) => {
|
||||||
const loadingProgressTypes = [
|
|
||||||
'loading',
|
|
||||||
'processing',
|
|
||||||
'queued',
|
|
||||||
'printing',
|
|
||||||
'used'
|
|
||||||
]
|
|
||||||
const currentState = state || {
|
const currentState = state || {
|
||||||
type: 'unknown',
|
type: 'unknown',
|
||||||
progress: 0
|
progress: 0
|
||||||
@ -23,14 +16,10 @@ const StateDisplay = ({ state, showProgress = true, showState = true }) => {
|
|||||||
<StateTag state={currentState.type} />
|
<StateTag state={currentState.type} />
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
{showProgress &&
|
{showProgress && currentState?.progress && currentState?.progress > 0 ? (
|
||||||
loadingProgressTypes.includes(currentState.type) &&
|
|
||||||
currentState?.progress &&
|
|
||||||
currentState?.progress > 0 ? (
|
|
||||||
<Progress
|
<Progress
|
||||||
percent={Math.round(currentState.progress * 100)}
|
percent={Math.round(currentState.progress * 100)}
|
||||||
status={currentState.type === 'used' ? '' : 'active'}
|
status='active'
|
||||||
strokeColor={currentState.type === 'used' ? 'orange' : ''}
|
|
||||||
style={{ width: '150px', marginBottom: '2px' }}
|
style={{ width: '150px', marginBottom: '2px' }}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@ -56,9 +56,9 @@ const StateTag = ({ state, showBadge = true, style = {} }) => {
|
|||||||
status = 'success'
|
status = 'success'
|
||||||
text = 'Ready'
|
text = 'Ready'
|
||||||
break
|
break
|
||||||
case 'new':
|
case 'unconsumed':
|
||||||
status = 'success'
|
status = 'success'
|
||||||
text = 'New'
|
text = 'Unconsumed'
|
||||||
break
|
break
|
||||||
case 'error':
|
case 'error':
|
||||||
status = 'error'
|
status = 'error'
|
||||||
@ -80,10 +80,6 @@ const StateTag = ({ state, showBadge = true, style = {} }) => {
|
|||||||
status = 'warning'
|
status = 'warning'
|
||||||
text = 'Queued'
|
text = 'Queued'
|
||||||
break
|
break
|
||||||
case 'used':
|
|
||||||
status = 'warning'
|
|
||||||
text = 'Used'
|
|
||||||
break
|
|
||||||
default:
|
default:
|
||||||
status = 'default'
|
status = 'default'
|
||||||
text = state || 'Unknown'
|
text = state || 'Unknown'
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useState, useContext, useEffect, useRef, useCallback } from 'react'
|
import { useState, useContext, useEffect, useRef, useCallback } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Flex, Button, Input, Select } from 'antd'
|
import { Flex, Button, Input } from 'antd'
|
||||||
import PlusIcon from '../../Icons/PlusIcon.jsx'
|
import PlusIcon from '../../Icons/PlusIcon.jsx'
|
||||||
import MinusIcon from '../../Icons/MinusIcon.jsx'
|
import MinusIcon from '../../Icons/MinusIcon.jsx'
|
||||||
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
|
||||||
@ -14,26 +14,22 @@ const TemplatePreview = ({
|
|||||||
isEditing,
|
isEditing,
|
||||||
onTestObjectOpen,
|
onTestObjectOpen,
|
||||||
onPreviewMessage,
|
onPreviewMessage,
|
||||||
showTestObject = false,
|
showTestObject = false
|
||||||
showPreviewSwitch = true
|
|
||||||
}) => {
|
}) => {
|
||||||
const iframeRef = useRef(null)
|
const iframeRef = useRef(null)
|
||||||
const { fetchTemplatePreview, fetchTemplatePDF } =
|
const { fetchTemplatePreview } = useContext(ApiServerContext)
|
||||||
useContext(ApiServerContext)
|
const [previewContent, setPreviewContent] = useState('')
|
||||||
const [previewContentHTML, setPreviewContentHTML] = useState('')
|
|
||||||
const [pdfBlob, setPDFBlob] = useState(null)
|
|
||||||
const [reloadLoading, setReloadLoading] = useState(false)
|
const [reloadLoading, setReloadLoading] = useState(false)
|
||||||
const [previewScale, setPreviewScale] = useState(1)
|
const [previewScale, setPreviewScale] = useState(1)
|
||||||
const [previewType, setPreviewType] = useState('HTML')
|
|
||||||
|
|
||||||
const updatePreviewContentHTML = (html) => {
|
const updatePreviewContent = (html) => {
|
||||||
if (iframeRef.current) {
|
if (iframeRef.current) {
|
||||||
// Save current scroll position
|
// Save current scroll position
|
||||||
const scrollY = iframeRef.current.contentWindow.scrollY
|
const scrollY = iframeRef.current.contentWindow.scrollY
|
||||||
const scrollX = iframeRef.current.contentWindow.scrollX
|
const scrollX = iframeRef.current.contentWindow.scrollX
|
||||||
|
|
||||||
// Update srcDoc
|
// Update srcDoc
|
||||||
setPreviewContentHTML(html)
|
setPreviewContent(html)
|
||||||
|
|
||||||
// Restore scroll position after iframe loads new content
|
// Restore scroll position after iframe loads new content
|
||||||
const handleLoad = () => {
|
const handleLoad = () => {
|
||||||
@ -44,23 +40,6 @@ const TemplatePreview = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reloadPreviewPDF = (content, testObject = {}) => {
|
|
||||||
setReloadLoading(true)
|
|
||||||
fetchTemplatePDF(documentTemplate._id, content, testObject, (result) => {
|
|
||||||
setReloadLoading(false)
|
|
||||||
if (result?.error) {
|
|
||||||
// Handle error through parent component
|
|
||||||
onPreviewMessage(result.error, true)
|
|
||||||
} else {
|
|
||||||
const pdfBlob = new Blob([result.pdf], { type: 'application/pdf' })
|
|
||||||
const pdfUrl = URL.createObjectURL(pdfBlob)
|
|
||||||
|
|
||||||
setPDFBlob(pdfUrl)
|
|
||||||
onPreviewMessage('No issues found.', false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const reloadPreview = useCallback(
|
const reloadPreview = useCallback(
|
||||||
(content, testObject = {}, scale = 1) => {
|
(content, testObject = {}, scale = 1) => {
|
||||||
setReloadLoading(true)
|
setReloadLoading(true)
|
||||||
@ -75,7 +54,7 @@ const TemplatePreview = ({
|
|||||||
// Handle error through parent component
|
// Handle error through parent component
|
||||||
onPreviewMessage(result.error, true)
|
onPreviewMessage(result.error, true)
|
||||||
} else {
|
} else {
|
||||||
updatePreviewContentHTML(result.html)
|
updatePreviewContent(result.html)
|
||||||
onPreviewMessage('No issues found.', false)
|
onPreviewMessage('No issues found.', false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,13 +66,9 @@ const TemplatePreview = ({
|
|||||||
// Move useEffect to component level and use state to track objectData changes
|
// Move useEffect to component level and use state to track objectData changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (documentTemplate?.content) {
|
if (documentTemplate?.content) {
|
||||||
if (previewType == 'HTML') {
|
reloadPreview(documentTemplate.content, objectData, previewScale)
|
||||||
reloadPreview(documentTemplate.content, objectData, previewScale)
|
|
||||||
} else {
|
|
||||||
reloadPreviewPDF(documentTemplate.content, objectData)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [objectData, documentTemplate, previewScale, previewType])
|
}, [objectData, documentTemplate, previewScale, reloadPreview])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex vertical gap={'middle'} style={{ height: '100%' }}>
|
<Flex vertical gap={'middle'} style={{ height: '100%' }}>
|
||||||
@ -123,51 +98,34 @@ const TemplatePreview = ({
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
icon={<PlusIcon />}
|
icon={<PlusIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPreviewScale((prev) => prev + 0.05)
|
setPreviewScale((prev) => prev + 0.05)
|
||||||
}}
|
}}
|
||||||
disabled={loading || reloadLoading || previewType == 'PDF'}
|
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon={<MinusIcon />}
|
icon={<MinusIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPreviewScale((prev) => prev - 0.05)
|
setPreviewScale((prev) => prev - 0.05)
|
||||||
}}
|
}}
|
||||||
disabled={loading || reloadLoading || previewType == 'PDF'}
|
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
readOnly={true}
|
readOnly={true}
|
||||||
style={{ width: '65px' }}
|
style={{ width: '65px' }}
|
||||||
disabled={loading || reloadLoading || previewType == 'PDF'}
|
loading={loading || reloadLoading}
|
||||||
|
disabled={loading || reloadLoading}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPreviewScale(1)
|
setPreviewScale(1)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{previewScale.toFixed(2)}x
|
{previewScale.toFixed(2)}x
|
||||||
</Button>
|
</Button>
|
||||||
{showPreviewSwitch == true ? (
|
|
||||||
<Select
|
|
||||||
options={[
|
|
||||||
{ value: 'HTML', label: 'HTML' },
|
|
||||||
{ value: 'PDF', label: 'PDF' }
|
|
||||||
]}
|
|
||||||
loading={loading || reloadLoading}
|
|
||||||
disabled={loading || reloadLoading}
|
|
||||||
value={previewType}
|
|
||||||
onChange={(value) => {
|
|
||||||
setPreviewType(value)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<iframe
|
<iframe
|
||||||
ref={iframeRef}
|
ref={iframeRef}
|
||||||
srcDoc={previewType == 'HTML' ? previewContentHTML : undefined}
|
srcDoc={previewContent}
|
||||||
src={previewType == 'PDF' ? pdfBlob : undefined}
|
|
||||||
frameBorder='0'
|
frameBorder='0'
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -188,8 +146,7 @@ TemplatePreview.propTypes = {
|
|||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
showTestObject: PropTypes.bool,
|
showTestObject: PropTypes.bool,
|
||||||
onTestObjectOpen: PropTypes.func.isRequired,
|
onTestObjectOpen: PropTypes.func.isRequired,
|
||||||
onPreviewMessage: PropTypes.func.isRequired,
|
onPreviewMessage: PropTypes.func.isRequired
|
||||||
showPreviewSwitch: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TemplatePreview
|
export default TemplatePreview
|
||||||
|
|||||||
@ -1,15 +1,7 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useMediaQuery } from 'react-responsive'
|
import { useMediaQuery } from 'react-responsive'
|
||||||
import {
|
import { Typography, Flex, Steps, Divider, Progress } from 'antd'
|
||||||
Typography,
|
|
||||||
Flex,
|
|
||||||
Steps,
|
|
||||||
Divider,
|
|
||||||
Progress,
|
|
||||||
Button,
|
|
||||||
Dropdown
|
|
||||||
} from 'antd'
|
|
||||||
import NewObjectButtons from './NewObjectButtons'
|
import NewObjectButtons from './NewObjectButtons'
|
||||||
|
|
||||||
const { Title } = Typography
|
const { Title } = Typography
|
||||||
@ -23,10 +15,7 @@ const WizardView = ({
|
|||||||
loading,
|
loading,
|
||||||
sideBar = null,
|
sideBar = null,
|
||||||
submitText = 'Done',
|
submitText = 'Done',
|
||||||
progress = 0,
|
progress = 0
|
||||||
actions = [],
|
|
||||||
sideBarGrow = false,
|
|
||||||
disabled = false
|
|
||||||
}) => {
|
}) => {
|
||||||
const [currentStep, setCurrentStep] = useState(0)
|
const [currentStep, setCurrentStep] = useState(0)
|
||||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
@ -37,7 +26,7 @@ const WizardView = ({
|
|||||||
sideBar != null ? (
|
sideBar != null ? (
|
||||||
sideBar
|
sideBar
|
||||||
) : (
|
) : (
|
||||||
<div style={{ minWidth: sideBarGrow == true ? '100%' : '160px' }}>
|
<div style={{ minWidth: '160px' }}>
|
||||||
<Steps
|
<Steps
|
||||||
current={currentStep}
|
current={currentStep}
|
||||||
items={steps}
|
items={steps}
|
||||||
@ -56,13 +45,7 @@ const WizardView = ({
|
|||||||
vertical
|
vertical
|
||||||
justify='space-between'
|
justify='space-between'
|
||||||
gap={'middle'}
|
gap={'middle'}
|
||||||
style={
|
style={{ width: '100%' }}
|
||||||
sideBarGrow == false
|
|
||||||
? { width: '100%' }
|
|
||||||
: isMobile
|
|
||||||
? { width: '100%' }
|
|
||||||
: { width: '400px' }
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Flex vertical gap='middle' style={{ flexGrow: 1, width: '100%' }}>
|
<Flex vertical gap='middle' style={{ flexGrow: 1, width: '100%' }}>
|
||||||
<Title level={2} style={{ margin: 0 }}>
|
<Title level={2} style={{ margin: 0 }}>
|
||||||
@ -80,37 +63,8 @@ const WizardView = ({
|
|||||||
percent={progress}
|
percent={progress}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{(actions || []).map((action) => {
|
|
||||||
if (action.steps.includes(steps[currentStep].key)) {
|
|
||||||
if (action.children) {
|
|
||||||
return (
|
|
||||||
<Dropdown menu={{ items: action.children }} key={action.key}>
|
|
||||||
<Button
|
|
||||||
onClick={action?.onClick}
|
|
||||||
disabled={action?.disabled || disabled}
|
|
||||||
loading={action?.loading || false}
|
|
||||||
>
|
|
||||||
{action.label}
|
|
||||||
</Button>
|
|
||||||
</Dropdown>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
key={action.key}
|
|
||||||
onClick={action?.onClick}
|
|
||||||
disabled={action?.disabled || disabled}
|
|
||||||
loading={action?.loading || false}
|
|
||||||
>
|
|
||||||
{action.label}
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
})}
|
|
||||||
|
|
||||||
<NewObjectButtons
|
<NewObjectButtons
|
||||||
disabled={disabled}
|
|
||||||
currentStep={currentStep}
|
currentStep={currentStep}
|
||||||
totalSteps={steps.length}
|
totalSteps={steps.length}
|
||||||
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
onPrevious={() => setCurrentStep((prev) => prev - 1)}
|
||||||
@ -133,12 +87,9 @@ WizardView.propTypes = {
|
|||||||
showSteps: PropTypes.bool,
|
showSteps: PropTypes.bool,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
disabled: PropTypes.bool,
|
|
||||||
sideBar: PropTypes.node,
|
sideBar: PropTypes.node,
|
||||||
submitText: PropTypes.string,
|
submitText: PropTypes.string,
|
||||||
progress: PropTypes.number,
|
progress: PropTypes.number
|
||||||
actions: PropTypes.array,
|
|
||||||
sideBarGrow: PropTypes.bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WizardView
|
export default WizardView
|
||||||
|
|||||||
@ -308,14 +308,9 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
.get(callbacksRefKey)
|
.get(callbacksRefKey)
|
||||||
.filter((cb) => cb !== callback)
|
.filter((cb) => cb !== callback)
|
||||||
if (callbacks.length === 0) {
|
if (callbacks.length === 0) {
|
||||||
logger.debug(
|
|
||||||
'No callbacks found for object:',
|
|
||||||
callbacksRefKey,
|
|
||||||
'unsubscribing from object update...'
|
|
||||||
)
|
|
||||||
subscribedCallbacksRef.current.delete(callbacksRefKey)
|
subscribedCallbacksRef.current.delete(callbacksRefKey)
|
||||||
socketRef.current.emit('unsubscribeObjectUpdate', {
|
socketRef.current.emit('unsubscribeObjectUpdate', {
|
||||||
_id: id,
|
id: id,
|
||||||
objectType: objectType
|
objectType: objectType
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -534,7 +529,7 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
`Added lock callback for object ${id}, total lock callbacks: ${subscribedLockCallbacksRef.current.get(id).length}`
|
`Added lock callback for object ${id}, total lock callbacks: ${subscribedLockCallbacksRef.current.get(id).length}`
|
||||||
)
|
)
|
||||||
|
|
||||||
socketRef.current.emit('subscribe_lock', { _id: id, objectType: type })
|
socketRef.current.emit('subscribe_lock', { id: id, type: type })
|
||||||
logger.debug('Registered lock event listener for object:', id)
|
logger.debug('Registered lock event listener for object:', id)
|
||||||
|
|
||||||
// Return cleanup function
|
// Return cleanup function
|
||||||
@ -858,53 +853,6 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchTemplatePDF = async (id, content, testObject, callback) => {
|
|
||||||
logger.debug('Fetching pdf template...')
|
|
||||||
if (socketRef.current && socketRef.current.connected) {
|
|
||||||
return socketRef.current.emit(
|
|
||||||
'renderTemplatePDF',
|
|
||||||
{
|
|
||||||
_id: id,
|
|
||||||
content: content,
|
|
||||||
object: testObject
|
|
||||||
},
|
|
||||||
callback
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const downloadTemplatePDF = async (
|
|
||||||
id,
|
|
||||||
content,
|
|
||||||
object,
|
|
||||||
filename,
|
|
||||||
callback
|
|
||||||
) => {
|
|
||||||
logger.debug('Downloading template PDF...')
|
|
||||||
|
|
||||||
fetchTemplatePDF(id, content, object, (result) => {
|
|
||||||
logger.debug('Downloading template PDF result:', result)
|
|
||||||
if (result?.error) {
|
|
||||||
console.error(result.error)
|
|
||||||
if (callback) {
|
|
||||||
callback(result.error)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const pdfBlob = new Blob([result.pdf], { type: 'application/pdf' })
|
|
||||||
const pdfUrl = URL.createObjectURL(pdfBlob)
|
|
||||||
const fileLink = document.createElement('a')
|
|
||||||
fileLink.href = pdfUrl
|
|
||||||
fileLink.setAttribute('download', `${filename}.pdf`)
|
|
||||||
document.body.appendChild(fileLink)
|
|
||||||
fileLink.click()
|
|
||||||
fileLink.parentNode.removeChild(fileLink)
|
|
||||||
if (callback) {
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchHostOTP = async (id, callback) => {
|
const fetchHostOTP = async (id, callback) => {
|
||||||
logger.debug('Fetching host OTP...')
|
logger.debug('Fetching host OTP...')
|
||||||
if (socketRef.current && socketRef.current.connected) {
|
if (socketRef.current && socketRef.current.connected) {
|
||||||
@ -994,22 +942,6 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanitize a string so it is safe to use as a filename on most file systems
|
|
||||||
const formatFileName = (name) => {
|
|
||||||
if (!name || typeof name !== 'string') {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove characters that are problematic on most common file systems
|
|
||||||
const cleaned = name.replace(/[^a-zA-Z0-9.\-_\s]/g, '')
|
|
||||||
|
|
||||||
// Normalize whitespace to single underscores
|
|
||||||
const normalized = cleaned.trim().replace(/\s+/g, '_')
|
|
||||||
|
|
||||||
// Most file systems limit filenames to 255 characters
|
|
||||||
return normalized.slice(0, 255)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ApiServerContext.Provider
|
<ApiServerContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@ -1035,14 +967,11 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
showError,
|
showError,
|
||||||
fetchFileContent,
|
fetchFileContent,
|
||||||
fetchTemplatePreview,
|
fetchTemplatePreview,
|
||||||
fetchTemplatePDF,
|
|
||||||
fetchNotes,
|
fetchNotes,
|
||||||
downloadTemplatePDF,
|
|
||||||
fetchHostOTP,
|
fetchHostOTP,
|
||||||
sendObjectAction,
|
sendObjectAction,
|
||||||
uploadFile,
|
uploadFile,
|
||||||
flushFile,
|
flushFile
|
||||||
formatFileName
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{contextHolder}
|
{contextHolder}
|
||||||
|
|||||||
@ -6,9 +6,9 @@ const config = {
|
|||||||
logLevel: 'trace'
|
logLevel: 'trace'
|
||||||
},
|
},
|
||||||
production: {
|
production: {
|
||||||
backendUrl: 'https://dev.tombutcher.work/api',
|
backendUrl: 'http://192.168.68.53:8080',
|
||||||
printServerUrl: 'ws://192.168.68.53:8081',
|
printServerUrl: 'ws://192.168.68.53:8081',
|
||||||
apiServerUrl: 'https://dev-wss.tombutcher.work',
|
apiServerUrl: 'ws://192.168.68.53:9090',
|
||||||
logLevel: 'error'
|
logLevel: 'error'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,11 @@ import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
|||||||
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
import EditIcon from '../../components/Icons/EditIcon'
|
import EditIcon from '../../components/Icons/EditIcon'
|
||||||
import DocumentJobIcon from '../../components/Icons/DocumentJobIcon'
|
import DocumentJobIcon from '../../components/Icons/DocumentJobIcon'
|
||||||
import dayjs from 'dayjs'
|
|
||||||
|
|
||||||
export const DocumentJob = {
|
export const DocumentJob = {
|
||||||
name: 'documentJob',
|
name: 'documentJob',
|
||||||
label: 'Document Job',
|
label: 'Document Job',
|
||||||
prefix: 'DJB',
|
prefix: 'DSZ',
|
||||||
icon: DocumentJobIcon,
|
icon: DocumentJobIcon,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
@ -61,9 +60,7 @@ export const DocumentJob = {
|
|||||||
columnWidth: 200,
|
columnWidth: 200,
|
||||||
columnFixed: 'left',
|
columnFixed: 'left',
|
||||||
value: (objectData) => {
|
value: (objectData) => {
|
||||||
if (objectData?.createdAt == undefined) {
|
return `${objectData?.documentTemplate?.name || 'No template'} (${objectData?.object?.name || 'No name'})`
|
||||||
return `${objectData?.documentTemplate?.name || 'No template'} ${dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss')} (${objectData?.object?.name || objectData?.object?._id})`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -72,14 +69,6 @@ export const DocumentJob = {
|
|||||||
type: 'dateTime',
|
type: 'dateTime',
|
||||||
readOnly: true
|
readOnly: true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'state',
|
|
||||||
label: 'Status',
|
|
||||||
type: 'state',
|
|
||||||
objectType: 'printer',
|
|
||||||
showName: false,
|
|
||||||
readOnly: true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'objectType',
|
name: 'objectType',
|
||||||
label: 'Object Type',
|
label: 'Object Type',
|
||||||
@ -97,15 +86,6 @@ export const DocumentJob = {
|
|||||||
return objectData?.objectType
|
return objectData?.objectType
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'object._id',
|
|
||||||
label: 'Object ID',
|
|
||||||
type: 'id',
|
|
||||||
showHyperlink: true,
|
|
||||||
objectType: (objectData) => {
|
|
||||||
return objectData?.objectType
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'documentTemplate',
|
name: 'documentTemplate',
|
||||||
label: 'Template',
|
label: 'Template',
|
||||||
@ -121,13 +101,6 @@ export const DocumentJob = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'documentTemplate._id',
|
|
||||||
label: 'Template ID',
|
|
||||||
type: 'id',
|
|
||||||
showHyperlink: true,
|
|
||||||
objectType: 'documentTemplate'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'documentPrinter',
|
name: 'documentPrinter',
|
||||||
label: 'Printer',
|
label: 'Printer',
|
||||||
@ -141,13 +114,6 @@ export const DocumentJob = {
|
|||||||
online: true
|
online: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'documentPrinter._id',
|
|
||||||
label: 'Printer ID',
|
|
||||||
type: 'id',
|
|
||||||
showHyperlink: true,
|
|
||||||
objectType: 'documentPrinter'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -74,88 +74,52 @@ export const DocumentPrinter = {
|
|||||||
readOnly: true
|
readOnly: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'state',
|
name: 'documentSize',
|
||||||
label: 'Status',
|
label: 'Document Size',
|
||||||
type: 'state',
|
|
||||||
objectType: 'printer',
|
|
||||||
showName: false,
|
|
||||||
readOnly: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'active',
|
|
||||||
label: 'Active',
|
|
||||||
type: 'bool',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'online',
|
|
||||||
label: 'Online',
|
|
||||||
type: 'bool',
|
|
||||||
readOnly: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'host',
|
|
||||||
label: 'Host',
|
|
||||||
required: true,
|
required: true,
|
||||||
type: 'object',
|
type: 'object',
|
||||||
objectType: 'host',
|
|
||||||
showHyperlink: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'host._id',
|
|
||||||
label: 'Host ID',
|
|
||||||
type: 'id',
|
|
||||||
objectType: 'host',
|
|
||||||
showCopy: true,
|
|
||||||
showHyperlink: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'connection.mode',
|
|
||||||
label: 'Mode',
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
{ label: 'Network', value: 'network' },
|
|
||||||
{ label: 'Serial', value: 'serial' }
|
|
||||||
],
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'connection.interface',
|
|
||||||
label: 'Interface',
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
{ label: 'CUPS', value: 'cups' },
|
|
||||||
{ label: 'Epson Receipt', value: 'epsonReceipt' },
|
|
||||||
{ label: 'Star Receipt', value: 'starReceipt' }
|
|
||||||
],
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'connection.host',
|
|
||||||
label: 'Connection String',
|
|
||||||
type: 'text',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'currentDocumentSize',
|
|
||||||
label: 'Current Document Size',
|
|
||||||
required: false,
|
|
||||||
type: 'object',
|
|
||||||
objectType: 'documentSize'
|
objectType: 'documentSize'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'currentDocumentSize._id',
|
name: 'documentSize._id',
|
||||||
label: 'Current Document Size ID',
|
label: 'Document Size ID',
|
||||||
type: 'id',
|
type: 'id',
|
||||||
objectType: 'documentSize',
|
objectType: 'documentSize',
|
||||||
showCopy: true,
|
showCopy: true,
|
||||||
showHyperlink: true
|
showHyperlink: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'active',
|
||||||
|
label: 'Active',
|
||||||
|
required: true,
|
||||||
|
type: 'bool'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'tags',
|
name: 'tags',
|
||||||
label: 'Tags',
|
label: 'Tags',
|
||||||
required: false,
|
required: false,
|
||||||
type: 'tags'
|
type: 'tags'
|
||||||
|
},
|
||||||
|
{ name: 'global', label: 'Global', required: false, type: 'bool' },
|
||||||
|
{
|
||||||
|
name: 'parent',
|
||||||
|
label: 'Parent',
|
||||||
|
required: false,
|
||||||
|
type: 'object',
|
||||||
|
objectType: 'documentPrinter',
|
||||||
|
disabled: (documentPrinter) => {
|
||||||
|
if (documentPrinter.global == true) {
|
||||||
|
documentPrinter.parent = null
|
||||||
|
}
|
||||||
|
return documentPrinter.global
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'parent._id',
|
||||||
|
label: 'Parent ID',
|
||||||
|
required: false,
|
||||||
|
type: 'id',
|
||||||
|
objectType: 'documentPrinter'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import EditIcon from '../../components/Icons/EditIcon'
|
|||||||
import PlayCircleIcon from '../../components/Icons/PlayCircleIcon'
|
import PlayCircleIcon from '../../components/Icons/PlayCircleIcon'
|
||||||
import PauseCircleIcon from '../../components/Icons/PauseCircleIcon'
|
import PauseCircleIcon from '../../components/Icons/PauseCircleIcon'
|
||||||
import StopCircleIcon from '../../components/Icons/StopCircleIcon'
|
import StopCircleIcon from '../../components/Icons/StopCircleIcon'
|
||||||
import FilamentStockIcon from '../../components/Icons/FilamentStockIcon'
|
|
||||||
export const Printer = {
|
export const Printer = {
|
||||||
name: 'printer',
|
name: 'printer',
|
||||||
label: 'Printer',
|
label: 'Printer',
|
||||||
@ -94,8 +94,8 @@ export const Printer = {
|
|||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
name: 'startQueue',
|
name: 'Start',
|
||||||
label: 'Start Queue',
|
label: 'Start',
|
||||||
icon: PlayCircleIcon,
|
icon: PlayCircleIcon,
|
||||||
disabled: (objectData) => {
|
disabled: (objectData) => {
|
||||||
console.log(objectData?.subJobs?.length)
|
console.log(objectData?.subJobs?.length)
|
||||||
@ -109,60 +109,28 @@ export const Printer = {
|
|||||||
url: (_id) =>
|
url: (_id) =>
|
||||||
`/dashboard/production/printers/control?printerId=${_id}&action=startQueue`
|
`/dashboard/production/printers/control?printerId=${_id}&action=startQueue`
|
||||||
},
|
},
|
||||||
{ type: 'divider' },
|
|
||||||
{
|
{
|
||||||
name: 'pauseJob',
|
name: 'pause',
|
||||||
label: 'Pause Job',
|
label: 'Pause',
|
||||||
icon: PauseCircleIcon,
|
icon: PauseCircleIcon,
|
||||||
disabled: (objectData) => {
|
disabled: (objectData) => {
|
||||||
return objectData?.state?.type != 'printing'
|
return objectData?.state?.type != 'printing'
|
||||||
},
|
},
|
||||||
url: (_id) =>
|
url: (_id) =>
|
||||||
`/dashboard/production/printers/control?printerId=${_id}&action=pauseJob`
|
`/dashboard/production/printers/control?printerId=${_id}&action=pauseQueue`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'resumeJob',
|
name: 'Stop',
|
||||||
label: 'Resume Job',
|
label: 'Stop',
|
||||||
icon: PlayCircleIcon,
|
|
||||||
disabled: (objectData) => {
|
|
||||||
return objectData?.state?.type != 'printing'
|
|
||||||
},
|
|
||||||
url: (_id) =>
|
|
||||||
`/dashboard/production/printers/control?printerId=${_id}&action=resumeJob`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'cancelJob',
|
|
||||||
label: 'Cancel Job',
|
|
||||||
icon: StopCircleIcon,
|
icon: StopCircleIcon,
|
||||||
disabled: (objectData) => {
|
disabled: (objectData) => {
|
||||||
return (
|
return (
|
||||||
objectData?.state?.type != 'printing' &&
|
objectData?.state?.type != 'printing' ||
|
||||||
objectData?.state?.type != 'error'
|
objectData?.state?.type != 'error'
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
url: (_id) =>
|
url: (_id) =>
|
||||||
`/dashboard/production/printers/control?printerId=${_id}&action=cancelJob`
|
`/dashboard/production/printers/control?printerId=${_id}&action=stopQueue`
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'filamentStock',
|
|
||||||
label: 'Filament Stock',
|
|
||||||
icon: FilamentStockIcon,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
name: 'loadFilamentStock',
|
|
||||||
label: 'Load Filament Stock',
|
|
||||||
icon: FilamentStockIcon,
|
|
||||||
url: (_id) =>
|
|
||||||
`/dashboard/production/printers/control?printerId=${_id}&action=loadFilamentStock`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'unloadFilamentStock',
|
|
||||||
label: 'Unload Filament Stock',
|
|
||||||
icon: FilamentStockIcon,
|
|
||||||
url: (_id) =>
|
|
||||||
`/dashboard/production/printers/control?printerId=${_id}&action=unloadFilamentStock`
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -331,14 +299,6 @@ export const Printer = {
|
|||||||
label: 'Alerts',
|
label: 'Alerts',
|
||||||
type: 'alerts',
|
type: 'alerts',
|
||||||
required: false
|
required: false
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'subJobs',
|
|
||||||
label: 'Queue',
|
|
||||||
type: 'objectList',
|
|
||||||
objectType: 'subJob',
|
|
||||||
required: false,
|
|
||||||
readOnly: true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user