diff --git a/src/components/Dashboard/Developer/ApiContextDebug.jsx b/src/components/Dashboard/Developer/ApiContextDebug.jsx
new file mode 100644
index 0000000..48a75c2
--- /dev/null
+++ b/src/components/Dashboard/Developer/ApiContextDebug.jsx
@@ -0,0 +1,842 @@
+import { useContext, useState, useEffect } from 'react'
+import {
+ Descriptions,
+ Button,
+ Typography,
+ Flex,
+ Space,
+ Dropdown,
+ message,
+ Tag,
+ Input,
+ InputNumber,
+ Select
+} from 'antd'
+import ReloadIcon from '../../Icons/ReloadIcon.jsx'
+import CloudIcon from '../../Icons/CloudIcon.jsx'
+import { ApiServerContext } from '../context/ApiServerContext.jsx'
+import BoolDisplay from '../common/BoolDisplay.jsx'
+import InfoCollapse from '../common/InfoCollapse.jsx'
+
+const { Text, Paragraph } = Typography
+
+const ApiContextDebug = () => {
+ const {
+ apiServer,
+ error,
+ connecting,
+ connected,
+ fetchLoading,
+ lockObject,
+ unlockObject,
+ fetchObjectLock,
+ updateObject,
+ createObject,
+ deleteObject,
+ subscribeToObjectUpdates,
+ subscribeToObjectEvent,
+ subscribeToObjectTypeUpdates,
+ subscribeToObjectLock,
+ fetchObject,
+ fetchObjects,
+ fetchObjectsByProperty,
+ fetchSpotlightData,
+ fetchObjectContent,
+ fetchTemplatePreview,
+ fetchNotes,
+ fetchHostOTP,
+ sendObjectAction
+ } = useContext(ApiServerContext)
+
+ const [msgApi, contextHolder] = message.useMessage()
+ const [connectionStatus, setConnectionStatus] = useState('disconnected')
+ const [socketId, setSocketId] = useState(null)
+
+ // Test input states
+ const [testInputs, setTestInputs] = useState({
+ objectId: 'test-id',
+ objectType: 'user',
+ hostId: 'test-host-id',
+ parentId: 'test-parent-id',
+ fileName: 'test.gcode',
+ query: 'test query',
+ action: 'start',
+ eventType: 'status',
+ properties: ['name', 'email'],
+ filter: { active: true },
+ scale: 1.0,
+ templateContent: 'Test template content',
+ testObject: { name: 'Test Object' }
+ })
+
+ // Collapse states
+ const [collapseStates, setCollapseStates] = useState({
+ fetchTests: false,
+ crudTests: false,
+ subscriptionTests: false,
+ specialTests: false
+ })
+
+ useEffect(() => {
+ if (apiServer) {
+ setConnectionStatus(apiServer.connected ? 'connected' : 'disconnected')
+ setSocketId(apiServer.id)
+
+ // Listen for connection status changes
+ const handleConnect = () => {
+ setConnectionStatus('connected')
+ setSocketId(apiServer.id)
+ }
+
+ const handleDisconnect = () => {
+ setConnectionStatus('disconnected')
+ setSocketId(null)
+ }
+
+ apiServer.on('connect', handleConnect)
+ apiServer.on('disconnect', handleDisconnect)
+
+ return () => {
+ apiServer.off('connect', handleConnect)
+ apiServer.off('disconnect', handleDisconnect)
+ }
+ } else {
+ setConnectionStatus('disconnected')
+ setSocketId(null)
+ }
+ }, [apiServer])
+
+ // Helper functions
+ const updateTestInput = (key, value) => {
+ setTestInputs((prev) => ({ ...prev, [key]: value }))
+ }
+
+ const toggleCollapse = (key) => {
+ setCollapseStates((prev) => ({ ...prev, [key]: !prev[key] }))
+ }
+
+ const handleReconnect = () => {
+ if (apiServer) {
+ apiServer.connect()
+ msgApi.info('Attempting to reconnect...')
+ } else {
+ msgApi.warning('No API server instance available')
+ }
+ }
+
+ const handleDisconnect = () => {
+ if (apiServer) {
+ apiServer.disconnect()
+ msgApi.info('Disconnected from API server')
+ }
+ }
+
+ const testFetchObject = async () => {
+ try {
+ msgApi.loading('Testing fetchObject...', 0)
+ const result = await fetchObject(
+ testInputs.objectId,
+ testInputs.objectType
+ )
+ msgApi.destroy()
+ msgApi.success('fetchObject test completed')
+ console.log('fetchObject result:', result)
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('fetchObject test failed')
+ console.error('fetchObject error:', err)
+ }
+ }
+
+ const testFetchObjects = async () => {
+ try {
+ msgApi.loading('Testing fetchObjects...', 0)
+ const result = await fetchObjects(testInputs.objectType, {
+ page: 1,
+ limit: 5
+ })
+ msgApi.destroy()
+ msgApi.success('fetchObjects test completed')
+ console.log('fetchObjects result:', result)
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('fetchObjects test failed')
+ console.error('fetchObjects error:', err)
+ }
+ }
+
+ const testLockObject = () => {
+ try {
+ lockObject(testInputs.objectId, testInputs.objectType)
+ msgApi.success('Lock command sent')
+ } catch (err) {
+ msgApi.error('Lock command failed')
+ console.error('Lock error:', err)
+ }
+ }
+
+ const testUnlockObject = () => {
+ try {
+ unlockObject(testInputs.objectId, testInputs.objectType)
+ msgApi.success('Unlock command sent')
+ } catch (err) {
+ msgApi.error('Unlock command failed')
+ console.error('Unlock error:', err)
+ }
+ }
+
+ const testFetchObjectLock = async () => {
+ try {
+ msgApi.loading('Testing fetchObjectLock...', 0)
+ const result = await fetchObjectLock(
+ testInputs.objectId,
+ testInputs.objectType
+ )
+ msgApi.destroy()
+ msgApi.success('fetchObjectLock test completed')
+ console.log('fetchObjectLock result:', result)
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('fetchObjectLock test failed')
+ console.error('fetchObjectLock error:', err)
+ }
+ }
+
+ const testUpdateObject = async () => {
+ try {
+ msgApi.loading('Testing updateObject...', 0)
+ const testData = {
+ name: 'Test Update',
+ updated: new Date().toISOString()
+ }
+ const result = await updateObject(
+ testInputs.objectId,
+ testInputs.objectType,
+ testData
+ )
+ msgApi.destroy()
+ msgApi.success('updateObject test completed')
+ console.log('updateObject result:', result)
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('updateObject test failed')
+ console.error('updateObject error:', err)
+ }
+ }
+
+ const testCreateObject = async () => {
+ try {
+ msgApi.loading('Testing createObject...', 0)
+ const testData = {
+ name: 'Test Create',
+ created: new Date().toISOString()
+ }
+ const result = await createObject(testInputs.objectType, testData)
+ msgApi.destroy()
+ msgApi.success('createObject test completed')
+ console.log('createObject result:', result)
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('createObject test failed')
+ console.error('createObject error:', err)
+ }
+ }
+
+ const testDeleteObject = async () => {
+ try {
+ msgApi.loading('Testing deleteObject...', 0)
+ const result = await deleteObject(
+ testInputs.objectId,
+ testInputs.objectType
+ )
+ msgApi.destroy()
+ msgApi.success('deleteObject test completed')
+ console.log('deleteObject result:', result)
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('deleteObject test failed')
+ console.error('deleteObject error:', err)
+ }
+ }
+
+ const testFetchObjectsByProperty = async () => {
+ try {
+ msgApi.loading('Testing fetchObjectsByProperty...', 0)
+ const params = {
+ properties: testInputs.properties,
+ filter: testInputs.filter,
+ masterFilter: {}
+ }
+ const result = await fetchObjectsByProperty(testInputs.objectType, params)
+ msgApi.destroy()
+ msgApi.success('fetchObjectsByProperty test completed')
+ console.log('fetchObjectsByProperty result:', result)
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('fetchObjectsByProperty test failed')
+ console.error('fetchObjectsByProperty error:', err)
+ }
+ }
+
+ const testFetchSpotlightData = async () => {
+ try {
+ msgApi.loading('Testing fetchSpotlightData...', 0)
+ const result = await fetchSpotlightData(testInputs.query)
+ msgApi.destroy()
+ msgApi.success('fetchSpotlightData test completed')
+ console.log('fetchSpotlightData result:', result)
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('fetchSpotlightData test failed')
+ console.error('fetchSpotlightData error:', err)
+ }
+ }
+
+ const testFetchObjectContent = async () => {
+ try {
+ msgApi.loading('Testing fetchObjectContent...', 0)
+ await fetchObjectContent(
+ testInputs.objectId,
+ 'gcodefile',
+ testInputs.fileName
+ )
+ msgApi.destroy()
+ msgApi.success('fetchObjectContent test completed')
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('fetchObjectContent test failed')
+ console.error('fetchObjectContent error:', err)
+ }
+ }
+
+ const testFetchTemplatePreview = async () => {
+ try {
+ msgApi.loading('Testing fetchTemplatePreview...', 0)
+
+ fetchTemplatePreview(
+ testInputs.objectId,
+ testInputs.templateContent,
+ testInputs.testObject,
+ testInputs.scale,
+ (result) => {
+ msgApi.destroy()
+ if (result.success) {
+ msgApi.success('fetchTemplatePreview test completed')
+ } else {
+ msgApi.error('fetchTemplatePreview test failed')
+ }
+ console.log('fetchTemplatePreview result:', result)
+ }
+ )
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('fetchTemplatePreview test failed')
+ console.error('fetchTemplatePreview error:', err)
+ }
+ }
+
+ const testFetchNotes = async () => {
+ try {
+ msgApi.loading('Testing fetchNotes...', 0)
+ const result = await fetchNotes(testInputs.parentId)
+ msgApi.destroy()
+ msgApi.success('fetchNotes test completed')
+ console.log('fetchNotes result:', result)
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('fetchNotes test failed')
+ console.error('fetchNotes error:', err)
+ }
+ }
+
+ const testFetchHostOTP = async () => {
+ try {
+ msgApi.loading('Testing fetchHostOTP...', 0)
+
+ fetchHostOTP(testInputs.hostId, (result) => {
+ msgApi.destroy()
+ if (result.otp) {
+ msgApi.success('fetchHostOTP test completed: ' + result.otp)
+ } else {
+ msgApi.error('fetchHostOTP test failed')
+ }
+ console.log('fetchHostOTP result:', result)
+ })
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('fetchHostOTP test failed')
+ console.error('fetchHostOTP error:', err)
+ }
+ }
+
+ const testSendObjectAction = async () => {
+ try {
+ msgApi.loading('Testing sendObjectAction...', 0)
+
+ sendObjectAction(
+ testInputs.objectId,
+ 'printer',
+ testInputs.action,
+ (result) => {
+ msgApi.destroy()
+ if (result.success) {
+ msgApi.success('sendObjectAction test completed')
+ } else {
+ msgApi.error('sendObjectAction test failed')
+ }
+ console.log('sendObjectAction result:', result)
+ }
+ )
+ } catch (err) {
+ msgApi.destroy()
+ msgApi.error('sendObjectAction test failed')
+ console.error('sendObjectAction error:', err)
+ }
+ }
+
+ const testSubscribeToObjectUpdates = () => {
+ try {
+ const callback = (data) => {
+ console.log('Object update received:', data)
+ }
+ const unsubscribe = subscribeToObjectUpdates(
+ testInputs.objectId,
+ testInputs.objectType,
+ callback
+ )
+ msgApi.success('Subscribed to object updates')
+ console.log('Subscribed to object updates for test-id')
+
+ // Store unsubscribe function for cleanup
+ setTimeout(() => {
+ if (unsubscribe) {
+ unsubscribe()
+ console.log('Unsubscribed from object updates')
+ }
+ }, 10000) // Auto-unsubscribe after 10 seconds
+ } catch (err) {
+ msgApi.error('Subscribe to object updates failed')
+ console.error('Subscribe error:', err)
+ }
+ }
+
+ const testSubscribeToObjectEvent = () => {
+ try {
+ const callback = (event) => {
+ console.log('Object event received:', event)
+ }
+ const unsubscribe = subscribeToObjectEvent(
+ testInputs.objectId,
+ 'printer',
+ testInputs.eventType,
+ callback
+ )
+ msgApi.success('Subscribed to object events')
+ console.log('Subscribed to object events for test-id')
+
+ // Store unsubscribe function for cleanup
+ setTimeout(() => {
+ if (unsubscribe) {
+ unsubscribe()
+ console.log('Unsubscribed from object events')
+ }
+ }, 10000) // Auto-unsubscribe after 10 seconds
+ } catch (err) {
+ msgApi.error('Subscribe to object events failed')
+ console.error('Subscribe error:', err)
+ }
+ }
+
+ const testSubscribeToObjectTypeUpdates = () => {
+ try {
+ const callback = (data) => {
+ console.log('Object type update received:', data)
+ }
+ const unsubscribe = subscribeToObjectTypeUpdates(
+ testInputs.objectType,
+ callback
+ )
+ msgApi.success('Subscribed to object type updates')
+ console.log('Subscribed to object type updates for user')
+
+ // Store unsubscribe function for cleanup
+ setTimeout(() => {
+ if (unsubscribe) {
+ unsubscribe()
+ console.log('Unsubscribed from object type updates')
+ }
+ }, 10000) // Auto-unsubscribe after 10 seconds
+ } catch (err) {
+ msgApi.error('Subscribe to object type updates failed')
+ console.error('Subscribe error:', err)
+ }
+ }
+
+ const testSubscribeToObjectLock = () => {
+ try {
+ const callback = (lockData) => {
+ console.log('Object lock update received:', lockData)
+ }
+ const unsubscribe = subscribeToObjectLock(
+ testInputs.objectId,
+ testInputs.objectType,
+ callback
+ )
+ msgApi.success('Subscribed to object lock updates')
+ console.log('Subscribed to object lock updates for test-id')
+
+ // Store unsubscribe function for cleanup
+ setTimeout(() => {
+ if (unsubscribe) {
+ unsubscribe()
+ console.log('Unsubscribed from object lock updates')
+ }
+ }, 10000) // Auto-unsubscribe after 10 seconds
+ } catch (err) {
+ msgApi.error('Subscribe to object lock updates failed')
+ console.error('Subscribe error:', err)
+ }
+ }
+
+ const actionItems = {
+ items: [
+ {
+ label: 'Reconnect',
+ key: 'reconnect',
+ disabled: connected
+ },
+ {
+ label: 'Disconnect',
+ key: 'disconnect',
+ disabled: !connected
+ },
+ {
+ type: 'divider'
+ },
+ {
+ label: 'Reload',
+ key: 'reload',
+ icon:
+ {apiServer ? (
+
+
+ {JSON.stringify(
+ {
+ connected: apiServer.connected,
+ id: apiServer.id,
+ transport: apiServer.io.engine.transport.name,
+ readyState: apiServer.io.engine.readyState
+ },
+ null,
+ 2
+ )}
+
+ {getSocketInfo()}
-