const Stand_card = { id: null, async init(id) { let html = await fetch('/lib/pages/stand/card/index.html').then((response) => response.text()); app.innerHTML = html; Stand_card.id = id; this.info.setHTML(); }, 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(); }, info: { list: [], sheeps: [], async setHTML() { this.list = await Stand_card.loadAPI(`${CONFIG.api}stand/${Stand_card.id}`); this.sheeps = await Stand_card.loadAPI(`${CONFIG.api}sheeps/list/stand`); document.getElementById('stand-info-title').innerText = this.list.title; if(this.list.geo[0]>0){ document.getElementById('stand-info-geo').innerHTML = 'Відкрити Google Maps'; document.getElementById('stand-info-geo').href = `https://www.google.com/maps?q=${this.list.geo[0]},${this.list.geo[1]}`; document.getElementById('stand-info-block-geo').style.display = ""; } else { document.getElementById('stand-info-block-geo').style.display = "none"; } document.getElementById('stand-info-image').setAttribute('src', ''); Stand_card.schedule.setHTML(); } }, // Робота з WebSocket cloud: { update(msg) { const { type, data, user } = msg; const el = document.getElementById(`name-${data?.id}`); if (!el) return; // якщо елемент не знайдено - виходимо const isSelf = user.id == USER.id; switch (type) { case "stand_locking": if (!isSelf) { el.disabled = true; el.style.border = "2px solid var(--PrimaryColor)"; el.style.backgroundColor = "#EAFF0024"; } break; case "stand_unlocking": // Розблокуємо лише якщо подія від іншого користувача if (!isSelf) { el.style.border = ""; el.style.backgroundColor = ""; } if ((!isSelf && !el.value) || USER.possibilities.can_manager_stand) { el.removeAttribute("disabled"); } break; case "stand_update": { const sid = data.sheep_id; const sname = data.sheep_name ?? ""; // Менеджеру показуємо весь перелік if (USER.possibilities.can_manager_stand && Array.isArray(Sheeps?.sheeps_list?.list)) { el.innerHTML = ""; // порожній варіант el.appendChild(Object.assign(document.createElement("option"), { value: "", textContent: " " })); // заповнюємо всіх овечок Stand_card.info.sheeps.forEach(s => { const opt = document.createElement("option"); opt.value = s.id; opt.textContent = s.name; if (s.id == sid) opt.selected = true; el.appendChild(opt); }); el.removeAttribute("disabled"); } else { // Звичайна поведінка для звичайних користувачів if (sid == USER.id) { el.innerHTML = ``; el.removeAttribute("disabled"); } else if (!sid) { el.innerHTML = ``; el.removeAttribute("disabled"); } else { el.innerHTML = ``; el.disabled = true; } } el.style.border = ""; el.style.backgroundColor = ""; break; } default: return; } }, mess: { locking({ id }) { const message = { event: 'message', user: { name: USER.name, id: USER.id }, type: "stand_locking", data: { id: id, stand_id: Stand_card.id, sheep_name: null } }; if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) { Cloud.socket.send(JSON.stringify(message)); } else { if (confirm("З'єднання розірвано! Перепідключитись?")) { Cloud.start(); } } }, unlocking({ id }) { const message = { event: 'message', user: { name: USER.name, id: USER.id }, type: "stand_unlocking", data: { id: id, stand_id: Stand_card.id } }; if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) { Cloud.socket.send(JSON.stringify(message)); } else { if (confirm("З'єднання розірвано! Перепідключитись?")) { Cloud.start(); } } }, update({ sheep_id, id }) { const message = { event: 'message', user: { name: USER.name, id: USER.id }, type: "stand_update", data: { id: id, stand_id: Stand_card.id, sheep_id: sheep_id, sheep_name: null } }; if (USER.possibilities.can_manager_stand) { const pos = Stand_card.info.sheeps.map(e => e.id).indexOf(Number(sheep_id)); if (pos != -1) { let name = Stand_card.info.sheeps[pos].name; message.data.sheep_name = name; } } if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) { Cloud.socket.send(JSON.stringify(message)); } else { if (confirm("З'єднання розірвано! Перепідключитись?")) { Cloud.start(); } } } }, }, schedule: { list: [], genNull() { return `

Доступних днів поки немає

Зачекайте ...

