This commit is contained in:
81
www/app.js
81
www/app.js
@@ -1,13 +1,16 @@
|
||||
const el = (id) => document.getElementById(id);
|
||||
|
||||
const t = (key, vars) => window.I18n?.t(key, vars) ?? key;
|
||||
|
||||
const statusEl = el("status");
|
||||
const listEl = el("lidarList");
|
||||
const lidarFormHintEl = el("lidarFormHint");
|
||||
const navItemEls = Array.from(document.querySelectorAll(".navItem[data-page]"));
|
||||
const pageOverviewEl = el("pageOverview");
|
||||
const pageConfigEl = el("pageConfig");
|
||||
const pageMissionsEl = el("pageMissions");
|
||||
const pageIntegrationsEl = el("pageIntegrations");
|
||||
const pageMonitoringEl = el("pageMonitoring");
|
||||
const pageHelpEl = el("pageHelp");
|
||||
const contentEl = document.querySelector(".content");
|
||||
const contentRightEl = el("contentRight");
|
||||
const overviewBackendEl = el("overviewBackend");
|
||||
@@ -120,23 +123,19 @@ const state = {
|
||||
};
|
||||
|
||||
function setActivePage(page) {
|
||||
const valid = ["dashboard", "config", "missions", "integrations"];
|
||||
const valid = ["dashboard", "config", "missions", "integrations", "monitoring", "help"];
|
||||
let p = valid.includes(page) ? page : "config";
|
||||
if (window.AuthApp && !window.AuthApp.canAccessPage(p)) {
|
||||
const fallback = valid.find((v) => window.AuthApp.canAccessPage(v));
|
||||
p = fallback || "dashboard";
|
||||
}
|
||||
if (page === "overview") p = "dashboard";
|
||||
navItemEls.forEach((a) => {
|
||||
const on = (a.dataset.page || "") === p;
|
||||
a.classList.toggle("active", on);
|
||||
if (on) a.setAttribute("aria-current", "page");
|
||||
else a.removeAttribute("aria-current");
|
||||
});
|
||||
if (pageOverviewEl) pageOverviewEl.hidden = p !== "dashboard";
|
||||
if (pageConfigEl) pageConfigEl.hidden = p !== "config";
|
||||
if (pageMissionsEl) pageMissionsEl.hidden = p !== "missions";
|
||||
if (pageIntegrationsEl) pageIntegrationsEl.hidden = p !== "integrations";
|
||||
if (pageMonitoringEl) pageMonitoringEl.hidden = p !== "monitoring";
|
||||
if (pageHelpEl) pageHelpEl.hidden = p !== "help";
|
||||
if (configSplitterEl) configSplitterEl.hidden = p !== "config";
|
||||
if (contentRightEl) contentRightEl.hidden = p !== "config";
|
||||
if (contentEl) {
|
||||
@@ -144,6 +143,8 @@ function setActivePage(page) {
|
||||
contentEl.classList.toggle("content--config", p === "config");
|
||||
contentEl.classList.toggle("content--missions", p === "missions");
|
||||
contentEl.classList.toggle("content--integrations", p === "integrations");
|
||||
contentEl.classList.toggle("content--monitoring", p === "monitoring");
|
||||
contentEl.classList.toggle("content--help", p === "help");
|
||||
}
|
||||
if (p === "missions" && window.MissionsApp) window.MissionsApp.onPageShow();
|
||||
else if (window.MissionsApp?.onPageHide) window.MissionsApp.onPageHide();
|
||||
@@ -151,6 +152,7 @@ function setActivePage(page) {
|
||||
else if (window.DashboardApp?.onPageHide) window.DashboardApp.onPageHide();
|
||||
if (p === "integrations" && window.IntegrationsApp) window.IntegrationsApp.onPageShow();
|
||||
else if (window.IntegrationsApp?.onPageHide) window.IntegrationsApp.onPageHide();
|
||||
window.NavApp?.syncFromPage?.(p);
|
||||
try {
|
||||
localStorage.setItem("activePage", p);
|
||||
} catch {
|
||||
@@ -159,25 +161,12 @@ function setActivePage(page) {
|
||||
}
|
||||
|
||||
function initNavigation() {
|
||||
navItemEls.forEach((a) => {
|
||||
a.addEventListener("click", (evt) => {
|
||||
evt.preventDefault();
|
||||
setActivePage(a.dataset.page || "config");
|
||||
});
|
||||
});
|
||||
// Restore last page, default to config (màn hình chính).
|
||||
let initial = "config";
|
||||
try {
|
||||
const saved = localStorage.getItem("activePage");
|
||||
if (saved === "dashboard" || saved === "overview" || saved === "config" || saved === "missions" || saved === "integrations") {
|
||||
initial = saved === "overview" ? "dashboard" : saved;
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
setActivePage(initial);
|
||||
if (window.NavApp?.init) window.NavApp.init();
|
||||
else setActivePage("config");
|
||||
}
|
||||
|
||||
window.LmApp = { setActivePage };
|
||||
|
||||
function setLeftPaneWidth(px) {
|
||||
const v = Math.round(clamp(Number(px), 320, 720));
|
||||
document.documentElement.style.setProperty("--leftPaneW", `${v}px`);
|
||||
@@ -629,7 +618,7 @@ function findDuplicateImuFrame(frameId, excludeId = null) {
|
||||
function clearCanvasSelection() {
|
||||
state.selectedId = null;
|
||||
state.selectedImuId = null;
|
||||
selectedText.textContent = "none";
|
||||
selectedText.textContent = t("common.none");
|
||||
setSelectedRelText();
|
||||
}
|
||||
|
||||
@@ -1697,7 +1686,10 @@ function updateLayoutActiveHint() {
|
||||
if (!layoutActiveHintEl) return;
|
||||
const name = state.activeLayoutName || "—";
|
||||
const dirty = state.layoutDirty ? " • chưa lưu" : "";
|
||||
layoutActiveHintEl.textContent = `Đang chỉnh: ${name}${dirty}`;
|
||||
layoutActiveHintEl.textContent = t("config.layout.editingHint", {
|
||||
name,
|
||||
dirty: dirty ? t("config.layout.unsavedDirty") : "",
|
||||
});
|
||||
}
|
||||
|
||||
function renderLayoutSelect() {
|
||||
@@ -1783,7 +1775,7 @@ async function deleteActiveLayoutFromUI() {
|
||||
return;
|
||||
}
|
||||
const name = state.activeLayoutName || state.activeLayoutId;
|
||||
if (!window.confirm(`Xóa layout «${name}»? Hành động không hoàn tác.`)) return;
|
||||
if (!window.confirm(t("config.layout.deleteConfirm", { name }))) return;
|
||||
await api(`/api/layouts/${state.activeLayoutId}`, { method: "DELETE" });
|
||||
state.viewInitialized = false;
|
||||
await loadAll();
|
||||
@@ -2138,7 +2130,7 @@ function setSelectedRelText() {
|
||||
|
||||
function renderList() {
|
||||
if (!state.lidars.length) {
|
||||
listEl.innerHTML = `<div class="item"><div class="itemName">Chưa có LiDAR</div><div class="itemMeta">Hãy thêm LiDAR ở form phía trên.</div></div>`;
|
||||
listEl.innerHTML = `<div class="item"><div class="itemName">${t("config.lidar.empty")}</div><div class="itemMeta">${t("config.lidar.emptyHint")}</div></div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2244,7 +2236,7 @@ function updateImuItemPoseUI(id) {
|
||||
function renderImuList() {
|
||||
if (!imuListEl) return;
|
||||
if (!state.imus.length) {
|
||||
imuListEl.innerHTML = `<div class="item"><div class="itemName">Chưa có IMU</div><div class="itemMeta">Thêm IMU ở form phía trên.</div></div>`;
|
||||
imuListEl.innerHTML = `<div class="item"><div class="itemName">${t("config.imu.empty")}</div><div class="itemMeta">${t("config.imu.emptyHint")}</div></div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3117,7 +3109,7 @@ async function loadAll() {
|
||||
state.selectedImuId = null;
|
||||
}
|
||||
if (!state.selectedId && !state.selectedImuId) {
|
||||
selectedText.textContent = "none";
|
||||
selectedText.textContent = t("common.none");
|
||||
}
|
||||
setSelectedRelText();
|
||||
renderList();
|
||||
@@ -3130,7 +3122,10 @@ async function loadAll() {
|
||||
overviewActiveModelEl.textContent = state.layout?.robot?.model || "diff";
|
||||
}
|
||||
if (overviewActiveSensorsEl) {
|
||||
overviewActiveSensorsEl.textContent = `${state.lidars.length} LiDAR • ${state.imus.length} IMU`;
|
||||
overviewActiveSensorsEl.textContent = t("dashboard.system.sensorCount", {
|
||||
lidars: state.lidars.length,
|
||||
imus: state.imus.length,
|
||||
});
|
||||
}
|
||||
if (!state.viewInitialized) {
|
||||
fitViewToWorld();
|
||||
@@ -3404,16 +3399,16 @@ saveLayoutBtn?.addEventListener("click", async () => {
|
||||
await api("/api/health");
|
||||
await loadMotorCatalog();
|
||||
await loadAll();
|
||||
selectedText.textContent = "none";
|
||||
selectedText.textContent = t("common.none");
|
||||
selectedRelText.textContent = "—";
|
||||
setStatus("Sẵn sàng");
|
||||
setStatus(t("app.status.ready"));
|
||||
} catch (e) {
|
||||
const msg = String(e.message || e);
|
||||
if (overviewBackendEl) overviewBackendEl.textContent = `Lỗi: ${msg}`;
|
||||
if (overviewBackendEl) overviewBackendEl.textContent = t("common.error", { msg });
|
||||
if (msg.includes("stack") || msg.includes("Maximum call")) {
|
||||
setStatus(`Lỗi JavaScript: ${msg}`);
|
||||
setStatus(`${t("app.status.jsError")}: ${msg}`);
|
||||
} else {
|
||||
setStatus(`Không kết nối được backend: ${msg}`);
|
||||
setStatus(`${t("app.status.backendError")}: ${msg}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3421,3 +3416,15 @@ saveLayoutBtn?.addEventListener("click", async () => {
|
||||
else window.AuthApp?.whenReady(() => { boot(); });
|
||||
})();
|
||||
|
||||
window.addEventListener("lm:locale-change", () => {
|
||||
if (typeof renderList === "function") renderList();
|
||||
if (typeof renderImuList === "function") renderImuList();
|
||||
if (typeof renderLayoutSelect === "function") renderLayoutSelect();
|
||||
if (typeof renderLayoutSelect === "function") renderLayoutSelect();
|
||||
if (typeof updateLayoutActiveHint === "function") updateLayoutActiveHint();
|
||||
if (typeof renderMotorWheels === "function") renderMotorWheels();
|
||||
if (typeof renderBicycleMotorWheels === "function") renderBicycleMotorWheels();
|
||||
if (typeof updateOverview === "function") updateOverview();
|
||||
window.I18n?.applyDOM?.();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user