257 lines
6.6 KiB
JavaScript
257 lines
6.6 KiB
JavaScript
// SpotlightTooltip.js
|
|
import PropTypes from 'prop-types'
|
|
import {
|
|
message,
|
|
Descriptions,
|
|
Card,
|
|
Flex,
|
|
Typography,
|
|
Skeleton,
|
|
Spin,
|
|
Badge
|
|
} from 'antd'
|
|
import { LoadingOutlined, ExportOutlined } from '@ant-design/icons'
|
|
import React, { useEffect, useState, useContext, useCallback } from 'react'
|
|
import axios from 'axios'
|
|
import { AuthContext } from '../context/AuthContext'
|
|
import config from '../../../config'
|
|
import IdText from './IdText'
|
|
import TimeDisplay from './TimeDisplay'
|
|
import { Tag } from 'antd'
|
|
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
|
|
import PrinterState from './PrinterState'
|
|
import JobState from './JobState'
|
|
import FilamentStockState from './FilamentStockState'
|
|
import SubJobState from './SubJobState'
|
|
|
|
const { Text, Link } = Typography
|
|
|
|
const SpotlightTooltip = ({ query, type }) => {
|
|
const [spotlightData, setSpotlightData] = useState(null)
|
|
const [loading, setLoading] = useState(true)
|
|
const [messageApi] = message.useMessage()
|
|
|
|
const { authenticated } = useContext(AuthContext)
|
|
|
|
const fetchSpotlightData = useCallback(async () => {
|
|
if (!authenticated) {
|
|
return
|
|
}
|
|
setLoading(true)
|
|
|
|
try {
|
|
const response = await axios.get(
|
|
`${config.backendUrl}/spotlight/${query}`,
|
|
{
|
|
headers: {
|
|
Accept: 'application/json'
|
|
},
|
|
withCredentials: true // Important for including cookies
|
|
}
|
|
)
|
|
setSpotlightData(response.data)
|
|
setLoading(false)
|
|
} catch (error) {
|
|
setLoading(false)
|
|
if (error.response) {
|
|
messageApi.error(
|
|
`Error fetching spotlight data: ${error.response.status}`
|
|
)
|
|
} else {
|
|
messageApi.error(
|
|
'An unexpected error occurred. Please try again later.'
|
|
)
|
|
}
|
|
}
|
|
}, [authenticated, messageApi, query])
|
|
|
|
useEffect(() => {
|
|
if (authenticated) {
|
|
fetchSpotlightData()
|
|
}
|
|
}, [authenticated, fetchSpotlightData])
|
|
|
|
if (!spotlightData && !loading) {
|
|
return (
|
|
<Card className='spotlight-tooltip'>
|
|
<Flex
|
|
justify='center'
|
|
gap={'small'}
|
|
style={{ height: '100%', minWidth: '270px' }}
|
|
align='center'
|
|
>
|
|
<Text type='secondary'>
|
|
<InfoCircleIcon />
|
|
</Text>
|
|
<Text type='secondary'>No spotlight data.</Text>
|
|
</Flex>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
// Helper to render value nicely
|
|
const renderValue = (key, value) => {
|
|
if (key === '_id' || key === 'id') {
|
|
return (
|
|
<IdText
|
|
id={value}
|
|
type={type}
|
|
showCopy={true}
|
|
longId={false}
|
|
showSpotlight={false}
|
|
/>
|
|
)
|
|
}
|
|
if (key === 'state') {
|
|
if (type === 'printer') {
|
|
return (
|
|
<PrinterState
|
|
printer={spotlightData}
|
|
showControls={false}
|
|
showPrinterName={false}
|
|
/>
|
|
)
|
|
}
|
|
if (type === 'job') {
|
|
return (
|
|
<JobState job={spotlightData} showId={false} showQuantity={false} />
|
|
)
|
|
}
|
|
if (type === 'subjob') {
|
|
return (
|
|
<SubJobState
|
|
subJob={spotlightData}
|
|
showId={false}
|
|
showQuantity={false}
|
|
/>
|
|
)
|
|
}
|
|
if (type === 'filamentstock') {
|
|
return (
|
|
<FilamentStockState
|
|
filamentStock={spotlightData}
|
|
showProgress={false}
|
|
/>
|
|
)
|
|
}
|
|
}
|
|
if (key === 'tags' && Array.isArray(value)) {
|
|
if (value.length == 0) {
|
|
return <Text>n/a</Text>
|
|
}
|
|
return value.map((tag) => (
|
|
<Tag key={tag} color='blue'>
|
|
{tag}
|
|
</Tag>
|
|
))
|
|
}
|
|
if (key === 'email') {
|
|
return (
|
|
<Link href={`mailto:${value}`}>
|
|
{value + ' '}
|
|
<ExportOutlined />
|
|
</Link>
|
|
)
|
|
}
|
|
if (key === 'color') {
|
|
return <Badge color={value} text={value} />
|
|
}
|
|
if (
|
|
typeof value === 'string' &&
|
|
value.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}/)
|
|
) {
|
|
// Format ISO date strings
|
|
return (
|
|
<TimeDisplay
|
|
dateTime={value}
|
|
showDate={true}
|
|
showTime={true}
|
|
showSince={true}
|
|
/>
|
|
)
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return value.join(', ')
|
|
}
|
|
if (typeof value === 'object' && value !== null) {
|
|
// For nested objects, show JSON string
|
|
return JSON.stringify(value)
|
|
}
|
|
if (value == '' || value.length == 0) {
|
|
return <Text>n/a</Text>
|
|
}
|
|
if (value != null) {
|
|
return <Text>{value.toString()}</Text>
|
|
}
|
|
return <Text>n/a</Text>
|
|
}
|
|
|
|
// Map of property names to user-friendly labels
|
|
const LABEL_MAP = {
|
|
name: 'Name',
|
|
state: 'State',
|
|
tags: 'Tags',
|
|
email: 'Email',
|
|
updatedAt: 'Updated At',
|
|
_id: 'ID'
|
|
// Add more mappings as needed
|
|
}
|
|
|
|
return (
|
|
<div className='spotlight-tooltip'>
|
|
<Spin indicator={<LoadingOutlined />} spinning={loading}>
|
|
<Descriptions bordered column={1} size='small'>
|
|
{loading ? (
|
|
<>
|
|
<Descriptions.Item
|
|
label={
|
|
<Skeleton.Input active size='small' style={{ width: 80 }} />
|
|
}
|
|
>
|
|
<Skeleton.Input active size='small' style={{ width: 120 }} />
|
|
</Descriptions.Item>
|
|
<Descriptions.Item
|
|
label={
|
|
<Skeleton.Input active size='small' style={{ width: 80 }} />
|
|
}
|
|
>
|
|
<Skeleton.Input active size='small' style={{ width: 120 }} />
|
|
</Descriptions.Item>
|
|
<Descriptions.Item
|
|
label={
|
|
<Skeleton.Input active size='small' style={{ width: 80 }} />
|
|
}
|
|
>
|
|
<Skeleton.Input active size='small' style={{ width: 120 }} />
|
|
</Descriptions.Item>
|
|
</>
|
|
) : (
|
|
Object.entries(spotlightData).map(([key, value]) =>
|
|
value !== undefined && value !== null && value !== '' ? (
|
|
<Descriptions.Item
|
|
key={key}
|
|
label={
|
|
LABEL_MAP[key] || key.charAt(0).toUpperCase() + key.slice(1)
|
|
}
|
|
>
|
|
{renderValue(
|
|
key,
|
|
key === 'state' && value.type ? value.type : value
|
|
)}
|
|
</Descriptions.Item>
|
|
) : null
|
|
)
|
|
)}
|
|
</Descriptions>
|
|
</Spin>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
SpotlightTooltip.propTypes = {
|
|
query: PropTypes.string.isRequired,
|
|
type: PropTypes.string.isRequired
|
|
}
|
|
|
|
export default SpotlightTooltip
|