const perPage=10 let currentPage=1 let totalPages=1 let regionsCurrentPage=1 let sortField="region_id" let sortOrder="asc" let currentFetchController=null let currentSystemIdToDelete=null let systemsCurrentPage=1 let systemsTotalPages=1 let systemsSortField="system_id" let systemsSortOrder="asc" // Предварительно создаём объекты модалей (чтобы не создавать каждый раз заново) const editRegionNameModal = new bootstrap.Modal(document.getElementById('editRegionNameModal')); const deleteRegionModal = new bootstrap.Modal(document.getElementById('deleteRegionModal')); const regionSubscribersModal = new bootstrap.Modal(document.getElementById('regionSubscribersModal')); const editSystemNameModal = new bootstrap.Modal(document.getElementById('editSystemNameModal')); const deleteSystemModal = new bootstrap.Modal(document.getElementById('deleteSystemModal')); function loadRegions(e){ if(e<1||e>totalPages)return; currentPage=e; let t=`/telezab/rest/api/regions?page=${currentPage}&per_page=${perPage}&sort_field=${sortField}&sort_order=${sortOrder}`; currentFetchController&¤tFetchController.abort(), safeFetch(t,{signal:(currentFetchController=new AbortController).signal}) .then(e=>e.json()) .then(e=>{ currentFetchController=null, totalPages=e.total_pages, updateRegionsTable(e.regions), updatePagination(e.current_page,e.total_pages,"pagination-regions") }) .catch(e=>{ "AbortError"===e.name||console.error("Error fetching regions:",e), currentFetchController=null }) } function updateRegionsTable(e){ let t=document.getElementById("regions-table"); if(!t) { console.error("regions-table element not found!"); return; } t.innerHTML=""; e.forEach(e=>{ let n=document.createElement("tr"); n.innerHTML=` ${e.region_id} ${e.name} ${e.active ? "Включен" : "Выключен"}
`; t.appendChild(n) }); setupRegionActions(); } function setupRegionActions(){ // Используем делегирование событий для избежания множественных обработчиков const regionsTable = document.getElementById("regions-table"); if (!regionsTable) return; // Удаляем старые обработчики событий с таблицы const newTable = regionsTable.cloneNode(true); regionsTable.parentNode.replaceChild(newTable, regionsTable); // Используем делегирование событий newTable.addEventListener('click', function(e) { const target = e.target.closest('button'); if (!target) return; if (target.classList.contains('delete-btn')) { e.preventDefault(); deleteRegion(target.dataset.id); } else if (target.classList.contains('edit-name-btn')) { e.preventDefault(); handleEditRegionName(target); } else if (target.classList.contains('subscribers-btn')) { e.preventDefault(); showRegionSubscribers(target.dataset.id); } }); // Обработчик для переключателей статуса newTable.addEventListener('change', function(e) { if (e.target.classList.contains('region-status-switch')) { let id = e.target.dataset.id; let active = e.target.checked; toggleRegionStatus(id, active); document.getElementById(`region-status-label-${id}`).textContent = active ? "Включен" : "Выключен"; } }); // Обновляем заголовки для сортировки document.querySelectorAll("th[data-sort]").forEach(e=>{ const newHeader = e.cloneNode(true); e.parentNode.replaceChild(newHeader, e); newHeader.addEventListener("click", ()=>{ let field = newHeader.dataset.sort; if(field === sortField) { sortOrder = sortOrder === "asc" ? "desc" : "asc"; } else { sortField = field; sortOrder = "asc"; } loadRegions(currentPage); }); }); const tooltipTriggerList = [].slice.call(document.querySelectorAll('[title]')); tooltipTriggerList.map(el => new bootstrap.Tooltip(el)); } function handleEditRegionName(e){ let t = e.dataset.id, n = e.dataset.name; document.getElementById("old-region-name").value = n; document.getElementById("new-region-name").value = n; editRegionNameModal.show(); let s = 5; document.getElementById("edit-region-name-timer").textContent = s; let a = setInterval(() => { s--; document.getElementById("edit-region-name-timer").textContent = s; if (s === 0) { clearInterval(a); document.getElementById("save-region-name-btn").removeAttribute("disabled"); } }, 1000); let r = document.getElementById("save-region-name-btn"), o = r.cloneNode(true); r.parentNode.replaceChild(o, r); o.addEventListener("click", () => { updateRegionName(t, document.getElementById("new-region-name").value); editRegionNameModal.hide(); }); } function showRegionSubscribers(e){ safeFetch(`/telezab/rest/api/regions/${e}/subscribers`) .then(res => res.json()) .then(data => { let t = document.getElementById("regionSubscribersTableBody"); t.innerHTML = ""; if(data.subscribers && data.subscribers.length > 0){ data.subscribers.forEach(sub => { let n = document.createElement("tr"); n.innerHTML = ` ${sub.telegram_id} ${sub.email} `; t.appendChild(n); }); } else { let n = document.createElement("tr"); n.innerHTML = `Нет подписчиков для этого региона.`; t.appendChild(n); } regionSubscribersModal.show(); }) .catch(err => { console.error("Ошибка при получении подписчиков региона:", err); toastr.error("Ошибка при получении подписчиков региона. Пожалуйста, попробуйте позже."); }); } function updateRegionName(regionId, newName) { safeFetch("/telezab/rest/api/regions/name", { method:"PUT", headers:{"Content-Type":"application/json"}, body:JSON.stringify({region_id:regionId, name:newName}) }) .then(()=>{ loadRegions(currentPage); toastr.success("Название региона изменено."); }) .catch(e=>{ console.error("Ошибка при изменении названия региона:",e); toastr.error("Ошибка при изменении названия региона. Пожалуйста, попробуйте позже."); }); } function toggleRegionStatus(e,t){ safeFetch("/telezab/rest/api/regions/status",{ method:"PUT", headers:{"Content-Type":"application/json"}, body:JSON.stringify({region_id:e,active:t}) }) .then(()=>{ loadRegions(currentPage); t?toastr.success("Регион активирован."):toastr.success("Регион деактивирован."); }) .catch(e=>{ console.error("Ошибка при изменении статуса региона:",e); toastr.error("Ошибка при изменении статуса региона. Пожалуйста, попробуйте позже."); }); } function deleteRegion(e){ deleteRegionModal.show(); let t = document.getElementById("deleteConfirmationInput"), n = document.getElementById("confirmDeleteButton"), s = document.getElementById("deleteRegionModal"), a = t.cloneNode(true), r = n.cloneNode(true); t.parentNode.replaceChild(a, t); n.parentNode.replaceChild(r, n); a.addEventListener("input", function(){ let val = this.value; r.disabled = val === "УДАЛИТЬ" ? false : true; }); r.addEventListener("click", function(){ safeFetch(`/telezab/rest/api/regions/${e}`, { method: "DELETE" }) .then(() => { loadRegions(currentPage); toastr.success("Регион успешно удален."); deleteRegionModal.hide(); }) .catch(err => { console.error("Ошибка при удалении региона:", err); toastr.error("Ошибка при удалении региона. Пожалуйста, попробуйте позже."); }); }); s.addEventListener("hidden.bs.modal", function(){ a.value = ""; r.disabled = true; }, { once: true }); } function loadSystems(e){ if(e<1||e>systemsTotalPages)return; systemsCurrentPage=e; let t=`/telezab/rest/api/systems?page=${systemsCurrentPage}&per_page=10&sort_field=${systemsSortField}&sort_order=${systemsSortOrder}`; currentFetchController&¤tFetchController.abort(), safeFetch(t,{signal:(currentFetchController=new AbortController).signal}) .then(e=>e.json()) .then(e=>{ currentFetchController=null, systemsTotalPages=e.total_pages, updateSystemsTable(e.systems), updatePagination(e.current_page,e.total_pages,"pagination-systems") }) .catch(e=>{ "AbortError"===e.name||console.error("Error fetching systems:",e), currentFetchController=null }); } function updateSystemsTable(e) { let t = document.getElementById("systems-table"); if (!t) { console.error("systems-table element not found!"); return; } t.innerHTML = ""; e.forEach(e => { let n = document.createElement("tr"); n.innerHTML = ` ${e.system_id} ${e.system_name} ${e.name}
`; t.appendChild(n); }); setupSystemActions(); // Инициализация Bootstrap tooltips const tooltipTriggerList = [].slice.call(document.querySelectorAll('[title]')); tooltipTriggerList.map(el => new bootstrap.Tooltip(el)); } function setupSystemActions(){ // Используем делегирование событий для таблицы систем const systemsTable = document.getElementById("systems-table"); if (!systemsTable) return; // Удаляем старые обработчики событий с таблицы const newTable = systemsTable.cloneNode(true); systemsTable.parentNode.replaceChild(newTable, systemsTable); // Используем делегирование событий newTable.addEventListener('click', function(e) { const target = e.target.closest('button'); if (!target) return; if (target.classList.contains('delete-btn')) { e.preventDefault(); deleteSystem(target.dataset.id); } else if (target.classList.contains('edit-name-btn')) { e.preventDefault(); handleEditSystemName(target); } }); // Обновляем заголовки для сортировки document.querySelectorAll("th[data-sort]").forEach(e=>{ const newHeader = e.cloneNode(true); e.parentNode.replaceChild(newHeader, e); newHeader.addEventListener("click", ()=>{ let field = newHeader.dataset.sort; if(field === systemsSortField) { systemsSortOrder = systemsSortOrder === "asc" ? "desc" : "asc"; } else { systemsSortField = field; systemsSortOrder = "asc"; } loadSystems(systemsCurrentPage); }); }); } function handleEditSystemName(e){ let t = e.dataset.id, n = e.dataset.latinName, s = e.dataset.name; let a = document.getElementById("edit-system-id"), r = document.getElementById("edit-system-name-lat"), o = document.getElementById("old-system-name"), l = document.getElementById("new-system-name"); if(!a || !r || !o || !l){ console.error("❌ Один или несколько элементов не найдены:"); !a && console.error(" - Не найден элемент #edit-system-id"); !r && console.error(" - Не найден элемент #edit-system-name-lat"); !o && console.error(" - Не найден элемент #old-system-name"); !l && console.error(" - Не найден элемент #new-system-name"); return; } a.value = t; r.value = n; o.value = s; l.value = s; editSystemNameModal.show(); let d = 5, i = document.getElementById("edit-system-name-timer"); i.textContent = d; let m = setInterval(() => { d--; i.textContent = d; if(d === 0){ clearInterval(m); document.getElementById("saveSystemNameBtn").removeAttribute("disabled"); } }, 1000); let c = document.getElementById("saveSystemNameBtn"), g = c.cloneNode(true); c.parentNode.replaceChild(g, c); g.addEventListener("click", () => { let id = a.value, latinName = r.value, newName = l.value.trim(); if(!newName){ toastr.error("Введите новое название системы."); return; } updateSystemName(id, latinName, newName); editSystemNameModal.hide(); }); } function updateSystemName(systemId, systemNameLat, nameCyr) { if (!systemId || !systemNameLat || !nameCyr) { toastr.error("Не все параметры заполнены."); return; } safeFetch("/telezab/rest/api/systems", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ system_id: systemId, system_name: systemNameLat, name: nameCyr }) }) .then(res => res.ok ? res.json() : res.json().then(e => { throw new Error(e.message || "Ошибка сервера") })) .then(() => { loadSystems(systemsCurrentPage); toastr.success("Название системы успешно обновлено."); }) .catch(e => { console.error("Ошибка при изменении названия системы:", e); toastr.error(e.message || "Ошибка при изменении названия системы. Пожалуйста, попробуйте позже."); }); } function deleteSystem(e){ deleteSystemModal.show(); let t = document.getElementById("deleteSystemConfirmationInput"), n = document.getElementById("confirmDeleteSystemButton"), s = document.getElementById("deleteSystemModal"), a = t.cloneNode(true), r = n.cloneNode(true); t.parentNode.replaceChild(a, t); n.parentNode.replaceChild(r, n); a.addEventListener("input", function(){ let val = this.value; r.disabled = val === "УДАЛИТЬ" ? false : true; }); r.addEventListener("click", function(){ safeFetch(`/telezab/rest/api/systems/${e}`, { method: "DELETE" }) .then(() => { loadSystems(systemsCurrentPage); toastr.success("Система успешно удалена."); deleteSystemModal.hide(); }) .catch(err => { console.error("Ошибка при удалении системы:", err); toastr.error("Ошибка при удалении системы. Пожалуйста, попробуйте позже."); }); }); s.addEventListener("hidden.bs.modal", function(){ a.value = ""; r.disabled = true; }, { once: true }); } function updatePagination(e,t,n){ let s=document.getElementById(n); if(!s){ console.error(`Container with ID ${n} not found!`); return; } s.innerHTML=""; let a=document.createElement("li"); a.classList.add("page-item"); a.classList.toggle("disabled",1===e); a.innerHTML=`«`; s.appendChild(a); for(let r=1;r<=t;r++){ let o=document.createElement("li"); o.classList.add("page-item"); o.classList.toggle("active",r===e); let l=document.createElement("a"); l.classList.add("page-link"); l.href="#"; l.textContent=r; l.onclick=()=>loadPage(r,n); o.appendChild(l); s.appendChild(o); } let i=document.createElement("li"); i.classList.add("page-item"); i.classList.toggle("disabled",e===t); i.innerHTML=`»`; s.appendChild(i); } function loadPage(e,t){ "pagination-regions"===t?loadRegions(e):"pagination-systems"===t&&loadSystems(e); } // Настройка toastr toastr.options={ closeButton:!0, debug:!1, newestOnTop:!1, progressBar:!1, positionClass:"toast-bottom-right", preventDuplicates:!1, onclick:null, showDuration:"300", hideDuration:"1000", timeOut:"5000", extendedTimeOut:"1000", showEasing:"swing", hideEasing:"linear", showMethod:"fadeIn", hideMethod:"fadeOut" }; // Обработчики форм document.getElementById("add-region-form").addEventListener("submit",e=>{ e.preventDefault(); let regionId=document.getElementById("region-id").value; let regionName=document.getElementById("region-name").value; let regionActive=document.getElementById("region-active").checked; if(!/^\d+$/.test(regionId)){ toastr.error("ID региона должен содержать только числа."); console.log("Ошибка: ID региона не является числом."); return; } safeFetch("/telezab/rest/api/regions",{ method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({region_id:regionId,name:regionName,active:regionActive}) }) .then(e=>e.ok?e.json():e.json().then(e=>{ throw console.error("Ошибка от сервера:",e),new Error(e.message||"Ошибка добавления региона"); })) .then(e=>{ if("success"===e.status){ console.log("Регион успешно добавлен."); document.getElementById("region-id").value=""; document.getElementById("region-name").value=""; document.getElementById("region-active").checked=!1; loadRegions(regionsCurrentPage); toastr.success(e.message); $("#addRegionModal").modal("hide"); }else{ throw console.log("Ошибка: Статус ответа не 'success'."),new Error(e.message||"Неизвестная ошибка"); } }) .catch(e=>{ console.error("Ошибка при добавлении региона:",e); toastr.error(e.message||"Ошибка при добавлении региона. Пожалуйста, попробуйте позже."); }); }); document.getElementById("add-system-form").addEventListener("submit",e=>{ e.preventDefault(); let systemId=document.getElementById("system-id").value; let systemNameLat=document.getElementById("system-name-lat").value; let systemNameCyr=document.getElementById("system-name-cyr").value; if(!/^\d+$/.test(systemId)){ toastr.error("ID системы должен содержать только числа."); return; } safeFetch("/telezab/rest/api/systems",{ method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({system_id:systemId,system_name:systemNameLat,name:systemNameCyr}) }) .then(e=>e.ok?e.json():e.json().then(e=>{ throw new Error(e.message||"Ошибка добавления системы"); })) .then(e=>{ document.getElementById("system-id").value=""; document.getElementById("system-name-lat").value=""; document.getElementById("system-name-cyr").value=""; loadSystems(systemsCurrentPage); toastr.success("Система успешно добавлена."); $("#addSystemModal").modal("hide"); }) .catch(e=>{ console.error("Ошибка при добавлении системы:",e); toastr.error(e.message||"Ошибка при добавлении системы. Пожалуйста, попробуйте позже."); }); }); // Инициализация при загрузке страницы document.addEventListener("DOMContentLoaded",()=>{ let regionsTab=document.getElementById("regions-tab"); let systemsTab=document.getElementById("systems-tab"); regionsTab.addEventListener("shown.bs.tab",()=>{ loadRegions(currentPage); }); systemsTab.addEventListener("shown.bs.tab",()=>{ loadSystems(systemsCurrentPage); }); regionsTab.classList.contains("active")&&loadRegions(currentPage); // Очистка форм при закрытии модальных окон let modalsConfig=[ {modalId:"addRegionModal",formId:"add-region-form"}, {modalId:"addSystemModal",formId:"add-system-form"}, {modalId:"editRegionNameModal",formId:"edit-region-name-form"}, {modalId:"editSystemNameModal",formId:"edit-system-name-form"} ]; modalsConfig.forEach(({modalId,formId})=>{ let modal=document.getElementById(modalId); let form=document.getElementById(formId); if(modal&&form){ modal.addEventListener("hidden.bs.modal",()=>{ form.reset(); }); } }); });