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}