From 97554fc6bb56b547f7197aeb6cad1c5698b3fddb Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 11 Sep 2023 08:59:00 +0300 Subject: [PATCH] admin panel, users, bug fixes --- server.js | 318 ++++++++++++++++++++ static/img/keyboard.svg | 11 + static/img/magnifyingglass.svg | 11 + static/img/plus.svg | 11 + static/scripts/index.browser.js | 1 + static/scripts/table-admin.js | 225 ++++++++++++++ static/styles/main.css | 188 +++++++++++- static/templates/admin/index.html | 411 ++++++++++++++++++++++++++ static/templates/admin/user.html | 375 +++++++++++++++++++++++ static/templates/index.html | 3 + static/templates/live.html | 3 + static/templates/reports/index.html | 3 + static/templates/reports/report.html | 3 + static/templates/settings.html | 3 + static/templates/videos/export.html | 3 + static/templates/videos/playback.html | 3 + 16 files changed, 1560 insertions(+), 12 deletions(-) create mode 100644 static/img/keyboard.svg create mode 100644 static/img/magnifyingglass.svg create mode 100644 static/img/plus.svg create mode 100644 static/scripts/index.browser.js create mode 100644 static/scripts/table-admin.js create mode 100644 static/templates/admin/index.html create mode 100644 static/templates/admin/user.html diff --git a/server.js b/server.js index a7a5ee7..230628c 100644 --- a/server.js +++ b/server.js @@ -41,6 +41,7 @@ app.get("/devices/update", update); app.get("/videos", videos); app.get("/videos/export",videoExport); app.get("/settings", settings); +app.get("/admin", adminPanel); const connectionProperties = { host: process.env.SSH_HOST, @@ -1989,6 +1990,31 @@ app.post("/driverdata", async (req, res) => { } }); +app.post("/userdata", async (req, res) => { + const id = req.body.id; + + const pool = new Pool({ + user: DB_User, + host: DB_Host, + database: DB_Name, + password: DB_Password, + port: DB_Port, + }); + const client = await pool.connect(); + + try { + // Выполняем запрос и получаем результат + const query = "SELECT * FROM users WHERE id = $1;"; + const userdata = await client.query(query, [id]); + + // Формирование и отправка ответа + const response = userdata.rows[0]; + res.json(response); + } finally { + client.release(); + } +}); + app.post("/deletedriver", async (req, res) => { const id = req.body.id; @@ -2013,6 +2039,30 @@ app.post("/deletedriver", async (req, res) => { } }); +app.post("/deleteuser", async (req, res) => { + const id = req.body.id; + + const pool = new Pool({ + user: DB_User, + host: DB_Host, + database: DB_Name, + password: DB_Password, + port: DB_Port, + }); + const client = await pool.connect(); + + try { + // Выполняем запрос и получаем результат + const query = "DELETE FROM users WHERE id = $1;"; + const userdata = await client.query(query, [id]); + + // Формирование и отправка ответа + res.send("Data deleted successfully"); + } finally { + client.release(); + } +}); + async function drivers(req, res) { let templateData = { Organisation: "Название организации", @@ -2094,6 +2144,274 @@ function settings(req, res) { res.sendFile(path.join(__dirname, "static/templates/settings.html")); } +async function adminPanel(req, res) { + let templateData = { + Organisation: "Название организации", + User: "Тестовое Имя", + ifDBError: false, + Users: [], + }; + + try { + const pool = new Pool({ + user: DB_User, + host: DB_Host, + database: DB_Name, + password: DB_Password, + port: DB_Port, + }); + const client = await pool.connect(); + const queryUsers = ` + SELECT id, name, surname, email, phone, added + FROM users + ORDER BY id + `; + const usersResult = await client.query(queryUsers); + const allUsers = usersResult.rows; + + templateData.Users = allUsers.map((user) => ({ + id: user.id, + name: user.name, + surname: user.surname, + email: user.email, + phone: user.phone, + added: new Date(user.added).toLocaleDateString('ru-RU', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }), + })); + + // console.log(templateData); + + const source = fs.readFileSync("static/templates/admin/index.html", "utf8"); + const template = handlebars.compile(source); + const resultT = template(templateData); + res.send(resultT); + + client.release(); + } catch (error) { + console.error(error); + templateData.ifDBError = true; + + const source = fs.readFileSync( + "static/templates/admin/index.html", + "utf8" + ); + const template = handlebars.compile(source); + const resultT = template(templateData); + res.send(resultT); + } +} + +// Обработка POST-запроса для добавления пользователя +app.post("/add-user", async (req, res) => { + const { name, surname, email, phone, password } = req.body; + + console.log(name, surname, email, phone, password) + + try { + + const pool = new Pool({ + user: DB_User, + host: DB_Host, + database: DB_Name, + password: DB_Password, + port: DB_Port, + }); + + const client = await pool.connect(); + + const query = ` + INSERT INTO users (name, surname, email, phone, password, added) + VALUES ($1, $2, $3, $4, $5, NOW()) + RETURNING id + `; + + const result = await client.query(query, [name, surname, email, phone, password]); + + // Освобождение клиента + client.release(); + + console.log("Пользователь успешно добавлен"); + res.json({ message: "Пользователь успешно добавлен" }); + } catch (err) { + console.error("Ошибка при вставке данных в базу данных:", err); + res.status(500).json({ error: "Ошибка при добавлении пользователя" }); + } +}); + +app.get('/admin/user/:id', async (req, res) => { + const id = req.params.id; + + let templateData = { + Organisation: "Название организации", + User: "Тестовое Имя", + ifDBError: false, + Id: id, + Name: "", + Surname: "", + Email: "", + Phone: "", + Password: "", + Devices: [], + EditTransport: false, + DeleteTransport: false, + Update: false, + + Groups: [], + }; + + try { + const pool = new Pool({ + user: DB_User, + host: DB_Host, + database: DB_Name, + password: DB_Password, + port: DB_Port, + }); + const client = await pool.connect(); + + const queryRegistrars = ` + SELECT id, serial, lastkeepalive, name, "group", plate, sim, ip, port + FROM registrars + ORDER BY id + `; + const registrarsResult = await client.query(queryRegistrars); + const allRegistrars = registrarsResult.rows; + + const groupedRegistrars = {}; + allRegistrars.forEach((registrar) => { + if (!groupedRegistrars[registrar.group]) { + groupedRegistrars[registrar.group] = []; + } + groupedRegistrars[registrar.group].push({ serial: registrar.serial, checked: false }); + }); + + for (const groupName in groupedRegistrars) { + templateData.Groups.push({ + name: groupName, + serials: groupedRegistrars[groupName], + }); + } + + const query = "SELECT * FROM users WHERE id = $1;"; + const userdata = await client.query(query, [id]); + + // Формирование и отправка ответа + const response = userdata.rows[0]; + + if (response.devices && response.devices.length > 0) { + templateData.Groups.forEach((group) => { + group.serials.forEach((serial) => { + serial.checked = response.devices.includes(serial.serial); + }); + }); + } + + templateData.Name = response.name; + templateData.Surname = response.surname; + templateData.Email = response.email; + templateData.Phone = response.phone; + templateData.Password = response.password; + templateData.Devices = response.devices; + templateData.DeleteTransport = response.deletetransport; + templateData.EditTransport = response.edittransport; + templateData.Update = response.update; + + // console.log(templateData); + + const source = fs.readFileSync("static/templates/admin/user.html", "utf8"); + const template = handlebars.compile(source); + const resultT = template(templateData); + res.send(resultT); + + client.release(); + } catch (error) { + console.error(error); + templateData.ifDBError = true; + + const source = fs.readFileSync( + "static/templates/admin/user.html", + "utf8" + ); + const template = handlebars.compile(source); + const resultT = template(templateData); + res.send(resultT); + } +}); + +app.post("/updateuser/:id", async (req, res) => { + const id = req.params.id; + const pool = new Pool({ + user: DB_User, + host: DB_Host, + database: DB_Name, + password: DB_Password, + port: DB_Port, + }); + const client = await pool.connect(); + + var { + name, + surname, + email, + phone, + password, + EditTransport, + DeleteTransport, + Update, + } = req.body.formData; + + var devices = req.body.devices + + try { + + const query = ` + UPDATE users + SET + name = $2, + surname = $3, + email = $4, + phone = $5, + password = $6, + editTransport = $7, + deleteTransport = $8, + update = $9, + devices = $10 + WHERE id = $1 + RETURNING *; + `; + + const values = [ + id, + name, + surname, + email, + phone, + password, + EditTransport, + DeleteTransport, + Update, + devices, + ]; + + const result = await client.query(query, values); + + const updatedRow = result.rows[0]; + // console.log("Updated row:", updatedRow); + + res.send("Data updated successfully"); + } catch (error) { + console.error("Error updating data:", error); + res.status(500).send("An error occurred while updating data"); + } finally { + client.release(); + } +}); + async function videos(req, res) { let templateData = { Organisation: "Название организации", diff --git a/static/img/keyboard.svg b/static/img/keyboard.svg new file mode 100644 index 0000000..28e6f60 --- /dev/null +++ b/static/img/keyboard.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/static/img/magnifyingglass.svg b/static/img/magnifyingglass.svg new file mode 100644 index 0000000..27f1ede --- /dev/null +++ b/static/img/magnifyingglass.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/static/img/plus.svg b/static/img/plus.svg new file mode 100644 index 0000000..ed7cd8d --- /dev/null +++ b/static/img/plus.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/static/scripts/index.browser.js b/static/scripts/index.browser.js new file mode 100644 index 0000000..5243b55 --- /dev/null +++ b/static/scripts/index.browser.js @@ -0,0 +1 @@ +function renderSA(e,t){var r=create(e,t);if(r.finished)return r.asset;throw new Error("SmartAvatar ERROR: something went wrong.")}function create(e,t){var r=["mp","identicon","monsterid","wavatar","retro","robohash"],a={};switch(!0){case"smart"===t.priority.src1&&t.initials:a.asset=generateEl("initEl",t),a.finished=!0;break;case"smart"===t.priority.src1&&t.icon&&!r.includes(t.icon):a.asset=generateEl("iconEl",t),a.finished=!0;break;case"smart"===t.priority.src1&&t.icon&&r.includes(t.icon):a.asset=generateEl("iconImg",t),a.finished=!0;break;default:a.asset=generateComplexImage(e,t),a.finished=!0}return a}function generateEl(e,t){switch(e){case"iconImg":return generateIconImg(t);case"initEl":return generateInitEl(t);case"iconEl":return generateIconEl(t)}}function generateGravatarIconUrl(e){return"https://www.gravatar.com/avatar?d="+e+"&f=y"}function generateIconImg(e){var t=new Image,r=generateGravatarIconUrl(e.icon);if(t.src=r,e.unstyled||(t.style.cssText=(e.round?"border-radius: 50%;":"")+(e.size?"height:"+e.size+"px;width:"+e.size+"px;":"")+"object-fit:cover;"),t.classList.add("smart-avatar"),e.cssClass){const r=e.cssClass.length;for(let a=0;a"+r+"",a}function generateComplexImage(e,t){var r=parseComplexAssets(t),a=r.asset1,n=r.asset2,s=r.asset3,i=new Image;if(i.src=a.content,t.unstyled||(i.style.cssText=(t.round?"border-radius: 50%;":"")+(t.size?"height:"+t.size+"px;width:"+t.size+"px;":"")+"object-fit:cover;"),i.onerror=function(){handleErrFallback(e,i,n),"el"!==n.type&&(i.onerror=function(){handleErrFallback(e,i,s)})},i.classList.add("smart-avatar"),t.cssClass){const e=t.cssClass.length;for(let r=0;r'}export default function smartAvatar(e,t){var r={};if(arguments[1]||(t={}),!arguments.length)throw new Error("SmartAvatar ERROR: missing 'element' argument.");if("dispose"!==arguments[1]){if(arguments[1]&&"object"!=typeof arguments[1])throw new Error("SmartAvatar ERROR: 'options' argument must be an object.");var a=function(e){return e instanceof Element||e instanceof HTMLDocument};if(!a(e)){var n=typeof e;throw new Error("SmartAvatar ERROR: cannot append asset to "+n+".")}r.parent=e,r.priority=t.priority?parsePriority(t.priority):{src1:"gravatar",src2:"src",src3:"smart"},r.timestamp="boolean"==typeof t.timestamp&&t.timestamp,r.alt=t.alt?t.alt.toString():null,r.icon=t.icon?t.icon.toString():"smartfox",r.size=t.size?parseImgRes(t.size.toString()):null,r.round=!0===t.round,r.initials=t.initials?parseInitials(t.initials.toString()):null,r.color=t.color?parseColor(t.color.toString()):null,r.textColor=t.textColor?parseColor(t.textColor.toString()):null,r.colorScheme=t.colorScheme?parseColorScheme(t.colorScheme):null,r.cssClass=t.cssClass?parseCssClass(t.cssClass.toString()):null,r.email=t.email?parseEmail(t.email.toString()):null,r.hash=!!t.hash&&t.hash.toString()||(r.email?md5(r.email):null),r.protocol=t.protocol?parseProtocol(t.protocol.toString()):"secure",r.format=t.format?parseImgFormat(t.format.toString()):"jpg",r.resolution=t.resolution?parseImgRes(t.resolution.toString()):"80",r.src=t.src?t.src.toString():null,r.unstyled=!0===t.unstyled,!r.unstyled&&t.setDefaults&&(r.color=r.color||"#AAA",r.size=r.size||"48",r.textColor=r.textColor||"#FFF"),r.gravatarUrl=r.hash?generateGravatarUrl(r):"";var s=renderSA(e,r);e.appendChild(s)}else{const t=e.querySelectorAll(".smart-avatar");for(let r=0;r0)throw new Error("SmartAvatar ERROR: could not dispose SmartAvatar nodes."+e.querySelectorAll(".smart-avatar").length+"left.")}}function generateGravatarUrl(e){var t=parseProtocol(e.protocol),r=parseImgFormat(e.format),a="?d=404&"+parseGravatarURIRes(e.resolution);return t+"www.gravatar.com/avatar/"+e.hash+"."+r+a}function md5cycle(e,t){var r=e[0],a=e[1],n=e[2],s=e[3];r=ff(r,a,n,s,t[0],7,-680876936),s=ff(s,r,a,n,t[1],12,-389564586),n=ff(n,s,r,a,t[2],17,606105819),a=ff(a,n,s,r,t[3],22,-1044525330),r=ff(r,a,n,s,t[4],7,-176418897),s=ff(s,r,a,n,t[5],12,1200080426),n=ff(n,s,r,a,t[6],17,-1473231341),a=ff(a,n,s,r,t[7],22,-45705983),r=ff(r,a,n,s,t[8],7,1770035416),s=ff(s,r,a,n,t[9],12,-1958414417),n=ff(n,s,r,a,t[10],17,-42063),a=ff(a,n,s,r,t[11],22,-1990404162),r=ff(r,a,n,s,t[12],7,1804603682),s=ff(s,r,a,n,t[13],12,-40341101),n=ff(n,s,r,a,t[14],17,-1502002290),r=gg(r,a=ff(a,n,s,r,t[15],22,1236535329),n,s,t[1],5,-165796510),s=gg(s,r,a,n,t[6],9,-1069501632),n=gg(n,s,r,a,t[11],14,643717713),a=gg(a,n,s,r,t[0],20,-373897302),r=gg(r,a,n,s,t[5],5,-701558691),s=gg(s,r,a,n,t[10],9,38016083),n=gg(n,s,r,a,t[15],14,-660478335),a=gg(a,n,s,r,t[4],20,-405537848),r=gg(r,a,n,s,t[9],5,568446438),s=gg(s,r,a,n,t[14],9,-1019803690),n=gg(n,s,r,a,t[3],14,-187363961),a=gg(a,n,s,r,t[8],20,1163531501),r=gg(r,a,n,s,t[13],5,-1444681467),s=gg(s,r,a,n,t[2],9,-51403784),n=gg(n,s,r,a,t[7],14,1735328473),r=hh(r,a=gg(a,n,s,r,t[12],20,-1926607734),n,s,t[5],4,-378558),s=hh(s,r,a,n,t[8],11,-2022574463),n=hh(n,s,r,a,t[11],16,1839030562),a=hh(a,n,s,r,t[14],23,-35309556),r=hh(r,a,n,s,t[1],4,-1530992060),s=hh(s,r,a,n,t[4],11,1272893353),n=hh(n,s,r,a,t[7],16,-155497632),a=hh(a,n,s,r,t[10],23,-1094730640),r=hh(r,a,n,s,t[13],4,681279174),s=hh(s,r,a,n,t[0],11,-358537222),n=hh(n,s,r,a,t[3],16,-722521979),a=hh(a,n,s,r,t[6],23,76029189),r=hh(r,a,n,s,t[9],4,-640364487),s=hh(s,r,a,n,t[12],11,-421815835),n=hh(n,s,r,a,t[15],16,530742520),r=ii(r,a=hh(a,n,s,r,t[2],23,-995338651),n,s,t[0],6,-198630844),s=ii(s,r,a,n,t[7],10,1126891415),n=ii(n,s,r,a,t[14],15,-1416354905),a=ii(a,n,s,r,t[5],21,-57434055),r=ii(r,a,n,s,t[12],6,1700485571),s=ii(s,r,a,n,t[3],10,-1894986606),n=ii(n,s,r,a,t[10],15,-1051523),a=ii(a,n,s,r,t[1],21,-2054922799),r=ii(r,a,n,s,t[8],6,1873313359),s=ii(s,r,a,n,t[15],10,-30611744),n=ii(n,s,r,a,t[6],15,-1560198380),a=ii(a,n,s,r,t[13],21,1309151649),r=ii(r,a,n,s,t[4],6,-145523070),s=ii(s,r,a,n,t[11],10,-1120210379),n=ii(n,s,r,a,t[2],15,718787259),a=ii(a,n,s,r,t[9],21,-343485551),e[0]=add32(r,e[0]),e[1]=add32(a,e[1]),e[2]=add32(n,e[2]),e[3]=add32(s,e[3])}function cmn(e,t,r,a,n,s){return t=add32(add32(t,e),add32(a,s)),add32(t<>>32-n,r)}function ff(e,t,r,a,n,s,i){return cmn(t&r|~t&a,e,t,n,s,i)}function gg(e,t,r,a,n,s,i){return cmn(t&a|r&~a,e,t,n,s,i)}function hh(e,t,r,a,n,s,i){return cmn(t^r^a,e,t,n,s,i)}function ii(e,t,r,a,n,s,i){return cmn(r^(t|~a),e,t,n,s,i)}function md51(e){var t,r=e.length,a=[1732584193,-271733879,-1732584194,271733878];for(t=64;t<=e.length;t+=64)md5cycle(a,md5blk(e.substring(t-64,t)));e=e.substring(t-64);var n=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(t=0;t>2]|=e.charCodeAt(t)<<(t%4<<3);if(n[t>>2]|=128<<(t%4<<3),t>55)for(md5cycle(a,n),t=0;t<16;t++)n[t]=0;return n[14]=8*r,md5cycle(a,n),a}function md5blk(e){var t,r=[];for(t=0;t<64;t+=4)r[t>>2]=e.charCodeAt(t)+(e.charCodeAt(t+1)<<8)+(e.charCodeAt(t+2)<<16)+(e.charCodeAt(t+3)<<24);return r}var hex_chr="0123456789abcdef".split("");function rhex(e){for(var t="",r=0;r<4;r++)t+=hex_chr[e>>8*r+4&15]+hex_chr[e>>8*r&15];return t}function hex(e){for(var t=0;t1&&-1!=e.indexOf(" ")){var r=e.split(" ");t=r[0][0]+r[1][0]}else t=e.length>1?e[0]+e[1]:e;return t.toUpperCase()}function parsePriority(e){var t=["gravatar","src","smart"],r={};for(let a=0;a { + const table = document.getElementById("adminTable"); + const tbody = table.querySelector("tbody"); + // Очищаем таблицу + tbody.innerHTML = ""; + + // Добавляем строки таблицы + const startIndex = (currentPage - 1) * rowsPerPage; + const endIndex = startIndex + rowsPerPage; + const usersToDisplay = filteredUsers.slice(startIndex, endIndex); + + usersToDisplay.forEach((user) => { + const row = document.createElement("tr"); + + // Добавляем ячейку с данными + const dataCell = document.createElement("td"); + dataCell.classList.add("user-data"); + + // Создаем контейнер для аватара и имени + const avatarContainer = document.createElement("div"); + avatarContainer.classList.add("avatar-container"); + avatarContainer.style.display = "inline-block"; + + // Добавляем аватар в контейнер + const avatar = document.createElement("div"); + avatar.textContent = user.name[0] + user.surname[0]; // Инициалы + avatar.classList.add("avatar"); + avatar.style.backgroundColor = getRandomColor(); // Добавляем случайный цвет фона (функция getRandomColor определена ниже) + avatar.style.color = "#fff"; // Устанавливаем белый цвет текста на фоне + avatar.style.borderRadius = "50%"; // Делаем аватар круглым + avatar.style.width = "45px"; // Устанавливаем ширину + avatar.style.height = "43px"; // Устанавливаем высоту + avatar.style.display = "flex"; // Делаем аватар flex-элементом + avatar.style.justifyContent = "center"; // Выравниваем содержимое по центру + avatar.style.alignItems = "center"; // Выравниваем содержимое по центру + avatar.style.fontSize = "17px"; // Устанавливаем размер шрифта + avatar.style.paddingTop = "2px"; // Устанавливаем отступ сверху + avatar.style.cursor = "default"; // Устанавливаем курсор в виде стрелки + + // Добавляем аватар в контейнер + avatarContainer.appendChild(avatar); + + // Добавляем имя в контейнер + const name = document.createElement("div"); + name.textContent = user.name + " " + user.surname; + name.classList.add("user-name"); + name.style.display = "inline-block"; + name.style.marginLeft = "14px"; + + // Добавляем контейнер с аватаром и именем в ячейку + dataCell.appendChild(avatarContainer); + dataCell.appendChild(name); + + // Добавляем ячейку с данными в строку + row.appendChild(dataCell); + + // Добавляем ячейки с данными + // const name = document.createElement("td"); + // name.textContent = user.name + " " + user.surname; + // row.appendChild(name); + const ID = document.createElement("td"); + ID.textContent = user.id; + row.appendChild(ID); + const mail = document.createElement("td"); + mail.textContent = user.email; + row.appendChild(mail); + const phone = document.createElement("td"); + phone.textContent = user.phone; + row.appendChild(phone); + const date = document.createElement("td"); + date.textContent = user.date; + row.appendChild(date); + + const optionsCell = document.createElement("td"); + optionsCell.setAttribute("class", "optionsCell"); + const trashButton = document.createElement("button"); + trashButton.setAttribute("class", "trash"); + trashButton.setAttribute("onclick", `deleteUser(${user.id})`); + trashButton.value = `delete-user-${user.id}`; + trashButton.id = `delete-user-${user.id}`; + const optionsButton = document.createElement("button"); + optionsButton.setAttribute("class", "options"); + optionsButton.setAttribute("onclick", `location.href = '/admin/user/${user.id}';`); + + optionsCell.appendChild(optionsButton); + optionsCell.appendChild(trashButton); + + row.appendChild(optionsCell); + tbody.appendChild(row); + }); +}; + +function getRandomColor() { + const letters = "0123456789ABCDEF"; + let color = "#"; + for (let i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; +}; + +window.addEventListener("resize", function (event) { + tableHeight = document.getElementById("content").offsetHeight; + rowHeight = 60; + rowsPerPage = Math.floor(tableHeight / rowHeight) - 3; + createTable(); + createPagination(); +}); + +const createPagination = () => { + const count = document.getElementById("count"); + count.textContent = `Всего результатов: ${filteredUsers.length}`; + + const pagination = document.getElementById("pagination"); + pagination.innerHTML = ""; + + const pageCount = Math.ceil(filteredUsers.length / rowsPerPage); + for (let i = 1; i <= pageCount; i++) { + const pageLink = document.createElement("a"); + pageLink.href = "#"; + if (i === currentPage) { + // document.querySelector("#device-all").checked = false; + 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(); + + filteredUsers = users.filter((user) => { + const searchString = + `${user.id} ${user.name} ${user.surname} ${user.email} ${user.phone} ${user.date}`.toLowerCase(); + const matchSearch = !searchValue || searchString.includes(searchValue); + + // Фильтр по временному диапазону + let matchTimeRange = true; + if (timeRangeStart) { + const startTimestamp = new Date(timeRangeStart).getTime(); + const deviceTimestamp = parseTableTime(user.time); // Преобразование времени из таблицы + matchTimeRange = startTimestamp <= deviceTimestamp; + } + if (timeRangeEnd) { + const endTimestamp = new Date(timeRangeEnd).getTime(); + const deviceTimestamp = parseTableTime(user.time); // Преобразование времени из таблицы + matchTimeRange = matchTimeRange && deviceTimestamp <= endTimestamp; + } + + return 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); +}); + + +createTable(); +createPagination(); diff --git a/static/styles/main.css b/static/styles/main.css index 98cbcda..026c327 100644 --- a/static/styles/main.css +++ b/static/styles/main.css @@ -173,6 +173,11 @@ header h2 span { bottom: 27px; } +.admin-panel { + position: absolute; + bottom: 79px; +} + .main { /* position: absolute; */ position: relative; @@ -430,24 +435,54 @@ header h2 span { width: fit-content; } +.permissions { + display: flex; +} + +.permission-group:first-child { + margin-left: 44px; +} + +.permission-group { + display: inline-block; + height: fit-content; + width: 160px; +} + +.permission-group h2 { + margin: 20px 0 5px 0; + font-size: 20px; + font-weight: 500; +} + +.permission-group .checkbox-label { + margin-top: 10px; + float: left; + font-size: 15px; +} + .organisation label, -#parameters label { +#parameters label, +.permission-group label { color: rgba(0, 0, 0, 0.5); cursor: pointer !important; } .organisation .checkbox-label:hover, -.add-new .checkbox-label:hover { +.add-new .checkbox-label:hover, +.permission-group .chart-label:hover { color: rgba(0, 0, 0, 0.7) !important; } .organisation .checkbox-input:checked + .checkbox-label, -.add-new .checkbox-input:checked + .checkbox-label { +.add-new .checkbox-input:checked + .checkbox-label, +.permission-group .checkbox-input:checked + .checkbox-label { color: rgba(0, 0, 0, 0.9) !important; } .organisation .checkmark, -.add-new .checkmark { +.add-new .checkmark, +.permission-group .checkmark { display: inline-block; width: 18px; height: 18px; @@ -458,12 +493,14 @@ header h2 span { } .organisation .checkbox-input:hover + .checkbox-label .checkmark, -.add-new .checkbox-input:hover + .checkbox-label .checkmar { +.add-new .checkbox-input:hover + .checkbox-label .checkmar, +.permission-group .checkbox-input:hover + .checkbox-label .checkmark { filter: brightness(0.95) !important; } .organisation .checkbox-input:checked + .checkbox-label .checkmark, -.add-new .checkbox-input:checked + .checkbox-label .checkmark { +.add-new .checkbox-input:checked + .checkbox-label .checkmark, +.permission-group .checkbox-input:checked + .checkbox-label .checkmark { background: url(../img/checkbox-check.svg); } @@ -596,7 +633,8 @@ header h2 span { width: fit-content; } -.table .search { +.table .search, +.admin-search { display: inline-block; float: right; margin: 12px 42px 12px 0; @@ -617,6 +655,20 @@ header h2 span { transition: 0.2s; } +.admin-search { + float: left; + margin: 32px 0 33px 0; + display: block; + border-radius: 7px; + width: 550px; + height: 40px; + background-size: 18px 18px; + background-position: 12px 12px; + padding-left: 40px; + font-size: 18px; + background-image: url("../img/magnifyingglass.svg"); +} + .search:focus, .search:hover { border: 1px solid rgba(0, 0, 0, 0.3); @@ -627,6 +679,42 @@ header h2 span { opacity: 100%; } +.add-user { + background-image: url("../img/plus.svg") !important; + background-size: 16px 16px !important; + background-position: 10px 13px !important; + background-repeat: no-repeat !important; + padding-left: 36px !important; + height: 44px !important; + outline: none !important; + float: right !important; + border-radius: 7px !important; + margin: 32px 0 33px 0 !important; + font-size: 16px !important; +} + +.add-user-form { + position: relative; + background-color: white; + border-radius: 20px; + width: 565px !important; + padding: 33px !important; +} + +.add-user-form h1 { + margin: 0; + font-size: 30px; + color: rgba(0, 0, 0, 0.70); + margin-bottom: 10px; +} + +.add-user-form #close-form-btn { + top: 37px !important; + right: 33px !important; + width: 25px !important; + height: 25px !important; +} + table { border-collapse: collapse; width: 100%; @@ -679,7 +767,8 @@ td { background: url(../img/checkbox-table-check.svg); } -.table .trash { +.table .trash, +#adminTable .trash { height: 22px; width: 19px; background: no-repeat url(../img/trash.svg); @@ -689,7 +778,8 @@ td { float: right; } -.table .options { +.table .options, +#adminTable .options { height: 19px; width: 22px; margin-top: 3px; @@ -1066,9 +1156,17 @@ tr:nth-child(even) { background-color: #0000000a; } +#edit-user-form { + width: 550px; + /* float: right; */ + margin: 30px 0 0 44px; +} + .new-parameters label, .update-info label, -.input-name { +.input-name, +.add-user-form label, +#edit-user-form label { display: inline-block; width: 100%; font-weight: 400; @@ -1080,9 +1178,16 @@ tr:nth-child(even) { cursor: default; } +.add-user-form label { + margin-top: 16px; + color: rgba(0, 0, 0, 0.75); +} + .new-parameters input, .new-parameters select, -.update-info input { +.update-info input, +.add-user-form input, +#edit-user-form input { margin-top: 6px; margin-bottom: 10px; display: inline-block; @@ -1101,9 +1206,22 @@ tr:nth-child(even) { transition: 0.2s; } +.add-user-form input { + width: 540px; + height: 25px; +} + +#edit-user-form input { + width: 500px; +} + .new-parameters input:hover, .new-parameters select:hover, .new-parameters input:focus, +.add-user-form input:focus, +.add-user-form input:hover, +#edit-user-form input:hover, +#edit-user-form input:focus, .update-info input:hover, .update-info input:focus { border: 1px solid rgba(0, 0, 0, 0.3); @@ -1119,11 +1237,14 @@ tr:nth-child(even) { background-position-x: calc(100% - 14px); } -.new-parameters input::placeholder { +.new-parameters input::placeholder, +.add-user-form input::placeholder, +#edit-user-form input::placeholder { color: rgba(0, 0, 0, 0.25); } .new-parameters button, +.add-user-form button, .update-info button, .button-purple { padding: 10px 16px; @@ -1140,10 +1261,14 @@ tr:nth-child(even) { } .new-parameters button, +.add-user-form button, .update-info button { float: right; margin-bottom: 50px; } +.add-user-form button { + margin: 0; +} .update-info button { width: 100%; @@ -1152,6 +1277,7 @@ tr:nth-child(even) { .new-parameters button:hover, .update-info button:hover, +.add-user-form button:hover, .button-purple:hover { filter: brightness(0.9); } @@ -2237,6 +2363,44 @@ input[type="time"]:focus { border: 1px solid rgba(0, 0, 0, 0.30); } +#adminTable { + border-collapse: collapse; + overflow: hidden; + box-shadow: 0 0 0 2px #F5F5FA; + border-radius: 20px; + background-color: white; + -moz-border-radius: 20px; + -webkit-border-radius: 20px; +} + +#adminTable th { + background-color: white !important; + color: rgba(0, 0, 0, 0.80); + font-size: 24px; + font-weight: 600; + padding: 18px 18px 12px 28px; + border: 0; +} + +#adminTable td { + color: rgba(0, 0, 0, 0.60); + font-size: 18px; + padding: 14px 18px 14px 28px; + border-bottom: 0; +} + +.name .user-id { + padding: 0 11px; + color: rgba(0, 0, 0, 0.60); + font-size: 15px !important; + font-weight: 500; + border-radius: 20px; + border: 1px solid rgba(0, 0, 0, 0.10); + background: rgba(0, 0, 0, 0.05); + margin-left: 15px; + height: 30px !important; +} + @media (max-width: 1950px) { /* при разрешении монитора до 1950 пикселей */ diff --git a/static/templates/admin/index.html b/static/templates/admin/index.html new file mode 100644 index 0000000..3cb6477 --- /dev/null +++ b/static/templates/admin/index.html @@ -0,0 +1,411 @@ + + + + + + + Панель управления + + + + +
+

Аргус

+

/ {{Organisation}}

+
+ + + + + + + + +
+ {{#if ifDBError}} +
+
+
+

Ошибка


+ Не удалось получить данные из БД + +
+
+ {{/if}} + + + + +
+ Панель управления +
+ + +
+
+ + + + + + + + + + + + + + + + + + +
ИмяIDПочтаНомер телефонаДата добавления
+ +
+ +
+ + +
+
+
+
+ +
+

Добавить пользователя

+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/templates/admin/user.html b/static/templates/admin/user.html new file mode 100644 index 0000000..dc1bf5a --- /dev/null +++ b/static/templates/admin/user.html @@ -0,0 +1,375 @@ + + + + + + + {{Name}} {{Surname}} + + + + +
+

Аргус

+

/ {{Organisation}}

+
+ + + + + + + + +
+ {{#if ifDBError}} +
+
+
+

Ошибка


+ Не удалось получить данные из БД + +
+
+ {{/if}} + + +
+ {{Name}} {{Surname}} ID {{Id}} +
+ +
+
+ +
+ +
+

Доступ

+ +
    + {{#each Groups}} +
  • +
      + {{#each serials}} + {{#if this.checked}} +
    • + {{else}} +
    • + {{/if}} + {{/each}} +
    +
  • + {{/each}} +
+ +
+ +
+

Данные пользователя

+

Кнопка "Обновить" также распространяется на доступ к устройствам.

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ +

Права пользователя

+ +
+ +
+

Устройства

+ {{#if EditTransport}} + + {{else}} + + {{/if}} + {{#if DeleteTransport}} + + {{else}} + + {{/if}} +
+ +
+

Обновление ПО

+ {{#if Update}} + + {{else}} + + {{/if}} +
+ +
+ +
+ + + + + +
+ + + + + + + +
+ +
+
+
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/static/templates/index.html b/static/templates/index.html index a20c6a1..fdacd82 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -45,6 +45,9 @@
Записи
+ +
Админка
+
Настройки
diff --git a/static/templates/live.html b/static/templates/live.html index 12c8e75..289dd06 100644 --- a/static/templates/live.html +++ b/static/templates/live.html @@ -47,6 +47,9 @@
Записи
+ +
Админка
+
Настройки
diff --git a/static/templates/reports/index.html b/static/templates/reports/index.html index a52f13b..6ca2494 100644 --- a/static/templates/reports/index.html +++ b/static/templates/reports/index.html @@ -43,6 +43,9 @@
Записи
+ +
Админка
+
Настройки
diff --git a/static/templates/reports/report.html b/static/templates/reports/report.html index d63bb2c..17806cb 100644 --- a/static/templates/reports/report.html +++ b/static/templates/reports/report.html @@ -45,6 +45,9 @@
Записи
+ +
Админка
+
Настройки
diff --git a/static/templates/settings.html b/static/templates/settings.html index 590c09d..a2ddf1e 100644 --- a/static/templates/settings.html +++ b/static/templates/settings.html @@ -43,6 +43,9 @@
Записи
+ +
Админка
+
Настройки
diff --git a/static/templates/videos/export.html b/static/templates/videos/export.html index 3f3f937..b9821bb 100644 --- a/static/templates/videos/export.html +++ b/static/templates/videos/export.html @@ -47,6 +47,9 @@
Записи
+ +
Админка
+
Настройки
diff --git a/static/templates/videos/playback.html b/static/templates/videos/playback.html index 585880a..bd84b6e 100644 --- a/static/templates/videos/playback.html +++ b/static/templates/videos/playback.html @@ -47,6 +47,9 @@
Записи
+ +
Админка
+
Настройки