From 6ec6523d7137a17cc5ed50c72d480bc73bd71fe2 Mon Sep 17 00:00:00 2001 From: Rozenrod Date: Fri, 3 Oct 2025 17:11:31 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D0=BD=D1=8B=20=D1=80=D0=BE=D1=83=D1=82=D0=B5?= =?UTF-8?q?=D1=80=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=9F=D0=B5=D1=80=D0=B5=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=BE=20APi=20WebSocket=20=D0=B4=D0=BB=D1=8F=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D1=81=20=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D0=BC=D0=B8=20=D1=80=D0=BE=D1=83=D1=82=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 74 +- api/app.js | 12 +- docker-compose.yml | 49 +- nginx/default.conf.template | 49 +- web/index.html | 37 +- web/lib/pages/card/script_old.js | 452 ----- web/lib/pages/constructor_old/index.html | 181 -- web/lib/pages/constructor_old/script.js | 731 -------- web/lib/pages/constructor_old/style.css | 495 ----- web/lib/pages/editor_old/index.html | 89 - web/lib/pages/editor_old/script.js | 636 ------- web/lib/pages/editor_old/style.css | 536 ------ web/lib/pages/stand/{ => card}/index.html | 2 +- web/lib/pages/stand/{ => card}/script.js | 18 +- web/lib/pages/stand/{ => card}/style.css | 6 +- web/lib/pages/stand/list/index.html | 3 + web/lib/pages/stand/list/script.js | 6 + web/lib/pages/stand/list/style.css | 7 + web/lib/pages/{ => territory}/card/index.html | 10 +- web/lib/pages/{ => territory}/card/script.js | 158 +- web/lib/pages/{ => territory}/card/style.css | 0 .../{ => territory}/constructor/index.html | 4 +- .../{ => territory}/constructor/script.js | 112 +- .../{ => territory}/constructor/style.css | 0 .../pages/{ => territory}/editor/index.html | 6 +- .../pages/{ => territory}/editor/script.js | 164 +- .../pages/{ => territory}/editor/style.css | 0 .../history}/index.html | 0 .../history}/script.js | 10 +- .../history}/style.css | 10 +- web/lib/pages/territory/{ => list}/index.html | 18 +- web/lib/pages/territory/{ => list}/script.js | 22 +- web/lib/pages/territory/{ => list}/style.css | 0 .../manager}/index.html | 0 .../manager}/script.js | 2 +- .../manager}/style.css | 0 web/lib/router/routes.js | 20 +- web/server.js | 2 +- ws/config/db.js | 21 + ws/middleware/auth.js | 50 + ws/middleware/requirePermission.js | 11 + ws/routes/connection.js | 6 + ws/routes/index.js | 18 + ws/routes/message.js | 25 + ws/services/apartments.service.js | 31 + ws/services/buildings.service.js | 31 + ws/services/stand.service.js | 31 + ws/utils/broadcaster.js | 8 + ws/utils/ping.js | 14 + ws/ws.js | 240 +-- ws_old/Dockerfile | 13 + ws_old/package-lock.json | 1644 +++++++++++++++++ ws_old/package.json | 18 + ws_old/ws.js | 260 +++ 54 files changed, 2593 insertions(+), 3749 deletions(-) delete mode 100644 web/lib/pages/card/script_old.js delete mode 100644 web/lib/pages/constructor_old/index.html delete mode 100644 web/lib/pages/constructor_old/script.js delete mode 100644 web/lib/pages/constructor_old/style.css delete mode 100644 web/lib/pages/editor_old/index.html delete mode 100644 web/lib/pages/editor_old/script.js delete mode 100644 web/lib/pages/editor_old/style.css rename web/lib/pages/stand/{ => card}/index.html (99%) rename web/lib/pages/stand/{ => card}/script.js (91%) rename web/lib/pages/stand/{ => card}/style.css (97%) create mode 100644 web/lib/pages/stand/list/index.html create mode 100644 web/lib/pages/stand/list/script.js create mode 100644 web/lib/pages/stand/list/style.css rename web/lib/pages/{ => territory}/card/index.html (98%) rename web/lib/pages/{ => territory}/card/script.js (79%) rename web/lib/pages/{ => territory}/card/style.css (100%) rename web/lib/pages/{ => territory}/constructor/index.html (98%) rename web/lib/pages/{ => territory}/constructor/script.js (88%) rename web/lib/pages/{ => territory}/constructor/style.css (100%) rename web/lib/pages/{ => territory}/editor/index.html (90%) rename web/lib/pages/{ => territory}/editor/script.js (88%) rename web/lib/pages/{ => territory}/editor/style.css (100%) rename web/lib/pages/{territory_history => territory/history}/index.html (100%) rename web/lib/pages/{territory_history => territory/history}/script.js (93%) rename web/lib/pages/{territory_history => territory/history}/style.css (89%) rename web/lib/pages/territory/{ => list}/index.html (96%) rename web/lib/pages/territory/{ => list}/script.js (95%) rename web/lib/pages/territory/{ => list}/style.css (100%) rename web/lib/pages/{territory_manager => territory/manager}/index.html (100%) rename web/lib/pages/{territory_manager => territory/manager}/script.js (99%) rename web/lib/pages/{territory_manager => territory/manager}/style.css (100%) create mode 100644 ws/config/db.js create mode 100644 ws/middleware/auth.js create mode 100644 ws/middleware/requirePermission.js create mode 100644 ws/routes/connection.js create mode 100644 ws/routes/index.js create mode 100644 ws/routes/message.js create mode 100644 ws/services/apartments.service.js create mode 100644 ws/services/buildings.service.js create mode 100644 ws/services/stand.service.js create mode 100644 ws/utils/broadcaster.js create mode 100644 ws/utils/ping.js create mode 100644 ws_old/Dockerfile create mode 100644 ws_old/package-lock.json create mode 100644 ws_old/package.json create mode 100644 ws_old/ws.js diff --git a/README.md b/README.md index 00435cb..7d7e82c 100644 --- a/README.md +++ b/README.md @@ -68,19 +68,10 @@ cd /home/username/webapps/sheep-service.com ```bash docker-compose pull -docker-compose -p sheep-service up --build -d api ws web +docker-compose -p sheep-service up --build -d +docker-compose -p sheep-service up --build -d --scale api=5 --scale ws=5 --scale web=2 ``` -### Запуск з вбудованим NGINX - -```bash -docker-compose --profile with-nginx -p sheep-service up --build -d -``` - -💡 **Важливо:** Команду `docker-compose --profile with-nginx ...` використовують тільки якщо на сервері **не встановлений та не планується встановлення NGINX**. В цьому випадку буде запущений **вбудований NGINX на порті 80**, і **не потрібно встановлювати окремо NGINX та Certbot**. - -💡 **Порада:** Перевірте, що порти 4000, 4001, 4002 відкриті або перенаправлені на сервері. - --- ## 5. Встановлення NGINX @@ -156,71 +147,14 @@ server { server_name sheep-service.com www.sheep-service.com; - error_log /home/username/webapps/log/sheep-service.com.error.log error; - access_log /home/username/webapps/log/sheep-service.com.access.log; - - root /home/username/webapps/sheep-service.com; - index index.html; - error_page 404 /404.html; - - # Захист службових файлів - location ~ /\.(git|env) { - deny all; - } - - # Загальні CORS-заголовки - map $request_method $cors_preflight { - OPTIONS 1; - default 0; - } - - # API - location /api/ { - proxy_pass http://127.0.0.1:4000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - add_header 'Access-Control-Allow-Origin' '*' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; - - if ($cors_preflight) { - add_header 'Access-Control-Max-Age' 1728000; - add_header 'Content-Type' 'text/plain; charset=utf-8'; - add_header 'Content-Length' 0; - return 204; - } - } - - # WebSocket - location /ws { - proxy_pass http://127.0.0.1:4001; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } - - # Frontend location / { - proxy_pass http://127.0.0.1:4002; + proxy_pass http://127.0.0.1:4000$request_uri; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; - - if ($request_method = 'GET') { - add_header 'Access-Control-Allow-Origin' '*' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; - } + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } ``` diff --git a/api/app.js b/api/app.js index 0ab7b05..204d6d9 100644 --- a/api/app.js +++ b/api/app.js @@ -2,11 +2,17 @@ const express = require('express'); const app = express(); const routes = require('./routes/index'); require("dotenv").config(); -// const cors = require('cors'); +const cors = require('cors'); -const port = process.env.API_PORT || 4000; +const port = 4003; -// app.use(cors()) +app.use(cors({ + origin: "*", + methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], + allowedHeaders: ["DNT","User-Agent","X-Requested-With","If-Modified-Since","Cache-Control","Content-Type","Range","Authorization"], + credentials: true, + maxAge: 1728000 +})); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb' })); diff --git a/docker-compose.yml b/docker-compose.yml index 74e8b25..17aa20e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,25 @@ services: + web: + image: sheep-service/web + build: ./web + restart: always + expose: + - "4002" + volumes: + - "${CARDS_PATH:-./data}:/app/data/cards" + - "${MAP_PATH:-./data}:/app/data/map" + environment: + - CARDS_PATH=/app/data/cards/ + - MAP_PATH=/app/data/map/ + networks: + - network + api: - container_name: sheep-service-api image: sheep-service/api build: ./api restart: always - ports: - - "4000:4000" + expose: + - "4003" volumes: - "${DB_PATH:-./data}:/app/data" - "${CARDS_PATH:-./data}:/app/data/cards" @@ -21,12 +35,11 @@ services: - network ws: - container_name: sheep-service-ws image: sheep-service/ws build: ./ws restart: always - ports: - - "4001:4001" + expose: + - "4004" volumes: - "${DB_PATH:-./data}:/app/data" environment: @@ -34,30 +47,12 @@ services: networks: - network - web: - container_name: sheep-service-web - image: sheep-service/web - build: ./web - restart: always - ports: - - "4002:4002" - volumes: - - "${CARDS_PATH:-./data}:/app/data/cards" - - "${MAP_PATH:-./data}:/app/data/map" - environment: - - CARDS_PATH=/app/data/cards/ - - MAP_PATH=/app/data/map/ - networks: - - network - nginx: - profiles: ["with-nginx"] image: nginx:latest - container_name: sheep-service-nginx restart: always ports: - - "80:80" - - "443:443" + - "${HTTP_PORT}:80" + - "${HTTPS_PORT}:443" volumes: - ./nginx/default.conf.template:/etc/nginx/templates/default.conf.template:ro - ./nginx/certs:/etc/letsencrypt/live @@ -74,4 +69,4 @@ volumes: networks: network: - external: false \ No newline at end of file + driver: bridge \ No newline at end of file diff --git a/nginx/default.conf.template b/nginx/default.conf.template index b6b3dc5..2c0b94a 100644 --- a/nginx/default.conf.template +++ b/nginx/default.conf.template @@ -1,3 +1,21 @@ +upstream frontend { + server web:4002; +} + +upstream api_backend { + server api:4003; +} + +upstream ws_backend { + server ws:4004; +} + +# Загальні CORS-заголовки +map $request_method $cors_preflight { + OPTIONS 1; + default 0; +} + server { listen 80; listen [::]:80; @@ -16,38 +34,20 @@ server { deny all; } - # Загальні CORS-заголовки - map $request_method $cors_preflight { - OPTIONS 1; - default 0; - } - # API location /api/ { - proxy_pass http://api:4000; + proxy_pass http://api_backend$request_uri; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - add_header 'Access-Control-Allow-Origin' '*' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; - - if ($cors_preflight) { - add_header 'Access-Control-Max-Age' 1728000; - add_header 'Content-Type' 'text/plain; charset=utf-8'; - add_header 'Content-Length' 0; - return 204; - } } # WebSocket location /ws { - proxy_pass http://ws:4001; + proxy_pass http://ws_backend$request_uri; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; @@ -56,18 +56,11 @@ server { # Frontend location / { - proxy_pass http://web:4002; + proxy_pass http://frontend$request_uri; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; - - if ($request_method = 'GET') { - add_header 'Access-Control-Allow-Origin' '*' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, OPTIONS' always; - add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; - } } } \ No newline at end of file diff --git a/web/index.html b/web/index.html index 8171c5a..e9e29ce 100644 --- a/web/index.html +++ b/web/index.html @@ -73,35 +73,38 @@ - - + + - - + + - - + + - - + + + + + + + + - - - - - + + - - + + - - + + diff --git a/web/lib/pages/card/script_old.js b/web/lib/pages/card/script_old.js deleted file mode 100644 index ce0f238..0000000 --- a/web/lib/pages/card/script_old.js +++ /dev/null @@ -1,452 +0,0 @@ - -const Card = { - socket: null, - reconnectTimeout: null, - reconnectNumber: 0, - username: null, - listEntrances: [], - listApartment: [], - color_status: [ - ["var(--ColorThemes2)", "var(--ColorThemes3)"], - ["#fbf1e0", "#ff8300"], - ["#fce3e2", "#ff0000"], - ["#d7ddec", "#2919bd"], - ["#d5e9dd", "#11a568"], - ["#d7ebfa", "#3fb4fc"], - ["#e8dbf5", "#b381eb"] - ], - init: async (type, id) => { - let html = await fetch('/lib/pages/card/index.html').then((response) => response.text()); - app.innerHTML = html; - - house = id; - - if (Card.socket) Card.socket.close(1000, "Перезапуск соединения"); - - Card.sort(localStorage.getItem('sort_mode'), false) - - if (type == "house") { - Card.getEntrances({ update: false }); - Card.cloud.start(makeid(6)); - } - - - // Закриття вікно popup при натисканні за його межі - const block_card = document.getElementById('card-new-date'); - const mess = block_card.querySelector('.mess'); - if (!block_card.dataset.listenerAdded) { - block_card.addEventListener('click', function (event) { - if (!mess.contains(event.target)) { - Card.dateEditor.close(); - } - }); - block_card.dataset.listenerAdded = 'true'; - } - }, - cloud: { - status: (mode) => { - - let cloud_1 = document.getElementById('cloud_1'); - let cloud_2 = document.getElementById('cloud_2'); - let cloud_3 = document.getElementById('cloud_3'); - - switch (mode) { - case 'sync': - cloud_1.setAttribute('data-state', 'active'); - cloud_2.setAttribute('data-state', ''); - cloud_3.setAttribute('data-state', ''); - break; - case 'ok': - cloud_1.setAttribute('data-state', ''); - cloud_2.setAttribute('data-state', 'active'); - cloud_3.setAttribute('data-state', ''); - break; - case 'err': - cloud_1.setAttribute('data-state', ''); - cloud_2.setAttribute('data-state', ''); - cloud_3.setAttribute('data-state', 'active'); - break; - } - }, - start: (name) => { - if (!name) return; - - Card.username = name; - - let uuid = localStorage.getItem("uuid"); - Card.socket = new WebSocket(`${CONFIG.wss}?uuid=${uuid}`); - - Card.socket.onopen = function (e) { - console.log("[WebSocket | open] З'єднання встановлено"); - Card.cloud.status('ok'); - - const message = { - event: 'connection', - id: getTimeInSeconds(), - date: getTimeInSeconds(), - uuid: uuid, - username: name, - data: { - id: 1, - entrance_id: 1, - apartment_number: 1, - title: "1", - group_number: 1, - status: 1, - description: "", - created_at: 1727541827, - updated_at: 1727541827 - } - } - - Card.socket.send(JSON.stringify(message)); - - Card.reconnectNumber = 0; - clearTimeout(Card.reconnectTimeout); - }; - - Card.socket.onmessage = function (event) { - let data = JSON.parse(event.data) - - if (data.event == 'connection') { - if (data.username == Card.username) return - - console.log(`Доданий новий користувач на ім'я ${data.username}`); - } else if (data.event == 'message') { - Card.cloud.update(data); - - if (data.username == Card.username) return - - console.log(`${data.username} пише: `, data.data); - } - }; - - Card.socket.onclose = function (event) { - if (event.wasClean) { - console.log(`[WebSocket | close] З'єднання закрито чисто, код =${event.code} причина=${event.reason}`); - Card.cloud.status('err'); - } else { - console.log(`[WebSocket | close] З'єднання перервано`); - Card.cloud.status('err'); - - Card.reconnectTimeout = setTimeout(function () { - Card.reconnectNumber++; - if (Card.reconnectNumber > 5) { - Card.reconnectNumber = 0; - clearTimeout(Card.reconnectTimeout); - - const result = confirm(`З'єднання розірвано! Перепідключитись?`); - if (result) { - Card.getEntrances({ update: true }); - Card.cloud.start(Card.username); - } - } else { - Card.getEntrances({ update: true }); - Card.cloud.start(Card.username); - } - }, 500); - } - }; - - Card.socket.onerror = function (error) { - console.log(`[WebSocket | error]`); - Card.cloud.status('err'); - }; - }, - mess: ({ number, id, update, time }) => { - const pos = Card.listApartment[number].map(e => e.id).indexOf(id); - let apartment = Card.listApartment[number][pos]; - - - let status = document.getElementById(`status_${id}`); - let description = document.getElementById(`description_${id}`); - - let date = () => { - if (!update && !time) { - return apartment.updated_at; - } else if (update && !time) { - return getTimeInSeconds(); - } else if (update && time) { - return getTimeInSeconds(time); - } - } - - apartment.description = description.value; - apartment.status = Number(status.value); - apartment.updated_at = date(); - - status.style.backgroundColor = Card.color_status[status.value][0]; - status.style.color = Card.color_status[status.value][1]; - status.style.border = `1px solid ${Card.color_status[status.value][1]}`; - - let message = { - event: 'message', - id: getTimeInSeconds(), - date: getTimeInSeconds(), - - username: Card.username, - data: { - id: apartment.id, - entrance_id: apartment.entrance_id, - apartment_number: apartment.apartment_number, - title: apartment.title, - group_number: apartment.group_number, - status: apartment.status, - description: apartment.description, - updated_at: apartment.updated_at, - sheep_id: USER.id - } - } - - if (Card.socket && Card.socket.readyState === WebSocket.OPEN) { - Card.socket.send(JSON.stringify(message)); - } else { - console.warn("WebSocket не підключено. Повідомлення не надіслано."); - const result = confirm(`З'єднання розірвано! Перепідключитись?`); - if (result) { - Card.getEntrances({ update: true }); - Card.start(Card.username); - } - } - - if (update) { - let sort_mode = localStorage.getItem('sort_mode') ?? '1'; - - if (sort_mode == '3') { - let child = document.getElementById(`card_${apartment.id}`); - document.getElementById(`apartments_${apartment.entrance_id}`).removeChild(child); - document.getElementById(`apartments_${apartment.entrance_id}`).append(child); - child.style.border = "1px solid var(--PrimaryColor)"; - } else if (sort_mode == '4') { - let child = document.getElementById(`card_${apartment.id}`); - document.getElementById(`apartments_${apartment.entrance_id}`).removeChild(child); - document.getElementById(`apartments_${apartment.entrance_id}`).prepend(child); - child.style.border = "1px solid var(--PrimaryColor)"; - } - } - }, - update: (message) => { - if (!document.getElementById(`status_${message.data.id}`)) return; - - document.getElementById(`card_${message.data.id}`).style.backgroundColor = Card.color_status[message.data.status][0]; - document.getElementById(`card_${message.data.id}`).style.color = Card.color_status[message.data.status][1]; - document.getElementById(`card_${message.data.id}`).style.border = `1px solid ${Card.color_status[message.data.status][1]}`; - - document.getElementById(`status_${message.data.id}`).style.backgroundColor = Card.color_status[message.data.status][0]; - document.getElementById(`status_${message.data.id}`).style.color = Card.color_status[message.data.status][1]; - document.getElementById(`status_${message.data.id}`).style.border = `1px solid ${Card.color_status[message.data.status][1]}`; - - - - document.getElementById(`status_${message.data.id}`).value = message.data.status; - document.getElementById(`description_${message.data.id}`).value = message.data.description; - document.getElementById(`date_text_${message.data.id}`).innerText = formattedDateTime(message.data.updated_at); - } - }, - getEntrances: ({ house_id = house, update = false }) => { - const uuid = localStorage.getItem('uuid'); - const URL = `${CONFIG.api}/house/${house_id}/entrances`; - fetch(URL, { - method: 'GET', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - } - }) - .then(function (response) { - return response.json(); - }) - .then(function (data) { - Card.listEntrances = data; - - const element_list = document.getElementById('list'); - - if (update) { - for (let i = 0; i < Card.listEntrances.length; i++) { - const element = Card.listEntrances[i]; - Card.getApartment({ id: element.id, number: element.entrance_number, update: update }); - } - } else { - element_list.innerHTML = ""; - for (let i = 0; i < Card.listEntrances.length; i++) { - const element = Card.listEntrances[i]; - - let status = () => { - if ((element.history.name == "Групова" || element.history.name == USER.name) && element.working) return "open"; - else if (USER.mode == 2 || (USER.mode == 1 && USER.possibilities.can_manager_territory)) return "close"; - else return "style='display: none;'" - } - - let statusIcon = () => { - if ((element.history.name == "Групова" || element.history.name == USER.name) && element.working) return '' - else return ' ' - } - - element_list.innerHTML += ` -
- -

