Compare commits

..

14 Commits

Author SHA1 Message Date
8993caeac5 Add objectData to state in DocumentTemplateDesign component for improved data handling and management. 2025-09-05 23:21:24 +01:00
800e0d82a2 Enhance PrinterTemperaturePanel component by adding sendObjectAction for temperature setting; refactor handleSetTemperature to support async operations and improve event handling for temperature input changes. 2025-09-05 23:21:14 +01:00
d07e2be330 Refactor PropertyChanges component to replace Space with Flex for improved layout and spacing in rendering property changes. 2025-09-05 23:21:00 +01:00
09bf3b81c7 Refactor ObjectSelect component to improve state management and UI structure; add useRef for tracking previous values and enhance rendering with additional div wrapper for ObjectProperty. 2025-09-05 23:20:50 +01:00
e2a96949b3 Enhance ObjectProperty component by adding AlertsDisplay for rendering alerts; update value checks for improved data handling and styling adjustments. 2025-09-05 23:20:27 +01:00
0eac407db4 Update ObjectInfo component to conditionally include showHyperlink prop in ObjectProperty, enhancing flexibility in rendering object properties. 2025-09-05 23:20:19 +01:00
f2e7959a00 Enhance ObjectForm component to include objectData in state changes; add useEffect for syncing objectData updates, improving form validation handling and state management. 2025-09-05 23:20:08 +01:00
1b7fd77a99 Add new 'deploy' action to Job model and enhance Printer model with additional fields for 'active', 'online', 'currentJob', 'currentSubJob', and 'alerts'; update existing fields for improved data handling. 2025-09-05 23:19:56 +01:00
f90eb4ed37 Add File and DocumentJob models to ObjectModels; update exports for improved model accessibility. 2025-09-05 23:19:45 +01:00
45d9aabb87 Enhance WizardView component by adding sideBar and submitText props for improved customization; refactor layout for better responsiveness and structure. 2025-09-05 23:19:32 +01:00
c7ddcd36e3 Refactor TemplateEditor component to utilize TemplatePreview for improved preview functionality; remove unnecessary state and effects, enhancing code clarity and maintainability. 2025-09-05 23:19:20 +01:00
b6cd1bac0b Integrate IdDisplay component into ObjectDisplay for improved ID handling; conditionally render ID when object name is absent. 2025-09-05 23:18:57 +01:00
5cf4976635 Update IdDisplay component to enhance styling with minWidth and width properties for better layout control 2025-09-05 23:18:45 +01:00
475f06e5f0 Enhance ObjectActions component to support dynamic action disabling based on objectData; refactor mapActionsToMenuItems function to include objectData parameter. 2025-09-05 23:18:37 +01:00
16 changed files with 415 additions and 188 deletions

View File

