This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
const SAMPLE_CARTS = ["Any valid cart", "Cart A", "Cart B"];
|
||||
|
||||
const el = (id) => document.getElementById(id);
|
||||
const t = (key, vars) => window.I18n?.t(key, vars) ?? key;
|
||||
|
||||
const missionListEl = el("missionList");
|
||||
const missionListEmptyEl = el("missionListEmpty");
|
||||
@@ -136,9 +137,9 @@
|
||||
function actionMeta(type) {
|
||||
for (const items of Object.values(ACTION_GROUPS)) {
|
||||
const hit = items.find((a) => a.type === type);
|
||||
if (hit) return hit;
|
||||
if (hit) return { ...hit, label: t(`missions.action.${type}`) || hit.label };
|
||||
}
|
||||
return { type, label: type };
|
||||
return { type, label: t(`missions.action.${type}`) || type };
|
||||
}
|
||||
|
||||
function createAction(type, overrides = {}) {
|
||||
@@ -336,7 +337,7 @@
|
||||
if (action.id === actionId) return { action, list, index: i, path, parent };
|
||||
if (Array.isArray(action.children)) {
|
||||
const hit = findActionWithParent(actionId, action.children, `${path}.${action.id}`, action);
|
||||
if (hit) return hit;
|
||||
if (hit) return { ...hit, label: t(`missions.action.${type}`) || hit.label };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -476,13 +477,14 @@
|
||||
|
||||
function queueStatusLabel(status) {
|
||||
const map = {
|
||||
pending: "Chờ",
|
||||
executing: "Đang chạy",
|
||||
completed: "Xong",
|
||||
failed: "Lỗi",
|
||||
cancelled: "Đã hủy",
|
||||
pending: "missions.queue.status.pending",
|
||||
executing: "missions.queue.status.executing",
|
||||
completed: "missions.queue.status.done",
|
||||
failed: "missions.queue.status.error",
|
||||
cancelled: "missions.queue.status.cancelled",
|
||||
};
|
||||
return map[status] || status;
|
||||
const key = map[status];
|
||||
return key ? t(key) : status;
|
||||
}
|
||||
|
||||
async function refreshQueue() {
|
||||
@@ -495,7 +497,7 @@
|
||||
notifyQueueUpdate();
|
||||
} catch (e) {
|
||||
if (String(e.message || "").includes("not authenticated")) return;
|
||||
if (missionQueueRunnerEl) missionQueueRunnerEl.textContent = `Không tải được queue: ${e.message}`;
|
||||
if (missionQueueRunnerEl) missionQueueRunnerEl.textContent = `${t("common.error", { msg: e.message })}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,8 +538,8 @@
|
||||
? `${store.runner.message}${action}`
|
||||
: st === "idle"
|
||||
? compact
|
||||
? "Sẵn sàng"
|
||||
: "Robot sẵn sàng — queue trống hoặc chờ mission mới."
|
||||
? t("missions.queue.ready")
|
||||
: t("missions.queue.idleMessage")
|
||||
: "—";
|
||||
}
|
||||
|
||||
@@ -554,12 +556,12 @@
|
||||
${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>`}
|
||||
${entry.status === "pending" ? `<button type="button" class="iconBtn danger" data-queue-remove="${entry.id}" title="" data-i18n-title="common.delete">×</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>
|
||||
<button type="button" class="iconBtn" data-queue-up="${entry.id}" title="${t("missions.queue.moveUp")}" ${canReorder && index > 0 ? "" : "disabled"}>↑</button>
|
||||
<button type="button" class="iconBtn" data-queue-down="${entry.id}" title="${t("missions.queue.moveDown")}" ${canReorder && index < store.queue.length - 1 ? "" : "disabled"}>↓</button>
|
||||
</div>
|
||||
<div>
|
||||
<div class="missionQueueItemTitle">${escapeHtml(entry.mission_name || "Mission")}</div>
|
||||
@@ -568,7 +570,7 @@
|
||||
</div>
|
||||
<div style="display:flex;flex-direction:column;align-items:flex-end;gap:6px;">
|
||||
<span class="missionQueueStatus ${escapeHtml(entry.status || "pending")}">${queueStatusLabel(entry.status)}</span>
|
||||
${entry.status === "pending" ? `<button type="button" class="btn subtle danger" data-queue-remove="${entry.id}">Xóa</button>` : ""}
|
||||
${entry.status === "pending" ? `<button type="button" class="btn subtle danger" data-queue-remove="${entry.id}">${t("common.delete")}</button>` : ""}
|
||||
</div>`;
|
||||
|
||||
row.querySelector("[data-queue-up]")?.addEventListener("click", () => moveQueueItem(entry.id, -1));
|
||||
@@ -619,7 +621,7 @@
|
||||
}
|
||||
|
||||
async function clearQueue() {
|
||||
if (!confirm("Xóa các mission đang chờ trong queue?")) return;
|
||||
if (!confirm(t("missions.queue.clearConfirm"))) return;
|
||||
try {
|
||||
await missionApi("/api/mission_queue", { method: "DELETE" });
|
||||
await refreshQueue();
|
||||
@@ -650,7 +652,7 @@
|
||||
}
|
||||
|
||||
async function cancelRunner() {
|
||||
if (!confirm("Hủy mission đang chạy? (thoát loop và dừng ngay)")) return;
|
||||
if (!confirm(t("missions.queue.cancelConfirm"))) return;
|
||||
await missionApi("/api/mission_queue/cancel", { method: "POST", body: "{}" });
|
||||
await refreshQueue();
|
||||
}
|
||||
@@ -756,8 +758,8 @@
|
||||
</div>
|
||||
<div class="missionListItemActions">
|
||||
<button type="button" class="iconBtn missionQueueBtn" data-queue="${mission.id}" title="Thêm vào mission queue" aria-label="Thêm vào queue">▤</button>
|
||||
<button type="button" class="btn subtle" data-edit="${mission.id}">Sửa</button>
|
||||
<button type="button" class="btn subtle danger" data-delete="${mission.id}">Xóa</button>
|
||||
<button type="button" class="btn subtle" data-edit="${mission.id}">${t("common.edit")}</button>
|
||||
<button type="button" class="btn subtle danger" data-delete="${mission.id}">${t("common.delete")}</button>
|
||||
</div>`;
|
||||
row.addEventListener("click", (evt) => {
|
||||
if (evt.target.closest("button")) return;
|
||||
@@ -773,7 +775,7 @@
|
||||
});
|
||||
row.querySelector("[data-delete]").addEventListener("click", (evt) => {
|
||||
evt.stopPropagation();
|
||||
if (!confirm(`Xóa mission «${mission.name}»?`)) return;
|
||||
if (!confirm(t("missions.deleteConfirm", { name: mission.name }))) return;
|
||||
store.missions = store.missions.filter((m) => m.id !== mission.id);
|
||||
persistStore();
|
||||
renderMissionList();
|
||||
@@ -782,6 +784,12 @@
|
||||
});
|
||||
}
|
||||
|
||||
function groupLabel(name) {
|
||||
const key = `missions.group.${name}`;
|
||||
const v = t(key);
|
||||
return v !== key ? v : name;
|
||||
}
|
||||
|
||||
function renderActionPalette() {
|
||||
if (!missionGroupTabsEl) return;
|
||||
missionGroupTabsEl.innerHTML = "";
|
||||
@@ -889,7 +897,7 @@
|
||||
</div>
|
||||
<div class="missionActionBtns">
|
||||
<button type="button" class="iconBtn" data-config="${action.id}" title="Cấu hình">⚙</button>
|
||||
<button type="button" class="iconBtn danger" data-remove="${action.id}" title="Xóa">×</button>
|
||||
<button type="button" class="iconBtn danger" data-remove="${action.id}" title="" data-i18n-title="common.delete">×</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
@@ -1044,7 +1052,7 @@
|
||||
}
|
||||
|
||||
function closeEditor() {
|
||||
if (store.dirty && !confirm("Bỏ thay đổi chưa lưu?")) return;
|
||||
if (store.dirty && !confirm(t("missions.editor.discardConfirm"))) return;
|
||||
store.editingId = null;
|
||||
store.draft = null;
|
||||
setDirty(false);
|
||||
@@ -1057,7 +1065,7 @@
|
||||
const draft = getDraft();
|
||||
if (!draft) return false;
|
||||
if (!draft.name.trim()) {
|
||||
alert("Tên mission không được trống.");
|
||||
alert(t("missions.error.nameRequired"));
|
||||
return false;
|
||||
}
|
||||
draft.updated_at = new Date().toISOString();
|
||||
@@ -1076,7 +1084,7 @@
|
||||
const name = newName.trim();
|
||||
if (!name) return false;
|
||||
if (store.missions.some((m) => m.name === name && m.id !== draft.id)) {
|
||||
alert("Tên mission đã tồn tại.");
|
||||
alert(t("missions.error.nameDuplicate"));
|
||||
return false;
|
||||
}
|
||||
const copy = JSON.parse(JSON.stringify(draft));
|
||||
@@ -1213,7 +1221,7 @@
|
||||
addField("Timeout (s)", textInput("timeout_s", p.timeout_s, "number"));
|
||||
{
|
||||
const chk = document.createElement("label");
|
||||
chk.innerHTML = `<input type="checkbox" data-param="expected" ${p.expected ? "checked" : ""} /> Chờ mức ON`;
|
||||
chk.innerHTML = `<input type="checkbox" data-param="expected" ${p.expected ? "checked" : ""} /> ${t("missions.action.waitOnLevel")}`;
|
||||
addField("Kỳ vọng", chk);
|
||||
}
|
||||
break;
|
||||
@@ -1286,7 +1294,7 @@
|
||||
if (!store.groups.includes(group)) store.groups.push(group);
|
||||
}
|
||||
if (store.missions.some((m) => m.name === name)) {
|
||||
alert("Tên mission đã tồn tại.");
|
||||
alert(t("missions.error.nameDuplicate"));
|
||||
return;
|
||||
}
|
||||
const mission = createMission(name, group, el("missionCreateDesc").value);
|
||||
@@ -1299,7 +1307,7 @@
|
||||
|
||||
el("missionEditorBackBtn")?.addEventListener("click", closeEditor);
|
||||
el("missionSaveBtn")?.addEventListener("click", () => {
|
||||
if (saveDraft()) alert("Đã lưu mission.");
|
||||
if (saveDraft()) alert(t("missions.saveSuccess"));
|
||||
});
|
||||
el("missionSaveAsBtn")?.addEventListener("click", openSaveAsDialog);
|
||||
el("missionSettingsBtn")?.addEventListener("click", openSettingsDialog);
|
||||
@@ -1312,7 +1320,7 @@
|
||||
draft.group = el("missionSettingsGroup").value;
|
||||
draft.description = el("missionSettingsDesc").value.trim();
|
||||
if (!draft.name) {
|
||||
alert("Tên không được trống.");
|
||||
alert(t("missions.error.nameEmpty"));
|
||||
return;
|
||||
}
|
||||
setDirty(true);
|
||||
@@ -1393,6 +1401,16 @@
|
||||
function boot() {
|
||||
init();
|
||||
}
|
||||
function onLocaleChange() {
|
||||
if (!missionEditorViewEl?.hidden) renderMissionEditor();
|
||||
else {
|
||||
renderMissionList();
|
||||
renderQueuePanel();
|
||||
}
|
||||
renderActionPalette();
|
||||
}
|
||||
window.addEventListener("lm:locale-change", onLocaleChange);
|
||||
|
||||
if (window.AuthApp?.isReady()) boot();
|
||||
else window.addEventListener("lm:auth-ready", boot, { once: true });
|
||||
window.addEventListener("lm:auth-logout", stopQueuePollForce);
|
||||
|
||||
Reference in New Issue
Block a user