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 CheckIcon from '../../Icons/CheckIcon'
|
||||
import BoolDisplay from '../common/BoolDisplay'
|
||||
import StateTag from '../common/StateTag'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
@ -48,9 +49,7 @@ const formatValue = (value, propertyName) => {
|
||||
}
|
||||
|
||||
if (propertyName === 'state' && typeof value === 'object' && value.type) {
|
||||
return (
|
||||
<Text>{value.type.charAt(0).toUpperCase() + value.type.slice(1)}</Text>
|
||||
)
|
||||
return <StateTag state={value.type} />
|
||||
}
|
||||
|
||||
// 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 TimeDisplay from '../common/TimeDisplay'
|
||||
import BoolDisplay from './BoolDisplay'
|
||||
import StateTag from './StateTag'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
@ -33,9 +34,7 @@ const formatValue = (value, propertyName) => {
|
||||
}
|
||||
|
||||
if (propertyName === 'state' && typeof value === 'object' && value.type) {
|
||||
return (
|
||||
<Text>{value.type.charAt(0).toUpperCase() + value.type.slice(1)}</Text>
|
||||
)
|
||||
return <StateTag state={value.type} />
|
||||
}
|
||||
|
||||
// Check if the value is a timestamp (ISO date string)
|
||||
|
||||
@ -28,9 +28,4 @@ FilamentSelect.propTypes = {
|
||||
useFilter: PropTypes.bool
|
||||
}
|
||||
|
||||
FilamentSelect.defaultProps = {
|
||||
filter: {},
|
||||
useFilter: false
|
||||
}
|
||||
|
||||
export default FilamentSelect
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
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 { SocketContext } from '../context/SocketContext'
|
||||
import IdText from './IdText'
|
||||
import StateTag from './StateTag'
|
||||
|
||||
const JobState = ({
|
||||
job,
|
||||
@ -12,13 +13,12 @@ const JobState = ({
|
||||
showQuantity = true
|
||||
}) => {
|
||||
const { socket } = useContext(SocketContext)
|
||||
const [badgeStatus, setBadgeStatus] = useState('default')
|
||||
const [badgeText, setBadgeText] = useState('Unknown')
|
||||
const [currentState, setCurrentState] = useState(
|
||||
job?.state || { type: 'unknown', progress: 0 }
|
||||
)
|
||||
const [initialized, setInitialized] = useState(false)
|
||||
const { Text } = Typography
|
||||
|
||||
useEffect(() => {
|
||||
if (socket && !initialized && job?._id) {
|
||||
setInitialized(true)
|
||||
@ -35,38 +35,6 @@ const JobState = ({
|
||||
}
|
||||
}, [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 (
|
||||
<Flex gap='small' align={'center'}>
|
||||
{showId && (
|
||||
@ -75,12 +43,7 @@ const JobState = ({
|
||||
{showQuantity && <Text>({job.quantity})</Text>}
|
||||
{showStatus && (
|
||||
<Space>
|
||||
<Tag color={badgeStatus} style={{ marginRight: 0 }}>
|
||||
<Flex gap={6}>
|
||||
<Badge status={badgeStatus} />
|
||||
{badgeText}
|
||||
</Flex>
|
||||
</Tag>
|
||||
<StateTag state={currentState?.type} />
|
||||
</Space>
|
||||
)}
|
||||
{showProgress &&
|
||||
|
||||
@ -4,8 +4,9 @@ import { TreeSelect, Typography, Flex, Badge } from 'antd'
|
||||
import axios from 'axios'
|
||||
import { getTypeMeta } from '../utils/Utils'
|
||||
import IdText from './IdText'
|
||||
import CountryDisplay from './CountryDisplay'
|
||||
const { Text } = Typography
|
||||
|
||||
const { SHOW_CHILD } = TreeSelect
|
||||
/**
|
||||
* 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)
|
||||
* - filter: object for filtering (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)
|
||||
* - showSearch: bool (optional, default false)
|
||||
* - treeCheckable: bool (optional, default false) - enables multi-select mode with checkboxes
|
||||
* - treeSelectProps: any other TreeSelect props (optional)
|
||||
*/
|
||||
const ObjectSelect = ({
|
||||
@ -27,13 +29,14 @@ const ObjectSelect = ({
|
||||
value,
|
||||
onChange,
|
||||
showSearch = false,
|
||||
treeCheckable = false,
|
||||
treeSelectProps = {},
|
||||
type = 'unknown',
|
||||
...rest
|
||||
}) => {
|
||||
const [treeData, setTreeData] = useState([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [defaultValue, setDefaultValue] = useState(value)
|
||||
const [defaultValue, setDefaultValue] = useState(treeCheckable ? [] : value)
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
const [error, setError] = useState(false)
|
||||
|
||||
@ -102,7 +105,12 @@ const ObjectSelect = ({
|
||||
const renderTitle = useCallback(
|
||||
(item, 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>
|
||||
}
|
||||
// For leaf nodes, show icon, name, and id
|
||||
@ -129,8 +137,9 @@ const ObjectSelect = ({
|
||||
let currentPId = 0
|
||||
|
||||
// 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]
|
||||
console.log('propname', propertyName)
|
||||
let propertyValue
|
||||
|
||||
// Handle nested property access (e.g., 'filament.diameter')
|
||||
@ -372,9 +381,25 @@ const ObjectSelect = ({
|
||||
// OnChange handler
|
||||
const handleOnChange = (val, selectedOptions) => {
|
||||
if (onChange) {
|
||||
// Find the raw object for the selected value
|
||||
const node = treeData.find((n) => n.value === val)
|
||||
onChange(node ? node.raw : val, selectedOptions)
|
||||
if (treeCheckable) {
|
||||
// Handle multiple selections with checkboxes
|
||||
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)
|
||||
setDefaultValue(val)
|
||||
@ -388,29 +413,56 @@ const ObjectSelect = ({
|
||||
|
||||
// Keep defaultValue in sync and handle object values
|
||||
useEffect(() => {
|
||||
if (value?._id) {
|
||||
setDefaultValue(value._id)
|
||||
}
|
||||
if (treeCheckable) {
|
||||
// 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)
|
||||
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)
|
||||
// Load tree paths for any objects that aren't already loaded
|
||||
value.forEach((item) => {
|
||||
if (item && typeof item === 'object' && item._id) {
|
||||
const existingNode = treeData.find(
|
||||
(node) => node.value === item._id
|
||||
)
|
||||
if (!existingNode) {
|
||||
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
|
||||
useEffect(() => {
|
||||
if (treeData.length === 0 && !error && !loading) {
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -429,7 +481,8 @@ const ObjectSelect = ({
|
||||
handleTreeLoad,
|
||||
error,
|
||||
loading,
|
||||
value
|
||||
value,
|
||||
treeCheckable
|
||||
])
|
||||
|
||||
return error ? (
|
||||
@ -455,6 +508,8 @@ const ObjectSelect = ({
|
||||
value={defaultValue}
|
||||
showSearch={showSearch}
|
||||
onSearch={showSearch ? handleSearch : undefined}
|
||||
treeCheckable={treeCheckable}
|
||||
showCheckedStrategy={treeCheckable ? SHOW_CHILD : undefined}
|
||||
{...treeSelectProps}
|
||||
{...rest}
|
||||
/>
|
||||
@ -469,6 +524,7 @@ ObjectSelect.propTypes = {
|
||||
value: PropTypes.any,
|
||||
onChange: PropTypes.func,
|
||||
showSearch: PropTypes.bool,
|
||||
treeCheckable: PropTypes.bool,
|
||||
treeSelectProps: PropTypes.object,
|
||||
type: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
@ -1,36 +1,18 @@
|
||||
// PrinterSelect.js
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Tag } from 'antd'
|
||||
import config from '../../../config'
|
||||
import ObjectSelect from './ObjectSelect'
|
||||
import PrinterState from './PrinterState'
|
||||
|
||||
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 (
|
||||
<ObjectSelect
|
||||
endpoint={`${config.backendUrl}/printers`}
|
||||
propertyOrder={['tags']}
|
||||
getTitle={getTitle}
|
||||
getValue={getValue}
|
||||
getKey={getKey}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
placeholder='Select Printer'
|
||||
type='printer'
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,20 +1,12 @@
|
||||
// PrinterSelect.js
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
Badge,
|
||||
Progress,
|
||||
Flex,
|
||||
Space,
|
||||
Tag,
|
||||
Typography,
|
||||
Button,
|
||||
Tooltip
|
||||
} from 'antd'
|
||||
import { Progress, Flex, Space, Typography, Button, Tooltip } from 'antd'
|
||||
import React, { useState, useContext, useEffect } from 'react'
|
||||
import { SocketContext } from '../context/SocketContext'
|
||||
import { CaretLeftOutlined } from '@ant-design/icons'
|
||||
import XMarkIcon from '../../Icons/XMarkIcon'
|
||||
import PauseIcon from '../../Icons/PauseIcon'
|
||||
import StateTag from './StateTag'
|
||||
|
||||
const PrinterState = ({
|
||||
printer,
|
||||
@ -24,8 +16,6 @@ const PrinterState = ({
|
||||
showControls = true
|
||||
}) => {
|
||||
const { socket } = useContext(SocketContext)
|
||||
const [badgeStatus, setBadgeStatus] = useState('unknown')
|
||||
const [badgeText, setBadgeText] = useState('Unknown')
|
||||
const [currentState, setCurrentState] = useState(
|
||||
printer?.state || {
|
||||
type: 'unknown',
|
||||
@ -51,81 +41,12 @@ const PrinterState = ({
|
||||
}
|
||||
}, [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 (
|
||||
<Flex gap='small' align={'center'}>
|
||||
{showPrinterName && <Text>{printer.name}</Text>}
|
||||
{showStatus && (
|
||||
<Space>
|
||||
<Tag color={badgeStatus} style={{ marginRight: 0 }}>
|
||||
<Flex gap={6}>
|
||||
<Badge status={badgeStatus} />
|
||||
{badgeText}
|
||||
</Flex>
|
||||
</Tag>
|
||||
<StateTag state={currentState.type} />
|
||||
</Space>
|
||||
)}
|
||||
{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 { 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 React, { useState, useContext, useEffect } from 'react'
|
||||
import { SocketContext } from '../context/SocketContext'
|
||||
import IdText from './IdText'
|
||||
import StateTag from './StateTag'
|
||||
import XMarkIcon from '../../Icons/XMarkIcon'
|
||||
import PauseIcon from '../../Icons/PauseIcon'
|
||||
import BinIcon from '../../Icons/BinIcon'
|
||||
@ -16,8 +17,6 @@ const SubJobState = ({
|
||||
showControls = true //eslint-disable-line
|
||||
}) => {
|
||||
const { socket } = useContext(SocketContext)
|
||||
const [badgeStatus, setBadgeStatus] = useState('unknown')
|
||||
const [badgeText, setBadgeText] = useState('Unknown')
|
||||
const [currentState, setCurrentState] = useState(
|
||||
subJob?.state || {
|
||||
type: 'unknown',
|
||||
@ -45,42 +44,6 @@ const SubJobState = ({
|
||||
}
|
||||
}, [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 (
|
||||
<Flex gap='small' align={'center'}>
|
||||
{showId && (
|
||||
@ -88,12 +51,7 @@ const SubJobState = ({
|
||||
)}
|
||||
{showStatus && (
|
||||
<Space>
|
||||
<Tag color={badgeStatus} style={{ marginRight: 0 }}>
|
||||
<Flex gap={6}>
|
||||
<Badge status={badgeStatus} />
|
||||
{badgeText}
|
||||
</Flex>
|
||||
</Tag>
|
||||
<StateTag state={currentState?.type} />
|
||||
</Space>
|
||||
)}
|
||||
{showProgress &&
|
||||
|
||||
@ -1,188 +1,30 @@
|
||||
import { TreeSelect, Space } from 'antd'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import axios from 'axios'
|
||||
import CountryDisplay from './CountryDisplay'
|
||||
import VendorIcon from '../../Icons/VendorIcon'
|
||||
import config from '../../../config'
|
||||
import ObjectSelect from './ObjectSelect'
|
||||
|
||||
const propertyOrder = ['country']
|
||||
|
||||
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 (
|
||||
<TreeSelect
|
||||
treeDataSimpleMode
|
||||
loadData={handleVendorsTreeLoad}
|
||||
treeData={vendorsTreeData}
|
||||
onChange={handleOnChange}
|
||||
loading={loading}
|
||||
<ObjectSelect
|
||||
endpoint={`${config.backendUrl}/vendors`}
|
||||
propertyOrder={propertyOrder}
|
||||
filter={filter}
|
||||
useFilter={useFilter}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder='Select a vendor'
|
||||
style={{ width: '100%' }}
|
||||
value={defaultValue}
|
||||
type={'vendor'}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
VendorSelect.propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
value: PropTypes.object,
|
||||
filter: PropTypes.object,
|
||||
useFilter: PropTypes.bool,
|
||||
value: PropTypes.object
|
||||
useFilter: PropTypes.bool
|
||||
}
|
||||
|
||||
export default VendorSelect
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user