Rebranded to console tester.
This commit is contained in:
17
README.md
17
README.md
@@ -1,11 +1,12 @@
|
|||||||
# HDMI Tester Enterprise
|
# HDMI Console Diagnostics (Enterprise)
|
||||||
|
|
||||||
A high-performance orchestration and telemetry platform designed for real-time monitoring and lifecycle management of HDMI testing hardware. This platform provides a "single pane of glass" view for large fleets of test nodes, offering sub-ms edge latency tracking and remote hardware orchestration.
|
A high-performance orchestration platform designed for real-time monitoring and repair diagnostics of **Console HDMI Ports** (PS5, Xbox, Switch). This platform provides a "single pane of glass" view for large fleets of test nodes, offering sub-ms edge latency tracking and remote hardware orchestration for front-desk and technical staff.
|
||||||
|
|
||||||
## 🚀 Key Features
|
## 🚀 Key Features
|
||||||
|
|
||||||
- **Operational Telemetry**: Real-time monitoring of active nodes, test results, and system health.
|
- **Port Telemetry**: Real-time monitoring of active nodes, port diagnostic results, and system health.
|
||||||
- **Node Insights**: Tactical spectral analysis of HDMI bus metrics including DDC, CEC, and TMDS voltage verification.
|
- **Port Insights**: Tactical spectral analysis of HDMI pins (DDC, CEC, TMDS) for board-level repair verification.
|
||||||
|
- **Retail & Enterprise Views**: Simplified "Good/Bad" UI for front-desk staff and detailed telemetry for technicians.
|
||||||
- **Command Center**: Enterprise-grade fleet management for device enrollment and environment orchestration.
|
- **Command Center**: Enterprise-grade fleet management for device enrollment and environment orchestration.
|
||||||
- **Device Configuration**:
|
- **Device Configuration**:
|
||||||
- Remote renaming and identification.
|
- Remote renaming and identification.
|
||||||
@@ -15,6 +16,7 @@ A high-performance orchestration and telemetry platform designed for real-time m
|
|||||||
- **Premium UI/UX**:
|
- **Premium UI/UX**:
|
||||||
- High-contrast Light and Dark modes.
|
- High-contrast Light and Dark modes.
|
||||||
- Safari-optimized rendering and backdrop filters.
|
- Safari-optimized rendering and backdrop filters.
|
||||||
|
- **Virtual Hardware Simulator**: A web-based tactile OLED emulator for immediate testing.
|
||||||
- Fluid micro-animations and responsive layouts.
|
- Fluid micro-animations and responsive layouts.
|
||||||
|
|
||||||
## 🛠 Tech Stack
|
## 🛠 Tech Stack
|
||||||
@@ -53,8 +55,13 @@ Open [http://localhost:3000](http://localhost:3000) to access the dashboard.
|
|||||||
|
|
||||||
## 🤖 Hardware Simulation
|
## 🤖 Hardware Simulation
|
||||||
|
|
||||||
To test the platform without physical hardware, use the provided simulator. It mimics a head unit with active mesh capabilities.
|
To test the platform without physical hardware, you can use either the interactive web emulator or the Python-based CLI simulator.
|
||||||
|
|
||||||
|
### Web Simulator (Recommended)
|
||||||
|
Access the tactile OLED emulator directly in your browser: [http://localhost:3000/simulator](http://localhost:3000/simulator)
|
||||||
|
- **Features**: Real-time OLED display, keyboard shortcuts (R/T/S/B), and live system logs.
|
||||||
|
|
||||||
|
### Python CLI Simulator
|
||||||
```bash
|
```bash
|
||||||
# Register a simulated device
|
# Register a simulated device
|
||||||
python3 firmware/simulator.py register
|
python3 firmware/simulator.py register
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import requests
|
|||||||
import uuid
|
import uuid
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
class HeadUnit:
|
class PortTester:
|
||||||
def __init__(self, server_url="http://localhost:3000", config_path="firmware/config/device.json"):
|
def __init__(self, server_url="http://localhost:3000", config_path="firmware/config/device.json"):
|
||||||
self.server_url = server_url
|
self.server_url = server_url
|
||||||
self.config_path = config_path
|
self.config_path = config_path
|
||||||
@@ -109,42 +109,39 @@ class HeadUnit:
|
|||||||
self.save_config()
|
self.save_config()
|
||||||
print(f"[*] Switched to Slot {self.active_slot}.")
|
print(f"[*] Switched to Slot {self.active_slot}.")
|
||||||
|
|
||||||
def generate_mock_hdmi_test():
|
def generate_console_port_test():
|
||||||
|
"""Generates mock spectral data for a console HDMI port."""
|
||||||
|
pins = ["DDC_SCL", "DDC_SDA", "CEC", "HPD", "TMDS_CLK+", "TMDS_CLK-"]
|
||||||
|
results = {pin: round(0.400 + (random.random() * 0.2), 3) for pin in pins}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"type": "HDMI_OFFLINE",
|
"type": "PORT_DIAGNOSTIC",
|
||||||
"status": "PASS",
|
"status": "PASS",
|
||||||
"hdmi5v": False,
|
"hdmi5v": True,
|
||||||
"summary": "Offline diode check completed. All lines within tolerance.",
|
"summary": "Port handshake confirmed. Pin continuity within spec.",
|
||||||
"diodeResults": {
|
"diodeResults": results
|
||||||
"DDC_SCL": 0.542,
|
|
||||||
"DDC_SDA": 0.544,
|
|
||||||
"CEC": 0.612,
|
|
||||||
"HPD": 0.589,
|
|
||||||
"TMDS_CLK+": 0.498,
|
|
||||||
"TMDS_CLK-": 0.497
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
hu = HeadUnit()
|
hu = PortTester()
|
||||||
|
|
||||||
def print_menu():
|
def print_menu():
|
||||||
print("\n" + "="*45)
|
print("\n" + "="*45)
|
||||||
print(f" HDMI TESTER - HARDWARE SIMULATOR (v1.2.0)")
|
print(f" HDMI PORT DIAGNOSTICS - HARDWARE SIMULATOR")
|
||||||
print("="*45)
|
print("="*45)
|
||||||
print(f" Status: [{hu.status}]")
|
print(f" Status: [{hu.status}]")
|
||||||
print(f" Mode: [{hu.mode}]")
|
print(f" Mode: [{hu.mode}]")
|
||||||
print(f" Active: [Slot {hu.active_slot}]")
|
print(f" Active: [Slot {hu.active_slot}]")
|
||||||
print(f" Serial: [{hu.serial_number}]")
|
print(f" Node: [{hu.serial_number}]")
|
||||||
print("-" * 45)
|
print("-" * 45)
|
||||||
print(" 1. Register / Sync Device")
|
print(" 1. Register / Sync Port Diagnostics")
|
||||||
print(" 2. Report Mock HDMI Test")
|
print(" 2. Run Console Port Sweep")
|
||||||
print(" 3. Switch to DEV Mode")
|
print(" 3. Switch to DEV Mode")
|
||||||
print(" 4. Switch to PROD Mode")
|
print(" 4. Switch to PROD Mode")
|
||||||
print(" 5. Swap Active Slot (A/B)")
|
print(" 5. Swap Active Slot (A/B)")
|
||||||
print(" 6. Start Automated Loop")
|
print(" 6. Start Automated Loop")
|
||||||
print(" 7. Request Re-enlistment")
|
print(" 7. Request Re-enlistment")
|
||||||
print(" 0. Exit Orchestrator")
|
print(" 0. Power Down Node")
|
||||||
print("-" * 45)
|
print("-" * 45)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@@ -154,7 +151,7 @@ if __name__ == "__main__":
|
|||||||
if choice == "1":
|
if choice == "1":
|
||||||
hu.register()
|
hu.register()
|
||||||
elif choice == "2":
|
elif choice == "2":
|
||||||
hu.report_test(generate_mock_hdmi_test())
|
hu.report_test(generate_console_port_test())
|
||||||
elif choice == "3":
|
elif choice == "3":
|
||||||
hu.switch_mode("DEV")
|
hu.switch_mode("DEV")
|
||||||
elif choice == "4":
|
elif choice == "4":
|
||||||
@@ -171,7 +168,7 @@ if __name__ == "__main__":
|
|||||||
break
|
break
|
||||||
|
|
||||||
if hu.status == "ENROLLED":
|
if hu.status == "ENROLLED":
|
||||||
hu.report_test(generate_mock_hdmi_test())
|
hu.report_test(generate_console_port_test())
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
if hu.status == "OFFLINE":
|
if hu.status == "OFFLINE":
|
||||||
|
|||||||
BIN
prisma/dev.db
BIN
prisma/dev.db
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Shield, ArrowRight, Settings2, PlusCircle, CheckCircle2, XCircle, Clock, ChevronRight, RefreshCw } from "lucide-react";
|
import { Shield, ArrowRight, Settings2, PlusCircle, CheckCircle2, XCircle, Clock, ChevronRight, RefreshCw, Activity } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
interface Device {
|
interface Device {
|
||||||
@@ -73,10 +73,24 @@ export default function AdminPage() {
|
|||||||
</h1>
|
</h1>
|
||||||
<p className="text-secondary font-medium text-sm tracking-wide">Manage device fleet, enrollment, and firmware orchestration.</p>
|
<p className="text-secondary font-medium text-sm tracking-wide">Manage device fleet, enrollment, and firmware orchestration.</p>
|
||||||
</div>
|
</div>
|
||||||
<button className="bg-sky-500 hover:bg-sky-400 text-white px-6 py-2.5 rounded-2xl text-[10px] font-black uppercase tracking-widest flex items-center gap-3 transition-all shadow-[0_0_25px_rgba(56,189,248,0.2)] hover:scale-105 active:scale-95">
|
<div className="flex items-center gap-4">
|
||||||
<PlusCircle className="w-4 h-4" />
|
<Link
|
||||||
Proactive Provisioning
|
href="/simulator"
|
||||||
</button>
|
className="group flex items-center gap-3 bg-secondary/5 border border-border px-5 py-2.5 rounded-2xl hover:bg-sky-500/10 hover:border-sky-500/30 transition-all active:scale-95"
|
||||||
|
>
|
||||||
|
<div className="w-8 h-8 rounded-lg bg-sky-500/10 flex items-center justify-center text-sky-500 group-hover:bg-sky-500 group-hover:text-white transition-all">
|
||||||
|
<Activity className="w-4 h-4" />
|
||||||
|
</div>
|
||||||
|
<div className="text-left">
|
||||||
|
<p className="text-[10px] font-black uppercase tracking-widest text-foreground">Launch Virtual Node</p>
|
||||||
|
<p className="text-[8px] font-bold text-secondary uppercase tracking-tight">OLED Hardware Emulator</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
<button className="bg-sky-500 hover:bg-sky-400 text-white px-6 py-2.5 rounded-2xl text-[10px] font-black uppercase tracking-widest flex items-center gap-3 transition-all shadow-[0_0_25px_rgba(56,189,248,0.2)] hover:scale-105 active:scale-95">
|
||||||
|
<PlusCircle className="w-4 h-4" />
|
||||||
|
Proactive Provisioning
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-6">
|
<div className="grid grid-cols-1 gap-6">
|
||||||
|
|||||||
420
src/app/page.tsx
420
src/app/page.tsx
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Activity, Cpu, CheckCircle2, AlertCircle, Clock, Zap } from "lucide-react";
|
import { Activity, Cpu, CheckCircle2, AlertCircle, Clock, Zap, RefreshCw } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { clsx, type ClassValue } from "clsx";
|
import { clsx, type ClassValue } from "clsx";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
@@ -41,6 +41,7 @@ export default function DashboardPage() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [lastUpdate, setLastUpdate] = useState<Date | null>(null);
|
const [lastUpdate, setLastUpdate] = useState<Date | null>(null);
|
||||||
const [isUpdating, setIsUpdating] = useState(false);
|
const [isUpdating, setIsUpdating] = useState(false);
|
||||||
|
const [viewMode, setViewMode] = useState<"ENTERPRISE" | "RETAIL">("ENTERPRISE");
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -97,194 +98,277 @@ export default function DashboardPage() {
|
|||||||
Operational Telemetry
|
Operational Telemetry
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-4">
|
||||||
<div className="text-[10px] font-mono text-secondary uppercase tracking-tighter">
|
<div className="flex items-center gap-2 bg-secondary/5 p-1 rounded-xl border border-border">
|
||||||
Last sync: <span className="text-foreground">{lastUpdate ? lastUpdate.toLocaleTimeString() : "--:--:--"}</span>
|
<button
|
||||||
|
onClick={() => setViewMode("RETAIL")}
|
||||||
|
className={cn(
|
||||||
|
"px-4 py-1.5 rounded-lg text-[9px] font-black uppercase tracking-widest transition-all",
|
||||||
|
viewMode === "RETAIL" ? "bg-background text-foreground shadow-sm" : "text-secondary hover:text-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Retail
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setViewMode("ENTERPRISE")}
|
||||||
|
className={cn(
|
||||||
|
"px-4 py-1.5 rounded-lg text-[9px] font-black uppercase tracking-widest transition-all",
|
||||||
|
viewMode === "ENTERPRISE" ? "bg-background text-foreground shadow-sm" : "text-secondary hover:text-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Enterprise
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="text-[10px] font-mono text-secondary uppercase tracking-tighter">
|
||||||
|
Last sync: <span className="text-foreground">{lastUpdate ? lastUpdate.toLocaleTimeString() : "--:--:--"}</span>
|
||||||
|
</div>
|
||||||
|
<div className={cn(
|
||||||
|
"w-2 h-2 rounded-full transition-all duration-500",
|
||||||
|
isUpdating ? "bg-sky-400 shadow-[0_0_8px_rgba(56,189,248,0.8)]" : "bg-secondary/20"
|
||||||
|
)} />
|
||||||
</div>
|
</div>
|
||||||
<div className={cn(
|
|
||||||
"w-2 h-2 rounded-full transition-all duration-500",
|
|
||||||
isUpdating ? "bg-sky-400 shadow-[0_0_8px_rgba(56,189,248,0.8)]" : "bg-secondary/20"
|
|
||||||
)} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Hero Stats */}
|
{viewMode === "RETAIL" ? (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
/* Retail Mode UI - Ultra Simplified */
|
||||||
{[
|
<div className="flex flex-col items-center justify-center py-20 animate-in zoom-in-95 duration-700">
|
||||||
{ label: "Active Nodes", value: data.stats.onlineCount, icon: Activity, color: "text-sky-400", glow: "shadow-sky-500/10" },
|
<div className={cn(
|
||||||
{ label: "Tests Conducted", value: data.stats.testCount, icon: Zap, color: "text-amber-400", glow: "shadow-amber-500/10" },
|
"w-full max-w-2xl overflow-hidden rounded-[3rem] p-16 text-center border-2 transition-all duration-1000",
|
||||||
{ label: "Registered Hardware", value: data.stats.deviceCount, icon: Cpu, color: "text-purple-400", glow: "shadow-purple-500/10" },
|
data.recentTests[0]?.status === "PASS"
|
||||||
].map((stat, i) => (
|
? "bg-emerald-500/5 border-emerald-500/20 text-emerald-500 shadow-[0_20px_100px_rgba(16,185,129,0.1)]"
|
||||||
<div
|
: "bg-rose-500/5 border-rose-500/20 text-rose-500 shadow-[0_20px_100px_rgba(244,63,94,0.1)]"
|
||||||
key={i}
|
)}>
|
||||||
className={cn(
|
<div className="space-y-8">
|
||||||
"bg-background border border-border rounded-2xl p-6 backdrop-blur-sm relative overflow-hidden group transition-all duration-700",
|
<div className="flex justify-center">
|
||||||
stat.glow
|
<div className={cn(
|
||||||
)}
|
"w-32 h-32 rounded-full flex items-center justify-center border-8 transition-all duration-1000 shadow-xl",
|
||||||
>
|
data.recentTests[0]?.status === "PASS" ? "bg-emerald-500/10 border-emerald-500" : "bg-rose-500/10 border-rose-500"
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.02] to-transparent pointer-events-none" />
|
)}>
|
||||||
<div className="flex items-center justify-between relative z-10">
|
{data.recentTests[0]?.status === "PASS" ? (
|
||||||
<div>
|
<CheckCircle2 className="w-16 h-16" />
|
||||||
<p className="text-slate-500 text-[10px] font-black uppercase tracking-[0.2em] mb-2">{stat.label}</p>
|
) : (
|
||||||
<h3 className={cn("text-4xl font-black tracking-tighter transition-all duration-500 font-mono", stat.color)}>
|
<AlertCircle className="w-16 h-16" />
|
||||||
{stat.value.toString().padStart(2, '0')}
|
)}
|
||||||
</h3>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-12 h-12 rounded-xl bg-secondary/5 flex items-center justify-center transition-all duration-700 group-hover:bg-secondary/10 group-hover:scale-110">
|
|
||||||
<stat.icon className={cn(
|
<div className="space-y-4">
|
||||||
"w-6 h-6 transition-all duration-700 opacity-60 group-hover:opacity-100",
|
<h2 className="text-8xl font-black tracking-tighter uppercase">
|
||||||
stat.color
|
{data.recentTests[0]?.status === "PASS" ? "Port OK" : "Port Fail"}
|
||||||
)} />
|
</h2>
|
||||||
|
<div className="h-px w-24 bg-current/20 mx-auto" />
|
||||||
|
<p className="text-foreground/70 font-bold text-2xl uppercase tracking-widest italic">
|
||||||
|
{data.recentTests[0]?.status === "PASS" ? "HDMI Port is Functional" : "Console Port Requires Repair"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pt-10 flex flex-col items-center gap-4">
|
||||||
|
<div className="px-6 py-2 bg-foreground/5 border border-foreground/10 rounded-full flex items-center gap-3">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-foreground animate-pulse" />
|
||||||
|
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-foreground">Waiting for Next Console</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-secondary font-medium text-sm">
|
||||||
|
Last test: {data.recentTests[0]?.summary}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Main Content Grid */}
|
<div className="mt-12 flex items-center gap-8">
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
<div className="text-center">
|
||||||
{/* Recent Activity Feed */}
|
<p className="text-[10px] font-black text-secondary uppercase tracking-widest mb-1">Active Station</p>
|
||||||
<div className="lg:col-span-2 space-y-4">
|
<p className="text-sm font-black text-foreground">{data.enrolledDevices[0]?.name || "Front Desk 01"}</p>
|
||||||
<div className="flex items-center justify-between mb-2">
|
</div>
|
||||||
<h2 className="text-sm font-black uppercase tracking-[0.2em] flex items-center gap-3 text-slate-300">
|
<div className="w-px h-8 bg-border" />
|
||||||
<Clock className="w-4 h-4 text-sky-400" />
|
<div className="text-center">
|
||||||
Live Sequence Stream
|
<p className="text-[10px] font-black text-secondary uppercase tracking-widest mb-1">Today's Volume</p>
|
||||||
</h2>
|
<p className="text-sm font-black text-foreground">{data.stats.testCount} Tests</p>
|
||||||
<div className="h-px flex-1 mx-6 bg-gradient-to-r from-white/5 to-transparent" />
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-10">
|
||||||
|
{/* Hero Stats */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
{[
|
||||||
|
{ label: "Active Nodes", value: data.stats.onlineCount, icon: Activity, color: "text-sky-400", glow: "shadow-sky-500/10" },
|
||||||
|
{ label: "Tests Conducted", value: data.stats.testCount, icon: Zap, color: "text-amber-400", glow: "shadow-amber-500/10" },
|
||||||
|
{ label: "Registered Hardware", value: data.stats.deviceCount, icon: Cpu, color: "text-purple-400", glow: "shadow-purple-500/10" },
|
||||||
|
].map((stat, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={cn(
|
||||||
|
"bg-background border border-border rounded-2xl p-6 backdrop-blur-sm relative overflow-hidden group transition-all duration-700",
|
||||||
|
stat.glow
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.02] to-transparent pointer-events-none" />
|
||||||
|
<div className="flex items-center justify-between relative z-10">
|
||||||
|
<div>
|
||||||
|
<p className="text-slate-500 text-[10px] font-black uppercase tracking-[0.2em] mb-2">{stat.label}</p>
|
||||||
|
<h3 className={cn("text-4xl font-black tracking-tighter transition-all duration-500 font-mono", stat.color)}>
|
||||||
|
{stat.value.toString().padStart(2, '0')}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div className="w-12 h-12 rounded-xl bg-secondary/5 flex items-center justify-center transition-all duration-700 group-hover:bg-secondary/10 group-hover:scale-110">
|
||||||
|
<stat.icon className={cn(
|
||||||
|
"w-6 h-6 transition-all duration-700 opacity-60 group-hover:opacity-100",
|
||||||
|
stat.color
|
||||||
|
)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-background border border-border rounded-2xl overflow-hidden backdrop-blur-md">
|
{/* Main Content Grid */}
|
||||||
<table className="w-full text-left border-collapse">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||||
<thead>
|
{/* Recent Activity Feed */}
|
||||||
<tr className="border-b border-border bg-background/30">
|
<div className="lg:col-span-2 space-y-4">
|
||||||
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary">Node</th>
|
<div className="flex items-center justify-between mb-2">
|
||||||
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary">Operation</th>
|
<h2 className="text-sm font-black uppercase tracking-[0.2em] flex items-center gap-3 text-slate-300">
|
||||||
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary">Status</th>
|
<Clock className="w-4 h-4 text-sky-400" />
|
||||||
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary">Execution</th>
|
Live Sequence Stream
|
||||||
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary"></th>
|
</h2>
|
||||||
</tr>
|
<div className="h-px flex-1 mx-6 bg-gradient-to-r from-white/5 to-transparent" />
|
||||||
</thead>
|
</div>
|
||||||
<tbody className="divide-y divide-white/[0.02]">
|
|
||||||
{data.recentTests.length === 0 ? (
|
<div className="bg-background border border-border rounded-2xl overflow-hidden backdrop-blur-md">
|
||||||
<tr>
|
<table className="w-full text-left border-collapse">
|
||||||
<td colSpan={5} className="px-6 py-16 text-center text-slate-500 italic font-medium">No telemetry data reported yet.</td>
|
<thead>
|
||||||
</tr>
|
<tr className="border-b border-border bg-background/30">
|
||||||
) : (
|
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary">Node</th>
|
||||||
data.recentTests.map((test) => (
|
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary">Operation</th>
|
||||||
<tr key={test.id} className="hover:bg-secondary/5 transition-all group animate-in fade-in slide-in-from-left-2 duration-500">
|
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary">Status</th>
|
||||||
<td className="px-6 py-4">
|
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary">Execution</th>
|
||||||
|
<th className="px-6 py-4 text-[10px] font-black uppercase tracking-[0.2em] text-secondary"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-white/[0.02]">
|
||||||
|
{data.recentTests.length === 0 ? (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={5} className="px-6 py-16 text-center text-slate-500 italic font-medium">No telemetry data reported yet.</td>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
data.recentTests.map((test) => (
|
||||||
|
<tr key={test.id} className="hover:bg-secondary/5 transition-all group animate-in fade-in slide-in-from-left-2 duration-500">
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="w-8 h-8 rounded-lg bg-background border border-border flex items-center justify-center font-mono text-[10px] font-bold text-sky-400 shadow-sm">
|
||||||
|
HU
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-bold text-foreground text-xs">
|
||||||
|
{test.device.name && test.device.name !== "Unnamed Device" ? test.device.name : test.device.serialNumber}
|
||||||
|
</span>
|
||||||
|
<span className="text-[9px] text-secondary font-mono tracking-tighter uppercase whitespace-nowrap">
|
||||||
|
ID: {test.device.serialNumber}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<span className="text-[10px] px-2 py-0.5 rounded bg-secondary/5 border border-border text-secondary font-black uppercase tracking-widest whitespace-nowrap">
|
||||||
|
{test.type.replace("_", " ")}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
{test.status === "PASS" ? (
|
||||||
|
<div className="flex items-center gap-2 text-emerald-400 text-[10px] font-black uppercase tracking-widest">
|
||||||
|
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse shadow-[0_0_8px_rgba(16,185,129,0.5)]" />
|
||||||
|
Nominal
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center gap-2 text-rose-400 text-[10px] font-black uppercase tracking-widest">
|
||||||
|
<span className="w-1.5 h-1.5 rounded-full bg-rose-500 shadow-[0_0_8px_rgba(244,63,94,0.5)]" />
|
||||||
|
Fault
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-[10px] font-bold text-foreground font-mono">
|
||||||
|
{new Date(test.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })}
|
||||||
|
</span>
|
||||||
|
<span className="text-[8px] text-secondary font-black uppercase tracking-widest">
|
||||||
|
{new Date(test.timestamp).toLocaleDateString([], { month: 'short', day: 'numeric' })}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 text-right">
|
||||||
|
<Link
|
||||||
|
href={`/tests/${test.id}`}
|
||||||
|
className="px-3 py-1 bg-sky-500/10 hover:bg-sky-500/20 border border-sky-500/20 rounded-lg text-[9px] font-black text-sky-400 uppercase tracking-widest transition-all hover:scale-105 active:scale-95"
|
||||||
|
>
|
||||||
|
Insights
|
||||||
|
</Link>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar */}
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="bg-gradient-to-br from-sky-500/10 to-transparent border border-sky-500/20 rounded-2xl p-6 relative overflow-hidden">
|
||||||
|
<div className="absolute top-0 right-0 p-4 opacity-5">
|
||||||
|
<Zap className="w-20 h-20 text-sky-400" />
|
||||||
|
</div>
|
||||||
|
<h4 className="font-black text-sky-400 text-[10px] uppercase tracking-[0.3em] mb-4 flex items-center gap-2 relative z-10">
|
||||||
|
<Activity className="w-3 h-3" />
|
||||||
|
Intelligence Engine
|
||||||
|
</h4>
|
||||||
|
<div className="space-y-3 relative z-10">
|
||||||
|
<p className="text-xs text-secondary leading-relaxed font-medium">
|
||||||
|
Real-time spectral analysis of <span className="text-foreground">Console HDMI Ports</span>. Processing orchestrated with <span className="text-sky-400 font-bold">sub-ms</span> edge latency.
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-2 py-3 border-t border-sky-500/10">
|
||||||
|
<span className="relative flex h-2 w-2">
|
||||||
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||||
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
|
||||||
|
</span>
|
||||||
|
<span className="text-[9px] font-black uppercase tracking-[0.15em] text-foreground">Neural Engine Online</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-background border border-border rounded-2xl p-6">
|
||||||
|
<div className="flex items-center justify-between mb-6">
|
||||||
|
<h4 className="font-black text-secondary text-[10px] uppercase tracking-[0.3em]">Fleet Nodes</h4>
|
||||||
|
<Link href="/admin" className="text-[9px] text-sky-400 hover:text-sky-300 uppercase font-black tracking-widest">Manage All</Link>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-3">
|
||||||
|
{data.enrolledDevices.length === 0 ? (
|
||||||
|
<p className="text-[10px] text-secondary italic text-center py-4 border border-dashed border-border rounded-xl">No active nodes in mesh.</p>
|
||||||
|
) : (
|
||||||
|
data.enrolledDevices.map((device) => (
|
||||||
|
<div key={device.id} className="p-3 bg-secondary/5 hover:bg-secondary/10 transition-colors rounded-xl border border-border flex items-center justify-between group cursor-default">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-8 h-8 rounded-lg bg-background border border-border flex items-center justify-center font-mono text-[10px] font-bold text-sky-400 shadow-sm">
|
<div className="w-8 h-8 rounded-lg bg-sky-500/10 flex items-center justify-center group-hover:bg-sky-500/20 transition-colors">
|
||||||
HU
|
<Cpu className="w-4 h-4 text-sky-400" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="font-bold text-foreground text-xs">
|
<span className="text-[10px] font-black text-foreground uppercase tracking-wider">
|
||||||
{test.device.name && test.device.name !== "Unnamed Device" ? test.device.name : test.device.serialNumber}
|
{device.name && device.name !== "Unnamed Device" ? device.name : device.serialNumber}
|
||||||
</span>
|
|
||||||
<span className="text-[9px] text-secondary font-mono tracking-tighter uppercase whitespace-nowrap">
|
|
||||||
ID: {test.device.serialNumber}
|
|
||||||
</span>
|
</span>
|
||||||
|
<span className="text-[8px] text-secondary font-mono uppercase tracking-tighter">System Health: Optimal</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
<div className="w-1.5 h-1.5 rounded-full bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.5)]" />
|
||||||
<td className="px-6 py-4">
|
</div>
|
||||||
<span className="text-[10px] px-2 py-0.5 rounded bg-secondary/5 border border-border text-secondary font-black uppercase tracking-widest whitespace-nowrap">
|
))
|
||||||
{test.type.replace("_", " ")}
|
)}
|
||||||
</span>
|
</div>
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4">
|
|
||||||
{test.status === "PASS" ? (
|
|
||||||
<div className="flex items-center gap-2 text-emerald-400 text-[10px] font-black uppercase tracking-widest">
|
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse shadow-[0_0_8px_rgba(16,185,129,0.5)]" />
|
|
||||||
Nominal
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center gap-2 text-rose-400 text-[10px] font-black uppercase tracking-widest">
|
|
||||||
<span className="w-1.5 h-1.5 rounded-full bg-rose-500 shadow-[0_0_8px_rgba(244,63,94,0.5)]" />
|
|
||||||
Fault
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4">
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="text-[10px] font-bold text-foreground font-mono">
|
|
||||||
{new Date(test.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })}
|
|
||||||
</span>
|
|
||||||
<span className="text-[8px] text-secondary font-black uppercase tracking-widest">
|
|
||||||
{new Date(test.timestamp).toLocaleDateString([], { month: 'short', day: 'numeric' })}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-6 py-4 text-right">
|
|
||||||
<Link
|
|
||||||
href={`/tests/${test.id}`}
|
|
||||||
className="px-3 py-1 bg-sky-500/10 hover:bg-sky-500/20 border border-sky-500/20 rounded-lg text-[9px] font-black text-sky-400 uppercase tracking-widest transition-all hover:scale-105 active:scale-95"
|
|
||||||
>
|
|
||||||
Insights
|
|
||||||
</Link>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Sidebar */}
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div className="bg-gradient-to-br from-sky-500/10 to-transparent border border-sky-500/20 rounded-2xl p-6 relative overflow-hidden">
|
|
||||||
<div className="absolute top-0 right-0 p-4 opacity-5">
|
|
||||||
<Zap className="w-20 h-20 text-sky-400" />
|
|
||||||
</div>
|
|
||||||
<h4 className="font-black text-sky-400 text-[10px] uppercase tracking-[0.3em] mb-4 flex items-center gap-2 relative z-10">
|
|
||||||
<Activity className="w-3 h-3" />
|
|
||||||
Intelligence Engine
|
|
||||||
</h4>
|
|
||||||
<div className="space-y-3 relative z-10">
|
|
||||||
<p className="text-xs text-secondary leading-relaxed font-medium">
|
|
||||||
Real-time spectral analysis of HDMI bus metrics. Processing orchestrated with <span className="text-sky-400 font-bold">sub-ms</span> edge latency.
|
|
||||||
</p>
|
|
||||||
<div className="flex items-center gap-2 py-3 border-t border-sky-500/10">
|
|
||||||
<span className="relative flex h-2 w-2">
|
|
||||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
|
||||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
|
|
||||||
</span>
|
|
||||||
<span className="text-[9px] font-black uppercase tracking-[0.15em] text-foreground">Neural Engine Online</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-background border border-border rounded-2xl p-6">
|
|
||||||
<div className="flex items-center justify-between mb-6">
|
|
||||||
<h4 className="font-black text-secondary text-[10px] uppercase tracking-[0.3em]">Fleet Nodes</h4>
|
|
||||||
<Link href="/admin" className="text-[9px] text-sky-400 hover:text-sky-300 uppercase font-black tracking-widest">Manage All</Link>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-3">
|
|
||||||
{data.enrolledDevices.length === 0 ? (
|
|
||||||
<p className="text-[10px] text-secondary italic text-center py-4 border border-dashed border-border rounded-xl">No active nodes in mesh.</p>
|
|
||||||
) : (
|
|
||||||
data.enrolledDevices.map((device) => (
|
|
||||||
<div key={device.id} className="p-3 bg-secondary/5 hover:bg-secondary/10 transition-colors rounded-xl border border-border flex items-center justify-between group cursor-default">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="w-8 h-8 rounded-lg bg-sky-500/10 flex items-center justify-center group-hover:bg-sky-500/20 transition-colors">
|
|
||||||
<Cpu className="w-4 h-4 text-sky-400" />
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="text-[10px] font-black text-foreground uppercase tracking-wider">
|
|
||||||
{device.name && device.name !== "Unnamed Device" ? device.name : device.serialNumber}
|
|
||||||
</span>
|
|
||||||
<span className="text-[8px] text-secondary font-mono uppercase tracking-tighter">System Health: Optimal</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-1.5 h-1.5 rounded-full bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.5)]" />
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default function WebSimulatorPage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState(s => ({ ...s, isTesting: true }));
|
setState(s => ({ ...s, isTesting: true }));
|
||||||
addLog("[OP] Initiating HDMI spectral sweep...");
|
addLog("[OP] Initiating HDMI Port Diagnostic sweep...");
|
||||||
|
|
||||||
await new Promise(r => setTimeout(r, 1500));
|
await new Promise(r => setTimeout(r, 1500));
|
||||||
|
|
||||||
@@ -111,10 +111,10 @@ export default function WebSimulatorPage() {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
serialNumber: state.serialNumber,
|
serialNumber: state.serialNumber,
|
||||||
type: "HDMI_OFFLINE",
|
type: "PORT_DIAGNOSTIC",
|
||||||
status: "PASS",
|
status: "PASS",
|
||||||
hdmi5v: false,
|
hdmi5v: true,
|
||||||
summary: "Virtual sweep completed within threshold.",
|
summary: "Port handshake confirmed. Pin continuity within spec.",
|
||||||
diodeResults: {
|
diodeResults: {
|
||||||
"DDC_SCL": 0.500 + Math.random() * 0.1,
|
"DDC_SCL": 0.500 + Math.random() * 0.1,
|
||||||
"DDC_SDA": 0.500 + Math.random() * 0.1,
|
"DDC_SDA": 0.500 + Math.random() * 0.1,
|
||||||
@@ -215,9 +215,9 @@ export default function WebSimulatorPage() {
|
|||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div className="flex flex-col items-center justify-center flex-1">
|
<div className="flex flex-col items-center justify-center flex-1">
|
||||||
<div className="text-white text-lg font-black tracking-tighter leading-none mb-1">
|
<div className="text-white text-lg font-black tracking-tighter leading-none mb-1">
|
||||||
{state.isTesting ? "RUNNING SWEEP" : state.serialNumber}
|
{state.isTesting ? "PORT SWEEP" : state.serialNumber}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-[8px] text-sky-400/60 font-black uppercase tracking-[0.2em]">Validated Path v1.0</div>
|
<div className="text-[8px] text-sky-400/60 font-black uppercase tracking-[0.2em]">Console Interface v1.0</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom Console */}
|
{/* Bottom Console */}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default async function TestDetailsPage({ params }: { params: Promise<{ id
|
|||||||
<Activity className="w-3 h-3" /> Dashboard
|
<Activity className="w-3 h-3" /> Dashboard
|
||||||
</Link>
|
</Link>
|
||||||
<span className="opacity-20">/</span>
|
<span className="opacity-20">/</span>
|
||||||
<span className="text-secondary/70">Node Insights</span>
|
<span className="text-secondary/70">Port Diagnostic Insights</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-8 pb-8 border-b border-border">
|
<div className="flex flex-col md:flex-row md:items-end justify-between gap-8 pb-8 border-b border-border">
|
||||||
@@ -85,7 +85,7 @@ export default async function TestDetailsPage({ params }: { params: Promise<{ id
|
|||||||
<div className="w-10 h-10 rounded-xl bg-sky-500/10 flex items-center justify-center">
|
<div className="w-10 h-10 rounded-xl bg-sky-500/10 flex items-center justify-center">
|
||||||
<Info className="w-5 h-5 text-sky-400" />
|
<Info className="w-5 h-5 text-sky-400" />
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-[10px] font-black uppercase tracking-[0.3em] text-secondary">Response Spectrum Analysis</h2>
|
<h2 className="text-[10px] font-black uppercase tracking-[0.3em] text-secondary">HDMI Port Pinout Analysis</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-4 py-1.5 bg-sky-500/10 border border-sky-500/20 rounded-full text-[9px] font-black text-sky-400 uppercase tracking-widest">
|
<div className="px-4 py-1.5 bg-sky-500/10 border border-sky-500/20 rounded-full text-[9px] font-black text-sky-400 uppercase tracking-widest">
|
||||||
Calibration: 0.8V VDD
|
Calibration: 0.8V VDD
|
||||||
|
|||||||
Reference in New Issue
Block a user