Compare commits
No commits in common. "be2109505f449019b180cd006392857c61da01fd" and "3f353c1f70dafaaa7bcec9ca147bf0dd6950ebc6" have entirely different histories.
be2109505f
...
3f353c1f70
@ -1,6 +1,6 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { useContext, useEffect, useState, useRef } from 'react'
|
||||
import { Input, Result, Typography, Flex, Progress } from 'antd'
|
||||
import { Input, Result, Typography, Flex, Progress, Button } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import { ApiServerContext } from '../../context/ApiServerContext'
|
||||
import CopyButton from '../../common/CopyButton'
|
||||
@ -9,7 +9,7 @@ import OTPIcon from '../../../Icons/OTPIcon'
|
||||
const { Text } = Typography
|
||||
|
||||
const HostOTP = ({ id }) => {
|
||||
const { fetchHostOTP } = useContext(ApiServerContext)
|
||||
const { fetchHostOTP, sendObjectAction } = useContext(ApiServerContext)
|
||||
const [hostObject, setHostObject] = useState(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [timeRemaining, setTimeRemaining] = useState(0)
|
||||
@ -35,6 +35,13 @@ const HostOTP = ({ id }) => {
|
||||
})
|
||||
}
|
||||
|
||||
const sendTestAction = () => {
|
||||
console.log('Sending test action...')
|
||||
sendObjectAction(id, 'host', { method: 'testMethod' }, (result) => {
|
||||
console.log('Got callback', result)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (hostObject === null && initialized == false) {
|
||||
setInitialized(true)
|
||||
@ -78,6 +85,7 @@ const HostOTP = ({ id }) => {
|
||||
|
||||
return (
|
||||
<Flex vertical align='center'>
|
||||
<Button onClick={sendTestAction}>Test Action</Button>
|
||||
<Result
|
||||
title={'Connect a Host.'}
|
||||
subTitle={<Text>Enter the following one time passcode.</Text>}
|
||||
|
||||
@ -1,61 +1,19 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Flex } from 'antd'
|
||||
import { useState, useEffect, useContext, useCallback } from 'react'
|
||||
import { Tag, Typography } from 'antd'
|
||||
import { getModelByName } from '../../../database/ObjectModels'
|
||||
import { ApiServerContext } from '../context/ApiServerContext'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
import merge from 'lodash/merge'
|
||||
|
||||
const { Text } = Typography
|
||||
|
||||
const ObjectDisplay = ({ object, objectType }) => {
|
||||
const [objectData, setObjectData] = useState(object)
|
||||
const { subscribeToObjectUpdates, connected } = useContext(ApiServerContext)
|
||||
const { token } = useContext(AuthContext)
|
||||
|
||||
// Update event handler
|
||||
const updateObjectEventHandler = useCallback((value) => {
|
||||
setObjectData((prev) => merge({}, prev, value))
|
||||
}, [])
|
||||
|
||||
// Subscribe to object updates when component mounts
|
||||
useEffect(() => {
|
||||
if (object?._id && objectType && connected && token) {
|
||||
const objectUpdatesUnsubscribe = subscribeToObjectUpdates(
|
||||
object._id,
|
||||
objectType,
|
||||
updateObjectEventHandler
|
||||
)
|
||||
|
||||
return () => {
|
||||
if (objectUpdatesUnsubscribe) objectUpdatesUnsubscribe()
|
||||
}
|
||||
}
|
||||
}, [
|
||||
object?._id,
|
||||
objectType,
|
||||
subscribeToObjectUpdates,
|
||||
connected,
|
||||
token,
|
||||
updateObjectEventHandler
|
||||
])
|
||||
|
||||
// Update local state when object prop changes
|
||||
useEffect(() => {
|
||||
setObjectData(object)
|
||||
}, [object])
|
||||
|
||||
if (!objectData) {
|
||||
if (!object) {
|
||||
return <Text type='secondary'>n/a</Text>
|
||||
}
|
||||
|
||||
const model = getModelByName(objectType)
|
||||
const Icon = model.icon
|
||||
return (
|
||||
<Flex gap={'small'}>
|
||||
<Icon />
|
||||
<Text>{objectData?.name ? objectData.name : null}</Text>
|
||||
</Flex>
|
||||
<Tag color='default' style={{ margin: 0 }} icon={<Icon />}>
|
||||
{object?.name ? object.name : null}
|
||||
</Tag>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -87,10 +87,8 @@ const ObjectTable = forwardRef(
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [lazyLoading, setLazyLoading] = useState(false)
|
||||
|
||||
const subscribedIdsRef = useRef([])
|
||||
const [subscribedIds, setSubscribedIds] = useState([])
|
||||
const [typeSubscribed, setTypeSubscribed] = useState(false)
|
||||
const unsubscribesRef = useRef([])
|
||||
const updateEventHandlerRef = useRef()
|
||||
|
||||
const rowActions =
|
||||
model.actions?.filter((action) => action.row == true) || []
|
||||
@ -289,7 +287,6 @@ const ObjectTable = forwardRef(
|
||||
|
||||
// Update event handler for real-time updates
|
||||
const updateEventHandler = useCallback((id, updatedData) => {
|
||||
console.log('GOT UPDATE FOR', id)
|
||||
setPages((prevPages) =>
|
||||
prevPages.map((page) => ({
|
||||
...page,
|
||||
@ -300,9 +297,6 @@ const ObjectTable = forwardRef(
|
||||
)
|
||||
}, [])
|
||||
|
||||
// Store the latest updateEventHandler in a ref
|
||||
updateEventHandlerRef.current = updateEventHandler
|
||||
|
||||
const newEventHandler = useCallback(() => {
|
||||
console.log('GOT NEW EVENT')
|
||||
reload()
|
||||
@ -310,68 +304,44 @@ const ObjectTable = forwardRef(
|
||||
|
||||
// Subscribe to real-time updates for all items
|
||||
useEffect(() => {
|
||||
if (pages.length > 0 && connected == true) {
|
||||
// Get all non-skeleton item IDs from all pages
|
||||
const allItemIds = pages
|
||||
.flatMap((page) => page.items || [])
|
||||
.filter((item) => !item.isSkeleton)
|
||||
.map((item) => item._id)
|
||||
.filter(Boolean)
|
||||
|
||||
// Find new items that need subscription
|
||||
const newItemIds = allItemIds.filter(
|
||||
(id) => !subscribedIdsRef.current.includes(id)
|
||||
)
|
||||
|
||||
// Subscribe to new items only
|
||||
newItemIds.forEach((itemId) => {
|
||||
console.log('SUB', itemId)
|
||||
const unsubscribe = subscribeToObjectUpdates(
|
||||
itemId,
|
||||
type,
|
||||
(updateData) => {
|
||||
updateEventHandlerRef.current(itemId, updateData)
|
||||
}
|
||||
)
|
||||
subscribedIdsRef.current.push(itemId)
|
||||
if (unsubscribe) {
|
||||
unsubscribesRef.current.push(unsubscribe)
|
||||
if (pages.length > 0 && connected) {
|
||||
const unsubscribes = []
|
||||
// Subscribe to each item in all pages
|
||||
pages.forEach((page) => {
|
||||
if (page?.items && page?.items?.length > 0) {
|
||||
page.items.forEach((item) => {
|
||||
if (!item.isSkeleton && !subscribedIds.includes(item?._id)) {
|
||||
const unsubscribe = subscribeToObjectUpdates(
|
||||
item._id,
|
||||
type,
|
||||
(updateData) => {
|
||||
updateEventHandler(item._id, updateData)
|
||||
}
|
||||
)
|
||||
setSubscribedIds((prev) => [...prev, item._id])
|
||||
if (unsubscribe) {
|
||||
unsubscribes.push(unsubscribe)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Clean up subscriptions for items that are no longer in the pages
|
||||
const currentSubscribedIds = subscribedIdsRef.current
|
||||
const itemsToUnsubscribe = currentSubscribedIds.filter(
|
||||
(id) => !allItemIds.includes(id)
|
||||
)
|
||||
|
||||
itemsToUnsubscribe.forEach((itemId) => {
|
||||
const index = subscribedIdsRef.current.indexOf(itemId)
|
||||
if (index > -1) {
|
||||
subscribedIdsRef.current.splice(index, 1)
|
||||
const unsubscribe = unsubscribesRef.current[index]
|
||||
if (unsubscribe) {
|
||||
console.log('UNSUB', itemId)
|
||||
unsubscribe()
|
||||
}
|
||||
unsubscribesRef.current.splice(index, 1)
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
// Clean up all subscriptions
|
||||
unsubscribes.forEach((unsubscribe) => {
|
||||
if (unsubscribe) unsubscribe()
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [pages, type, subscribeToObjectUpdates, connected])
|
||||
|
||||
// Cleanup effect for component unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// Clean up all subscriptions when component unmounts
|
||||
unsubscribesRef.current.forEach((unsubscribe) => {
|
||||
console.log('CALLING UNSUB on unmount')
|
||||
if (unsubscribe) unsubscribe()
|
||||
})
|
||||
unsubscribesRef.current = []
|
||||
subscribedIdsRef.current = []
|
||||
}
|
||||
}, [])
|
||||
}, [
|
||||
pages,
|
||||
type,
|
||||
subscribeToObjectUpdates,
|
||||
updateEventHandler,
|
||||
connected,
|
||||
subscribedIds
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (connected == true && typeSubscribed == false) {
|
||||
|
||||
@ -7,8 +7,6 @@ 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
|
||||
|
||||
@ -18,17 +16,7 @@ const SpotlightTooltip = ({ query, type }) => {
|
||||
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 { fetchSpotlightData } = useContext(ApiServerContext)
|
||||
|
||||
const fetchSpotlight = useCallback(async () => {
|
||||
setLoading(true)
|
||||
@ -45,28 +33,6 @@ const SpotlightTooltip = ({ query, type }) => {
|
||||
}
|
||||
}, [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'>
|
||||
@ -85,9 +51,29 @@ const SpotlightTooltip = ({ query, type }) => {
|
||||
)
|
||||
}
|
||||
|
||||
// Helper to get nested property value
|
||||
const getNestedValue = (obj, path) => {
|
||||
return path.split('.').reduce((current, key) => current?.[key], obj)
|
||||
// Helper to determine property type based on key and value
|
||||
const getPropertyType = (key, value) => {
|
||||
if (key === '_id') {
|
||||
return 'id'
|
||||
}
|
||||
if (key === 'createdAt' || key === 'updatedAt') {
|
||||
return 'dateTime'
|
||||
}
|
||||
if (typeof value === 'boolean') {
|
||||
return 'bool'
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// 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 (
|
||||
@ -119,31 +105,33 @@ const SpotlightTooltip = ({ query, type }) => {
|
||||
</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>
|
||||
)
|
||||
})
|
||||
Object.entries(spotlightData).map(([key, value]) =>
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
value !== '' &&
|
||||
key !== 'objectType' ? (
|
||||
<Descriptions.Item
|
||||
key={key}
|
||||
label={
|
||||
LABEL_MAP[key] || key.charAt(0).toUpperCase() + key.slice(1)
|
||||
}
|
||||
>
|
||||
<ObjectProperty
|
||||
type={getPropertyType(key)}
|
||||
value={value}
|
||||
objectData={spotlightData}
|
||||
objectType={type}
|
||||
isEditing={false}
|
||||
longId={false}
|
||||
showSpotlight={false}
|
||||
showLabel={false}
|
||||
showName={false}
|
||||
showId={false}
|
||||
showQuantity={false}
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
) : null
|
||||
)
|
||||
)}
|
||||
</Descriptions>
|
||||
</Spin>
|
||||
|
||||
@ -285,34 +285,21 @@ const ApiServerProvider = ({ children }) => {
|
||||
if (!subscribedCallbacksRef.current.has(callbacksRefKey)) {
|
||||
subscribedCallbacksRef.current.set(callbacksRefKey, [])
|
||||
}
|
||||
|
||||
const callbacksLength =
|
||||
subscribedCallbacksRef.current.get(callbacksRefKey).length
|
||||
|
||||
if (callbacksLength <= 0) {
|
||||
socketRef.current.emit(
|
||||
'subscribeToObjectUpdate',
|
||||
{
|
||||
_id: id,
|
||||
objectType: objectType
|
||||
},
|
||||
(result) => {
|
||||
if (result.success) {
|
||||
logger.info('Subscribed to id:', id, 'objectType:', objectType)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
logger.info(
|
||||
'Adding callback id:',
|
||||
id,
|
||||
'objectType:',
|
||||
objectType,
|
||||
'callbacks length:',
|
||||
callbacksLength + 1
|
||||
)
|
||||
subscribedCallbacksRef.current.get(callbacksRefKey).push(callback)
|
||||
|
||||
socketRef.current.emit(
|
||||
'subscribeToObjectUpdate',
|
||||
{
|
||||
_id: id,
|
||||
objectType: objectType
|
||||
},
|
||||
(result) => {
|
||||
if (result.success) {
|
||||
logger.info('Subscribed to id:', id, 'objectType:', objectType)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Return cleanup function
|
||||
return () => offObjectUpdatesEvent(id, objectType, callback)
|
||||
}
|
||||
|
||||
@ -209,12 +209,7 @@ const AuthProvider = ({ children }) => {
|
||||
}, [token])
|
||||
|
||||
const setUnauthenticated = () => {
|
||||
setToken(null)
|
||||
setExpiresAt(null)
|
||||
setUserProfile(null)
|
||||
sessionStorage.removeItem('authToken')
|
||||
sessionStorage.removeItem('authExpiresAt')
|
||||
sessionStorage.removeItem('user')
|
||||
setAuthenticated(false)
|
||||
setShowUnauthorizedModal(true)
|
||||
}
|
||||
|
||||
|
||||
@ -126,16 +126,14 @@ export const Filament = {
|
||||
label: 'Diameter',
|
||||
columnWidth: 150,
|
||||
required: true,
|
||||
type: 'number',
|
||||
suffix: 'mm'
|
||||
type: 'mm'
|
||||
},
|
||||
{
|
||||
name: 'density',
|
||||
label: 'Density',
|
||||
columnWidth: 150,
|
||||
required: true,
|
||||
type: 'number',
|
||||
suffix: 'g/cm³'
|
||||
type: 'density'
|
||||
},
|
||||
{
|
||||
name: 'emptySpoolWeight',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user