${element.title}

- ${statusIcon()} -
-
- -
-
- `; - Card.getApartment({ id: element.id, number: element.entrance_number, update: false }); - } - } - }) - - }, - - getApartment: ({ id, number, update }) => { - const uuid = localStorage.getItem('uuid'); - const URL = `${CONFIG.api}/apartment/${id}`; - fetch(URL, { - method: 'GET', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - } - }) - .then(function (response) { - return response.json(); - }) - .then(function (data) { - Card.listApartment[number] = data; - - if (update) { - for (let i = 0; i < data.length; i++) { - const element = data[i]; - - let now = new Date(element.updated_at); - now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); - now = now.toISOString().slice(0, 16) - - document.getElementById(`card_${element.id}`).setAttribute('style', `border: 1px solid ${Card.color_status[element.status][1]};background: ${Card.color_status[element.status][0]};color: ${Card.color_status[element.status][1]};`); - document.getElementById(`status_${element.id}`).value = element.status; - document.getElementById(`status_${element.id}`).setAttribute('style', `background-color: ${Card.color_status[element.status][0]}; color: ${Card.color_status[element.status][1]}; border: 1px solid ${Card.color_status[element.status][1]};`); - document.getElementById(`date_${element.id}`).setAttribute('onclick', `Card.dateEditor.open({id: ${element.id}, number: ${number}, updated_at: ${element.updated_at}})`); - document.getElementById(`date_text_${element.id}`).innerText = element.updated_at ? formattedDateTime(element.updated_at) : "0.0.0000 00:00"; - document.getElementById(`description_${element.id}`).innerText = element.description ?? ""; - } - } else { - let sort_mode = localStorage.getItem('sort_mode') ?? 1; - - if (sort_mode == "1") - data.sort((a, b) => a.apartment_number - b.apartment_number); - else if (sort_mode == "2") - data.sort((a, b) => b.apartment_number - a.apartment_number); - else if (sort_mode == "3") - data.sort((a, b) => a.updated_at - b.updated_at); - else if (sort_mode == "4") - data.sort((a, b) => b.updated_at - a.updated_at); - else - data.sort((a, b) => a.apartment_number - b.apartment_number); - - for (let i = 0; i < data.length; i++) { - const element = data[i]; - - let now = new Date(element.updated_at); - now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); - now = now.toISOString().slice(0, 16) - - let disabled = () => { - if (USER.possibilities.can_manager_territory) return ''; - else if (element.status == 2) return "disabled"; - } - - document.getElementById(`apartments_${id}`).innerHTML += ` -
-
- кв.${element.title} - - -
- -
- `; - } - } - }) - - }, - sort: (mode, load) => { - const sortIds = ['sort_1', 'sort_2', 'sort_3', 'sort_4']; - - sortIds.forEach(id => { - const el = document.getElementById(id); - if (el) el.setAttribute('data-state', ''); - }); - - let index = parseInt(mode, 10); - if (isNaN(index) || index < 1 || index > 4) index = 1; - - const activeEl = document.getElementById(`sort_${index}`); - if (activeEl) activeEl.setAttribute('data-state', 'active'); - - localStorage.setItem('sort_mode', index.toString()); - - if (!load) Card.getEntrances({ update: false }); - }, - dateEditor: { - open: ({ id, number, updated_at }) => { - const block = document.getElementById('card-new-date'); - const card_new_date_input = document.getElementById('card-new-date-input'); - const card_new_date_button = document.getElementById('card-new-date-button'); - - let now = new Date(updated_at); - now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); - now = now.toISOString().slice(0, 16) - - card_new_date_input.value = now; - card_new_date_input.setAttribute("onchange", `Card.dateEditor.edit({ id: ${id}, number: ${number} })`) - card_new_date_button.setAttribute("onclick", `Card.dateEditor.edit({ id: ${id}, number: ${number}, type: 'now'})`) - - block.style.display = ""; - setTimeout(() => { - block.style.opacity = "1"; - }, 100) - - }, - close: () => { - const block = document.getElementById('card-new-date'); - - block.style.opacity = "0"; - setTimeout(() => { - block.style.display = "none"; - }, 200) - }, - edit: ({ id, number, type }) => { - const card_new_date_input = document.getElementById('card-new-date-input'); - - if (type == "now") { - Card.cloud.mess({ number: number, id: id, update: true }); - } else { - if (card_new_date_input.value) { - let date = new Date(card_new_date_input.value); - const timestamp = date.getTime(); - Card.cloud.mess({ number: number, id: id, update: true, time: timestamp }); - } else { - Card.cloud.mess({ number: number, id: id }); - } - } - - Card.dateEditor.close(); - } - } -} \ No newline at end of file diff --git a/web/lib/pages/constructor_old/index.html b/web/lib/pages/constructor_old/index.html deleted file mode 100644 index 1546915..0000000 --- a/web/lib/pages/constructor_old/index.html +++ /dev/null @@ -1,181 +0,0 @@ -
-
- - Крок 1. Інформація про будинок, територію, точки на карті - -
-
-
- - - - - - - -
-
- -
- - -
- -
- - -
- -
- - -
- -
- -
- - - -
-
- - -
-
- -
- - Крок 2. Створення підїздів - -
-
- -
- - -
-
- - - - - - -
-
-
- -
-
- -
- Крок 3. Конструктор квартир - -
-
- - -
-
-
diff --git a/web/lib/pages/constructor_old/script.js b/web/lib/pages/constructor_old/script.js deleted file mode 100644 index ae4a6f5..0000000 --- a/web/lib/pages/constructor_old/script.js +++ /dev/null @@ -1,731 +0,0 @@ -let map, houseGroup, entransePolygonsGroup, entranseNumPolygonsGroup, splitPolygonsGroup, RectangleGroup; -let numApartments = 1; -let mode = ''; - - -const Constructor = { - info: { - type: null, - title: null, - number: null, - points: [], - points_number: [], - point_icons: [], - geo: [], - osm_id: [], - settlement: [], - description: null, - entrance: [], - apartments: {}, - zoom: null - }, - init: async () => { - let html = await fetch('/lib/pages/constructor/index.html').then((response) => response.text()); - app.innerHTML = html; - - map = ""; - map = ""; - houseGroup = ""; - entransePolygonsGroup = ""; - entranseNumPolygonsGroup = ""; - splitPolygonsGroup = ""; - RectangleGroup = ""; - numApartments = 1; - - Constructor.apartments.init(); - - document.querySelectorAll('input[name="info-type"]').forEach(radio => { - radio.addEventListener('change', event => { - console.log(`Выбран: ${event.target.value}`); - Constructor.info.type = event.target.value; - - let detailsInfo_number = document.getElementById('details-info-number'); - let detailsInfo_osm = document.getElementById('details-info-osm'); - let detailsInfo_number_title = document.getElementById('info-number-title'); - let detailsInfo_osm_title = document.getElementById('info-osm-title'); - - let detailsMap_title = document.getElementById('details-map-title'); - let detailsMap_buttons_entranse = document.getElementById('details-map-buttons-entranse'); - let detailsMap_buttons_homestead = document.getElementById('details-map-buttons-homestead'); - let detailsMap_button = document.getElementById('map-form-button'); - - - let detailsArea = document.getElementById('details-area'); - - switch (event.target.value) { - case 'points': - detailsInfo_number.style.display = "none"; - detailsInfo_osm.style.display = "none"; - detailsInfo_number_title.removeAttribute("required"); - detailsInfo_osm_title.removeAttribute("required"); - - detailsMap_title.innerHTML = "Крок 2. Створення точок на карті" - detailsMap_buttons_entranse.style.display = "none"; - detailsMap_buttons_homestead.style.display = "none"; - detailsMap_button.innerText = "Зберегти"; - - detailsArea.style.display = "none"; - break; - - case 'homestead': - detailsInfo_number.style.display = ""; - detailsInfo_osm.style.display = ""; - detailsInfo_number_title.setAttribute("required", ""); - detailsInfo_osm_title.setAttribute("required", ""); - - detailsMap_title.innerHTML = "Крок 2. Створення житлових територій" - detailsMap_buttons_entranse.style.display = "none"; - detailsMap_buttons_homestead.style.display = ""; - detailsMap_button.innerText = "Зберегти"; - - detailsArea.style.display = "none"; - break; - - default: - detailsInfo_number.style.display = ""; - detailsInfo_osm.style.display = ""; - detailsInfo_number_title.setAttribute("required", ""); - detailsInfo_osm_title.setAttribute("required", ""); - - detailsMap_title.innerHTML = "Крок 2. Створення підʼїздів" - detailsMap_buttons_entranse.style.display = ""; - detailsMap_buttons_homestead.style.display = "none"; - detailsMap_button.innerText = "Далі"; - - detailsArea.style.display = ""; - break; - } - }); - }); - - document.getElementById("info-form").addEventListener("submit", function (event) { - event.preventDefault(); - - let details_map = document.getElementById("details-map"); - details_map.removeAttribute("disabled"); - details_map.open = true; - - const infoForm = document.getElementById("info-form"); - let osm = () => { - const a = document.getElementById("info-osm-title").value; - const b = a.replace(/\s+/g, "").split(',') - - return b; - } - - Constructor.info.type = infoForm.querySelector('input[name="info-type"]:checked').value; - Constructor.info.title = document.getElementById("info-address-title").value; - Constructor.info.number = document.getElementById("info-number-title").value; - Constructor.info.settlement = document.getElementById("info-settlement-title").value; - Constructor.info.osm_id = osm(); - - Constructor.osm.init(); - - Constructor.osm.setMap(); - }) - - document.getElementById("map-form").addEventListener("submit", async function (event) { - event.preventDefault(); - - let details_area = document.getElementById("details-area"); - details_area.removeAttribute("disabled"); - details_area.open = true; - - switch (Constructor.info.type) { - case 'points': - - break; - - case 'homestead': - await Constructor.api.setPack(); - break; - - default: - let details_area = document.getElementById("details-area"); - details_area.removeAttribute("disabled"); - details_area.open = true; - break; - } - }) - - document.getElementById("area-form").addEventListener("submit", async function (event) { - event.preventDefault(); - - await Constructor.api.setPack(); - }) - }, - apartments: { - init: () => { - let listArea = document.getElementById('list-area'); - listArea.innerHTML = ``; - - for (const key in Constructor.info.apartments) { - const element = Constructor.info.apartments[key]; - const pos = Constructor.info.entrance.map(e => e.editor_id).indexOf(key); - console.log(element); - - let listArea = document.getElementById('list-area'); - listArea.innerHTML += ` -
-

