admin panel, users, bug fixes
This commit is contained in:
parent
8ccd2f2a5c
commit
97554fc6bb
318
server.js
318
server.js
@ -41,6 +41,7 @@ app.get("/devices/update", update);
|
|||||||
app.get("/videos", videos);
|
app.get("/videos", videos);
|
||||||
app.get("/videos/export",videoExport);
|
app.get("/videos/export",videoExport);
|
||||||
app.get("/settings", settings);
|
app.get("/settings", settings);
|
||||||
|
app.get("/admin", adminPanel);
|
||||||
|
|
||||||
const connectionProperties = {
|
const connectionProperties = {
|
||||||
host: process.env.SSH_HOST,
|
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) => {
|
app.post("/deletedriver", async (req, res) => {
|
||||||
const id = req.body.id;
|
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) {
|
async function drivers(req, res) {
|
||||||
let templateData = {
|
let templateData = {
|
||||||
Organisation: "Название организации",
|
Organisation: "Название организации",
|
||||||
@ -2094,6 +2144,274 @@ function settings(req, res) {
|
|||||||
res.sendFile(path.join(__dirname, "static/templates/settings.html"));
|
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) {
|
async function videos(req, res) {
|
||||||
let templateData = {
|
let templateData = {
|
||||||
Organisation: "Название организации",
|
Organisation: "Название организации",
|
||||||
|
11
static/img/keyboard.svg
Normal file
11
static/img/keyboard.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Generator: Apple Native CoreSVG 175.5-->
|
||||||
|
<!DOCTYPE svg
|
||||||
|
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24.5801" height="15.4395">
|
||||||
|
<g>
|
||||||
|
<rect height="15.4395" opacity="0" width="24.5801" x="0" y="0"/>
|
||||||
|
<path d="M3.06641 15.4297L21.5137 15.4297C23.5449 15.4297 24.5801 14.4141 24.5801 12.4023L24.5801 3.01758C24.5801 1.01562 23.5449 0 21.5137 0L3.06641 0C1.01562 0 0 1.00586 0 3.01758L0 12.4023C0 14.4141 1.01562 15.4297 3.06641 15.4297ZM3.08594 13.8574C2.10938 13.8574 1.57227 13.3301 1.57227 12.3145L1.57227 3.10547C1.57227 2.08984 2.10938 1.57227 3.08594 1.57227L21.4844 1.57227C22.4512 1.57227 23.0078 2.08984 23.0078 3.10547L23.0078 12.3145C23.0078 13.3301 22.4512 13.8574 21.4844 13.8574ZM3.96484 5.43945L4.86328 5.43945C5.13672 5.43945 5.30273 5.27344 5.30273 5.00977L5.30273 4.11133C5.30273 3.83789 5.13672 3.67188 4.86328 3.67188L3.96484 3.67188C3.70117 3.67188 3.53516 3.83789 3.53516 4.11133L3.53516 5.00977C3.53516 5.27344 3.70117 5.43945 3.96484 5.43945ZM7.10938 5.43945L8.00781 5.43945C8.27148 5.43945 8.4375 5.27344 8.4375 5.00977L8.4375 4.11133C8.4375 3.83789 8.27148 3.67188 8.00781 3.67188L7.10938 3.67188C6.83594 3.67188 6.66992 3.83789 6.66992 4.11133L6.66992 5.00977C6.66992 5.27344 6.83594 5.43945 7.10938 5.43945ZM10.2539 5.43945L11.1426 5.43945C11.416 5.43945 11.582 5.27344 11.582 5.00977L11.582 4.11133C11.582 3.83789 11.416 3.67188 11.1426 3.67188L10.2539 3.67188C9.98047 3.67188 9.81445 3.83789 9.81445 4.11133L9.81445 5.00977C9.81445 5.27344 9.98047 5.43945 10.2539 5.43945ZM13.3887 5.43945L14.2871 5.43945C14.5605 5.43945 14.7266 5.27344 14.7266 5.00977L14.7266 4.11133C14.7266 3.83789 14.5605 3.67188 14.2871 3.67188L13.3887 3.67188C13.125 3.67188 12.959 3.83789 12.959 4.11133L12.959 5.00977C12.959 5.27344 13.125 5.43945 13.3887 5.43945ZM16.5332 5.43945L17.4316 5.43945C17.6953 5.43945 17.8613 5.27344 17.8613 5.00977L17.8613 4.11133C17.8613 3.83789 17.6953 3.67188 17.4316 3.67188L16.5332 3.67188C16.2598 3.67188 16.0938 3.83789 16.0938 4.11133L16.0938 5.00977C16.0938 5.27344 16.2598 5.43945 16.5332 5.43945ZM19.6777 5.43945L20.5664 5.43945C20.8398 5.43945 21.0059 5.27344 21.0059 5.00977L21.0059 4.11133C21.0059 3.83789 20.8398 3.67188 20.5664 3.67188L19.6777 3.67188C19.4043 3.67188 19.2383 3.83789 19.2383 4.11133L19.2383 5.00977C19.2383 5.27344 19.4043 5.43945 19.6777 5.43945ZM3.96484 8.58398L4.86328 8.58398C5.13672 8.58398 5.30273 8.41797 5.30273 8.14453L5.30273 7.25586C5.30273 6.98242 5.13672 6.81641 4.86328 6.81641L3.96484 6.81641C3.70117 6.81641 3.53516 6.98242 3.53516 7.25586L3.53516 8.14453C3.53516 8.41797 3.70117 8.58398 3.96484 8.58398ZM7.10938 8.58398L8.00781 8.58398C8.27148 8.58398 8.4375 8.41797 8.4375 8.14453L8.4375 7.25586C8.4375 6.98242 8.27148 6.81641 8.00781 6.81641L7.10938 6.81641C6.83594 6.81641 6.66992 6.98242 6.66992 7.25586L6.66992 8.14453C6.66992 8.41797 6.83594 8.58398 7.10938 8.58398ZM10.2539 8.58398L11.1426 8.58398C11.416 8.58398 11.582 8.41797 11.582 8.14453L11.582 7.25586C11.582 6.98242 11.416 6.81641 11.1426 6.81641L10.2539 6.81641C9.98047 6.81641 9.81445 6.98242 9.81445 7.25586L9.81445 8.14453C9.81445 8.41797 9.98047 8.58398 10.2539 8.58398ZM13.3887 8.58398L14.2871 8.58398C14.5605 8.58398 14.7266 8.41797 14.7266 8.14453L14.7266 7.25586C14.7266 6.98242 14.5605 6.81641 14.2871 6.81641L13.3887 6.81641C13.125 6.81641 12.959 6.98242 12.959 7.25586L12.959 8.14453C12.959 8.41797 13.125 8.58398 13.3887 8.58398ZM16.5332 8.58398L17.4316 8.58398C17.6953 8.58398 17.8613 8.41797 17.8613 8.14453L17.8613 7.25586C17.8613 6.98242 17.6953 6.81641 17.4316 6.81641L16.5332 6.81641C16.2598 6.81641 16.0938 6.98242 16.0938 7.25586L16.0938 8.14453C16.0938 8.41797 16.2598 8.58398 16.5332 8.58398ZM19.6777 8.58398L20.5664 8.58398C20.8398 8.58398 21.0059 8.41797 21.0059 8.14453L21.0059 7.25586C21.0059 6.98242 20.8398 6.81641 20.5664 6.81641L19.6777 6.81641C19.4043 6.81641 19.2383 6.98242 19.2383 7.25586L19.2383 8.14453C19.2383 8.41797 19.4043 8.58398 19.6777 8.58398ZM3.96484 11.7285L4.86328 11.7285C5.13672 11.7285 5.30273 11.5625 5.30273 11.2891L5.30273 10.3906C5.30273 10.127 5.13672 9.96094 4.86328 9.96094L3.96484 9.96094C3.70117 9.96094 3.53516 10.127 3.53516 10.3906L3.53516 11.2891C3.53516 11.5625 3.70117 11.7285 3.96484 11.7285ZM7.20703 11.7285L17.334 11.7285C17.666 11.7285 17.8613 11.5332 17.8613 11.1914L17.8613 10.4883C17.8613 10.1562 17.666 9.96094 17.334 9.96094L7.20703 9.96094C6.86523 9.96094 6.66992 10.1562 6.66992 10.4883L6.66992 11.1914C6.66992 11.5332 6.86523 11.7285 7.20703 11.7285ZM19.6777 11.7285L20.5664 11.7285C20.8398 11.7285 21.0059 11.5625 21.0059 11.2891L21.0059 10.3906C21.0059 10.127 20.8398 9.96094 20.5664 9.96094L19.6777 9.96094C19.4043 9.96094 19.2383 10.127 19.2383 10.3906L19.2383 11.2891C19.2383 11.5625 19.4043 11.7285 19.6777 11.7285Z" fill="#000000" fill-opacity="0.85"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.9 KiB |
11
static/img/magnifyingglass.svg
Normal file
11
static/img/magnifyingglass.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Generator: Apple Native CoreSVG 175.5-->
|
||||||
|
<!DOCTYPE svg
|
||||||
|
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="19.082" height="19.2676">
|
||||||
|
<g>
|
||||||
|
<rect height="19.2676" opacity="0" width="19.082" x="0" y="0"/>
|
||||||
|
<path d="M0 7.79297C0 12.0898 3.49609 15.5859 7.79297 15.5859C9.49219 15.5859 11.0449 15.0391 12.3242 14.1211L17.1289 18.9355C17.3535 19.1602 17.6465 19.2676 17.959 19.2676C18.623 19.2676 19.082 18.7695 19.082 18.1152C19.082 17.8027 18.9648 17.5195 18.7598 17.3145L13.9844 12.5098C14.9902 11.2012 15.5859 9.57031 15.5859 7.79297C15.5859 3.49609 12.0898 0 7.79297 0C3.49609 0 0 3.49609 0 7.79297ZM1.66992 7.79297C1.66992 4.41406 4.41406 1.66992 7.79297 1.66992C11.1719 1.66992 13.916 4.41406 13.916 7.79297C13.916 11.1719 11.1719 13.916 7.79297 13.916C4.41406 13.916 1.66992 11.1719 1.66992 7.79297Z" fill="#000000" fill-opacity="0.75"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
11
static/img/plus.svg
Normal file
11
static/img/plus.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Generator: Apple Native CoreSVG 175.5-->
|
||||||
|
<!DOCTYPE svg
|
||||||
|
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16.1133" height="16.123">
|
||||||
|
<g>
|
||||||
|
<rect height="16.123" opacity="0" width="16.1133" x="0" y="0"/>
|
||||||
|
<path d="M0 8.05664C0 8.53516 0.400391 8.92578 0.869141 8.92578L7.1875 8.92578L7.1875 15.2441C7.1875 15.7129 7.57812 16.1133 8.05664 16.1133C8.53516 16.1133 8.93555 15.7129 8.93555 15.2441L8.93555 8.92578L15.2441 8.92578C15.7129 8.92578 16.1133 8.53516 16.1133 8.05664C16.1133 7.57812 15.7129 7.17773 15.2441 7.17773L8.93555 7.17773L8.93555 0.869141C8.93555 0.400391 8.53516 0 8.05664 0C7.57812 0 7.1875 0.400391 7.1875 0.869141L7.1875 7.17773L0.869141 7.17773C0.400391 7.17773 0 7.57812 0 8.05664Z" fill="#ffffff" fill-opacity="0.85"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 943 B |
1
static/scripts/index.browser.js
Normal file
1
static/scripts/index.browser.js
Normal file
File diff suppressed because one or more lines are too long
225
static/scripts/table-admin.js
Normal file
225
static/scripts/table-admin.js
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
// Получаем высоту таблицы и определяем, сколько строк помещается на странице
|
||||||
|
let currentPage = 1;
|
||||||
|
let tableHeight = document.getElementById("content").offsetHeight;
|
||||||
|
let rowHeight = 60;
|
||||||
|
let rowsPerPage = Math.floor(tableHeight / rowHeight) - 3;
|
||||||
|
let filteredUsers = [...users];
|
||||||
|
let timeRangeStart = null;
|
||||||
|
let timeRangeEnd = null;
|
||||||
|
|
||||||
|
const createTable = () => {
|
||||||
|
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();
|
@ -173,6 +173,11 @@ header h2 span {
|
|||||||
bottom: 27px;
|
bottom: 27px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-panel {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 79px;
|
||||||
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
/* position: absolute; */
|
/* position: absolute; */
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -430,24 +435,54 @@ header h2 span {
|
|||||||
width: fit-content;
|
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,
|
.organisation label,
|
||||||
#parameters label {
|
#parameters label,
|
||||||
|
.permission-group label {
|
||||||
color: rgba(0, 0, 0, 0.5);
|
color: rgba(0, 0, 0, 0.5);
|
||||||
cursor: pointer !important;
|
cursor: pointer !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.organisation .checkbox-label:hover,
|
.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;
|
color: rgba(0, 0, 0, 0.7) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.organisation .checkbox-input:checked + .checkbox-label,
|
.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;
|
color: rgba(0, 0, 0, 0.9) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.organisation .checkmark,
|
.organisation .checkmark,
|
||||||
.add-new .checkmark {
|
.add-new .checkmark,
|
||||||
|
.permission-group .checkmark {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
@ -458,12 +493,14 @@ header h2 span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.organisation .checkbox-input:hover + .checkbox-label .checkmark,
|
.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;
|
filter: brightness(0.95) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.organisation .checkbox-input:checked + .checkbox-label .checkmark,
|
.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);
|
background: url(../img/checkbox-check.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,7 +633,8 @@ header h2 span {
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table .search {
|
.table .search,
|
||||||
|
.admin-search {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
float: right;
|
float: right;
|
||||||
margin: 12px 42px 12px 0;
|
margin: 12px 42px 12px 0;
|
||||||
@ -617,6 +655,20 @@ header h2 span {
|
|||||||
transition: 0.2s;
|
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:focus,
|
||||||
.search:hover {
|
.search:hover {
|
||||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||||
@ -627,6 +679,42 @@ header h2 span {
|
|||||||
opacity: 100%;
|
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 {
|
table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -679,7 +767,8 @@ td {
|
|||||||
background: url(../img/checkbox-table-check.svg);
|
background: url(../img/checkbox-table-check.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.table .trash {
|
.table .trash,
|
||||||
|
#adminTable .trash {
|
||||||
height: 22px;
|
height: 22px;
|
||||||
width: 19px;
|
width: 19px;
|
||||||
background: no-repeat url(../img/trash.svg);
|
background: no-repeat url(../img/trash.svg);
|
||||||
@ -689,7 +778,8 @@ td {
|
|||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table .options {
|
.table .options,
|
||||||
|
#adminTable .options {
|
||||||
height: 19px;
|
height: 19px;
|
||||||
width: 22px;
|
width: 22px;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
@ -1066,9 +1156,17 @@ tr:nth-child(even) {
|
|||||||
background-color: #0000000a;
|
background-color: #0000000a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#edit-user-form {
|
||||||
|
width: 550px;
|
||||||
|
/* float: right; */
|
||||||
|
margin: 30px 0 0 44px;
|
||||||
|
}
|
||||||
|
|
||||||
.new-parameters label,
|
.new-parameters label,
|
||||||
.update-info label,
|
.update-info label,
|
||||||
.input-name {
|
.input-name,
|
||||||
|
.add-user-form label,
|
||||||
|
#edit-user-form label {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
@ -1080,9 +1178,16 @@ tr:nth-child(even) {
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-user-form label {
|
||||||
|
margin-top: 16px;
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
.new-parameters input,
|
.new-parameters input,
|
||||||
.new-parameters select,
|
.new-parameters select,
|
||||||
.update-info input {
|
.update-info input,
|
||||||
|
.add-user-form input,
|
||||||
|
#edit-user-form input {
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -1101,9 +1206,22 @@ tr:nth-child(even) {
|
|||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-user-form input {
|
||||||
|
width: 540px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#edit-user-form input {
|
||||||
|
width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
.new-parameters input:hover,
|
.new-parameters input:hover,
|
||||||
.new-parameters select:hover,
|
.new-parameters select:hover,
|
||||||
.new-parameters input:focus,
|
.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:hover,
|
||||||
.update-info input:focus {
|
.update-info input:focus {
|
||||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||||
@ -1119,11 +1237,14 @@ tr:nth-child(even) {
|
|||||||
background-position-x: calc(100% - 14px);
|
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);
|
color: rgba(0, 0, 0, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.new-parameters button,
|
.new-parameters button,
|
||||||
|
.add-user-form button,
|
||||||
.update-info button,
|
.update-info button,
|
||||||
.button-purple {
|
.button-purple {
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
@ -1140,10 +1261,14 @@ tr:nth-child(even) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.new-parameters button,
|
.new-parameters button,
|
||||||
|
.add-user-form button,
|
||||||
.update-info button {
|
.update-info button {
|
||||||
float: right;
|
float: right;
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
}
|
}
|
||||||
|
.add-user-form button {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.update-info button {
|
.update-info button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -1152,6 +1277,7 @@ tr:nth-child(even) {
|
|||||||
|
|
||||||
.new-parameters button:hover,
|
.new-parameters button:hover,
|
||||||
.update-info button:hover,
|
.update-info button:hover,
|
||||||
|
.add-user-form button:hover,
|
||||||
.button-purple:hover {
|
.button-purple:hover {
|
||||||
filter: brightness(0.9);
|
filter: brightness(0.9);
|
||||||
}
|
}
|
||||||
@ -2237,6 +2363,44 @@ input[type="time"]:focus {
|
|||||||
border: 1px solid rgba(0, 0, 0, 0.30);
|
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) {
|
@media (max-width: 1950px) {
|
||||||
/* при разрешении монитора до 1950 пикселей */
|
/* при разрешении монитора до 1950 пикселей */
|
||||||
|
|
||||||
|
411
static/templates/admin/index.html
Normal file
411
static/templates/admin/index.html
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Панель управления</title>
|
||||||
|
<link rel="stylesheet" href="../styles/main.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1>Аргус</h1>
|
||||||
|
<h2><span>/</span> {{Organisation}}</h2>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="account-info">
|
||||||
|
<div id="account-main">
|
||||||
|
<img id="person" src="../img/person.svg">
|
||||||
|
<span>{{User}}</span>
|
||||||
|
<img id="down" src="../img/down.svg">
|
||||||
|
<img id="up" src="../img/up.svg">
|
||||||
|
</div>
|
||||||
|
<a href="/login"><div id="account-additional" class="additional">Выйти</div></a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<section class="navigation">
|
||||||
|
<a href="/">
|
||||||
|
<div><img src="../img/chart.svg">Главная</div>
|
||||||
|
</a>
|
||||||
|
<a href="/devices">
|
||||||
|
<div><img src="../img/cloud.svg">Устройства</div>
|
||||||
|
</a>
|
||||||
|
<a href="/reports">
|
||||||
|
<div><img src="../img/bubble.svg">Отчёты</div>
|
||||||
|
</a>
|
||||||
|
<a href="/live">
|
||||||
|
<div><img src="../img/waves.svg">Трансляция</div>
|
||||||
|
</a>
|
||||||
|
<a href="/videos">
|
||||||
|
<div><img src="../img/play.svg">Записи</div>
|
||||||
|
</a>
|
||||||
|
<a class="admin-panel" href="/admin">
|
||||||
|
<div class="selected"><img src="../img/keyboard.svg">Админка</div>
|
||||||
|
</a>
|
||||||
|
<a class="settings" href="/settings">
|
||||||
|
<div><img src="../img/gear.svg">Настройки</div>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="main">
|
||||||
|
{{#if ifDBError}}
|
||||||
|
<section class="dberror">
|
||||||
|
<div class="erorr-container">
|
||||||
|
<img src="../img/warning.svg"> <br>
|
||||||
|
<h1>Ошибка </h1> <br>
|
||||||
|
<span>Не удалось получить данные из БД</span>
|
||||||
|
<button type="button" onclick="location.reload();">Повторить попытку</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<section style="display: none;" class="dberror" id="addInformation" >
|
||||||
|
<div class="erorr-container">
|
||||||
|
<div id="loader" class="loader">
|
||||||
|
<div class="square" id="sq1"></div>
|
||||||
|
<div class="square" id="sq2"></div>
|
||||||
|
<div class="square" id="sq3"></div>
|
||||||
|
<div class="square" id="sq4"></div>
|
||||||
|
<div class="square" id="sq5"></div>
|
||||||
|
<div class="square" id="sq6"></div>
|
||||||
|
<div class="square" id="sq7"></div>
|
||||||
|
<div class="square" id="sq8"></div>
|
||||||
|
<div class="square" id="sq9"></div>
|
||||||
|
</div>
|
||||||
|
<svg id="success-mark" style="display: none;" xmlns="http://www.w3.org/2000/svg" width="108" height="108" fill="none" viewBox="0 0 108 108">
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<path fill="#8086F9" fill-opacity=".85" d="M54 107.947c29.541 0 54-24.5 54-53.973C108 24.447 83.488 0 53.947 0 24.459 0 0 24.447 0 53.974c0 29.474 24.512 53.973 54 53.973Zm0-8.995c-24.988 0-44.947-20.002-44.947-44.978 0-24.976 19.906-44.978 44.894-44.978S99 28.998 99 53.974c0 24.976-20.012 44.978-45 44.978Zm-5.824-19.844c1.747 0 3.23-.846 4.289-2.487l24.194-38.046c.582-1.058 1.27-2.222 1.27-3.386 0-2.382-2.117-3.916-4.341-3.916-1.323 0-2.647.847-3.653 2.381l-21.97 35.241-10.43-13.493c-1.27-1.693-2.435-2.116-3.917-2.116-2.277 0-4.077 1.852-4.077 4.18 0 1.164.477 2.276 1.218 3.28l12.917 15.875c1.324 1.747 2.753 2.487 4.5 2.487Z"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="a">
|
||||||
|
<path fill="#fff" d="M0 0h108v108H0z"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<h1>Добавление пользователя</h1> <br>
|
||||||
|
<span id="status">Пожалуйста, подождите</span>
|
||||||
|
<button id="closeButton" style="display: none;" onclick="hideMessage()">Закрыть</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="name">
|
||||||
|
<span>Панель управления</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<a class="selected" href="/admin">Пользователи</a>
|
||||||
|
</nav>
|
||||||
|
<section class="bg">
|
||||||
|
<section id="content" class="content">
|
||||||
|
|
||||||
|
<input id="table-search" class="search admin-search" type="text" placeholder="Поиск по имени, почте, номеру телефона или ID">
|
||||||
|
<button class="button-purple add-user" onclick="addUser();">Добавить пользователя</button>
|
||||||
|
|
||||||
|
<table id="adminTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Имя</th>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Почта</th>
|
||||||
|
<th>Номер телефона</th>
|
||||||
|
<th>Дата добавления</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- Сюда будут добавляться строки таблицы -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div id="count">
|
||||||
|
<!-- Сюда добавится итоговое количество результатов -->
|
||||||
|
</div>
|
||||||
|
<div id="pagination">
|
||||||
|
<!-- Сюда будут добавляться ссылки для переключения между страницами -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
<section id="form-bg" class="edit-container">
|
||||||
|
|
||||||
|
<section class="add-user-form" id="form">
|
||||||
|
<h1>Добавить пользователя</h1>
|
||||||
|
<img src="../img/xmark.svg" id="close-form-btn">
|
||||||
|
|
||||||
|
|
||||||
|
<form id="add-user-form">
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-name">Имя<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="name" type="text" id="user-name" placeholder="Имя пользователя" required>
|
||||||
|
</div>
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-surname">Фамилия<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="surname" type="text" id="user-surname" placeholder="Фамилия пользователя" required>
|
||||||
|
</div>
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-email">Email<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="email" type="text" id="user-email" placeholder="Email пользователя" required>
|
||||||
|
</div>
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-phone">Номер телефона<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="phone" type="text" id="user-phone" placeholder="Номер телефона пользователя" required>
|
||||||
|
</div>
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-password">Пароль<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="password" type="text" id="user-password" placeholder="Пароль пользователя" required>
|
||||||
|
</div>
|
||||||
|
<div class="horizontal-line"></div>
|
||||||
|
|
||||||
|
<button id="user-add" type="submit">Добавить</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="display: none;" class="dberror" id="deleteConfirmation">
|
||||||
|
<div class="erorr-container">
|
||||||
|
<img src="../img/warning.svg"> <br>
|
||||||
|
<h1>Удаление водителя </h1> <br>
|
||||||
|
<span>Вы уверены что хотите удалить <span id="driverDeleteInfo"></span>?</span>
|
||||||
|
<div class="buttons">
|
||||||
|
<button id="deleteCancel" onclick="closeDeletion();" style="display: inline-block; background-color: white; color: rgba(0, 0, 0, 0.7); margin-right: 5px;" type="button" onclick="deleteDriver()">Отменить</button>
|
||||||
|
<button id="deleteDriver" style="display: inline-block;" type="button">Подтвердить</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const users = [
|
||||||
|
{{#each Users}}
|
||||||
|
{
|
||||||
|
id: "{{this.id}}",
|
||||||
|
name: "{{this.name}}",
|
||||||
|
surname: "{{this.surname}}",
|
||||||
|
phone: "{{this.phone}}",
|
||||||
|
email: "{{this.email}}",
|
||||||
|
date: "{{this.added}}",
|
||||||
|
},
|
||||||
|
{{/each}}
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="../scripts/jquery.min.js"></script>
|
||||||
|
<script src="../scripts/table-admin.js"></script>
|
||||||
|
<script src="https://rawgit.com/RobinHerbots/Inputmask/5.x/dist/jquery.inputmask.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
|
||||||
|
$('#user-phone').inputmask({"mask": "+7 (999) 999-9999"});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var formContainer = $("#form-bg");
|
||||||
|
var fform = $("#form");
|
||||||
|
|
||||||
|
function closeForm() {
|
||||||
|
formContainer.removeClass("active");
|
||||||
|
fform.removeClass("form-animation");
|
||||||
|
$("body").css("overflow", "auto");
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
const form = document.getElementById("add-user-form");
|
||||||
|
|
||||||
|
form.addEventListener("submit", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const deleteConfirmation = document.getElementById("addInformation");
|
||||||
|
const loader = document.getElementById("loader");
|
||||||
|
const status = document.getElementById("status");
|
||||||
|
const closeButton = document.getElementById("closeButton");
|
||||||
|
const mark = document.getElementById("success-mark");
|
||||||
|
|
||||||
|
loader.style.display = "block";
|
||||||
|
closeButton.style.display = "none";
|
||||||
|
deleteConfirmation.style.display = "flex";
|
||||||
|
mark.style.display = "none";
|
||||||
|
|
||||||
|
const formData = new FormData(form);
|
||||||
|
|
||||||
|
const jsonObject = {};
|
||||||
|
formData.forEach((value, key) => {
|
||||||
|
jsonObject[key] = value;
|
||||||
|
});
|
||||||
|
const jsonData = JSON.stringify(jsonObject);
|
||||||
|
|
||||||
|
closeForm();
|
||||||
|
form.reset();
|
||||||
|
|
||||||
|
fetch("/add-user", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: jsonData,
|
||||||
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
showMessage("Пользователь успешно добавлен", true);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
showMessage("Не удалось добавить пользователя", false);
|
||||||
|
console.error("Ошибка:", error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function addUser() {
|
||||||
|
var formContainer = $("#form-bg");
|
||||||
|
var form = $("#form");
|
||||||
|
formContainer.addClass("active");
|
||||||
|
form.addClass("form-animation");
|
||||||
|
$("body").css("overflow", "hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var formContainer = $("#form-bg");
|
||||||
|
var form = $("#form");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Закрывает popup форму
|
||||||
|
$("#close-form-btn").click(function() {
|
||||||
|
closeForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Закрывает popup форму при клике вне её области
|
||||||
|
$(document).click(function(event) {
|
||||||
|
if (
|
||||||
|
!formContainer.is(event.target) &&
|
||||||
|
formContainer.has(event.target).length === 0 &&
|
||||||
|
formContainer.hasClass("active") &&
|
||||||
|
!openButton.is(event.target)
|
||||||
|
) {
|
||||||
|
closeForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Функция для закрытия формы
|
||||||
|
function closeForm() {
|
||||||
|
formContainer.removeClass("active");
|
||||||
|
form.removeClass("form-animation");
|
||||||
|
$("body").css("overflow", "auto");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function hideMessage() {
|
||||||
|
location.reload();
|
||||||
|
const deleteConfirmation = document.getElementById("addInformation");
|
||||||
|
deleteConfirmation.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMessage(messageText, isSuccess) {
|
||||||
|
const loader = document.getElementById("loader");
|
||||||
|
const status = document.getElementById("status");
|
||||||
|
const closeButton = document.getElementById("closeButton");
|
||||||
|
const mark = document.getElementById("success-mark");
|
||||||
|
|
||||||
|
loader.style.display = "none";
|
||||||
|
status.textContent = messageText;
|
||||||
|
if (isSuccess) {
|
||||||
|
mark.style.display = "block";
|
||||||
|
status.style.color = "green";
|
||||||
|
} else {
|
||||||
|
status.style.color = "red";
|
||||||
|
}
|
||||||
|
closeButton.style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function deleteUser(id) {
|
||||||
|
|
||||||
|
var deleteConfirmation = $("#deleteConfirmation");
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/userdata",
|
||||||
|
method: "POST",
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify({ id: id }),
|
||||||
|
success: function(response) {
|
||||||
|
// Установка значений полей формы
|
||||||
|
$("#driverDeleteInfo").html(response.name + " " + response.surname);
|
||||||
|
document.getElementById('deleteDriver').setAttribute("onclick", `confirmDelete(${response.id})`);
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById('deleteConfirmation').style.display = "flex";
|
||||||
|
|
||||||
|
$("body").css("overflow", "hidden");
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
// Обработка ошибки при запросе к серверу
|
||||||
|
alert("Произошла ошибка при запросе к серверу.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDeletion() {
|
||||||
|
document.getElementById('deleteConfirmation').style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmDelete(id) {
|
||||||
|
$.ajax({
|
||||||
|
url: "/deleteuser",
|
||||||
|
method: "POST",
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify({ id: id }),
|
||||||
|
success: function(response) {
|
||||||
|
location.reload();
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
// Обработка ошибки при запросе к серверу
|
||||||
|
alert("Произошла ошибка при запросе к серверу.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
// Скрытие/Показ дополнительных меню аккаунта
|
||||||
|
const accountMain = document.getElementById('account-main');
|
||||||
|
const accountAdditional = document.getElementById('account-additional');
|
||||||
|
const accountUp = document.getElementById('up');
|
||||||
|
const accountDown = document.getElementById('down');
|
||||||
|
accountAdditional.style.display = 'none';
|
||||||
|
accountUp.style.display = 'none';
|
||||||
|
|
||||||
|
accountMain.addEventListener('click', () => {
|
||||||
|
if (accountAdditional.style.display === 'none') {
|
||||||
|
accountAdditional.style.display = 'flex';
|
||||||
|
accountUp.style.display = 'unset';
|
||||||
|
accountDown.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
accountAdditional.style.display = 'none';
|
||||||
|
accountUp.style.display = 'none';
|
||||||
|
accountDown.style.display = 'unset';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
375
static/templates/admin/user.html
Normal file
375
static/templates/admin/user.html
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{{Name}} {{Surname}}</title>
|
||||||
|
<link rel="stylesheet" href="../../styles/main.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h1>Аргус</h1>
|
||||||
|
<h2><span>/</span> {{Organisation}}</h2>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="account-info">
|
||||||
|
<div id="account-main">
|
||||||
|
<img id="person" src="../../img/person.svg">
|
||||||
|
<span>{{User}}</span>
|
||||||
|
<img id="down" src="../../img/down.svg">
|
||||||
|
<img id="up" src="../../img/up.svg">
|
||||||
|
</div>
|
||||||
|
<a href="/login"><div id="account-additional" class="additional">Выйти</div></a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<section class="navigation">
|
||||||
|
<a href="/">
|
||||||
|
<div><img src="../../img/chart.svg">Главная</div>
|
||||||
|
</a>
|
||||||
|
<a href="/devices">
|
||||||
|
<div><img src="../../img/cloud.svg">Устройства</div>
|
||||||
|
</a>
|
||||||
|
<a href="/reports">
|
||||||
|
<div><img src="../../img/bubble.svg">Отчёты</div>
|
||||||
|
</a>
|
||||||
|
<a href="/live">
|
||||||
|
<div><img src="../../img/waves.svg">Трансляция</div>
|
||||||
|
</a>
|
||||||
|
<a href="/videos">
|
||||||
|
<div><img src="../../img/play.svg">Записи</div>
|
||||||
|
</a>
|
||||||
|
<a class="admin-panel" href="/admin">
|
||||||
|
<div class="selected"><img src="../../img/keyboard.svg">Админка</div>
|
||||||
|
</a>
|
||||||
|
<a class="settings" href="/settings">
|
||||||
|
<div><img src="../../img/gear.svg">Настройки</div>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="main">
|
||||||
|
{{#if ifDBError}}
|
||||||
|
<section class="dberror">
|
||||||
|
<div class="erorr-container">
|
||||||
|
<img src="../../img/warning.svg"> <br>
|
||||||
|
<h1>Ошибка </h1> <br>
|
||||||
|
<span>Не удалось получить данные из БД</span>
|
||||||
|
<button type="button" onclick="location.reload();">Повторить попытку</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<section style="display: none;" class="dberror" id="addInformation" >
|
||||||
|
<div class="erorr-container">
|
||||||
|
<div id="loader" class="loader">
|
||||||
|
<div class="square" id="sq1"></div>
|
||||||
|
<div class="square" id="sq2"></div>
|
||||||
|
<div class="square" id="sq3"></div>
|
||||||
|
<div class="square" id="sq4"></div>
|
||||||
|
<div class="square" id="sq5"></div>
|
||||||
|
<div class="square" id="sq6"></div>
|
||||||
|
<div class="square" id="sq7"></div>
|
||||||
|
<div class="square" id="sq8"></div>
|
||||||
|
<div class="square" id="sq9"></div>
|
||||||
|
</div>
|
||||||
|
<svg id="success-mark" style="display: none;" xmlns="http://www.w3.org/2000/svg" width="108" height="108" fill="none" viewBox="0 0 108 108">
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<path fill="#8086F9" fill-opacity=".85" d="M54 107.947c29.541 0 54-24.5 54-53.973C108 24.447 83.488 0 53.947 0 24.459 0 0 24.447 0 53.974c0 29.474 24.512 53.973 54 53.973Zm0-8.995c-24.988 0-44.947-20.002-44.947-44.978 0-24.976 19.906-44.978 44.894-44.978S99 28.998 99 53.974c0 24.976-20.012 44.978-45 44.978Zm-5.824-19.844c1.747 0 3.23-.846 4.289-2.487l24.194-38.046c.582-1.058 1.27-2.222 1.27-3.386 0-2.382-2.117-3.916-4.341-3.916-1.323 0-2.647.847-3.653 2.381l-21.97 35.241-10.43-13.493c-1.27-1.693-2.435-2.116-3.917-2.116-2.277 0-4.077 1.852-4.077 4.18 0 1.164.477 2.276 1.218 3.28l12.917 15.875c1.324 1.747 2.753 2.487 4.5 2.487Z"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="a">
|
||||||
|
<path fill="#fff" d="M0 0h108v108H0z"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<h1>Обновление пользователя</h1> <br>
|
||||||
|
<span id="status">Пожалуйста, подождите</span>
|
||||||
|
<button id="closeButton" style="display: none;" onclick="hideMessage()">Закрыть</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<div class="name">
|
||||||
|
<span>{{Name}} {{Surname}} <span class="user-id">ID {{Id}}</span></span>
|
||||||
|
</div>
|
||||||
|
<nav>
|
||||||
|
<a class="return-name" href="/admin">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="17" fill="none" viewBox="0 0 10 17">
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<path fill="#8086F9" d="M0 8.477a.88.88 0 0 0 .273.644l7.745 7.568a.84.84 0 0 0 .634.264c.508 0 .899-.38.899-.889a.917.917 0 0 0-.264-.634l-7.11-6.953 7.11-6.954A.936.936 0 0 0 9.551.89.876.876 0 0 0 8.652 0a.869.869 0 0 0-.634.254L.273 7.832A.864.864 0 0 0 0 8.477Z"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="a">
|
||||||
|
<path fill="#fff" d="M0 0h9.551v16.963H0z"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
Вернуться</a>
|
||||||
|
</nav>
|
||||||
|
<section class="bg">
|
||||||
|
<section class="content">
|
||||||
|
|
||||||
|
<section class="for-table">
|
||||||
|
|
||||||
|
<section class="organisation">
|
||||||
|
<h1>Доступ</h1>
|
||||||
|
|
||||||
|
<ul class="area">
|
||||||
|
{{#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">{{name}}</label>
|
||||||
|
<ul class="area-devices" id="devices-1">
|
||||||
|
{{#each serials}}
|
||||||
|
{{#if this.checked}}
|
||||||
|
<li class="device"><img><input type="checkbox" id="{{this.serial}}" class="checkbox-input device-filter device-serial" value="{{this.serial}}" hidden checked><label for="{{this.serial}}" class="checkbox-label"><div class="checkmark"></div>{{this.serial}}</label></li>
|
||||||
|
{{else}}
|
||||||
|
<li class="device"><img><input type="checkbox" id="{{this.serial}}" class="checkbox-input device-filter device-serial" value="{{this.serial}}" hidden><label for="{{this.serial}}" class="checkbox-label"><div class="checkmark"></div>{{this.serial}}</label></li>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="whole-width">
|
||||||
|
<h1>Данные пользователя</h1>
|
||||||
|
<h3>Кнопка "Обновить" также распространяется на доступ к устройствам.</h3>
|
||||||
|
<form id="edit-user-form">
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-name">Имя<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="name" type="text" id="user-name" placeholder="Имя пользователя" value="{{Name}}" required>
|
||||||
|
</div>
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-surname">Фамилия<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="surname" type="text" id="user-surname" placeholder="Фамилия пользователя" value="{{Surname}}" required>
|
||||||
|
</div>
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-email">Email<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="email" type="text" id="user-email" placeholder="Email пользователя" value="{{Email}}" required>
|
||||||
|
</div>
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-phone">Номер телефона<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="phone" type="text" id="user-phone" placeholder="Номер телефона пользователя" value="{{Phone}}" required>
|
||||||
|
</div>
|
||||||
|
<div class="parameters-input">
|
||||||
|
<label for="user-password">Пароль<span style="color: rgba(255, 69, 58, 1);">*</span></label>
|
||||||
|
<input name="password" type="text" id="user-password" placeholder="Пароль пользователя" value="{{Password}}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div style="margin-left: 44px; margin-right: 72px; width: calc(100% - 44px - 72px);" class="horizontal-line"></div>
|
||||||
|
|
||||||
|
<h1>Права пользователя</h1>
|
||||||
|
|
||||||
|
<section class="permissions">
|
||||||
|
|
||||||
|
<div class="permission-group">
|
||||||
|
<h2>Устройства</h2>
|
||||||
|
{{#if EditTransport}}
|
||||||
|
<img><input name="EditTransport" type="checkbox" id="device-edit" class="checkbox-input device-filter" hidden checked><label for="device-edit" class="checkbox-label"><div class="checkmark"></div>Изменять</label>
|
||||||
|
{{else}}
|
||||||
|
<img><input name="EditTransport" type="checkbox" id="device-edit" class="checkbox-input device-filter" hidden><label for="device-edit" class="checkbox-label"><div class="checkmark"></div>Изменять</label>
|
||||||
|
{{/if}}
|
||||||
|
{{#if DeleteTransport}}
|
||||||
|
<img><input name="DeleteTransport" type="checkbox" id="device-delete" class="checkbox-input device-filter" hidden checked><label for="device-delete" class="checkbox-label"><div class="checkmark"></div>Удалять</label>
|
||||||
|
{{else}}
|
||||||
|
<img><input name="DeleteTransport" type="checkbox" id="device-delete" class="checkbox-input device-filter" hidden><label for="device-delete" class="checkbox-label"><div class="checkmark"></div>Удалять</label>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="permission-group">
|
||||||
|
<h2>Обновление ПО</h2>
|
||||||
|
{{#if Update}}
|
||||||
|
<img><input name="Update" type="checkbox" id="update-do" class="checkbox-input device-filter" hidden checked><label for="update-do" class="checkbox-label"><div class="checkmark"></div>Обновлять</label>
|
||||||
|
{{else}}
|
||||||
|
<img><input name="Update" type="checkbox" id="update-do" class="checkbox-input device-filter" hidden><label for="update-do" class="checkbox-label"><div class="checkmark"></div>Обновлять</label>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div style="margin-left: 44px; margin-right: 72px; width: calc(100% - 44px - 72px);" class="horizontal-line"></div>
|
||||||
|
|
||||||
|
<button style="margin-top: 15px;" id="user-edit" class="button-purple" type="button">Обновить</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.whole-width {
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(100% - 275px);
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script src="../../scripts/jquery.min.js"></script>
|
||||||
|
<script src="https://rawgit.com/RobinHerbots/Inputmask/5.x/dist/jquery.inputmask.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
|
||||||
|
$('#user-phone').inputmask({"mask": "+7 (999) 999-9999"});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('user-edit').addEventListener('click', function() {
|
||||||
|
|
||||||
|
const deleteConfirmation = document.getElementById("addInformation");
|
||||||
|
const loader = document.getElementById("loader");
|
||||||
|
const status = document.getElementById("status");
|
||||||
|
const closeButton = document.getElementById("closeButton");
|
||||||
|
const mark = document.getElementById("success-mark");
|
||||||
|
|
||||||
|
loader.style.display = "block";
|
||||||
|
closeButton.style.display = "none";
|
||||||
|
deleteConfirmation.style.display = "flex";
|
||||||
|
mark.style.display = "none";
|
||||||
|
// Собираем данные из инпутов с классом "device" в массив
|
||||||
|
const deviceInputs = document.querySelectorAll('.device-serial:checked');
|
||||||
|
const devices = Array.from(deviceInputs).map(input => input.value);
|
||||||
|
|
||||||
|
// Собираем остальные данные из формы
|
||||||
|
const formData = {
|
||||||
|
name: document.getElementById('user-name').value,
|
||||||
|
surname: document.getElementById('user-surname').value,
|
||||||
|
email: document.getElementById('user-email').value,
|
||||||
|
phone: document.getElementById('user-phone').value,
|
||||||
|
password: document.getElementById('user-password').value,
|
||||||
|
EditTransport: document.getElementById('device-edit').checked,
|
||||||
|
DeleteTransport: document.getElementById('device-delete').checked,
|
||||||
|
Update: document.getElementById('update-do').checked,
|
||||||
|
// Другие данные, которые необходимо включить
|
||||||
|
};
|
||||||
|
|
||||||
|
// Объединяем все данные в один объект
|
||||||
|
const jsonData = {
|
||||||
|
devices,
|
||||||
|
formData
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
fetch('/updateuser/{{Id}}', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(jsonData)
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
showMessage("Данные успешно обновлены", true);
|
||||||
|
} else {
|
||||||
|
showMessage("Не удалось обновить данные", false);
|
||||||
|
console.error("Ошибка:", error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Произошла ошибка:', error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function hideMessage() {
|
||||||
|
location.reload();
|
||||||
|
const deleteConfirmation = document.getElementById("addInformation");
|
||||||
|
deleteConfirmation.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMessage(messageText, isSuccess) {
|
||||||
|
const loader = document.getElementById("loader");
|
||||||
|
const status = document.getElementById("status");
|
||||||
|
const closeButton = document.getElementById("closeButton");
|
||||||
|
const mark = document.getElementById("success-mark");
|
||||||
|
|
||||||
|
loader.style.display = "none";
|
||||||
|
status.textContent = messageText;
|
||||||
|
if (isSuccess) {
|
||||||
|
mark.style.display = "block";
|
||||||
|
status.style.color = "green";
|
||||||
|
} else {
|
||||||
|
status.style.color = "red";
|
||||||
|
}
|
||||||
|
closeButton.style.display = "block";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
// Скрытие/Показ дополнительных меню аккаунта
|
||||||
|
const accountMain = document.getElementById('account-main');
|
||||||
|
const accountAdditional = document.getElementById('account-additional');
|
||||||
|
const accountUp = document.getElementById('up');
|
||||||
|
const accountDown = document.getElementById('down');
|
||||||
|
accountAdditional.style.display = 'none';
|
||||||
|
accountUp.style.display = 'none';
|
||||||
|
|
||||||
|
accountMain.addEventListener('click', () => {
|
||||||
|
if (accountAdditional.style.display === 'none') {
|
||||||
|
accountAdditional.style.display = 'flex';
|
||||||
|
accountUp.style.display = 'unset';
|
||||||
|
accountDown.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
accountAdditional.style.display = 'none';
|
||||||
|
accountUp.style.display = 'none';
|
||||||
|
accountDown.style.display = 'unset';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const checkboxes = document.querySelectorAll('.organisation .checkbox-input');
|
||||||
|
|
||||||
|
checkboxes.forEach((checkbox) => {
|
||||||
|
checkbox.addEventListener('change', function() {
|
||||||
|
const devices = this.parentNode.querySelector('.area-devices');
|
||||||
|
if (this.checked) {
|
||||||
|
devices.style.display = 'block';
|
||||||
|
|
||||||
|
// Активируем дочерние чекбоксы
|
||||||
|
const childCheckboxes = devices.querySelectorAll('.device-filter');
|
||||||
|
childCheckboxes.forEach((childCheckbox) => {
|
||||||
|
childCheckbox.checked = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
devices.style.display = 'none';
|
||||||
|
|
||||||
|
// Деактивируем дочерние чекбоксы
|
||||||
|
const childCheckboxes = devices.querySelectorAll('.device-filter');
|
||||||
|
childCheckboxes.forEach((childCheckbox) => {
|
||||||
|
childCheckbox.checked = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Деактивируем дочерние чекбоксы, если родительский чекбокс не выбран
|
||||||
|
if (!this.checked) {
|
||||||
|
const childCheckboxes = devices.querySelectorAll('.device-filter');
|
||||||
|
childCheckboxes.forEach((childCheckbox) => {
|
||||||
|
childCheckbox.checked = false;
|
||||||
|
});
|
||||||
|
devices.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -45,6 +45,9 @@
|
|||||||
<a href="/videos">
|
<a href="/videos">
|
||||||
<div><img src="../img/play.svg">Записи</div>
|
<div><img src="../img/play.svg">Записи</div>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="admin-panel" href="/admin">
|
||||||
|
<div><img src="../img/keyboard.svg">Админка</div>
|
||||||
|
</a>
|
||||||
<a class="settings" href="/settings">
|
<a class="settings" href="/settings">
|
||||||
<div><img src="../img/gear.svg">Настройки</div>
|
<div><img src="../img/gear.svg">Настройки</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -46,6 +46,9 @@
|
|||||||
</a>
|
</a>
|
||||||
<a href="/videos">
|
<a href="/videos">
|
||||||
<div><img src="../img/play.svg">Записи</div>
|
<div><img src="../img/play.svg">Записи</div>
|
||||||
|
</a>
|
||||||
|
<a class="admin-panel" href="/admin">
|
||||||
|
<div><img src="../img/keyboard.svg">Админка</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="settings" href="/settings">
|
<a class="settings" href="/settings">
|
||||||
<div><img src="../img/gear.svg">Настройки</div>
|
<div><img src="../img/gear.svg">Настройки</div>
|
||||||
|
@ -43,6 +43,9 @@
|
|||||||
<a href="/videos">
|
<a href="/videos">
|
||||||
<div><img src="../img/play.svg">Записи</div>
|
<div><img src="../img/play.svg">Записи</div>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="admin-panel" href="/admin">
|
||||||
|
<div><img src="../img/keyboard.svg">Админка</div>
|
||||||
|
</a>
|
||||||
<a class="settings" href="/settings">
|
<a class="settings" href="/settings">
|
||||||
<div><img src="../img/gear.svg">Настройки</div>
|
<div><img src="../img/gear.svg">Настройки</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -45,6 +45,9 @@
|
|||||||
<a href="/videos">
|
<a href="/videos">
|
||||||
<div><img src="../img/play.svg">Записи</div>
|
<div><img src="../img/play.svg">Записи</div>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="admin-panel" href="/admin">
|
||||||
|
<div><img src="../img/keyboard.svg">Админка</div>
|
||||||
|
</a>
|
||||||
<a class="settings" href="/settings">
|
<a class="settings" href="/settings">
|
||||||
<div><img src="../img/gear.svg">Настройки</div>
|
<div><img src="../img/gear.svg">Настройки</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -43,6 +43,9 @@
|
|||||||
<a href="/videos">
|
<a href="/videos">
|
||||||
<div><img src="../img/play.svg">Записи</div>
|
<div><img src="../img/play.svg">Записи</div>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="admin-panel" href="/admin">
|
||||||
|
<div><img src="../img/keyboard.svg">Админка</div>
|
||||||
|
</a>
|
||||||
<a class="settings" href="/settings">
|
<a class="settings" href="/settings">
|
||||||
<div class="selected"><img src="../img/gear.svg">Настройки</div>
|
<div class="selected"><img src="../img/gear.svg">Настройки</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -47,6 +47,9 @@
|
|||||||
<a href="/videos">
|
<a href="/videos">
|
||||||
<div class="selected"><img src="../img/play.svg">Записи</div>
|
<div class="selected"><img src="../img/play.svg">Записи</div>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="admin-panel" href="/admin">
|
||||||
|
<div><img src="../img/keyboard.svg">Админка</div>
|
||||||
|
</a>
|
||||||
<a class="settings" href="/settings">
|
<a class="settings" href="/settings">
|
||||||
<div><img src="../img/gear.svg">Настройки</div>
|
<div><img src="../img/gear.svg">Настройки</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -47,6 +47,9 @@
|
|||||||
<a href="/videos">
|
<a href="/videos">
|
||||||
<div class="selected"><img src="../img/play.svg">Записи</div>
|
<div class="selected"><img src="../img/play.svg">Записи</div>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="admin-panel" href="/admin">
|
||||||
|
<div><img src="../img/keyboard.svg">Админка</div>
|
||||||
|
</a>
|
||||||
<a class="settings" href="/settings">
|
<a class="settings" href="/settings">
|
||||||
<div><img src="../img/gear.svg">Настройки</div>
|
<div><img src="../img/gear.svg">Настройки</div>
|
||||||
</a>
|
</a>
|
||||||
|
Loading…
Reference in New Issue
Block a user