Complete QF100 ops commands and detail UI

This commit is contained in:
Wira Basalamah
2026-06-07 02:55:57 +07:00
parent 1550484d1d
commit e3d7e60ff3
8 changed files with 608 additions and 63 deletions

View File

@ -181,6 +181,26 @@
<span class="text-sm font-semibold text-slate-600">Export worker</span>
<span id="export-worker" class="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-bold text-slate-600">-</span>
</div>
<div class="rounded-lg border border-slate-200 bg-white p-4">
<div class="mb-3 flex items-center justify-between gap-3">
<div>
<p class="text-sm font-extrabold text-slate-950">Remote Actions</p>
<p class="mt-0.5 text-xs text-slate-500">Send operational command to a selected soundbox</p>
</div>
<span class="material-symbols-outlined text-blue-700">settings_remote</span>
</div>
<label class="block">
<span class="mb-1 block text-xs font-bold uppercase text-slate-500">Device</span>
<select id="command-device-select" class="w-full rounded-lg border-slate-200 bg-white py-2 text-sm focus:border-blue-600 focus:ring-blue-600">
<option value="">Select device</option>
</select>
</label>
<button id="send-reboot-command" class="mt-3 inline-flex w-full items-center justify-center gap-2 rounded-lg border border-slate-200 bg-white px-4 py-2.5 text-sm font-extrabold text-slate-800 hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-50" disabled>
<span class="material-symbols-outlined text-[20px]">restart_alt</span>
Reboot Device
</button>
<p id="command-status" class="mt-2 min-h-5 text-xs font-semibold text-slate-500"></p>
</div>
</div>
</section>
@ -346,6 +366,27 @@
$("export-worker").className = `rounded-full px-2.5 py-1 text-xs font-bold ${worker?.enabled === false ? "bg-amber-50 text-amber-700" : "bg-emerald-50 text-emerald-700"}`;
}
function renderCommandDevices() {
const select = $("command-device-select");
const rebootButton = $("send-reboot-command");
if (!select || !rebootButton) {
return;
}
const current = select.value;
select.innerHTML = '<option value="">Select device</option>';
state.devices.forEach((device) => {
const option = document.createElement("option");
option.value = device.id;
option.textContent = `${device.device_code || device.serial_number || device.id} · ${device.serial_number || device.model || "soundbox"}`;
select.appendChild(option);
});
if (current && state.devices.some((device) => device.id === current)) {
select.value = current;
}
rebootButton.disabled = !select.value;
}
function renderMqtt() {
const list = $("mqtt-list");
const messages = Array.isArray(state.mqtt?.last_messages) ? state.mqtt.last_messages : [];
@ -373,6 +414,7 @@
renderKpis();
renderTable();
renderOps();
renderCommandDevices();
renderMqtt();
}
@ -517,9 +559,46 @@
}
}
async function sendRebootCommand() {
const select = $("command-device-select");
const button = $("send-reboot-command");
const status = $("command-status");
const deviceId = select?.value || "";
const device = state.devices.find((item) => item.id === deviceId);
if (!deviceId || !button || !status) {
return;
}
button.disabled = true;
button.classList.add("opacity-60");
status.textContent = `Sending reboot to ${device?.device_code || device?.serial_number || deviceId}...`;
status.className = "mt-2 min-h-5 text-xs font-semibold text-slate-500";
try {
const result = await api.createDeviceCommand(deviceId, {
command: "device.reboot",
payload: { requested_from: "soundbox_ops" }
});
const topic = result?.result_payload?.topic || "-";
status.textContent = `Reboot command ${result.status || "queued"} · ${topic}`;
status.className = "mt-2 min-h-5 text-xs font-semibold text-emerald-700";
await refresh();
} catch (error) {
status.textContent = error?.message || "Unable to send reboot command.";
status.className = "mt-2 min-h-5 text-xs font-semibold text-red-700";
} finally {
button.classList.remove("opacity-60");
button.disabled = !select.value;
}
}
$("refresh-button").addEventListener("click", refresh);
$("search-input").addEventListener("input", renderTable);
$("status-filter").addEventListener("change", renderTable);
$("command-device-select")?.addEventListener("change", () => {
$("send-reboot-command").disabled = !$("command-device-select").value;
$("command-status").textContent = "";
});
$("send-reboot-command")?.addEventListener("click", sendRebootCommand);
$("logout-button").addEventListener("click", () => {
api.clearToken();
window.location.href = "/ui/admin-login";