unlimited reports rows and pages with filters

This commit is contained in:
Ivan 2023-10-10 04:14:13 +03:00
parent fb3dcf80e3
commit 34bbbd0c1a
Signed by untrusted user who does not match committer: ppechenkoo
GPG Key ID: 0C191B86D9582583
4 changed files with 546 additions and 171 deletions

376
server.js
View File

@ -885,6 +885,7 @@ async function reports(req, res) {
ifDBError: false, ifDBError: false,
Registrars: [], Registrars: [],
Groups: [], Groups: [],
Count: 0,
}; };
try { try {
const pool = new Pool({ const pool = new Pool({
@ -927,7 +928,7 @@ async function reports(req, res) {
FROM geo FROM geo
) AS g ON a.serial = g.serial AND g.row_num = 1 ) AS g ON a.serial = g.serial AND g.row_num = 1
ORDER BY a.time DESC ORDER BY a.time DESC
LIMIT 100; LIMIT 14;
`; `;
const alarms = await client.query(query, templateData.isAdmin ? [] : [serialValues]); const alarms = await client.query(query, templateData.isAdmin ? [] : [serialValues]);
@ -1104,6 +1105,27 @@ const formattedDate = `${("0" + day).slice(-2)}.${("0" + month).slice(-2)}.${yea
numbers: groupedNumbers[groupName], numbers: groupedNumbers[groupName],
})); }));
const countQueryText = `
SELECT COUNT(*) AS total
FROM (
SELECT DISTINCT a.evtuuid
FROM alarms AS a
LEFT JOIN registrars AS r ON a.serial = r.serial
LEFT JOIN (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY serial ORDER BY ABS(EXTRACT(EPOCH FROM (time - NOW())))) AS row_num
FROM geo
) AS g ON a.serial = g.serial AND g.row_num = 1
WHERE a.alarmtype = 56
${!templateData.isAdmin ? 'AND a.serial = ANY($1)' : ''}
) AS unique_events
`;
const countResult = await pool.query(countQueryText, templateData.isAdmin ? [] : [serialValues]);
templateData.Count = countResult.rows[0].total;
const source = fs.readFileSync( const source = fs.readFileSync(
"static/templates/reports/index.html", "static/templates/reports/index.html",
"utf8" "utf8"
@ -1127,6 +1149,358 @@ const formattedDate = `${("0" + day).slice(-2)}.${("0" + month).slice(-2)}.${yea
} }
} }
app.post('/getreports', async (req, res) => {
if (req.session.userId === undefined) {
return res.redirect("/signin?page=reports");
}
try {
const pool = new Pool({
user: DB_User,
host: DB_Host,
database: DB_Name,
password: DB_Password,
port: DB_Port,
});
let serialValues = [];
if (req.session.userId != 'admin') {
const userDevicesQuery = `
SELECT devices
FROM users
WHERE id = $1
`;
const userDevicesResult = await pool.query(userDevicesQuery, [req.session.userId]);
if (userDevicesResult.rows[0].devices.length > 0) {
serialValues = userDevicesResult.rows[0].devices;
}
}
const { page, timeRangeStart, timeRangeEnd, serials, searchText } = req.body;
let timeRangeStartCheck = timeRangeStart;
let timeRangeEndCheck = timeRangeEnd;
let serialsCheck = serials;
console.log(req.body);
if (!timeRangeStartCheck || !timeRangeEndCheck || serialsCheck.length < 1) {
const minMaxSerialQuery = `
SELECT
MIN(time) AS min_date,
MAX(time) AS max_date,
ARRAY_AGG(DISTINCT serial) AS unique_serials
FROM
alarms;
`;
const minMaxDateResult = await pool.query(minMaxSerialQuery);
if (!timeRangeStartCheck) {
timeRangeStartCheck = minMaxDateResult.rows[0].min_date;
}
if (!timeRangeEndCheck) {
timeRangeEndCheck = minMaxDateResult.rows[0].max_date;
}
if (serialsCheck.length < 1) {
if (req.session.userId != 'admin') {
serialsCheck = serialValues;
} else {
serialsCheck = minMaxDateResult.rows[0].unique_serials;
}
}
}
const violationsMapping = {
0: "усталость",
1: "водитель пропал",
2: "разговор по телефону",
3: "курение за рулём",
4: "водитель отвлекся",
5: "выезд с полосы движения",
6: "!!! лобовое столкновение",
7: "скорость превышена",
8: "распознавание номерных знаков",
9: "!! маленькое расстояние спереди",
10: "водитель зевает",
11: "!!! столкновение с пешеходом",
12: "проходы переполнены",
13: "!! посадка/высадка вне остановки",
14: "!! смена полосы с нарушением ПДД",
15: "! включенный телефон у водителя",
16: "!!! ремень безопасности",
17: "проверка не удалась",
18: "слепые зоны справа",
19: "!!! заднее столкновение",
20: "!!! управление без рук",
21: "!! управление одной рукой",
22: "очки, блокирующие инфракрасное излучение",
23: "слепые зоны слева",
24: "помехи для пассажиров",
25: "на перекрестке ограничена скорость",
26: "обнаружен перекресток",
27: "пешеходы на переходе",
28: "! неучтивое отношение к пешеходам",
29: "обнаружен пешеходный переход",
30: "водитель матерится"
};
const idList = Object.entries(violationsMapping)
.filter(([id, violation]) => violation.includes(searchText.toLowerCase()))
.map(([id, violation]) => id);
console.log(idList);
const searchConditions = [
'a.evtuuid::TEXT ILIKE $4',
'a.id::TEXT ILIKE $4',
'r.plate::TEXT ILIKE $4',
'a.serial::TEXT ILIKE $4',
'g.latitude::TEXT ILIKE $4',
'g.longitude::TEXT ILIKE $4',
];
const countQueryText = `
SELECT COUNT(*) AS total
FROM (
SELECT DISTINCT a.evtuuid
FROM alarms AS a
LEFT JOIN registrars AS r ON a.serial = r.serial
LEFT JOIN (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY serial ORDER BY ABS(EXTRACT(EPOCH FROM (time - NOW())))) AS row_num
FROM geo
) AS g ON a.serial = g.serial AND g.row_num = 1
WHERE a.alarmtype = 56
AND a.time >= $1::timestamp
AND a.time <= $2::timestamp
AND a.serial = ANY($3)
${searchText ? `AND (${idList.length > 0 ? 'st = ANY($5) OR' : ''} (${searchConditions.join(' OR ')}))` : ''}
) AS unique_events;
`;
const countValues = [
timeRangeStartCheck,
timeRangeEndCheck,
serialsCheck,
];
if (searchText.length > 0) {
countValues.push(`%${searchText}%`);
}
if (idList.length > 0 && idList.length !== 31) {
countValues.push(idList);
}
const countResult = await pool.query(countQueryText, countValues);
const totalCount = countResult.rows[0].total;
const queryConditions = [
'a.evtuuid::TEXT ILIKE $6',
'a.id::TEXT ILIKE $6',
'r.plate::TEXT ILIKE $6',
'a.serial::TEXT ILIKE $6',
'g.latitude::TEXT ILIKE $6',
'g.longitude::TEXT ILIKE $6',
];
const queryText = `
SELECT a.evtuuid, a.id, a.cmdno, a.time, a.serial, a.st, r.plate, g.latitude, g.longitude, r.number
FROM (
SELECT DISTINCT ON (evtuuid) evtuuid, id, cmdno, time, serial, st
FROM alarms
WHERE alarmtype = 56
AND time >= $1::timestamp
AND time <= $2::timestamp
ORDER BY evtuuid, time DESC
) AS a
LEFT JOIN registrars AS r ON a.serial = r.serial
LEFT JOIN (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY serial ORDER BY ABS(EXTRACT(EPOCH FROM (time - NOW())))) AS row_num
FROM geo
) AS g ON a.serial = g.serial AND g.row_num = 1
WHERE a.time >= $1::timestamp
AND a.time <= $2::timestamp
AND a.serial = ANY($3)
${searchText ? `AND (${idList.length > 0 ? 'st = ANY($7) OR' : ''} (${queryConditions.join(' OR ')}))` : ``}
ORDER BY a.time DESC
OFFSET $4 LIMIT $5;
`;
const values = [
timeRangeStartCheck,
timeRangeEndCheck,
serialsCheck,
(page - 1) * 14,
14,
];
if (searchText.length > 0) {
values.push(`%${searchText}%`);
}
if (idList.length > 0 && idList.length !== 31) {
values.push(idList);
}
const result = await pool.query(queryText, values);
function formatDate(date) {
const options = {
year: "2-digit",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
hour12: false,
};
const dateString = date.toISOString().replace("T", " ").slice(0, 19);
const [datePart, timePart] = dateString.split(' ');
const [year, month, day] = datePart.split('-');
const [hour, minute, second] = timePart.split(':');
const formattedDate = `${("0" + day).slice(-2)}.${("0" + month).slice(-2)}.${year.slice(-2)} ${("0" + hour).slice(-2)}:${("0" + minute).slice(-2)}`;
return formattedDate;
}
const Alarms = result.rows.map((alarm) => {
let type;
switch (alarm.st) {
case "0":
type = "Усталость";
break;
case "1":
type = "Водитель пропал";
break;
case "2":
type = "Разговор по телефону";
break;
case "3":
type = "Курение за рулём";
break;
case "4":
type = "Водитель отвлекся";
break;
case "5":
type = "Выезд с полосы движения";
break;
case "6":
type = "!!! Лобовое столкновение";
break;
case "7":
type = "Скорость превышена";
break;
case "8":
type = "Распознавание номерных знаков";
break;
case "9":
type = "!! Маленькое расстояние спереди";
break;
case "10":
type = "Водитель зевает";
break;
case "11":
type = "!!! Столкновение с пешеходом";
break;
case "12":
type = "Проходы переполнены";
break;
case "13":
type = "!! Посадка/высадка вне остановки";
break;
case "14":
type = "!! Смена полосы с нарушением ПДД";
break;
case "15":
type = "! Включенный телефон у водителя";
break;
case "16":
type = "!!! Ремень безопасности";
break;
case "17":
type = "Проверка не удалась";
break;
case "18":
type = "Слепые зоны справа";
break;
case "19":
type = "!!! Заднее столкновение";
break;
case "20":
type = "!!! Управление без рук";
break;
case "21":
type = "!! Управление одной рукой";
break;
case "22":
type = "Очки, блокирующие инфракрасное излучение";
break;
case "23":
type = "Слепые зоны слева";
break;
case "24":
type = "Помехи для пассажиров";
break;
case "25":
type = "На перекрестке ограничена скорость";
break;
case "26":
type = "Обнаружен перекресток";
break;
case "27":
type = "Пешеходы на переходе";
break;
case "28":
type = "! Неучтивое отношение к пешеходам";
break;
case "29":
type = "Обнаружен пешеходный переход";
break;
case "30":
type = "Водитель матерится";
break;
default:
type = "Неизвестный тип";
}
return {
id: alarm.id,
cmdno: alarm.cmdno,
time: formatDate(alarm.time),
number: alarm.number,
serial: alarm.serial,
st: alarm.st,
type: type,
plate: alarm.plate,
latitude: (alarm.latitude).toFixed(6),
longitude: (alarm.longitude).toFixed(6),
geo: (alarm.latitude).toFixed(6) + "," + (alarm.longitude).toFixed(6),
};
})
res.json({
total: totalCount,
data: Alarms,
});
} catch (error) {
console.error('Error handling request:', error);
res.status(500).send('Internal Server Error');
}
});
app.get("/api/devices", async (req, res) => { app.get("/api/devices", async (req, res) => {
if (req.session.userId === undefined) { if (req.session.userId === undefined) {
return res.redirect("/signin?page=live"); return res.redirect("/signin?page=live");

View File

@ -1,25 +1,11 @@
// Получаем высоту таблицы и определяем, сколько строк помещается на странице
let currentPage = 1;
let tableHeight = document.getElementById("table-area").offsetHeight;
let rowHeight = 60;
let rowsPerPage = Math.floor(tableHeight / rowHeight) - 3;
let filteredDevices = [...devices];
let timeRangeStart = null;
let timeRangeEnd = null;
const createTable = () => { const createTable = () => {
const table = document.getElementById("deviceTable"); const table = document.getElementById("deviceTable");
const tbody = table.querySelector("tbody"); const tbody = table.querySelector("tbody");
// Очищаем таблицу // Очищаем таблицу
tbody.innerHTML = ""; tbody.innerHTML = "";
// Добавляем строки таблицы
const startIndex = (currentPage - 1) * rowsPerPage;
const endIndex = startIndex + rowsPerPage;
const devicesToDisplay = filteredDevices.slice(startIndex, endIndex);
devicesToDisplay.forEach((device) => { devices.forEach((device) => {
const row = document.createElement("tr"); const row = document.createElement("tr");
// Добавляем ячейки с данными // Добавляем ячейки с данными
@ -57,137 +43,5 @@ const createTable = () => {
}); });
}; };
window.addEventListener("resize", function (event) {
tableHeight = document.getElementById("table-area").offsetHeight;
rowHeight = 60;
rowsPerPage = Math.floor(tableHeight / rowHeight) - 3;
createTable();
createPagination();
});
const createPagination = () => {
const count = document.getElementById("count");
count.textContent = `Всего результатов: ${filteredDevices.length}`;
const pagination = document.getElementById("pagination");
pagination.innerHTML = "";
const pageCount = Math.ceil(filteredDevices.length / rowsPerPage);
for (let i = 1; i <= pageCount; i++) {
const pageLink = document.createElement("a");
pageLink.href = "#";
if (i === currentPage) {
pageLink.classList.add("active");
}
pageLink.textContent = i;
pageLink.addEventListener("click", (event) => {
event.preventDefault();
currentPage = i - 1;
currentPage = i;
createTable();
createPagination();
});
pagination.appendChild(pageLink);
}
// var currentPageSpan = document.createElement("span");
// currentPageSpan.textContent = currentPage;
// pagination.appendChild(currentPageSpan);
// Добавляем кнопки "Next" и "Previous"
// const prevButton = document.createElement("button");
// prevButton.innerText = "Previous";
// prevButton.onclick = () => {
// if (currentPage === 1) return;
// currentPage--;
// createTable();
// };
// pagination.appendChild(prevButton);
// const nextButton = document.createElement("button");
// nextButton.innerText = "Next";
// nextButton.onclick = () => {
// if (currentPage === pageCount) return;
// currentPage++;
// createTable();
// };
// pagination.appendChild(nextButton);
};
const applyFilterAndSearch = () => {
const searchValue = searchInput.value.toLowerCase();
const groupFilters = Array.from(
document.querySelectorAll('input[type="checkbox"].device-filter:checked')
).map((checkbox) => checkbox.value);
filteredDevices = devices.filter((device) => {
const searchString =
`${device.group} ${device.name} ${device.id} ${device.place} ${device.numberTS} ${device.time} ${device.place} ${device.geo} ${device.serial}`.toLowerCase();
const matchGroup =
groupFilters.length === 0 || groupFilters.includes(device.group) || groupFilters.includes(device.serial);
const matchSearch = !searchValue || searchString.includes(searchValue);
// Фильтр по временному диапазону
let matchTimeRange = true;
if (timeRangeStart) {
const startTimestamp = new Date(timeRangeStart).getTime();
const deviceTimestamp = parseTableTime(device.time); // Преобразование времени из таблицы
matchTimeRange = startTimestamp <= deviceTimestamp;
}
if (timeRangeEnd) {
const endTimestamp = new Date(timeRangeEnd).getTime();
const deviceTimestamp = parseTableTime(device.time); // Преобразование времени из таблицы
matchTimeRange = matchTimeRange && deviceTimestamp <= endTimestamp;
}
return matchGroup && matchSearch && matchTimeRange;
});
currentPage = 1;
createTable();
createPagination();
};
// Функция для преобразования времени из таблицы в миллисекунды
function parseTableTime(tableTime) {
// Парсинг даты и времени из строки формата "12.03.23 17:33"
const parts = tableTime.split(" ");
const dateParts = parts[0].split(".");
const timeParts = parts[1].split(":");
const year = 2000 + parseInt(dateParts[2]);
const month = parseInt(dateParts[1]) - 1; // Месяцы в JavaScript начинаются с 0
const day = parseInt(dateParts[0]);
const hours = parseInt(timeParts[0]);
const minutes = parseInt(timeParts[1]);
return new Date(year, month, day, hours, minutes).getTime();
}
const searchInput = document.getElementById("table-search");
searchInput.addEventListener("input", applyFilterAndSearch);
const filterCheckboxes = Array.from(
document.querySelectorAll('input[type="checkbox"].device-filter')
);
filterCheckboxes.forEach((checkbox) => {
checkbox.addEventListener("change", applyFilterAndSearch);
});
// Обработчик изменения значения в поле начала временного диапазона
const timeRangeStartInput = document.getElementById("timeRangeStart");
timeRangeStartInput.addEventListener("change", () => {
timeRangeStart = timeRangeStartInput.value;
console.log(timeRangeStart);
applyFilterAndSearch();
});
// Обработчик изменения значения в поле конца временного диапазона
const timeRangeEndInput = document.getElementById("timeRangeEnd");
timeRangeEndInput.addEventListener("change", () => {
timeRangeEnd = timeRangeEndInput.value;
console.log(timeRangeEnd);
applyFilterAndSearch();
});
createTable(); createTable();
createPagination();

View File

@ -20,6 +20,16 @@ body {
min-height: 100%; min-height: 100%;
} }
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
header { header {
position: fixed; position: fixed;
top: 0; top: 0;
@ -1687,10 +1697,24 @@ input[type="datetime-local"] {
} }
.time-range input:hover, .time-range input:hover,
.time-range input:focus { .time-range input:focus,
#pagination input:hover,
#pagination input:focus {
border: 1px solid rgba(0, 0, 0, 0.3); border: 1px solid rgba(0, 0, 0, 0.3);
} }
#pagination input {
width: 20px;
height: 15px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 10px;
padding: 10px;
outline: none;
color: rgba(0, 0, 0, 0.9);
transition: 0.2s;
text-align: center;
}
.return-name { .return-name {
color: #8086f9 !important; color: #8086f9 !important;
} }
@ -1933,6 +1957,29 @@ input[type="datetime-local"] {
background-color: rgba(0, 0, 0, 0.1); background-color: rgba(0, 0, 0, 0.1);
} }
#pagination #left-slider {
padding: 5px;
border-radius: 5px;
cursor: pointer;
margin: 4px;
transition: 0.2s;
float: left;
}
#pagination #left-slider:hover,
#pagination #right-slider:hover {
background-color: rgba(0, 0, 0, 0.1);
}
#pagination #right-slider {
padding: 5px;
border-radius: 5px;
cursor: pointer;
margin: 4px;
transition: 0.2s;
float: right;
}
.signals-list .search { .signals-list .search {
float: left; float: left;
width: calc(305px - 30px); width: calc(305px - 30px);

View File

@ -74,17 +74,17 @@
<section class="bg"> <section class="bg">
<section class="content"> <section class="content">
<section class="for-table"> <section style="height: 945px !important;" class="for-table">
<section class="organisation"> <section class="organisation">
<h1>Организация</h1> <h1>Организация</h1>
<ul class="area"> <ul class="area">
{{#each Groups}} {{#each Groups}}
<li class="area-name"><img src="../img/ul.svg"><input type="checkbox" id="{{name}}" class="checkbox-input" hidden checked><label for="{{name}}" class="checkbox-label checkbox-name"><span class="text">{{name}}</span></label> <li class="area-name"><img src="../img/ul.svg"><input type="checkbox" id="{{name}}" class="checkbox-input" onchange="requestUpdate()" hidden checked><label for="{{name}}" class="checkbox-label checkbox-name"><span class="text">{{name}}</span></label>
<ul class="area-devices" id="devices-1"> <ul class="area-devices" id="devices-1">
{{#each serials}} {{#each serials}}
<li class="device"><img><input type="checkbox" id="{{this}}" class="checkbox-input device-filter" value="{{this}}" hidden checked><label for="{{this}}" class="checkbox-label"> <li class="device"><img><input type="checkbox" id="{{this}}" class="checkbox-input device-filter" value="{{this}}" onchange="requestUpdate()" hidden checked><label for="{{this}}" class="checkbox-label">
{{#if ../numbers}} {{#if ../numbers}}
{{#if (lookup ../numbers @index)}} {{#if (lookup ../numbers @index)}}
<div class="checkmark"></div>{{lookup ../numbers @index}} <div class="checkmark"></div>{{lookup ../numbers @index}}
@ -102,8 +102,8 @@
</ul> </ul>
<div class="area-time-range"> <div class="area-time-range">
<div class="time-range"><label for="timeRangeStart">с</label><input id="timeRangeStart" name="timeRangeStart" type="datetime-local"></div> <div class="time-range"><label for="timeRangeStart">с</label><input id="timeRangeStart" name="timeRangeStart" onblur="requestUpdate()" type="datetime-local"></div>
<div class="time-range"><label for="timeRangeEnd">до</label><input id="timeRangeEnd" name="timeRangeEnd" type="datetime-local"></div> <div class="time-range"><label for="timeRangeEnd">до</label><input id="timeRangeEnd" name="timeRangeEnd" onblur="requestUpdate()" type="datetime-local"></div>
</div> </div>
</section> </section>
@ -112,7 +112,7 @@
<section id="table-area" class="table"> <section id="table-area" class="table">
<h1>Список предупреждений</h1> <h1>Список предупреждений</h1>
<input id="table-search" class="search" type="text" placeholder="Поиск"> <input id="table-search" class="search" type="text" onblur="requestUpdate()" placeholder="Поиск">
<table id="deviceTable"> <table id="deviceTable">
@ -135,12 +135,44 @@
</section> </section>
<div id="count"> <div id="count">Всего результатов: <span id="count-value">{{Count}}</span></div>
<!-- Сюда добавится итоговое количество результатов --> <!-- Сюда добавится итоговое количество результатов -->
</div>
<div id="pagination"> <div id="pagination">
<svg id="left-slider" xmlns="http://www.w3.org/2000/svg" width="11" height="19" fill="none" viewBox="0 0 11 19" onclick="decrementPage()">
<path fill="#000" fill-opacity=".75" d="M0 9.495c0 .273.101.514.315.722l8.92 8.477a.981.981 0 0 0 .73.295c.585 0 1.035-.427 1.035-.995 0-.285-.124-.525-.304-.711L2.508 9.495l8.188-7.789c.18-.186.304-.437.304-.71C11 .425 10.55 0 9.965 0c-.292 0-.54.098-.73.284L.314 8.773A.955.955 0 0 0 0 9.495Z"/>
</svg>
<input id="page-number" type="number" onblur="requestUpdate()" value="1">
<svg id="right-slider" xmlns="http://www.w3.org/2000/svg" width="11" height="19" fill="none" viewBox="0 0 11 19" onclick="incrementPage()">
<path fill="#000" fill-opacity=".75" d="M11 9.495a.967.967 0 0 0-.326-.722L1.766.284A1.062 1.062 0 0 0 1.024 0C.45 0 0 .427 0 .995c0 .274.112.525.292.711l8.189 7.789-8.189 7.788c-.18.186-.292.426-.292.71 0 .57.45.996 1.024.996.292 0 .54-.098.742-.295l8.908-8.477c.213-.208.326-.449.326-.722Z"/>
</svg>
<!-- Сюда будут добавляться ссылки для переключения между страницами --> <!-- Сюда будут добавляться ссылки для переключения между страницами -->
</div> </div>
<script>
var pageNumberInput = document.getElementById('page-number');
var countMax = Math.ceil({{Count}} / 14);
function decrementPage() {
var currentPage = parseInt(pageNumberInput.value, 10);
if (currentPage > 1) {
pageNumberInput.value = currentPage - 1;
requestUpdate();
}
}
function incrementPage() {
var currentPage = parseInt(pageNumberInput.value, 10);
if (currentPage === countMax || currentPage > countMax) {
pageNumberInput.value = countMax;
requestUpdate();
}
if (currentPage < countMax) {
pageNumberInput.value = currentPage + 1;
requestUpdate();
}
}
</script>
<!-- <br> <!-- <br>
<br> <br>
<span style="opacity:50%">Временное ограничение: 100 последних предупреждений</span> --> <span style="opacity:50%">Временное ограничение: 100 последних предупреждений</span> -->
@ -154,7 +186,7 @@
</section> </section>
<script> <script>
const devices = [ let devices = [
{{#each Alarms}} {{#each Alarms}}
{ {
id: "{{this.id}}", id: "{{this.id}}",
@ -188,6 +220,78 @@
}); });
</script> </script>
<script>
function requestUpdate() {
document.getElementById("deviceTable").style.filter = "brightness(0.85)";
const requestData = {
page: parseInt(document.getElementById("page-number").value),
timeRangeStart: document.getElementById("timeRangeStart").value,
timeRangeEnd: document.getElementById("timeRangeEnd").value,
serials: Array.from(
document.querySelectorAll('input[type="checkbox"].device-filter:checked')
).map((checkbox) => checkbox.value),
searchText: document.getElementById("table-search").value,
};
console.log(requestData);
fetch("/getreports", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestData),
})
.then((response) => response.json())
.then((data) => {
console.log(data);
console.log(devices);
devices.splice(0, devices.length);
devices.push(...data.data.map(item => ({
id: item.id,
cmdno: item.cmdno,
number: item.number,
time: item.time,
serial: item.serial,
type: item.type,
geo: item.geo,
plate: item.plate,
})));
console.log(devices);
createTable();
document.getElementById("count-value").innerHTML = data.total;
countMax = Math.ceil(data.total / 14);
var currentPage = parseInt(pageNumberInput.value, 10);
if (currentPage === countMax || currentPage > countMax) {
pageNumberInput.value = countMax;
requestUpdate();
}
document.getElementById("deviceTable").style.filter = "brightness(1)";
})
.catch((error) => {
document.getElementById("dataLoading").style.display = 'none';
h1Element.textContent = 'Ошибка отправки запроса.';
console.error("Ошибка при отправке запроса:", error);
});
};
</script>
<script> <script>
// Скрытие/Показ дополнительных меню аккаунта // Скрытие/Показ дополнительных меню аккаунта
const accountMain = document.getElementById('account-main'); const accountMain = document.getElementById('account-main');
@ -214,41 +318,37 @@
const checkboxes = document.querySelectorAll('.organisation .checkbox-input'); const checkboxes = document.querySelectorAll('.organisation .checkbox-input');
checkboxes.forEach((checkbox) => { checkboxes.forEach((checkbox) => {
applyFilterAndSearch();
checkbox.addEventListener('change', function() { checkbox.addEventListener('change', function() {
applyFilterAndSearch(); const areaDevices = this.parentNode.querySelector('.area-devices');
const devices = this.parentNode.querySelector('.area-devices');
if (this.checked) { if (this.checked) {
devices.style.display = 'block'; areaDevices.style.display = 'block';
// Активируем дочерние чекбоксы // Активируем дочерние чекбоксы
const childCheckboxes = devices.querySelectorAll('.device-filter'); const childCheckboxes = areaDevices.querySelectorAll('.device-filter');
childCheckboxes.forEach((childCheckbox) => { childCheckboxes.forEach((childCheckbox) => {
childCheckbox.checked = true; childCheckbox.checked = true;
applyFilterAndSearch();
}); });
} else { } else {
devices.style.display = 'none'; areaDevices.style.display = 'none';
applyFilterAndSearch();
// Деактивируем дочерние чекбоксы // Деактивируем дочерние чекбоксы
const childCheckboxes = devices.querySelectorAll('.device-filter'); const childCheckboxes = areaDevices.querySelectorAll('.device-filter');
childCheckboxes.forEach((childCheckbox) => { childCheckboxes.forEach((childCheckbox) => {
childCheckbox.checked = false; childCheckbox.checked = false;
applyFilterAndSearch();
}); });
} }
// Деактивируем дочерние чекбоксы, если родительский чекбокс не выбран // Деактивируем дочерние чекбоксы, если родительский чекбокс не выбран
if (!this.checked) { if (!this.checked) {
const childCheckboxes = devices.querySelectorAll('.device-filter'); const childCheckboxes = areaDevices.querySelectorAll('.device-filter');
childCheckboxes.forEach((childCheckbox) => { childCheckboxes.forEach((childCheckbox) => {
childCheckbox.checked = false; childCheckbox.checked = false;
applyFilterAndSearch();
}); });
devices.style.display = 'none'; areaDevices.style.display = 'none';
} }
requestUpdate();
}); });
}); });