Refactored state display components to utilize StateTag for consistent state representation across AuditLogs, JobState, PrinterState, SubJobState, and others. Removed redundant badge logic and improved ObjectSelect for multi-select capabilities. Cleaned up unused props and streamlined vendor selection process.
This commit is contained in:
parent
f220d81722
commit
859ae656d6
@ -24,6 +24,7 @@ import AuditLogIcon from '../../Icons/AuditLogIcon'
|
|||||||
import XMarkIcon from '../../Icons/XMarkIcon'
|
import XMarkIcon from '../../Icons/XMarkIcon'
|
||||||
import CheckIcon from '../../Icons/CheckIcon'
|
import CheckIcon from '../../Icons/CheckIcon'
|
||||||
import BoolDisplay from '../common/BoolDisplay'
|
import BoolDisplay from '../common/BoolDisplay'
|
||||||
|
import StateTag from '../common/StateTag'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
@ -48,9 +49,7 @@ const formatValue = (value, propertyName) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (propertyName === 'state' && typeof value === 'object' && value.type) {
|
if (propertyName === 'state' && typeof value === 'object' && value.type) {
|
||||||
return (
|
return <StateTag state={value.type} />
|
||||||
<Text>{value.type.charAt(0).toUpperCase() + value.type.slice(1)}</Text>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the value is a timestamp (ISO date string)
|
// Check if the value is a timestamp (ISO date string)
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import IdText from './IdText'
|
|||||||
import { AuditOutlined, LoadingOutlined } from '@ant-design/icons'
|
import { AuditOutlined, LoadingOutlined } from '@ant-design/icons'
|
||||||
import TimeDisplay from '../common/TimeDisplay'
|
import TimeDisplay from '../common/TimeDisplay'
|
||||||
import BoolDisplay from './BoolDisplay'
|
import BoolDisplay from './BoolDisplay'
|
||||||
|
import StateTag from './StateTag'
|
||||||
|
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
@ -33,9 +34,7 @@ const formatValue = (value, propertyName) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (propertyName === 'state' && typeof value === 'object' && value.type) {
|
if (propertyName === 'state' && typeof value === 'object' && value.type) {
|
||||||
return (
|
return <StateTag state={value.type} />
|
||||||
<Text>{value.type.charAt(0).toUpperCase() + value.type.slice(1)}</Text>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the value is a timestamp (ISO date string)
|
// Check if the value is a timestamp (ISO date string)
|
||||||
|
|||||||
@ -28,9 +28,4 @@ FilamentSelect.propTypes = {
|
|||||||
useFilter: PropTypes.bool
|
useFilter: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
FilamentSelect.defaultProps = {
|
|
||||||
filter: {},
|
|
||||||
useFilter: false
|
|
||||||
}
|
|
||||||
|
|
||||||
export default FilamentSelect
|
export default FilamentSelect
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Badge, Progress, Flex, Typography, Tag, Space } from 'antd'
|
import { Progress, Flex, Typography, Space } from 'antd'
|
||||||
import React, { useState, useContext, useEffect } from 'react'
|
import React, { useState, useContext, useEffect } from 'react'
|
||||||
import { SocketContext } from '../context/SocketContext'
|
import { SocketContext } from '../context/SocketContext'
|
||||||
import IdText from './IdText'
|
import IdText from './IdText'
|
||||||
|
import StateTag from './StateTag'
|
||||||
|
|
||||||
const JobState = ({
|
const JobState = ({
|
||||||
job,
|
job,
|
||||||
@ -12,13 +13,12 @@ const JobState = ({
|
|||||||
showQuantity = true
|
showQuantity = true
|
||||||
}) => {
|
}) => {
|
||||||
const { socket } = useContext(SocketContext)
|
const { socket } = useContext(SocketContext)
|
||||||
const [badgeStatus, setBadgeStatus] = useState('default')
|
|
||||||
const [badgeText, setBadgeText] = useState('Unknown')
|
|
||||||
const [currentState, setCurrentState] = useState(
|
const [currentState, setCurrentState] = useState(
|
||||||
job?.state || { type: 'unknown', progress: 0 }
|
job?.state || { type: 'unknown', progress: 0 }
|
||||||
)
|
)
|
||||||
const [initialized, setInitialized] = useState(false)
|
const [initialized, setInitialized] = useState(false)
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (socket && !initialized && job?._id) {
|
if (socket && !initialized && job?._id) {
|
||||||
setInitialized(true)
|
setInitialized(true)
|
||||||
@ -35,38 +35,6 @@ const JobState = ({
|
|||||||
}
|
}
|
||||||
}, [socket, initialized, job?._id])
|
}, [socket, initialized, job?._id])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
switch (currentState?.type) {
|
|
||||||
case 'draft':
|
|
||||||
setBadgeStatus('default')
|
|
||||||
setBadgeText('Draft')
|
|
||||||
break
|
|
||||||
case 'printing':
|
|
||||||
setBadgeStatus('processing')
|
|
||||||
setBadgeText('Printing')
|
|
||||||
break
|
|
||||||
case 'complete':
|
|
||||||
setBadgeStatus('success')
|
|
||||||
setBadgeText('Complete')
|
|
||||||
break
|
|
||||||
case 'failed':
|
|
||||||
setBadgeStatus('error')
|
|
||||||
setBadgeText('Failed')
|
|
||||||
break
|
|
||||||
case 'queued':
|
|
||||||
setBadgeStatus('warning')
|
|
||||||
setBadgeText('Queued')
|
|
||||||
break
|
|
||||||
case 'paused':
|
|
||||||
setBadgeStatus('warning')
|
|
||||||
setBadgeText('Paused')
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
setBadgeStatus('default')
|
|
||||||
setBadgeText('Unknown')
|
|
||||||
}
|
|
||||||
}, [currentState])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap='small' align={'center'}>
|
<Flex gap='small' align={'center'}>
|
||||||
{showId && (
|
{showId && (
|
||||||
@ -75,12 +43,7 @@ const JobState = ({
|
|||||||
{showQuantity && <Text>({job.quantity})</Text>}
|
{showQuantity && <Text>({job.quantity})</Text>}
|
||||||
{showStatus && (
|
{showStatus && (
|
||||||
<Space>
|
<Space>
|
||||||
<Tag color={badgeStatus} style={{ marginRight: 0 }}>
|
<StateTag state={currentState?.type} />
|
||||||
<Flex gap={6}>
|
|
||||||
<Badge status={badgeStatus} />
|
|
||||||
{badgeText}
|
|
||||||
</Flex>
|
|
||||||
</Tag>
|
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
{showProgress &&
|
{showProgress &&
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import { TreeSelect, Typography, Flex, Badge } from 'antd'
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { getTypeMeta } from '../utils/Utils'
|
import { getTypeMeta } from '../utils/Utils'
|
||||||
import IdText from './IdText'
|
import IdText from './IdText'
|
||||||
|
import CountryDisplay from './CountryDisplay'
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
const { SHOW_CHILD } = TreeSelect
|
||||||
/**
|
/**
|
||||||
* ObjectSelect - a generic, reusable async TreeSelect for hierarchical object selection.
|
* ObjectSelect - a generic, reusable async TreeSelect for hierarchical object selection.
|
||||||
*
|
*
|
||||||
@ -14,9 +15,10 @@ const { Text } = Typography
|
|||||||
* - propertyOrder: array of property names for category levels (required)
|
* - propertyOrder: array of property names for category levels (required)
|
||||||
* - filter: object for filtering (optional)
|
* - filter: object for filtering (optional)
|
||||||
* - useFilter: bool (optional)
|
* - useFilter: bool (optional)
|
||||||
* - value: selected value (optional) - can be an object with _id or a simple value
|
* - value: selected value (optional) - can be an object with _id, array of objects, or simple value/array
|
||||||
* - onChange: function (optional)
|
* - onChange: function (optional)
|
||||||
* - showSearch: bool (optional, default false)
|
* - showSearch: bool (optional, default false)
|
||||||
|
* - treeCheckable: bool (optional, default false) - enables multi-select mode with checkboxes
|
||||||
* - treeSelectProps: any other TreeSelect props (optional)
|
* - treeSelectProps: any other TreeSelect props (optional)
|
||||||
*/
|
*/
|
||||||
const ObjectSelect = ({
|
const ObjectSelect = ({
|
||||||
@ -27,13 +29,14 @@ const ObjectSelect = ({
|
|||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
showSearch = false,
|
showSearch = false,
|
||||||
|
treeCheckable = false,
|
||||||
treeSelectProps = {},
|
treeSelectProps = {},
|
||||||
type = 'unknown',
|
type = 'unknown',
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const [treeData, setTreeData] = useState([])
|
const [treeData, setTreeData] = useState([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [defaultValue, setDefaultValue] = useState(value)
|
const [defaultValue, setDefaultValue] = useState(treeCheckable ? [] : value)
|
||||||
const [searchValue, setSearchValue] = useState('')
|
const [searchValue, setSearchValue] = useState('')
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
|
|
||||||
@ -102,7 +105,12 @@ const ObjectSelect = ({
|
|||||||
const renderTitle = useCallback(
|
const renderTitle = useCallback(
|
||||||
(item, isLeaf) => {
|
(item, isLeaf) => {
|
||||||
if (!isLeaf) {
|
if (!isLeaf) {
|
||||||
// For category nodes, just show the value
|
// For category nodes, check if it's a country property
|
||||||
|
const currentProperty = propertyOrder[item.propertyId]
|
||||||
|
if (currentProperty === 'country' && item.value) {
|
||||||
|
return <CountryDisplay countryCode={item.value} />
|
||||||
|
}
|
||||||
|
// For other category nodes, just show the value
|
||||||
return <Text>{item[propertyOrder[item.propertyId]] || item.value}</Text>
|
return <Text>{item[propertyOrder[item.propertyId]] || item.value}</Text>
|
||||||
}
|
}
|
||||||
// For leaf nodes, show icon, name, and id
|
// For leaf nodes, show icon, name, and id
|
||||||
@ -129,8 +137,9 @@ const ObjectSelect = ({
|
|||||||
let currentPId = 0
|
let currentPId = 0
|
||||||
|
|
||||||
// Build category nodes for each property level and load all available options
|
// Build category nodes for each property level and load all available options
|
||||||
for (let i = 0; i < propertyOrder.length - 1; i++) {
|
for (let i = 0; i < propertyOrder.length; i++) {
|
||||||
const propertyName = propertyOrder[i]
|
const propertyName = propertyOrder[i]
|
||||||
|
console.log('propname', propertyName)
|
||||||
let propertyValue
|
let propertyValue
|
||||||
|
|
||||||
// Handle nested property access (e.g., 'filament.diameter')
|
// Handle nested property access (e.g., 'filament.diameter')
|
||||||
@ -372,9 +381,25 @@ const ObjectSelect = ({
|
|||||||
// OnChange handler
|
// OnChange handler
|
||||||
const handleOnChange = (val, selectedOptions) => {
|
const handleOnChange = (val, selectedOptions) => {
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
// Find the raw object for the selected value
|
if (treeCheckable) {
|
||||||
const node = treeData.find((n) => n.value === val)
|
// Handle multiple selections with checkboxes
|
||||||
onChange(node ? node.raw : val, selectedOptions)
|
const selectedObjects = []
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
val.forEach((selectedValue) => {
|
||||||
|
const node = treeData.find((n) => n.value === selectedValue)
|
||||||
|
if (node) {
|
||||||
|
selectedObjects.push(node.raw)
|
||||||
|
} else {
|
||||||
|
selectedObjects.push(selectedValue)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onChange(selectedObjects, selectedOptions)
|
||||||
|
} else {
|
||||||
|
// Handle single selection
|
||||||
|
const node = treeData.find((n) => n.value === val)
|
||||||
|
onChange(node ? node.raw : val, selectedOptions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
console.log('val', val)
|
console.log('val', val)
|
||||||
setDefaultValue(val)
|
setDefaultValue(val)
|
||||||
@ -388,29 +413,56 @@ const ObjectSelect = ({
|
|||||||
|
|
||||||
// Keep defaultValue in sync and handle object values
|
// Keep defaultValue in sync and handle object values
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (value?._id) {
|
if (treeCheckable) {
|
||||||
setDefaultValue(value._id)
|
// Handle array of values for multi-select
|
||||||
}
|
if (Array.isArray(value)) {
|
||||||
|
const valueIds = value.map((v) => v._id || v.id || v)
|
||||||
|
setDefaultValue(valueIds)
|
||||||
|
|
||||||
// Check if value is an object with _id (default object case)
|
// Load tree paths for any objects that aren't already loaded
|
||||||
if (value && typeof value === 'object' && value._id) {
|
value.forEach((item) => {
|
||||||
// If we already have this object loaded, don't fetch again
|
if (item && typeof item === 'object' && item._id) {
|
||||||
const existingNode = treeData.find((node) => node.value === value._id)
|
const existingNode = treeData.find(
|
||||||
if (!existingNode) {
|
(node) => node.value === item._id
|
||||||
fetchObjectById(value._id).then((object) => {
|
)
|
||||||
if (object) {
|
if (!existingNode) {
|
||||||
buildTreePathForObject(object)
|
fetchObjectById(item._id).then((object) => {
|
||||||
|
if (object) {
|
||||||
|
buildTreePathForObject(object)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
setDefaultValue([])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle single value
|
||||||
|
if (value?._id) {
|
||||||
|
setDefaultValue(value._id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if value is an object with _id (default object case)
|
||||||
|
if (value && typeof value === 'object' && value._id) {
|
||||||
|
// If we already have this object loaded, don't fetch again
|
||||||
|
const existingNode = treeData.find((node) => node.value === value._id)
|
||||||
|
if (!existingNode) {
|
||||||
|
fetchObjectById(value._id).then((object) => {
|
||||||
|
if (object) {
|
||||||
|
buildTreePathForObject(object)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [value, treeData, fetchObjectById, buildTreePathForObject])
|
}, [value, treeData, fetchObjectById, buildTreePathForObject, treeCheckable])
|
||||||
|
|
||||||
// Initial load
|
// Initial load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (treeData.length === 0 && !error && !loading) {
|
if (treeData.length === 0 && !error && !loading) {
|
||||||
// If we have a default object value, don't load the regular tree
|
// If we have a default object value, don't load the regular tree
|
||||||
if (value && typeof value === 'object' && value._id) {
|
if (!treeCheckable && value && typeof value === 'object' && value._id) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,7 +481,8 @@ const ObjectSelect = ({
|
|||||||
handleTreeLoad,
|
handleTreeLoad,
|
||||||
error,
|
error,
|
||||||
loading,
|
loading,
|
||||||
value
|
value,
|
||||||
|
treeCheckable
|
||||||
])
|
])
|
||||||
|
|
||||||
return error ? (
|
return error ? (
|
||||||
@ -455,6 +508,8 @@ const ObjectSelect = ({
|
|||||||
value={defaultValue}
|
value={defaultValue}
|
||||||
showSearch={showSearch}
|
showSearch={showSearch}
|
||||||
onSearch={showSearch ? handleSearch : undefined}
|
onSearch={showSearch ? handleSearch : undefined}
|
||||||
|
treeCheckable={treeCheckable}
|
||||||
|
showCheckedStrategy={treeCheckable ? SHOW_CHILD : undefined}
|
||||||
{...treeSelectProps}
|
{...treeSelectProps}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
@ -469,6 +524,7 @@ ObjectSelect.propTypes = {
|
|||||||
value: PropTypes.any,
|
value: PropTypes.any,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
showSearch: PropTypes.bool,
|
showSearch: PropTypes.bool,
|
||||||
|
treeCheckable: PropTypes.bool,
|
||||||
treeSelectProps: PropTypes.object,
|
treeSelectProps: PropTypes.object,
|
||||||
type: PropTypes.string.isRequired
|
type: PropTypes.string.isRequired
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,36 +1,18 @@
|
|||||||
// PrinterSelect.js
|
// PrinterSelect.js
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Tag } from 'antd'
|
|
||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
import ObjectSelect from './ObjectSelect'
|
import ObjectSelect from './ObjectSelect'
|
||||||
import PrinterState from './PrinterState'
|
|
||||||
|
|
||||||
const PrinterSelect = ({ onChange, disabled }) => {
|
const PrinterSelect = ({ onChange, disabled }) => {
|
||||||
// getTitle: if isLeaf, render PrinterState, else render Tag or 'Untagged'
|
|
||||||
const getTitle = (item, isLeaf) =>
|
|
||||||
isLeaf ? (
|
|
||||||
<PrinterState printer={item} showProgress={false} showControls={false} />
|
|
||||||
) : item === 'Untagged' ? (
|
|
||||||
'Untagged'
|
|
||||||
) : (
|
|
||||||
<Tag color='blue'>{item}</Tag>
|
|
||||||
)
|
|
||||||
|
|
||||||
// getValue/getKey: for leaf, use _id; for tag, use tag string
|
|
||||||
const getValue = (item, isLeaf) => (isLeaf ? item._id : item)
|
|
||||||
const getKey = (item, isLeaf) => (isLeaf ? item._id : item)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ObjectSelect
|
<ObjectSelect
|
||||||
endpoint={`${config.backendUrl}/printers`}
|
endpoint={`${config.backendUrl}/printers`}
|
||||||
propertyOrder={['tags']}
|
propertyOrder={['tags']}
|
||||||
getTitle={getTitle}
|
|
||||||
getValue={getValue}
|
|
||||||
getKey={getKey}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
placeholder='Select Printer'
|
placeholder='Select Printer'
|
||||||
|
type='printer'
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,12 @@
|
|||||||
// PrinterSelect.js
|
// PrinterSelect.js
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import {
|
import { Progress, Flex, Space, Typography, Button, Tooltip } from 'antd'
|
||||||
Badge,
|
|
||||||
Progress,
|
|
||||||
Flex,
|
|
||||||
Space,
|
|
||||||
Tag,
|
|
||||||
Typography,
|
|
||||||
Button,
|
|
||||||
Tooltip
|
|
||||||
} from 'antd'
|
|
||||||
import React, { useState, useContext, useEffect } from 'react'
|
import React, { useState, useContext, useEffect } from 'react'
|
||||||
import { SocketContext } from '../context/SocketContext'
|
import { SocketContext } from '../context/SocketContext'
|
||||||
import { CaretLeftOutlined } from '@ant-design/icons'
|
import { CaretLeftOutlined } from '@ant-design/icons'
|
||||||
import XMarkIcon from '../../Icons/XMarkIcon'
|
import XMarkIcon from '../../Icons/XMarkIcon'
|
||||||
import PauseIcon from '../../Icons/PauseIcon'
|
import PauseIcon from '../../Icons/PauseIcon'
|
||||||
|
import StateTag from './StateTag'
|
||||||
|
|
||||||
const PrinterState = ({
|
const PrinterState = ({
|
||||||
printer,
|
printer,
|
||||||
@ -24,8 +16,6 @@ const PrinterState = ({
|
|||||||
showControls = true
|
showControls = true
|
||||||
}) => {
|
}) => {
|
||||||
const { socket } = useContext(SocketContext)
|
const { socket } = useContext(SocketContext)
|
||||||
const [badgeStatus, setBadgeStatus] = useState('unknown')
|
|
||||||
const [badgeText, setBadgeText] = useState('Unknown')
|
|
||||||
const [currentState, setCurrentState] = useState(
|
const [currentState, setCurrentState] = useState(
|
||||||
printer?.state || {
|
printer?.state || {
|
||||||
type: 'unknown',
|
type: 'unknown',
|
||||||
@ -51,81 +41,12 @@ const PrinterState = ({
|
|||||||
}
|
}
|
||||||
}, [socket, initialized, printer?.id])
|
}, [socket, initialized, printer?.id])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
switch (currentState.type) {
|
|
||||||
case 'online':
|
|
||||||
setBadgeStatus('success')
|
|
||||||
setBadgeText('Online')
|
|
||||||
break
|
|
||||||
case 'standby':
|
|
||||||
setBadgeStatus('success')
|
|
||||||
setBadgeText('Standby')
|
|
||||||
break
|
|
||||||
case 'complete':
|
|
||||||
setBadgeStatus('success')
|
|
||||||
setBadgeText('Complete')
|
|
||||||
break
|
|
||||||
case 'offline':
|
|
||||||
setBadgeStatus('default')
|
|
||||||
setBadgeText('Offline')
|
|
||||||
break
|
|
||||||
case 'shutdown':
|
|
||||||
setBadgeStatus('default')
|
|
||||||
setBadgeText('Shutdown')
|
|
||||||
break
|
|
||||||
case 'initializing':
|
|
||||||
setBadgeStatus('warning')
|
|
||||||
setBadgeText('Initializing')
|
|
||||||
break
|
|
||||||
case 'printing':
|
|
||||||
setBadgeStatus('processing')
|
|
||||||
setBadgeText('Printing')
|
|
||||||
break
|
|
||||||
case 'paused':
|
|
||||||
setBadgeStatus('warning')
|
|
||||||
setBadgeText('Paused')
|
|
||||||
break
|
|
||||||
case 'cancelled':
|
|
||||||
setBadgeStatus('warning')
|
|
||||||
setBadgeText('Cancelled')
|
|
||||||
break
|
|
||||||
case 'loading':
|
|
||||||
setBadgeStatus('processing')
|
|
||||||
setBadgeText('Uploading')
|
|
||||||
break
|
|
||||||
case 'processing':
|
|
||||||
setBadgeStatus('processing')
|
|
||||||
setBadgeText('Processing')
|
|
||||||
break
|
|
||||||
case 'ready':
|
|
||||||
setBadgeStatus('success')
|
|
||||||
setBadgeText('Ready')
|
|
||||||
break
|
|
||||||
case 'error':
|
|
||||||
setBadgeStatus('error')
|
|
||||||
setBadgeText('Error')
|
|
||||||
break
|
|
||||||
case 'startup':
|
|
||||||
setBadgeStatus('warning')
|
|
||||||
setBadgeText('Startup')
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
setBadgeStatus('default')
|
|
||||||
setBadgeText(currentState.type)
|
|
||||||
}
|
|
||||||
}, [currentState])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap='small' align={'center'}>
|
<Flex gap='small' align={'center'}>
|
||||||
{showPrinterName && <Text>{printer.name}</Text>}
|
{showPrinterName && <Text>{printer.name}</Text>}
|
||||||
{showStatus && (
|
{showStatus && (
|
||||||
<Space>
|
<Space>
|
||||||
<Tag color={badgeStatus} style={{ marginRight: 0 }}>
|
<StateTag state={currentState.type} />
|
||||||
<Flex gap={6}>
|
|
||||||
<Badge status={badgeStatus} />
|
|
||||||
{badgeText}
|
|
||||||
</Flex>
|
|
||||||
</Tag>
|
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
{showProgress &&
|
{showProgress &&
|
||||||
|
|||||||
103
src/components/Dashboard/common/StateTag.jsx
Normal file
103
src/components/Dashboard/common/StateTag.jsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Badge, Flex, Tag } from 'antd'
|
||||||
|
import React, { useMemo } from 'react'
|
||||||
|
|
||||||
|
const StateTag = ({ state, showBadge = true, style = {} }) => {
|
||||||
|
const { badgeStatus, badgeText } = useMemo(() => {
|
||||||
|
let status = 'default'
|
||||||
|
let text = 'Unknown'
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case 'online':
|
||||||
|
status = 'success'
|
||||||
|
text = 'Online'
|
||||||
|
break
|
||||||
|
case 'standby':
|
||||||
|
status = 'success'
|
||||||
|
text = 'Standby'
|
||||||
|
break
|
||||||
|
case 'complete':
|
||||||
|
status = 'success'
|
||||||
|
text = 'Complete'
|
||||||
|
break
|
||||||
|
case 'offline':
|
||||||
|
status = 'default'
|
||||||
|
text = 'Offline'
|
||||||
|
break
|
||||||
|
case 'shutdown':
|
||||||
|
status = 'default'
|
||||||
|
text = 'Shutdown'
|
||||||
|
break
|
||||||
|
case 'initializing':
|
||||||
|
status = 'warning'
|
||||||
|
text = 'Initializing'
|
||||||
|
break
|
||||||
|
case 'printing':
|
||||||
|
status = 'processing'
|
||||||
|
text = 'Printing'
|
||||||
|
break
|
||||||
|
case 'paused':
|
||||||
|
status = 'warning'
|
||||||
|
text = 'Paused'
|
||||||
|
break
|
||||||
|
case 'cancelled':
|
||||||
|
status = 'error'
|
||||||
|
text = 'Cancelled'
|
||||||
|
break
|
||||||
|
case 'loading':
|
||||||
|
status = 'processing'
|
||||||
|
text = 'Uploading'
|
||||||
|
break
|
||||||
|
case 'processing':
|
||||||
|
status = 'processing'
|
||||||
|
text = 'Processing'
|
||||||
|
break
|
||||||
|
case 'ready':
|
||||||
|
status = 'success'
|
||||||
|
text = 'Ready'
|
||||||
|
break
|
||||||
|
case 'error':
|
||||||
|
status = 'error'
|
||||||
|
text = 'Error'
|
||||||
|
break
|
||||||
|
case 'startup':
|
||||||
|
status = 'warning'
|
||||||
|
text = 'Startup'
|
||||||
|
break
|
||||||
|
case 'draft':
|
||||||
|
status = 'default'
|
||||||
|
text = 'Draft'
|
||||||
|
break
|
||||||
|
case 'failed':
|
||||||
|
status = 'error'
|
||||||
|
text = 'Failed'
|
||||||
|
break
|
||||||
|
case 'queued':
|
||||||
|
status = 'warning'
|
||||||
|
text = 'Queued'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
status = 'default'
|
||||||
|
text = state || 'Unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
return { badgeStatus: status, badgeText: text }
|
||||||
|
}, [state])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tag color={badgeStatus} style={{ marginRight: 0, ...style }}>
|
||||||
|
<Flex gap={6}>
|
||||||
|
{showBadge && <Badge status={badgeStatus} />}
|
||||||
|
{badgeText}
|
||||||
|
</Flex>
|
||||||
|
</Tag>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
StateTag.propTypes = {
|
||||||
|
state: PropTypes.string,
|
||||||
|
showBadge: PropTypes.bool,
|
||||||
|
style: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StateTag
|
||||||
@ -1,9 +1,10 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { Badge, Progress, Flex, Button, Space, Tag, Tooltip } from 'antd' // eslint-disable-line
|
import { Progress, Flex, Button, Space, Tooltip } from 'antd' // eslint-disable-line
|
||||||
import { CaretLeftOutlined } from '@ant-design/icons' // eslint-disable-line
|
import { CaretLeftOutlined } from '@ant-design/icons' // eslint-disable-line
|
||||||
import React, { useState, useContext, useEffect } from 'react'
|
import React, { useState, useContext, useEffect } from 'react'
|
||||||
import { SocketContext } from '../context/SocketContext'
|
import { SocketContext } from '../context/SocketContext'
|
||||||
import IdText from './IdText'
|
import IdText from './IdText'
|
||||||
|
import StateTag from './StateTag'
|
||||||
import XMarkIcon from '../../Icons/XMarkIcon'
|
import XMarkIcon from '../../Icons/XMarkIcon'
|
||||||
import PauseIcon from '../../Icons/PauseIcon'
|
import PauseIcon from '../../Icons/PauseIcon'
|
||||||
import BinIcon from '../../Icons/BinIcon'
|
import BinIcon from '../../Icons/BinIcon'
|
||||||
@ -16,8 +17,6 @@ const SubJobState = ({
|
|||||||
showControls = true //eslint-disable-line
|
showControls = true //eslint-disable-line
|
||||||
}) => {
|
}) => {
|
||||||
const { socket } = useContext(SocketContext)
|
const { socket } = useContext(SocketContext)
|
||||||
const [badgeStatus, setBadgeStatus] = useState('unknown')
|
|
||||||
const [badgeText, setBadgeText] = useState('Unknown')
|
|
||||||
const [currentState, setCurrentState] = useState(
|
const [currentState, setCurrentState] = useState(
|
||||||
subJob?.state || {
|
subJob?.state || {
|
||||||
type: 'unknown',
|
type: 'unknown',
|
||||||
@ -45,42 +44,6 @@ const SubJobState = ({
|
|||||||
}
|
}
|
||||||
}, [socket, initialized, subJob?._id])
|
}, [socket, initialized, subJob?._id])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
switch (currentState.type) {
|
|
||||||
case 'draft':
|
|
||||||
setBadgeStatus('default')
|
|
||||||
setBadgeText('Draft')
|
|
||||||
break
|
|
||||||
case 'printing':
|
|
||||||
setBadgeStatus('processing')
|
|
||||||
setBadgeText('Printing')
|
|
||||||
break
|
|
||||||
case 'complete':
|
|
||||||
setBadgeStatus('success')
|
|
||||||
setBadgeText('Complete')
|
|
||||||
break
|
|
||||||
case 'failed':
|
|
||||||
setBadgeStatus('error')
|
|
||||||
setBadgeText('Failed')
|
|
||||||
break
|
|
||||||
case 'queued':
|
|
||||||
setBadgeStatus('warning')
|
|
||||||
setBadgeText('Queued')
|
|
||||||
break
|
|
||||||
case 'paused':
|
|
||||||
setBadgeStatus('warning')
|
|
||||||
setBadgeText('Paused')
|
|
||||||
break
|
|
||||||
case 'cancelled':
|
|
||||||
setBadgeStatus('error')
|
|
||||||
setBadgeText('Cancelled')
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
setBadgeStatus('default')
|
|
||||||
setBadgeText('Unknown')
|
|
||||||
}
|
|
||||||
}, [currentState])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap='small' align={'center'}>
|
<Flex gap='small' align={'center'}>
|
||||||
{showId && (
|
{showId && (
|
||||||
@ -88,12 +51,7 @@ const SubJobState = ({
|
|||||||
)}
|
)}
|
||||||
{showStatus && (
|
{showStatus && (
|
||||||
<Space>
|
<Space>
|
||||||
<Tag color={badgeStatus} style={{ marginRight: 0 }}>
|
<StateTag state={currentState?.type} />
|
||||||
<Flex gap={6}>
|
|
||||||
<Badge status={badgeStatus} />
|
|
||||||
{badgeText}
|
|
||||||
</Flex>
|
|
||||||
</Tag>
|
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
{showProgress &&
|
{showProgress &&
|
||||||
|
|||||||
@ -1,188 +1,30 @@
|
|||||||
import { TreeSelect, Space } from 'antd'
|
import React from 'react'
|
||||||
import React, { useEffect, useState } from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import axios from 'axios'
|
|
||||||
import CountryDisplay from './CountryDisplay'
|
|
||||||
import VendorIcon from '../../Icons/VendorIcon'
|
|
||||||
import config from '../../../config'
|
import config from '../../../config'
|
||||||
|
import ObjectSelect from './ObjectSelect'
|
||||||
|
|
||||||
const propertyOrder = ['country']
|
const propertyOrder = ['country']
|
||||||
|
|
||||||
const VendorSelect = ({ onChange, filter = {}, useFilter = false, value }) => {
|
const VendorSelect = ({ onChange, filter = {}, useFilter = false, value }) => {
|
||||||
const [vendorsTreeData, setVendorsTreeData] = useState([])
|
|
||||||
const [loading, setLoading] = useState(true)
|
|
||||||
const [defaultValue, setDefaultValue] = useState(null)
|
|
||||||
|
|
||||||
const fetchVendorsData = async (property, filter) => {
|
|
||||||
setLoading(true)
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${config.backendUrl}/vendors`, {
|
|
||||||
params: {
|
|
||||||
...filter,
|
|
||||||
property
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json'
|
|
||||||
},
|
|
||||||
withCredentials: true
|
|
||||||
})
|
|
||||||
setLoading(false)
|
|
||||||
return response.data
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFilter = (node) => {
|
|
||||||
var filter = {}
|
|
||||||
var currentId = node.id
|
|
||||||
while (currentId != 0) {
|
|
||||||
const currentNode = vendorsTreeData.filter(
|
|
||||||
(treeData) => treeData['id'] === currentId
|
|
||||||
)[0]
|
|
||||||
filter[propertyOrder[currentNode.propertyId]] =
|
|
||||||
currentNode.value.split('-')[0]
|
|
||||||
currentId = currentNode.pId
|
|
||||||
}
|
|
||||||
return filter
|
|
||||||
}
|
|
||||||
|
|
||||||
const generateVendorTreeNodes = async (node = null, filter = null) => {
|
|
||||||
if (!node) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter === null) {
|
|
||||||
filter = getFilter(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
const vendorData = await fetchVendorsData(null, filter)
|
|
||||||
|
|
||||||
let newNodeList = []
|
|
||||||
|
|
||||||
for (const vendor of vendorData) {
|
|
||||||
const random = Math.random().toString(36).substring(2, 6)
|
|
||||||
|
|
||||||
const newNode = {
|
|
||||||
id: random,
|
|
||||||
pId: node.id,
|
|
||||||
value: vendor._id,
|
|
||||||
vendor: vendor,
|
|
||||||
key: vendor._id,
|
|
||||||
title: (
|
|
||||||
<Space>
|
|
||||||
<VendorIcon />
|
|
||||||
{vendor.name}
|
|
||||||
</Space>
|
|
||||||
),
|
|
||||||
isLeaf: true
|
|
||||||
}
|
|
||||||
|
|
||||||
newNodeList.push(newNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
setVendorsTreeData(vendorsTreeData.concat(newNodeList))
|
|
||||||
}
|
|
||||||
|
|
||||||
const generateVendorCategoryTreeNodes = async (node = null) => {
|
|
||||||
var filter = {}
|
|
||||||
|
|
||||||
var propertyId = 0
|
|
||||||
|
|
||||||
if (!node) {
|
|
||||||
node = {}
|
|
||||||
node.id = 0
|
|
||||||
} else {
|
|
||||||
filter = getFilter(node)
|
|
||||||
propertyId = node.propertyId + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
const propertyName = propertyOrder[propertyId]
|
|
||||||
|
|
||||||
const propertyData = await fetchVendorsData(propertyName, filter)
|
|
||||||
|
|
||||||
const newNodeList = []
|
|
||||||
|
|
||||||
for (const item of propertyData) {
|
|
||||||
const property = item[propertyName]
|
|
||||||
const random = Math.random().toString(36).substring(2, 6)
|
|
||||||
|
|
||||||
const newNode = {
|
|
||||||
id: random,
|
|
||||||
pId: node.id,
|
|
||||||
value: property + '-' + random,
|
|
||||||
key: property + '-' + random,
|
|
||||||
propertyId: propertyId,
|
|
||||||
title: <CountryDisplay countryCode={property} />,
|
|
||||||
isLeaf: false,
|
|
||||||
selectable: false
|
|
||||||
}
|
|
||||||
|
|
||||||
newNodeList.push(newNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
setVendorsTreeData(vendorsTreeData.concat(newNodeList))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleVendorsTreeLoad = async (node) => {
|
|
||||||
if (node) {
|
|
||||||
if (node.propertyId !== propertyOrder.length - 1) {
|
|
||||||
await generateVendorCategoryTreeNodes(node)
|
|
||||||
} else {
|
|
||||||
await generateVendorTreeNodes(node) // End of properties
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await generateVendorCategoryTreeNodes(null) // First property
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleOnChange = (value, selectedOptions) => {
|
|
||||||
const vendorObject = vendorsTreeData.find(
|
|
||||||
(node) => node.value === value
|
|
||||||
)?.vendor
|
|
||||||
onChange(vendorObject, selectedOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setVendorsTreeData([])
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (vendorsTreeData.length === 0) {
|
|
||||||
if (useFilter === true) {
|
|
||||||
generateVendorTreeNodes({ id: 0 }, filter)
|
|
||||||
} else {
|
|
||||||
handleVendorsTreeLoad(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [vendorsTreeData])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log('value', value)
|
|
||||||
if (value?.name) {
|
|
||||||
setDefaultValue(value.name)
|
|
||||||
}
|
|
||||||
}, [value])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TreeSelect
|
<ObjectSelect
|
||||||
treeDataSimpleMode
|
endpoint={`${config.backendUrl}/vendors`}
|
||||||
loadData={handleVendorsTreeLoad}
|
propertyOrder={propertyOrder}
|
||||||
treeData={vendorsTreeData}
|
filter={filter}
|
||||||
onChange={handleOnChange}
|
useFilter={useFilter}
|
||||||
loading={loading}
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
placeholder='Select a vendor'
|
placeholder='Select a vendor'
|
||||||
style={{ width: '100%' }}
|
type={'vendor'}
|
||||||
value={defaultValue}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
VendorSelect.propTypes = {
|
VendorSelect.propTypes = {
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
value: PropTypes.object,
|
||||||
filter: PropTypes.object,
|
filter: PropTypes.object,
|
||||||
useFilter: PropTypes.bool,
|
useFilter: PropTypes.bool
|
||||||
value: PropTypes.object
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default VendorSelect
|
export default VendorSelect
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user