${Constructor.info.entrance[pos].title}

- - -
-
- `; - - element.sort((a, b) => a.floors_number - b.floors_number); - - let uniqueFloors = [...new Set(element.map(obj => obj.floors_number))]; - - for (let i = 0; i < uniqueFloors.length; i++) { - let num = uniqueFloors[i]; - - let areaBlock = document.getElementById(`area-${key}`); - - let div = document.createElement('div'); - div.className = "block-apartments-floors"; - div.id = `floors-${key}-${num}` - div.innerHTML = ` - - `; - - areaBlock.prepend(div); - } - - element.sort((a, b) => b.title - a.title); - - for (let i = 0; i < element.length; i++) { - const apartment = element[i]; - let num = apartment.floors_number; - - let floorsBlock = document.getElementById(`floors-${key}-${apartment.floors_number}`); - - let div = document.createElement('div'); - div.className = "block-apartments-number"; - div.id = `block-apartments-${key}-${apartment.editor_id}` - div.innerHTML = ` - - - `; - - floorsBlock.prepend(div); - } - - element.sort((a, b) => b.floors_number - a.floors_number); - } - }, - editNum: (element) => { - numApartments = Number(element.value); - }, - addFloors: (area) => { - let areaBlock = document.getElementById(`area-${area}`); - - let uniqueFloors = [...new Set(Constructor.info.apartments[area].map(obj => obj.floors_number))]; - let new_floors = uniqueFloors.length + 1; - let new_id = makeid(5); - - let div = document.createElement('div'); - div.className = "block-apartments-floors"; - div.id = `floors-${area}-${new_floors}` - div.innerHTML = ` -

Поверх ${new_floors}

