feat: Implement vision menu, processed data panel, and UI improvements
- Add VisionMenu component with Camera (QRCode) and Barcode (Keyence) submenus - Remove old CameraPanel component and replace with dropdown menu structure - Add ProcessedDataPanel to display processed data in bottom dock (5 rows visible) - Create SystemStatusPanel component with horizontal button layout (START/STOP/RESET) - Create EventLogPanel component for better code organization - Add device initialization feature with 7-axis progress tracking - Add GetProcessedData and GetInitializeStatus backend methods - Update Header menu layout to vertical (icon on top, text below) for more space - Update HomePage layout with bottom-docked ProcessedDataPanel - Refactor HomePage to use new modular components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
133
FrontEnd/components/ProcessedDataPanel.tsx
Normal file
133
FrontEnd/components/ProcessedDataPanel.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Database, RefreshCw } from 'lucide-react';
|
||||
import { comms } from '../communication';
|
||||
import { ProcessedDataRow } from '../types';
|
||||
import { PanelHeader } from './common/PanelHeader';
|
||||
import { CyberPanel } from './common/CyberPanel';
|
||||
|
||||
export const ProcessedDataPanel: React.FC = () => {
|
||||
const [data, setData] = useState<ProcessedDataRow[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const loadData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const dataJson = await comms.getProcessedData();
|
||||
const parsedData = JSON.parse(dataJson) as ProcessedDataRow[];
|
||||
// Display only the first 5 rows
|
||||
setData(parsedData.slice(0, 5));
|
||||
} catch (error) {
|
||||
console.error('[ProcessedDataPanel] Failed to load data:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
// Refresh every 5 seconds
|
||||
const interval = setInterval(loadData, 5000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const getRowBackgroundColor = (row: ProcessedDataRow) => {
|
||||
if (row.REMARK.startsWith('(BYPASS')) {
|
||||
return 'bg-sky-300/20';
|
||||
}
|
||||
return row.LOC === 'L' ? 'bg-slate-700/20' : 'bg-slate-800/10';
|
||||
};
|
||||
|
||||
const getRowTextColor = (row: ProcessedDataRow) => {
|
||||
if (row.REMARK.startsWith('(BYPASS')) {
|
||||
return 'text-white';
|
||||
}
|
||||
if (!row.PRNATTACH) {
|
||||
return 'text-red-500';
|
||||
}
|
||||
if (!row.PRNVALID) {
|
||||
return 'text-emerald-900';
|
||||
}
|
||||
return 'text-slate-200';
|
||||
};
|
||||
|
||||
return (
|
||||
<CyberPanel className="flex flex-col h-full">
|
||||
<PanelHeader
|
||||
icon={Database}
|
||||
title="PROCESSED DATA"
|
||||
className="mb-3"
|
||||
>
|
||||
<button
|
||||
onClick={loadData}
|
||||
disabled={loading}
|
||||
className="p-1.5 hover:bg-white/10 rounded transition-colors disabled:opacity-50"
|
||||
title="Refresh"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />
|
||||
</button>
|
||||
</PanelHeader>
|
||||
|
||||
<div className="flex-1 overflow-auto">
|
||||
<table className="w-full text-xs border-collapse">
|
||||
<thead className="sticky top-0 bg-slate-900/90 backdrop-blur-sm z-10">
|
||||
<tr className="border-b border-neon-blue/30">
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">R</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">MODEL</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">START</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">BATCH</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">SID</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">RID</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">VENDER</th>
|
||||
<th className="px-2 py-1.5 text-right font-tech text-neon-blue">QTY</th>
|
||||
<th className="px-2 py-1.5 text-right font-tech text-neon-blue">(MAX)</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">MFG</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">V.LOT</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">PARTNO</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">CPN</th>
|
||||
<th className="px-2 py-1.5 text-left font-tech text-neon-blue">Remark</th>
|
||||
<th className="px-2 py-1.5 text-center font-tech text-neon-blue">Attach</th>
|
||||
<th className="px-2 py-1.5 text-center font-tech text-neon-blue">Valid</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={16} className="px-2 py-8 text-center text-slate-500 font-mono text-xs">
|
||||
{loading ? 'Loading...' : 'No data available'}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
data.map((row, index) => (
|
||||
<tr
|
||||
key={index}
|
||||
className={`border-b border-white/5 ${getRowBackgroundColor(row)} ${getRowTextColor(row)} hover:bg-white/5 transition-colors`}
|
||||
>
|
||||
<td className="px-2 py-1.5 font-mono text-center">{row.target}</td>
|
||||
<td className="px-2 py-1.5 font-mono">{row.JTYPE}</td>
|
||||
<td className="px-2 py-1.5 font-mono">{row.STIME}</td>
|
||||
<td className="px-2 py-1.5 font-mono">{row.BATCH}</td>
|
||||
<td className="px-2 py-1.5 font-mono font-bold">{row.SID}</td>
|
||||
<td className="px-2 py-1.5 font-mono font-bold">{row.RID}</td>
|
||||
<td className="px-2 py-1.5 font-mono text-center">{row.VNAME}</td>
|
||||
<td className="px-2 py-1.5 font-mono text-right">{row.QTY.toLocaleString()}</td>
|
||||
<td className="px-2 py-1.5 font-mono text-right">{row.qtymax.toLocaleString()}</td>
|
||||
<td className="px-2 py-1.5 font-mono text-center">{row.MFGDATE}</td>
|
||||
<td className="px-2 py-1.5 font-mono">{row.VLOT}</td>
|
||||
<td className="px-2 py-1.5 font-mono">{row.PARTNO}</td>
|
||||
<td className="px-2 py-1.5 font-mono">{row.MCN}</td>
|
||||
<td className="px-2 py-1.5 font-mono">{row.REMARK}</td>
|
||||
<td className="px-2 py-1.5 text-center">
|
||||
<span className={`inline-block w-3 h-3 rounded-full ${row.PRNATTACH ? 'bg-neon-green' : 'bg-red-500'}`} />
|
||||
</td>
|
||||
<td className="px-2 py-1.5 text-center">
|
||||
<span className={`inline-block w-3 h-3 rounded-full ${row.PRNVALID ? 'bg-neon-green' : 'bg-red-500'}`} />
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</CyberPanel>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user