excuting misstion from queue
This commit is contained in:
322
www/dashboard.js
322
www/dashboard.js
@@ -1,322 +0,0 @@
|
||||
(() => {
|
||||
const el = (id) => document.getElementById(id);
|
||||
const canvasEl = el("dashboardCanvas");
|
||||
const saveBtn = el("dashboardSaveBtn");
|
||||
const modeSelectEl = el("dashboardModeSelect");
|
||||
|
||||
const store = {
|
||||
widgets: [],
|
||||
pollTimer: null,
|
||||
};
|
||||
|
||||
async function api(path, opts = {}) {
|
||||
if (window.MissionsApp?.missionsApi) return window.MissionsApp.missionsApi(path, opts);
|
||||
const res = await fetch(path, { headers: { "Content-Type": "application/json" }, ...opts });
|
||||
if (res.status === 204) return null;
|
||||
const data = res.ok ? await res.json() : null;
|
||||
if (!res.ok) throw new Error(data?.error || `HTTP ${res.status}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
function newWidgetId() {
|
||||
return `w_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
||||
}
|
||||
|
||||
async function loadDashboard() {
|
||||
const data = await api("/api/dashboard");
|
||||
store.widgets = Array.isArray(data.widgets) ? data.widgets : [];
|
||||
if (!store.widgets.length) {
|
||||
store.widgets = [
|
||||
{ id: newWidgetId(), type: "mission_queue" },
|
||||
{ id: newWidgetId(), type: "pause_continue" },
|
||||
{ id: newWidgetId(), type: "action_log" },
|
||||
];
|
||||
}
|
||||
renderCanvas();
|
||||
}
|
||||
|
||||
async function saveDashboard() {
|
||||
await api("/api/dashboard", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ widgets: store.widgets }),
|
||||
});
|
||||
}
|
||||
|
||||
function missions() {
|
||||
return window.MissionsApp?.getMissions?.() || [];
|
||||
}
|
||||
|
||||
function groups() {
|
||||
const g = new Set();
|
||||
missions().forEach((m) => g.add(m.group || "Missions"));
|
||||
return [...g].sort();
|
||||
}
|
||||
|
||||
function renderCanvas() {
|
||||
if (!canvasEl) return;
|
||||
canvasEl.innerHTML = "";
|
||||
if (!store.widgets.length) {
|
||||
const empty = document.createElement("p");
|
||||
empty.className = "mutedNote";
|
||||
empty.textContent = "Thêm widget từ panel trái.";
|
||||
canvasEl.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
store.widgets.forEach((widget) => {
|
||||
canvasEl.appendChild(buildWidget(widget));
|
||||
});
|
||||
}
|
||||
|
||||
function buildWidget(widget) {
|
||||
const box = document.createElement("div");
|
||||
box.className = "dashboardWidget";
|
||||
box.dataset.widgetId = widget.id;
|
||||
|
||||
const head = document.createElement("div");
|
||||
head.className = "dashboardWidgetHead";
|
||||
head.innerHTML = `<span class="dashboardWidgetTitle">${widget.type.replace(/_/g, " ")}</span>`;
|
||||
const removeBtn = document.createElement("button");
|
||||
removeBtn.type = "button";
|
||||
removeBtn.className = "dashboardWidgetRemove";
|
||||
removeBtn.textContent = "×";
|
||||
removeBtn.addEventListener("click", () => {
|
||||
store.widgets = store.widgets.filter((w) => w.id !== widget.id);
|
||||
renderCanvas();
|
||||
});
|
||||
head.appendChild(removeBtn);
|
||||
box.appendChild(head);
|
||||
|
||||
const body = document.createElement("div");
|
||||
body.className = "dashboardWidgetBody";
|
||||
|
||||
switch (widget.type) {
|
||||
case "mission_button":
|
||||
renderMissionButton(body, widget);
|
||||
break;
|
||||
case "mission_group":
|
||||
renderMissionGroup(body, widget);
|
||||
break;
|
||||
case "mission_queue":
|
||||
renderMissionQueue(body);
|
||||
break;
|
||||
case "pause_continue":
|
||||
renderPauseContinue(body);
|
||||
break;
|
||||
case "action_log":
|
||||
renderActionLog(body);
|
||||
break;
|
||||
default:
|
||||
body.textContent = "Unknown widget";
|
||||
}
|
||||
box.appendChild(body);
|
||||
return box;
|
||||
}
|
||||
|
||||
function renderMissionButton(container, widget) {
|
||||
const select = document.createElement("select");
|
||||
missions().forEach((m) => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = m.id;
|
||||
opt.textContent = m.name;
|
||||
if (m.id === widget.mission_id) opt.selected = true;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
select.addEventListener("change", () => {
|
||||
widget.mission_id = select.value;
|
||||
});
|
||||
container.appendChild(select);
|
||||
|
||||
const btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.className = "dashboardMissionBtn";
|
||||
btn.textContent = "Start mission";
|
||||
btn.addEventListener("click", async () => {
|
||||
try {
|
||||
if (window.MissionsApp?.queueMission) {
|
||||
await window.MissionsApp.queueMission(select.value, {});
|
||||
} else {
|
||||
await api("/api/mission_queue", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ mission_id: select.value, parameters: {} }),
|
||||
});
|
||||
}
|
||||
await refreshWidgetsDynamic();
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
});
|
||||
container.appendChild(btn);
|
||||
}
|
||||
|
||||
function renderMissionGroup(container, widget) {
|
||||
const select = document.createElement("select");
|
||||
groups().forEach((g) => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = g;
|
||||
opt.textContent = g;
|
||||
if (g === (widget.group || "Missions")) opt.selected = true;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
select.addEventListener("change", () => {
|
||||
widget.group = select.value;
|
||||
renderCanvas();
|
||||
});
|
||||
container.appendChild(select);
|
||||
|
||||
const list = document.createElement("div");
|
||||
list.className = "dashboardMissionGroup";
|
||||
missions()
|
||||
.filter((m) => (m.group || "Missions") === (widget.group || select.value))
|
||||
.forEach((m) => {
|
||||
const btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.className = "btn subtle btnBlock";
|
||||
btn.textContent = m.name;
|
||||
btn.addEventListener("click", async () => {
|
||||
try {
|
||||
await window.MissionsApp.queueMission(m.id, {});
|
||||
await refreshWidgetsDynamic();
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
});
|
||||
list.appendChild(btn);
|
||||
});
|
||||
container.appendChild(list);
|
||||
}
|
||||
|
||||
function renderMissionQueue(container) {
|
||||
const wrap = document.createElement("div");
|
||||
wrap.className = "dashboardQueueMini";
|
||||
wrap.dataset.dynamic = "queue";
|
||||
container.appendChild(wrap);
|
||||
updateQueueMini(wrap);
|
||||
}
|
||||
|
||||
function renderPauseContinue(container) {
|
||||
const play = document.createElement("button");
|
||||
play.type = "button";
|
||||
play.className = "btn primary dashboardPauseBtn";
|
||||
play.textContent = "▶ Continue";
|
||||
play.addEventListener("click", async () => {
|
||||
await api("/api/mission_runner/state", { method: "PUT", body: JSON.stringify({ state_id: 3 }) });
|
||||
await refreshWidgetsDynamic();
|
||||
});
|
||||
const pause = document.createElement("button");
|
||||
pause.type = "button";
|
||||
pause.className = "btn subtle dashboardPauseBtn";
|
||||
pause.textContent = "⏸ Pause";
|
||||
pause.addEventListener("click", async () => {
|
||||
await api("/api/mission_runner/state", { method: "PUT", body: JSON.stringify({ state_id: 4 }) });
|
||||
await refreshWidgetsDynamic();
|
||||
});
|
||||
container.appendChild(play);
|
||||
container.appendChild(pause);
|
||||
}
|
||||
|
||||
function renderActionLog(container) {
|
||||
const wrap = document.createElement("div");
|
||||
wrap.className = "dashboardQueueMini";
|
||||
wrap.dataset.dynamic = "log";
|
||||
container.appendChild(wrap);
|
||||
updateActionLogMini(wrap);
|
||||
}
|
||||
|
||||
function updateQueueMini(node) {
|
||||
const status = window.MissionsApp?.getRunnerStatus?.();
|
||||
const queue = status?.queue || [];
|
||||
node.innerHTML = queue.length
|
||||
? queue
|
||||
.map(
|
||||
(q) =>
|
||||
`<div><strong>${q.mission_name || q.mission_id}</strong> <span class="mutedNote">${q.state}</span></div>`
|
||||
)
|
||||
.join("")
|
||||
: `<span class="mutedNote">Queue trống</span>`;
|
||||
}
|
||||
|
||||
function updateActionLogMini(node) {
|
||||
const status = window.MissionsApp?.getRunnerStatus?.();
|
||||
const log = status?.action_log || [];
|
||||
node.innerHTML = [...log]
|
||||
.reverse()
|
||||
.slice(0, 12)
|
||||
.map((row) => `<div>${row.message || ""}</div>`)
|
||||
.join("") || `<span class="mutedNote">—</span>`;
|
||||
}
|
||||
|
||||
async function refreshWidgetsDynamic() {
|
||||
if (window.MissionsApp?.refreshRunnerStatus) await window.MissionsApp.refreshRunnerStatus();
|
||||
canvasEl?.querySelectorAll("[data-dynamic=queue]").forEach(updateQueueMini);
|
||||
canvasEl?.querySelectorAll("[data-dynamic=log]").forEach(updateActionLogMini);
|
||||
}
|
||||
|
||||
function addWidget(type) {
|
||||
const widget = { id: newWidgetId(), type };
|
||||
if (type === "mission_button") {
|
||||
widget.mission_id = missions()[0]?.id || "";
|
||||
}
|
||||
if (type === "mission_group") {
|
||||
widget.group = groups()[0] || "Missions";
|
||||
}
|
||||
store.widgets.push(widget);
|
||||
renderCanvas();
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
document.querySelectorAll(".dashboardAddWidget").forEach((btn) => {
|
||||
btn.addEventListener("click", () => addWidget(btn.dataset.widget));
|
||||
});
|
||||
saveBtn?.addEventListener("click", async () => {
|
||||
try {
|
||||
await saveDashboard();
|
||||
alert("Đã lưu dashboard.");
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
});
|
||||
modeSelectEl?.addEventListener("change", async () => {
|
||||
try {
|
||||
await api("/api/mission_runner/mode", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ mode: modeSelectEl.value }),
|
||||
});
|
||||
await refreshWidgetsDynamic();
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
if (store.pollTimer) clearInterval(store.pollTimer);
|
||||
store.pollTimer = setInterval(() => {
|
||||
const page = el("pageDashboard");
|
||||
if (page && !page.hidden) refreshWidgetsDynamic();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
async function init() {
|
||||
bindEvents();
|
||||
try {
|
||||
await loadDashboard();
|
||||
} catch {
|
||||
store.widgets = [
|
||||
{ id: newWidgetId(), type: "mission_queue" },
|
||||
{ id: newWidgetId(), type: "pause_continue" },
|
||||
];
|
||||
renderCanvas();
|
||||
}
|
||||
startPolling();
|
||||
}
|
||||
|
||||
window.DashboardApp = {
|
||||
init,
|
||||
onPageShow() {
|
||||
loadDashboard().catch(() => renderCanvas());
|
||||
refreshWidgetsDynamic();
|
||||
},
|
||||
};
|
||||
|
||||
init();
|
||||
})();
|
||||
Reference in New Issue
Block a user