Skip to content

Commit 9a8c85d

Browse files
committed
refactor: remove unused TVStatic and CameraFeed components from Viewer3D
1 parent a2823c8 commit 9a8c85d

2 files changed

Lines changed: 56 additions & 156 deletions

File tree

workspaces/mission_planner_ui/frontend/src/components/MissionResultViewer.jsx

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ const MissionResultViewer = ({ onBack }) => {
310310
) : (
311311
<>
312312
{/* 3D Canvas */}
313-
<div className="flex-1 relative min-h-0" style={{ flexBasis: results.length > 0 ? '55%' : '100%' }}>
313+
<div className="flex-1 relative min-h-0" style={{ flexBasis: results.length > 0 ? '35%' : '100%' }}>
314314
<Canvas camera={{ position: [5, 5, 5], fov: 50 }}>
315315
<color attach="background" args={['#0a0a0f']} />
316316
<fog attach="fog" args={['#0a0a0f', 40, 120]} />
@@ -356,70 +356,66 @@ const MissionResultViewer = ({ onBack }) => {
356356
</div>
357357
</div>
358358

359-
{/* Results table */}
359+
{/* Results list */}
360360
{results.length > 0 && (
361-
<div className="border-t border-white/5 overflow-auto" style={{ flexBasis: '45%', flexShrink: 0 }}>
362-
<table className="w-full text-xs">
363-
<thead className="sticky top-0 bg-[#0a0a0f]/95 backdrop-blur-sm">
364-
<tr className="text-left text-zinc-600 uppercase tracking-wider border-b border-white/5 text-[9px]">
365-
<th className="px-4 py-2.5 font-bold">#</th>
366-
<th className="px-4 py-2.5 font-bold">Waypoint</th>
367-
<th className="px-4 py-2.5 font-bold">Purpose</th>
368-
<th className="px-4 py-2.5 font-bold">Result</th>
369-
<th className="px-4 py-2.5 font-bold">Confidence</th>
370-
<th className="px-4 py-2.5 font-bold">Analysis</th>
371-
<th className="px-4 py-2.5 font-bold">Image</th>
372-
</tr>
373-
</thead>
374-
<tbody>
375-
{waypoints.map((wp, idx) => {
376-
const r = resultMap[wp.ID];
377-
const isSuccess = r?.success === 'yes';
378-
const isActive = selectedWp?.ID === wp.ID;
379-
return (
380-
<tr key={wp.ID} onClick={() => handleWpSelect(wp, r)}
381-
className={`border-b border-white/[0.03] transition-colors cursor-pointer ${isActive ? 'bg-sky-600/10' : 'hover:bg-white/[0.02]'}`}>
382-
<td className="px-4 py-2.5 text-zinc-600 font-mono">{idx + 1}</td>
383-
<td className="px-4 py-2.5 font-bold text-zinc-200">{wp.name}</td>
384-
<td className="px-4 py-2.5">
385-
<span className="px-1.5 py-0.5 rounded-full bg-zinc-800 text-zinc-400 text-[8px] font-bold uppercase">{wp.purpose || 'none'}</span>
386-
</td>
387-
<td className="px-4 py-2.5">
388-
{r ? (
389-
<span className={`inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[8px] font-black uppercase ${isSuccess ? 'bg-emerald-500/20 text-emerald-400' : 'bg-red-500/20 text-red-400'}`}>
390-
{isSuccess ? <CheckCircle size={8} /> : <XCircle size={8} />} {isSuccess ? 'PASS' : 'FAIL'}
391-
</span>
392-
) : <span className="text-zinc-700 text-[9px]"></span>}
393-
</td>
394-
<td className="px-4 py-2.5">
395-
{r ? (
396-
<div className="flex items-center gap-2">
397-
<div className="w-14 h-1 rounded-full bg-zinc-800 overflow-hidden">
361+
<div className="border-t border-white/5 overflow-auto p-4 space-y-4 bg-[#050508]" style={{ flexBasis: '65%', flexShrink: 0 }}>
362+
{waypoints.map((wp, idx) => {
363+
const r = resultMap[wp.ID];
364+
const isSuccess = r?.success === 'yes';
365+
const isActive = selectedWp?.ID === wp.ID;
366+
return (
367+
<div key={wp.ID} onClick={() => handleWpSelect(wp, r)}
368+
className={`flex flex-col md:flex-row gap-4 p-4 rounded-xl border transition-colors cursor-pointer shadow-lg ${isActive ? 'bg-sky-900/10 border-sky-500/30' : 'bg-[#0a0a0f] border-white/5 hover:border-white/10'}`}>
369+
370+
{/* Left Column: Image */}
371+
<div className="shrink-0">
372+
{r?.image ? (
373+
<img src={r.image} alt="" className="h-40 w-56 rounded-lg border border-zinc-800 object-cover shadow-xl"
374+
onError={e => e.target.style.display = 'none'} />
375+
) : (
376+
<div className="h-40 w-56 rounded-lg border border-zinc-800 bg-zinc-900/50 flex items-center justify-center text-zinc-700 text-xs font-mono shadow-inner">NO IMAGE</div>
377+
)}
378+
</div>
379+
380+
{/* Right Column: Info */}
381+
<div className="flex-1 flex flex-col min-w-0">
382+
<div className="flex items-start justify-between mb-3 border-b border-white/5 pb-2">
383+
<div className="flex items-center gap-3">
384+
<span className="text-zinc-600 font-mono text-xs font-bold">#{idx + 1}</span>
385+
<h3 className="font-bold text-zinc-200 text-sm">{wp.name}</h3>
386+
<span className="px-2 py-0.5 rounded-full bg-zinc-800 text-zinc-400 text-[9px] font-bold uppercase">{wp.purpose || 'none'}</span>
387+
</div>
388+
<div className="flex items-center gap-4">
389+
{r && (
390+
<div className="flex items-center gap-2" title="Confidence Score">
391+
<div className="w-20 h-1.5 rounded-full bg-zinc-800 overflow-hidden">
398392
<div className={`h-full rounded-full ${(r.confidence || 0) > 0.7 ? 'bg-emerald-500' : (r.confidence || 0) > 0.4 ? 'bg-amber-500' : 'bg-red-500'}`}
399393
style={{ width: `${(r.confidence || 0) * 100}%` }} />
400394
</div>
401-
<span className="text-zinc-400 font-mono text-[9px]">{Math.round((r.confidence || 0) * 100)}%</span>
395+
<span className="text-zinc-400 font-mono text-[10px]">{Math.round((r.confidence || 0) * 100)}%</span>
402396
</div>
403-
) : <span className="text-zinc-700"></span>}
404-
</td>
405-
<td className="px-4 py-2.5 text-zinc-500 max-w-[160px] truncate text-[9px]">
406-
{(() => {
407-
const parsed = safeParseJson(r?.analysis);
408-
if (!parsed) return '—';
409-
return typeof parsed === 'object' ? (parsed.summary || JSON.stringify(parsed)) : parsed;
410-
})()}
411-
</td>
412-
<td className="px-4 py-2.5">
413-
{r?.image ? (
414-
<img src={r.image} alt="" className="h-7 w-10 rounded border border-zinc-800 object-cover"
415-
onError={e => e.target.style.display = 'none'} />
416-
) : <span className="text-zinc-700"></span>}
417-
</td>
418-
</tr>
419-
);
420-
})}
421-
</tbody>
422-
</table>
397+
)}
398+
{r ? (
399+
<span className={`inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-[10px] font-black uppercase shadow-md ${isSuccess ? 'bg-emerald-500/20 text-emerald-400 shadow-emerald-500/10' : 'bg-red-500/20 text-red-400 shadow-red-500/10'}`}>
400+
{isSuccess ? <CheckCircle size={12} /> : <XCircle size={12} />} {isSuccess ? 'PASS' : 'FAIL'}
401+
</span>
402+
) : <span className="text-zinc-700 text-[10px] uppercase font-bold">— pending</span>}
403+
</div>
404+
</div>
405+
406+
<div className="flex-1">
407+
{r ? (
408+
<div className="rounded-lg h-full overflow-hidden">
409+
<AnalysisRenderer analysisData={r.analysis} />
410+
</div>
411+
) : (
412+
<div className="h-full flex items-center text-zinc-600 text-xs italic">No analysis data available.</div>
413+
)}
414+
</div>
415+
</div>
416+
</div>
417+
);
418+
})}
423419
</div>
424420
)}
425421

workspaces/mission_planner_ui/frontend/src/components/Viewer3D.jsx

Lines changed: 0 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -394,94 +394,6 @@ const MiniMap = ({ waypoints = [], theme }) => {
394394
);
395395
};
396396

397-
const TVStatic = ({ width = "100%", height = "100%" }) => {
398-
const canvasRef = useRef(null);
399-
400-
useEffect(() => {
401-
const canvas = canvasRef.current;
402-
if (!canvas) return;
403-
const ctx = canvas.getContext('2d');
404-
let animationFrameId;
405-
406-
const resize = () => {
407-
canvas.width = canvas.offsetWidth;
408-
canvas.height = canvas.offsetHeight;
409-
};
410-
window.addEventListener('resize', resize);
411-
resize();
412-
413-
const render = () => {
414-
const imageData = ctx.createImageData(canvas.width, canvas.height);
415-
const data = imageData.data;
416-
for (let i = 0; i < data.length; i += 4) {
417-
const val = Math.random() * 255;
418-
data[i] = val; // R
419-
data[i + 1] = val; // G
420-
data[i + 2] = val; // B
421-
data[i + 3] = 255; // A
422-
}
423-
ctx.putImageData(imageData, 0, 0);
424-
animationFrameId = requestAnimationFrame(render);
425-
};
426-
427-
render();
428-
429-
return () => {
430-
cancelAnimationFrame(animationFrameId);
431-
window.removeEventListener('resize', resize);
432-
};
433-
}, []);
434-
435-
return (
436-
<div className="relative w-full h-full overflow-hidden bg-zinc-900">
437-
<canvas ref={canvasRef} className="w-full h-full opacity-40 mix-blend-screen" />
438-
<div className="absolute inset-0 bg-neutral-900/20 pointer-events-none"></div>
439-
{/* Scanlines Effect */}
440-
<div className="absolute inset-0 pointer-events-none opacity-20"
441-
style={{ backgroundImage: 'linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06))', backgroundSize: '100% 4px, 3px 100%' }}>
442-
</div>
443-
{/* NO SIGNAL Text Overlay */}
444-
<div className="absolute inset-0 flex flex-col items-center justify-center gap-2">
445-
<div className="px-3 py-1 bg-black/60 backdrop-blur-md border border-white/10 rounded flex items-center gap-3">
446-
<div className="w-2 h-2 bg-red-600 rounded-full animate-pulse shadow-[0_0_8px_rgba(220,38,38,0.8)]"></div>
447-
<span className="text-[10px] font-black tracking-[0.3em] text-white uppercase font-mono">No Signal</span>
448-
</div>
449-
</div>
450-
</div>
451-
);
452-
};
453-
454-
const CameraFeed = () => {
455-
const [imageSrc, setImageSrc] = useState(null);
456-
457-
useEffect(() => {
458-
const token = localStorage.getItem('auth_token');
459-
const ws = new WebSocket(`${WS_BASE}/ws/video?token=${token}`);
460-
ws.onmessage = (event) => {
461-
setImageSrc(`data:image/jpeg;base64,${event.data}`);
462-
};
463-
return () => ws.close();
464-
}, []);
465-
466-
if (!imageSrc) return (
467-
<div className="w-64 h-48 md:w-80 md:h-56 bg-black/80 flex items-center justify-center border border-zinc-800 rounded-lg overflow-hidden relative shadow-2xl transition-all duration-300 hover:scale-105">
468-
<TVStatic />
469-
</div>
470-
);
471-
472-
return (
473-
<div className="relative border border-zinc-500/50 rounded-lg overflow-hidden shadow-2xl bg-black w-64 md:w-80 transition-all duration-300 hover:scale-105">
474-
<img src={imageSrc} alt="Robot Camera" className="w-full h-auto object-cover" />
475-
<div className="absolute top-2 right-2 flex gap-1">
476-
<div className="w-2 h-2 bg-red-500 rounded-full animate-pulse shadow-[0_0_8px_red]"></div>
477-
</div>
478-
<div className={`absolute bottom-2 left-2 text-[10px] bg-zinc-950/80 px-2 py-0.5 rounded font-mono backdrop-blur-sm border border-white/10 ${isDark ? 'text-zinc-300' : 'text-blue-600'}`}>
479-
CAM_01 :: RAW
480-
</div>
481-
</div>
482-
);
483-
}
484-
485397
const InteractionPlane = ({ onPointSelected }) => {
486398
const pointerDownPos = useRef(null);
487399

@@ -1118,14 +1030,6 @@ const Viewer3D = ({ onBack, onLogout }) => {
11181030
<MiniMap waypoints={waypoints} theme={theme} />
11191031
</div>
11201032

1121-
{/* Camera Feed Overlay - Bottom Right */}
1122-
<div className="absolute bottom-6 right-6 z-20 flex flex-col items-end gap-2">
1123-
<div className={`text-[10px] font-bold uppercase tracking-wider mb-1 px-2 py-0.5 rounded backdrop-blur-md border ${isDark ? 'text-zinc-400 bg-black/40 border-white/5' : 'text-zinc-600 bg-white/60 border-zinc-200'}`}>
1124-
Live Feed
1125-
</div>
1126-
<CameraFeed />
1127-
</div>
1128-
11291033
{/* Mission Status HUD */}
11301034
{missionStatus && (missionStatus.status === 'running' || missionStatus.status === 'mapping') && (
11311035
<div className="absolute top-4 left-1/2 -translate-x-1/2 z-30 pointer-events-none animate-in fade-in slide-in-from-top-4 duration-500">

0 commit comments

Comments
 (0)