diff --git a/api/controllers/history.apartment.controller.js b/api/controllers/history.apartment.controller.js new file mode 100644 index 0000000..e6bf467 --- /dev/null +++ b/api/controllers/history.apartment.controller.js @@ -0,0 +1,26 @@ +const historyApartmentService = require('../services/history.apartment.service'); + +class historyApartmentController { + async getList(req, res) { + const { limit } = req.query; + + if (req.mode == 2) { + let result = await historyApartmentService.getList(limit); + if (result) { + return res + .status(200) + .send(result); + } else { + return res + .status(500) + .send({ message: 'Internal server error.' }); + } + } else { + return res + .status(403) + .send({ message: 'The user does not have enough rights.' }); + } + } +} + +module.exports = new historyApartmentController(); \ No newline at end of file diff --git a/api/routes/history.apartment.routes.js b/api/routes/history.apartment.routes.js new file mode 100644 index 0000000..1ec5b1d --- /dev/null +++ b/api/routes/history.apartment.routes.js @@ -0,0 +1,10 @@ +const express = require('express'); +const router = express.Router({ mergeParams: true }); +const historyApartmentController = require('../controllers/history.apartment.controller'); +const authenticate = require("../middleware/auth"); + +router + .route('/list') + .get(authenticate, historyApartmentController.getList); + +module.exports = router; \ No newline at end of file diff --git a/api/routes/index.js b/api/routes/index.js index dbb3a5e..69e08c9 100644 --- a/api/routes/index.js +++ b/api/routes/index.js @@ -11,6 +11,7 @@ const entrancesRoutes = require('./entrances.routes'); const apartmentsRoutes = require('./apartments.routes'); const historyEntranceRoutes = require('./history.entrance.routes'); const historyHomesteadRoutes = require('./history.homestead.routes'); +const historyApartmentRoutes = require('./history.apartment.routes'); const standRoutes = require('./stand.routes'); const pushRoutes = require('./push.routes'); @@ -27,6 +28,7 @@ router.use('/house/:house_id/entrances', entrancesRoutes); router.use('/apartments?/:entrance_id', apartmentsRoutes); router.use('/history/entrance/:entrance_id', historyEntranceRoutes); router.use('/history/homestead/:homestead_id', historyHomesteadRoutes); +router.use('/history/apartments?', historyApartmentRoutes); router.use('/stand', standRoutes); router.use('/push', pushRoutes); diff --git a/api/services/entrances.service.js b/api/services/entrances.service.js index 7c7628b..f2a9b43 100644 --- a/api/services/entrances.service.js +++ b/api/services/entrances.service.js @@ -69,7 +69,7 @@ class EntrancesService { `; db.run(sql, [ - house_id, + Number(house_id), Number(data.entrance_number), data.title, data.description, @@ -101,10 +101,6 @@ class EntrancesService { `; db.run(sql, [ data.title, - JSON.stringify(data.points), - JSON.stringify(data.points_number), - data.floors_quantity, - data.apartments_quantity, data.description, Math.floor(new Date(Date.now()).getTime()), data.id diff --git a/api/services/history.apartment.service.js b/api/services/history.apartment.service.js new file mode 100644 index 0000000..f26af79 --- /dev/null +++ b/api/services/history.apartment.service.js @@ -0,0 +1,69 @@ +const db = require("../config/db"); + +class historyApartmentService { + getList(limit) { + return new Promise((res, rej) => { + let sql = ` + SELECT + ah.*, + s.name AS sheep_name, + s.group_id AS sheep_group_id, + s.icon AS sheep_icon, + h.title AS house_title, + h.number AS house_number, + h.id AS house_id, + e.title AS entrance_title, + a.title AS apartment_title + FROM + apartments_history ah + LEFT JOIN + sheeps s ON s.id = ah.sheep_id + LEFT JOIN + apartments a ON a.id = ah.apartments_id + LEFT JOIN + entrance e ON e.id = a.entrance_id + LEFT JOIN + house h ON h.id = e.house_id + ORDER BY + ah.id DESC + LIMIT ${limit ?? 100}; + `; + + db.all(sql, (err, rows) => { + if (err) { + console.error(err.message); + return res(false); + } else { + let data = rows.map((row) => { + return { + "id": Number(row.id), + "apartments_id": Number(row.apartments_id), + "house_id": Number(row.house_id), + "address": { + "house": { + "title": row.house_title, + "number": row.house_number + }, + "entrance": row.entrance_title, + "apartment": row.apartment_title + }, + "status": Number(row.status), + "description": row.description, + "sheep": { + "id": Number(row.sheep_id), + "name": row.sheep_name, + "group_id": Number(row.sheep_group_id), + "icon": row.sheep_icon + }, + "created_at": Number(row.created_at) + } + }) + + return res(data); + } + }); + }); + } +} + +module.exports = new historyApartmentService(); \ No newline at end of file diff --git a/api/services/houses.service.js b/api/services/houses.service.js index 535dfda..c1d00eb 100644 --- a/api/services/houses.service.js +++ b/api/services/houses.service.js @@ -36,8 +36,6 @@ class HousesService { }, "entrance_number": Number(row.entrance_number), "title": row.title, - "points": JSON.parse(row.points), - "points_number": JSON.parse(row.points_number), "floors_quantity": row.floors_quantity, "apartments_quantity": row.apartments_quantity, "description": row.description, diff --git a/web/css/main.css b/web/css/main.css index 138218b..ac84f6b 100644 --- a/web/css/main.css +++ b/web/css/main.css @@ -1,11 +1,11 @@ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap'); :root { - --FontSize1: 12px; - --FontSize2: 13px; - --FontSize3: 14px; - --FontSize4: 15px; - --FontSize5: 16px; + --FontSize1: 12px; + --FontSize2: 13px; + --FontSize3: 14px; + --FontSize4: 15px; + --FontSize5: 16px; } @media (prefers-color-scheme: light) { @@ -100,7 +100,12 @@ } } -a { +a, +a:visited, +a:hover, +a:active { + color: inherit; + text-decoration: none; text-decoration: none; font-size: var(--FontSize3); font-weight: 700; @@ -708,7 +713,7 @@ body.modal-open { padding: 0 !important; } -.leaflet-popup-content > .map_dell { +.leaflet-popup-content>.map_dell { border-radius: 10px; padding: 5px 10px; width: 100%; @@ -737,9 +742,11 @@ body.modal-open { border: 2px solid #fff; margin: -3px 0 0 -3px; } + .leaflet-pm-tooltip { display: none !important; } + .tooltip-hidden { - display: none; + display: none; } \ No newline at end of file diff --git a/web/index.html b/web/index.html index b34c071..8171c5a 100644 --- a/web/index.html +++ b/web/index.html @@ -79,6 +79,9 @@ + + + diff --git a/web/lib/pages/constructor/script.js b/web/lib/pages/constructor/script.js index 12b85af..bea7bcb 100644 --- a/web/lib/pages/constructor/script.js +++ b/web/lib/pages/constructor/script.js @@ -799,22 +799,37 @@ const Constructor = { delete(house) { // убрать слой с карты - if (Constructor.info.type === 'house') { + if (Editor.info.type === 'house') { houseGroup.removeLayer(house); - } else if (Constructor.info.type === 'homestead') { + } else if (Editor.info.type === 'homestead') { homesteadGroup.removeLayer(house); } // найти индекс полигона в points - const index = Constructor.info.points.findIndex(p => p === house.getLatLngs()); + const target = house.getLatLngs()[0]; // вершины полигона - if (index !== -1) { - // удалить из points и points_number по индексу - Constructor.info.points.splice(index, 1); - Constructor.info.points_number.splice(index, 1); + const index = Editor.info.points.findIndex((p) => { + const copy = p.slice(); // делаем копию + copy[0].pop(); // убираем последний элемент + + if (isSamePolygon(p, target)) return true; // проверка как есть + if (isSamePolygon(copy, target)) return true; // проверка без последнего + return false; + }); + + function isSamePolygon(a, b) { + if (a.length !== b.length) return false; + return a.every((pt, i) => pt.lat === b[i].lat && pt.lng === b[i].lng); } - Constructor.osm.autoZoom(Constructor.info.points); + + if (index) { + // удалить из points и points_number по индексу + Editor.info.points.splice(index, 1); + Editor.info.points_number.splice(index, 1); + } + + Editor.osm.autoZoom(Editor.info.points); }, async getOSM(wayId) { diff --git a/web/lib/pages/constructor/style.css b/web/lib/pages/constructor/style.css index 679f954..8a187c6 100644 --- a/web/lib/pages/constructor/style.css +++ b/web/lib/pages/constructor/style.css @@ -6,9 +6,9 @@ margin: 20px 20px 0 20px; } -#part-1, -#part-2, -#part-3 { +.page-constructor>#part-1, +.page-constructor>#part-2, +.page-constructor>#part-3 { border-radius: 10px; width: calc(100% - 40px); display: flex; @@ -23,9 +23,9 @@ position: relative; } -#part-1>h1, -#part-2>h1, -#part-3>h1 { +.page-constructor>#part-1>h1, +.page-constructor>#part-2>h1, +.page-constructor>#part-3>h1 { width: calc(100% - 40px); color: var(--ColorThemes3); border-radius: var(--border-radius); @@ -35,19 +35,19 @@ position: relative; } -#part-1>h1>span, -#part-2>h1>span, -#part-3>h1>span { +.page-constructor>#part-1>h1>span, +.page-constructor>#part-2>h1>span, +.page-constructor>#part-3>h1>span { font-weight: 500; } -#part-1>#info-type { +.page-constructor>#part-1>#info-type { display: flex; align-items: center; justify-content: center; } -#part-1>#info-type>.tabs { +.page-constructor>#part-1>#info-type>.tabs { display: flex; position: relative; background-color: var(--ColorThemes0); @@ -58,11 +58,11 @@ } -#part-1>#info-type>.tabs>input[type="radio"] { +.page-constructor>#part-1>#info-type>.tabs>input[type="radio"] { display: none; } -#part-1>#info-type>.tabs>.tab { +.page-constructor>#part-1>#info-type>.tabs>.tab { display: flex; align-items: center; justify-content: center; @@ -77,23 +77,23 @@ z-index: 2; } -#part-1>#info-type>.tabs>.tab>svg { +.page-constructor>#part-1>#info-type>.tabs>.tab>svg { width: 20px; height: 20px; } -#part-1>#info-type>.tabs>.tab>span { +.page-constructor>#part-1>#info-type>.tabs>.tab>span { margin-left: 6px; font-size: var(--FontSize1); font-weight: 400; } -#part-1>#info-type>.tabs>input[type="radio"]:checked+label { +.page-constructor>#part-1>#info-type>.tabs>input[type="radio"]:checked+label { color: var(--PrimaryColorText); fill: var(--PrimaryColorText); } -#part-1>#info-type>.tabs>.glider { +.page-constructor>#part-1>#info-type>.tabs>.glider { position: absolute; display: flex; height: 40px; @@ -105,47 +105,47 @@ } @media (min-width: 601px) { - #part-1>#info-type>.tabs>input[id="info-type-house"]:checked~.glider { + .page-constructor>#part-1>#info-type>.tabs>input[id="info-type-house"]:checked~.glider { transform: translateX(0); } - #part-1>#info-type>.tabs>input[id="info-type-homestead"]:checked~.glider { + .page-constructor>#part-1>#info-type>.tabs>input[id="info-type-homestead"]:checked~.glider { transform: translateX(100%); } - #part-1>#info-type>.tabs>input[id="info-type-points"]:checked~.glider { + .page-constructor>#part-1>#info-type>.tabs>input[id="info-type-points"]:checked~.glider { transform: translateX(200%); } } @media (max-width: 600px) { - #part-1>#info-type>.tabs { + .page-constructor>#part-1>#info-type>.tabs { flex-direction: column; } - #part-1>#info-type>.tabs>.tab { + .page-constructor>#part-1>#info-type>.tabs>.tab { width: calc(100% - 8px); padding: 0 4px; } - #part-1>#info-type>.tabs>.glider { + .page-constructor>#part-1>#info-type>.tabs>.glider { width: calc(100% - 8px); } - #part-1>#info-type>.tabs>input[id="info-type-house"]:checked~.glider { + .page-constructor>#part-1>#info-type>.tabs>input[id="info-type-house"]:checked~.glider { transform: translateY(0); } - #part-1>#info-type>.tabs>input[id="info-type-homestead"]:checked~.glider { + .page-constructor>#part-1>#info-type>.tabs>input[id="info-type-homestead"]:checked~.glider { transform: translateY(100%); } - #part-1>#info-type>.tabs>input[id="info-type-points"]:checked~.glider { + .page-constructor>#part-1>#info-type>.tabs>input[id="info-type-points"]:checked~.glider { transform: translateY(200%); } } -#part-1>form>div { +.page-constructor>#part-1>form>div { width: 100%; display: flex; margin: 20px 0; @@ -153,7 +153,7 @@ flex-direction: column; } -#part-1>form>div>label { +.page-constructor>#part-1>form>div>label { display: flex; justify-content: center; flex-direction: column; @@ -162,7 +162,7 @@ margin-bottom: 5px; } -#part-1>form>div>input { +.page-constructor>#part-1>form>div>input { width: calc(100% - 10px); min-width: 140px; padding: 0 5px; @@ -173,9 +173,9 @@ font-size: var(--FontSize2); } -#part-1>form>button, -#part-2>button, -#part-3>button { +.page-constructor>#part-1>form>button, +.page-constructor>#part-2>button, +.page-constructor>#part-3>button { border-radius: 6px; background: var(--PrimaryColor); color: var(--PrimaryColorText); @@ -187,27 +187,27 @@ text-transform: uppercase; } -#part-2>.osm-info { +.page-constructor>#part-2>.osm-info { padding-bottom: 20px; display: flex; flex-direction: column; align-items: center; } -#part-2>.osm-info>div { +.page-constructor>#part-2>.osm-info>div { display: flex; align-items: center; width: 100%; } -#part-2>.osm-info>div { +.page-constructor>#part-2>.osm-info>div { width: 100%; display: flex; align-items: flex-start; flex-direction: column; } -#part-2>.osm-info>div>label { +.page-constructor>#part-2>.osm-info>div>label { display: flex; justify-content: center; flex-direction: column; @@ -216,13 +216,13 @@ margin-bottom: 5px; } -#part-2>.osm-info>div>div { +.page-constructor>#part-2>.osm-info>div>div { display: flex; align-items: center; width: 100%; } -#part-2>.osm-info>div>div>input { +.page-constructor>#part-2>.osm-info>div>div>input { width: calc(100% - 10px); min-width: 140px; padding: 0 5px; @@ -233,25 +233,25 @@ font-size: var(--FontSize2); } -#part-2>.osm-info>div>div>a { +.page-constructor>#part-2>.osm-info>div>div>a { height: 26px; width: 26px; margin-left: 10px; } -#part-2>.osm-info>div>div>a>svg { +.page-constructor>#part-2>.osm-info>div>div>a>svg { height: 26px; width: 26px; fill: var(--ColorThemes3); } -#part-2>.osm-info>span { +.page-constructor>#part-2>.osm-info>span { font-size: var(--FontSize5); margin: 10px; color: var(--ColorThemes3); } -#part-2>.osm-info>button { +.page-constructor>#part-2>.osm-info>button { display: flex; align-items: center; justify-content: center; @@ -267,7 +267,7 @@ font-size: var(--FontSize3); } -#part-2>.block-map { +.page-constructor>#part-2>.block-map { width: 100%; height: 500px; border-radius: 6px; @@ -279,12 +279,12 @@ box-shadow: var(--shadow-l1); } -#part-2>#map { +.page-constructor>#part-2>.block-map>#map { width: 100%; height: 100%; } -#part-3>input { +.page-constructor>#part-3>input { font-weight: 500; position: absolute; right: 0; @@ -298,12 +298,12 @@ width: 40px; } -#part-3>#house { +.page-constructor>#part-3>#house { display: flex; overflow: auto; } -#part-3>#house>button { +.page-constructor>#part-3>#house>button { display: flex; position: relative; width: 34px; @@ -319,14 +319,14 @@ cursor: pointer; } -#part-3>#house>button>svg { +.page-constructor>#part-3>#house>button>svg { width: 20px; height: 20px; fill: var(--PrimaryColorText); transform: rotate(45deg); } -#part-3>#house>.entrance { +.page-constructor>#part-3>#house>.entrance { min-height: 200px; border: 1px solid var(--ColorThemes3); border-style: dashed; @@ -334,7 +334,7 @@ margin: 0 10px 10px 0; } -#part-3>#house>.entrance>.entrance-info>input { +.page-constructor>#part-3>#house>.entrance>.entrance-info>input { text-align: center; font-size: var(--FontSize5); font-weight: 400; @@ -346,8 +346,8 @@ width: calc(100% - 14px - 20px); } -#part-3>#house>.entrance>.entrance-info>button, -#part-3>#house>.entrance>.floor>.floor-info>button { +.page-constructor>#part-3>#house>.entrance>.entrance-info>button, +.page-constructor>#part-3>#house>.entrance>.floor>.floor-info>button { display: flex; position: relative; width: 34px; @@ -362,15 +362,15 @@ cursor: pointer; } -#part-3>#house>.entrance>.entrance-info>button>svg, -#part-3>#house>.entrance>.floor>.floor-info>button>svg { +.page-constructor>#part-3>#house>.entrance>.entrance-info>button>svg, +.page-constructor>#part-3>#house>.entrance>.floor>.floor-info>button>svg { width: 20px; height: 20px; fill: var(--PrimaryColorText); transform: rotate(45deg); } -#part-3>#house>.entrance>.floor { +.page-constructor>#part-3>#house>.entrance>.floor { position: relative; display: flex; width: calc(100% - 22px); @@ -379,7 +379,7 @@ border-radius: 4px; } -#part-3>#house>.entrance>.floor>.floor-info>h2 { +.page-constructor>#part-3>#house>.entrance>.floor>.floor-info>h2 { position: absolute; width: 65px; right: -1px; @@ -393,7 +393,7 @@ text-align: center; } -#part-3>#house>.entrance>.floor>.apartment { +.page-constructor>#part-3>#house>.entrance>.floor>.apartment { display: flex; position: relative; width: 60px; @@ -406,7 +406,7 @@ justify-content: center; } -#part-3>#house>.entrance>.floor>.apartment>input { +.page-constructor>#part-3>#house>.entrance>.floor>.apartment>input { width: 50px; height: 50px; font-size: var(--FontSize5); @@ -416,7 +416,7 @@ background: 0; } -#part-3>#house>.entrance>.floor>.apartment>button { +.page-constructor>#part-3>#house>.entrance>.floor>.apartment>button { position: absolute; top: 0; right: 0; @@ -433,22 +433,22 @@ justify-content: center; } -#part-3>#house>.entrance>.floor>.apartment>button>svg { +.page-constructor>#part-3>#house>.entrance>.floor>.apartment>button>svg { width: 16px; height: 16px; fill: var(--PrimaryColorText); } -#part-3>.info {} +.page-constructor>#part-3>.info {} -#part-3>.info>p { +.page-constructor>#part-3>.info>p { font-size: var(--FontSize2); color: var(--ColorThemes3); opacity: 0.8; font-style: oblique; } -#part-3>.info>button { +.page-constructor>#part-3>.info>button { border-radius: 6px; background: var(--ColorThemes3); width: fit-content; @@ -464,14 +464,14 @@ cursor: pointer; } -#part-3>.info>button>svg { +.page-constructor>#part-3>.info>button>svg { width: 20px; height: 20px; fill: var(--ColorThemes0); margin-right: 10px; } -#part-3>.info>button>span { +.page-constructor>#part-3>.info>button>span { font-size: var(--FontSize3); font-weight: 400; color: var(--ColorThemes0); diff --git a/web/lib/pages/editor/index.html b/web/lib/pages/editor/index.html index 8672af1..befd507 100644 --- a/web/lib/pages/editor/index.html +++ b/web/lib/pages/editor/index.html @@ -1,89 +1,84 @@