624 lines
26 KiB
JavaScript
624 lines
26 KiB
JavaScript
let map_card;
|
||
|
||
const Territory_card = {
|
||
// Глобальні змінні стану
|
||
id: null,
|
||
socket: null,
|
||
reconnectTimeout: null,
|
||
reconnectAttempts: 0,
|
||
listEntrances: [],
|
||
listApartment: [],
|
||
listBuilding: [],
|
||
|
||
// Кольори статусів квартир
|
||
color_status: [
|
||
["var(--ColorThemes2)", "var(--ColorThemes3)"],
|
||
["#fbf1e0", "#ff8300"],
|
||
["#fce3e2", "#ff0000"],
|
||
["#d7ddec", "#2919bd"],
|
||
["#d5e9dd", "#11a568"],
|
||
["#d7ebfa", "#3fb4fc"],
|
||
["#e8dbf5", "#b381eb"]
|
||
],
|
||
|
||
// Ініціалізація сторінки
|
||
async init(type, Id) {
|
||
// Завантажуємо HTML
|
||
const html = await fetch('/lib/pages/territory/card/index.html').then(r => r.text());
|
||
app.innerHTML = html;
|
||
Territory_card.id = Id;
|
||
|
||
// Закриваємо старий WebSocket
|
||
if (this.socket) this.socket.close(1000, "Перезапуск з'єднання");
|
||
// this.cloud.start(makeid(6));
|
||
this.cloud.start()
|
||
|
||
// Якщо це сторінка будинку, отримуємо під’їзди та стартуємо WebSocket
|
||
if (type === "house") {
|
||
const controls = document.getElementById('page-card-controls');
|
||
controls.style.display = "flex";
|
||
|
||
// Застосовуємо режим сортування
|
||
this.sort(localStorage.getItem('sort_mode'), false);
|
||
this.getEntrances({ update: false });
|
||
} else if (type === "homestead") {
|
||
this.getHomestead.map({});
|
||
}
|
||
|
||
// Додаємо обробник закриття попапу
|
||
const popup = document.getElementById('card-new-date');
|
||
if (!popup.dataset.listenerAdded) {
|
||
popup.addEventListener('click', (e) => {
|
||
if (!popup.querySelector('.mess').contains(e.target)) {
|
||
this.dateEditor.close();
|
||
}
|
||
});
|
||
popup.dataset.listenerAdded = 'true';
|
||
}
|
||
},
|
||
|
||
// Робота з WebSocket
|
||
cloud: {
|
||
start() {
|
||
const uuid = localStorage.getItem("uuid");
|
||
const ws = new WebSocket(CONFIG.wss, uuid);
|
||
Territory_card.socket = ws;
|
||
|
||
ws.onopen = () => {
|
||
console.log("[WebSocket] З'єднання встановлено");
|
||
Territory_card.cloud.setStatus('ok');
|
||
ws.send(JSON.stringify({
|
||
event: 'connection',
|
||
id: getTimeInSeconds(),
|
||
date: getTimeInSeconds(),
|
||
uuid,
|
||
user: {
|
||
name: USER.name,
|
||
id: USER.id
|
||
},
|
||
data: {}
|
||
}));
|
||
Territory_card.reconnectAttempts = 0;
|
||
clearTimeout(Territory_card.reconnectTimeout);
|
||
};
|
||
|
||
ws.onmessage = (e) => {
|
||
const data = JSON.parse(e.data);
|
||
if (data.event === 'connection' && data.user.id !== USER.id) {
|
||
console.log(`Новий користувач: ${data.user}`);
|
||
}
|
||
if (data.event === 'message') {
|
||
Territory_card.cloud.update(data);
|
||
}
|
||
};
|
||
|
||
ws.onclose = () => {
|
||
console.warn("[WebSocket] З'єднання розірвано");
|
||
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("З'єднання розірвано! Перепідключитись?")) {
|
||
Territory_card.reconnectAttempts = 0;
|
||
Territory_card.getEntrances({ update: true });
|
||
Territory_card.cloud.start();
|
||
}
|
||
}
|
||
};
|
||
|
||
ws.onerror = (err) => {
|
||
console.error("[WebSocket] Помилка", err);
|
||
Territory_card.cloud.setStatus('err');
|
||
};
|
||
},
|
||
|
||
setStatus(mode) {
|
||
const ids = ['cloud_1', 'cloud_2', 'cloud_3'];
|
||
ids.forEach((id, idx) => {
|
||
const el = document.getElementById(id);
|
||
el.setAttribute('data-state', ['sync', 'ok', 'err'].indexOf(mode) === idx ? 'active' : '');
|
||
});
|
||
},
|
||
|
||
update(msg) {
|
||
if (msg.type !== "apartment" && msg.type !== "building") return;
|
||
|
||
const [bg, color] = Territory_card.color_status[msg.data.status];
|
||
|
||
const id = msg.data.id;
|
||
const el = document.getElementById(`status_${id}`);
|
||
const redDot = document.getElementById(`redDot_${id}`);
|
||
|
||
if (msg.type === "building") {
|
||
redDot.style = `background:${bg};border:2px solid ${color}`;
|
||
const apt = Territory_card.listBuilding.find(e => e.id === id);
|
||
|
||
if (!apt) return;
|
||
|
||
apt.status = msg.data.status;
|
||
apt.description = msg.data.description;
|
||
apt.updated_at = msg.data.updated_at;
|
||
|
||
if (!el) return;
|
||
|
||
let date = new Date(msg.data.updated_at);
|
||
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
||
document.getElementById(`date_${id}`).value = date.toISOString().slice(0, 16);
|
||
|
||
} else if (msg.type === "apartment") {
|
||
if (!el) return;
|
||
document.getElementById(`date_text_${id}`).innerText = formattedDateTime(msg.data.updated_at);
|
||
|
||
}
|
||
|
||
if (!el) return;
|
||
document.getElementById(`card_${id}`).style = `background:${bg};color:${color};border:1px solid ${color}`;
|
||
el.value = msg.data.status;
|
||
el.style = `background:${bg};color:${color};border:1px solid ${color}`;
|
||
document.getElementById(`description_${id}`).value = msg.data.description;
|
||
|
||
},
|
||
|
||
messApartment({ number, id, update, time }) {
|
||
const apt = Territory_card.listApartment[number]?.find(e => e.id === id);
|
||
if (!apt) return;
|
||
|
||
const statusEl = document.getElementById(`status_${id}`);
|
||
const descEl = document.getElementById(`description_${id}`);
|
||
|
||
let date = () => {
|
||
if (!update && !time) {
|
||
return apt.updated_at;
|
||
} else if (update && !time) {
|
||
return getTimeInSeconds();
|
||
} else if (update && time) {
|
||
return getTimeInSeconds(time);
|
||
}
|
||
}
|
||
|
||
apt.status = Number(statusEl.value);
|
||
apt.description = descEl.value;
|
||
apt.updated_at = date();
|
||
|
||
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(),
|
||
user: {
|
||
name: USER.name,
|
||
id: USER.id
|
||
},
|
||
type: "apartment",
|
||
data: {
|
||
...apt,
|
||
sheep_id: USER.id
|
||
}
|
||
};
|
||
|
||
if (Territory_card.socket?.readyState === WebSocket.OPEN) {
|
||
Territory_card.socket.send(JSON.stringify(message));
|
||
} else {
|
||
if (confirm("З'єднання розірвано! Перепідключитись?")) {
|
||
Territory_card.getEntrances({ update: true });
|
||
Territory_card.cloud.start();
|
||
}
|
||
}
|
||
},
|
||
|
||
messBuildings({ id, update, time }) {
|
||
const apt = Territory_card.listBuilding.find(e => e.id === id);
|
||
if (!apt) return;
|
||
|
||
const statusEl = document.getElementById(`status_${id}`);
|
||
const descEl = document.getElementById(`description_${id}`);
|
||
const dateEl = document.getElementById(`date_text_${id}`);
|
||
|
||
let date = () => {
|
||
if (!update && !time) {
|
||
return apt.updated_at;
|
||
} else if (update && !time) {
|
||
return getTimeInSeconds();
|
||
} else if (update && time) {
|
||
const ts = new Date(time).getTime();
|
||
return getTimeInSeconds(ts);
|
||
}
|
||
}
|
||
|
||
apt.status = Number(statusEl.value);
|
||
apt.description = descEl.value;
|
||
apt.updated_at = date();
|
||
|
||
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(),
|
||
user: {
|
||
name: USER.name,
|
||
id: USER.id
|
||
},
|
||
type: "building",
|
||
data: {
|
||
...apt,
|
||
sheep_id: USER.id
|
||
}
|
||
};
|
||
|
||
if (Territory_card.socket?.readyState === WebSocket.OPEN) {
|
||
Territory_card.socket.send(JSON.stringify(message));
|
||
} else {
|
||
if (confirm("З'єднання розірвано! Перепідключитись?")) {
|
||
Territory_card.getEntrances({ update: true });
|
||
Territory_card.cloud.start();
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
// Отримання під’їздів
|
||
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: {
|
||
"Content-Type": "application/json",
|
||
"Authorization": uuid
|
||
}
|
||
});
|
||
|
||
const data = await res.json();
|
||
this.listEntrances = data;
|
||
|
||
const container = document.getElementById('list');
|
||
if (!update) container.innerHTML = "";
|
||
|
||
if (update) {
|
||
for (const { id, entrance_number } of data) {
|
||
this.getApartment({ id, number: entrance_number, update: true });
|
||
}
|
||
return;
|
||
}
|
||
|
||
const fragment = document.createDocumentFragment();
|
||
|
||
const canManage = USER.mode === 2 || (USER.mode === 1 && USER.possibilities.can_manager_territory);
|
||
|
||
for (const element of data) {
|
||
const { id, entrance_number, title, history, working } = element;
|
||
const isMy = ((history.name === "Групова" && history.group_id == USER.group_id) || history.name === USER.name);
|
||
|
||
const show = (isMy && working) ? "open" : canManage ? "close" : null;
|
||
if (!show) continue;
|
||
|
||
const icon = isMy && working
|
||
? `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M 12 1 C 9.1277778 1 6.7189086 3.0461453 6.1230469 5.7871094 L 8.078125 6.2128906 C 8.4822632 4.3538547 10.072222 3 12 3 C 14.27619 3 16 4.7238095 16 7 L 16 8 L 6 8 C 4.9069372 8 4 8.9069372 4 10 L 4 20 C 4 21.093063 4.9069372 22 6 22 L 18 22 C 19.093063 22 20 21.093063 20 20 L 20 10 C 20 8.9069372 19.093063 8 18 8 L 18 7 C 18 3.6761905 15.32381 1 12 1 z M 6 10 L 18 10 L 18 20 L 6 20 L 6 10 z M 12 13 C 10.9 13 10 13.9 10 15 C 10 16.1 10.9 17 12 17 C 13.1 17 14 16.1 14 15 C 14 13.9 13.1 13 12 13 z"/></svg>`
|
||
: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M 12 1 C 8.6761905 1 6 3.6761905 6 7 L 6 8 C 4.9069372 8 4 8.9069372 4 10 L 4 20 C 4 21.093063 4.9069372 22 6 22 L 18 22 C 19.093063 22 20 21.093063 20 20 L 20 10 C 20 8.9069372 19.093063 8 18 8 L 18 7 C 18 3.6761905 15.32381 1 12 1 z M 12 3 C 14.27619 3 16 4.7238095 16 7 L 16 8 L 8 8 L 8 7 C 8 4.7238095 9.7238095 3 12 3 z M 6 10 L 18 10 L 18 20 L 6 20 L 6 10 z M 12 13 C 10.9 13 10 13.9 10 15 C 10 16.1 10.9 17 12 17 C 13.1 17 14 16.1 14 15 C 14 13.9 13.1 13 12 13 z"/></svg>`;
|
||
|
||
const details = document.createElement('details');
|
||
if (show === "open") details.setAttribute('open', '');
|
||
details.innerHTML = `
|
||
<summary><p>${title}</p>${icon}</summary>
|
||
<div id="apartments_${id}" class="apartments_list"></div>
|
||
`;
|
||
fragment.appendChild(details);
|
||
|
||
this.getApartment({ id, number: entrance_number, update: false });
|
||
}
|
||
|
||
container.appendChild(fragment);
|
||
},
|
||
|
||
async getApartment({ id, number, update }) {
|
||
const uuid = localStorage.getItem('uuid');
|
||
const res = await fetch(`${CONFIG.api}/apartment/${id}`, {
|
||
headers: { "Authorization": uuid }
|
||
});
|
||
const data = await res.json();
|
||
this.listApartment[number] = data;
|
||
|
||
const sort_mode = localStorage.getItem('sort_mode') ?? "1";
|
||
const sorters = {
|
||
"1": (a, b) => a.apartment_number - b.apartment_number,
|
||
"2": (a, b) => b.apartment_number - a.apartment_number,
|
||
"3": (a, b) => a.updated_at - b.updated_at,
|
||
"4": (a, b) => b.updated_at - a.updated_at,
|
||
};
|
||
data.sort(sorters[sort_mode] || sorters["1"]);
|
||
|
||
const container = document.getElementById(`apartments_${id}`);
|
||
if (!update) container.innerHTML = "";
|
||
|
||
const statusOptions = (selected) => {
|
||
const labels = ["", "Відмова (Не цікавить)", "Не заходити (Груба відмова)", "Нема домофона", "Повторна відвідина", "Немає вдома", "Свідки Єгови"];
|
||
return labels.map((txt, i) => `<option value="${i}" ${i === selected ? "selected" : ""}>${txt}</option>`).join("");
|
||
};
|
||
|
||
if (update) {
|
||
for (const apt of data) {
|
||
const [bg, color] = this.color_status[apt.status];
|
||
const style = `background:${bg};color:${color};border:1px solid ${color}`;
|
||
const dateEl = document.getElementById(`date_${apt.id}`);
|
||
const cardEl = document.getElementById(`card_${apt.id}`);
|
||
const statusEl = document.getElementById(`status_${apt.id}`);
|
||
const dateTextEl = document.getElementById(`date_text_${apt.id}`);
|
||
const descEl = document.getElementById(`description_${apt.id}`);
|
||
|
||
if (cardEl) cardEl.style = style;
|
||
if (statusEl) {
|
||
statusEl.value = apt.status;
|
||
statusEl.style = style;
|
||
}
|
||
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 ?? "";
|
||
}
|
||
} else {
|
||
const fragment = document.createDocumentFragment();
|
||
|
||
if (data.length == 0) {
|
||
const p = document.createElement('p');
|
||
|
||
p.innerHTML = `
|
||
Інформація про цей під'їзд відсутня. Надайте інформацію відповідальному за території.
|
||
`;
|
||
|
||
return container.appendChild(p);
|
||
|
||
|
||
}
|
||
|
||
for (const apt of data) {
|
||
const [bg, color] = this.color_status[apt.status];
|
||
const style = `background:${bg};color:${color};border:1px solid ${color}`;
|
||
|
||
const div = document.createElement('div');
|
||
|
||
div.className = `card_info`;
|
||
div.id = `card_${apt.id}`;
|
||
div.style = style;
|
||
|
||
div.innerHTML = `
|
||
<div class="info">
|
||
<span>кв.${apt.title}</span>
|
||
<select id="status_${apt.id}" onchange="Territory_card.cloud.messApartment({number:${number},id:${apt.id},update:true})" style="${style}">
|
||
${statusOptions(apt.status)}
|
||
</select>
|
||
<button id="date_${apt.id}" onclick="Territory_card.dateEditor.open({id:${apt.id},number:${number},updated_at:${apt.updated_at}})">
|
||
<p id="date_text_${apt.id}">${formattedDateTime(apt.updated_at)}</p>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M 22.828125 3 C 22.316375 3 21.804562 3.1954375 21.414062 3.5859375 L 19 6 L 24 11 L 26.414062 8.5859375 C 27.195062 7.8049375 27.195062 6.5388125 26.414062 5.7578125 L 24.242188 3.5859375 C 23.851688 3.1954375 23.339875 3 22.828125 3 z M 17 8 L 5.2597656 19.740234 C 5.2597656 19.740234 6.1775313 19.658 6.5195312 20 C 6.8615312 20.342 6.58 22.58 7 23 C 7.42 23.42 9.6438906 23.124359 9.9628906 23.443359 C 10.281891 23.762359 10.259766 24.740234 10.259766 24.740234 L 22 13 L 17 8 z M 4 23 L 3.0566406 25.671875 A 1 1 0 0 0 3 26 A 1 1 0 0 0 4 27 A 1 1 0 0 0 4.328125 26.943359 A 1 1 0 0 0 4.3378906 26.939453 L 4.3632812 26.931641 A 1 1 0 0 0 4.3691406 26.927734 L 7 26 L 5.5 24.5 L 4 23 z"></path></svg>
|
||
</button>
|
||
</div>
|
||
<textarea id="description_${apt.id}" onchange="Territory_card.cloud.messApartment({number:${number},id:${apt.id}})" placeholder="Коротка нотатка.">${apt.description || ""}</textarea>
|
||
`;
|
||
|
||
fragment.appendChild(div);
|
||
}
|
||
|
||
container.appendChild(fragment);
|
||
}
|
||
},
|
||
|
||
getHomestead: {
|
||
markers: {},
|
||
|
||
async loadAPI({ url }) {
|
||
const uuid = localStorage.getItem("uuid");
|
||
|
||
const response = await fetch(url, {
|
||
method: 'GET',
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
"Authorization": uuid
|
||
}
|
||
});
|
||
|
||
return await response.json();
|
||
},
|
||
|
||
async map({ homestead_id = Territory_card.id }) {
|
||
let data = await this.loadAPI({ url: `${CONFIG.api}homestead/${homestead_id}` });
|
||
|
||
console.log(data);
|
||
|
||
|
||
let lat = data.geo?.lat ?? data.points?.[0]?.[0]?.[0]?.lat ?? 49.5629016;
|
||
let lng = data.geo?.lng ?? data.points?.[0]?.[0]?.[0]?.lng ?? 25.6145625;
|
||
let zoom = 15;
|
||
|
||
if (map_card && map_Territory_card.remove) {
|
||
map_Territory_card.stopLocate();
|
||
map_Territory_card.remove();
|
||
}
|
||
|
||
const mapElement = document.getElementById('map_card');
|
||
mapElement.style.display = "flex";
|
||
if (!mapElement) return;
|
||
|
||
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', {
|
||
});
|
||
|
||
let mytile = L.tileLayer('https://sheep-service.com/map/{z}/{x}/{y}.webp', {
|
||
maxZoom: 20,
|
||
minZoom: 15,
|
||
tms: true
|
||
});
|
||
|
||
map_card = L.map(mapElement, {
|
||
renderer: L.canvas(),
|
||
center: [lat, lng],
|
||
zoom,
|
||
zoomControl: false,
|
||
layers: [
|
||
googleHybrid,
|
||
osm,
|
||
mytile
|
||
]
|
||
});
|
||
|
||
// слежение в реальном времени
|
||
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_Territory_card._userMarker.setLatLng(e.latlng);
|
||
}
|
||
});
|
||
|
||
let baseMaps = {
|
||
"Google Hybrid": googleHybrid,
|
||
"OpenStreetMap": osm,
|
||
"Sheep Service Map": mytile,
|
||
};
|
||
|
||
let layerControl = L.control.layers(baseMaps, [], { position: 'bottomright' }).addTo(map_card);
|
||
|
||
map_Territory_card.pm.setLang("ua");
|
||
|
||
const polygonOptions = {
|
||
color: "#f2bd53",
|
||
radius: 500,
|
||
fillOpacity: 0.3,
|
||
dashArray: '20,15',
|
||
dashOffset: '20',
|
||
};
|
||
|
||
L.polygon(data.points, polygonOptions).addTo(map_card);
|
||
|
||
map_Territory_card.setZoom(data.zoom);
|
||
|
||
// map_Territory_card.getZoom()
|
||
|
||
// console.log(data.zoom);
|
||
|
||
|
||
Territory_card.listBuilding = await this.loadAPI({ url: `${CONFIG.api}building/${homestead_id}` });
|
||
|
||
const statusOptions = (selected) => {
|
||
const labels = ["", "Відмова (Не цікавить)", "Не заходити (Груба відмова)", "Нема домофона", "Повторна відвідина", "Немає вдома", "Свідки Єгови"];
|
||
return labels.map((txt, i) => `<option value="${i}" ${i === selected ? "selected" : ""}>${txt}</option>`).join("");
|
||
};
|
||
|
||
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",
|
||
html: `<div id="redDot_${element.id}" style='background:${bg};border:2px solid ${color}'></div>`,
|
||
iconSize: [16, 16],
|
||
iconAnchor: [8, 8]
|
||
});
|
||
|
||
// создаём маркер
|
||
const marker = L.marker(element.geo, { icon: redDot }).addTo(map_card);
|
||
|
||
marker.bindPopup("");
|
||
|
||
// при открытии popup генерим div заново
|
||
marker.on("popupopen", () => {
|
||
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");
|
||
div.className = "card_info card_info_homestead";
|
||
div.id = `card_${el.id}`;
|
||
div.style = style;
|
||
|
||
let date = new Date(el.updated_at);
|
||
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
||
|
||
div.innerHTML = `
|
||
<div class="info">
|
||
<select id="status_${element.id}" onchange="Territory_card.cloud.messBuildings({id:${element.id},update:true})" style="${style}">
|
||
${statusOptions(element.status)}
|
||
</select>
|
||
<input type="datetime-local" id="date_${element.id}" placeholder="Дата" onchange="Territory_card.cloud.messBuildings({id:${element.id},update:true, time: this.value })" value="${date.toISOString().slice(0, 16)}">
|
||
</div>
|
||
<textarea id="description_${element.id}" onchange="Territory_card.cloud.messBuildings({id:${element.id}})" placeholder="Коротка нотатка.">${element.description || ""}</textarea>
|
||
`;
|
||
|
||
marker.setPopupContent(div);
|
||
});
|
||
|
||
Territory_card.getHomestead.markers[element.id] = marker; // сохраним ссылку на маркер
|
||
}
|
||
},
|
||
},
|
||
|
||
// Сортування
|
||
sort(mode, load) {
|
||
const idx = Math.max(1, Math.min(4, Number(mode) || 1));
|
||
['sort_1', 'sort_2', 'sort_3', 'sort_4'].forEach((id, i) => {
|
||
document.getElementById(id)?.setAttribute('data-state', i + 1 === idx ? 'active' : '');
|
||
});
|
||
localStorage.setItem('sort_mode', idx);
|
||
if (!load) this.getEntrances({ update: false });
|
||
},
|
||
|
||
// Редактор дати
|
||
dateEditor: {
|
||
open({ id, number, updated_at }) {
|
||
const block = document.getElementById('card-new-date');
|
||
const input = document.getElementById('card-new-date-input');
|
||
const button = document.getElementById('card-new-date-button');
|
||
|
||
// Приводимо дату до ISO без зсуву часового поясу
|
||
let date = new Date(updated_at == 0 ? Date.now() : updated_at);
|
||
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
||
input.value = date.toISOString().slice(0, 16)
|
||
|
||
// Призначаємо обробники
|
||
input.setAttribute("onchange", `Territory_card.dateEditor.edit({ id: ${id}, number: ${number} })`)
|
||
button.setAttribute("onclick", `Territory_card.dateEditor.edit({ id: ${id}, number: ${number}, type: 'now'})`)
|
||
|
||
|
||
// Показуємо блок
|
||
block.style.display = "";
|
||
requestAnimationFrame(() => block.style.opacity = "1");
|
||
},
|
||
close() {
|
||
// Робимо плавне зникнення
|
||
const block = document.getElementById('card-new-date');
|
||
block.style.opacity = "0";
|
||
|
||
const onTransitionEnd = () => {
|
||
block.style.display = "none";
|
||
block.removeEventListener("transitionend", onTransitionEnd);
|
||
};
|
||
block.addEventListener("transitionend", onTransitionEnd);
|
||
},
|
||
edit({ id, number, type }) {
|
||
let input = document.getElementById('card-new-date-input').value;
|
||
|
||
if (type == "now") {
|
||
Territory_card.cloud.messApartment({ number: number, id: id, update: true });
|
||
} else {
|
||
if (input) {
|
||
const ts = new Date(input).getTime();
|
||
Territory_card.cloud.messApartment({ number, id, update: true, time: ts });
|
||
} else {
|
||
Territory_card.cloud.messApartment({ number: number, id: id });
|
||
}
|
||
}
|
||
this.close();
|
||
}
|
||
}
|
||
} |