Add frontend/src/components/PortCard.tsx
This commit is contained in:
parent
d8e4386426
commit
606004a443
1 changed files with 117 additions and 0 deletions
117
frontend/src/components/PortCard.tsx
Normal file
117
frontend/src/components/PortCard.tsx
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import React from 'react';
|
||||
import { PortStatus } from '../types';
|
||||
|
||||
interface PortCardProps {
|
||||
port: PortStatus;
|
||||
isSelected: boolean;
|
||||
onSelect: (portIndex: number) => void;
|
||||
onStartRecording: (portIndex: number) => void;
|
||||
onStopRecording: (portIndex: number) => void;
|
||||
onOpenConfig: (portIndex: number) => void;
|
||||
}
|
||||
|
||||
function formatUptime(seconds: number): string {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const secs = Math.floor(seconds % 60);
|
||||
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function truncateFilePath(path: string, maxLength: number = 50): string {
|
||||
if (path.length <= maxLength) {
|
||||
return path;
|
||||
}
|
||||
return '...' + path.slice(-(maxLength - 3));
|
||||
}
|
||||
|
||||
export function PortCard({
|
||||
port,
|
||||
isSelected,
|
||||
onSelect,
|
||||
onStartRecording,
|
||||
onStopRecording,
|
||||
onOpenConfig,
|
||||
}: PortCardProps) {
|
||||
const recordingBadgeClass = port.is_recording
|
||||
? 'bg-red-500 text-white text-xs px-2 py-1 rounded'
|
||||
: 'bg-gray-500 text-white text-xs px-2 py-1 rounded';
|
||||
|
||||
const recordingStatus = port.is_recording ? 'RECORDING' : 'IDLE';
|
||||
|
||||
const cardClass = `bg-gray-800 border border-gray-700 rounded-lg p-4 cursor-pointer hover:border-blue-500 transition-colors ${
|
||||
isSelected ? 'border-blue-500 ring-2 ring-blue-500' : ''
|
||||
}`;
|
||||
|
||||
return (
|
||||
<div className={cardClass} onClick={() => onSelect(port.port_index)}>
|
||||
{/* Header: Port number and recording status badge */}
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-lg font-semibold text-white">Port {port.port_index}</h3>
|
||||
<span className={recordingBadgeClass}>{recordingStatus}</span>
|
||||
</div>
|
||||
|
||||
{/* Metrics row */}
|
||||
<div className="grid grid-cols-2 gap-2 mb-3 text-sm text-gray-300">
|
||||
<div>
|
||||
<span className="text-gray-400">Frames:</span> {port.frame_count}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-400">FPS:</span> {port.fps.toFixed(2)}
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-400">Bitrate:</span> {port.bitrate_mbps.toFixed(2)} Mbps
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-400">Uptime:</span> {formatUptime(port.uptime_seconds)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Current file path */}
|
||||
<div className="mb-3 text-sm">
|
||||
<span className="text-gray-400">File:</span>
|
||||
<div className="text-gray-300 truncate font-mono text-xs mt-1">
|
||||
{truncateFilePath(port.current_file)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Codec info */}
|
||||
<div className="mb-4 text-sm text-gray-300">
|
||||
<span className="text-gray-400">Codec:</span> {port.codec}
|
||||
</div>
|
||||
|
||||
{/* Control buttons */}
|
||||
<div className="flex gap-2">
|
||||
{!port.is_recording ? (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onStartRecording(port.port_index);
|
||||
}}
|
||||
className="bg-green-600 hover:bg-green-700 text-white px-3 py-1 rounded text-sm transition-colors"
|
||||
>
|
||||
Start
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onStopRecording(port.port_index);
|
||||
}}
|
||||
className="bg-red-600 hover:bg-red-700 text-white px-3 py-1 rounded text-sm transition-colors"
|
||||
>
|
||||
Stop
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onOpenConfig(port.port_index);
|
||||
}}
|
||||
className="bg-gray-600 hover:bg-gray-700 text-white px-3 py-1 rounded text-sm transition-colors"
|
||||
>
|
||||
Config
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue