356 lines
18 KiB
HTML
356 lines
18 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Swap Station – Dashboard</title>
|
||
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
|
||
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
|
||
<script src="https://unpkg.com/lucide@latest" defer></script>
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
|
||
<script src="./js/dashboard.js"></script>
|
||
<script>
|
||
tailwind.config = {
|
||
theme: {
|
||
extend: {
|
||
fontFamily: { sans: ["Inter","ui-sans-serif","system-ui"] },
|
||
keyframes: {
|
||
pulseDot: { "0%,100%": { transform:"scale(1)", opacity: 1 }, "50%": { transform:"scale(1.2)", opacity: .7 } }
|
||
},
|
||
animation: { pulseDot: "pulseDot 1.2s ease-in-out infinite" }
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
|
||
<style>
|
||
/* --- FINAL: Polished Flatpickr Dark/Glass Theme --- */
|
||
.flatpickr-calendar {
|
||
background: rgba(30, 41, 59, 0.7); /* Darker, glassier background */
|
||
backdrop-filter: saturate(150%) blur(16px);
|
||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||
border-radius: 0.75rem;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||
width: 320px;
|
||
}
|
||
|
||
/* FIX: Make month/year text visible */
|
||
.flatpickr-current-month .flatpickr-monthDropdown-months,
|
||
.flatpickr-current-month .numInput.cur-year {
|
||
color: #e2e8f0; /* Light text color */
|
||
font-weight: 600;
|
||
background: transparent;
|
||
}
|
||
|
||
/* Month navigation arrows */
|
||
.flatpickr-months .flatpickr-prev-month svg,
|
||
.flatpickr-months .flatpickr-next-month svg {
|
||
fill: #10b981; /* Emerald green */
|
||
}
|
||
|
||
/* Day of the week header (Sun, Mon, etc.) */
|
||
span.flatpickr-weekday {
|
||
color: #94a3b8; /* Lighter gray for contrast */
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Individual day numbers */
|
||
.flatpickr-day {
|
||
color: #cbd5e1;
|
||
border: 1px solid transparent;
|
||
transition: background 0.1s ease-in-out;
|
||
}
|
||
.flatpickr-day:hover {
|
||
background: rgba(56, 189, 248, 0.15); /* Subtle sky blue hover */
|
||
}
|
||
.flatpickr-day.today {
|
||
border-color: #34d399;
|
||
}
|
||
.flatpickr-day.selected {
|
||
background: linear-gradient(to right, #10b981, #14b8a6);
|
||
border-color: #10b981;
|
||
box-shadow: 0 0 15px rgba(16, 185, 129, 0.4);
|
||
}
|
||
|
||
/* FIX: Prevent white flash on time input focus */
|
||
.flatpickr-time input, .flatpickr-time .flatpickr-am-pm {
|
||
color: #e2e8f0;
|
||
background: rgba(0,0,0,0.3);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
.flatpickr-time input:focus {
|
||
background: rgba(0,0,0,0.4); /* Keep background dark on focus */
|
||
}
|
||
|
||
/* Time selection up/down arrows */
|
||
.flatpickr-time .arrowUp path, .flatpickr-time .arrowDown path {
|
||
fill: #94a3b8;
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
:root { color-scheme: dark; }
|
||
html, body { height: 100%; margin: 0; }
|
||
body { background:#0a0a0a; }
|
||
|
||
.page { max-width:1400px; }
|
||
|
||
.glass {
|
||
background: rgba(30,41,59,.45);
|
||
border: 1px solid rgba(255,255,255,.10);
|
||
border-radius: .9rem;
|
||
backdrop-filter: saturate(150%) blur(12px);
|
||
}
|
||
|
||
.badge { font-size:12px; font-weight:800; padding:.35rem .6rem; border-radius:.6rem; border:1px solid; display:inline-flex; align-items:center; gap:.4rem; }
|
||
.chip{display:inline-flex;align-items:center;gap:.4rem;padding:.1rem .5rem;border-radius:.4rem;
|
||
font-size:10px;font-weight:800;letter-spacing:.02em;text-transform:uppercase;border:1px solid}
|
||
.chip-emerald{background:rgba(16,185,129,.15);color:#a7f3d0;border-color:rgba(16,185,129,.35)}
|
||
.chip-rose{background:rgba(244,63,94,.16);color:#fecaca;border-color:rgba(244,63,94,.35)}
|
||
.chip-amber{background:rgba(245,158,11,.18);color:#fde68a;border-color:rgba(245,158,11,.40)}
|
||
.chip-sky{background:rgba(56,189,248,.15);color:#bae6fd;border-color:rgba(56,189,248,.35)}
|
||
.chip-slate{background:rgba(148,163,184,.12);color:#cbd5e1;border-color:rgba(148,163,184,.30)}
|
||
|
||
.cham_chip{display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .5rem;border-radius:.4rem;
|
||
font-size:10px;font-weight:800;letter-spacing:.02em;text-transform:uppercase;border:1px solid}
|
||
.cham_chip-emerald{background:rgba(16,185,129,.15);color:#a7f3d0;border-color:rgba(16,185,129,.35)}
|
||
.cham_chip-rose{background:rgba(244,63,94,.16);color:#fecaca;border-color:rgba(244,63,94,.35)}
|
||
.cham_chip-amber{background:rgba(245,158,11,.18);color:#fde68a;border-color:rgba(245,158,11,.40)}
|
||
.cham_chip-sky{background:rgba(56,189,248,.15);color:#bae6fd;border-color:rgba(56,189,248,.35)}
|
||
.cham_chip-slate{background:rgba(148,163,184,.12);color:#cbd5e1;border-color:rgba(148,163,184,.30)}
|
||
|
||
.btn { font-weight:700; font-size:10px; padding: 0.15rem 0.5rem; border-radius:.5rem; border:1px solid rgba(255,255,255,.12); background:rgba(255,255,255,.06); transition:.18s; transition: background-color 0.1s ease-in-out, transform 0.1s ease-in-out;}
|
||
.btn:hover { border-color: rgba(16,185,129,.45); background: rgba(16,185,129,.10); }
|
||
.btn-primary{background-image:linear-gradient(to right,#10b981,#14b8a6,#06b6d4);color:#fff;border-color:transparent}
|
||
.btn-primary:hover{filter:brightness(1.05);transform:translateY(-1px)}
|
||
.btn-danger { border-color: rgba(244,63,94,.35); background: rgba(244,63,94,.14); color: #fecaca; }
|
||
.btn-danger:hover { background: rgba(244,63,94,.22); }
|
||
.btn-ghost { border-color: rgba(255,255,255,.10); background: rgba(255,255,255,.05); }
|
||
.btn-feedback {
|
||
background-color: rgba(34, 197, 94, 0.4); /* A semi-transparent green */
|
||
transform: scale(0.97); /* Makes the button slightly smaller */
|
||
}
|
||
|
||
.field{font-size:10px;color:#9ca3af}
|
||
.value{font-size:12px;font-weight:600;color:#e5e7eb}
|
||
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}
|
||
|
||
.chamber-card.paired{border-color:#34d399!important;box-shadow:0 0 0 2px rgba(52,211,153,.45)}
|
||
.chamber-card.pending{border-color:#60a5fa!important;box-shadow:0 0 0 2px rgba(96,165,250,.45)}
|
||
|
||
.door-pill{color:#fff;font-size:10px;font-weight:700;padding:4px;border-radius:6px; width: 100%; text-align: center;}
|
||
.door-open{background:#22c55e}.door-close{background:#ef4444}
|
||
|
||
.bat-id-big{font-size:14px;font-weight:800;border-radius:.5rem;padding:.2rem .4rem;
|
||
background:rgba(0,0,0,.25);border:1px solid rgba(255,255,255,.10)}
|
||
</style>
|
||
</head>
|
||
<body class="min-h-screen text-gray-100 flex flex-col">
|
||
|
||
<div class="pointer-events-none fixed inset-0">
|
||
<div class="absolute -top-24 -left-24 w-[32rem] h-[32rem] rounded-full bg-emerald-500/10 blur-3xl"></div>
|
||
<div class="absolute -bottom-24 -right-24 w-[36rem] h-[36rem] rounded-full bg-sky-500/10 blur-3xl"></div>
|
||
</div>
|
||
|
||
<header class="relative z-10 border-b border-white/10 bg-black/20 backdrop-blur">
|
||
<div class="mx-auto max-w-7.5xl px-3 sm:px-4 py-2 grid grid-cols-[auto_1fr_auto] items-center gap-2">
|
||
<div class="flex items-center gap-2 sm:gap-3">
|
||
<a href="./station_selection.html"
|
||
class="h-9 w-9 flex items-center justify-center rounded-full bg-white/5 hover:bg-white/10 transition"
|
||
title="Back">
|
||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/>
|
||
</svg>
|
||
</a>
|
||
<div class="flex flex-col leading-tight">
|
||
<div id="station-name" class="text-base sm:text-lg font-extrabold tracking-tight">
|
||
Loading...
|
||
</div>
|
||
<div id="station-location" class="text-xs sm:text-sm text-slate-100">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flex items-center justify-center scale-100">
|
||
<img src="./assets/vec_logo.png" alt="VECMOCON"
|
||
class="h-7 w-auto opacity-90" onerror="this.style.display='none'"/>
|
||
</div>
|
||
|
||
<div class="flex items-center flex-wrap justify-end gap-1.5 sm:gap-2">
|
||
|
||
<span class="badge border-white/10 bg-white/5 text-slate-200">
|
||
<span>Device ID:</span>
|
||
<span id="device-id">—</span>
|
||
</span>
|
||
|
||
<span class="badge border-white/10 bg-white/5 text-slate-200">
|
||
<svg class="w-2.5 h-2.5 opacity-90" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
|
||
<circle cx="12" cy="12" r="9"></circle><path d="M12 7v5l3 3"></path>
|
||
</svg>
|
||
<span id="last-update-status">Waiting...</span>
|
||
</span>
|
||
|
||
<span id="connection-status-chip" class="cham_chip cham_chip-amber" title="Connection to backend">
|
||
<span class="h-2 w-2 rounded-full bg-amber-400"></span> Connecting...
|
||
</span>
|
||
|
||
<button id="station-reset-btn" class="btn btn-danger !p-2" title="Station Reset">
|
||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M18.36 6.64a9 9 0 1 1-12.73 0"/><line x1="12" y1="2" x2="12" y2="12"/>
|
||
</svg>
|
||
</button>
|
||
|
||
<!-- <span id="backup-power-chip" class="cham_chip cham_chip-slate" title="Station power source" style="display: none;">On Backup</span> -->
|
||
|
||
<div class="hidden sm:block w-px h-5 bg-white/10"></div>
|
||
|
||
<button id="refreshBtn" class="btn btn-ghost !p-2">
|
||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||
<path d="M21 12a9 9 0 10-3.5 7.1M21 12h-4m4 0l-2.5-2.5"></path>
|
||
</svg>
|
||
</button>
|
||
|
||
<button id="downloadBtn" class="btn btn-ghost !p-2">
|
||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||
<path d="M12 3v12m0 0l4-4m-4 4l-4-4"></path><path d="M5 21h14"></path>
|
||
</svg>
|
||
</button>
|
||
|
||
<button id="logout-btn" class="btn btn-danger !p-2">
|
||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||
<path d="M16 17l5-5-5-5"></path><path d="M21 12H9"></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="border-t border-white/10 bg-black/10">
|
||
<nav class="mx-auto max-w-7.5xl px-3 sm:px-4 flex">
|
||
<a href="./dashboard.html" class="px-4 py-2 text-sm font-semibold border-b-2 border-emerald-400/70 text-white">Main</a>
|
||
<a href="./logs.html" class="px-4 py-2 text-sm text-gray-400 hover:text-gray-200 hover:border-b-2 hover:border-white/30 font-semibold">Logs</a>
|
||
<a href="./analytics.html" class="px-4 py-2 text-sm text-gray-400 hover:text-gray-200 hover:border-b-2 hover:border-white/30 font-semibold">Analytics</a>
|
||
</nav>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="relative z-10 flex-1 w-full px-3 py-3 overflow-y-auto lg:overflow-hidden">
|
||
<div class="page mx-auto flex flex-col lg:h-full lg:flex-row gap-3">
|
||
<section id="chambersGrid" class="flex-1 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 lg:grid-rows-3 gap-3"></section>
|
||
|
||
<aside class="w-full lg:w-96 lg:shrink-0 flex flex-col gap-3 overflow-y-auto">
|
||
<section class="glass p-4">
|
||
<div class="flex items-center justify-between mb-3">
|
||
<span class="text-xm font-bold mb-2">System Diagnostics Code</span>
|
||
<span id="station-diag-code" class="text-sm font-bold text-emerald-300">—</span>
|
||
</div>
|
||
<div id="diag-flags-grid" class="grid grid-cols-2 gap-x-8 gap-y-1 text-sm">
|
||
<div class="text-rose-300 text-center">Lock Power Cut</div><div class="text-rose-300 text-center">Main Power Cut</div>
|
||
<div class="text-rose-300 text-center">Relayboard CAN</div><div class="text-rose-300 text-center">DB CAN Recv</div>
|
||
<div class="text-rose-300 text-center">MB Can Recv</div><div class="text-rose-300 text-center">Smoke Alarm</div>
|
||
<div class="text-rose-300 text-center">Water Alarm</div><div class="text-rose-300 text-center">Phase Failure</div>
|
||
<div class="text-rose-300 text-center">Earth Leakage</div>
|
||
</div>
|
||
<span id="backup-power-chip" class="cham_chip cham_chip-slate w-full justify-center mt-3">
|
||
</section>
|
||
|
||
<section id="swap-panel" class="glass p-4 flex flex-col min-h-[220px]">
|
||
<h3 class="text-sm font-bold mb-2">Swap Process</h3>
|
||
<div id="swap-pairs-list" class="flex-1 flex flex-wrap gap-2 content-center justify-center">
|
||
<p id="swap-idle-text" class="w-full text-sm text-center text-gray-400">
|
||
Click a <span class="text-sky-300 font-semibold">empty</span> slot, then an <span class="text-emerald-300 font-semibold">full</span> slot.
|
||
</p>
|
||
</div>
|
||
<div class="mt-3">
|
||
<div class="grid grid-cols-2 gap-2">
|
||
<button id="start-swap-btn" class="btn btn-primary !py-2" disabled>Start Swaps</button>
|
||
<button id="abort-swap-btn" class="btn btn-danger !py-2">Abort Swap</button>
|
||
</div>
|
||
<button id="clear-swap-btn" class="btn w-full mt-2 !py-2">Clear Selection</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="glass p-4">
|
||
<h3 class="text-sm font-bold mb-2">Audio Command</h3>
|
||
<div class="flex items-center gap-2">
|
||
<select id="audio-command-select" class="w-full rounded-md bg-white/5 border border-white/10 px-2 py-2 text-sm outline-none focus:ring-2 focus:ring-emerald-500/60">
|
||
<option>English</option>
|
||
<option>Hindi</option>
|
||
<option>Tamil</option>
|
||
</select>
|
||
<button id="send-audio-btn" class="btn btn-primary !py-2 px-4">Send</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="glass p-4 flex-1 flex flex-col min-h-[610px]">
|
||
<h3 class="text-sm font-bold mb-2">Instance Log</h3>
|
||
<textarea id="instance-log" class="flex-1 bg-black/20 border border-white/10 rounded-md p-2 text-xs font-mono resize-none" readonly>[--:--:--] Waiting for data…</textarea>
|
||
</section>
|
||
</aside>
|
||
</div>
|
||
</main>
|
||
|
||
<template id="chamberTemplate">
|
||
<div class="chamber-card relative glass rounded-xl p-2 flex flex-col transition border border-white/20">
|
||
|
||
<div class="text-center absolute left-0 right-0 top-0 -translate-y-1/2">
|
||
<span class="bg-slate-800 px-2 text-xs font-extrabold text-gray-200 tracking-wide rounded">
|
||
CHAMBER <span class="slotNo">#</span>
|
||
</span>
|
||
</div>
|
||
|
||
<div class="filled-state flex-1 flex flex-col">
|
||
<div class="mt-2 mb-1.5">
|
||
<div class="flex items-center gap-2">
|
||
<h4 class="field font-bold text-gray-300 shrink-0">BAT_ID</h4>
|
||
<div class="bat-id-big mono truncate flex-1 text-left" title="—">Waiting...</div>
|
||
</div>
|
||
</div>
|
||
<div class="flex-1 flex gap-2">
|
||
<div class="flex-1 space-y-0.5">
|
||
<div class="flex items-center justify-between border-b border-white/10 pb-0.5 mb-1">
|
||
<h4 class="field font-bold text-gray-300">BATTERY</h4>
|
||
<span class="battery-status-pill chip chip-slate">--</span>
|
||
</div>
|
||
<div class="flex justify-between items-baseline"><span class="field">SOC</span><span class="value soc">—</span></div>
|
||
<div class="flex justify-between items-baseline"><span class="field">Voltage</span><span class="value voltage">—</span></div>
|
||
<div class="flex justify-between items-baseline"><span class="field">Bat Temp</span><span class="value bat-temp">—</span></div>
|
||
<div class="flex justify-between items-baseline"><span class="field">Bat Fault</span><span class="value bat-fault text-rose-300">—</span></div>
|
||
</div>
|
||
<div class="flex-1 space-y-0.5 border-l border-white/10 pl-2">
|
||
<div class="flex items-center justify-between border-b border-white/10 pb-0.5 mb-1">
|
||
<h4 class="field font-bold text-gray-300">CHARGER</h4>
|
||
<span class="charger-status-pill chip chip-slate">--</span>
|
||
</div>
|
||
<div class="flex justify-between items-baseline"><span class="field">Current</span><span class="value current">—</span></div>
|
||
<div class="flex justify-between items-baseline"><span class="field">Slot Temp</span><span class="value slot-temp">—</span></div>
|
||
<div class="flex justify-between items-baseline"><span class="field">Chg Temp</span><span class="value chg-temp">—</span></div>
|
||
<div class="flex justify-between items-baseline"><span class="field">Chg Fault</span><span class="value chg-fault text-rose-300">—</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="empty-state relative flex-1 flex-col items-center justify-center text-slate-500 hidden pt-4">
|
||
<div class="absolute top-1 right-2 flex items-center gap-1 text-xs">
|
||
<i data-lucide="thermometer" class="w-3 h-3 text-slate-400"></i>
|
||
<span class="value slot-temp-empty text-slate-400">—</span>
|
||
</div>
|
||
<i data-lucide="battery" class="w-16 h-16 opacity-50 text-slate-500"></i>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-4 gap-1.5 mt-2 shrink-0">
|
||
<div class="door-pill door-close">--</div>
|
||
<button data-command="OPEN" class="btn">OPEN</button>
|
||
<button data-command="CHG_ON" class="btn">CHG ON</button>
|
||
<button data-command="CHG_OFF" class="btn">CHG OFF</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
</body>
|
||
</html> |