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,11 +73,25 @@ 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>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<Link
|
||||||
|
href="/simulator"
|
||||||
|
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">
|
<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" />
|
<PlusCircle className="w-4 h-4" />
|
||||||
Proactive Provisioning
|
Proactive Provisioning
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-6">
|
<div className="grid grid-cols-1 gap-6">
|
||||||
<div className="bg-background border border-border rounded-[2rem] overflow-hidden backdrop-blur-xl">
|
<div className="bg-background border border-border rounded-[2rem] overflow-hidden backdrop-blur-xl">
|
||||||
|
|||||||
@@ -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,6 +98,27 @@ export default function DashboardPage() {
|
|||||||
Operational Telemetry
|
Operational Telemetry
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="flex items-center gap-2 bg-secondary/5 p-1 rounded-xl border border-border">
|
||||||
|
<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="flex items-center gap-3">
|
||||||
<div className="text-[10px] font-mono text-secondary uppercase tracking-tighter">
|
<div className="text-[10px] font-mono text-secondary uppercase tracking-tighter">
|
||||||
Last sync: <span className="text-foreground">{lastUpdate ? lastUpdate.toLocaleTimeString() : "--:--:--"}</span>
|
Last sync: <span className="text-foreground">{lastUpdate ? lastUpdate.toLocaleTimeString() : "--:--:--"}</span>
|
||||||
@@ -107,7 +129,67 @@ export default function DashboardPage() {
|
|||||||
)} />
|
)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{viewMode === "RETAIL" ? (
|
||||||
|
/* Retail Mode UI - Ultra Simplified */
|
||||||
|
<div className="flex flex-col items-center justify-center py-20 animate-in zoom-in-95 duration-700">
|
||||||
|
<div className={cn(
|
||||||
|
"w-full max-w-2xl overflow-hidden rounded-[3rem] p-16 text-center border-2 transition-all duration-1000",
|
||||||
|
data.recentTests[0]?.status === "PASS"
|
||||||
|
? "bg-emerald-500/5 border-emerald-500/20 text-emerald-500 shadow-[0_20px_100px_rgba(16,185,129,0.1)]"
|
||||||
|
: "bg-rose-500/5 border-rose-500/20 text-rose-500 shadow-[0_20px_100px_rgba(244,63,94,0.1)]"
|
||||||
|
)}>
|
||||||
|
<div className="space-y-8">
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<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"
|
||||||
|
)}>
|
||||||
|
{data.recentTests[0]?.status === "PASS" ? (
|
||||||
|
<CheckCircle2 className="w-16 h-16" />
|
||||||
|
) : (
|
||||||
|
<AlertCircle className="w-16 h-16" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h2 className="text-8xl font-black tracking-tighter uppercase">
|
||||||
|
{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 className="mt-12 flex items-center gap-8">
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-[10px] font-black text-secondary uppercase tracking-widest mb-1">Active Station</p>
|
||||||
|
<p className="text-sm font-black text-foreground">{data.enrolledDevices[0]?.name || "Front Desk 01"}</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-px h-8 bg-border" />
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-[10px] font-black text-secondary uppercase tracking-widest mb-1">Today's Volume</p>
|
||||||
|
<p className="text-sm font-black text-foreground">{data.stats.testCount} Tests</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-10">
|
||||||
{/* Hero Stats */}
|
{/* Hero Stats */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{[
|
{[
|
||||||
@@ -243,7 +325,7 @@ export default function DashboardPage() {
|
|||||||
</h4>
|
</h4>
|
||||||
<div className="space-y-3 relative z-10">
|
<div className="space-y-3 relative z-10">
|
||||||
<p className="text-xs text-secondary leading-relaxed font-medium">
|
<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.
|
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>
|
</p>
|
||||||
<div className="flex items-center gap-2 py-3 border-t border-sky-500/10">
|
<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="relative flex h-2 w-2">
|
||||||
@@ -286,5 +368,7 @@ export default function DashboardPage() {
|
|||||||
</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