chức năng dashboard
This commit is contained in:
110
www/missions.js
110
www/missions.js
@@ -435,33 +435,70 @@
|
||||
store.queue = Array.isArray(data.queue) ? data.queue : [];
|
||||
store.runner = data.runner && typeof data.runner === "object" ? data.runner : { state: "idle", message: "" };
|
||||
renderQueuePanel();
|
||||
notifyQueueUpdate();
|
||||
} catch (e) {
|
||||
if (missionQueueRunnerEl) missionQueueRunnerEl.textContent = `Không tải được queue: ${e.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderQueuePanel() {
|
||||
if (!missionQueueListEl) return;
|
||||
missionQueueListEl.innerHTML = "";
|
||||
if (missionQueueEmptyEl) missionQueueEmptyEl.hidden = store.queue.length > 0;
|
||||
const queueListeners = new Set();
|
||||
function notifyQueueUpdate() {
|
||||
queueListeners.forEach((fn) => {
|
||||
try {
|
||||
fn(getQueueSnapshot());
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (missionQueueRunnerEl) {
|
||||
function getQueueSnapshot() {
|
||||
return {
|
||||
queue: JSON.parse(JSON.stringify(store.queue)),
|
||||
runner: JSON.parse(JSON.stringify(store.runner)),
|
||||
};
|
||||
}
|
||||
|
||||
function renderQueueInto(target, options = {}) {
|
||||
const listEl = target.listEl;
|
||||
const runnerEl = target.runnerEl;
|
||||
const emptyEl = target.emptyEl;
|
||||
const compact = !!options.compact;
|
||||
if (!listEl) return;
|
||||
|
||||
listEl.innerHTML = "";
|
||||
if (emptyEl) emptyEl.hidden = store.queue.length > 0;
|
||||
|
||||
if (runnerEl) {
|
||||
const st = store.runner.state || "idle";
|
||||
missionQueueRunnerEl.classList.toggle("running", st === "running");
|
||||
runnerEl.classList.toggle("running", st === "running" || st === "paused");
|
||||
runnerEl.classList.toggle("paused", st === "paused");
|
||||
const action = store.runner.current_action ? ` • ${store.runner.current_action}` : "";
|
||||
missionQueueRunnerEl.textContent = store.runner.message
|
||||
runnerEl.textContent = store.runner.message
|
||||
? `${store.runner.message}${action}`
|
||||
: st === "idle"
|
||||
? "Robot sẵn sàng — queue trống hoặc chờ mission mới."
|
||||
? compact
|
||||
? "Sẵn sàng"
|
||||
: "Robot sẵn sàng — queue trống hoặc chờ mission mới."
|
||||
: "—";
|
||||
}
|
||||
|
||||
store.queue.forEach((entry, index) => {
|
||||
const row = document.createElement("div");
|
||||
row.className = `missionQueueItem status-${entry.status || "pending"}`;
|
||||
row.className = `missionQueueItem status-${entry.status || "pending"}${compact ? " compact" : ""}`;
|
||||
const paramHtml = formatQueueParameters(entry);
|
||||
const canReorder = entry.status === "pending";
|
||||
row.innerHTML = `
|
||||
const canReorder = entry.status === "pending" && !compact;
|
||||
row.innerHTML = compact
|
||||
? `
|
||||
<div>
|
||||
<div class="missionQueueItemTitle">${escapeHtml(entry.mission_name || "Mission")}</div>
|
||||
<div class="missionQueueItemMeta">${queueStatusLabel(entry.status)} • #${index + 1}</div>
|
||||
${paramHtml ? `<div class="missionQueueItemParams">${paramHtml}</div>` : ""}
|
||||
</div>
|
||||
<div class="missionQueueWidgetActions">
|
||||
${entry.status === "pending" ? `<button type="button" class="iconBtn danger" data-queue-remove="${entry.id}" title="Xóa">×</button>` : `<span class="missionQueueStatus ${escapeHtml(entry.status || "pending")}">${queueStatusLabel(entry.status)}</span>`}
|
||||
</div>`
|
||||
: `
|
||||
<div class="missionQueueOrder">
|
||||
<button type="button" class="iconBtn" data-queue-up="${entry.id}" title="Lên" ${canReorder && index > 0 ? "" : "disabled"}>↑</button>
|
||||
<button type="button" class="iconBtn" data-queue-down="${entry.id}" title="Xuống" ${canReorder && index < store.queue.length - 1 ? "" : "disabled"}>↓</button>
|
||||
@@ -479,10 +516,21 @@
|
||||
row.querySelector("[data-queue-up]")?.addEventListener("click", () => moveQueueItem(entry.id, -1));
|
||||
row.querySelector("[data-queue-down]")?.addEventListener("click", () => moveQueueItem(entry.id, 1));
|
||||
row.querySelector("[data-queue-remove]")?.addEventListener("click", () => removeQueueItem(entry.id));
|
||||
missionQueueListEl.appendChild(row);
|
||||
listEl.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
function renderQueuePanel() {
|
||||
renderQueueInto(
|
||||
{
|
||||
listEl: missionQueueListEl,
|
||||
runnerEl: missionQueueRunnerEl,
|
||||
emptyEl: missionQueueEmptyEl,
|
||||
},
|
||||
{ compact: false }
|
||||
);
|
||||
}
|
||||
|
||||
async function moveQueueItem(id, delta) {
|
||||
const ids = store.queue.map((q) => q.id);
|
||||
const idx = ids.indexOf(id);
|
||||
@@ -522,6 +570,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function enqueueMission(missionId, parameters = {}) {
|
||||
const mission = findMission(missionId);
|
||||
if (!mission) throw new Error("Mission không tồn tại");
|
||||
const payload = {
|
||||
mission: resolveMissionSnapshot(mission),
|
||||
parameters,
|
||||
};
|
||||
await missionApi("/api/mission_queue", { method: "POST", body: JSON.stringify(payload) });
|
||||
await refreshQueue();
|
||||
}
|
||||
|
||||
async function pauseRunner() {
|
||||
await missionApi("/api/mission_queue/pause", { method: "POST", body: "{}" });
|
||||
await refreshQueue();
|
||||
}
|
||||
|
||||
async function continueRunner() {
|
||||
await missionApi("/api/mission_queue/continue", { method: "POST", body: "{}" });
|
||||
await refreshQueue();
|
||||
}
|
||||
|
||||
function openQueueDialog(missionId) {
|
||||
const mission = findMission(missionId);
|
||||
if (!mission) return;
|
||||
@@ -1192,6 +1261,23 @@
|
||||
|
||||
window.MissionsApp = {
|
||||
init,
|
||||
getMissions: () => [...store.missions],
|
||||
getGroups: () => allGroups(),
|
||||
getMissionById: findMission,
|
||||
queueMission: openQueueDialog,
|
||||
enqueueMission,
|
||||
pauseRunner,
|
||||
continueRunner,
|
||||
refreshQueue,
|
||||
clearQueue,
|
||||
getQueueSnapshot,
|
||||
renderQueueInto,
|
||||
onQueueUpdate(fn) {
|
||||
queueListeners.add(fn);
|
||||
return () => queueListeners.delete(fn);
|
||||
},
|
||||
startQueuePoll,
|
||||
stopQueuePoll,
|
||||
onPageShow() {
|
||||
if (!missionEditorViewEl?.hidden) renderMissionEditor();
|
||||
else {
|
||||
|
||||
Reference in New Issue
Block a user