@ -41,7 +41,8 @@ const DocumentTemplateDesign = () => {
editLoading: false,
formValid: false,
locked: false,
loading: false
loading: false,
objectData: {}
})
const actions = {

View File

@ -45,10 +45,18 @@ const IdDisplay = ({
}
return (
<Flex align={'end'} className='iddisplay'>
<Flex
align={'end'}
className='iddisplay'
style={{ minWidth: '0px', width: '100%' }}
>
{(() => {
const textElement = (
<Text code ellipsis style={showCopy ? { marginRight: 6 } : undefined}>
<Text
code
ellipsis
style={showCopy ? { marginRight: 6, minWidth: '0px' } : undefined}
>
{displayId}
</Text>
)

View File

@ -35,18 +35,29 @@ function filterActionsByVisibility(actions, visibleActions) {
}
// Recursively map actions to AntD Dropdown items
function mapActionsToMenuItems(actions, currentUrlWithActions, id) {
function mapActionsToMenuItems(actions, currentUrlWithActions, id, objectData) {
return actions.map((action) => {
if (action.type === 'divider') {
return { type: 'divider' }
}
const actionUrl = action.url ? action.url(id) : undefined
var disabled = actionUrl && actionUrl === currentUrlWithActions
if (action.disabled) {
if (typeof action.disabled === 'function') {
disabled = action.disabled(objectData)
} else {
disabled = action.disabled
}
}
const item = {
key: action.key || action.name,
label: action.label,
danger: action?.danger || false,
icon: action.icon ? createElement(action.icon) : undefined,
disabled: actionUrl && actionUrl === currentUrlWithActions
disabled
}
if (action.children && Array.isArray(action.children)) {
item.children = mapActionsToMenuItems(
@ -69,6 +80,7 @@ const stripActionParam = (pathname, search) => {
const ObjectActions = ({
type,
id,
objectData,
disabled = false,
buttonProps = {},
visibleActions = {},
@ -101,7 +113,12 @@ const ObjectActions = ({
// Compose AntD Dropdown menu items
const menu = {
items: mapActionsToMenuItems(filteredActions, currentUrlWithActions, id),
items: mapActionsToMenuItems(
filteredActions,
currentUrlWithActions,
id,
objectData
),
onClick: (info) => {
// Find the action by key
const findAction = (acts, key) => {
@ -132,6 +149,7 @@ const ObjectActions = ({
ObjectActions.propTypes = {
type: PropTypes.string.isRequired,
objectData: PropTypes.object.isRequired,
id: PropTypes.string.isRequired,
disabled: PropTypes.bool,
buttonProps: PropTypes.object,

View File

@ -5,6 +5,7 @@ import { getModelByName } from '../../../database/ObjectModels'
import { ApiServerContext } from '../context/ApiServerContext'
import { AuthContext } from '../context/AuthContext'
import merge from 'lodash/merge'
import IdDisplay from './IdDisplay'
const { Text } = Typography
@ -55,7 +56,15 @@ const ObjectDisplay = ({ object, objectType }) => {
<Flex gap={'small'} align='center'>
<Icon />
{objectData?.color ? <Badge color={objectData?.color} /> : null}
<Text ellipsis>{objectData?.name ? objectData.name : null}</Text>
{objectData?.name ? <Text ellipsis>{objectData.name}</Text> : null}
{objectData?._id && !objectData?.name ? (
<IdDisplay
id={objectData?._id}
type={objectType}
longId={false}
showCopy={false}
/>
) : null}
</Flex>
)
}

View File

@ -59,11 +59,10 @@ const ObjectForm = forwardRef(
.validateFields({ validateOnly: true })
.then(() => {
setFormValid(true)
onStateChange({ formValid: true })
onStateChange({ formValid: true, objectData: form.getFieldsValue() })
})
.catch(() => {
setFormValid(false)
onStateChange({ formValid: true })
onStateChange({ formValid: true, objectData: form.getFieldsValue() })
})
}, [form, formUpdateValues])
@ -146,6 +145,10 @@ const ObjectForm = forwardRef(
updateLockEventHandler
])
useEffect(() => {
onStateChange({ objectData })
}, [objectData])
const startEditing = () => {
setIsEditing(true)
onStateChange({ isEditing: true })

View File

@ -42,6 +42,9 @@ const ObjectInfo = ({
items = items.filter((item) => item.required === required)
}
if (showHyperlink) {
objectPropertyProps = { ...objectPropertyProps, showHyperlink }
}
// Filter items based on visibleProperties
// If a property key exists in visibleProperties and is false, hide it
items = items.filter((item) => {
@ -67,7 +70,6 @@ const ObjectInfo = ({
<ObjectProperty
{...item}
{...objectPropertyProps}
showHyperlink={showHyperlink}
isEditing={isEditing}
objectData={objectData}
/>

View File

@ -38,6 +38,7 @@ import ObjectTypeSelect from './ObjectTypeSelect'
import ObjectTypeDisplay from './ObjectTypeDisplay'
import CodeBlockEditor from './CodeBlockEditor'
import StateDisplay from './StateDisplay'
import AlertsDisplay from './AlertsDisplay'
const { Text } = Typography
@ -105,6 +106,10 @@ const ObjectProperty = ({
suffix = suffix(objectData)
}
if (masterFilter && typeof masterFilter == 'function' && objectData) {
masterFilter = masterFilter(objectData)
}
if (!value) {
value = getPropertyValue(objectData, name)
}
@ -116,7 +121,7 @@ const ObjectProperty = ({
formItemName = name ? name.split('.') : undefined
}
var textParams = {}
var textParams = { style: { whiteSpace: 'nowrap' } }
if (disabled == true) {
textParams = { ...textParams, delete: true, type: 'secondary' }
@ -317,7 +322,7 @@ const ObjectProperty = ({
)
}
case 'object': {
if (value && value.name) {
if (value && value._id) {
return <ObjectDisplay object={value} objectType={objectType} />
} else {
return (
@ -385,9 +390,9 @@ const ObjectProperty = ({
)
}
}
case 'mm': {
if (value != null) {
return <Text {...textParams}>{`${value} mm`}</Text>
case 'alerts': {
if (value != null && value?.length != 0) {
return <AlertsDisplay alerts={value} />
} else {
return (
<Text type='secondary' {...textParams}>

View File

@ -1,4 +1,11 @@
import { useEffect, useState, useContext, useCallback, useMemo } from 'react'
import {
useEffect,
useState,
useContext,
useCallback,
useMemo,
useRef
} from 'react'
import PropTypes from 'prop-types'
import { TreeSelect, Space, Button, Input } from 'antd'
import ReloadIcon from '../../Icons/ReloadIcon'
@ -72,6 +79,7 @@ const ObjectSelect = ({
})
return {
title: (
<div style={{ paddingTop: '1px' }}>
<ObjectProperty
key={object._id}
type='object'
@ -80,6 +88,7 @@ const ObjectSelect = ({
objectData={object}
isEditing={false}
/>
</div>
),
value: object._id,
key: object._id,
@ -199,7 +208,7 @@ const ObjectSelect = ({
return prev
})
}
}, [objectPropertiesTree, properties, type, buildTreeData])
}, [objectPropertiesTree, properties, buildTreeData])
useEffect(() => {
if (value && typeof value === 'object' && value !== null && !initialized) {
@ -236,6 +245,24 @@ const ObjectSelect = ({
token
])
const prevValuesRef = useRef({ type, masterFilter })
useEffect(() => {
const prevValues = prevValuesRef.current
// Deep comparison for objects, simple comparison for primitives
const hasChanged =
prevValues.type !== type ||
JSON.stringify(prevValues.masterFilter) !== JSON.stringify(masterFilter)
if (hasChanged) {
setObjectPropertiesTree({})
setTreeData([])
setInitialized(false)
prevValuesRef.current = { type, masterFilter }
}
}, [type, masterFilter])
// --- Error UI ---
if (error) {
return (

View File

@ -53,7 +53,8 @@ const PrinterTemperaturePanel = ({
ambiant: 0
})
const { subscribeToObjectEvent, connected } = useContext(ApiServerContext)
const { subscribeToObjectEvent, connected, sendObjectAction } =
useContext(ApiServerContext)
// Sync input values with actual temperature targets
useEffect(() => {
@ -90,10 +91,19 @@ const PrinterTemperaturePanel = ({
const [extruderTarget, setExtruderTarget] = useState(0)
const [bedTarget, setBedTarget] = useState(0)
const handleSetTemperature = (data) => {
const handleSetTemperature = async (data) => {
if (id && connected == true) {
console.log(data)
//sendObjectAction(id, 'printer', { type: 'setTemperature', data })
await sendObjectAction(
id,
'printer',
{
type: 'setTemperature',
data
},
(result) => {
console.log('setTemperatureResult', result)
}
)
}
}
@ -169,16 +179,20 @@ const PrinterTemperaturePanel = ({
style={{ width: '120px' }}
addonAfter='°C'
onChange={(value) => setExtruderTarget(value || 0)}
onPressEnter={handleSetTemperature({
onPressEnter={() =>
handleSetTemperature({
extruder: { target: extruderTarget }
})}
})
}
/>
<Button
type='default'
style={{ width: 40 }}
onClick={handleSetTemperature({
onClick={() =>
handleSetTemperature({
extruder: { target: extruderTarget }
})}
})
}
>
Set
</Button>
@ -226,16 +240,20 @@ const PrinterTemperaturePanel = ({
style={{ width: '120px' }}
addonAfter='°C'
onChange={(value) => setBedTarget(value || 0)}
onPressEnter={handleSetTemperature({
onPressEnter={() =>
handleSetTemperature({
bed: { target: bedTarget }
})}
})
}
/>
<Button
type='default'
style={{ width: 40 }}
onClick={handleSetTemperature({
onClick={() =>
handleSetTemperature({
bed: { target: bedTarget }
})}
})
}
>
Set
</Button>

View File

@ -1,5 +1,5 @@
import PropTypes from 'prop-types'
import { Descriptions, Typography, Space } from 'antd'
import { Descriptions, Typography, Flex } from 'antd'
import { getModelProperty } from '../../../database/ObjectModels'
import ObjectProperty from './ObjectProperty'
import ArrowRightIcon from '../../Icons/ArrowRightIcon'
@ -51,7 +51,7 @@ const PropertyChanges = ({ type, value }) => {
}
return (
<Descriptions.Item key={key} label={changeProperty.label}>
<Space>
<Flex gap={'small'}>
{value?.old ? (
<ObjectProperty
{...changeProperty}
@ -73,7 +73,7 @@ const PropertyChanges = ({ type, value }) => {
objectData={value?.new}
/>
) : null}
</Space>
</Flex>
</Descriptions.Item>
)
})}

View File

@ -1,14 +1,11 @@
import { useState, useContext, useEffect, useRef, useCallback } from 'react'
import { useState } from 'react'
import PropTypes from 'prop-types'
import { Flex, Alert, Card, Spin, Splitter, Button, Modal, Input } from 'antd'
import { Flex, Alert, Card, Spin, Splitter, Button, Modal } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import PlusIcon from '../../Icons/PlusIcon.jsx'
import MinusIcon from '../../Icons/MinusIcon.jsx'
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon.jsx'
import CheckCircleIcon from '../../Icons/CheckCircleIcon.jsx'
import ObjectProperty from '../common/ObjectProperty.jsx'
import { ApiServerContext } from '../context/ApiServerContext.jsx'
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
import TemplatePreview from './TemplatePreview.jsx'
const TemplateEditor = ({
objectData,
@ -17,134 +14,36 @@ const TemplateEditor = ({
isEditing,
style
}) => {
const iframeRef = useRef(null)
const { fetchTemplatePreview } = useContext(ApiServerContext)
const [testObjectOpen, setTestObjectOpen] = useState(false)
const [previewMessage, setPreviewMessage] = useState('No issues found.')
const [previewError, setPreviewError] = useState(false)
const [previewContent, setPreviewContent] = useState('')
const [reloadLoading, setReloadLoading] = useState(false)
const [previewScale, setPreviewScale] = useState(1)
const updatePreviewContent = (html) => {
if (iframeRef.current) {
// Save current scroll position
const scrollY = iframeRef.current.contentWindow.scrollY
const scrollX = iframeRef.current.contentWindow.scrollX
// Update srcDoc
setPreviewContent(html)
// Restore scroll position after iframe loads new content
const handleLoad = () => {
iframeRef.current.contentWindow.scrollTo(scrollX, scrollY)
iframeRef.current.removeEventListener('load', handleLoad)
const handlePreviewMessage = (message, isError) => {
setPreviewMessage(message)
setPreviewError(isError)
}
iframeRef.current.addEventListener('load', handleLoad)
}
}
const reloadPreview = useCallback(
(content, testObject = {}, scale = 1) => {
fetchTemplatePreview(
objectData._id,
content,
testObject,
scale,
(result) => {
setReloadLoading(false)
if (result?.error) {
setPreviewError(true)
setPreviewMessage(result.error)
} else {
setPreviewError(false)
updatePreviewContent(result.html)
setPreviewMessage('No issues found.')
}
}
)
},
[fetchTemplatePreview, objectData?._id]
)
// Move useEffect to component level and use state to track objectData changes
useEffect(() => {
if (objectData) {
console.log('PreviewScale', previewScale)
reloadPreview(objectData.content, objectData.testObject, previewScale)
}
}, [objectData, previewScale, reloadPreview])
return (
<>
<Splitter className={'farmcontrol-splitter'}>
{collapseState.preview == true && (
<Splitter.Panel style={{ height: '100%' }}>
<Spin
spinning={loading || reloadLoading}
indicator={<LoadingOutlined />}
<Card
spinning={loading}
style={style}
styles={{ body: { height: '100%' } }}
>
<Card style={style} styles={{ body: { height: '100%' } }}>
<Flex vertical gap={'middle'} style={{ height: '100%' }}>
<Flex gap={'small'}>
{objectData?.objectType ? (
<ObjectProperty
objectType={objectData?.objectType}
name={'testObject'}
isEditing={true}
objectData={objectData}
disabled={!isEditing || objectData?.global}
type={'object'}
<TemplatePreview
objectData={objectData?.testObject}
documentTemplate={objectData}
loading={loading}
isEditing={isEditing}
style={style}
onTestObjectOpen={() => setTestObjectOpen(true)}
onPreviewMessage={handlePreviewMessage}
showTestObject={true}
/>
) : (
<div style={{ flexGrow: 1 }}>
<Input disabled={true} />
</div>
)}
<Button
icon={<InfoCircleIcon />}
disabled={objectData?.global}
onClick={() => {
setTestObjectOpen(true)
}}
/>
<Button
icon={<PlusIcon />}
onClick={() => {
setPreviewScale((prev) => prev + 0.05)
}}
/>
<Button
icon={<MinusIcon />}
onClick={() => {
setPreviewScale((prev) => prev - 0.05)
}}
/>
<Button
readOnly={true}
style={{ width: '65px' }}
onClick={() => {
setPreviewScale(1)
}}
>
{previewScale.toFixed(2)}x
</Button>
</Flex>
<iframe
ref={iframeRef}
srcDoc={previewContent}
frameBorder='0'
style={{
width: '100%',
flexGrow: 1,
border: '1px solid #85858541',
overflow: 'auto'
}}
/>
</Flex>
</Card>
</Spin>
</Splitter.Panel>
)}
{collapseState.editor == true && (

View File

@ -0,0 +1,158 @@
import { useState, useContext, useEffect, useRef, useCallback } from 'react'
import PropTypes from 'prop-types'
import { Flex, Button, Input } from 'antd'
import PlusIcon from '../../Icons/PlusIcon.jsx'
import MinusIcon from '../../Icons/MinusIcon.jsx'
import InfoCircleIcon from '../../Icons/InfoCircleIcon.jsx'
import ObjectProperty from '../common/ObjectProperty.jsx'
import { ApiServerContext } from '../context/ApiServerContext.jsx'
const TemplatePreview = ({
objectData,
documentTemplate,
loading,
isEditing,
onTestObjectOpen,
onPreviewMessage,
showTestObject = false
}) => {
const iframeRef = useRef(null)
const { fetchTemplatePreview } = useContext(ApiServerContext)
const [previewContent, setPreviewContent] = useState('')
const [reloadLoading, setReloadLoading] = useState(false)
const [previewScale, setPreviewScale] = useState(1)
const updatePreviewContent = (html) => {
if (iframeRef.current) {
// Save current scroll position
const scrollY = iframeRef.current.contentWindow.scrollY
const scrollX = iframeRef.current.contentWindow.scrollX
// Update srcDoc
setPreviewContent(html)
// Restore scroll position after iframe loads new content
const handleLoad = () => {
iframeRef.current.contentWindow.scrollTo(scrollX, scrollY)
iframeRef.current.removeEventListener('load', handleLoad)
}
iframeRef.current.addEventListener('load', handleLoad)
}
}
const reloadPreview = useCallback(
(content, testObject = {}, scale = 1) => {
if (!objectData?._id) {
onPreviewMessage('No object data available for preview.', true)
return
}
setReloadLoading(true)
fetchTemplatePreview(
documentTemplate._id,
content,
testObject,
scale,
(result) => {
setReloadLoading(false)
if (result?.error) {
// Handle error through parent component
onPreviewMessage(result.error, true)
} else {
updatePreviewContent(result.html)
onPreviewMessage('No issues found.', false)
}
}
)
},
[fetchTemplatePreview, objectData?._id, onPreviewMessage]
)
// Move useEffect to component level and use state to track objectData changes
useEffect(() => {
if (objectData && documentTemplate?.content) {
console.log('PreviewScale', previewScale)
reloadPreview(documentTemplate.content, objectData, previewScale)
}
}, [objectData, documentTemplate, previewScale, reloadPreview])
return (
<Flex vertical gap={'middle'} style={{ height: '100%' }}>
<Flex gap={'small'}>
{showTestObject == true ? (
<>
{documentTemplate?.objectType ? (
<ObjectProperty
objectType={documentTemplate?.objectType}
name={'testObject'}
isEditing={true}
objectData={objectData}
disabled={!isEditing || objectData?.global}
type={'object'}
/>
) : (
<div style={{ flexGrow: 1 }}>
<Input disabled={true} />
</div>
)}
<Button
icon={<InfoCircleIcon />}
disabled={objectData?.global}
onClick={() => {
onTestObjectOpen()
}}
/>
</>
) : null}
<Button
icon={<PlusIcon />}
onClick={() => {
setPreviewScale((prev) => prev + 0.05)
}}
/>
<Button
icon={<MinusIcon />}
onClick={() => {
setPreviewScale((prev) => prev - 0.05)
}}
/>
<Button
readOnly={true}
style={{ width: '65px' }}
loading={loading || reloadLoading}
disabled={loading || reloadLoading}
onClick={() => {
setPreviewScale(1)
}}
>
{previewScale.toFixed(2)}x
</Button>
</Flex>
<iframe
ref={iframeRef}
srcDoc={previewContent}
frameBorder='0'
style={{
width: '100%',
flexGrow: 1,
border: '1px solid #85858541',
overflow: 'auto'
}}
/>
</Flex>
)
}
TemplatePreview.propTypes = {
loading: PropTypes.bool,
objectData: PropTypes.object,
documentTemplate: PropTypes.object,
isEditing: PropTypes.bool,
style: PropTypes.object,
showTestObject: PropTypes.bool,
onTestObjectOpen: PropTypes.func.isRequired,
onPreviewMessage: PropTypes.func.isRequired
}
export default TemplatePreview

View File

@ -12,7 +12,9 @@ const WizardView = ({
title = 'Wizard View',
onSubmit,
formValid,
loading
loading,
sideBar = null,
submitText = 'Done'
}) => {
const [currentStep, setCurrentStep] = useState(0)
const isMobile = useMediaQuery({ maxWidth: 768 })
@ -20,6 +22,9 @@ const WizardView = ({
return (
<Flex gap='middle'>
{!isMobile && showSteps == true ? (
sideBar != null ? (
sideBar
) : (
<div style={{ minWidth: '160px' }}>
<Steps
current={currentStep}
@ -28,19 +33,22 @@ const WizardView = ({
style={{ width: 'fit-content' }}
/>
</div>
)
) : null}
{!isMobile && showSteps == true ? (
<Divider type='vertical' style={{ height: 'unset' }} />
) : null}
<Flex vertical justify='space-between' gap={'middle'}>
<Flex vertical gap='middle' style={{ flexGrow: 1 }}>
<Title level={2} style={{ margin: 0 }}>
{title}
</Title>
<div style={{ minHeight: '260px', marginBottom: 8 }}>
<div style={{ minHeight: '260px', marginBottom: 4 }}>
{steps[currentStep].content}
</div>
</Flex>
<NewObjectButtons
currentStep={currentStep}
totalSteps={steps.length}
@ -49,6 +57,7 @@ const WizardView = ({
onSubmit={onSubmit}
formValid={formValid}
submitLoading={loading}
submitText={submitText}
/>
</Flex>
</Flex>
@ -61,7 +70,9 @@ WizardView.propTypes = {
steps: PropTypes.array.isRequired,
showSteps: PropTypes.bool,
title: PropTypes.string,
loading: PropTypes.bool
loading: PropTypes.bool,
sideBar: PropTypes.node,
submitText: PropTypes.string
}
export default WizardView

View File

@ -7,6 +7,7 @@ import { Job } from './models/Job'
import { Product } from './models/Product'
import { Part } from './models/Part.js'
import { Vendor } from './models/Vendor'
import { File } from './models/File'
import { SubJob } from './models/SubJob'
import { Initial } from './models/Initial'
import { FilamentStock } from './models/FilamentStock'
@ -21,6 +22,7 @@ import { Note } from './models/Note'
import { DocumentSize } from './models/DocumentSize.js'
import { DocumentTemplate } from './models/DocumentTemplate.js'
import { DocumentPrinter } from './models/DocumentPrinter.js'
import { DocumentJob } from './models/DocumentJob.js'
import QuestionCircleIcon from '../components/Icons/QuestionCircleIcon'
export const objectModels = [
@ -33,6 +35,7 @@ export const objectModels = [
Product,
Part,
Vendor,
File,
SubJob,
Initial,
FilamentStock,
@ -46,7 +49,8 @@ export const objectModels = [
Note,
DocumentSize,
DocumentTemplate,
DocumentPrinter
DocumentPrinter,
DocumentJob
]
// Re-export individual models for direct access
@ -60,6 +64,7 @@ export {
Product,
Part,
Vendor,
File,
SubJob,
Initial,
FilamentStock,
@ -73,7 +78,8 @@ export {
Note,
DocumentSize,
DocumentTemplate,
DocumentPrinter
DocumentPrinter,
DocumentJob
}
export function getModelByName(name, ignoreCase = false) {

View File

@ -1,6 +1,7 @@
import JobIcon from '../../components/Icons/JobIcon'
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
import ReloadIcon from '../../components/Icons/ReloadIcon'
import CheckIcon from '../../components/Icons/CheckIcon'
export const Job = {
name: 'job',
@ -16,6 +17,19 @@ export const Job = {
icon: InfoCircleIcon,
url: (_id) => `/dashboard/production/jobs/info?jobId=${_id}`
},
{
name: 'deploy',
label: 'Deploy',
default: true,
row: true,
icon: CheckIcon,
url: (_id) =>
`/dashboard/production/jobs/info?jobId=${_id}?action=deploy`,
disabled: (objectData) => {
console.log('Should be disabled', objectData?.state?.type != 'draft')
return objectData?.state?.type != 'draft'
}
},
{
name: 'reload',
label: 'Reload',

View File

@ -76,12 +76,24 @@ export const Printer = {
showName: false,
readOnly: true
},
{
name: 'active',
label: 'Active',
type: 'bool',
required: true
},
{
name: 'online',
label: 'Online',
type: 'bool',
readOnly: true
},
{
name: 'vendor',
label: 'Vendor',
type: 'object',
objectType: 'vendor',
required: true
required: false
},
{
name: 'vendor._id',
@ -149,7 +161,7 @@ export const Printer = {
label: 'Filament Stock',
type: 'object',
objectType: 'filamentStock',
required: true
required: false
},
{
name: 'currentFilamentStock._id',
@ -158,6 +170,42 @@ export const Printer = {
objectType: 'filamentStock',
showHyperlink: true,
readOnly: true
},
{
name: 'currentJob',
label: 'Current Job',
type: 'object',
objectType: 'job',
required: false
},
{
name: 'currentJob._id',
label: 'Current Job ID',
type: 'id',
objectType: 'job',
showHyperlink: true,
readOnly: true
},
{
name: 'currentSubJob',
label: 'Current Sub Job',
type: 'object',
objectType: 'subJob',
required: false
},
{
name: 'currentSubJob._id',
label: 'Current Sub Job ID',
type: 'id',
objectType: 'subJob',
showHyperlink: true,
readOnly: true
},
{
name: 'alerts',
label: 'Alerts',
type: 'alerts',
required: false
}
]
}