Add scheduling-remote service to docker-compose and enhance camera error handling

- Introduced a new service for scheduling-remote in docker-compose.yml, allowing for better management of scheduling functionalities.
- Enhanced error handling in CameraMonitor and CameraStreamer classes to improve robustness during camera initialization and streaming processes.
- Updated various components in the management dashboard to support dark mode and improve user experience with consistent styling.
- Implemented feature flags for enabling/disabling modules, including the new scheduling module.
This commit is contained in:
salirezav
2025-11-02 19:33:13 -05:00
parent f6a37ca1ba
commit 868aa3f036
33 changed files with 7471 additions and 136 deletions

View File

@@ -57,8 +57,10 @@ export default function App() {
setRecordings(recordingsData)
setLastUpdate(new Date())
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to fetch data')
// Don't set error state - let widgets show API is unavailable
// Keep existing state so UI can still render
console.error('Failed to fetch initial data:', err)
setLastUpdate(new Date()) // Update timestamp even on error to show we tried
} finally {
setLoading(false)
}
@@ -221,12 +223,32 @@ export default function App() {
const handlePreviewModal = useCallback((cameraName: string) => {
setPreviewCamera(cameraName)
setPreviewModalOpen(true)
// The modal will start streaming and notify us via onStreamStarted callback
}, [])
const handlePreviewNewWindow = useCallback((cameraName: string) => {
// Open camera stream in new window/tab
const streamUrl = visionApi.getStreamUrl(cameraName)
window.open(streamUrl, '_blank')
const handlePreviewNewWindow = useCallback(async (cameraName: string) => {
try {
// Start streaming before opening new window
const result = await visionApi.startStream(cameraName)
if (result.success) {
// Immediately update camera status to show "Stop Streaming" button
setCameras((prev) => ({
...prev,
[cameraName]: {
...prev[cameraName],
status: 'streaming',
},
}))
// Open camera stream in new window/tab
const streamUrl = visionApi.getStreamUrl(cameraName)
window.open(streamUrl, '_blank')
} else {
setNotification({ type: 'error', message: `Failed to start stream: ${result.message}` })
}
} catch (err) {
setNotification({ type: 'error', message: err instanceof Error ? err.message : 'Failed to start stream' })
}
}, [])
const handleConfigure = useCallback((cameraName: string) => {
@@ -259,8 +281,20 @@ export default function App() {
const result = await visionApi.stopStream(cameraName)
if (result.success) {
setNotification({ type: 'success', message: 'Streaming stopped' })
// Refresh camera status
visionApi.getCameras().then(setCameras).catch(console.error)
// Immediately update camera status (UI updates instantly)
setCameras((prev) => ({
...prev,
[cameraName]: {
...prev[cameraName],
status: 'available',
},
}))
// Refresh camera status from API as backup
setTimeout(() => {
visionApi.getCameras().then(setCameras).catch(console.error)
}, 500)
} else {
setNotification({ type: 'error', message: `Failed: ${result.message}` })
}
@@ -438,6 +472,26 @@ export default function App() {
setPreviewModalOpen(false)
setPreviewCamera(null)
}}
onStreamStarted={() => {
// Update camera status when streaming starts
setCameras((prev) => ({
...prev,
[previewCamera]: {
...prev[previewCamera],
status: 'streaming',
},
}))
}}
onStreamStopped={() => {
// Update camera status when streaming stops
setCameras((prev) => ({
...prev,
[previewCamera]: {
...prev[previewCamera],
status: 'available',
},
}))
}}
/>
)}