Files
usda-vision/management-dashboard-web-app/src/components/LiveCameraView.tsx
salirezav dbee99c316 Update environment configuration and enhance user management features
- Changed VITE_SUPABASE_URL in .env.example for deployment consistency.
- Added new user management functionality to reset user passwords in UserManagement component.
- Updated supabase.ts to include first and last name fields in user profiles and added password reset functionality.
- Enhanced DashboardLayout to include a user profile view and improved user display in TopNavbar.
- Updated seed.sql to create additional users with roles for testing purposes.
2025-09-22 11:20:15 -04:00

145 lines
4.7 KiB
TypeScript
Executable File

import { useState, useEffect, useRef } from 'react'
interface LiveCameraViewProps {
cameraName: string
}
export function LiveCameraView({ cameraName }: LiveCameraViewProps) {
const [isStreaming, setIsStreaming] = useState(false)
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(true)
const imgRef = useRef<HTMLImageElement>(null)
const API_BASE = import.meta.env.VITE_VISION_API_URL || 'http://localhost:8000'
useEffect(() => {
startStreaming()
return () => stopStreaming()
}, [cameraName])
const startStreaming = async () => {
try {
setLoading(true)
setError(null)
// Start the stream
const response = await fetch(`${API_BASE}/cameras/${cameraName}/start-stream`, {
method: 'POST'
})
if (response.ok) {
setIsStreaming(true)
// Set the stream source with timestamp to prevent caching
if (imgRef.current) {
imgRef.current.src = `${API_BASE}/cameras/${cameraName}/stream?t=${Date.now()}`
}
} else {
throw new Error(`Failed to start stream: ${response.statusText}`)
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to start stream'
setError(errorMessage)
} finally {
setLoading(false)
}
}
const stopStreaming = async () => {
try {
if (isStreaming) {
await fetch(`${API_BASE}/cameras/${cameraName}/stop-stream`, {
method: 'POST'
})
setIsStreaming(false)
}
} catch (err) {
console.error('Error stopping stream:', err)
}
}
const handleImageError = () => {
setError('Failed to load camera stream')
}
const handleImageLoad = () => {
setError(null)
}
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-900">
<div className="text-center text-white">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white mx-auto"></div>
<p className="mt-4">Starting camera stream...</p>
</div>
</div>
)
}
if (error) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-900">
<div className="text-center text-white">
<div className="bg-red-600 rounded-full w-16 h-16 flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<h2 className="text-xl font-semibold mb-2">Stream Error</h2>
<p className="text-gray-300 mb-4">{error}</p>
<button
onClick={startStreaming}
className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-md"
>
Retry
</button>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
<div className="relative">
{/* Camera Label */}
<div className="absolute top-4 left-4 z-10">
<div className="bg-black bg-opacity-75 text-white px-3 py-1 rounded-md text-sm font-medium">
{cameraName} - Live View
</div>
</div>
{/* Live Stream */}
<img
ref={imgRef}
alt={`Live stream from ${cameraName}`}
className="max-w-full max-h-screen object-contain"
onError={handleImageError}
onLoad={handleImageLoad}
/>
{/* Status Indicator */}
<div className="absolute bottom-4 right-4 z-10">
<div className="flex items-center space-x-2 bg-black bg-opacity-75 text-white px-3 py-1 rounded-md">
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
<span className="text-sm">LIVE</span>
</div>
</div>
</div>
</div>
)
}