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
? ``
: ``;
const details = document.createElement('details');
if (show === "open") details.setAttribute('open', '');
details.innerHTML = `
${title}