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: [],
async setHTML() {
const url = `${CONFIG.api}stand/${Stand_card.id}`;
this.list = await Stand_card.loadAPI(url);
document.getElementById('stand-info-title').innerText = this.list.title;
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-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: " "
}));
// заполняем всех овечек
Sheeps.sheeps_list.list.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 (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 (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 = Sheeps.sheeps_list.list.map(e => e.id).indexOf(Number(sheep_id));
if (pos != -1) {
let name = Sheeps.sheeps_list.list[pos].name;
message.data.sheep_name = name;
}
}
if (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(Sheeps?.sheeps_list?.list)) {
Sheeps.sheeps_list.list.sort((a, b) => a.name.localeCompare(b.name, 'uk'));
Sheeps.sheeps_list.list.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 = "Стенд(и) додано";
return response.json()
} else {
console.log('err');
button.innerText = "Помилка запису";
return
}
})
.then(data => {
console.log(data);
Stand_card.schedule.setHTML();
setTimeout(() => {
button.innerText = "Додати стенд(и)";
}, 3000);
})
.catch(err => {
console.log(err);
button.innerText = "Помилка запису";
})
},
edit({ sheep_id, stand_id }) {
}
}