-
- - -
- - ` - - areaBlock.prepend(div); - - Constructor.info.apartments[area].push({ - editor_id: new_id, - entrance_id: null, - apartment_number: Constructor.info.apartments[area].length, - title: numApartments, - floors_number: new_floors - }); - - numApartments++; - - let next_apartment_title = document.getElementById('next-apartment-title'); - next_apartment_title.value = numApartments; - - }, - addApartment: (area, floors) => { - let new_id = makeid(5); - - Constructor.info.apartments[area].push({ - editor_id: new_id, - entrance_id: null, - apartment_number: Constructor.info.apartments[area].length, - title: numApartments, - floors_number: Number(floors) - }) - - let floorsBlock = document.getElementById(`floors-${area}-${floors}`); - document.getElementById(`buttonApartment-${area}-${floors}`).remove(); - - floorsBlock.innerHTML += ` -
- - -
- `; - floorsBlock.innerHTML += `` - - numApartments++; - - let next_apartment_title = document.getElementById('next-apartment-title'); - next_apartment_title.value = numApartments; - }, - editApartment: (area, apartament) => { - let input = document.getElementById(`apartament-${area}-${apartament}`); - input.setAttribute("value", input.value); - - const pos = Constructor.info.apartments[area].map(e => e.editor_id).indexOf(apartament); - - info.apartments[area][pos].title = input.value; - }, - deleteApartment: (area, apartament) => { - document.getElementById(`block-apartments-${area}-${apartament}`).remove(); - const pos = Constructor.info.apartments[area].map(e => e.editor_id).indexOf(apartament); - Constructor.info.apartments[area].splice(pos, 1); - - numApartments--; - let next_apartment_title = document.getElementById('next-apartment-title'); - next_apartment_title.value = numApartments; - } - }, - osm: { - init: () => { - let center = { lat: 49.5629016, lng: 25.6145625 }; - let zoom = 19; - - let googleHybrid = L.tileLayer('http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', { - maxZoom: 20, - minZoom: 15, - subdomains: ['mt0', 'mt1', 'mt2', 'mt3'] - }); - - let osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 19, - minZoom: 15 - }); - - let mytile = L.tileLayer('https://sheep-service.com/map/{z}/{x}/{y}.webp', { - maxZoom: 20, - minZoom: 15, - tms: true - }); - - if (!map) { - - houseGroup = new L.FeatureGroup(); - splitPolygonsGroup = new L.FeatureGroup(); - RectangleGroup = new L.FeatureGroup(); - entransePolygonsGroup = new L.FeatureGroup(); - entranseNumPolygonsGroup = new L.FeatureGroup(); - - map = L.map('map', { - renderer: L.canvas(), - center, - zoom, - layers: [ - googleHybrid, - osm, - mytile, - houseGroup, - entransePolygonsGroup, - entranseNumPolygonsGroup, - splitPolygonsGroup, - RectangleGroup, - ], - zoomControl: false - }); - - - let baseMaps = { - "Google Hybrid": googleHybrid, - "OpenStreetMap": osm, - "Territory Map": mytile - }; - let overlayMaps = { - "Будинки": houseGroup, - "Під'їзди": entransePolygonsGroup, - "Номера під'їздів": entranseNumPolygonsGroup, - "Слой редактирования": splitPolygonsGroup, - "Слой линейки": RectangleGroup, - }; - - L.control.layers(baseMaps, overlayMaps, { position: 'bottomright' }).addTo(map); - - map.pm.setLang("ua"); - - map.pm.addControls({ - position: 'bottomright', - drawCircleMarker: false, - drawPolyline: false, - drawPolygon: false, - drawRectangle: false, - drawCircle: false, - drawText: false, - drawMarker: false, - cutPolygon: false, - tooltips: false - }); - map.pm.toggleControls() - - map.pm.setGlobalOptions({ - layerGroup: splitPolygonsGroup - }) - } - - Constructor.editor.init(); - }, - getOSM: async (wayId) => { - const overpassUrl = `https://overpass-api.de/api/interpreter?data=[out:json];way(${wayId});(._;>;);out;`; - - return await fetch(overpassUrl) - .then(response => response.json()) - .then(data => { - const nodes = new Map(); - - data.elements.forEach(el => { - if (el.type === "node") { - nodes.set(el.id, { lat: el.lat, lng: el.lon }); - } - }); - - const way = data.elements.find(el => el.type === "way"); - if (way) { - const coordinates = way.nodes.map(nodeId => nodes.get(nodeId)); - console.log("Координаты точек:", coordinates); - - return [coordinates]; - } else { - console.log("Way не найден!"); - } - }) - .catch(error => console.error("Ошибка запроса:", error)); - }, - setMap: async () => { - houseGroup.clearLayers(); - - if (!Constructor.info.osm_id) { - let osm = () => { - const a = document.getElementById("info-osm-title").value; - const b = a.replace(/\s+/g, "").split(',') - - return b; - } - Constructor.info.osm_id = osm(); - } - - for (let i = 0; i < Constructor.info.osm_id.length; i++) { - const element = await Constructor.osm.getOSM(Constructor.info.osm_id[i]); - let coords = []; - element[0].forEach((feature) => coords.push([feature.lat, feature.lng])); - - let centerPoint = turf.centerOfMass(turf.polygon([coords])); - - - if (Constructor.info.type == "homestead") { - map.setView([centerPoint.geometry.coordinates[0], centerPoint.geometry.coordinates[1]], 17); - - L.polygon(element, { - color: "#f2bd53", - radius: 500, - fillOpacity: 0.3, - dashArray: '20, 15', - dashOffset: '20', - }).addTo(houseGroup); - } else if (Constructor.info.type == "house") { - map.setView([centerPoint.geometry.coordinates[0], centerPoint.geometry.coordinates[1]], 18); - - Constructor.info.points.push(element); - Constructor.info.points_number.push(element[0][0]); - - L.polygon(element, { - color: "#585858", - fillColor: "#f2bd53", - fillOpacity: 0.8, - tm_id: `house_${i}` - }).addTo(houseGroup); - } - } - - console.log(Constructor.info); - - } - }, - api: { - setPack: async () => { - let area_form_button = document.getElementById('area-form-button'); - area_form_button.innerText = "Зачекайте..."; - - Constructor.info.geo = await map.getCenter(); - Constructor.info.zoom = await map.getZoom(); - - console.log(Constructor.info); - - const uuid = localStorage.getItem('uuid'); - const URL = `${CONFIG.api}constructor`; - await fetch(URL, { - method: 'POST', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - }, - body: JSON.stringify(Constructor.info) - }) - .then(response => { - if (response.status == 200) { - console.log({ 'setPack': 'ok' }); - area_form_button.innerText = "Запис додано"; - - return response.json() - } else { - console.log('err'); - area_form_button.innerText = "Помилка запису"; - - return - } - }) - .then(data => { - console.log(data); - - Territory.house.list = []; - Territory.homestead.list = []; - Router.navigate(`/territory/manager/${Constructor.info.type}/${data.id}`); - - setTimeout(() => { - area_form_button.innerText = "Зберегти"; - }, 3000); - }) - .catch(err => { - console.log(err); - area_form_button.innerText = "Помилка запису"; - }) - } - }, - editor: { - init: () => { - map.on('pm:create', function (event) { - let layer = event.layer; - - let newCoords = layer.getLatLngs()[0].map(function (coords) { - return [coords.lng, coords.lat]; - }); - newCoords.push(newCoords[0]); - let turfNew = turf.polygon([newCoords]); - - if (mode == 'entranse') { - console.log(L.PM.Utils.findLayers(houseGroup)); - for (let i = 0; i < L.PM.Utils.findLayers(houseGroup).length; i++) { - const polygon = L.PM.Utils.findLayers(houseGroup)[i]._latlngs; - - let polygonCoords = polygon[0].map(function (coords) { - return [coords.lng, coords.lat]; - }); - polygonCoords.push(polygonCoords[0]); // Замикаємо полігон - let turfPolygon = turf.polygon([polygonCoords]); - - // Пошук точки перехрестя - let intersections = turf.intersect(turfNew, turfPolygon); - - if (intersections) { - let points = []; - let coords = []; - - intersections.geometry.coordinates[0].forEach(function (feature) { - coords.push([feature[1], feature[0]]) - points.push({ lat: feature[1], lng: feature[0] }) - }); - - let centerPoint = turf.centerOfMass(turf.polygon([coords])); - - let points_number = { lat: centerPoint.geometry.coordinates[0], lng: centerPoint.geometry.coordinates[1] } - - let newID = makeid(6); - - Constructor.info.entrance.push({ - editor_id: newID, - house_id: null, - entrance_number: Constructor.info.entrance.length, - title: `Під'їзд ${Constructor.info.entrance.length + 1}`, - points: points, - points_number: points_number, - floors_quantity: null, - apartments_quantity: null, - created_at: null - }) - - Constructor.info.apartments[newID] = []; - Constructor.apartments.init(); - - let listEntranse = document.getElementById('list-entranse'); - listEntranse.style.display = ""; - listEntranse.innerHTML += ` -
- - -
- `; - - L.polygon(coords, { color: 'red' }).addTo(entransePolygonsGroup); - - let myIcon = L.divIcon({ className: 'entranse_number', html: `
${Constructor.info.entrance.length}
` }); - L.marker(centerPoint.geometry.coordinates, { icon: myIcon }).addTo(entranseNumPolygonsGroup); - - } - } - } - }); - }, - drawEntranse: () => { - mode = 'entranse'; - - map.pm.setGlobalOptions({ - layerGroup: splitPolygonsGroup - }) - map.pm.setPathOptions({ - color: '#f2bd53', - fillColor: '#f2bd53', - fillOpacity: 0.5, - radius: 500 - }); - - if (map.pm.globalDragModeEnabled()) { - map.pm.disableDraw(); - } else { - map.pm.enableDraw("Polygon", { - snappable: true, - snapDistance: 20, - tooltips: false, - templineStyle: { color: '#f2bd53' }, - hintlineStyle: { color: '#f2bd53', dashArray: [5, 5] } - }); - } - }, - drawRectangle: () => { - mode = 'rectangle'; - - document.getElementById('ruler_divide').style.display = 'block' - document.getElementById('ruler').style.width = "calc(50% - 5px)" - document.getElementById('ruler_divide').style.width = "calc(50% - 5px)" - document.getElementById('ruler').innerHTML = 'Лінійка' - - map.pm.toggleControls() - RectangleGroup.clearLayers(); - RectangleGroup.addTo(map); - - map.pm.setGlobalOptions({ - layerGroup: RectangleGroup - }) - map.pm.setPathOptions({ - color: '#b645ef', - fillColor: '#b645ef', - fillOpacity: 0.5, - radius: 500 - }); - - if (map.pm.globalDragModeEnabled()) { - map.pm.disableDraw(); - } else { - map.pm.enableDraw("Rectangle", { - snappable: true, - snapDistance: 20, - tooltips: false, - templineStyle: { color: '#b645ef' }, - hintlineStyle: { color: '#b645ef', dashArray: [5, 5] } - }); - } - }, - ruler: (n) => { - n = prompt('На сколько поделить линейку ?', 2); - const polygon = L.PM.Utils.findLayers(RectangleGroup)[0]._latlngs; - let newCoords = polygon[0].map(function (coords) { - return [coords.lng, coords.lat]; - }); - newCoords.push(newCoords[0]); - let turfNew = turf.polygon([newCoords]); - - console.log(turfNew); - - - var line = turf.polygonToLine(turfNew); - console.log(line.geometry.coordinates); - - coords = line.geometry.coordinates; - - for (let i = 1; i < n; i++) { - let a1 = (((coords[2][1] - coords[1][1]) / n) * i + coords[1][1]) - let b1 = (((coords[2][0] - coords[1][0]) / n) * i + coords[1][0]) - let a2 = (((coords[3][1] - coords[0][1]) / n) * i + coords[0][1]) - let b2 = (((coords[3][0] - coords[0][0]) / n) * i + coords[0][0]) - - let c1 = (((coords[1][1] - coords[0][1]) / n) * i + coords[0][1]) - let d1 = (((coords[1][0] - coords[0][0]) / n) * i + coords[0][0]) - let c2 = (((coords[2][1] - coords[3][1]) / n) * i + coords[3][1]) - let d2 = (((coords[2][0] - coords[3][0]) / n) * i + coords[3][0]) - - L.circleMarker([a1, b1], { radius: 2, color: 'red' }).addTo(RectangleGroup); - L.circleMarker([a2, b2], { radius: 2, color: 'red' }).addTo(RectangleGroup); - L.circleMarker([c1, d1], { radius: 2, color: 'red' }).addTo(RectangleGroup); - L.circleMarker([c2, d2], { radius: 2, color: 'red' }).addTo(RectangleGroup); - } - - coords.forEach(function (feature) { - L.circleMarker([feature[1], feature[0]], { radius: 2, color: 'red' }).addTo(RectangleGroup); - }); - }, - dellEntranse: (id) => { - delete Constructor.info.apartments[id]; - - const pos = Constructor.info.entrance.map(e => e.editor_id).indexOf(id); - console.log(pos); - - - Constructor.info.entrance.splice(pos, 1); - console.log(id); - - document.getElementById(`Entranse_${id}`).remove(); - - let Entranse = Object.keys(entransePolygonsGroup._layers); - let numsEntranse = Object.keys(entranseNumPolygonsGroup._layers); - console.log(Entranse, Entranse[pos]); - console.log(numsEntranse, numsEntranse[pos]); - - entransePolygonsGroup.removeLayer(entransePolygonsGroup._layers[Entranse[pos]]); - entranseNumPolygonsGroup.removeLayer(entranseNumPolygonsGroup._layers[numsEntranse[pos]]); - - Constructor.apartments.init(); - }, - editEntranse: (id) => { - const input = document.getElementById(`Entranse_input_${id}`); - - const pos = Constructor.info.entrance.map(e => e.editor_id).indexOf(id); - - Constructor.info.entrance[pos].entrance_number = Number(input.value) - 1; - Constructor.info.entrance[pos].title = `Під'їзд ${input.value}`; - - let numsEntranse = Object.keys(entranseNumPolygonsGroup._layers); - let newIcon = L.divIcon({ className: 'entranse_number', html: `
${input.value}
` }); - entranseNumPolygonsGroup._layers[numsEntranse[pos]].setIcon(newIcon); - - input.setAttribute("value", input.value); - - Constructor.apartments.init(); - } - } -} \ No newline at end of file diff --git a/web/lib/pages/constructor_old/style.css b/web/lib/pages/constructor_old/style.css deleted file mode 100644 index 8587192..0000000 --- a/web/lib/pages/constructor_old/style.css +++ /dev/null @@ -1,495 +0,0 @@ -.page-constructor { - width: calc(100% - 40px); - display: flex; - flex-direction: column; - align-items: center; - margin: 20px 20px 0 20px; -} - -.page-constructor form>button { - border-radius: 6px; - background: var(--PrimaryColor); - color: var(--PrimaryColorText); - width: 100%; - height: 40px; - font-size: var(--FontSize3); - font-weight: 400; - margin: 20px 0; - text-transform: uppercase; -} - -.page-constructor details { - border-radius: 10px; - width: 100%; - display: flex; - flex-direction: column; - align-items: stretch; - margin-bottom: 20px; - background: var(--ColorThemes1); - color: var(--ColorThemes3); - border: 1px solid var(--ColorThemes2); - box-shadow: var(--shadow-l1); -} - -.page-constructor>details[disabled] summary, -.page-constructor>details.disabled summary { - pointer-events: none; - user-select: none; -} - -.page-constructor>details summary::-webkit-details-marker, -.page-constructor>details summary::marker { - display: none; - content: ""; -} - -.page-constructor summary { - width: calc(100% - 40px); - cursor: pointer; - color: var(--ColorThemes3); - border-radius: var(--border-radius); - font-size: var(--FontSize5); - font-weight: 300; - padding: 20px; - position: relative; -} - -.page-constructor summary span { - font-weight: 500; -} - -.page-constructor summary input { - font-weight: 500; - position: absolute; - right: 0; - top: 0; - padding: 10px; - margin: 13px; - font-size: var(--FontSize1); - background: var(--ColorThemes3); - color: var(--ColorThemes0); - border-radius: 6px; - width: 40px; -} - -.page-constructor #info-form, -.page-constructor #map-form, -.page-constructor #area-form { - padding: 0 20px; -} - -#details-info-type { - display: flex; - align-items: center; - justify-content: center; -} - -#details-info-type>.tabs { - display: flex; - position: relative; - background-color: var(--ColorThemes0); - padding: 4px; - border-radius: 6px; - width: calc(100% - 8px); - z-index: 2; -} - -#details-info-type>.tabs>input[type="radio"] { - display: none; -} - -#details-info-type>.tabs>.tab { - display: flex; - align-items: center; - justify-content: center; - height: 40px; - width: calc(100% / 3); - cursor: pointer; - padding: 0 15px; - transition: 0.15s ease-in; - color: var(--ColorThemes3); - fill: var(--ColorThemes3); - flex-direction: row; - z-index: 2; -} - -#details-info-type>.tabs>.tab>svg { - width: 20px; - height: 20px; -} - -#details-info-type>.tabs>.tab>span { - margin-left: 6px; - font-size: var(--FontSize1); - font-weight: 400; -} - -#details-info-type>.tabs>input[type="radio"]:checked+label { - color: var(--PrimaryColorText); - fill: var(--PrimaryColorText); -} - -#details-info-type>.tabs>.glider { - position: absolute; - display: flex; - height: 40px; - width: calc((100% - 8px) / 3); - background-color: var(--PrimaryColor); - z-index: 1; - border-radius: 4px; - transition: 0.25s ease-out; -} - -@media (min-width: 601px) { - #details-info-type>.tabs>input[id="info-type-house"]:checked~.glider { - transform: translateX(0); - } - - #details-info-type>.tabs>input[id="info-type-homestead"]:checked~.glider { - transform: translateX(100%); - } - - #details-info-type>.tabs>input[id="info-type-points"]:checked~.glider { - transform: translateX(200%); - } -} - -@media (max-width: 600px) { - #details-info-type>.tabs { - flex-direction: column; - } - - #details-info-type>.tabs>.tab { - width: calc(100% - 8px); - padding: 0 4px; - } - - #details-info-type>.tabs>.glider { - width: calc(100% - 8px); - } - - #details-info-type>.tabs>input[id="info-type-house"]:checked~.glider { - transform: translateY(0); - } - - #details-info-type>.tabs>input[id="info-type-homestead"]:checked~.glider { - transform: translateY(100%); - } - - #details-info-type>.tabs>input[id="info-type-points"]:checked~.glider { - transform: translateY(200%); - } -} - -.page-constructor .details-info-input { - width: 100%; - display: flex; - margin: 20px 0; - align-items: flex-start; - flex-direction: column; -} - -.page-constructor .details-info-input label { - display: flex; - justify-content: center; - flex-direction: column; - font-size: var(--FontSize1); - font-weight: 500; - margin-bottom: 5px; - -} - -.page-constructor .details-info-input input { - width: calc(100% - 10px); - min-width: 140px; - padding: 0 5px; - border-radius: 6px; - height: 30px; - background: var(--ColorThemes0); - color: var(--ColorThemes3); - font-size: var(--FontSize2); -} - -.page-constructor #details-info-osm div { - display: flex; - align-items: center; - width: 100%; -} - -.page-constructor #details-info-osm input { - width: calc(100% - 10px); - min-width: 140px; - padding: 0 5px; - border-radius: 6px; - height: 30px; -} - -.page-constructor .details-info-input a { - height: 26px; - width: 26px; - margin-left: 10px; -} - -.page-constructor .details-info-input a>svg { - height: 26px; - width: 26px; - fill: var(--ColorThemes3) -} - -.page-constructor #list-area { - margin-top: 15px; - display: flex; - overflow: auto; -} - -.page-constructor #list-area h3 { - text-align: center; - font-size: var(--FontSize5); - font-weight: 400; - margin: 10px; - padding: 7px; - color: var(--ColorThemes0); - background: var(--ColorThemes3); - border-radius: 4px; -} - -.block-area { - min-height: 200px; - border: 1px solid var(--ColorThemes3); - border-style: dashed; - border-radius: 6px; - margin: 0 10px 10px 0; -} - -.addFloors, -.addApartment { - display: flex; - position: relative; - width: 34px; - height: 34px; - background: var(--PrimaryColor); - color: var(--PrimaryColorText); - margin: 25px; - border-radius: 50%; - align-items: center; - justify-content: center; - font-size: 30px; - cursor: pointer; -} - -.addFloors>svg, -.addApartment>svg { - width: 20px; - height: 20px; - fill: var(--PrimaryColorText); - transform: rotate(45deg); -} - -.block-apartments-floors { - position: relative; - display: flex; - width: calc(100% - 22px); - border: 1px solid var(--ColorThemes3); - margin: 10px; - border-radius: 4px; -} - -.block-apartments-floors h2 { - position: absolute; - width: 65px; - right: -1px; - top: -1px; - margin: 0; - background: var(--ColorThemes3); - color: var(--ColorThemes2); - border-radius: 0 4px 0 4px; - font-size: var(--FontSize1); - padding: 2px 4px; - text-align: center; -} - -.block-apartments-number { - display: flex; - position: relative; - width: 60px; - height: 60px; - background: var(--ColorThemes1); - border: 2px solid var(--PrimaryColor); - margin: 10px; - border-radius: 4px; - align-items: center; - justify-content: center; -} - -.block-apartments-number input { - width: 50px; - height: 50px; - font-size: var(--FontSize5); - font-weight: 400; - text-align: center; - color: var(--ColorThemes3); - background: 0; -} - -.block-apartments-number button { - position: absolute; - top: 0; - right: 0; - width: 20px; - height: 20px; - border-radius: 0 4px 0 4px; - background: var(--PrimaryColor); - font-size: var(--FontSize5); - margin: -2px; - border: 0; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; -} - -.block-apartments-number button>svg { - width: 16px; - height: 16px; - fill: var(--PrimaryColorText); -} - - -.block-map { - width: 100%; - height: 500px; - border-radius: 6px; - overflow: hidden; -} - -#map { - width: 100%; - height: 100%; -} - -.entranse_number { - left: -10px !important; - top: -10px !important; -} - -.markerEntranse { - background: hsl(0deg 0% 52.12% / 90%); - font-size: 24px; - border: 2px solid #676767; - border-radius: 2px; - display: flex !important; - justify-content: center; - align-items: center; - color: #fff; - min-width: 30px; - min-height: 30px; - height: 30px; - width: 30px; -} - -.editor-buttons { - margin-top: 15px; -} - -.editor-buttons>button { - width: 100%; - min-height: 30px; - margin: 5px 0; - border: 0; - color: var(--PrimaryColorText); - display: flex; - align-items: center; - justify-content: center; - background: var(--PrimaryColor); - text-transform: uppercase; - cursor: pointer; - border-radius: 6px; - font-weight: 400; - font-size: var(--FontSize1); -} - -.editor-buttons>div { - display: flex; - width: 100%; - margin: 10px 0; - height: 30px; - justify-content: space-between; -} - -.editor-buttons>div>button { - background: var(--ColorThemes3); - color: var(--ColorThemes1); - width: 100%; - margin: 0; - border-radius: 6px; - text-transform: uppercase; - font-weight: 400; - font-size: var(--FontSize1); -} - -.page-constructor #list-entranse, -.page-constructor #list-homestead { - width: 100%; - min-height: 86px; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - overflow-x: initial; - overflow-y: auto; - border: 1px solid var(--ColorThemes3); - background: 0; - border-radius: 6px; - border-style: dashed; - margin: 10px 0; -} - -.page-constructor #list-entranse>.house, -.page-constructor #list-homestead>.house { - display: flex; - position: relative; - width: 60px; - height: 60px; - background: var(--ColorThemes1); - border: 2px solid var(--PrimaryColor); - margin: 10px; - border-radius: 4px; - align-items: center; - justify-content: center; -} - - -.page-constructor #list-entranse>.house>input, -.page-constructor #list-homestead>.house>input { - width: 50px; - height: 50px; - font-size: var(--FontSize5); - font-weight: 400; - text-align: center; - color: var(--ColorThemes3); - background: 0; -} - -.page-constructor #list-entranse>.house>button, -.page-constructor #list-homestead>.house>button { - position: absolute; - top: 0; - right: 0; - width: 20px; - height: 20px; - border-radius: 0 4px 0 4px; - background: var(--PrimaryColor); - font-size: var(--FontSize5); - margin: -2px; - border: 0; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; -} - -.page-constructor #list-entranse>.house>button>svg, -.page-constructor #list-homestead>.house>button>svg { - width: 16px; - height: 16px; - fill: var(--PrimaryColorText); -} \ No newline at end of file diff --git a/web/lib/pages/editor_old/index.html b/web/lib/pages/editor_old/index.html deleted file mode 100644 index 8672af1..0000000 --- a/web/lib/pages/editor_old/index.html +++ /dev/null @@ -1,89 +0,0 @@ -
-
- - Крок 1. Інформація про будинок, територію, точки на карті - -
-
- - -
- -
- - -
- -
- - -
-
-
- -
- Крок 2. Перегляд -
- - -
- -
-
- -
-
- - -
diff --git a/web/lib/pages/editor_old/script.js b/web/lib/pages/editor_old/script.js deleted file mode 100644 index b6d627a..0000000 --- a/web/lib/pages/editor_old/script.js +++ /dev/null @@ -1,636 +0,0 @@ -const Editor = { - async init(type, id) { - let html = await fetch('/lib/pages/editor/index.html').then((response) => response.text()); - app.innerHTML = html; - - map = ""; - houseGroup = ""; - entransePolygonsGroup = ""; - entranseNumPolygonsGroup = ""; - splitPolygonsGroup = ""; - RectangleGroup = ""; - numApartments = 1; - - Editor.info.setHTML(type, id); - }, - - async loadAPI(URL) { - let uuid = localStorage.getItem("uuid"); - return await fetch(URL, { - method: 'GET', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - } - }).then((response) => response.json()); - }, - - info: { - list: { - type: null, title: null, number: null, - points: [], points_number: [], point_icons: [], - geo: [], osm_id: [], settlement: [], description: null, - entrance: [], apartments: {} - }, - - async setHTML(type, id) { - const els = { - title: document.getElementById('info-address-title'), - number: document.getElementById('info-number-title'), - settlement: document.getElementById('info-settlement-title') - }; - - this.list = await Editor.loadAPI(`${CONFIG.api}${type}/${id}`); - Editor.info.list.type = type; - Editor.info.list.entrance = []; - Editor.info.list.apartments = {}; - - console.log(Editor.info.list); - - els.title.value = this.list.title; - els.number.value = this.list.number; - els.settlement.value = this.list.settlement; - - Editor.osm.init(); - this.setMap(); - - if (type == "house") { - Editor.entrances.setHTML(id); - document.getElementById('details-area').style.display = ""; - } else if (type == "homestead") { - Editor.homestead.init(id); - } - }, - - setMap() { - houseGroup.clearLayers(); - - for (let i = 0; i < Editor.info.list.points.length; i++) { - const element = Editor.info.list.points[i]; - - if (Editor.info.list.type == "homestead") { - map.setView([this.list.geo.lat, this.list.geo.lng], this.list.zoom); - - L.polygon(element, { - color: "#f2bd53", - radius: 500, - fillOpacity: 0.3, - dashArray: '20,15', - dashOffset: '20', - }).addTo(houseGroup); - } else if (Editor.info.list.type == "house") { - map.setView([this.list.geo.lat, this.list.geo.lng], this.list.zoom); - - L.polygon(element, { - color: "#585858", - fillColor: "#f2bd53", - fillOpacity: 0.8, - tm_id: `house_${i}` - }).addTo(houseGroup); - } - } - } - }, - - osm: { - init() { - const googleHybrid = L.tileLayer('http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', { - subdomains: ['mt0', 'mt1', 'mt2', 'mt3'] - }); - const osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'); - const mytile = L.tileLayer('https://sheep-service.com/map/{z}/{x}/{y}.webp', { - maxZoom: 20, minZoom: 15, tms: true - }); - - if (!map) { - houseGroup = new L.FeatureGroup(); - splitPolygonsGroup = new L.FeatureGroup(); - RectangleGroup = new L.FeatureGroup(); - entransePolygonsGroup = new L.FeatureGroup(); - entranseNumPolygonsGroup = new L.FeatureGroup(); - - map = L.map('map', { - renderer: L.canvas(), zoom: 17, - layers: [googleHybrid, osm, mytile, houseGroup, - entransePolygonsGroup, entranseNumPolygonsGroup, - splitPolygonsGroup, RectangleGroup], - zoomControl: false - }); - - L.control.layers( - { "Google Hybrid": googleHybrid, "OpenStreetMap": osm, "Territory Map": mytile }, - { - "Будинки": houseGroup, - "Під'їзди": entransePolygonsGroup, - "Номера під'їздів": entranseNumPolygonsGroup, - "Слой редагування": splitPolygonsGroup, - "Слой лінійки": RectangleGroup - }, - { position: 'bottomright' } - ).addTo(map); - - map.pm.setLang("ua"); - map.pm.addControls({ - position: 'bottomright', - drawCircleMarker: false, - drawPolyline: false, - drawPolygon: false, - drawRectangle: false, - drawCircle: false, - drawText: false, - drawMarker: false, - cutPolygon: false, - tooltips: false - }); - map.pm.toggleControls(); - map.pm.setGlobalOptions({ layerGroup: splitPolygonsGroup }); - } - } - }, - - entrances: { - list: [], - - async setHTML(id) { - this.list = await Editor.loadAPI(`${CONFIG.api}house/${id}/entrances`); - - entransePolygonsGroup.clearLayers(); - entranseNumPolygonsGroup.clearLayers(); - - const listArea = document.getElementById('list-area'); - if (!listArea) return; - - listArea.innerHTML = ""; - - for (const element of this.list) { - // Блок area - const divArea = document.createElement('div'); - divArea.className = "block-area"; - divArea.id = `block-area-${element.id}`; - - const h3 = document.createElement('h3'); - h3.textContent = element.title; - - const addBtn = document.createElement('button'); - addBtn.className = "addFloors"; - addBtn.type = "button"; - addBtn.title = "Додати поверх"; - addBtn.dataset.entranceId = element.id; - addBtn.innerHTML = ``; - addBtn.setAttribute(`onclick`, `Editor.apartments.addFloors("${element.id}")`); - - const innerArea = document.createElement('div'); - innerArea.id = `area-${element.id}`; - - divArea.append(h3, addBtn, innerArea); - listArea.appendChild(divArea); - - // Завантажуємо квартири для ентрансів - Editor.apartments.setHTML(element.id); - } - } - }, - - apartments: { - list: {}, - - async setHTML(id) { - this.list[id] = await Editor.loadAPI(`${CONFIG.api}apartments/${id}`); - const area = document.getElementById(`area-${id}`); - if (!area) return; - - // Унікальні поверхи - const uniqueFloors = [...new Set(this.list[id].map(a => a.floors_number))].sort((a, b) => a - b); - - // Створюємо блоки поверхів - for (const num of uniqueFloors) { - const div = document.createElement('div'); - div.className = "block-apartments-floors"; - div.id = `floors-${id}-${num}`; - - const h2 = document.createElement('h2'); - h2.textContent = `Поверх ${num}`; - div.appendChild(h2); - - const addBtn = document.createElement('button'); - addBtn.className = "addApartment"; - addBtn.id = `buttonApartment-${id}-${num}`; - addBtn.type = "button"; - addBtn.title = "Додати квартиру"; - addBtn.dataset.area = id; - addBtn.dataset.floors = num; - addBtn.innerHTML = ``; - addBtn.setAttribute(`onclick`, `Editor.apartments.addApartment("${id}", "${num}")`); - - - div.appendChild(addBtn); - area.prepend(div); - } - - // Сортуємо квартири за назвою - this.list[id].sort((a, b) => b.title - a.title); - - // Створюємо блоки квартир - for (const apartment of this.list[id]) { - const floorsBlock = document.getElementById(`floors-${id}-${apartment.floors_number}`); - if (!floorsBlock) continue; - - const div = document.createElement('div'); - div.className = "block-apartments-number"; - div.id = `block-apartments-${id}-${apartment.id}`; - - const input = document.createElement('input'); - input.type = "text"; - input.value = apartment.title; - input.id = `apartament-${id}-${apartment.id}`; - input.dataset.area = id; - input.dataset.apartment = apartment.id; - input.setAttribute(`onclick`, `Editor.apartments.editApartment("${id}", "${apartment.id}")`); - - const delBtn = document.createElement('button'); - delBtn.type = "button"; - delBtn.dataset.area = id; - delBtn.dataset.apartment = apartment.id; - delBtn.innerHTML = ``; - delBtn.setAttribute(`onclick`, `Editor.apartments.deleteApartment("${id}", "${apartment.id}")`); - - div.append(input, delBtn); - floorsBlock.prepend(div); - - numApartments++; - } - - const nextApartmentTitle = document.getElementById('next-apartment-title'); - if (nextApartmentTitle) nextApartmentTitle.value = numApartments; - }, - - async addFloors(area) { - const areaBlock = document.getElementById(`area-${area}`); - const uniqueFloors = [...new Set(this.list[area].map(obj => obj.floors_number))]; - const newFloors = uniqueFloors.length + 1; - - const uuid = localStorage.getItem('uuid'); - const URL = `${CONFIG.api}/apartments/${area}`; - - try { - const response = await fetch(URL, { - method: 'POST', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - }, - body: JSON.stringify({ - apartment_number: this.list[area].length, - title: numApartments.toString(), - floors_number: newFloors - }) - }); - const data = await response.json(); - console.log(data); - - // Створюємо блок поверху - const div = document.createElement('div'); - div.className = "block-apartments-floors"; - div.id = `floors-${area}-${newFloors}`; - - // Заголовок поверху - const h2 = document.createElement('h2'); - h2.textContent = `Поверх ${newFloors}`; - div.appendChild(h2); - - // Блок квартири - const apartmentBlock = document.createElement('div'); - apartmentBlock.className = "block-apartments-number"; - apartmentBlock.id = `block-apartments-${area}-${data.id}`; - - const input = document.createElement('input'); - input.type = "text"; - input.value = numApartments; - input.id = `apartament-${area}-${data.id}`; - input.onchange = () => Editor.apartments.editApartment(area, data.id); - - const delBtn = document.createElement('button'); - delBtn.type = "button"; - delBtn.innerHTML = ``; - delBtn.onclick = () => Editor.apartments.deleteApartment(area, data.id); - - apartmentBlock.append(input, delBtn); - div.appendChild(apartmentBlock); - - // Кнопка додати квартиру - const addBtn = document.createElement('button'); - addBtn.className = "addApartment"; - addBtn.id = `buttonApartment-${area}-${newFloors}`; - addBtn.title = "Додати квартиру"; - addBtn.type = "button"; - addBtn.onclick = () => Editor.apartments.addApartment(area, newFloors); - addBtn.innerHTML = ``; - div.appendChild(addBtn); - - areaBlock.prepend(div); - - // Оновлюємо список квартир - this.list[area].push({ - id: data.id, - entrance_id: Number(area), - apartment_number: this.list[area].length, - title: numApartments.toString(), - floors_number: newFloors - }); - - numApartments++; - const nextApartmentTitle = document.getElementById('next-apartment-title'); - if (nextApartmentTitle) nextApartmentTitle.value = numApartments; - - } catch (err) { - console.error("Помилка при додаванні поверху:", err); - } - }, - - async addApartment(area, floors) { - const uuid = localStorage.getItem('uuid'); - const URL = `${CONFIG.api}/apartments/${area}`; - - try { - const response = await fetch(URL, { - method: 'POST', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - }, - body: JSON.stringify({ - apartment_number: this.list[area].length, - title: numApartments.toString(), - floors_number: Number(floors) - }) - }); - - const data = await response.json(); - console.log(data); - - // Оновлюємо список квартир - this.list[area].push({ - id: data.id, - entrance_id: Number(area), - apartment_number: this.list[area].length, - title: numApartments.toString(), - floors_number: Number(floors) - }); - - const floorsBlock = document.getElementById(`floors-${area}-${floors}`); - - // Видаляємо стару кнопку додати квартиру - const oldButton = document.getElementById(`buttonApartment-${area}-${floors}`); - if (oldButton) oldButton.remove(); - - // Створюємо блок нової квартири - const apartmentDiv = document.createElement('div'); - apartmentDiv.className = "block-apartments-number"; - apartmentDiv.id = `block-apartments-${area}-${data.id}`; - - const input = document.createElement('input'); - input.type = "text"; - input.value = numApartments; - input.id = `apartament-${area}-${data.id}`; - input.onchange = () => Editor.apartments.editApartment(area, data.id); - - const delBtn = document.createElement('button'); - delBtn.type = "button"; - delBtn.innerHTML = ``; - delBtn.onclick = () => Editor.apartments.deleteApartment(area, data.id); - - apartmentDiv.append(input, delBtn); - floorsBlock.appendChild(apartmentDiv); - - // Додаємо кнопку "додати квартиру" знову - const addBtn = document.createElement('button'); - addBtn.className = "addApartment"; - addBtn.id = `buttonApartment-${area}-${floors}`; - addBtn.title = "Додати квартиру"; - addBtn.type = "button"; - addBtn.onclick = () => Editor.apartments.addApartment(area, floors); - addBtn.innerHTML = ``; - floorsBlock.appendChild(addBtn); - - numApartments++; - const nextApartmentTitle = document.getElementById('next-apartment-title'); - if (nextApartmentTitle) nextApartmentTitle.value = numApartments; - - } catch (err) { - console.error("Помилка при додаванні квартири:", err); - } - }, - - async editApartment(area, apartment) { - const input = document.getElementById(`apartament-${area}-${apartment}`); - if (!input) return; - - const newTitle = input.value; - - // Оновлюємо локальний список квартир - const pos = this.list[area].findIndex(e => e.id === Number(apartment)); - if (pos === -1) return; - - this.list[area][pos].title = newTitle; - - const uuid = localStorage.getItem('uuid'); - const URL = `${CONFIG.api}/apartments/${area}`; - - try { - const response = await fetch(URL, { - method: 'PUT', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - }, - body: JSON.stringify({ - id: this.list[area][pos].id, - title: this.list[area][pos].title, - status: this.list[area][pos].status, - description: this.list[area][pos].description - }) - }); - - const data = await response.json(); - console.log(data); - - } catch (err) { - console.error("Помилка при редагуванні квартири:", err); - } - }, - - async deleteApartment(area, apartment) { - const pos = this.list[area].findIndex(e => e.id === Number(apartment)); - if (pos === -1) return; - - const uuid = localStorage.getItem('uuid'); - const URL = `${CONFIG.api}/apartments/${area}`; - - try { - const response = await fetch(URL, { - method: 'DELETE', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - }, - body: JSON.stringify({ id: this.list[area][pos].id }) - }); - - const data = await response.json(); - console.log(data); - - // Видаляємо елемент з DOM - const apartmentBlock = document.getElementById(`block-apartments-${area}-${apartment}`); - if (apartmentBlock) apartmentBlock.remove(); - - // Оновлюємо локальний список - this.list[area].splice(pos, 1); - - // Оновлюємо номер наступної квартири - numApartments = Math.max(0, numApartments - 1); - const nextApartmentTitle = document.getElementById('next-apartment-title'); - if (nextApartmentTitle) nextApartmentTitle.value = numApartments; - - } catch (err) { - console.error("Помилка при видаленні квартири:", err); - } - }, - - editNum(el) { numApartments = Number(el.value) }, - }, - - homestead: { - id: null, list: [], editing: false, - - async init(id) { - this.editing = false; - this.id = id; - setLeafletCursor('pointer'); - - document.getElementById('homestead-editing').style.display = ""; - - // Завантаження даних будівлі - this.list = await Editor.loadAPI(`${CONFIG.api}building/${id}`); - - // Обробник кліку на карту - houseGroup.on('click', e => { - if (e.layer instanceof L.Marker || !this.editing) return; - - const { lat, lng } = e.latlng; - console.log(`Координати: ${lat.toFixed(5)}, ${lng.toFixed(5)}`); - - setLeafletCursor('progress'); - this.editing.editing = false; - - this.addBuilding({ geo: e.latlng, title: this.list.length + 1 }); - this.list.push({}); - }); - - // Встановлюємо вид карти - const viewLatLng = Editor.info.list.geo?.lat - ? [Editor.info.list.geo.lat, Editor.info.list.geo.lng] - : [Editor.info.list.points[0][0][0].lat, Editor.info.list.points[0][0][0].lng]; - map.setView(viewLatLng, Editor.info.list.zoom); - - - for (const element of this.list) { - // Додаємо маркер на карту - const redDot = L.divIcon({ - className: "leaflet_drop", - html: `
`, - iconSize: [16, 16], - iconAnchor: [8, 8] - }); - - L.marker(element.geo, { icon: redDot }) - .addTo(houseGroup) - .bindPopup(` - Точка: ${element.id}
- Координати: ${element.geo.lat.toFixed(5)}, ${element.geo.lng.toFixed(5)}
- - `); - } - }, - - async addBuilding({ geo, title }) { - const uuid = localStorage.getItem('uuid'); - const URL = `${CONFIG.api}building/${this.id}`; - - try { - const response = await fetch(URL, { - method: 'POST', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - }, - body: JSON.stringify({ title, geo }) - }); - const data = await response.json(); - console.log(data); - - // Додаємо маркер на карту - const redDot = L.divIcon({ - className: "leaflet_drop", - html: `
`, - iconSize: [16, 16], - iconAnchor: [8, 8] - }); - - const marker = L.marker(geo, { icon: redDot }).addTo(houseGroup); - marker.bindPopup(` - Точка: ${data.id}
- Координати: ${geo.lat.toFixed(5)}, ${geo.lng.toFixed(5)}
- - `); - - setLeafletCursor('crosshair'); - this.editing = true; - - } catch (err) { - console.error("Помилка при додаванні будівлі:", err); - } - }, - - async delleteBuilding({ id }) { - const uuid = localStorage.getItem('uuid'); - const URL = `${CONFIG.api}building/${id}`; - - try { - const response = await fetch(URL, { - method: 'DELETE', - headers: { - "Content-Type": "application/json", - "Authorization": uuid - } - }); - const data = await response.json(); - - // Видаляємо елемент списку та маркер - const el = document.getElementById(`redDot_${id}`); - if (el) el.remove(); - - const block = document.getElementById(`Building_${id}`); - if (block) block.remove(); - - houseGroup.eachLayer(layer => { - if (layer instanceof L.Marker && layer.getPopup()?.getContent().includes(`Точка: ${id}`)) { - houseGroup.removeLayer(layer); - } - }); - - } catch (err) { - console.error("Помилка при видаленні будівлі:", err); - } - }, - - editing_mode() { - const btn = document.getElementById('homestead-editing'); - this.editing = !this.editing; - setLeafletCursor(this.editing ? 'crosshair' : 'pointer'); - btn.innerHTML = this.editing - ? ` ` - : ` `; - if (this.editing) alert("Натискаючи на карту будуть створюватись нові точки (будинки)"); - }, - } -} \ No newline at end of file diff --git a/web/lib/pages/editor_old/style.css b/web/lib/pages/editor_old/style.css deleted file mode 100644 index 14f2393..0000000 --- a/web/lib/pages/editor_old/style.css +++ /dev/null @@ -1,536 +0,0 @@ -.page-editor { - width: calc(100% - 40px); - display: flex; - flex-direction: column; - align-items: center; - margin: 20px 20px 0 20px; -} - -.page-editor form>button { - border-radius: 6px; - background: var(--PrimaryColor); - color: var(--PrimaryColorText); - width: 100%; - height: 40px; - font-size: var(--FontSize3); - font-weight: 400; - margin: 20px 0; - text-transform: uppercase; -} - -.page-editor details { - border-radius: 10px; - width: 100%; - display: flex; - flex-direction: column; - align-items: stretch; - margin-bottom: 20px; - background: var(--ColorThemes1); - color: var(--ColorThemes3); - border: 1px solid var(--ColorThemes2); - box-shadow: var(--shadow-l1); -} - -.page-editor>details[disabled] summary, -.page-editor>details.disabled summary { - pointer-events: none; - user-select: none; -} - -.page-editor>details summary::-webkit-details-marker, -.page-editor>details summary::marker { - display: none; - content: ""; -} - -.page-editor summary { - width: calc(100% - 40px); - cursor: pointer; - color: var(--ColorThemes3); - border-radius: var(--border-radius); - font-size: var(--FontSize5); - font-weight: 300; - padding: 20px; - position: relative; -} - -.page-editor summary span { - font-weight: 500; -} - -.page-editor summary input { - font-weight: 500; - position: absolute; - right: 0; - top: 0; - padding: 10px; - margin: 13px; - font-size: var(--FontSize1); - background: var(--ColorThemes3); - color: var(--ColorThemes0); - border-radius: 6px; - width: 40px; -} - -.page-editor #info-form, -.page-editor #map-form, -.page-editor #area-form { - padding: 0 20px; -} - -#details-info-type { - display: flex; - align-items: center; - justify-content: center; -} - -#details-info-type>.tabs { - display: flex; - position: relative; - background-color: var(--ColorThemes0); - padding: 4px; - border-radius: 6px; - width: calc(100% - 8px); - z-index: 2; -} - -#details-info-type>.tabs>input[type="radio"] { - display: none; -} - -#details-info-type>.tabs>.tab { - display: flex; - align-items: center; - justify-content: center; - height: 40px; - width: calc(100% / 3); - cursor: pointer; - padding: 0 15px; - transition: 0.15s ease-in; - color: var(--ColorThemes3); - fill: var(--ColorThemes3); - flex-direction: row; - z-index: 2; -} - -#details-info-type>.tabs>.tab>svg { - width: 20px; - height: 20px; -} - -#details-info-type>.tabs>.tab>span { - margin-left: 6px; - font-size: var(--FontSize1); - font-weight: 400; -} - -#details-info-type>.tabs>input[type="radio"]:checked+label { - color: var(--PrimaryColorText); - fill: var(--PrimaryColorText); -} - -#details-info-type>.tabs>.glider { - position: absolute; - display: flex; - height: 40px; - width: calc((100% - 8px) / 3); - background-color: var(--PrimaryColor); - z-index: 1; - border-radius: 4px; - transition: 0.25s ease-out; -} - -@media (min-width: 601px) { - #details-info-type>.tabs>input[id="info-type-house"]:checked~.glider { - transform: translateX(0); - } - - #details-info-type>.tabs>input[id="info-type-homestead"]:checked~.glider { - transform: translateX(100%); - } - - #details-info-type>.tabs>input[id="info-type-points"]:checked~.glider { - transform: translateX(200%); - } -} - -@media (max-width: 600px) { - #details-info-type>.tabs { - flex-direction: column; - } - - #details-info-type>.tabs>.tab { - width: calc(100% - 8px); - padding: 0 4px; - } - - #details-info-type>.tabs>.glider { - width: calc(100% - 8px); - } - - #details-info-type>.tabs>input[id="info-type-house"]:checked~.glider { - transform: translateY(0); - } - - #details-info-type>.tabs>input[id="info-type-homestead"]:checked~.glider { - transform: translateY(100%); - } - - #details-info-type>.tabs>input[id="info-type-points"]:checked~.glider { - transform: translateY(200%); - } -} - -.page-editor .details-info-input { - width: 100%; - display: flex; - margin: 20px 0; - align-items: flex-start; - flex-direction: column; -} - -.page-editor .details-info-input label { - display: flex; - justify-content: center; - flex-direction: column; - font-size: var(--FontSize1); - font-weight: 500; - margin-bottom: 5px; - -} - -.page-editor .details-info-input input { - width: calc(100% - 10px); - min-width: 140px; - padding: 0 5px; - border-radius: 6px; - height: 30px; - background: var(--ColorThemes0); - color: var(--ColorThemes3); - font-size: var(--FontSize2); -} - -.page-editor #details-info-osm div { - display: flex; - align-items: center; - width: 100%; -} - -.page-editor #details-info-osm input { - width: calc(100% - 10px); - min-width: 140px; - padding: 0 5px; - border-radius: 6px; - height: 30px; -} - -.page-editor .details-info-input a { - height: 26px; - width: 26px; - margin-left: 10px; -} - -.page-editor .details-info-input a>svg { - height: 26px; - width: 26px; - fill: var(--ColorThemes3) -} - -.page-editor #list-area { - display: flex; - overflow: auto; - margin: 15px 0 20px 0; -} - -.page-editor #list-area h3 { - text-align: center; - font-size: var(--FontSize5); - font-weight: 400; - margin: 10px; - padding: 7px; - color: var(--ColorThemes0); - background: var(--ColorThemes3); - border-radius: 4px; -} - -.block-area { - min-height: 200px; - border: 1px solid var(--ColorThemes3); - border-style: dashed; - border-radius: 6px; - margin: 0 10px 10px 0; -} - -.addFloors, -.addApartment { - display: flex; - position: relative; - width: 34px; - height: 34px; - background: var(--PrimaryColor); - color: var(--PrimaryColorText); - margin: 25px; - border-radius: 50%; - align-items: center; - justify-content: center; - font-size: 30px; - cursor: pointer; -} - -.addFloors>svg, -.addApartment>svg { - width: 20px; - height: 20px; - fill: var(--PrimaryColorText); - transform: rotate(45deg); -} - -.block-apartments-floors { - position: relative; - display: flex; - width: calc(100% - 22px); - border: 1px solid var(--ColorThemes3); - margin: 10px; - border-radius: 4px; -} - -.block-apartments-floors h2 { - position: absolute; - width: 65px; - right: -1px; - top: -1px; - margin: 0; - background: var(--ColorThemes3); - color: var(--ColorThemes2); - border-radius: 0 4px 0 4px; - font-size: var(--FontSize1); - padding: 2px 4px; - text-align: center; -} - -.block-apartments-number { - display: flex; - position: relative; - width: 60px; - height: 60px; - background: var(--ColorThemes1); - border: 2px solid var(--PrimaryColor); - margin: 10px; - border-radius: 4px; - align-items: center; - justify-content: center; -} - -.block-apartments-number input { - width: 50px; - height: 50px; - font-size: var(--FontSize5); - font-weight: 400; - text-align: center; - color: var(--ColorThemes3); - background: 0; -} - -.block-apartments-number button { - position: absolute; - top: 0; - right: 0; - width: 20px; - height: 20px; - border-radius: 0 4px 0 4px; - background: var(--PrimaryColor); - font-size: var(--FontSize5); - margin: -2px; - border: 0; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; -} - -.block-apartments-number button>svg { - width: 16px; - height: 16px; - fill: var(--PrimaryColorText); -} - - -.block-map { - width: 100%; - height: 500px; - border-radius: 6px; - overflow: hidden; - margin-bottom: 20px; - position: relative; - - background: var(--ColorThemes1); - color: var(--ColorThemes3); - border: 1px solid var(--ColorThemes2); - box-shadow: var(--shadow-l1); -} - -#homestead-editing { - position: absolute; - height: 40px; - background: var(--PrimaryColor); - font-size: var(--FontSize5); - margin: 10px; - border: 0; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-weight: 500; - padding: 10px; - border-radius: 6px; - width: 40px; - z-index: 999; - right: 0; - box-shadow: var(--shadow-l1); -} - -#homestead-editing>svg { - width: 20px; - height: 20px; - fill: var(--PrimaryColorText); -} - -#map { - width: 100%; - height: 100%; -} - -.entranse_number { - left: -10px !important; - top: -10px !important; -} - -.markerEntranse { - background: hsl(0deg 0% 52.12% / 90%); - font-size: 24px; - border: 2px solid #676767; - border-radius: 2px; - display: flex !important; - justify-content: center; - align-items: center; - color: #fff; - min-width: 30px; - min-height: 30px; - height: 30px; - width: 30px; -} - -.editor-buttons { - margin-top: 15px; -} - -.editor-buttons>button { - width: 100%; - min-height: 30px; - margin: 5px 0; - border: 0; - color: var(--PrimaryColorText); - display: flex; - align-items: center; - justify-content: center; - background: var(--PrimaryColor); - text-transform: uppercase; - cursor: pointer; - border-radius: 6px; - font-weight: 400; - font-size: var(--FontSize1); -} - -.editor-buttons>div { - display: flex; - width: 100%; - margin: 10px 0; - height: 30px; - justify-content: space-between; -} - -.editor-buttons>div>button { - background: var(--ColorThemes3); - color: var(--ColorThemes1); - width: 100%; - margin: 0; - border-radius: 6px; - text-transform: uppercase; - font-weight: 400; - font-size: var(--FontSize1); -} - -.page-editor #list-entranse, -.page-editor #list-homestead { - width: 100%; - min-height: 86px; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - overflow-x: initial; - overflow-y: auto; - border: 1px solid var(--ColorThemes3); - background: 0; - border-radius: 6px; - border-style: dashed; - margin: 10px 0; - position: relative; -} - -.page-editor #list-entranse>.house, -.page-editor #list-homestead>.house { - display: flex; - position: relative; - width: 60px; - height: 60px; - min-width: 60px; - min-height: 60px; - background: var(--ColorThemes1); - border: 2px solid var(--PrimaryColor); - margin: 10px; - border-radius: 4px; - align-items: center; - justify-content: center; -} - - -.page-editor #list-entranse>.house>input, -.page-editor #list-homestead>.house>input, -.page-editor #list-entranse>.house>p, -.page-editor #list-homestead>.house>p { - width: 50px; - height: 50px; - font-size: var(--FontSize5); - font-weight: 400; - text-align: center; - color: var(--ColorThemes3); - background: 0; - display: flex; - align-items: center; - justify-content: center; -} - -.page-editor #list-entranse>.house>button, -.page-editor #list-homestead>.house>button { - position: absolute; - top: 0; - right: 0; - width: 20px; - height: 20px; - border-radius: 0 4px 0 4px; - background: var(--PrimaryColor); - font-size: var(--FontSize5); - margin: -2px; - border: 0; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; -} - -.page-editor #list-entranse>.house>button>svg, -.page-editor #list-homestead>.house>button>svg { - width: 16px; - height: 16px; - fill: var(--PrimaryColorText); -} \ No newline at end of file diff --git a/web/lib/pages/stand/index.html b/web/lib/pages/stand/card/index.html similarity index 99% rename from web/lib/pages/stand/index.html rename to web/lib/pages/stand/card/index.html index 9e98ac2..5a51515 100644 --- a/web/lib/pages/stand/index.html +++ b/web/lib/pages/stand/card/index.html @@ -1,4 +1,4 @@ -
+
diff --git a/web/lib/pages/stand/script.js b/web/lib/pages/stand/card/script.js similarity index 91% rename from web/lib/pages/stand/script.js rename to web/lib/pages/stand/card/script.js index f34041d..553c652 100644 --- a/web/lib/pages/stand/script.js +++ b/web/lib/pages/stand/card/script.js @@ -1,7 +1,7 @@ -const Stand = { +const Stand_card = { schedule: [], - init: async () => { - let html = await fetch('/lib/pages/stand/index.html').then((response) => response.text()); + init: async (id) => { + let html = await fetch('/lib/pages/stand/card/index.html').then((response) => response.text()); app.innerHTML = html; let listDate = [1, 4]; @@ -35,7 +35,7 @@ const Stand = { } // generateAvailableDates(); - Stand.generator(); + Stand_card.generator(); }, generator: () => { let block_schedule = document.getElementById('stand-schedule'); @@ -48,19 +48,19 @@ const Stand = { geo: { lat: 0, lng: 0 }, hour_start: 9, hour_end: 14, - quantity_sheep: 4, + quantity_sheep: 2, week_days: [0, 2, 4, 6], processing_time: 0.5, updated_at: null } - Stand.schedule = []; + Stand_card.schedule = []; // Кількість годин служіння let stand_length = (stand.hour_end - stand.hour_start) / stand.processing_time; for (let z = 0; z < stand.week_days.length; z++) { - Stand.schedule.push([]); + Stand_card.schedule.push([]); let date = new Date(); date.setDate(date.getDate() + stand.week_days[z]); @@ -97,7 +97,7 @@ const Stand = { `; - Stand.schedule[z].push({ + Stand_card.schedule[z].push({ id: (i + z) * stand.quantity_sheep + q, hour: stand.hour_start + (stand.processing_time * i), number_sheep: q, @@ -114,7 +114,7 @@ const Stand = { html += `
`; // закриваємо day } - document.getElementById('stand-info-title').innerText = stand.title; + document.getElementById('stand-info-title').innerText = Stand_card.title; document.getElementById('stand-info-geo').innerHTML = ''; document.getElementById('stand-info-image').setAttribute('src', ''); diff --git a/web/lib/pages/stand/style.css b/web/lib/pages/stand/card/style.css similarity index 97% rename from web/lib/pages/stand/style.css rename to web/lib/pages/stand/card/style.css index 96ac335..328dc3f 100644 --- a/web/lib/pages/stand/style.css +++ b/web/lib/pages/stand/card/style.css @@ -1,4 +1,4 @@ -.page-stand { +.page-stand-card { width: calc(100% - 40px); display: flex; flex-direction: column; @@ -6,7 +6,7 @@ margin: 20px 20px 0 20px; } -.page-stand details { +.page-stand-card details { border-radius: var(--border-radius); width: 100%; display: flex; @@ -19,7 +19,7 @@ box-shadow: var(--shadow-l1); } -.page-stand summary { +.page-stand-card summary { width: calc(100% - 40px); cursor: pointer; color: var(--ColorThemes3); diff --git a/web/lib/pages/stand/list/index.html b/web/lib/pages/stand/list/index.html new file mode 100644 index 0000000..a08dcc7 --- /dev/null +++ b/web/lib/pages/stand/list/index.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/web/lib/pages/stand/list/script.js b/web/lib/pages/stand/list/script.js new file mode 100644 index 0000000..658b765 --- /dev/null +++ b/web/lib/pages/stand/list/script.js @@ -0,0 +1,6 @@ +const Stand_list = { + init: async () => { + let html = await fetch('/lib/pages/stand/list/index.html').then((response) => response.text()); + app.innerHTML = html; + }, +} \ No newline at end of file diff --git a/web/lib/pages/stand/list/style.css b/web/lib/pages/stand/list/style.css new file mode 100644 index 0000000..483364a --- /dev/null +++ b/web/lib/pages/stand/list/style.css @@ -0,0 +1,7 @@ +.page-stand-list { + width: calc(100% - 40px); + display: flex; + flex-direction: column; + align-items: center; + margin: 20px 20px 0 20px; +} \ No newline at end of file diff --git a/web/lib/pages/card/index.html b/web/lib/pages/territory/card/index.html similarity index 98% rename from web/lib/pages/card/index.html rename to web/lib/pages/territory/card/index.html index 4d8aaed..d8d40ca 100644 --- a/web/lib/pages/card/index.html +++ b/web/lib/pages/territory/card/index.html @@ -200,21 +200,21 @@
+
\ No newline at end of file diff --git a/web/lib/pages/card/script.js b/web/lib/pages/territory/card/script.js similarity index 79% rename from web/lib/pages/card/script.js rename to web/lib/pages/territory/card/script.js index 219df4e..43456f8 100644 --- a/web/lib/pages/card/script.js +++ b/web/lib/pages/territory/card/script.js @@ -1,12 +1,11 @@ let map_card; -const Card = { +const Territory_card = { // Глобальні змінні стану id: null, socket: null, reconnectTimeout: null, reconnectAttempts: 0, - username: null, listEntrances: [], listApartment: [], listBuilding: [], @@ -25,13 +24,14 @@ const Card = { // Ініціалізація сторінки async init(type, Id) { // Завантажуємо HTML - const html = await fetch('/lib/pages/card/index.html').then(r => r.text()); + const html = await fetch('/lib/pages/territory/card/index.html').then(r => r.text()); app.innerHTML = html; - Card.id = Id; + Territory_card.id = Id; // Закриваємо старий WebSocket if (this.socket) this.socket.close(1000, "Перезапуск з'єднання"); - this.cloud.start(makeid(6)); + // this.cloud.start(makeid(6)); + this.cloud.start() // Якщо це сторінка будинку, отримуємо під’їзди та стартуємо WebSocket if (type === "house") { @@ -59,60 +59,60 @@ const Card = { // Робота з WebSocket cloud: { - start(name) { - if (!name) return; - Card.username = name; - + start() { const uuid = localStorage.getItem("uuid"); - const ws = new WebSocket(`${CONFIG.wss}?uuid=${uuid}`); - Card.socket = ws; + const ws = new WebSocket(CONFIG.wss, uuid); + Territory_card.socket = ws; ws.onopen = () => { console.log("[WebSocket] З'єднання встановлено"); - Card.cloud.setStatus('ok'); + Territory_card.cloud.setStatus('ok'); ws.send(JSON.stringify({ event: 'connection', id: getTimeInSeconds(), date: getTimeInSeconds(), uuid, - username: name, + user: { + name: USER.name, + id: USER.id + }, data: {} })); - Card.reconnectAttempts = 0; - clearTimeout(Card.reconnectTimeout); + Territory_card.reconnectAttempts = 0; + clearTimeout(Territory_card.reconnectTimeout); }; ws.onmessage = (e) => { const data = JSON.parse(e.data); - if (data.event === 'connection' && data.username !== Card.username) { - console.log(`Новий користувач: ${data.username}`); + if (data.event === 'connection' && data.user.id !== USER.id) { + console.log(`Новий користувач: ${data.user}`); } if (data.event === 'message') { - Card.cloud.update(data); + Territory_card.cloud.update(data); } }; ws.onclose = () => { console.warn("[WebSocket] З'єднання розірвано"); - Card.cloud.setStatus('err'); - Card.reconnectAttempts++; - if (Card.reconnectAttempts <= 5) { - Card.reconnectTimeout = setTimeout(() => { - Card.getEntrances({ update: true }); - Card.cloud.start(Card.username); + Territory_card.cloud.setStatus('err'); + Territory_card.reconnectAttempts++; + if (Territory_card.reconnectAttempts <= 5) { + Territory_card.reconnectTimeout = setTimeout(() => { + Territory_card.getEntrances({ update: true }); + Territory_card.cloud.start(); }, 1000); } else { if (confirm("З'єднання розірвано! Перепідключитись?")) { - Card.reconnectAttempts = 0; - Card.getEntrances({ update: true }); - Card.cloud.start(Card.username); + Territory_card.reconnectAttempts = 0; + Territory_card.getEntrances({ update: true }); + Territory_card.cloud.start(); } } }; ws.onerror = (err) => { console.error("[WebSocket] Помилка", err); - Card.cloud.setStatus('err'); + Territory_card.cloud.setStatus('err'); }; }, @@ -127,7 +127,7 @@ const Card = { update(msg) { if (msg.type !== "apartment" && msg.type !== "building") return; - const [bg, color] = Card.color_status[msg.data.status]; + const [bg, color] = Territory_card.color_status[msg.data.status]; const id = msg.data.id; const el = document.getElementById(`status_${id}`); @@ -135,7 +135,7 @@ const Card = { if (msg.type === "building") { redDot.style = `background:${bg};border:2px solid ${color}`; - const apt = Card.listBuilding.find(e => e.id === id); + const apt = Territory_card.listBuilding.find(e => e.id === id); if (!apt) return; @@ -164,7 +164,7 @@ const Card = { }, messApartment({ number, id, update, time }) { - const apt = Card.listApartment[number]?.find(e => e.id === id); + const apt = Territory_card.listApartment[number]?.find(e => e.id === id); if (!apt) return; const statusEl = document.getElementById(`status_${id}`); @@ -184,14 +184,17 @@ const Card = { apt.description = descEl.value; apt.updated_at = date(); - const [bg, color] = Card.color_status[apt.status]; + const [bg, color] = Territory_card.color_status[apt.status]; statusEl.style = `background:${bg};color:${color};border:1px solid ${color}`; const message = { event: 'message', id: getTimeInSeconds(), date: getTimeInSeconds(), - username: Card.username, + user: { + name: USER.name, + id: USER.id + }, type: "apartment", data: { ...apt, @@ -199,18 +202,18 @@ const Card = { } }; - if (Card.socket?.readyState === WebSocket.OPEN) { - Card.socket.send(JSON.stringify(message)); + if (Territory_card.socket?.readyState === WebSocket.OPEN) { + Territory_card.socket.send(JSON.stringify(message)); } else { if (confirm("З'єднання розірвано! Перепідключитись?")) { - Card.getEntrances({ update: true }); - Card.cloud.start(Card.username); + Territory_card.getEntrances({ update: true }); + Territory_card.cloud.start(); } } }, messBuildings({ id, update, time }) { - const apt = Card.listBuilding.find(e => e.id === id); + const apt = Territory_card.listBuilding.find(e => e.id === id); if (!apt) return; const statusEl = document.getElementById(`status_${id}`); @@ -232,7 +235,7 @@ const Card = { apt.description = descEl.value; apt.updated_at = date(); - const [bg, color] = Card.color_status[apt.status]; + const [bg, color] = Territory_card.color_status[apt.status]; statusEl.style = `background:${bg};color:${color};border:1px solid ${color}`; @@ -240,7 +243,10 @@ const Card = { event: 'message', id: getTimeInSeconds(), date: getTimeInSeconds(), - username: Card.username, + user: { + name: USER.name, + id: USER.id + }, type: "building", data: { ...apt, @@ -248,19 +254,19 @@ const Card = { } }; - if (Card.socket?.readyState === WebSocket.OPEN) { - Card.socket.send(JSON.stringify(message)); + if (Territory_card.socket?.readyState === WebSocket.OPEN) { + Territory_card.socket.send(JSON.stringify(message)); } else { if (confirm("З'єднання розірвано! Перепідключитись?")) { - Card.getEntrances({ update: true }); - Card.cloud.start(Card.username); + Territory_card.getEntrances({ update: true }); + Territory_card.cloud.start(); } } } }, // Отримання під’їздів - async getEntrances({ house_id = Card.id, update = false }) { + async getEntrances({ house_id = Territory_card.id, update = false }) { const uuid = localStorage.getItem("uuid"); const res = await fetch(`${CONFIG.api}/house/${house_id}/entrances`, { headers: { @@ -351,7 +357,7 @@ const Card = { statusEl.value = apt.status; statusEl.style = style; } - if (dateEl) dateEl.setAttribute('onclick', `Card.dateEditor.open({id: ${apt.id}, number: ${number}, updated_at: ${apt.updated_at}})`); + if (dateEl) dateEl.setAttribute('onclick', `Territory_card.dateEditor.open({id: ${apt.id}, number: ${number}, updated_at: ${apt.updated_at}})`); if (dateTextEl) dateTextEl.innerText = formattedDateTime(apt.updated_at); if (descEl) descEl.innerText = apt.description ?? ""; } @@ -383,15 +389,15 @@ const Card = { div.innerHTML = `
кв.${apt.title} - ${statusOptions(apt.status)} -
- + `; fragment.appendChild(div); @@ -418,7 +424,7 @@ const Card = { return await response.json(); }, - async map({ homestead_id = Card.id }) { + async map({ homestead_id = Territory_card.id }) { let data = await this.loadAPI({ url: `${CONFIG.api}homestead/${homestead_id}` }); console.log(data); @@ -428,9 +434,9 @@ const Card = { let lng = data.geo?.lng ?? data.points?.[0]?.[0]?.[0]?.lng ?? 25.6145625; let zoom = 15; - if (map_card && map_card.remove) { - map_card.stopLocate(); - map_card.remove(); + if (map_card && map_Territory_card.remove) { + map_Territory_card.stopLocate(); + map_Territory_card.remove(); } const mapElement = document.getElementById('map_card'); @@ -465,13 +471,13 @@ const Card = { }); // слежение в реальном времени - map_card.locate({ setView: false, watch: true, enableHighAccuracy: true }); - map_card.on('locationfound', (e) => { - if (!map_card._userMarker) { - map_card._userMarker = L.marker(e.latlng).addTo(map_card) + map_Territory_card.locate({ setView: false, watch: true, enableHighAccuracy: true }); + map_Territory_card.on('locationfound', (e) => { + if (!map_Territory_card._userMarker) { + map_Territory_card._userMarker = L.marker(e.latlng).addTo(map_card) .bindPopup("Ви тут!"); } else { - map_card._userMarker.setLatLng(e.latlng); + map_Territory_card._userMarker.setLatLng(e.latlng); } }); @@ -483,7 +489,7 @@ const Card = { let layerControl = L.control.layers(baseMaps, [], { position: 'bottomright' }).addTo(map_card); - map_card.pm.setLang("ua"); + map_Territory_card.pm.setLang("ua"); const polygonOptions = { color: "#f2bd53", @@ -495,23 +501,23 @@ const Card = { L.polygon(data.points, polygonOptions).addTo(map_card); - map_card.setZoom(data.zoom); + map_Territory_card.setZoom(data.zoom); - // map_card.getZoom() + // map_Territory_card.getZoom() // console.log(data.zoom); - Card.listBuilding = await this.loadAPI({ url: `${CONFIG.api}building/${homestead_id}` }); + Territory_card.listBuilding = await this.loadAPI({ url: `${CONFIG.api}building/${homestead_id}` }); const statusOptions = (selected) => { const labels = ["", "Відмова (Не цікавить)", "Не заходити (Груба відмова)", "Нема домофона", "Повторна відвідина", "Немає вдома", "Свідки Єгови"]; return labels.map((txt, i) => ``).join(""); }; - for (let i = 0; i < Card.listBuilding.length; i++) { - const element = Card.listBuilding[i]; - const [bg, color] = Card.color_status[element.status]; + for (let i = 0; i < Territory_card.listBuilding.length; i++) { + const element = Territory_card.listBuilding[i]; + const [bg, color] = Territory_card.color_status[element.status]; const redDot = L.divIcon({ className: "leaflet_drop", @@ -527,8 +533,8 @@ const Card = { // при открытии popup генерим div заново marker.on("popupopen", () => { - const el = Card.listBuilding.find(e => e.id === element.id); - const [bg, color] = Card.color_status[el.status]; + const el = Territory_card.listBuilding.find(e => e.id === element.id); + const [bg, color] = Territory_card.color_status[el.status]; const style = `background:${bg};color:${color};border:1px solid ${color}`; const div = document.createElement("div"); @@ -541,18 +547,18 @@ const Card = { div.innerHTML = `
- ${statusOptions(element.status)} - +
- + `; marker.setPopupContent(div); }); - Card.getHomestead.markers[element.id] = marker; // сохраним ссылку на маркер + Territory_card.getHomestead.markers[element.id] = marker; // сохраним ссылку на маркер } }, }, @@ -580,8 +586,8 @@ const Card = { input.value = date.toISOString().slice(0, 16) // Призначаємо обробники - input.setAttribute("onchange", `Card.dateEditor.edit({ id: ${id}, number: ${number} })`) - button.setAttribute("onclick", `Card.dateEditor.edit({ id: ${id}, number: ${number}, type: 'now'})`) + input.setAttribute("onchange", `Territory_card.dateEditor.edit({ id: ${id}, number: ${number} })`) + button.setAttribute("onclick", `Territory_card.dateEditor.edit({ id: ${id}, number: ${number}, type: 'now'})`) // Показуємо блок @@ -603,13 +609,13 @@ const Card = { let input = document.getElementById('card-new-date-input').value; if (type == "now") { - Card.cloud.messApartment({ number: number, id: id, update: true }); + Territory_card.cloud.messApartment({ number: number, id: id, update: true }); } else { if (input) { const ts = new Date(input).getTime(); - Card.cloud.messApartment({ number, id, update: true, time: ts }); + Territory_card.cloud.messApartment({ number, id, update: true, time: ts }); } else { - Card.cloud.messApartment({ number: number, id: id }); + Territory_card.cloud.messApartment({ number: number, id: id }); } } this.close(); diff --git a/web/lib/pages/card/style.css b/web/lib/pages/territory/card/style.css similarity index 100% rename from web/lib/pages/card/style.css rename to web/lib/pages/territory/card/style.css diff --git a/web/lib/pages/constructor/index.html b/web/lib/pages/territory/constructor/index.html similarity index 98% rename from web/lib/pages/constructor/index.html rename to web/lib/pages/territory/constructor/index.html index bd82dee..895a9dc 100644 --- a/web/lib/pages/constructor/index.html +++ b/web/lib/pages/territory/constructor/index.html @@ -97,7 +97,7 @@
або - +
diff --git a/web/lib/pages/constructor/script.js b/web/lib/pages/territory/constructor/script.js similarity index 88% rename from web/lib/pages/constructor/script.js rename to web/lib/pages/territory/constructor/script.js index bea7bcb..9d502ba 100644 --- a/web/lib/pages/constructor/script.js +++ b/web/lib/pages/territory/constructor/script.js @@ -3,10 +3,10 @@ let numApartments = 1; let mode = ''; -const Constructor = { +const Territory_constructor = { info: {}, async init() { - let html = await fetch('/lib/pages/constructor/index.html').then((response) => response.text()); + let html = await fetch('/lib/pages/territory/constructor/index.html').then((response) => response.text()); app.innerHTML = html; map = ""; @@ -36,25 +36,25 @@ const Constructor = { title: "Назва", number: "Номер", settlement: "Місто", - init: () => Constructor.points.init(), - next: () => Constructor.points.next(), - save: () => Constructor.points.save() + init: () => Territory_constructor.points.init(), + next: () => Territory_constructor.points.next(), + save: () => Territory_constructor.points.save() }, homestead: { title: "Назва району / села", number: "Номер району", settlement: "Місто", - init: () => Constructor.homestead.init(), - next: () => Constructor.homestead.next(), - save: () => Constructor.homestead.save() + init: () => Territory_constructor.homestead.init(), + next: () => Territory_constructor.homestead.next(), + save: () => Territory_constructor.homestead.save() }, house: { title: "Назва вулиці", number: "Номер будинку", settlement: "Місто", - init: () => Constructor.house.init(), - next: () => Constructor.house.next(), - save: () => Constructor.house.save() + init: () => Territory_constructor.house.init(), + next: () => Territory_constructor.house.next(), + save: () => Territory_constructor.house.save() } }; @@ -95,7 +95,7 @@ const Constructor = { document.getElementById('part-1-button').style.display = "none"; ['title', 'number', 'settlement'].forEach(key => { - Constructor.info[key] = document.getElementById(`info-${key}`).value; + Territory_constructor.info[key] = document.getElementById(`info-${key}`).value; }); if (currentInit) currentInit(); @@ -173,7 +173,7 @@ const Constructor = { part_2_title.innerHTML = `Крок 2. Створення ділянки`; part_2.style.display = ""; - Constructor.osm.init(); + Territory_constructor.osm.init(); }, next() { @@ -191,7 +191,7 @@ const Constructor = {

*Натисніть кнопку нижче, а потім клацайте на карті, щоб додати будинки. Після цього натисніть "Зберегти".

*Щоб видалити будинок, клацніть на ньому та у спливаючому вікні оберіть "Видалити".


- @@ -205,11 +205,11 @@ const Constructor = { }, async save() { - Constructor.info.buildings = Constructor.homestead.building.list; + Territory_constructor.info.buildings = Territory_constructor.homestead.building.list; - console.log(Constructor.info); + console.log(Territory_constructor.info); - Constructor.save(); + Territory_constructor.save(); }, building: { @@ -252,7 +252,7 @@ const Constructor = { marker.bindPopup(` Будинок: ${this.list.length}
Координати: ${geo.lat.toFixed(5)}, ${geo.lng.toFixed(5)}
- + `); setLeafletCursor('crosshair'); @@ -295,7 +295,7 @@ const Constructor = { part_2_title.innerHTML = `Крок 2. Конструктор будинків`; part_2.style.display = ""; - Constructor.osm.init(); + Territory_constructor.osm.init(); }, next() { @@ -308,7 +308,7 @@ const Constructor = { title.innerHTML = `Крок 3. Конструктор квартир`; part_3.appendChild(title); - part_3.innerHTML += `` + part_3.innerHTML += `` part_3.appendChild(button); part_3.style.display = ""; @@ -318,7 +318,7 @@ const Constructor = { async save() { console.log('house next save'); - Constructor.info.entrances = this.apartments.list.map((entrance, entranceIndex) => { + Territory_constructor.info.entrances = this.apartments.list.map((entrance, entranceIndex) => { let apartments = []; let apartmentCounter = 0; @@ -339,7 +339,7 @@ const Constructor = { }; }); - Constructor.save(); + Territory_constructor.save(); }, apartments: { @@ -377,8 +377,8 @@ const Constructor = { div.className = "apartment"; div.id = `apartment-${entrance}-${floor}-${apartment}`; div.innerHTML = ` - - `; @@ -392,7 +392,7 @@ const Constructor = { div.innerHTML = `

Поверх ${floor + 1}

-
`; @@ -405,8 +405,8 @@ const Constructor = { div.id = `entrance-${entrance}`; div.innerHTML = `
- -
`; @@ -417,7 +417,7 @@ const Constructor = { const div = document.createElement("div"); div.id = `house`; div.innerHTML = ` - `; return div; @@ -560,15 +560,15 @@ const Constructor = { let LatLngs = layer.getLatLngs(); LatLngs[0].push(LatLngs[0][0]); - Constructor.info.points.push(LatLngs); - Constructor.info.points_number.push(this.center(layer.getLatLngs())); + Territory_constructor.info.points.push(LatLngs); + Territory_constructor.info.points_number.push(this.center(layer.getLatLngs())); let geo = this.center(layer.getLatLngs()); const house = layer; // сохраняем именно слой - if (Constructor.info.type === 'house') { + if (Territory_constructor.info.type === 'house') { houseGroup.addLayer(house); - } else if (Constructor.info.type === 'homestead') { + } else if (Territory_constructor.info.type === 'homestead') { homesteadGroup.addLayer(house); } @@ -579,7 +579,7 @@ const Constructor = { // при открытии popup вешаем обработчик удаления house.on('popupopen', (e) => { - if (Constructor.homestead.building.editing) { + if (Territory_constructor.homestead.building.editing) { house.closePopup(); return; } @@ -587,12 +587,12 @@ const Constructor = { const btn = e.popup.getElement().querySelector('.map_dell'); if (btn) { btn.addEventListener('click', () => { - Constructor.osm.delete(house); + Territory_constructor.osm.delete(house); }); } }); - Constructor.osm.autoZoom(Constructor.info.points); + Territory_constructor.osm.autoZoom(Territory_constructor.info.points); }); map.pm.setLang("ua"); @@ -605,7 +605,7 @@ const Constructor = { }, newPoligon() { - if (Constructor.info.type === 'house') { + if (Territory_constructor.info.type === 'house') { map.pm.enableDraw('Polygon', { snappable: true, snapDistance: 20, @@ -627,7 +627,7 @@ const Constructor = { fillOpacity: 0.8 } }); - } else if (Constructor.info.type === 'homestead') { + } else if (Territory_constructor.info.type === 'homestead') { map.pm.enableDraw('Polygon', { snappable: true, snapDistance: 20, @@ -658,21 +658,21 @@ const Constructor = { if (!IDs) return; const ids_list = IDs.replace(/\s+/g, "").split(','); - Constructor.info.osm_id = ids_list; + Territory_constructor.info.osm_id = ids_list; houseGroup.clearLayers(); homesteadGroup.clearLayers(); - Constructor.info.points = []; - Constructor.info.points_number = []; - Constructor.info.geo = {} + Territory_constructor.info.points = []; + Territory_constructor.info.points_number = []; + Territory_constructor.info.geo = {} // 1006306041, 1006306065 for (let i = 0; i < ids_list.length; i++) { - const element = await Constructor.osm.getOSM(Constructor.info.osm_id[i]); + const element = await Territory_constructor.osm.getOSM(Territory_constructor.info.osm_id[i]); // Преобразуем координаты в LatLng const LatLngs = [[]]; @@ -687,20 +687,20 @@ const Constructor = { const center = this.center(LatLngs); // Сохраняем в points / points_number - Constructor.info.points.push(LatLngs); - Constructor.info.points_number.push(center); + Territory_constructor.info.points.push(LatLngs); + Territory_constructor.info.points_number.push(center); // Создаем L.polygon - const polyOptions = Constructor.info.type === 'homestead' + const polyOptions = Territory_constructor.info.type === 'homestead' ? { color: "#f2bd53", fillColor: "#f2bd53", fillOpacity: 0.4, dashArray: '5,10' } : { color: "#585858", fillColor: "#f2bd53", fillOpacity: 0.8 }; const house = L.polygon(LatLngs, polyOptions); // Добавляем в нужную группу - if (Constructor.info.type === 'house') { + if (Territory_constructor.info.type === 'house') { houseGroup.addLayer(house); - } else if (Constructor.info.type === 'homestead') { + } else if (Territory_constructor.info.type === 'homestead') { homesteadGroup.addLayer(house); } @@ -711,7 +711,7 @@ const Constructor = { `); house.on('popupopen', (e) => { - if (Constructor.homestead.building.editing) { + if (Territory_constructor.homestead.building.editing) { house.closePopup(); return; } @@ -719,13 +719,13 @@ const Constructor = { const btn = e.popup.getElement().querySelector('.map_dell'); if (btn) { btn.addEventListener('click', () => { - Constructor.osm.delete(house); + Territory_constructor.osm.delete(house); }); } }); } - Constructor.osm.autoZoom(Constructor.info.points); + Territory_constructor.osm.autoZoom(Territory_constructor.info.points); }, center(geo) { @@ -792,8 +792,8 @@ const Constructor = { if (map.getZoom() > 18) map.setZoom(18); setTimeout(() => { - Constructor.info.zoom = map.getZoom(); - Constructor.info.geo = map.getCenter(); + Territory_constructor.info.zoom = map.getZoom(); + Territory_constructor.info.geo = map.getCenter(); }, 200) }, @@ -862,10 +862,10 @@ const Constructor = { async save() { const part_3_button = document.getElementById('part-3-button'); - console.log(Constructor.info); + console.log(Territory_constructor.info); setLeafletCursor('pointer'); - Constructor.homestead.building.editing = false; + Territory_constructor.homestead.building.editing = false; const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}constructor`; @@ -875,7 +875,7 @@ const Constructor = { "Content-Type": "application/json", "Authorization": uuid }, - body: JSON.stringify(Constructor.info) + body: JSON.stringify(Territory_constructor.info) }) .then(response => { if (response.status == 200) { @@ -895,7 +895,7 @@ const Constructor = { Territory.house.list = []; Territory.homestead.list = []; - Router.navigate(`/territory/manager/${Constructor.info.type}/${data.id}`); + Router.navigate(`/territory/manager/${Territory_constructor.info.type}/${data.id}`); setTimeout(() => { part_3_button.innerText = "Зберегти"; diff --git a/web/lib/pages/constructor/style.css b/web/lib/pages/territory/constructor/style.css similarity index 100% rename from web/lib/pages/constructor/style.css rename to web/lib/pages/territory/constructor/style.css diff --git a/web/lib/pages/editor/index.html b/web/lib/pages/territory/editor/index.html similarity index 90% rename from web/lib/pages/editor/index.html rename to web/lib/pages/territory/editor/index.html index befd507..3317989 100644 --- a/web/lib/pages/editor/index.html +++ b/web/lib/pages/territory/editor/index.html @@ -45,7 +45,7 @@ id="info-osm" type="text" placeholder="123, 345, 678" - onchange="Editor.osm.autoPoligon(this.value)" + onchange="Territory_editor.osm.autoPoligon(this.value)" />
або - +
- +