` }, async setHTML() { const block = document.getElementById('stand-schedule'); block.innerHTML = this.genNull(); const null_list = document.getElementById('null-list'); const load_list = document.getElementById('load-list'); null_list.setAttribute("data-visible", "false"); load_list.setAttribute("data-visible", "true"); const url = `${CONFIG.api}stand/schedule/list/${Stand_card.id}`; this.list = Stand_card.grouped(await Stand_card.loadAPI(url)); if (this.list.length == 0) { null_list.setAttribute("data-visible", "true"); load_list.setAttribute("data-visible", "false"); } else { null_list.setAttribute("data-visible", "false"); load_list.setAttribute("data-visible", "false"); } const fragment = document.createDocumentFragment(); const createSelect = (sheep) => { const select = document.createElement("select"); select.id = `name-${sheep.id}`; // пустой option select.appendChild(Object.assign(document.createElement("option"), { value: "", textContent: " " })); // якщо є права менеджера - додаємо всіх користувачів if (USER.possibilities.can_manager_stand && Array.isArray(Stand_card.info.sheeps)) { Stand_card.info.sheeps.sort((a, b) => a.name.localeCompare(b.name, 'uk')); Stand_card.info.sheeps.forEach(s => { const option = document.createElement("option"); option.value = s.id; option.textContent = s.name; if (s.id === sheep.sheep_id) option.selected = true; select.appendChild(option); }); } else { // якщо є власник - показуємо його const opt = document.createElement("option"); if (sheep.sheep_id) { opt.value = sheep.sheep_id; opt.textContent = sheep.sheep_name ?? USER.name; opt.selected = sheep.sheep_id === USER.id; } else { opt.value = USER.id; opt.textContent = USER.name; } select.appendChild(opt); } // якщо зайнятий іншим користувачем - блокуємо if (sheep.sheep_id && sheep.sheep_id !== USER.id && !USER.possibilities.can_manager_stand) { select.disabled = true; select.value = sheep.sheep_id; } // --- обробники --- select.addEventListener("mousedown", () => Stand_card.cloud.mess.locking({ id: sheep.id })); select.addEventListener("change", () => Stand_card.cloud.mess.update({ sheep_id: select.value, id: sheep.id })); select.addEventListener("blur", () => Stand_card.cloud.mess.unlocking({ id: sheep.id })); return select; }; this.list.forEach((day, dayIndex) => { const timestamp = day[0][0].date; const dayDiv = Object.assign(document.createElement("div"), { className: "block-day", id: `day-${dayIndex}` }); dayDiv.appendChild(Object.assign(document.createElement("h3"), { textContent: `${formattedDate(timestamp)} • ${formattedDayName(timestamp)}` })); const step = day[1]?.[0]?.hour - day[0]?.[0]?.hour || Stand_card.info.list.processing_time; // крок між інтервалами day.forEach((hour, hourIndex) => { const hourDiv = Object.assign(document.createElement("div"), { id: `hour-${dayIndex}-${hourIndex}` }); const start = hour[0].hour; const end = start + step; hourDiv.appendChild(Object.assign(document.createElement("span"), { className: "time", textContent: `${Stand_card.formatTime(start)}-${Stand_card.formatTime(end)}` })); hour.forEach(sheep => hourDiv.appendChild(createSelect(sheep))); dayDiv.appendChild(hourDiv); }); fragment.appendChild(dayDiv); }); // кнопка додавання if (USER.possibilities.can_add_stand) { const btn = Object.assign(document.createElement("button"), { id: "stand-new-button", textContent: "Додати день", onclick: () => Stand_card.addStand() }); fragment.appendChild(btn); } block.appendChild(fragment); } }, grouped(list) { const groupedByDate = {}; for (const item of list) { if (!groupedByDate[item.date]) groupedByDate[item.date] = []; groupedByDate[item.date].push(item); } const result = Object.values(groupedByDate).map(dateGroup => { const groupedByHour = []; let currentGroup = []; let lastHour = null; for (const item of dateGroup) { if (item.hour !== lastHour) { if (currentGroup.length > 0) groupedByHour.push(currentGroup); currentGroup = []; lastHour = item.hour; } currentGroup.push(item); } if (currentGroup.length > 0) groupedByHour.push(currentGroup); return groupedByHour; }); return result; }, formatTime(hours) { let h = Math.floor(hours); let m = (hours % 1 === 0.5) ? "30" : "00"; let hh = h.toString().padStart(2, "0"); return `${hh}:${m}`; }, async addStand() { const button = document.getElementById('stand-new-button'); const uuid = localStorage.getItem('uuid'); const URL = `${CONFIG.api}stand/schedule/${Stand_card.id}`; await fetch(URL, { method: 'POST', headers: { "Content-Type": "application/json", "Authorization": uuid } }) .then(response => { if (response.status == 200) { console.log({ 'setPack': 'ok' }); button.innerText = "День додано"; Notifier.success('День додано'); return response.json() } else { console.log('err'); button.innerText = "Помилка запису"; Notifier.error('Помилка додавання'); return } }) .then(data => { console.log(data); Stand_card.schedule.setHTML(); setTimeout(() => { button.innerText = "Додати день"; }, 3000); }) .catch(err => { console.log(err); button.innerText = "Помилка запису"; }) } }