Compare commits
No commits in common. "34d919d88ebb729da598b7135e9f1172cc467cf6" and "7a4dec3f5422eb2815a9e66137a53f40e93e87bd" have entirely different histories.
34d919d88e
...
7a4dec3f54
@ -28,12 +28,7 @@
|
|||||||
.ant-select,
|
.ant-select,
|
||||||
.ant-progress,
|
.ant-progress,
|
||||||
.ant-collapse,
|
.ant-collapse,
|
||||||
.ant-picker-dropdown,
|
|
||||||
.ant-radio-group,
|
.ant-radio-group,
|
||||||
.g2-tooltip-title,
|
|
||||||
.g2-tooltip-list-item,
|
|
||||||
.ant-picker-input,
|
|
||||||
.ant-picker-header-view button,
|
|
||||||
[class*=' ant-radio'] {
|
[class*=' ant-radio'] {
|
||||||
font-family: 'DM Sans';
|
font-family: 'DM Sans';
|
||||||
}
|
}
|
||||||
@ -396,7 +391,3 @@ body {
|
|||||||
.object-info-descriptions table {
|
.object-info-descriptions table {
|
||||||
table-layout: fixed !important;
|
table-layout: fixed !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-btn-variant-outlined.ant-btn.ant-btn-icon-only {
|
|
||||||
min-width: 32px;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"homepage": "./",
|
"homepage": "./",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/charts": "^2.6.5",
|
"@ant-design/charts": "^2.6.2",
|
||||||
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
|
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
|
||||||
"@codemirror/lang-cpp": "^6.0.3",
|
"@codemirror/lang-cpp": "^6.0.3",
|
||||||
"@codemirror/lang-css": "^6.3.1",
|
"@codemirror/lang-css": "^6.3.1",
|
||||||
|
|||||||
@ -85,7 +85,6 @@ const Jobs = () => {
|
|||||||
open={newJobOpen}
|
open={newJobOpen}
|
||||||
footer={null}
|
footer={null}
|
||||||
width={700}
|
width={700}
|
||||||
destroyOnHidden={true}
|
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setNewJobOpen(false)
|
setNewJobOpen(false)
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -24,7 +24,6 @@ const NewJob = ({ onOk, defaultValues }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
labelWidth='100px'
|
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
@ -37,14 +36,11 @@ const NewJob = ({ onOk, defaultValues }) => {
|
|||||||
type='job'
|
type='job'
|
||||||
column={1}
|
column={1}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
labelWidth='100px'
|
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
_id: false,
|
_id: false,
|
||||||
createdAt: false,
|
createdAt: false,
|
||||||
updatedAt: false,
|
updatedAt: false,
|
||||||
startedAt: false,
|
startedAt: false
|
||||||
finishedAt: false,
|
|
||||||
totalTime: false
|
|
||||||
}}
|
}}
|
||||||
isEditing={false}
|
isEditing={false}
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
|
|||||||
@ -25,7 +25,6 @@ const NewPrinter = ({ onOk, defaultValues }) => {
|
|||||||
<ObjectInfo
|
<ObjectInfo
|
||||||
type='printer'
|
type='printer'
|
||||||
column={1}
|
column={1}
|
||||||
labelWidth='100px'
|
|
||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={true}
|
required={true}
|
||||||
@ -43,7 +42,6 @@ const NewPrinter = ({ onOk, defaultValues }) => {
|
|||||||
bordered={false}
|
bordered={false}
|
||||||
isEditing={true}
|
isEditing={true}
|
||||||
required={false}
|
required={false}
|
||||||
labelWidth='100px'
|
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
firmware: false,
|
firmware: false,
|
||||||
|
|||||||
@ -155,7 +155,6 @@ const PrinterInfo = () => {
|
|||||||
isEditing={isEditing}
|
isEditing={isEditing}
|
||||||
type='printer'
|
type='printer'
|
||||||
objectData={objectData}
|
objectData={objectData}
|
||||||
labelWidth='175px'
|
|
||||||
visibleProperties={{
|
visibleProperties={{
|
||||||
currentFilamentStock: false,
|
currentFilamentStock: false,
|
||||||
'currentFilamentStock._id': false,
|
'currentFilamentStock._id': false,
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import InfoCollapse from '../common/InfoCollapse'
|
|||||||
import ScrollBox from '../common/ScrollBox'
|
import ScrollBox from '../common/ScrollBox'
|
||||||
|
|
||||||
import { ApiServerContext } from '../context/ApiServerContext'
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
import ObjectTable from '../common/ObjectTable'
|
|
||||||
|
|
||||||
const ProductionOverview = () => {
|
const ProductionOverview = () => {
|
||||||
const { connected } = useContext(ApiServerContext)
|
const { connected } = useContext(ApiServerContext)
|
||||||
@ -17,7 +16,6 @@ const ProductionOverview = () => {
|
|||||||
{
|
{
|
||||||
printerStats: true,
|
printerStats: true,
|
||||||
printerHistory: true,
|
printerHistory: true,
|
||||||
onlinePrinters: true,
|
|
||||||
jobStats: true,
|
jobStats: true,
|
||||||
productionStats: true,
|
productionStats: true,
|
||||||
jobStatsDetails: true
|
jobStatsDetails: true
|
||||||
@ -42,7 +40,6 @@ const ProductionOverview = () => {
|
|||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Printer Statistics'
|
title='Printer Statistics'
|
||||||
icon={null}
|
icon={null}
|
||||||
canCollapse={false}
|
|
||||||
active={collapseState.printerStats}
|
active={collapseState.printerStats}
|
||||||
onToggle={(isActive) =>
|
onToggle={(isActive) =>
|
||||||
updateCollapseState('printerStats', isActive)
|
updateCollapseState('printerStats', isActive)
|
||||||
@ -63,7 +60,6 @@ const ProductionOverview = () => {
|
|||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Job Statistics'
|
title='Job Statistics'
|
||||||
icon={null}
|
icon={null}
|
||||||
canCollapse={false}
|
|
||||||
active={collapseState.jobStats}
|
active={collapseState.jobStats}
|
||||||
onToggle={(isActive) => updateCollapseState('jobStats', isActive)}
|
onToggle={(isActive) => updateCollapseState('jobStats', isActive)}
|
||||||
className='no-t-padding-collapse'
|
className='no-t-padding-collapse'
|
||||||
@ -80,7 +76,7 @@ const ProductionOverview = () => {
|
|||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
|
|
||||||
<Flex gap='large' wrap='wrap'>
|
<Flex gap='large' wrap='wrap'>
|
||||||
<Flex flex={1} vertical style={{ minWidth: '300px' }} gap='large'>
|
<Flex flex={1} vertical style={{ minWidth: '300px' }}>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Printer History'
|
title='Printer History'
|
||||||
icon={null}
|
icon={null}
|
||||||
@ -93,20 +89,8 @@ const ProductionOverview = () => {
|
|||||||
>
|
>
|
||||||
<HistoryDisplay objectType='printer' />
|
<HistoryDisplay objectType='printer' />
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
<InfoCollapse
|
|
||||||
title='Online Printers'
|
|
||||||
icon={null}
|
|
||||||
active={collapseState.onlinePrinters}
|
|
||||||
onToggle={(isActive) =>
|
|
||||||
updateCollapseState('onlinePrinters', isActive)
|
|
||||||
}
|
|
||||||
collapseKey='onlinePrinters'
|
|
||||||
canCollapse={false}
|
|
||||||
>
|
|
||||||
<ObjectTable type='printer' masterFilter={{ online: true }} />
|
|
||||||
</InfoCollapse>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex flex={1} vertical style={{ minWidth: '300px' }} gap='large'>
|
<Flex flex={1} vertical style={{ minWidth: '300px' }}>
|
||||||
<InfoCollapse
|
<InfoCollapse
|
||||||
title='Job History'
|
title='Job History'
|
||||||
icon={null}
|
icon={null}
|
||||||
@ -119,21 +103,6 @@ const ProductionOverview = () => {
|
|||||||
>
|
>
|
||||||
<HistoryDisplay objectType='job' />
|
<HistoryDisplay objectType='job' />
|
||||||
</InfoCollapse>
|
</InfoCollapse>
|
||||||
<InfoCollapse
|
|
||||||
title='Queued Jobs'
|
|
||||||
icon={null}
|
|
||||||
active={collapseState.queuedJobs}
|
|
||||||
onToggle={(isActive) =>
|
|
||||||
updateCollapseState('queuedJobs', isActive)
|
|
||||||
}
|
|
||||||
canCollapse={false}
|
|
||||||
collapseKey='queuedJobs'
|
|
||||||
>
|
|
||||||
<ObjectTable
|
|
||||||
type='job'
|
|
||||||
masterFilter={{ 'state.type': 'queued' }}
|
|
||||||
/>
|
|
||||||
</InfoCollapse>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState, useContext, useMemo } from 'react'
|
import { useEffect, useState, useContext, useMemo } from 'react'
|
||||||
import { Card, Segmented, Flex, Popover, DatePicker, Button, Space } from 'antd'
|
import { Card, Spin, Segmented, Flex } from 'antd'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
import { Column } from '@ant-design/charts'
|
import { Column } from '@ant-design/charts'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
@ -7,25 +8,14 @@ import { ApiServerContext } from '../context/ApiServerContext'
|
|||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { useThemeContext } from '../context/ThemeContext'
|
import { useThemeContext } from '../context/ThemeContext'
|
||||||
import LoadingPlaceholder from './LoadingPlaceholder'
|
|
||||||
import MissingPlaceholder from './MissingPlaceholder'
|
|
||||||
import CheckIcon from '../../Icons/CheckIcon'
|
|
||||||
|
|
||||||
const HistoryDisplay = ({
|
const HistoryDisplay = ({ objectType, startDate, endDate, styles }) => {
|
||||||
objectType,
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
styles,
|
|
||||||
height = 400
|
|
||||||
}) => {
|
|
||||||
const { getModelHistory, connected } = useContext(ApiServerContext)
|
const { getModelHistory, connected } = useContext(ApiServerContext)
|
||||||
const { token } = useContext(AuthContext)
|
const { token } = useContext(AuthContext)
|
||||||
const [historyData, setHistoryData] = useState([])
|
const [historyData, setHistoryData] = useState([])
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
const [timeRange, setTimeRange] = useState('4hrs')
|
const [timeRange, setTimeRange] = useState('4hrs')
|
||||||
const [startCustomDate, setStartCustomDate] = useState(null)
|
|
||||||
const [endCustomDate, setEndCustomDate] = useState(null)
|
|
||||||
const { isDarkMode, getColors } = useThemeContext()
|
const { isDarkMode, getColors } = useThemeContext()
|
||||||
// Calculate dates based on selected time range or provided props
|
// Calculate dates based on selected time range or provided props
|
||||||
const { defaultStartDate, defaultEndDate } = useMemo(() => {
|
const { defaultStartDate, defaultEndDate } = useMemo(() => {
|
||||||
@ -40,14 +30,6 @@ const HistoryDisplay = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle custom date range
|
|
||||||
if (timeRange === 'custom' && startCustomDate && endCustomDate) {
|
|
||||||
return {
|
|
||||||
defaultStartDate: startCustomDate.toDate(),
|
|
||||||
defaultEndDate: endCustomDate.toDate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, calculate based on selected time range
|
// Otherwise, calculate based on selected time range
|
||||||
const hoursMap = {
|
const hoursMap = {
|
||||||
'8hrs': 8,
|
'8hrs': 8,
|
||||||
@ -61,7 +43,7 @@ const HistoryDisplay = ({
|
|||||||
defaultStartDate: new Date(now.getTime() - hours * 60 * 60 * 1000),
|
defaultStartDate: new Date(now.getTime() - hours * 60 * 60 * 1000),
|
||||||
defaultEndDate: now
|
defaultEndDate: now
|
||||||
}
|
}
|
||||||
}, [startDate, endDate, timeRange, startCustomDate, endCustomDate])
|
}, [startDate, endDate, timeRange])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!objectType || !getModelHistory || !token || !connected) {
|
if (!objectType || !getModelHistory || !token || !connected) {
|
||||||
@ -269,7 +251,6 @@ const HistoryDisplay = ({
|
|||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
data: chartData,
|
data: chartData,
|
||||||
height,
|
|
||||||
xField: 'dateFormatted',
|
xField: 'dateFormatted',
|
||||||
yField: 'value',
|
yField: 'value',
|
||||||
theme: {
|
theme: {
|
||||||
@ -301,58 +282,14 @@ const HistoryDisplay = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const customTimeRangeContent = (
|
|
||||||
<Space.Compact>
|
|
||||||
<DatePicker.RangePicker
|
|
||||||
onChange={(dates) => {
|
|
||||||
if (dates) {
|
|
||||||
setStartCustomDate(dates[0])
|
|
||||||
setEndCustomDate(dates[1])
|
|
||||||
} else {
|
|
||||||
setStartCustomDate(null)
|
|
||||||
setEndCustomDate(null)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={[startCustomDate, endCustomDate]}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
type='primary'
|
|
||||||
onClick={() => {
|
|
||||||
if (startCustomDate && endCustomDate) {
|
|
||||||
setTimeRange('custom')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={!startCustomDate || !endCustomDate}
|
|
||||||
icon={<CheckIcon />}
|
|
||||||
></Button>
|
|
||||||
</Space.Compact>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Spin spinning={loading} indicator={<LoadingOutlined spin />}>
|
||||||
<Card
|
<Card
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
styles={{ body: { padding: '12px', ...styles } }}
|
styles={{ body: { padding: '12px', ...styles } }}
|
||||||
>
|
>
|
||||||
{!startDate && !endDate && (
|
{!startDate && !endDate && (
|
||||||
<Flex justify='space-between'>
|
<Flex justify='flex-end'>
|
||||||
<Flex align='center' gap='5px'>
|
|
||||||
<Popover
|
|
||||||
content={customTimeRangeContent}
|
|
||||||
trigger='hover'
|
|
||||||
arrow={false}
|
|
||||||
placement='bottomLeft'
|
|
||||||
styles={{ body: { borderRadius: '22.5px' } }}
|
|
||||||
>
|
|
||||||
<Segmented
|
|
||||||
size='small'
|
|
||||||
options={[{ label: 'Custom', value: 'custom' }]}
|
|
||||||
value={timeRange}
|
|
||||||
onChange={setTimeRange}
|
|
||||||
disabled={loading}
|
|
||||||
/>
|
|
||||||
</Popover>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Segmented
|
<Segmented
|
||||||
size='small'
|
size='small'
|
||||||
options={[
|
options={[
|
||||||
@ -364,22 +301,12 @@ const HistoryDisplay = ({
|
|||||||
]}
|
]}
|
||||||
value={timeRange}
|
value={timeRange}
|
||||||
onChange={setTimeRange}
|
onChange={setTimeRange}
|
||||||
disabled={loading}
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
{loading == true && (
|
<Column {...config} />
|
||||||
<Flex justify='center' align='center' style={{ height: `${height}px` }}>
|
|
||||||
<LoadingPlaceholder message='Loading history data...' />
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
{chartData.length > 0 && <Column {...config} />}
|
|
||||||
{loading == false && chartData.length == 0 && (
|
|
||||||
<Flex justify='center' align='center' style={{ height: `${height}px` }}>
|
|
||||||
<MissingPlaceholder message='No data available.' />
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Card>
|
</Card>
|
||||||
|
</Spin>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,8 +317,7 @@ HistoryDisplay.propTypes = {
|
|||||||
PropTypes.instanceOf(Date)
|
PropTypes.instanceOf(Date)
|
||||||
]),
|
]),
|
||||||
styles: PropTypes.object,
|
styles: PropTypes.object,
|
||||||
endDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
|
endDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)])
|
||||||
height: PropTypes.string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HistoryDisplay
|
export default HistoryDisplay
|
||||||
|
|||||||
@ -56,26 +56,6 @@ const ObjectSelect = ({
|
|||||||
// Normalize a value to an identity string so we can detect in-place _id updates
|
// Normalize a value to an identity string so we can detect in-place _id updates
|
||||||
const getValueIdentity = useCallback((val) => {
|
const getValueIdentity = useCallback((val) => {
|
||||||
if (val && typeof val === 'object') {
|
if (val && typeof val === 'object') {
|
||||||
// Handle arrays
|
|
||||||
if (Array.isArray(val)) {
|
|
||||||
const ids = val
|
|
||||||
.map((item) => {
|
|
||||||
if (item && typeof item === 'object') {
|
|
||||||
if (item._id) return String(item._id)
|
|
||||||
if (
|
|
||||||
item.value &&
|
|
||||||
typeof item.value === 'object' &&
|
|
||||||
item.value._id
|
|
||||||
)
|
|
||||||
return String(item.value._id)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
.filter(Boolean)
|
|
||||||
.sort()
|
|
||||||
return ids.length > 0 ? ids.join(',') : JSON.stringify(val)
|
|
||||||
}
|
|
||||||
// Handle single objects
|
|
||||||
if (val._id) return String(val._id)
|
if (val._id) return String(val._id)
|
||||||
if (val.value && typeof val.value === 'object' && val.value._id)
|
if (val.value && typeof val.value === 'object' && val.value._id)
|
||||||
return String(val.value._id)
|
return String(val.value._id)
|
||||||
|
|||||||
@ -969,16 +969,10 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
const timeRangeMs = endDate.getTime() - startDate.getTime()
|
const timeRangeMs = endDate.getTime() - startDate.getTime()
|
||||||
const oneHourMs = 60 * 60 * 1000
|
const oneHourMs = 60 * 60 * 1000
|
||||||
const twelveHoursMs = 12 * 60 * 60 * 1000
|
const twelveHoursMs = 12 * 60 * 60 * 1000
|
||||||
const oneDayMs = 24 * 60 * 60 * 1000
|
|
||||||
const threeDaysMs = 3 * 24 * 60 * 60 * 1000
|
|
||||||
|
|
||||||
// Determine interval based on time range
|
// Determine interval based on time range
|
||||||
let intervalMinutes = 1 // Default: 1 minute
|
let intervalMinutes = 1 // Default: 1 minute
|
||||||
if (timeRangeMs > threeDaysMs) {
|
if (timeRangeMs > twelveHoursMs) {
|
||||||
intervalMinutes = 60 // Over 1 day: 60 minutes
|
|
||||||
} else if (timeRangeMs > oneDayMs) {
|
|
||||||
intervalMinutes = 30 // Over 2 days: 30 minutes
|
|
||||||
} else if (timeRangeMs > twelveHoursMs) {
|
|
||||||
intervalMinutes = 10 // Over 12 hours: 10 minutes
|
intervalMinutes = 10 // Over 12 hours: 10 minutes
|
||||||
} else if (timeRangeMs > oneHourMs) {
|
} else if (timeRangeMs > oneHourMs) {
|
||||||
intervalMinutes = 5 // Over 1 hour: 5 minutes
|
intervalMinutes = 5 // Over 1 hour: 5 minutes
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import JobIcon from '../../components/Icons/JobIcon'
|
|||||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
import ReloadIcon from '../../components/Icons/ReloadIcon'
|
||||||
import CheckIcon from '../../components/Icons/CheckIcon'
|
import CheckIcon from '../../components/Icons/CheckIcon'
|
||||||
import dayjs from 'dayjs'
|
|
||||||
|
|
||||||
export const Job = {
|
export const Job = {
|
||||||
name: 'job',
|
name: 'job',
|
||||||
@ -38,7 +37,7 @@ export const Job = {
|
|||||||
url: (_id) => `/dashboard/production/jobs/info?jobId=${_id}&action=reload`
|
url: (_id) => `/dashboard/production/jobs/info?jobId=${_id}&action=reload`
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
columns: ['_id', 'quantity', 'state', 'gcodeFile', 'createdAt'],
|
columns: ['_id', 'gcodeFile', 'quantity', 'state', 'createdAt'],
|
||||||
filters: ['state', '_id', 'gcodeFile', 'quantity'],
|
filters: ['state', '_id', 'gcodeFile', 'quantity'],
|
||||||
sorters: ['createdAt', 'state', 'quantity', 'gcodeFile'],
|
sorters: ['createdAt', 'state', 'quantity', 'gcodeFile'],
|
||||||
properties: [
|
properties: [
|
||||||
@ -78,7 +77,6 @@ export const Job = {
|
|||||||
name: 'quantity',
|
name: 'quantity',
|
||||||
label: 'Quantity',
|
label: 'Quantity',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
columnFixed: 'left',
|
|
||||||
columnWidth: 125,
|
columnWidth: 125,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
@ -105,37 +103,10 @@ export const Job = {
|
|||||||
name: 'gcodeFile',
|
name: 'gcodeFile',
|
||||||
label: 'GCode File',
|
label: 'GCode File',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
columnFixed: 'left',
|
||||||
objectType: 'gcodeFile',
|
objectType: 'gcodeFile',
|
||||||
required: true,
|
required: true,
|
||||||
showHyperlink: true
|
showHyperlink: true
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'totalTime',
|
|
||||||
label: 'Total Time',
|
|
||||||
type: 'text',
|
|
||||||
readOnly: true,
|
|
||||||
value: (objectData) => {
|
|
||||||
if (!objectData?.startedAt || !objectData?.finishedAt) {
|
|
||||||
return '-'
|
|
||||||
}
|
|
||||||
const totalSeconds = dayjs(objectData?.finishedAt).diff(
|
|
||||||
dayjs(objectData?.startedAt),
|
|
||||||
'seconds'
|
|
||||||
)
|
|
||||||
const days = Math.floor(totalSeconds / 86400)
|
|
||||||
const hours = Math.floor((totalSeconds % 86400) / 3600)
|
|
||||||
const minutes = Math.floor((totalSeconds % 3600) / 60)
|
|
||||||
const seconds = totalSeconds % 60
|
|
||||||
|
|
||||||
const parts = []
|
|
||||||
if (days > 0) parts.push(`${days}d`)
|
|
||||||
if (hours > 0) parts.push(`${hours}h`)
|
|
||||||
if (minutes > 0) parts.push(`${minutes}m`)
|
|
||||||
if (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`)
|
|
||||||
|
|
||||||
return parts.join(' ')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
stats: [
|
stats: [
|
||||||
|
|||||||
@ -49,21 +49,7 @@ export const OrderItem = {
|
|||||||
type: 'dateTime',
|
type: 'dateTime',
|
||||||
readOnly: true
|
readOnly: true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: '_reference',
|
|
||||||
label: 'Reference',
|
|
||||||
type: 'reference',
|
|
||||||
objectType: 'orderItem',
|
|
||||||
showCopy: true,
|
|
||||||
readOnly: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updatedAt',
|
|
||||||
label: 'Updated At',
|
|
||||||
type: 'dateTime',
|
|
||||||
readOnly: true
|
|
||||||
},
|
|
||||||
{ name: 'state', label: 'State', type: 'state', readOnly: true },
|
|
||||||
{
|
{
|
||||||
name: 'orderType',
|
name: 'orderType',
|
||||||
label: 'Order Type',
|
label: 'Order Type',
|
||||||
@ -71,7 +57,12 @@ export const OrderItem = {
|
|||||||
masterFilter: ['purchaseOrder', 'salesOrder'],
|
masterFilter: ['purchaseOrder', 'salesOrder'],
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Updated At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'order',
|
name: 'order',
|
||||||
label: 'Order',
|
label: 'Order',
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import SubJobIcon from '../../components/Icons/SubJobIcon'
|
import SubJobIcon from '../../components/Icons/SubJobIcon'
|
||||||
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
import InfoCircleIcon from '../../components/Icons/InfoCircleIcon'
|
||||||
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
import XMarkIcon from '../../components/Icons/XMarkIcon'
|
||||||
import dayjs from 'dayjs'
|
|
||||||
|
|
||||||
export const SubJob = {
|
export const SubJob = {
|
||||||
name: 'subJob',
|
name: 'subJob',
|
||||||
@ -69,6 +68,7 @@ export const SubJob = {
|
|||||||
readOnly: true,
|
readOnly: true,
|
||||||
columnWidth: 175
|
columnWidth: 175
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'moonrakerJobId',
|
name: 'moonrakerJobId',
|
||||||
label: 'Moonraker Job ID',
|
label: 'Moonraker Job ID',
|
||||||
@ -76,12 +76,28 @@ export const SubJob = {
|
|||||||
columnWidth: 140,
|
columnWidth: 140,
|
||||||
showCopy: true
|
showCopy: true
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'startedAt',
|
name: 'startedAt',
|
||||||
label: 'Started At',
|
label: 'Started At',
|
||||||
type: 'dateTime',
|
type: 'dateTime',
|
||||||
readOnly: true
|
readOnly: true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'createdPartStock',
|
||||||
|
label: 'Created Part Stock',
|
||||||
|
type: 'bool',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'finishedAt',
|
||||||
|
label: 'Finished At',
|
||||||
|
type: 'dateTime',
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'job',
|
name: 'job',
|
||||||
label: 'Job',
|
label: 'Job',
|
||||||
@ -89,12 +105,7 @@ export const SubJob = {
|
|||||||
objectType: 'job',
|
objectType: 'job',
|
||||||
showHyperlink: true
|
showHyperlink: true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'finishedAt',
|
|
||||||
label: 'Finished At',
|
|
||||||
type: 'dateTime',
|
|
||||||
readOnly: true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'printer',
|
name: 'printer',
|
||||||
label: 'Printer',
|
label: 'Printer',
|
||||||
@ -102,33 +113,6 @@ export const SubJob = {
|
|||||||
columnFixed: 'left',
|
columnFixed: 'left',
|
||||||
objectType: 'printer',
|
objectType: 'printer',
|
||||||
showHyperlink: true
|
showHyperlink: true
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'totalTime',
|
|
||||||
label: 'Total Time',
|
|
||||||
type: 'text',
|
|
||||||
readOnly: true,
|
|
||||||
value: (objectData) => {
|
|
||||||
if (!objectData?.startedAt || !objectData?.finishedAt) {
|
|
||||||
return '-'
|
|
||||||
}
|
|
||||||
const totalSeconds = dayjs(objectData?.finishedAt).diff(
|
|
||||||
dayjs(objectData?.startedAt),
|
|
||||||
'seconds'
|
|
||||||
)
|
|
||||||
const days = Math.floor(totalSeconds / 86400)
|
|
||||||
const hours = Math.floor((totalSeconds % 86400) / 3600)
|
|
||||||
const minutes = Math.floor((totalSeconds % 3600) / 60)
|
|
||||||
const seconds = totalSeconds % 60
|
|
||||||
|
|
||||||
const parts = []
|
|
||||||
if (days > 0) parts.push(`${days}d`)
|
|
||||||
if (hours > 0) parts.push(`${hours}h`)
|
|
||||||
if (minutes > 0) parts.push(`${minutes}m`)
|
|
||||||
if (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`)
|
|
||||||
|
|
||||||
return parts.join(' ')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
"@ant-design/charts@^2.6.5":
|
"@ant-design/charts@^2.6.2":
|
||||||
version "2.6.6"
|
version "2.6.6"
|
||||||
resolved "https://registry.yarnpkg.com/@ant-design/charts/-/charts-2.6.6.tgz#236430f56bc4a130ca3119c9cd14873f45d51d1c"
|
resolved "https://registry.yarnpkg.com/@ant-design/charts/-/charts-2.6.6.tgz#236430f56bc4a130ca3119c9cd14873f45d51d1c"
|
||||||
integrity sha512-Mw2XqB9c7JoENyewJmtxU+5TU2sW5VEyct2f6n4HjJ/6hBo4ht3qdu965G3UrNLyiRctd47Qje32u+8DeFZ6Bg==
|
integrity sha512-Mw2XqB9c7JoENyewJmtxU+5TU2sW5VEyct2f6n4HjJ/6hBo4ht3qdu965G3UrNLyiRctd47Qje32u+8DeFZ6Bg==
|
||||||
@ -1041,9 +1041,9 @@
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
global-agent "^3.0.0"
|
global-agent "^3.0.0"
|
||||||
|
|
||||||
"@electron/node-gyp@https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2":
|
"@electron/node-gyp@git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2":
|
||||||
version "10.2.0-electron.1"
|
version "10.2.0-electron.1"
|
||||||
resolved "https://github.com/electron/node-gyp#06b29aafb7708acef8b3669835c8a7857ebc92d2"
|
resolved "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2"
|
||||||
dependencies:
|
dependencies:
|
||||||
env-paths "^2.2.0"
|
env-paths "^2.2.0"
|
||||||
exponential-backoff "^3.1.1"
|
exponential-backoff "^3.1.1"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user