(() => { const PAGE_SIZE = 10; const ICONS = { path: ``, view: ``, delete: ``, }; const el = (id) => document.getElementById(id); const t = (key, vars) => window.I18n?.t(key, vars) ?? key; const listEl = el("pathList"); const emptyEl = el("pathListEmpty"); const tableEl = el("pathsTable"); const filterInputEl = el("pathsFilterInput"); const filterCountEl = el("pathsFilterCount"); const pageLabelEl = el("pathsPageLabel"); const deleteConfirmDialogEl = el("pathDeleteConfirmDialog"); const deleteConfirmTextEl = el("pathDeleteConfirmText"); const store = { paths: [], maps: [], sites: [], pendingDeleteId: null, filter: "", page: 1, }; function canWrite() { if (!window.AuthApp?.canWrite) return true; return window.AuthApp.canWrite("maps"); } function escapeHtml(str) { return String(str) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } async function apiJson(url, opts = {}) { const res = await fetch(url, { credentials: "include", ...opts }); const text = await res.text(); let data = null; try { data = text ? JSON.parse(text) : null; } catch { data = null; } if (!res.ok) throw new Error((data && data.error) || text || res.statusText); return data; } async function refreshAll() { const [sitesData, mapsData, pathsData] = await Promise.all([ apiJson("/api/sites"), apiJson("/api/maps"), apiJson("/api/paths"), ]); store.sites = Array.isArray(sitesData.sites) ? sitesData.sites : []; store.maps = Array.isArray(mapsData.maps) ? mapsData.maps : []; store.paths = Array.isArray(pathsData.paths) ? pathsData.paths : []; return store.paths; } function mapName(id) { return store.maps.find((m) => m.id === id)?.name || id || "—"; } function positionLabel(mapId, positionId) { const map = store.maps.find((m) => m.id === mapId); const zones = Array.isArray(map?.zones) ? map.zones : []; const hit = zones.find((z) => z && z.type === "position" && z.id === positionId); const pname = hit?.name || positionId || "—"; return `${mapName(mapId)} / ${pname}`; } function filteredPaths() { const q = store.filter.trim().toLowerCase(); let items = [...store.paths]; if (q) { items = items.filter((p) => { const from = positionLabel(p.map_id, p.from_position_id).toLowerCase(); const to = positionLabel(p.map_id, p.to_position_id).toLowerCase(); const map = mapName(p.map_id).toLowerCase(); return from.includes(q) || to.includes(q) || map.includes(q); }); } return items.sort((a, b) => { const ma = mapName(a.map_id).localeCompare(mapName(b.map_id)); if (ma !== 0) return ma; return positionLabel(a.map_id, a.from_position_id).localeCompare( positionLabel(b.map_id, b.from_position_id), ); }); } function pageCount(total) { return Math.max(1, Math.ceil(total / PAGE_SIZE)); } function renderList() { if (!listEl) return; const items = filteredPaths(); const total = items.length; const pages = pageCount(total); if (store.page > pages) store.page = pages; const start = (store.page - 1) * PAGE_SIZE; const pageItems = items.slice(start, start + PAGE_SIZE); if (filterCountEl) filterCountEl.textContent = t("paths.itemsFound", { n: total }); if (pageLabelEl) pageLabelEl.textContent = t("paths.pageOf", { page: store.page, total: pages }); listEl.innerHTML = ""; if (tableEl) tableEl.hidden = total === 0; if (emptyEl) { emptyEl.hidden = total > 0; emptyEl.textContent = store.filter ? t("paths.emptyFilter") : t("paths.empty"); } pageItems.forEach((path) => { const tr = document.createElement("tr"); tr.className = "pathsMirRow"; tr.innerHTML = `