159 lines
4.9 KiB
JavaScript

// SpotlightTooltip.js
import PropTypes from 'prop-types'
import { Descriptions, Card, Flex, Typography, Skeleton, Spin } from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import { useEffect, useState, useContext, useCallback } from 'react'
import { AuthContext } from '../context/AuthContext'
import ObjectProperty from './ObjectProperty'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import { ApiServerContext } from '../context/ApiServerContext'
import merge from 'lodash/merge'
import { getModelByName } from '../../../database/ObjectModels'
const { Text } = Typography
const SpotlightTooltip = ({ query, type }) => {
const [spotlightData, setSpotlightData] = useState(null)
const [loading, setLoading] = useState(true)
const [initialized, setInitialized] = useState(false)
const { token } = useContext(AuthContext)
const { fetchSpotlightData, subscribeToObjectUpdates, connected } =
useContext(ApiServerContext)
// Get the model for this type
const model = getModelByName(type)
const modelProperties = model.properties || []
// Update event handler
const updateObjectEventHandler = useCallback((value) => {
setSpotlightData((prev) => merge({}, prev, value))
}, [])
const fetchSpotlight = useCallback(async () => {
setLoading(true)
const data = await fetchSpotlightData(query)
setSpotlightData(data)
setLoading(false)
}, [query, fetchSpotlightData])
useEffect(() => {
if (token != null && initialized == false) {
fetchSpotlight()
setInitialized(true)
}
}, [token, fetchSpotlight, initialized])
// Subscribe to object updates when component mounts
useEffect(() => {
if (spotlightData?._id && type && connected && token) {
const objectUpdatesUnsubscribe = subscribeToObjectUpdates(
spotlightData._id,
type,
updateObjectEventHandler
)
return () => {
if (objectUpdatesUnsubscribe) objectUpdatesUnsubscribe()
}
}
}, [
spotlightData?._id,
type,
subscribeToObjectUpdates,
connected,
token,
updateObjectEventHandler
])
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 get nested property value
const getNestedValue = (obj, path) => {
return path.split('.').reduce((current, key) => current?.[key], obj)
}
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>
</>
) : (
modelProperties
.filter((prop) => {
const value = getNestedValue(spotlightData, prop.name)
return value !== undefined && value !== null && value !== ''
})
.map((prop) => {
const value = getNestedValue(spotlightData, prop.name)
return (
<Descriptions.Item key={prop.name} label={prop.label}>
<ObjectProperty
type={prop.type}
value={value}
objectData={spotlightData}
objectType={prop.objectType || type}
isEditing={false}
longId={false}
showSpotlight={false}
showLabel={false}
showName={false}
showId={false}
showQuantity={false}
/>
</Descriptions.Item>
)
})
)}
</Descriptions>
</Spin>
</div>
)
}
SpotlightTooltip.propTypes = {
query: PropTypes.string.isRequired,
type: PropTypes.string.isRequired
}
export default SpotlightTooltip