leLab / src /components /control /MetricsPanel.tsx
jurmy24's picture
initial commit
9d3c32a
raw
history blame
7.8 kB
import React, { useEffect, useRef } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
import { Camera, MicOff } from 'lucide-react';
interface MetricsPanelProps {
activeTab: 'SENSORS' | 'MOTORS';
setActiveTab: (tab: 'SENSORS' | 'MOTORS') => void;
sensorData: any[];
motorData: any[];
hasPermissions: boolean;
streamRef: React.RefObject<MediaStream | null>;
isVoiceActive: boolean;
micLevel: number;
}
const MetricsPanel: React.FC<MetricsPanelProps> = ({
activeTab,
setActiveTab,
sensorData,
motorData,
hasPermissions,
streamRef,
isVoiceActive,
micLevel,
}) => {
const sensorVideoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
if (activeTab === 'SENSORS' && hasPermissions && sensorVideoRef.current && streamRef.current) {
if (sensorVideoRef.current.srcObject !== streamRef.current) {
sensorVideoRef.current.srcObject = streamRef.current;
}
}
}, [activeTab, hasPermissions, streamRef]);
return (
<div className="w-full lg:w-1/2 p-2 sm:p-4">
<div className="bg-gray-900 rounded-lg p-4 h-full flex flex-col">
{/* Tab Headers */}
<div className="flex mb-4">
<button
onClick={() => setActiveTab('MOTORS')}
className={`px-6 py-2 rounded-t-lg text-sm sm:text-base ${
activeTab === 'MOTORS'
? 'bg-orange-500 text-white'
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
}`}
>
MOTORS
</button>
<button
onClick={() => setActiveTab('SENSORS')}
className={`px-6 py-2 rounded-t-lg ml-2 text-sm sm:text-base ${
activeTab === 'SENSORS'
? 'bg-orange-500 text-white'
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
}`}
>
SENSORS
</button>
</div>
{/* Chart Content */}
<div className="flex-1 overflow-y-auto">
{activeTab === 'SENSORS' && (
<div className="space-y-4">
{/* Webcam Feed */}
<div className="border border-gray-800 rounded p-2 flex flex-col h-64">
<h3 className="text-sm text-white font-medium mb-2">Live Camera Feed</h3>
{hasPermissions ? (
<div className="flex-1 bg-black rounded overflow-hidden">
<video
ref={sensorVideoRef}
autoPlay
muted
playsInline
className="w-full h-full object-contain"
/>
</div>
) : (
<div className="flex-1 flex items-center justify-center bg-black rounded">
<div className="text-center">
<Camera className="w-12 h-12 mx-auto text-gray-500 mb-2" />
<p className="text-gray-400">Camera permission not granted.</p>
</div>
</div>
)}
</div>
{/* Mic Detection & Other Sensors */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="border border-gray-800 rounded p-2 flex flex-col justify-center min-h-[120px]">
<h3 className="text-sm text-center text-white font-medium mb-2">Voice Activity</h3>
{hasPermissions ? (
<div className="flex-1 flex flex-col items-center justify-center gap-2 text-center">
<div className="flex items-end h-10 gap-px w-full justify-center">
{[...Array(15)].map((_, i) => {
const barIsActive = isVoiceActive && i < (micLevel / 120 * 15);
return (
<div
key={i}
className={`w-1.5 rounded-full transition-colors duration-75 ${barIsActive ? 'bg-orange-500' : 'bg-gray-700'}`}
style={{ height: `${(i / 15 * 60) + 20}%` }}
/>
);
})}
</div>
<p className="text-xs text-gray-300">
{isVoiceActive ? "Voice commands active" : "Voice commands muted"}
</p>
</div>
) : (
<div className="flex-1 flex items-center justify-center bg-black rounded">
<div className="text-center">
<MicOff className="w-8 h-8 mx-auto text-gray-500 mb-2" />
<p className="text-gray-400">Microphone permission not granted.</p>
</div>
</div>
)}
</div>
{/* Sensor Charts */}
{['sensor3', 'sensor4'].map((sensor, index) => (
<div key={sensor} className="border border-gray-800 rounded p-2 flex flex-col h-auto min-h-[120px]">
<h3 className="text-sm text-white font-medium mb-2">Sensor {index + 3}</h3>
<ResponsiveContainer width="100%" height="90%">
<LineChart data={sensorData}>
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
<XAxis hide />
<YAxis fontSize={12} stroke="#9CA3AF" />
<Tooltip
contentStyle={{
backgroundColor: '#1F2937',
border: '1px solid #374151',
color: '#fff'
}}
/>
<Line
type="monotone"
dataKey={sensor}
stroke={index % 2 === 1 ? '#ff6b35' : '#ffdd44'}
strokeWidth={2}
dot={false}
/>
</LineChart>
</ResponsiveContainer>
</div>
))}
</div>
</div>
)}
{activeTab === 'MOTORS' && (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{['motor1', 'motor2', 'motor3', 'motor4', 'motor5', 'motor6'].map((motor, index) => (
<div key={motor} className="border border-gray-800 rounded p-2 h-40">
<h3 className="text-sm text-white font-medium mb-2">Motor {index + 1}</h3>
<ResponsiveContainer width="100%" height="80%">
<LineChart data={motorData}>
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
<XAxis hide />
<YAxis fontSize={12} stroke="#9CA3AF" />
<Tooltip
contentStyle={{
backgroundColor: '#1F2937',
border: '1px solid #374151',
color: '#fff'
}}
/>
<Line
type="monotone"
dataKey={motor}
stroke={index % 2 === 0 ? '#ff6b35' : '#ffdd44'}
strokeWidth={2}
dot={false}
/>
</LineChart>
</ResponsiveContainer>
</div>
))}
</div>
)}
</div>
</div>
</div>
);
};
export default MetricsPanel;