This commit is contained in:
2025-03-31 00:22:21 +03:00
commit 38f2a05107
146 changed files with 66771 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
<style>
*[disabled] {
opacity: 0.5;
}
select {
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
/* Arrow */
appearance: none;
background-image: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%237a899d%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E");
background-repeat: no-repeat;
background-position: right 0.3rem top 50%;
background-size: 0.55rem auto;
}
#header {
display: flex;
justify-content: space-around;
}
#header p {
font-size: 20px;
}
#list {
display: flex;
flex-direction: column;
align-items: center;
}
details {
color: var(--ColorThemes3);
width: 100%;
min-width: 320px;
background: var(--ColorThemes1);
margin: 20px 0px;
border-radius: 10px;
overflow: hidden;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.04), 0px 0px 2px rgba(0, 0, 0, 0.04),
0px 0px 1px rgba(0, 0, 0, 0.04);
}
@media (min-width: 900px) {
#list {
align-items: flex-start;
justify-content: space-between;
flex-direction: row;
flex-wrap: wrap;
}
details {
width: calc(50% - 10px);
}
}
details[disabled] summary,
details.disabled summary {
pointer-events: none;
user-select: none;
}
details summary::-webkit-details-marker,
details summary::marker {
display: none;
content: "";
}
summary {
cursor: pointer;
background: #7a8a9d;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
height: 45px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: space-between;
}
summary p {
padding: 0 10px;
color: var(--PrimaryColorText);
font-size: 14px;
font-weight: 500;
}
summary svg {
width: 25px;
height: 25px;
padding: 0 10px;
fill: var(--PrimaryColorText);
}
.apartments_list {
padding: 10px;
border-bottom: 2px solid var(--PrimaryColor);
border-left: 2px solid var(--PrimaryColor);
border-right: 2px solid var(--PrimaryColor);
border-radius: 0 0 10px 10px;
}
@media (max-width: 500px) {
.apartments_list {
padding: 1px;
}
}
.apartments_list > div {
display: flex;
font-size: 14px;
border-radius: 8px;
margin: 10px 10px 15px 10px;
flex-direction: column;
align-items: stretch;
border: 1px solid var(--ColorThemes3);
background: var(--ColorThemes2);
}
.apartments_list > div > .info {
display: flex;
font-size: 14px;
align-items: center;
justify-content: space-between;
border-radius: 8px;
}
.apartments_list > div > .info > span {
min-width: 40px;
font-size: 12px;
position: relative;
margin: 5px;
}
.apartments_list > div > .info > select {
color: #3d3d3d;
border-radius: 6px;
border: 1px solid #eaebef;
margin: 5px;
background-color: var(--ColorThemes3);
min-width: 110px;
width: 100%;
padding: 4px 20px 4px 4px;
height: 30px;
}
.apartments_list > div > .info > input {
color: #3d3d3d;
border-radius: 6px;
border: 1px solid #eaebef;
margin: 5px;
background-color: var(--ColorThemes3);
background-color: var(--ColorThemes0);
border: 1px solid var(--ColorThemes1);
color: var(--ColorThemes3);
width: 100%;
min-width: 70px;
padding: 0 4px;
height: calc(30px - 2px);
appearance: none;
-webkit-appearance: none;
}
.apartments_list > div > textarea {
border-radius: 6px;
font-size: 14px;
margin: 5px;
background-color: var(--ColorThemes0);
border: 1px solid var(--ColorThemes1);
color: var(--ColorThemes3);
width: calc(100% - 22px);
min-width: 70px;
padding: 5px;
min-height: 40px;
appearance: none;
resize: vertical;
-webkit-appearance: none;
}
</style>
<div class="page-card">
<div id="header">
<p id="status"></p>
<p id="hash"></p>
</div>
<div id="list"></div>
</div>

View File

@@ -0,0 +1,358 @@
let socket, username;
let listEntrances = []
let listApartment = []
let holdTimer;
let startTime;
const Card = {
init: async (type, id) => {
let html = await fetch('/lib/pages/card/index.html').then((response) => response.text());
app.innerHTML = html;
house = id;
if (socket) socket.close(1000, "Перезапуск соединения");
if (type == "house") {
getEntrances();
start(makeid(6));
}
document.addEventListener("mousedown", handleStart);
document.addEventListener("touchstart", handleStart);
document.addEventListener("mouseup", handleCancel);
document.addEventListener("mouseleave", handleCancel);
document.addEventListener("touchend", handleCancel);
document.addEventListener("touchcancel", handleCancel);
function handleStart(event) {
const button = event.target.closest(".hold-button");
if (!button) return;
// event.preventDefault();
startTime = Date.now();
holdTimer = setTimeout(() => {
console.log("Долгое нажатие на", button.name);
let number_id = button.name.split("-");
mess(Number(number_id[0]), Number(number_id[1]));
}, 1000);
}
function handleCancel() {
const holdDuration = Date.now() - startTime; // Считаем, сколько длилось нажатие
if (holdDuration < 1000) {
clearTimeout(holdTimer); // Если нажали менее 1 секунды, сбрасываем таймер
}
}
}
}
// let color_status = [
// "#000000",
// "#C16917",
// "#b10202",
// "#3d3d3d",
// "#11734b",
// "#6cc5fc",
// "#5a3286"
// ];
// let color_status = [
// ["#ffffff", "#000000"],
// ["#e7af32", "#ffffff"],
// ["#fc2a2a", "#ffffff"],
// ["#3d3d3d", "#ffffff"],
// ["#11a568", "#ffffff"],
// ["#6cc5fc", "#ffffff"],
// ["#b381eb", "#ffffff"]
// ];
let color_status = [
["var(--ColorThemes2)", "var(--ColorThemes3)"],
["#fbf1e0", "#ff8300"],
["#fce3e2", "#ff0000"],
["#d7ddec", "#2919bd"],
["#d5e9dd", "#11a568"],
["#d7ebfa", "#3fb4fc"],
["#e8dbf5", "#b381eb"]
];
function start(name) {
if (!name) return;
document.getElementById("hash").innerText = `HASH: ${name}`
username = name;
let uuid = localStorage.getItem("uuid");
socket = new WebSocket(`${CONFIG.wss}?uuid=${uuid}`);
socket.onopen = function (e) {
console.log("[WebSocket | open] Соединение установлено");
document.getElementById("status").innerText = "WebSocket | open";
const message = {
event: 'connection',
id: getTimeInSeconds(),
date: getTimeInSeconds(),
uuid: uuid,
username: name,
data: {
id: 1,
entrance_id: 1,
apartment_number: 1,
title: "1",
group_number: 1,
status: 1,
description: "",
created_at: 1727541827,
updated_at: 1727541827
}
}
socket.send(JSON.stringify(message))
};
socket.onmessage = function (event) {
let data = JSON.parse(event.data)
if (data.event == 'connection') {
if (data.username == username) return
console.log(`Добавлен новый пользователь по имени ${data.username}`);
} else if (data.event == 'message') {
update(data);
if (data.username == username) return
console.log(`${data.username} пишет: `, data.data);
}
};
socket.onclose = function (event) {
if (event.wasClean) {
document.getElementById("status").innerText = "WebSocket | close"
console.log(`[WebSocket | close] Соединение закрыто чисто, код=${event.code} причина=${event.reason}`);
} else {
document.getElementById("status").innerText = "WebSocket | close"
console.log('[WebSocket | close] Соединение прервано');
// setTimeout(function() {
// start(username);
// }, 1000);
const result = confirm(`З'єднання розірвано! Перепідключитись?`);
if (result) {
getEntrances();
start(username);
}
}
};
socket.onerror = function (error) {
console.log(`[WebSocket | error]`);
document.getElementById("status").innerText = "WebSocket | error"
};
}
function mess(entrance_number, id, date_type) {
let sort_mode = localStorage.getItem('sort_mode') ?? true;
console.log(id, listApartment[entrance_number]);
const pos = listApartment[entrance_number].map(e => e.id).indexOf(id);
let apartment = listApartment[entrance_number][pos];
console.log(pos, apartment);
let status = document.getElementById(`status_${id}`);
let description = document.getElementById(`description_${id}`);
let date = new Date(document.getElementById(`date_${id}`).value);
const timestamp = date.getTime();
apartment.description = description.value;
apartment.status = Number(status.value);
apartment.updated_at = date_type ? getTimeInSeconds(timestamp) : getTimeInSeconds(),
status.style.backgroundColor = color_status[status.value][0];
status.style.color = color_status[status.value][1];
status.style.border = `1px solid ${color_status[status.value][1]}`;
let user_hash = localStorage.getItem('hash');
let message = {
event: 'message',
id: getTimeInSeconds(),
date: getTimeInSeconds(),
hash: user_hash,
username: username,
data: {
id: apartment.id,
entrance_id: apartment.entrance_id,
apartment_number: apartment.apartment_number,
title: apartment.title,
group_number: apartment.group_number,
status: apartment.status,
description: apartment.description,
updated_at: apartment.updated_at,
}
}
socket.send(JSON.stringify(message));
if (!date_type && sort_mode != 'false') sort(apartment.id, apartment.entrance_id);
}
function update(message) {
if (!document.getElementById(`status_${message.data.id}`)) return;
let now = new Date(message.data.updated_at);
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
document.getElementById(`card_${message.data.id}`).style.backgroundColor = color_status[message.data.status][0];
document.getElementById(`card_${message.data.id}`).style.color = color_status[message.data.status][1];
document.getElementById(`card_${message.data.id}`).style.border = `1px solid ${color_status[message.data.status][1]}`;
document.getElementById(`status_${message.data.id}`).style.backgroundColor = color_status[message.data.status][0];
document.getElementById(`status_${message.data.id}`).style.color = color_status[message.data.status][1];
document.getElementById(`status_${message.data.id}`).style.border = `1px solid ${color_status[message.data.status][1]}`;
document.getElementById(`status_${message.data.id}`).value = message.data.status;
document.getElementById(`description_${message.data.id}`).value = message.data.description;
document.getElementById(`date_${message.data.id}`).value = now.toISOString().slice(0, 16);
}
function getEntrances(house_id = house) {
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}/house/${house_id}/entrances`;
fetch(URL, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
})
.then(function (response) {
return response.json();
})
.then(function (data) {
listEntrances = data;
document.getElementById('list').innerHTML = "";
for (let i = 0; i < listEntrances.length; i++) {
const element = listEntrances[i];
let status = () => {
if ((element.history.name == "Групова" || element.history.name == USER.name) && element.working) return "open";
else if (USER.administrator.uuid || (USER.moderator.uuid && USER.moderator.can_manager_territory)) return "close";
return "style='display: none;'"
}
let statusIcon = () => {
if ((element.history.name == "Групова" || element.history.name == USER.name) && element.working) return '<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>'
else return '<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>'
}
document.getElementById('list').innerHTML += `
<details ${status()}>
<summary>
<p>${element.title}</p>
${statusIcon()}
</summary>
<div id="apartments_${element.id}" class="apartments_list">
</div>
</details>
`;
getApartment(element.id, element.entrance_number);
console.log(element);
}
})
}
function getApartment(entrance_id, entrance_number) {
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}/apartment/${entrance_id}`;
fetch(URL, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
})
.then(function (response) {
return response.json();
})
.then(function (data) {
listApartment[entrance_number] = data;
data.sort((a, b) => a.apartment_number - b.apartment_number);
data.sort((a, b) => a.updated_at - b.updated_at);
for (let i = 0; i < data.length; i++) {
const element = data[i];
let now = new Date(element.updated_at);
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
now = now.toISOString().slice(0, 16)
let disabled = () => {
if (USER.administrator.uuid || (USER.moderator.uuid && USER.moderator.can_manager_territory)) return '';
else if (element.status == 2) return "disabled";
}
document.getElementById(`apartments_${entrance_id}`).innerHTML += `
<div id="card_${element.id}" style="border: 1px solid ${color_status[element.status][1]};background: ${color_status[element.status][0]};color: ${color_status[element.status][1]};">
<div class="info">
<span>кв.${element.title}</span>
<select id="status_${element.id}" onchange="mess(${entrance_number}, ${element.id})" style="background-color: ${color_status[element.status][0]}; color: ${color_status[element.status][1]}; border: 1px solid ${color_status[element.status][1]};" ${disabled()}>
<option value="0" ${element.status == 0 ? "selected" : ""}></option>
<option value="1" ${element.status == 1 ? "selected" : ""}>Відмова</option>
<option value="2" ${element.status == 2 ? "selected" : ""}>Не заходити (Груба відмова)</option>
<option value="3" ${element.status == 3 ? "selected" : ""}>Нема домофона</option>
<option value="4" ${element.status == 4 ? "selected" : ""}>Повторна відвідина</option>
<option value="5" ${element.status == 5 ? "selected" : ""}>Немає вдома</option>
<option value="6" ${element.status == 6 ? "selected" : ""}>Свідки Єгови</option>
</select>
<input onchange="mess(${entrance_number}, ${element.id}, true)" name="${entrance_number}-${element.id}" class="hold-button" type="datetime-local" id="date_${element.id}" placeholder="Дата" value="${element.updated_at ? now : ""}" ${disabled()} style="max-width: 170px;">
</div>
<textarea onchange="mess(${entrance_number}, ${element.id}, true)" id="description_${element.id}" placeholder="Нотатки..." ${disabled()}}>${element.description ?? ""}</textarea>
</div>
`;
}
})
}
function sort(id, entrance_id) {
let child = document.getElementById(`card_${id}`);
document.getElementById(`apartments_${entrance_id}`).removeChild(child);
document.getElementById(`apartments_${entrance_id}`).append(child);
child.style.border = "1px solid var(--PrimaryColor)";
}
function getTimeInSeconds(time = Date.now()) {
// Если время больше 10 знаков (это значит, что время в миллисекундах)
if (time.toString().length < 10) {
// Округляем до секунд, убирая последние 3 цифры (миллисекунды)
time = Math.floor(time * 1000);
}
return time;
}

View File

@@ -0,0 +1,6 @@
.page-card {
width: calc(100% - 40px);
display: flex;
flex-direction: column;
margin: 20px 20px 0 20px;
}

View File

@@ -0,0 +1,192 @@
<div class="page-constructor">
<details id="details-info" open>
<summary>
<span>Крок 1.</span> Інформація про будинок, територію, точки на карті
</summary>
<form id="info-form">
<div id="details-info-type">
<div class="tabs">
<input
type="radio"
id="info-type-house"
value="house"
name="info-type"
checked
/>
<label class="tab" for="info-type-house">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path
d="M 14.5 6 A 1.50015 1.50015 0 0 0 13 7.5 L 13 25 L 7.5 25 A 1.50015 1.50015 0 0 0 6 26.5 L 6 42.5 A 1.50015 1.50015 0 1 0 9 42.5 L 9 28 L 14.253906 28 A 1.50015 1.50015 0 0 0 14.740234 28 L 19 28 L 19 42.5 A 1.50015 1.50015 0 1 0 22 42.5 L 22 26.746094 A 1.50015 1.50015 0 0 0 22 26.259766 L 22 22 L 33.253906 22 A 1.50015 1.50015 0 0 0 33.740234 22 L 39 22 L 39 42.5 A 1.50015 1.50015 0 1 0 42 42.5 L 42 20.5 A 1.50015 1.50015 0 0 0 40.5 19 L 35 19 L 35 7.5 A 1.50015 1.50015 0 0 0 33.5 6 L 14.5 6 z M 16 9 L 32 9 L 32 19 L 20.5 19 A 1.50015 1.50015 0 0 0 19 20.5 L 19 25 L 16 25 L 16 9 z M 20 12 C 19.448 12 19 12.448 19 13 L 19 15 C 19 15.552 19.448 16 20 16 L 22 16 C 22.552 16 23 15.552 23 15 L 23 13 C 23 12.448 22.552 12 22 12 L 20 12 z M 26 12 C 25.448 12 25 12.448 25 13 L 25 15 C 25 15.552 25.448 16 26 16 L 28 16 C 28.552 16 29 15.552 29 15 L 29 13 C 29 12.448 28.552 12 28 12 L 26 12 z M 26 25 C 25.448 25 25 25.448 25 26 L 25 28 C 25 28.552 25.448 29 26 29 L 28 29 C 28.552 29 29 28.552 29 28 L 29 26 C 29 25.448 28.552 25 28 25 L 26 25 z M 33 25 C 32.448 25 32 25.448 32 26 L 32 28 C 32 28.552 32.448 29 33 29 L 35 29 C 35.552 29 36 28.552 36 28 L 36 26 C 36 25.448 35.552 25 35 25 L 33 25 z M 13 31 C 12.448 31 12 31.448 12 32 L 12 34 C 12 34.552 12.448 35 13 35 L 15 35 C 15.552 35 16 34.552 16 34 L 16 32 C 16 31.448 15.552 31 15 31 L 13 31 z M 26 31 C 25.448 31 25 31.448 25 32 L 25 34 C 25 34.552 25.448 35 26 35 L 28 35 C 28.552 35 29 34.552 29 34 L 29 32 C 29 31.448 28.552 31 28 31 L 26 31 z M 33 31 C 32.448 31 32 31.448 32 32 L 32 34 C 32 34.552 32.448 35 33 35 L 35 35 C 35.552 35 36 34.552 36 34 L 36 32 C 36 31.448 35.552 31 35 31 L 33 31 z M 13 37 C 12.448 37 12 37.448 12 38 L 12 40 C 12 40.552 12.448 41 13 41 L 15 41 C 15.552 41 16 40.552 16 40 L 16 38 C 16 37.448 15.552 37 15 37 L 13 37 z M 26 37 C 25.448 37 25 37.448 25 38 L 25 40 C 25 40.552 25.448 41 26 41 L 28 41 C 28.552 41 29 40.552 29 40 L 29 38 C 29 37.448 28.552 37 28 37 L 26 37 z M 33 37 C 32.448 37 32 37.448 32 38 L 32 40 C 32 40.552 32.448 41 33 41 L 35 41 C 35.552 41 36 40.552 36 40 L 36 38 C 36 37.448 35.552 37 35 37 L 33 37 z"
/>
</svg>
<span>Багатоквартирний будинок</span>
</label>
<input
type="radio"
id="info-type-homestead"
value="homestead"
name="info-type"
/>
<label class="tab" for="info-type-homestead">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path
d="M 24 5.015625 C 22.851301 5.015625 21.70304 5.3892757 20.753906 6.1367188 A 1.50015 1.50015 0 0 0 20.751953 6.1367188 L 8.859375 15.509766 C 7.0558128 16.931133 6 19.102989 6 21.400391 L 6 39.488281 C 6 41.403236 7.5850452 42.988281 9.5 42.988281 L 38.5 42.988281 C 40.414955 42.988281 42 41.403236 42 39.488281 L 42 21.400391 C 42 19.102989 40.944187 16.931133 39.140625 15.509766 L 39 15.396484 L 39 7.5 A 1.50015 1.50015 0 0 0 37.5 6 L 32.5 6 A 1.50015 1.50015 0 0 0 31 7.5 L 31 9.09375 L 27.246094 6.1367188 C 26.29696 5.3892758 25.148699 5.015625 24 5.015625 z M 24 8.0078125 C 24.489801 8.0078125 24.979759 8.1705836 25.390625 8.4941406 L 31.572266 13.363281 A 1.50015 1.50015 0 0 0 34 12.185547 L 34 9 L 36 9 L 36 16.125 A 1.50015 1.50015 0 0 0 36.572266 17.302734 L 37.285156 17.865234 C 38.369594 18.719867 39 20.019792 39 21.400391 L 39 39.488281 C 39 39.783326 38.795045 39.988281 38.5 39.988281 L 9.5 39.988281 C 9.2049548 39.988281 9 39.783326 9 39.488281 L 9 21.400391 C 9 20.019792 9.6304058 18.719867 10.714844 17.865234 L 22.609375 8.4941406 C 23.020241 8.1705836 23.510199 8.0078125 24 8.0078125 z M 14.5 23.988281 A 1.50015 1.50015 0 0 0 13 25.488281 L 13 33.488281 A 1.50015 1.50015 0 0 0 14.5 34.988281 L 20.5 34.988281 A 1.50015 1.50015 0 0 0 22 33.488281 L 22 25.488281 A 1.50015 1.50015 0 0 0 20.5 23.988281 L 14.5 23.988281 z M 27.5 23.988281 A 1.50015 1.50015 0 0 0 26 25.488281 L 26 33.488281 A 1.50015 1.50015 0 0 0 27.5 34.988281 L 33.5 34.988281 A 1.50015 1.50015 0 0 0 35 33.488281 L 35 25.488281 A 1.50015 1.50015 0 0 0 33.5 23.988281 L 27.5 23.988281 z M 16 26.988281 L 19 26.988281 L 19 31.988281 L 16 31.988281 L 16 26.988281 z M 29 26.988281 L 32 26.988281 L 32 31.988281 L 29 31.988281 L 29 26.988281 z"
/>
</svg>
<span>Житловий район</span>
</label>
<input
type="radio"
id="info-type-points"
value="points"
name="info-type"
/>
<label class="tab" for="info-type-points">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path
d="M 24 4 C 16.285455 4 10 10.285455 10 18 C 10 21.46372 11.272608 24.643548 13.359375 27.085938 L 13.359375 27.087891 L 13.361328 27.087891 C 13.361328 27.087891 19.149094 33.866566 21.298828 35.917969 C 22.798087 37.348278 25.199464 37.347492 26.699219 35.917969 C 29.129083 33.600994 34.636721 27.090553 34.640625 27.085938 L 34.642578 27.082031 C 36.728766 24.639939 38 21.462159 38 18 C 38 10.285455 31.714545 4 24 4 z M 24 7 C 30.093455 7 35 11.906545 35 18 C 35 20.73228 34.005417 23.211194 32.359375 25.136719 L 32.359375 25.138672 L 32.357422 25.138672 C 32.357422 25.138672 26.632181 31.83589 24.628906 33.746094 C 24.258577 34.099392 23.73947 34.099392 23.369141 33.746094 C 21.715477 32.16807 15.643092 25.141834 15.638672 25.136719 L 15.636719 25.132812 C 13.99327 23.20762 13 20.730712 13 18 C 13 11.906545 17.906545 7 24 7 z M 24 12 C 22.125 12 20.528815 12.757133 19.503906 13.910156 C 18.478997 15.063179 18 16.541667 18 18 C 18 19.458333 18.478997 20.936821 19.503906 22.089844 C 20.528815 23.242867 22.125 24 24 24 C 25.875 24 27.471185 23.242867 28.496094 22.089844 C 29.521003 20.936821 30 19.458333 30 18 C 30 16.541667 29.521003 15.063179 28.496094 13.910156 C 27.471185 12.757133 25.875 12 24 12 z M 24 15 C 25.124999 15 25.778816 15.367867 26.253906 15.902344 C 26.728997 16.436821 27 17.208333 27 18 C 27 18.791667 26.728997 19.563179 26.253906 20.097656 C 25.778816 20.632133 25.124999 21 24 21 C 22.875001 21 22.221184 20.632133 21.746094 20.097656 C 21.271003 19.563179 21 18.791667 21 18 C 21 17.208333 21.271003 16.436821 21.746094 15.902344 C 22.221184 15.367867 22.875001 15 24 15 z M 12.771484 29.441406 C 8.2264844 30.754406 5 32.953 5 36 C 5 41.252 14.558 44 24 44 C 33.442 44 43 41.252 43 36 C 43 32.954 39.775422 30.757359 35.232422 29.443359 C 34.654422 30.099359 33.863187 30.993844 32.992188 31.964844 C 37.418188 33.005844 40 34.691 40 36 C 40 38.039 33.767 41 24 41 C 14.233 41 8 38.039 8 36 C 8 34.69 10.586531 33.001938 15.019531 31.960938 C 14.152531 30.995938 13.355484 30.100406 12.771484 29.441406 z"
/>
</svg>
<span>Точки на карті</span>
</label>
<span class="glider"></span>
</div>
</div>
<div id="details-info-address" class="details-info-input">
<label for="info-address-title">Назва вулиці</label>
<input
type="text"
id="info-address-title"
name="address"
required
value=""
/>
</div>
<div id="details-info-number" class="details-info-input">
<label for="info-number-title">Номер будинку</label>
<input
type="text"
id="info-number-title"
name="number"
required
value=""
/>
</div>
<div id="details-info-settlement" class="details-info-input">
<label for="info-settlement-title">Місто</label>
<input
type="text"
id="info-settlement-title"
name="settlement"
required
value="Тернопіль"
/>
</div>
<div id="details-info-group" class="details-info-input">
<label for="info-group-title">Теократична група</label>
<input
type="number"
id="info-group-title"
name="group"
required
value=""
/>
</div>
<div id="details-info-osm" class="details-info-input">
<label for="info-settlement-title">OSM iD</label>
<div>
<input
type="text"
id="info-osm-title"
name="osm"
placeholder="123, 345, 678"
required
value=""
/>
<a href="https://www.openstreetmap.org/#map=19/49.561725/25.604458" target="_blank" title="Де знайти OSM iD ?"
><svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 48 48"
width="100px"
height="100px"
>
<path
d="M 24 4 C 12.972066 4 4 12.972074 4 24 C 4 35.027926 12.972066 44 24 44 C 35.027934 44 44 35.027926 44 24 C 44 12.972074 35.027934 4 24 4 z M 24 7 C 33.406615 7 41 14.593391 41 24 C 41 33.406609 33.406615 41 24 41 C 14.593385 41 7 33.406609 7 24 C 7 14.593391 14.593385 7 24 7 z M 24 14 A 2 2 0 0 0 24 18 A 2 2 0 0 0 24 14 z M 23.976562 20.978516 A 1.50015 1.50015 0 0 0 22.5 22.5 L 22.5 33.5 A 1.50015 1.50015 0 1 0 25.5 33.5 L 25.5 22.5 A 1.50015 1.50015 0 0 0 23.976562 20.978516 z"
/></svg
></a>
</div>
</div>
<button id="info-form-button" type="submit">Далі</button>
</form>
</details>
<details id="details-map" disabled>
<summary id="details-map-title">
<span>Крок 2.</span> Створення підїздів
</summary>
<form id="map-form">
<div class="editor-buttons" id="details-map-buttons-entranse">
<button type="button" onclick="Constructor.editor.drawEntranse()">
Створити новий під'їзд
</button>
<div>
<button type="button" id="ruler" onclick="Constructor.editor.drawRectangle()">
Лінійка
</button>
<button
type="button"
id="ruler_divide"
onclick="Constructor.editor.ruler()"
style="display: none"
>
Поділити
</button>
</div>
</div>
<div
class="editor-buttons"
id="details-map-buttons-homestead"
style="display: none"
>
<div>
<button type="button" id="ruler" onclick="Constructor.editor.drawRectangle()">
Лінійка
</button>
<button
type="button"
id="ruler_divide"
onclick="Constructor.editor.ruler()"
style="display: none"
>
Поділити
</button>
</div>
</div>
<div id="list-entranse" style="display: none"></div>
<div id="list-homestead" style="display: none"></div>
<div class="block-map">
<div id="map"></div>
</div>
<button id="map-form-button" type="submit">Далі</button>
</form>
</details>
<details id="details-area" disabled>
<summary><span>Крок 3.</span> Конструктор квартир <input type="number" value="1" id="next-apartment-title" onchange="Constructor.apartments.editNum(this)" title="Авто-номер наступної квартири"></summary>
<form id="area-form">
<div id="list-area"></div>
<button id="area-form-button" type="submit">Зберегти</button>
</form>
</details>
</div>

View File

@@ -0,0 +1,731 @@
let map, houseGroup, entransePolygonsGroup, entranseNumPolygonsGroup, splitPolygonsGroup, RectangleGroup;
let numApartments = 1;
let mode = '';
const Constructor = {
info: {
type: null,
group_id: null,
title: null,
number: null,
points: [],
points_number: [],
point_icons: [],
geo: [],
osm_id: [],
settlement: [],
description: null,
entrance: [],
apartments: {},
zoom: null
},
init: async () => {
let html = await fetch('/lib/pages/constructor/index.html').then((response) => response.text());
app.innerHTML = html;
map = "";
map = "";
houseGroup = "";
entransePolygonsGroup = "";
entranseNumPolygonsGroup = "";
splitPolygonsGroup = "";
RectangleGroup = "";
numApartments = 1;
Constructor.apartments.init();
document.querySelectorAll('input[name="info-type"]').forEach(radio => {
radio.addEventListener('change', event => {
console.log(`Выбран: ${event.target.value}`);
Constructor.info.type = event.target.value;
let detailsInfo_number = document.getElementById('details-info-number');
let detailsInfo_osm = document.getElementById('details-info-osm');
let detailsInfo_number_title = document.getElementById('info-number-title');
let detailsInfo_osm_title = document.getElementById('info-osm-title');
let detailsMap_title = document.getElementById('details-map-title');
let detailsMap_buttons_entranse = document.getElementById('details-map-buttons-entranse');
let detailsMap_buttons_homestead = document.getElementById('details-map-buttons-homestead');
let detailsMap_button = document.getElementById('map-form-button');
let detailsArea = document.getElementById('details-area');
switch (event.target.value) {
case 'points':
detailsInfo_number.style.display = "none";
detailsInfo_osm.style.display = "none";
detailsInfo_number_title.removeAttribute("required");
detailsInfo_osm_title.removeAttribute("required");
detailsMap_title.innerHTML = "<span>Крок 2.</span> Створення точок на карті"
detailsMap_buttons_entranse.style.display = "none";
detailsMap_buttons_homestead.style.display = "none";
detailsMap_button.innerText = "Зберегти";
detailsArea.style.display = "none";
break;
case 'homestead':
detailsInfo_number.style.display = "";
detailsInfo_osm.style.display = "";
detailsInfo_number_title.setAttribute("required", "");
detailsInfo_osm_title.setAttribute("required", "");
detailsMap_title.innerHTML = "<span>Крок 2.</span> Створення житлових територій"
detailsMap_buttons_entranse.style.display = "none";
detailsMap_buttons_homestead.style.display = "";
detailsMap_button.innerText = "Зберегти";
detailsArea.style.display = "none";
break;
default:
detailsInfo_number.style.display = "";
detailsInfo_osm.style.display = "";
detailsInfo_number_title.setAttribute("required", "");
detailsInfo_osm_title.setAttribute("required", "");
detailsMap_title.innerHTML = "<span>Крок 2.</span> Створення підʼїздів"
detailsMap_buttons_entranse.style.display = "";
detailsMap_buttons_homestead.style.display = "none";
detailsMap_button.innerText = "Далі";
detailsArea.style.display = "";
break;
}
});
});
document.getElementById("info-form").addEventListener("submit", function (event) {
event.preventDefault();
let details_map = document.getElementById("details-map");
details_map.removeAttribute("disabled");
details_map.open = true;
const infoForm = document.getElementById("info-form");
let osm = () => {
const a = document.getElementById("info-osm-title").value;
const b = a.replace(/\s+/g, "").split(',')
return b;
}
Constructor.info.type = infoForm.querySelector('input[name="info-type"]:checked').value;
Constructor.info.title = document.getElementById("info-address-title").value;
Constructor.info.number = document.getElementById("info-number-title").value;
Constructor.info.settlement = document.getElementById("info-settlement-title").value;
Constructor.info.group_id = Number(document.getElementById("info-group-title").value);
Constructor.info.osm_id = osm();
Constructor.osm.init();
Constructor.osm.setMap();
})
document.getElementById("map-form").addEventListener("submit", async function (event) {
event.preventDefault();
let details_area = document.getElementById("details-area");
details_area.removeAttribute("disabled");
details_area.open = true;
switch (Constructor.info.type) {
case 'points':
break;
case 'homestead':
await Constructor.api.setPack();
break;
default:
let details_area = document.getElementById("details-area");
details_area.removeAttribute("disabled");
details_area.open = true;
break;
}
})
document.getElementById("area-form").addEventListener("submit", async function (event) {
event.preventDefault();
await Constructor.api.setPack();
})
},
apartments: {
init: () => {
let listArea = document.getElementById('list-area');
listArea.innerHTML = ``;
for (const key in Constructor.info.apartments) {
const element = Constructor.info.apartments[key];
const pos = Constructor.info.entrance.map(e => e.editor_id).indexOf(key);
console.log(element);
let listArea = document.getElementById('list-area');
listArea.innerHTML += `
<div class="block-area" id="block-area-${key}">
<h3>${Constructor.info.entrance[pos].title}</h3>
<button class="addFloors" title="Додати поверх" type="button" onclick="Constructor.apartments.addFloors('${key}')"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg></button>
<div id="area-${key}"></div>
</div>
`;
element.sort((a, b) => a.floors_number - b.floors_number);
let uniqueFloors = [...new Set(element.map(obj => obj.floors_number))];
for (let i = 0; i < uniqueFloors.length; i++) {
let num = uniqueFloors[i];
let areaBlock = document.getElementById(`area-${key}`);
let div = document.createElement('div');
div.className = "block-apartments-floors";
div.id = `floors-${key}-${num}`
div.innerHTML = `
<button class="addApartment" id="buttonApartment-${key}-${num}" title="Додати квартиру" type="button" onclick="Constructor.apartments.addApartment('${key}', '${num}')"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg></button>
`;
areaBlock.prepend(div);
}
element.sort((a, b) => b.title - a.title);
for (let i = 0; i < element.length; i++) {
const apartment = element[i];
let num = apartment.floors_number;
let floorsBlock = document.getElementById(`floors-${key}-${apartment.floors_number}`);
let div = document.createElement('div');
div.className = "block-apartments-number";
div.id = `block-apartments-${key}-${apartment.editor_id}`
div.innerHTML = `
<input type="text" value="${apartment.title}" id="apartament-${key}-${apartment.editor_id}" onchange="Constructor.apartments.editApartment('${key}','${apartment.editor_id}')">
<button type="button" onclick="Constructor.apartments.deleteApartment('${key}','${apartment.editor_id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
`;
floorsBlock.prepend(div);
}
element.sort((a, b) => b.floors_number - a.floors_number);
}
},
editNum: (element) => {
numApartments = Number(element.value);
},
addFloors: (area) => {
let areaBlock = document.getElementById(`area-${area}`);
let uniqueFloors = [...new Set(Constructor.info.apartments[area].map(obj => obj.floors_number))];
let new_floors = uniqueFloors.length + 1;
let new_id = makeid(5);
let div = document.createElement('div');
div.className = "block-apartments-floors";
div.id = `floors-${area}-${new_floors}`
div.innerHTML = `
<h2>Поверх ${new_floors}</h2>
<div class="block-apartments-number" id="block-apartments-${area}-${new_id}">
<input type="text" value="${numApartments}" id="apartament-${area}-${new_id}" onchange="Constructor.apartments.editApartment('${area}', '${new_id}')">
<button type="button" onclick="Constructor.apartments.deleteApartment('${area}', '${new_id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
</div>
<button class="addApartment" id="buttonApartment-${area}-${new_floors}" title="Додати квартиру" type="button" onclick="Constructor.apartments.addApartment('${area}', '${new_floors}')"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg></button>
`
areaBlock.prepend(div);
Constructor.info.apartments[area].push({
editor_id: new_id,
entrance_id: null,
apartment_number: Constructor.info.apartments[area].length,
title: numApartments,
floors_number: new_floors
});
numApartments++;
let next_apartment_title = document.getElementById('next-apartment-title');
next_apartment_title.value = numApartments;
},
addApartment: (area, floors) => {
let new_id = makeid(5);
Constructor.info.apartments[area].push({
editor_id: new_id,
entrance_id: null,
apartment_number: Constructor.info.apartments[area].length,
title: numApartments,
floors_number: Number(floors)
})
let floorsBlock = document.getElementById(`floors-${area}-${floors}`);
document.getElementById(`buttonApartment-${area}-${floors}`).remove();
floorsBlock.innerHTML += `
<div class="block-apartments-number" id="block-apartments-${area}-${new_id}">
<input type="text" value="${numApartments}" id="apartament-${area}-${new_id}" onchange="Constructor.apartments.editApartment('${area}', '${new_id}')">
<button type="button" onclick="Constructor.apartments.deleteApartment('${area}', '${new_id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
</div>
`;
floorsBlock.innerHTML += `<button class="addApartment" id="buttonApartment-${area}-${floors}" title="Додати квартиру" type="button" onclick="Constructor.apartments.addApartment('${area}', '${floors}')"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg></button>`
numApartments++;
let next_apartment_title = document.getElementById('next-apartment-title');
next_apartment_title.value = numApartments;
},
editApartment: (area, apartament) => {
let input = document.getElementById(`apartament-${area}-${apartament}`);
input.setAttribute("value", input.value);
const pos = Constructor.info.apartments[area].map(e => e.editor_id).indexOf(apartament);
info.apartments[area][pos].title = input.value;
},
deleteApartment: (area, apartament) => {
document.getElementById(`block-apartments-${area}-${apartament}`).remove();
const pos = Constructor.info.apartments[area].map(e => e.editor_id).indexOf(apartament);
Constructor.info.apartments[area].splice(pos, 1);
numApartments--;
let next_apartment_title = document.getElementById('next-apartment-title');
next_apartment_title.value = numApartments;
}
},
osm: {
init: () => {
let center = { lat: 49.5629016, lng: 25.6145625 };
let zoom = 19;
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', {
maxZoom: 19,
minZoom: 15
});
let mytile = L.tileLayer('https://tm.rozenrod.com/webp/{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(),
center,
zoom,
layers: [
googleHybrid,
osm,
mytile,
houseGroup,
entransePolygonsGroup,
entranseNumPolygonsGroup,
splitPolygonsGroup,
RectangleGroup,
],
zoomControl: false
});
let baseMaps = {
"Google Hybrid": googleHybrid,
"OpenStreetMap": osm,
"Territory Map": mytile
};
let overlayMaps = {
"Будинки": houseGroup,
"Під'їзди": entransePolygonsGroup,
"Номера під'їздів": entranseNumPolygonsGroup,
"Слой редактирования": splitPolygonsGroup,
"Слой линейки": RectangleGroup,
};
L.control.layers(baseMaps, overlayMaps, { 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
})
}
Constructor.editor.init();
},
getOSM: async (wayId) => {
const overpassUrl = `https://overpass-api.de/api/interpreter?data=[out:json];way(${wayId});(._;>;);out;`;
return await fetch(overpassUrl)
.then(response => response.json())
.then(data => {
const nodes = new Map();
data.elements.forEach(el => {
if (el.type === "node") {
nodes.set(el.id, { lat: el.lat, lng: el.lon });
}
});
const way = data.elements.find(el => el.type === "way");
if (way) {
const coordinates = way.nodes.map(nodeId => nodes.get(nodeId));
console.log("Координаты точек:", coordinates);
return [coordinates];
} else {
console.log("Way не найден!");
}
})
.catch(error => console.error("Ошибка запроса:", error));
},
setMap: async () => {
houseGroup.clearLayers();
if (!Constructor.info.osm_id) {
let osm = () => {
const a = document.getElementById("info-osm-title").value;
const b = a.replace(/\s+/g, "").split(',')
return b;
}
Constructor.info.osm_id = osm();
}
for (let i = 0; i < Constructor.info.osm_id.length; i++) {
const element = await Constructor.osm.getOSM(Constructor.info.osm_id[i]);
let coords = [];
element[0].forEach((feature) => coords.push([feature.lat, feature.lng]));
let centerPoint = turf.centerOfMass(turf.polygon([coords]));
if (Constructor.info.type == "homestead") {
map.setView([centerPoint.geometry.coordinates[0], centerPoint.geometry.coordinates[1]], 17);
L.polygon(element, {
color: colorGroup(Constructor.info.group_id),
radius: 500,
fillOpacity: 0.3,
dashArray: '20, 15',
dashOffset: '20',
}).addTo(houseGroup);
} else if (Constructor.info.type == "house") {
map.setView([centerPoint.geometry.coordinates[0], centerPoint.geometry.coordinates[1]], 18);
Constructor.info.points.push(element);
Constructor.info.points_number.push(element[0][0]);
L.polygon(element, {
color: "#585858",
fillColor: colorGroup(Constructor.info.group_id),
fillOpacity: 0.8,
tm_id: `house_${i}`
}).addTo(houseGroup);
}
}
console.log(Constructor.info);
}
},
api: {
setPack: async () => {
let area_form_button = document.getElementById('area-form-button');
area_form_button.innerText = "Зачекайте...";
Constructor.info.geo = await map.getCenter();
Constructor.info.zoom = await map.getZoom();
console.log(Constructor.info);
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}constructor`;
await fetch(URL, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
body: JSON.stringify(Constructor.info)
})
.then(response => {
if (response.status == 200) {
console.log({ 'setPack': 'ok' });
area_form_button.innerText = "Запис додано";
return response.json()
} else {
console.log('err');
area_form_button.innerText = "Помилка запису";
return
}
})
.then(data => {
console.log(data);
Router.navigate((`/territory/manager/${Constructor.info.type}/${data.id}`).replace(window.location.origin, ''));
setTimeout(() => {
area_form_button.innerText = "Зберегти";
}, 3000);
})
.catch(err => {
console.log(err);
area_form_button.innerText = "Помилка запису";
})
}
},
editor: {
init: () => {
map.on('pm:create', function (event) {
let layer = event.layer;
let newCoords = layer.getLatLngs()[0].map(function (coords) {
return [coords.lng, coords.lat];
});
newCoords.push(newCoords[0]);
let turfNew = turf.polygon([newCoords]);
if (mode == 'entranse') {
console.log(L.PM.Utils.findLayers(houseGroup));
for (let i = 0; i < L.PM.Utils.findLayers(houseGroup).length; i++) {
const polygon = L.PM.Utils.findLayers(houseGroup)[i]._latlngs;
let polygonCoords = polygon[0].map(function (coords) {
return [coords.lng, coords.lat];
});
polygonCoords.push(polygonCoords[0]); // Замикаємо полігон
let turfPolygon = turf.polygon([polygonCoords]);
// Пошук точки перехрестя
let intersections = turf.intersect(turfNew, turfPolygon);
if (intersections) {
let points = [];
let coords = [];
intersections.geometry.coordinates[0].forEach(function (feature) {
coords.push([feature[1], feature[0]])
points.push({ lat: feature[1], lng: feature[0] })
});
let centerPoint = turf.centerOfMass(turf.polygon([coords]));
let points_number = { lat: centerPoint.geometry.coordinates[0], lng: centerPoint.geometry.coordinates[1] }
let newID = makeid(6);
Constructor.info.entrance.push({
editor_id: newID,
house_id: null,
entrance_number: Constructor.info.entrance.length,
title: `Під'їзд ${Constructor.info.entrance.length + 1}`,
points: points,
points_number: points_number,
floors_quantity: null,
apartments_quantity: null,
created_at: null
})
Constructor.info.apartments[newID] = [];
Constructor.apartments.init();
let listEntranse = document.getElementById('list-entranse');
listEntranse.style.display = "";
listEntranse.innerHTML += `
<div class="house" style="align-items: center;" id="Entranse_${newID}">
<input type="number" value="${Constructor.info.entrance.length}" id="Entranse_input_${newID}" onchange="Constructor.editor.editEntranse('${newID}')">
<button type="button" onclick="Constructor.editor.dellEntranse('${newID}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
</div>
`;
L.polygon(coords, { color: 'red' }).addTo(entransePolygonsGroup);
let myIcon = L.divIcon({ className: 'entranse_number', html: `<div class="markerEntranse">${Constructor.info.entrance.length}</div>` });
L.marker(centerPoint.geometry.coordinates, { icon: myIcon }).addTo(entranseNumPolygonsGroup);
}
}
}
});
},
drawEntranse: () => {
mode = 'entranse';
map.pm.setGlobalOptions({
layerGroup: splitPolygonsGroup
})
map.pm.setPathOptions({
color: '#f2bd53',
fillColor: '#f2bd53',
fillOpacity: 0.5,
radius: 500
});
if (map.pm.globalDragModeEnabled()) {
map.pm.disableDraw();
} else {
map.pm.enableDraw("Polygon", {
snappable: true,
snapDistance: 20,
tooltips: false,
templineStyle: { color: '#f2bd53' },
hintlineStyle: { color: '#f2bd53', dashArray: [5, 5] }
});
}
},
drawRectangle: () => {
mode = 'rectangle';
document.getElementById('ruler_divide').style.display = 'block'
document.getElementById('ruler').style.width = "calc(50% - 5px)"
document.getElementById('ruler_divide').style.width = "calc(50% - 5px)"
document.getElementById('ruler').innerHTML = 'Лінійка'
map.pm.toggleControls()
RectangleGroup.clearLayers();
RectangleGroup.addTo(map);
map.pm.setGlobalOptions({
layerGroup: RectangleGroup
})
map.pm.setPathOptions({
color: '#b645ef',
fillColor: '#b645ef',
fillOpacity: 0.5,
radius: 500
});
if (map.pm.globalDragModeEnabled()) {
map.pm.disableDraw();
} else {
map.pm.enableDraw("Rectangle", {
snappable: true,
snapDistance: 20,
tooltips: false,
templineStyle: { color: '#b645ef' },
hintlineStyle: { color: '#b645ef', dashArray: [5, 5] }
});
}
},
ruler: (n) => {
n = prompt('На сколько поделить линейку ?', 2);
const polygon = L.PM.Utils.findLayers(RectangleGroup)[0]._latlngs;
let newCoords = polygon[0].map(function (coords) {
return [coords.lng, coords.lat];
});
newCoords.push(newCoords[0]);
let turfNew = turf.polygon([newCoords]);
console.log(turfNew);
var line = turf.polygonToLine(turfNew);
console.log(line.geometry.coordinates);
coords = line.geometry.coordinates;
for (let i = 1; i < n; i++) {
let a1 = (((coords[2][1] - coords[1][1]) / n) * i + coords[1][1])
let b1 = (((coords[2][0] - coords[1][0]) / n) * i + coords[1][0])
let a2 = (((coords[3][1] - coords[0][1]) / n) * i + coords[0][1])
let b2 = (((coords[3][0] - coords[0][0]) / n) * i + coords[0][0])
let c1 = (((coords[1][1] - coords[0][1]) / n) * i + coords[0][1])
let d1 = (((coords[1][0] - coords[0][0]) / n) * i + coords[0][0])
let c2 = (((coords[2][1] - coords[3][1]) / n) * i + coords[3][1])
let d2 = (((coords[2][0] - coords[3][0]) / n) * i + coords[3][0])
L.circleMarker([a1, b1], { radius: 2, color: 'red' }).addTo(RectangleGroup);
L.circleMarker([a2, b2], { radius: 2, color: 'red' }).addTo(RectangleGroup);
L.circleMarker([c1, d1], { radius: 2, color: 'red' }).addTo(RectangleGroup);
L.circleMarker([c2, d2], { radius: 2, color: 'red' }).addTo(RectangleGroup);
}
coords.forEach(function (feature) {
L.circleMarker([feature[1], feature[0]], { radius: 2, color: 'red' }).addTo(RectangleGroup);
});
},
dellEntranse: (id) => {
delete Constructor.info.apartments[id];
const pos = Constructor.info.entrance.map(e => e.editor_id).indexOf(id);
console.log(pos);
Constructor.info.entrance.splice(pos, 1);
console.log(id);
document.getElementById(`Entranse_${id}`).remove();
let Entranse = Object.keys(entransePolygonsGroup._layers);
let numsEntranse = Object.keys(entranseNumPolygonsGroup._layers);
console.log(Entranse, Entranse[pos]);
console.log(numsEntranse, numsEntranse[pos]);
entransePolygonsGroup.removeLayer(entransePolygonsGroup._layers[Entranse[pos]]);
entranseNumPolygonsGroup.removeLayer(entranseNumPolygonsGroup._layers[numsEntranse[pos]]);
Constructor.apartments.init();
},
editEntranse: (id) => {
const input = document.getElementById(`Entranse_input_${id}`);
const pos = Constructor.info.entrance.map(e => e.editor_id).indexOf(id);
Constructor.info.entrance[pos].entrance_number = Number(input.value) - 1;
Constructor.info.entrance[pos].title = `Під'їзд ${input.value}`;
let numsEntranse = Object.keys(entranseNumPolygonsGroup._layers);
let newIcon = L.divIcon({ className: 'entranse_number', html: `<div class="markerEntranse">${input.value}</div>` });
entranseNumPolygonsGroup._layers[numsEntranse[pos]].setIcon(newIcon);
input.setAttribute("value", input.value);
Constructor.apartments.init();
}
}
}

View File

@@ -0,0 +1,494 @@
.page-constructor {
width: calc(100% - 40px);
display: flex;
flex-direction: column;
align-items: center;
margin: 20px 20px 0 20px;
}
.page-constructor form>button {
border-radius: 6px;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
width: 100%;
height: 40px;
font-size: 14px;
font-weight: 400;
margin: 20px 0;
text-transform: uppercase;
}
.page-constructor details {
border-radius: 10px;
width: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
margin-bottom: 20px;
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
}
.page-constructor>details[disabled] summary,
.page-constructor>details.disabled summary {
pointer-events: none;
user-select: none;
}
.page-constructor>details summary::-webkit-details-marker,
.page-constructor>details summary::marker {
display: none;
content: "";
}
.page-constructor summary {
width: calc(100% - 40px);
cursor: pointer;
color: var(--ColorThemes3);
border-radius: var(--border-radius);
font-size: 16px;
font-weight: 300;
padding: 20px;
position: relative;
}
.page-constructor summary span {
font-weight: 500;
}
.page-constructor summary input {
font-weight: 500;
position: absolute;
right: 0;
top: 0;
padding: 10px;
margin: 13px;
font-size: 12px;
background: var(--ColorThemes3);
color: var(--ColorThemes0);
border-radius: 6px;
width: 40px;
}
.page-constructor #info-form,
.page-constructor #map-form,
.page-constructor #area-form {
padding: 0 20px;
}
#details-info-type {
display: flex;
align-items: center;
justify-content: center;
}
#details-info-type>.tabs {
display: flex;
position: relative;
background-color: var(--ColorThemes0);
padding: 4px;
border-radius: 6px;
width: calc(100% - 8px);
z-index: 2;
}
#details-info-type>.tabs>input[type="radio"] {
display: none;
}
#details-info-type>.tabs>.tab {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
width: calc(100% / 3);
cursor: pointer;
padding: 0 15px;
transition: 0.15s ease-in;
color: var(--ColorThemes3);
fill: var(--ColorThemes3);
flex-direction: row;
z-index: 2;
}
#details-info-type>.tabs>.tab>svg {
width: 20px;
height: 20px;
}
#details-info-type>.tabs>.tab>span {
margin-left: 6px;
font-size: 12px;
font-weight: 400;
}
#details-info-type>.tabs>input[type="radio"]:checked+label {
color: var(--PrimaryColorText);
fill: var(--PrimaryColorText);
}
#details-info-type>.tabs>.glider {
position: absolute;
display: flex;
height: 40px;
width: calc((100% - 8px) / 3);
background-color: var(--PrimaryColor);
z-index: 1;
border-radius: 4px;
transition: 0.25s ease-out;
}
@media (min-width: 601px) {
#details-info-type>.tabs>input[id="info-type-house"]:checked~.glider {
transform: translateX(0);
}
#details-info-type>.tabs>input[id="info-type-homestead"]:checked~.glider {
transform: translateX(100%);
}
#details-info-type>.tabs>input[id="info-type-points"]:checked~.glider {
transform: translateX(200%);
}
}
@media (max-width: 600px) {
#details-info-type>.tabs {
flex-direction: column;
}
#details-info-type>.tabs>.tab {
width: calc(100% - 8px);
padding: 0 4px;
}
#details-info-type>.tabs>.glider {
width: calc(100% - 8px);
}
#details-info-type>.tabs>input[id="info-type-house"]:checked~.glider {
transform: translateY(0);
}
#details-info-type>.tabs>input[id="info-type-homestead"]:checked~.glider {
transform: translateY(100%);
}
#details-info-type>.tabs>input[id="info-type-points"]:checked~.glider {
transform: translateY(200%);
}
}
.page-constructor .details-info-input {
width: 100%;
display: flex;
margin: 20px 0;
align-items: flex-start;
flex-direction: column;
}
.page-constructor .details-info-input label {
display: flex;
justify-content: center;
flex-direction: column;
font-size: 12px;
font-weight: 500;
margin-bottom: 5px;
}
.page-constructor .details-info-input input {
width: calc(100% - 10px);
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
background: var(--ColorThemes0);
color: var(--ColorThemes3);
}
.page-constructor #details-info-osm div {
display: flex;
align-items: center;
width: 100%;
}
.page-constructor #details-info-osm input {
width: calc(100% - 10px);
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
}
.page-constructor .details-info-input a {
height: 26px;
width: 26px;
margin-left: 10px;
}
.page-constructor .details-info-input a>svg {
height: 26px;
width: 26px;
fill: var(--ColorThemes3)
}
.page-constructor #list-area {
margin-top: 15px;
display: flex;
overflow: auto;
}
.page-constructor #list-area h3 {
text-align: center;
font-size: 16px;
font-weight: 400;
margin: 10px;
padding: 7px;
color: var(--ColorThemes0);
background: var(--ColorThemes3);
border-radius: 4px;
}
.block-area {
min-height: 200px;
border: 1px solid var(--ColorThemes3);
border-style: dashed;
border-radius: 6px;
margin: 0 10px 10px 0;
}
.addFloors,
.addApartment {
display: flex;
position: relative;
width: 34px;
height: 34px;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
margin: 25px;
border-radius: 50%;
align-items: center;
justify-content: center;
font-size: 30px;
cursor: pointer;
}
.addFloors>svg,
.addApartment>svg {
width: 20px;
height: 20px;
fill: var(--PrimaryColorText);
transform: rotate(45deg);
}
.block-apartments-floors {
position: relative;
display: flex;
width: calc(100% - 22px);
border: 1px solid var(--ColorThemes3);
margin: 10px;
border-radius: 4px;
}
.block-apartments-floors h2 {
position: absolute;
width: 65px;
right: -1px;
top: -1px;
margin: 0;
background: var(--ColorThemes3);
color: var(--ColorThemes2);
border-radius: 0 4px 0 4px;
font-size: 12px;
padding: 2px 4px;
text-align: center;
}
.block-apartments-number {
display: flex;
position: relative;
width: 60px;
height: 60px;
background: var(--ColorThemes1);
border: 2px solid var(--PrimaryColor);
margin: 10px;
border-radius: 4px;
align-items: center;
justify-content: center;
}
.block-apartments-number input {
width: 50px;
height: 50px;
font-size: 16px;
font-weight: 400;
text-align: center;
color: var(--ColorThemes3);
background: 0;
}
.block-apartments-number button {
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
border-radius: 0 4px 0 4px;
background: var(--PrimaryColor);
font-size: 16px;
margin: -2px;
border: 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.block-apartments-number button>svg {
width: 16px;
height: 16px;
fill: var(--PrimaryColorText);
}
.block-map {
width: 100%;
height: 500px;
border-radius: 6px;
overflow: hidden;
}
#map {
width: 100%;
height: 100%;
}
.entranse_number {
left: -10px !important;
top: -10px !important;
}
.markerEntranse {
background: hsl(0deg 0% 52.12% / 90%);
font-size: 24px;
border: 2px solid #676767;
border-radius: 2px;
display: flex !important;
justify-content: center;
align-items: center;
color: #fff;
min-width: 30px;
min-height: 30px;
height: 30px;
width: 30px;
}
.editor-buttons {
margin-top: 15px;
}
.editor-buttons>button {
width: 100%;
min-height: 30px;
margin: 5px 0;
border: 0;
color: var(--PrimaryColorText);
display: flex;
align-items: center;
justify-content: center;
background: var(--PrimaryColor);
text-transform: uppercase;
cursor: pointer;
border-radius: 6px;
font-weight: 400;
font-size: 12px;
}
.editor-buttons>div {
display: flex;
width: 100%;
margin: 10px 0;
height: 30px;
justify-content: space-between;
}
.editor-buttons>div>button {
background: var(--ColorThemes3);
color: var(--ColorThemes1);
width: 100%;
margin: 0;
border-radius: 6px;
text-transform: uppercase;
font-weight: 400;
font-size: 12px;
}
.page-constructor #list-entranse,
.page-constructor #list-homestead {
width: 100%;
min-height: 86px;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
overflow-x: initial;
overflow-y: auto;
border: 1px solid var(--ColorThemes3);
background: 0;
border-radius: 6px;
border-style: dashed;
margin: 10px 0;
}
.page-constructor #list-entranse>.house,
.page-constructor #list-homestead>.house {
display: flex;
position: relative;
width: 60px;
height: 60px;
background: var(--ColorThemes1);
border: 2px solid var(--PrimaryColor);
margin: 10px;
border-radius: 4px;
align-items: center;
justify-content: center;
}
.page-constructor #list-entranse>.house>input,
.page-constructor #list-homestead>.house>input {
width: 50px;
height: 50px;
font-size: 16px;
font-weight: 400;
text-align: center;
color: var(--ColorThemes3);
background: 0;
}
.page-constructor #list-entranse>.house>button,
.page-constructor #list-homestead>.house>button {
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
border-radius: 0 4px 0 4px;
background: var(--PrimaryColor);
font-size: 16px;
margin: -2px;
border: 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.page-constructor #list-entranse>.house>button>svg,
.page-constructor #list-homestead>.house>button>svg {
width: 16px;
height: 16px;
fill: var(--PrimaryColorText);
}

View File

@@ -0,0 +1,139 @@
<div class="page-editor">
<details id="details-info" open>
<summary>
<span>Крок 1.</span> Інформація про будинок, територію, точки на карті
</summary>
<form id="info-form">
<div id="details-info-address" class="details-info-input">
<label for="info-address-title">Назва вулиці</label>
<input
type="text"
id="info-address-title"
name="address"
required
value=""
/>
</div>
<div id="details-info-number" class="details-info-input">
<label for="info-number-title">Номер будинку</label>
<input
type="text"
id="info-number-title"
name="number"
required
value=""
/>
</div>
<div id="details-info-settlement" class="details-info-input">
<label for="info-settlement-title">Місто</label>
<input
type="text"
id="info-settlement-title"
name="settlement"
required
value=""
/>
</div>
<div id="details-info-group" class="details-info-input">
<label for="info-group-title">Теократична група</label>
<input
type="number"
id="info-group-title"
name="group"
required
value=""
/>
</div>
<div id="details-info-osm" class="details-info-input">
<label for="info-settlement-title">OSM iD</label>
<div>
<input
type="text"
id="info-osm-title"
name="osm"
placeholder="123, 345, 678"
required
value=""
/>
<a href="https://www.openstreetmap.org/#map=19/49.561725/25.604458" target="_blank" title="Де знайти OSM iD ?"
><svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 48 48"
width="100px"
height="100px"
>
<path
d="M 24 4 C 12.972066 4 4 12.972074 4 24 C 4 35.027926 12.972066 44 24 44 C 35.027934 44 44 35.027926 44 24 C 44 12.972074 35.027934 4 24 4 z M 24 7 C 33.406615 7 41 14.593391 41 24 C 41 33.406609 33.406615 41 24 41 C 14.593385 41 7 33.406609 7 24 C 7 14.593391 14.593385 7 24 7 z M 24 14 A 2 2 0 0 0 24 18 A 2 2 0 0 0 24 14 z M 23.976562 20.978516 A 1.50015 1.50015 0 0 0 22.5 22.5 L 22.5 33.5 A 1.50015 1.50015 0 1 0 25.5 33.5 L 25.5 22.5 A 1.50015 1.50015 0 0 0 23.976562 20.978516 z"
/></svg
></a>
</div>
</div>
</form>
</details>
<details id="details-map" open>
<summary id="details-map-title">
<span>Крок 2.</span> Перегляд
</summary>
<form id="map-form">
<!-- <div class="editor-buttons" id="details-map-buttons-entranse">
<button type="button" onclick="Editor.editor.drawEntranse()">
Створити новий під'їзд
</button>
<div>
<button type="button" id="ruler" onclick="Editor.editor.drawRectangle()">
Лінійка
</button>
<button
type="button"
id="ruler_divide"
onclick="Editor.editor.ruler()"
style="display: none"
>
Поділити
</button>
</div>
</div> -->
<div
class="editor-buttons"
id="details-map-buttons-homestead"
style="display: none"
>
<!-- <div>
<button type="button" id="ruler" onclick="Editor.editor.drawRectangle()">
Лінійка
</button>
<button
type="button"
id="ruler_divide"
onclick="Editor.editor.ruler()"
style="display: none"
>
Поділити
</button>
</div> -->
</div>
<div id="list-entranse" style="display: none"></div>
<div id="list-homestead" style="display: none"></div>
<div class="block-map">
<div id="map"></div>
</div>
<button id="map-form-button" type="submit" style="display: none;">Далі</button>
</form>
</details>
<details id="details-area" open style="display: none;">
<summary><span>Крок 3.</span> Конструктор квартир <input type="number" value="1" id="next-apartment-title" onchange="Editor.apartments.editNum(this)" title="Авто-номер наступної квартири"></summary>
<form id="area-form">
<div id="list-area"></div>
</form>
</details>
</div>

View File

@@ -0,0 +1,449 @@
const Editor = {
init: async (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);
// document.getElementById("area-form").addEventListener("submit", async function (event) {
// event.preventDefault();
// await Editor.api.setPack();
// })
},
loadAPI: async function (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,
group_id: null,
title: null,
number: null,
points: [],
points_number: [],
point_icons: [],
geo: [],
osm_id: [],
settlement: [],
description: null,
entrance: [],
apartments: {}
},
setHTML: async (type, id) => {
let detailsInfo_address_title = document.getElementById('info-address-title');
let detailsInfo_number_title = document.getElementById('info-number-title');
let detailsInfo_settlement_title = document.getElementById('info-settlement-title');
let detailsInfo_group_title = document.getElementById('info-group-title');
let detailsInfo_osm_title = document.getElementById('info-osm-title');
Editor.info.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);
detailsInfo_address_title.value = Editor.info.list.title;
detailsInfo_number_title.value = Editor.info.list.number;
detailsInfo_settlement_title.value = Editor.info.list.settlement;
detailsInfo_group_title.value = Editor.info.list.group_id;
detailsInfo_osm_title.value = Editor.info.list.osm_id.join(", ");
Editor.osm.init();
Editor.info.setMap();
if (type == "house") {
Editor.entrances.setHTML(id);
document.getElementById('details-area').style.display = "";
}
},
setMap: async () => {
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([Editor.info.list.geo.lat, Editor.info.list.geo.lng], 17);
L.polygon(element, {
color: colorGroup(Editor.info.list.group_id),
radius: 500,
fillOpacity: 0.3,
dashArray: '20, 15',
dashOffset: '20',
}).addTo(houseGroup);
} else if (Editor.info.list.type == "house") {
map.setView([Editor.info.list.geo.lat, Editor.info.list.geo.lng], 18);
L.polygon(element, {
color: "#585858",
fillColor: colorGroup(Editor.info.list.group_id),
fillOpacity: 0.8,
tm_id: `house_${i}`
}).addTo(houseGroup);
}
}
}
},
entrances: {
list: [],
setHTML: async (id) => {
Editor.entrances.list = await Editor.loadAPI(`${CONFIG.api}house/${id}/entrances`);
console.log(Editor.entrances.list);
Editor.entrances.setMap()
},
setMap: async () => {
entransePolygonsGroup.clearLayers();
entranseNumPolygonsGroup.clearLayers();
for (let i = 0; i < Editor.entrances.list.length; i++) {
const element = Editor.entrances.list[i];
console.log(element);
let listEntranse = document.getElementById('list-entranse');
listEntranse.style.display = "";
listEntranse.innerHTML += `
<div class="house" style="align-items: center;" id="Entranse_${element.id}">
<p>${element.entrance_number + 1}</p>
</div>
`;
let listArea = document.getElementById('list-area');
listArea.innerHTML += `
<div class="block-area" id="block-area-${element.id}">
<h3>${element.title}</h3>
<button class="addFloors" title="Додати поверх" type="button" onclick="Editor.apartments.addFloors('${element.id}')"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg></button>
<div id="area-${element.id}"></div>
</div>
`;
Editor.apartments.setHTML(element.id);
L.polygon(element.points, { color: 'red' }).addTo(entransePolygonsGroup);
let myIcon = L.divIcon({ className: 'entranse_number', html: `<div class="markerEntranse">${element.entrance_number + 1}</div>` });
L.marker(element.points_number, { icon: myIcon }).addTo(entranseNumPolygonsGroup);
}
}
},
apartments: {
list: [],
setHTML: async (id) => {
Editor.apartments.list[`${id}`] = await Editor.loadAPI(`${CONFIG.api}apartments/${id}`);
const uniqueFloors = [...new Set(Editor.apartments.list[`${id}`].map(item => item.floors_number))];
for (let i = 0; i < uniqueFloors.length; i++) {
let num = uniqueFloors[i];
let area = document.getElementById(`area-${id}`);
let div = document.createElement('div');
div.className = "block-apartments-floors";
div.id = `floors-${id}-${num}`
div.innerHTML = `
<h2>Поверх ${num}</h2>
<button class="addApartment" id="buttonApartment-${id}-${num}" title="Додати квартиру" type="button" onclick="Editor.apartments.addApartment('${id}', '${num}')"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg></button>
`;
area.prepend(div);
}
Editor.apartments.list[`${id}`].sort((a, b) => b.title - a.title);
for (let i = 0; i < Editor.apartments.list[`${id}`].length; i++) {
const apartment = Editor.apartments.list[`${id}`][i];
let num = apartment.floors_number;
let floorsBlock = document.getElementById(`floors-${id}-${num}`);
let div = document.createElement('div');
div.className = "block-apartments-number";
div.id = `block-apartments-${id}-${apartment.id}`
div.innerHTML = `
<input type="text" value="${apartment.title}" id="apartament-${id}-${apartment.id}" onchange="Editor.apartments.editApartment('${id}','${apartment.id}')">
<button type="button" onclick="Editor.apartments.deleteApartment('${id}','${apartment.id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
`;
floorsBlock.prepend(div);
numApartments++;
let next_apartment_title = document.getElementById('next-apartment-title');
next_apartment_title.value = numApartments;
}
},
editNum: (element) => {
numApartments = Number(element.value);
},
addFloors: async (area) => {
let areaBlock = document.getElementById(`area-${area}`);
let uniqueFloors = [...new Set(Editor.apartments.list[area].map(obj => obj.floors_number))];
let new_floors = uniqueFloors.length + 1;
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}/apartments/${area}`;
await fetch(URL, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
body: JSON.stringify({
apartment_number: Editor.apartments.list[area].length,
title: numApartments.toString(),
floors_number: Number(new_floors)
})
})
.then(response => response.json())
.then(data => {
console.log(data);
let div = document.createElement('div');
div.className = "block-apartments-floors";
div.id = `floors-${area}-${new_floors}`
div.innerHTML = `
<h2>Поверх ${new_floors}</h2>
<div class="block-apartments-number" id="block-apartments-${area}-${data.id}">
<input type="text" value="${numApartments}" id="apartament-${area}-${data.id}" onchange="Editor.apartments.editApartment('${area}', '${data.id}')">
<button type="button" onclick="Editor.apartments.deleteApartment('${area}', '${data.id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
</div>
<button class="addApartment" id="buttonApartment-${area}-${new_floors}" title="Додати квартиру" type="button" onclick="Editor.apartments.addApartment('${area}', '${new_floors}')"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg></button>
`
areaBlock.prepend(div);
console.log(Editor.apartments.list[area]);
Editor.apartments.list[area].push({
id: data.id,
entrance_id: Number(area),
apartment_number: Editor.apartments.list[area].length,
title: numApartments.toString(),
floors_number: Number(new_floors)
});
numApartments++;
let next_apartment_title = document.getElementById('next-apartment-title');
next_apartment_title.value = numApartments;
})
.catch(err => {
console.log(err);
})
},
addApartment: async (area, floors) => {
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}/apartments/${area}`;
await fetch(URL, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
body: JSON.stringify({
apartment_number: Editor.apartments.list[area].length,
title: numApartments.toString(),
floors_number: Number(floors)
})
})
.then(response => response.json())
.then(data => {
console.log(data);
Editor.apartments.list[area].push({
id: data.id,
entrance_id: Number(area),
apartment_number: Editor.apartments.list[area].length,
title: numApartments.toString(),
floors_number: Number(floors)
});
let floorsBlock = document.getElementById(`floors-${area}-${floors}`);
document.getElementById(`buttonApartment-${area}-${floors}`).remove();
floorsBlock.innerHTML += `
<div class="block-apartments-number" id="block-apartments-${area}-${data.id}">
<input type="text" value="${numApartments}" id="apartament-${area}-${data.id}" onchange="Editor.apartments.editApartment('${area}', '${data.id}')">
<button type="button" onclick="Editor.apartments.deleteApartment('${area}', '${data.id}')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg>
</button>
</div>
`;
floorsBlock.innerHTML += `<button class="addApartment" id="buttonApartment-${area}-${floors}" title="Додати квартиру" type="button" onclick="Editor.apartments.addApartment('${area}', '${floors}')"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26"><path d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"></path></svg></button>`
numApartments++;
let next_apartment_title = document.getElementById('next-apartment-title');
next_apartment_title.value = numApartments;
})
.catch(err => {
console.log(err);
})
},
editApartment: async (area, apartament) => {
let input = document.getElementById(`apartament-${area}-${apartament}`);
input.setAttribute("value", input.value);
const pos = Editor.apartments.list[area].map(e => e.id).indexOf(Number(apartament));
Editor.apartments.list[area][pos].title = input.value;
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}/apartments/${area}`;
await fetch(URL, {
method: 'PUT',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
body: JSON.stringify({
title: Editor.apartments.list[area][pos].title,
status: Editor.apartments.list[area][pos].status,
description: Editor.apartments.list[area][pos].description,
id: Editor.apartments.list[area][pos].id
})
})
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(err => {
console.log(err);
})
},
deleteApartment: async (area, apartament) => {
const pos = Editor.apartments.list[area].map(e => e.id).indexOf(Number(apartament));
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}/apartments/${area}`;
await fetch(URL, {
method: 'DELETE',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
body: JSON.stringify({
id: Editor.apartments.list[area][pos].id
})
})
.then(response => response.json())
.then(data => {
console.log(data);
document.getElementById(`block-apartments-${area}-${apartament}`).remove();
Editor.apartments.list[area].splice(pos, 1);
numApartments--;
let next_apartment_title = document.getElementById('next-apartment-title');
next_apartment_title.value = numApartments;
})
.catch(err => {
console.log(err);
})
}
},
osm: {
init: () => {
let center = { lat: 49.5629016, lng: 25.6145625 };
let zoom = 19;
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', {
maxZoom: 19,
minZoom: 15
});
let mytile = L.tileLayer('https://tm.rozenrod.com/webp/{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(),
center,
zoom,
layers: [
googleHybrid,
osm,
mytile,
houseGroup,
entransePolygonsGroup,
entranseNumPolygonsGroup,
splitPolygonsGroup,
RectangleGroup,
],
zoomControl: false
});
let baseMaps = {
"Google Hybrid": googleHybrid,
"OpenStreetMap": osm,
"Territory Map": mytile
};
let overlayMaps = {
"Будинки": houseGroup,
"Під'їзди": entransePolygonsGroup,
"Номера під'їздів": entranseNumPolygonsGroup,
"Слой редактирования": splitPolygonsGroup,
"Слой линейки": RectangleGroup,
};
L.control.layers(baseMaps, overlayMaps, { 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
})
}
}
}
}

View File

@@ -0,0 +1,500 @@
.page-editor {
width: calc(100% - 40px);
display: flex;
flex-direction: column;
align-items: center;
margin: 20px 20px 0 20px;
}
.page-editor form>button {
border-radius: 6px;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
width: 100%;
height: 40px;
font-size: 14px;
font-weight: 400;
margin: 20px 0;
text-transform: uppercase;
}
.page-editor details {
border-radius: 10px;
width: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
margin-bottom: 20px;
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
}
.page-editor>details[disabled] summary,
.page-editor>details.disabled summary {
pointer-events: none;
user-select: none;
}
.page-editor>details summary::-webkit-details-marker,
.page-editor>details summary::marker {
display: none;
content: "";
}
.page-editor summary {
width: calc(100% - 40px);
cursor: pointer;
color: var(--ColorThemes3);
border-radius: var(--border-radius);
font-size: 16px;
font-weight: 300;
padding: 20px;
position: relative;
}
.page-editor summary span {
font-weight: 500;
}
.page-editor summary input {
font-weight: 500;
position: absolute;
right: 0;
top: 0;
padding: 10px;
margin: 13px;
font-size: 12px;
background: var(--ColorThemes3);
color: var(--ColorThemes0);
border-radius: 6px;
width: 40px;
}
.page-editor #info-form,
.page-editor #map-form,
.page-editor #area-form {
padding: 0 20px;
}
#details-info-type {
display: flex;
align-items: center;
justify-content: center;
}
#details-info-type>.tabs {
display: flex;
position: relative;
background-color: var(--ColorThemes0);
padding: 4px;
border-radius: 6px;
width: calc(100% - 8px);
z-index: 2;
}
#details-info-type>.tabs>input[type="radio"] {
display: none;
}
#details-info-type>.tabs>.tab {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
width: calc(100% / 3);
cursor: pointer;
padding: 0 15px;
transition: 0.15s ease-in;
color: var(--ColorThemes3);
fill: var(--ColorThemes3);
flex-direction: row;
z-index: 2;
}
#details-info-type>.tabs>.tab>svg {
width: 20px;
height: 20px;
}
#details-info-type>.tabs>.tab>span {
margin-left: 6px;
font-size: 12px;
font-weight: 400;
}
#details-info-type>.tabs>input[type="radio"]:checked+label {
color: var(--PrimaryColorText);
fill: var(--PrimaryColorText);
}
#details-info-type>.tabs>.glider {
position: absolute;
display: flex;
height: 40px;
width: calc((100% - 8px) / 3);
background-color: var(--PrimaryColor);
z-index: 1;
border-radius: 4px;
transition: 0.25s ease-out;
}
@media (min-width: 601px) {
#details-info-type>.tabs>input[id="info-type-house"]:checked~.glider {
transform: translateX(0);
}
#details-info-type>.tabs>input[id="info-type-homestead"]:checked~.glider {
transform: translateX(100%);
}
#details-info-type>.tabs>input[id="info-type-points"]:checked~.glider {
transform: translateX(200%);
}
}
@media (max-width: 600px) {
#details-info-type>.tabs {
flex-direction: column;
}
#details-info-type>.tabs>.tab {
width: calc(100% - 8px);
padding: 0 4px;
}
#details-info-type>.tabs>.glider {
width: calc(100% - 8px);
}
#details-info-type>.tabs>input[id="info-type-house"]:checked~.glider {
transform: translateY(0);
}
#details-info-type>.tabs>input[id="info-type-homestead"]:checked~.glider {
transform: translateY(100%);
}
#details-info-type>.tabs>input[id="info-type-points"]:checked~.glider {
transform: translateY(200%);
}
}
.page-editor .details-info-input {
width: 100%;
display: flex;
margin: 20px 0;
align-items: flex-start;
flex-direction: column;
}
.page-editor .details-info-input label {
display: flex;
justify-content: center;
flex-direction: column;
font-size: 12px;
font-weight: 500;
margin-bottom: 5px;
}
.page-editor .details-info-input input {
width: calc(100% - 10px);
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
background: var(--ColorThemes0);
color: var(--ColorThemes3);
}
.page-editor #details-info-osm div {
display: flex;
align-items: center;
width: 100%;
}
.page-editor #details-info-osm input {
width: calc(100% - 10px);
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
}
.page-editor .details-info-input a {
height: 26px;
width: 26px;
margin-left: 10px;
}
.page-editor .details-info-input a>svg {
height: 26px;
width: 26px;
fill: var(--ColorThemes3)
}
.page-editor #list-area {
display: flex;
overflow: auto;
margin: 15px 0 20px 0;
}
.page-editor #list-area h3 {
text-align: center;
font-size: 16px;
font-weight: 400;
margin: 10px;
padding: 7px;
color: var(--ColorThemes0);
background: var(--ColorThemes3);
border-radius: 4px;
}
.block-area {
min-height: 200px;
border: 1px solid var(--ColorThemes3);
border-style: dashed;
border-radius: 6px;
margin: 0 10px 10px 0;
}
.addFloors,
.addApartment {
display: flex;
position: relative;
width: 34px;
height: 34px;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
margin: 25px;
border-radius: 50%;
align-items: center;
justify-content: center;
font-size: 30px;
cursor: pointer;
}
.addFloors>svg,
.addApartment>svg {
width: 20px;
height: 20px;
fill: var(--PrimaryColorText);
transform: rotate(45deg);
}
.block-apartments-floors {
position: relative;
display: flex;
width: calc(100% - 22px);
border: 1px solid var(--ColorThemes3);
margin: 10px;
border-radius: 4px;
}
.block-apartments-floors h2 {
position: absolute;
width: 65px;
right: -1px;
top: -1px;
margin: 0;
background: var(--ColorThemes3);
color: var(--ColorThemes2);
border-radius: 0 4px 0 4px;
font-size: 12px;
padding: 2px 4px;
text-align: center;
}
.block-apartments-number {
display: flex;
position: relative;
width: 60px;
height: 60px;
background: var(--ColorThemes1);
border: 2px solid var(--PrimaryColor);
margin: 10px;
border-radius: 4px;
align-items: center;
justify-content: center;
}
.block-apartments-number input {
width: 50px;
height: 50px;
font-size: 16px;
font-weight: 400;
text-align: center;
color: var(--ColorThemes3);
background: 0;
}
.block-apartments-number button {
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
border-radius: 0 4px 0 4px;
background: var(--PrimaryColor);
font-size: 16px;
margin: -2px;
border: 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.block-apartments-number button>svg {
width: 16px;
height: 16px;
fill: var(--PrimaryColorText);
}
.block-map {
width: 100%;
height: 500px;
border-radius: 6px;
overflow: hidden;
margin-bottom: 20px;
}
#map {
width: 100%;
height: 100%;
}
.entranse_number {
left: -10px !important;
top: -10px !important;
}
.markerEntranse {
background: hsl(0deg 0% 52.12% / 90%);
font-size: 24px;
border: 2px solid #676767;
border-radius: 2px;
display: flex !important;
justify-content: center;
align-items: center;
color: #fff;
min-width: 30px;
min-height: 30px;
height: 30px;
width: 30px;
}
.editor-buttons {
margin-top: 15px;
}
.editor-buttons>button {
width: 100%;
min-height: 30px;
margin: 5px 0;
border: 0;
color: var(--PrimaryColorText);
display: flex;
align-items: center;
justify-content: center;
background: var(--PrimaryColor);
text-transform: uppercase;
cursor: pointer;
border-radius: 6px;
font-weight: 400;
font-size: 12px;
}
.editor-buttons>div {
display: flex;
width: 100%;
margin: 10px 0;
height: 30px;
justify-content: space-between;
}
.editor-buttons>div>button {
background: var(--ColorThemes3);
color: var(--ColorThemes1);
width: 100%;
margin: 0;
border-radius: 6px;
text-transform: uppercase;
font-weight: 400;
font-size: 12px;
}
.page-editor #list-entranse,
.page-editor #list-homestead {
width: 100%;
min-height: 86px;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
overflow-x: initial;
overflow-y: auto;
border: 1px solid var(--ColorThemes3);
background: 0;
border-radius: 6px;
border-style: dashed;
margin: 10px 0;
}
.page-editor #list-entranse>.house,
.page-editor #list-homestead>.house {
display: flex;
position: relative;
width: 60px;
height: 60px;
background: var(--ColorThemes1);
border: 2px solid var(--PrimaryColor);
margin: 10px;
border-radius: 4px;
align-items: center;
justify-content: center;
}
.page-editor #list-entranse>.house>input,
.page-editor #list-homestead>.house>input,
.page-editor #list-entranse>.house>p,
.page-editor #list-homestead>.house>p {
width: 50px;
height: 50px;
font-size: 16px;
font-weight: 400;
text-align: center;
color: var(--ColorThemes3);
background: 0;
display: flex;
align-items: center;
justify-content: center;
}
.page-editor #list-entranse>.house>button,
.page-editor #list-homestead>.house>button {
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
border-radius: 0 4px 0 4px;
background: var(--PrimaryColor);
font-size: 16px;
margin: -2px;
border: 0;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.page-editor #list-entranse>.house>button>svg,
.page-editor #list-homestead>.house>button>svg {
width: 16px;
height: 16px;
fill: var(--PrimaryColorText);
}

View File

@@ -0,0 +1,9 @@
<div class="page-home">
<details id="details-personal-territory" open>
<summary>
<span>Території для опрацювання</span>
</summary>
<div id="home-personal-territory-list"></div>
</details>
</div>

View File

@@ -0,0 +1,95 @@
const Home = {
init: async () => {
let html = await fetch('/lib/pages/home/index.html').then((response) => response.text());
app.innerHTML = html;
Home.house.setHTML();
Home.homestead.setHTML();
},
house: {
loadAPI: async function () {
let uuid = localStorage.getItem("uuid");
const URL = `${CONFIG.api}houses/list?mode=sheep`;
return await fetch(URL, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
}).then((response) => response.json());
},
setHTML: async function () {
let list = await Home.house.loadAPI();
list.sort((a, b) => b.id - a.id);
console.log(list);
let block_house = document.getElementById('home-personal-territory-list')
for (let i = 0; i < list.length; i++) {
const element = list[i];
block_house.innerHTML += `
<div class="card">
<i style="background-image: url(https://sheep-service.com/cards/house/T${element.id}.webp);"></i>
<div class="contents">
<div class="group" style="background: ${colorGroup(element.group_id)}">
<span>Група №${element.group_id}</span>
</div>
<div class="info">
<div>
<p>${element.title} ${element.number}</p>
</div>
</div>
</div>
<a href="/territory/card/house/${element.id}" data-route></a>
</div>
`;
}
}
},
homestead: {
loadAPI: async function () {
let uuid = localStorage.getItem("uuid");
const URL = `${CONFIG.api}homestead/list?mode=sheep`;
return await fetch(URL, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
}).then((response) => response.json());
},
setHTML: async function () {
let list = await Home.homestead.loadAPI();
list.sort((a, b) => b.id - a.id);
console.log(list);
let block_homestead = document.getElementById('home-personal-territory-list')
for (let i = 0; i < list.length; i++) {
const element = list[i];
block_homestead.innerHTML += `
<div class="card">
<i style="background-image: url(https://sheep-service.com/cards/homestead/H${element.id}.webp);"></i>
<div class="contents">
<div class="group" style="background: ${colorGroup(element.group_id)}">
<span>Група №${element.group_id}</span>
</div>
<div class="info">
<div>
<p>${element.title} ${element.number}</p>
</div>
</div>
</div>
<a href="/territory/card/homestead/${element.id}" data-route></a>
</div>
`;
}
}
}
}

View File

@@ -0,0 +1,191 @@
.page-home {
width: calc(100% - 40px);
display: flex;
flex-direction: column;
margin: 20px 20px 0 20px;
}
.page-home details {
border-radius: 15px;
width: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
margin-bottom: 20px;
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
}
.page-home>details[disabled] summary,
.page-home>details.disabled summary {
pointer-events: none;
user-select: none;
}
.page-home>details summary::-webkit-details-marker,
.page-home>details summary::marker {
display: none;
content: "";
}
.page-home summary {
width: calc(100% - 40px);
cursor: pointer;
color: var(--ColorThemes3);
border-radius: var(--border-radius);
font-size: 16px;
font-weight: 300;
padding: 20px;
position: relative;
}
.page-home summary span {
font-weight: 500;
}
.page-home #home-personal-territory-list {
width: 100%;
margin: 0;
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-content: flex-start;
justify-content: center;
overflow-y: auto;
align-items: flex-start;
transition: .3s ease;
}
.page-home .card {
position: relative;
width: 300px;
height: 200px;
background-color: var(--ColorThemes2);
margin: 0px 10px 20px 10px;
overflow: hidden;
cursor: pointer;
border-radius: 10px;
}
@media (max-width: 2300px) {
.page-home .card {
width: calc((100% / 5) - 30px);
}
}
@media (max-width: 1960px) {
.page-home .card {
width: calc((100% / 4) - 30px);
}
}
@media (max-width: 1640px) {
.page-home .card {
width: calc((100% / 3) - 30px);
}
}
@media (max-width: 1280px) {
.page-home .card {
width: calc((100% / 2) - 30px);
}
}
@media (max-width: 650px) {
.page-home .card {
width: 100%;
}
}
@media(hover: hover) {
.page-home .card:hover {
opacity: 0.8;
}
}
.page-home .card>i {
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 1;
filter: blur(2px);
/* background-repeat: round; */
background-size: cover;
background-position: center;
background-image: url(https://tm.rozenrod.com/web/img/bg.webp);
}
.page-home .card>a {
position: absolute;
width: 100%;
height: 100%;
z-index: 10;
}
.page-home .contents {
position: absolute;
z-index: 2;
background: rgb(64 64 64 / 0.7);
width: 100%;
height: 100%;
font-size: 40px;
font-weight: 500;
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
.page-home .group {
width: calc(100% - 20px);
max-height: 50px;
border-radius: 7px;
padding: 10px 0;
margin-top: 10px;
display: flex;
background: var(--PrimaryColor);
align-items: center;
flex-direction: column;
justify-content: space-around;
}
.page-home .group>span {
color: #fff;
font-size: 14px;
font-weight: 400;
}
.page-home .info {
width: calc(100% - 20px);
margin-bottom: 10px;
}
.page-home .info>div {
width: 100%;
height: 35px;
margin-top: 5px;
display: flex;
background: var(--ColorThemes0);
align-items: center;
justify-content: center;
font-size: 12px;
color: var(--ColorThemes3);
border-radius: 7px;
position: relative;
overflow: hidden;
}
.page-home .info>div>span {
color: var(--ColorThemes3);
font-size: 14px;
font-weight: 300;
z-index: 2;
}
.page-home .info>div>p {
color: var(--ColorThemes3);
font-size: 14px;
font-weight: 400;
padding: 10px;
z-index: 2;
}

View File

View File

@@ -0,0 +1,6 @@
const Options = {
init: async () => {
let html = await fetch('/lib/pages/options/index.html').then((response) => response.text());
app.innerHTML = html;
}
}

View File

View File

@@ -0,0 +1,250 @@
<div class="page-sheeps">
<div id="block-sheeps-list">
<div class="header">
<h1>Всі вісники</h1>
</div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
<div class="card-profile"></div>
</div>
<div id="block-sheep-info">
<div id="sheep-mess">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<path
d="M 25.042969 12 C 20.698969 12 18 15.029297 18 19.904297 C 18 24.104297 20.766812 28.599609 24.882812 28.599609 C 27.167812 28.599609 31 27.369813 31 19.132812 C 31 14.999813 28.494969 12 25.042969 12 z M 39.974609 12 C 35.672609 12 33 14.931437 33 19.648438 C 33 24.104438 34.862391 28.599609 39.025391 28.599609 C 43.603391 28.599609 46 23.922734 46 19.302734 C 46 14.285734 42.876609 12 39.974609 12 z M 25.042969 16 C 26.388969 16 27 17.623812 27 19.132812 C 27 20.776813 26.794812 24.599609 24.882812 24.599609 C 23.428813 24.599609 22 22.274297 22 19.904297 C 22 16.000297 24.132969 16 25.042969 16 z M 39.974609 16 C 41.224609 16 42 17.265734 42 19.302734 C 42 21.906734 40.886391 24.599609 39.025391 24.599609 C 37.594391 24.599609 37 21.375438 37 19.648438 C 37 16.000438 39.084609 16 39.974609 16 z M 13.689453 24 C 9.8134531 24 7 27.465234 7 32.240234 C 7 35.462234 9.0299688 40 13.542969 40 C 17.224969 40 20 36.250297 20 31.279297 C 20 27.061297 17.346453 24 13.689453 24 z M 50.75 24 C 46.94 24 43 27.440219 43 33.199219 C 43 36.541219 45.15 40 48.75 40 C 52.612 40 56 35.477312 56 30.320312 C 56 26.480313 53.939 24 50.75 24 z M 13.689453 28 C 15.514453 28 16 30.061297 16 31.279297 C 16 33.793297 14.852969 36 13.542969 36 C 11.603969 36 11 33.244234 11 32.240234 C 11 29.783234 12.131453 28 13.689453 28 z M 50.75 28 C 51.783 28 52 29.262313 52 30.320312 C 52 33.788312 49.866 36 48.75 36 C 47.514 36 47 34.175219 47 33.199219 C 47 29.652219 49.237 28 50.75 28 z M 31.806641 30.001953 C 29.371641 29.947953 27.296656 31.058937 25.597656 33.335938 C 22.515656 37.450938 21.695953 38.210313 19.376953 39.695312 C 17.528953 40.875312 14.998047 42.492141 14.998047 45.994141 C 14.998047 49.769141 18.164641 52.839844 22.056641 52.839844 C 23.872641 52.839844 25.588266 52.174453 27.697266 51.439453 C 29.071266 52.348453 30.722125 52.839844 32.453125 52.839844 C 34.215125 52.839844 35.869422 52.353125 37.232422 51.453125 C 41.805422 53.496125 44.150719 53.039344 45.636719 52.277344 C 47.314719 51.416344 48.403953 49.800703 48.876953 47.470703 C 49.107953 46.334703 48.997375 45.198781 48.609375 44.175781 C 48.012375 42.602781 46.757453 41.293531 45.064453 40.644531 C 43.137453 39.916531 42.576406 39.753172 41.316406 38.826172 C 39.672406 37.616172 39.015453 35.742422 37.814453 33.607422 C 36.601453 31.449422 34.298641 30.067953 31.806641 30.001953 z M 31.701172 34.001953 C 32.777172 34.029953 33.808125 34.644359 34.328125 35.568359 C 35.480125 37.621359 36.442312 40.205828 38.945312 42.048828 C 40.748312 43.375828 41.894813 43.725812 43.632812 44.382812 C 44.073813 44.550813 44.501812 44.883844 44.757812 45.339844 C 44.966813 45.711844 45.061031 46.164781 44.957031 46.675781 C 44.738031 47.753781 44.3535 48.44175 43.8125 48.71875 C 42.4475 49.41975 39.736125 48.192875 38.578125 47.671875 C 38.187125 47.495875 36.729781 46.582125 35.550781 47.703125 C 34.778781 48.436125 33.679125 48.839844 32.453125 48.839844 C 31.288125 48.839844 30.211922 48.444562 29.419922 47.726562 C 28.893922 47.247562 28.151609 47.085828 27.474609 47.298828 C 25.411609 47.948828 23.441594 48.839844 22.058594 48.839844 C 20.372594 48.839844 19.001953 47.562141 19.001953 45.994141 C 19.001953 44.795141 19.69525 44.240406 21.53125 43.066406 C 24.29525 41.302406 25.514688 40.146469 28.804688 35.730469 C 29.975687 34.161469 30.981172 33.980953 31.701172 34.001953 z"
/>
</svg>
<h1>Виберіть вісника для редагування</h1>
</div>
<form id="sheep-editor" style="display: none; opacity: 0">
<div class="header">
<h1>Інформація про вісника</h1>
<button onclick="Sheeps.editor.close()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26">
<path
d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"
></path>
</svg>
</button>
</div>
<i id="sheep-editor-icon"></i>
<div class="editor-blocks-inputs" id="editor-blocks-inputs-uuid">
<label>UUID</label>
<input
id="sheep-editor-uuid"
type="text"
name="uuid"
value=""
disabled
style="display: none"
/>
<p id="sheep-editor-uuid-copy" style="cursor: copy !important"></p>
</div>
<div class="editor-blocks-inputs">
<label for="sheep-editor-name">Імʼя</label>
<input id="sheep-editor-name" type="text" name="name" required="" />
</div>
<div class="editor-blocks-inputs">
<label for="sheep-editor-group_id">Група</label>
<select id="sheep-editor-group_id" name="group_id">
<option value="1">Група 1</option>
<option value="2" selected>Група 2</option>
<option value="3">Група 3</option>
<option value="4">Група 4</option>
<option value="5">Група 5</option>
<option value="6">Група 6</option>
<option value="7">Група 7</option>
</select>
</div>
<div class="editor-blocks-inputs">
<label for="sheep-editor-appointment">Призначення</label>
<select id="sheep-editor-appointment" name="appointment">
<option value="lamb" selected>Вісник</option>
<option value="pioneer">Піонер</option>
<option value="attender">Служитель збору</option>
<option value="elder">Старійшина збору</option>
</select>
</div>
<div class="editor-blocks-inputs">
<label for="sheep-editor-mode">Права</label>
<select id="sheep-editor-mode" name="mode">
<option value="sheep" selected>Користувач</option>
<option value="moderator">Модератор</option>
<option value="administrator">Адміністратор</option>
</select>
</div>
<div
class="editor-blocks-inputs"
id="editor-blocks-inputs-uuid-moder"
style="display: none"
>
<label>UUID адміністратора/модератора</label>
<p id="sheep-editor-uuid-moder" style="cursor: copy !important"></p>
</div>
<div
class="editor-blocks-checkbox"
id="sheep-editor-access-moder"
style="display: none"
>
<p for="editor-access">Дозволи модератора</p>
<div>
<div class="checkbox">
<input
name="can_add_sheeps"
class="custom-checkbox"
id="sheep-editor-can_add_sheeps"
type="checkbox"
/>
<label for="sheep-editor-can_add_sheeps"> Create Sheeps </label>
</div>
<div class="checkbox">
<input
name="can_add_territory"
class="custom-checkbox"
id="sheep-editor-can_add_territory"
type="checkbox"
/>
<label for="sheep-editor-can_add_territory">
Create Territory
</label>
</div>
<div class="checkbox">
<input
name="can_manager_territory"
class="custom-checkbox"
id="sheep-editor-can_manager_territory"
type="checkbox"
/>
<label for="sheep-editor-can_manager_territory">
Manager Territory
</label>
</div>
<div class="checkbox">
<input
name="can_add_stand"
class="custom-checkbox"
id="sheep-editor-can_add_stand"
type="checkbox"
/>
<label for="sheep-editor-can_add_stand"> Create Stand </label>
</div>
<div class="checkbox">
<input
name="can_manager_stand"
class="custom-checkbox"
id="sheep-editor-can_manager_stand"
type="checkbox"
/>
<label for="sheep-editor-can_manager_stand"> Manager Stand </label>
</div>
<div class="checkbox">
<input
name="can_add_schedule"
class="custom-checkbox"
id="sheep-editor-can_add_schedule"
type="checkbox"
/>
<label for="sheep-editor-can_add_schedule"> Create Schedule </label>
</div>
</div>
</div>
<div class="editor-blocks-checkbox">
<p for="editor-access">Дозволи вісника</p>
<div>
<div class="checkbox">
<input
name="can_view_schedule"
class="custom-checkbox"
id="sheep-editor-can_view_schedule"
type="checkbox"
/>
<label for="sheep-editor-can_view_schedule"> View Schedule </label>
</div>
<div class="checkbox">
<input
name="can_view_stand"
class="custom-checkbox"
id="sheep-editor-can_view_stand"
type="checkbox"
/>
<label for="sheep-editor-can_view_stand"> View Stand </label>
</div>
<div class="checkbox">
<input
name="can_view_territory"
class="custom-checkbox"
id="sheep-editor-can_view_territory"
type="checkbox"
/>
<label for="sheep-editor-can_view_territory">
View Territory
</label>
</div>
</div>
</div>
<button id="sheep-editor-button" style="display: none">Зберегти</button>
</form>
</div>
<div id="block-sheep-addeds" style="display: none; opacity: 0">
<form id="sheep-addeds">
<div class="header">
<h1>Додавання нового вісника</h1>
<button onclick="Sheeps.addeds.close()" class="close" type="button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26">
<path
d="M 6.65625 4 C 6.367188 4 6.105469 4.113281 5.90625 4.3125 L 4.3125 5.90625 C 3.914063 6.304688 3.914063 7 4.3125 7.5 L 9.8125 13 L 4.3125 18.5 C 3.914063 19 3.914063 19.695313 4.3125 20.09375 L 5.90625 21.6875 C 6.40625 22.085938 7.101563 22.085938 7.5 21.6875 L 13 16.1875 L 18.5 21.6875 C 19 22.085938 19.695313 22.085938 20.09375 21.6875 L 21.6875 20.09375 C 22.085938 19.59375 22.085938 18.898438 21.6875 18.5 L 16.1875 13 L 21.6875 7.5 C 22.085938 7 22.085938 6.304688 21.6875 5.90625 L 20.09375 4.3125 C 19.59375 3.914063 18.898438 3.914063 18.5 4.3125 L 13 9.8125 L 7.5 4.3125 C 7.25 4.113281 6.945313 4 6.65625 4 Z"
></path>
</svg>
</button>
</div>
<i id="sheep-addeds-icon"></i>
<div class="addeds-blocks-inputs">
<label for="sheep-addeds-name">Імʼя</label>
<input id="sheep-addeds-name" type="text" name="name" required="" />
</div>
<div class="addeds-blocks-inputs">
<label for="sheep-addeds-group_id">Група</label>
<select id="sheep-addeds-group_id" name="group_id" required>
<option value="" selected disabled>Оберіть...</option>
<option value="1">Група 1</option>
<option value="2">Група 2</option>
<option value="3">Група 3</option>
<option value="4">Група 4</option>
<option value="5">Група 5</option>
<option value="6">Група 6</option>
<option value="7">Група 7</option>
</select>
</div>
<div class="addeds-blocks-inputs">
<label for="sheep-addeds-appointment">Призначення</label>
<select id="sheep-addeds-appointment" name="appointment" required>
<option value="lamb" selected>Вісник</option>
<option value="pioneer">Піонер</option>
<option value="attender">Служитель збору</option>
<option value="elder">Старійшина збору</option>
</select>
</div>
<button id="sheep-addeds-button">Додати</button>
</form>
</div>
</div>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,482 @@
.page-sheeps {
width: calc(100% - 18px);
display: flex;
flex-direction: row;
margin: 20px 9px 0 9px;
justify-content: space-between;
position: relative;
}
#block-sheeps-list,
#block-sheep-info {
width: 100%;
margin: 0 10px 15px;
border-radius: 15px;
display: flex;
flex-direction: column;
overflow: hidden;
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
transition: all .2s ease 0s;
}
#block-sheep-info {
min-height: calc(100vh - 40px);
max-height: calc(100vh - 40px);
position: sticky;
overflow: auto;
top: 20px;
right: 0;
}
#block-sheeps-list>.header {
min-height: 40px;
width: calc(100% - 20px);
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
background: var(--PrimaryColor);
margin: 10px;
border-radius: 10px;
}
#block-sheeps-list>.header>h1 {
font-size: 16px;
font-weight: 400;
color: var(--PrimaryColorText);
margin-left: 10px;
}
#block-sheeps-list>.header>button {
display: flex;
position: relative;
width: 30px;
height: 30px;
background: var(--ColorThemes0);
margin-right: 5px;
border-radius: 8px;
align-items: center;
justify-content: center;
font-size: 30px;
cursor: pointer;
}
#block-sheeps-list>.header>button>svg {
width: 20px;
height: 20px;
fill: var(--ColorThemes3);
transform: rotate(45deg);
}
#block-sheeps-list>.card-profile {
width: calc(100% - 30px);
min-height: 100px;
background-color: var(--ColorThemes2);
border: 1px solid var(--ColorThemes0);
box-shadow: var(--shadow-l1);
border-radius: 10px;
margin: 10px;
display: flex;
flex-direction: row;
align-items: center;
padding: 5px;
cursor: pointer;
}
#block-sheeps-list>.card-profile>img,
#block-sheeps-list>.card-profile>svg {
width: 65px;
min-width: 65px;
height: 65px;
margin: 10px 15px 10px 10px;
fill: var(--PrimaryColor);
}
#block-sheeps-list>.card-profile>.info {
display: flex;
flex-direction: column;
height: 90px;
justify-content: space-between;
position: relative;
width: calc(100% - 95px);
}
#block-sheeps-list>.card-profile>.info>.text>h1 {
font-size: 16px;
color: var(--ColorThemes3);
font-weight: 400;
}
#block-sheeps-list>.card-profile>.info>.text>h2 {
font-size: 12px;
color: var(--ColorThemes3);
font-weight: 400;
opacity: 0.8;
}
#block-sheeps-list>.card-profile>.info>.access {
display: flex;
overflow-x: auto;
overflow-y: hidden;
max-width: 100%;
padding-bottom: 4px;
height: 30px;
}
#block-sheeps-list>.card-profile>.info>.access>b {
padding: 2px 5px;
border-radius: 5px;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
margin-right: 5px;
white-space: nowrap;
font-size: 13px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
#block-sheep-info>#sheep-mess {
width: 200px;
height: 200px;
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
margin-top: -100px;
margin-left: -100px;
justify-content: center;
opacity: 0.6;
transition: all .2s ease 0s;
z-index: 1;
}
#block-sheep-info>#sheep-mess>svg {
width: 100px;
height: 100px;
fill: var(--ColorThemes3);
}
#block-sheep-info>#sheep-mess>h1 {
font-size: 20px;
color: var(--ColorThemes3);
font-weight: 400;
text-align: center;
}
#block-sheep-info>#sheep-editor {
display: flex;
padding: 10px;
flex-direction: column;
align-items: center;
transition: all .3s ease 0s;
z-index: 2;
}
#block-sheep-info>#sheep-editor>.header {
display: none;
min-height: 40px;
width: 100%;
flex-direction: row;
align-items: center;
justify-content: space-between;
background: var(--ColorThemes3);
margin: 0 0 10px 0;
border-radius: 10px;
}
#block-sheep-info>#sheep-editor>.header>h1 {
font-size: 16px;
font-weight: 400;
color: var(--ColorThemes0);
margin-left: 10px;
}
#block-sheep-info>#sheep-editor>.header>button {
display: flex;
position: relative;
width: 30px;
height: 30px;
background: var(--ColorThemes0);
margin-right: 5px;
border-radius: 8px;
align-items: center;
justify-content: center;
font-size: 30px;
cursor: pointer;
}
#block-sheep-info>#sheep-editor>.header>button>svg {
width: 20px;
height: 20px;
fill: var(--ColorThemes3);
}
#block-sheep-info>#sheep-editor>i>svg {
width: 100px;
height: 100px;
fill: var(--PrimaryColor);
}
#block-sheep-info>#sheep-editor>.editor-blocks-inputs {
width: 100%;
display: flex;
margin: 10px 0;
align-items: flex-start;
flex-direction: column;
}
#block-sheep-info>#sheep-editor>.editor-blocks-inputs label {
display: flex;
justify-content: center;
flex-direction: column;
font-size: 12px;
font-weight: 500;
margin-bottom: 5px;
}
#block-sheep-info>#sheep-editor>.editor-blocks-inputs input {
width: calc(100% - 10px);
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
background: var(--ColorThemes0);
color: var(--ColorThemes3);
}
#block-sheep-info>#sheep-editor>.editor-blocks-inputs select {
width: 100%;
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
background-color: var(--ColorThemes0);
color: var(--ColorThemes3);
}
#block-sheep-info>#sheep-editor>.editor-blocks-inputs p {
display: flex;
width: calc(100% - 10px);
min-width: 140px;
font-size: 14px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
background: var(--ColorThemes0);
color: var(--ColorThemes3);
align-items: center;
cursor: copy;
}
#block-sheep-info>#sheep-editor>.editor-blocks-checkbox {
width: 100%;
display: flex;
margin: 10px 0;
align-items: flex-start;
flex-direction: column;
}
#block-sheep-info>#sheep-editor>.editor-blocks-checkbox>p {
display: flex;
justify-content: center;
flex-direction: column;
font-size: 12px;
font-weight: 500;
margin-bottom: 5px;
}
#block-sheep-info>#sheep-editor>.editor-blocks-checkbox>div {
background: var(--ColorThemes0);
border-radius: 6px;
width: calc(100% - 15px);
padding: 0 5px 0 10px;
}
#block-sheep-info>#sheep-editor>.editor-blocks-checkbox>div>.checkbox {
margin: 10px 0;
width: 100%;
font-size: 14px;
}
#block-sheep-info>#sheep-editor>.editor-blocks-checkbox>div>.checkbox>.custom-checkbox+label {
width: 100%;
display: flex;
align-items: center;
user-select: none;
flex-direction: row-reverse;
justify-content: space-between;
}
#block-sheep-info>#sheep-editor>button {
border-radius: 6px;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
width: 100%;
height: 40px;
font-size: 14px;
font-weight: 400;
margin: 20px 0 0 0;
text-transform: uppercase;
}
#block-sheep-addeds {
width: 100%;
height: fit-content;
margin: 0 10px 15px;
border-radius: 15px;
display: flex;
flex-direction: column;
overflow: hidden;
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
transition: all .2s ease 0s;
}
#block-sheep-addeds>#sheep-addeds {
display: flex;
padding: 10px;
flex-direction: column;
align-items: center;
transition: all .3s ease 0s;
}
#block-sheep-addeds>#sheep-addeds>div {
width: 100%;
display: flex;
justify-content: space-between;
}
#block-sheep-addeds>#sheep-addeds>.header {
min-height: 40px;
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
background: var(--ColorThemes3);
margin: 0 0 10px 0;
border-radius: 10px;
}
#block-sheep-addeds>#sheep-addeds>.header>h1 {
font-size: 16px;
font-weight: 400;
color: var(--ColorThemes0);
margin-left: 10px;
}
#block-sheep-addeds>#sheep-addeds>.header>button {
display: flex;
position: relative;
width: 30px;
height: 30px;
background: var(--ColorThemes0);
margin-right: 5px;
border-radius: 8px;
align-items: center;
justify-content: center;
font-size: 30px;
cursor: pointer;
}
#block-sheep-addeds>#sheep-addeds>.header>button>svg {
width: 20px;
height: 20px;
fill: var(--ColorThemes3);
}
#block-sheep-addeds>#sheep-addeds>i>svg {
width: 100px;
height: 100px;
fill: var(--PrimaryColor);
}
#block-sheep-addeds>#sheep-addeds>.addeds-blocks-inputs {
width: 100%;
display: flex;
margin: 10px 0;
align-items: flex-start;
flex-direction: column;
}
#block-sheep-addeds>#sheep-addeds>.addeds-blocks-inputs label {
display: flex;
justify-content: center;
flex-direction: column;
font-size: 12px;
font-weight: 500;
margin-bottom: 5px;
}
#block-sheep-addeds>#sheep-addeds>.addeds-blocks-inputs input {
width: calc(100% - 10px);
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
background: var(--ColorThemes0);
color: var(--ColorThemes3);
}
#block-sheep-addeds>#sheep-addeds>.addeds-blocks-inputs select {
width: 100%;
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
background-color: var(--ColorThemes0);
color: var(--ColorThemes3);
}
#block-sheep-addeds>#sheep-addeds>button {
border-radius: 6px;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
width: 100%;
height: 40px;
font-size: 14px;
font-weight: 400;
margin: 20px 0 0 0;
text-transform: uppercase;
}
@media (min-width: 1001px),
(min-height: 541px) {
#block-sheeps-list,
#block-sheep-info {
opacity: 1 !important;
}
}
@media (max-width: 1000px),
(max-height: 540px) {
#block-sheeps-list {
display: none;
}
#block-sheep-info {
display: none;
min-height: fit-content;
}
#block-sheep-info>#sheep-editor>.header {
display: flex;
}
}
/* @media (min-height: 600px) and (max-height: 700px) {
#block-sheep-info>#sheep-editor>i>svg {
display: none;
}
} */

View File

@@ -0,0 +1,4 @@
<div class="page-stand">
<label for="dateSelect">Виберіть дату:</label>
<select id="dateSelect"></select>
</div>

View File

@@ -0,0 +1,38 @@
const Stand = {
init: async () => {
let html = await fetch('/lib/pages/stand/index.html').then((response) => response.text());
app.innerHTML = html;
let listDate = [1, 4];
function generateAvailableDates() {
let select = document.getElementById("dateSelect");
select.innerHTML = "";
let today = new Date();
today.setHours(0, 0, 0, 0);
let months = [today.getMonth(), today.getMonth() + 1];
let year = today.getFullYear();
months.forEach(month => {
let date = new Date(year, month, 1);
while (date.getMonth() === month) {
if (date >= today) {
let day = date.getDay();
if (listDate.includes(day)) {
let option = document.createElement("option");
option.value = date.toISOString().split("T")[0];
option.textContent = date.toLocaleDateString("uk-UA", {
weekday: "long", year: "numeric", month: "long", day: "numeric"
});
select.appendChild(option);
}
}
date.setDate(date.getDate() + 1);
}
});
}
generateAvailableDates();
}
}

View File

@@ -0,0 +1,20 @@
.page-stand {
width: calc(100% - 40px);
display: flex;
flex-direction: column;
margin: 20px 20px 0 20px;
}
.page-stand select {
width: 100%;
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
background-color: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
margin: 10px 0;
box-shadow: var(--shadow-l1);
font-size: 14px;
}

View File

@@ -0,0 +1,39 @@
<div class="page-territory">
<div class="buttons-list">
<button onclick="Rotation()" id="rotationButton" style="display: none;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path
d="M16.5 25.5c-.52.893-1.476 1.5-2.584 1.5-1.657 0-3-1.343-3-3s1.343-3 3-3c1.108 0 2.064.607 2.584 1.5H23v-11C23 8.468 20.533 6 17.5 6h-6C8.467 6 6 8.468 6 11.5v25c0 3.032 2.467 5.5 5.5 5.5h6c3.033 0 5.5-2.468 5.5-5.5v-11H16.5zM36.5 6h-6C27.467 6 25 8.468 25 11.5v11h7.879l-1.439-1.439c-.586-.586-.586-1.535 0-2.121s1.535-.586 2.121 0l4 4c.586.586.586 1.535 0 2.121l-4 4C33.268 29.354 32.884 29.5 32.5 29.5s-.768-.146-1.061-.439c-.586-.586-.586-1.535 0-2.121l1.439-1.439H25v11c0 3.032 2.467 5.5 5.5 5.5h6c3.033 0 5.5-2.468 5.5-5.5v-25C42 8.468 39.533 6 36.5 6z"
/>
</svg>
<span id="rotationButton-title">Провести ротацію</span>
</button>
<a href="/constructor" data-route id="constructorButton" style="display: none;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path
d="M 14.5 4 C 12.015 4 10 6.015 10 8.5 L 10 39.5 C 10 41.985 12.015 44 14.5 44 L 22.042969 44 C 22.079969 43.749 22.138516 43.502672 22.228516 43.263672 L 22.283203 43.285156 C 22.353203 42.961156 23.236422 40.161109 23.982422 37.787109 C 24.272422 36.865109 24.779891 36.029703 25.462891 35.345703 L 33.904297 27 L 27.5 27 C 26.672 27 26 26.328 26 25.5 L 26 16 L 38 16 L 38 23.054688 C 38.72 22.587688 39.4975 22.254422 40.3125 22.107422 C 40.5365 22.067422 40.769 22.062922 41 22.044922 L 41 14.5 C 41 13.672 40.328 13 39.5 13 L 34 13 L 34 8.5 C 34 6.015 31.985 4 29.5 4 L 14.5 4 z M 30.5 18 A 1.50015 1.50015 0 1 0 30.5 21 L 33.5 21 A 1.50015 1.50015 0 1 0 33.5 18 L 30.5 18 z M 41.498047 24 C 41.224047 24.001 40.946969 24.025172 40.667969 24.076172 C 39.783969 24.235172 38.939563 24.696156 38.226562 25.410156 L 26.427734 37.208984 C 26.070734 37.565984 25.807969 38.011141 25.667969 38.494141 L 24.097656 43.974609 C 24.025656 44.164609 23.993 44.365406 24 44.566406 C 24.013 44.929406 24.155594 45.288406 24.433594 45.566406 C 24.710594 45.843406 25.067688 45.986 25.429688 46 C 25.630688 46.007 25.834391 45.975344 26.025391 45.902344 L 31.505859 44.332031 C 31.988859 44.192031 32.431062 43.930266 32.789062 43.572266 L 44.589844 31.773438 C 45.303844 31.060437 45.764828 30.216031 45.923828 29.332031 C 45.973828 29.053031 45.997047 28.775953 45.998047 28.501953 C 46.001047 27.307953 45.540687 26.179312 44.679688 25.320312 C 43.820687 24.460313 42.692047 23.998 41.498047 24 z M 22 35 C 22.828 35 23.5 35.672 23.5 36.5 C 23.5 37.328 22.828 38 22 38 C 21.172 38 20.5 37.328 20.5 36.5 C 20.5 35.672 21.172 35 22 35 z"
/>
</svg>
<span>Конструктор</span>
</a>
</div>
<div class="list-controls"></div>
<details open>
<summary>
<span>Багатоквартирні будинки</span>
</summary>
<div id="list-house"></div>
</details>
<details open>
<summary>
<span>Житлові райони</span>
</summary>
<div id="list-homestead"></div>
</details>
</div>

View File

@@ -0,0 +1,130 @@
const Territory = {
init: async () => {
let html = await fetch('/lib/pages/territory/index.html').then((response) => response.text());
app.innerHTML = html;
if (USER.administrator.uuid) document.getElementById('rotationButton').style.display = "";
if (USER.administrator.uuid || (USER.moderator.uuid && USER.moderator.can_add_territory)) document.getElementById("constructorButton").style.display = "";
Territory.house.setHTML();
Territory.homestead.setHTML();
},
house: {
loadAPI: async function () {
let uuid = localStorage.getItem("uuid");
const URL = `${CONFIG.api}houses/list`;
return await fetch(URL, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
}).then((response) => response.json());
},
setHTML: async function () {
let list = await Territory.house.loadAPI();
list.sort((a, b) => b.id - a.id);
console.log(list);
let block_house = document.getElementById('list-house')
block_house.innerHTML = "";
for (let i = 0; i < list.length; i++) {
const element = list[i];
let progress = ((element.entrance.working / element.entrance.quantity) * 100);
let pageURL = () => {
if (USER.administrator.uuid || (USER.moderator.uuid && USER.moderator.can_manager_territory)) return `/territory/manager/house/${element.id}`;
else return `/territory/card/house/${element.id}`
}
block_house.innerHTML += `
<div class="card">
<i style="background-image: url(https://sheep-service.com/cards/house/T${element.id}.webp);"></i>
<div class="contents">
<div class="group" style="background: ${colorGroup(element.group_id)}">
<span>Група №${element.group_id}</span>
</div>
<div class="info">
<div>
<div class="progress" style="width: ${progress}%"></div>
<span>Вільні під'їзди:</span>
<p>${element.entrance.quantity - element.entrance.working} / ${element.entrance.quantity}</p>
</div>
<div>
<p>${element.title} ${element.number}</p>
</div>
</div>
</div>
<a href="${pageURL()}" data-route></a>
</div>
`;
}
}
},
homestead: {
loadAPI: async function () {
let uuid = localStorage.getItem("uuid");
const URL = `${CONFIG.api}homestead/list`;
return await fetch(URL, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
}).then((response) => response.json());
},
setHTML: async function () {
let list = await Territory.homestead.loadAPI();
list.sort((a, b) => b.id - a.id);
console.log(list);
let block_homestead = document.getElementById('list-homestead')
block_homestead.innerHTML = "";
for (let i = 0; i < list.length; i++) {
const element = list[i];
let block_history = () => {
if (element.working) {
return `
<div>
<div class="progress" style="width: 100%"></div>
<p>${element.history.name}</p>
</div>
`
}
return ''
}
let pageURL = () => {
if (USER.administrator.uuid || (USER.moderator.uuid && USER.moderator.can_manager_territory)) return `/territory/manager/homestead/${element.id}`;
else return `/territory/card/homestead/${element.id}`
}
block_homestead.innerHTML += `
<div class="card">
<i style="background-image: url(https://sheep-service.com/cards/homestead/H${element.id}.webp);"></i>
<div class="contents">
<div class="group" style="background: ${colorGroup(element.group_id)}">
<span>Група №${element.group_id}</span>
</div>
<div class="info">
${block_history()}
<div>
<p>${element.title} ${element.number}</p>
</div>
</div>
</div>
<a href="${pageURL()}" data-route></a>
</div>
`;
}
}
}
}

View File

@@ -0,0 +1,247 @@
.page-territory {
width: calc(100% - 40px);
display: flex;
flex-direction: column;
margin: 20px 20px 0 20px;
}
.page-territory>.buttons-list {
padding: 10px;
margin-bottom: 20px;
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
border-radius: 15px;
overflow: auto;
display: flex;
}
.page-territory>.buttons-list>button,
.page-territory>.buttons-list>a {
cursor: pointer;
border-radius: 10px;
padding: 0 10px;
margin-right: 20px;
min-width: fit-content;
min-height: 40px;
background: var(--PrimaryColor);
display: flex;
align-items: center;
justify-content: center;
}
.page-territory>.buttons-list>button>span,
.page-territory>.buttons-list>a>span {
color: var(--PrimaryColorText);
font-size: 14px;
font-weight: normal;
}
.page-territory>.buttons-list>button>svg,
.page-territory>.buttons-list>a>svg {
width: 20px;
height: 20px;
fill: var(--PrimaryColorText);
margin-right: 10px;
}
.page-territory details {
border-radius: 15px;
width: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
margin-bottom: 20px;
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
}
.page-territory>details[disabled] summary,
.page-territory>details.disabled summary {
pointer-events: none;
user-select: none;
}
.page-territory>details summary::-webkit-details-marker,
.page-territory>details summary::marker {
display: none;
content: "";
}
.page-territory summary {
width: calc(100% - 40px);
cursor: pointer;
color: var(--ColorThemes3);
border-radius: var(--border-radius);
font-size: 16px;
font-weight: 300;
padding: 20px;
position: relative;
}
.page-territory summary span {
font-weight: 500;
}
.page-territory #list-house,
.page-territory #list-homestead {
width: 100%;
margin: 0;
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-content: flex-start;
justify-content: center;
overflow-y: auto;
align-items: flex-start;
transition: .3s ease;
}
.page-territory .card {
position: relative;
width: 300px;
height: 200px;
background-color: var(--ColorThemes2);
margin: 0px 10px 20px 10px;
overflow: hidden;
cursor: pointer;
border-radius: 10px;
}
@media (max-width: 2300px) {
.page-territory .card {
width: calc((100% / 5) - 40px);
}
}
@media (max-width: 1960px) {
.page-territory .card {
width: calc((100% / 4) - 40px);
}
}
@media (max-width: 1640px) {
.page-territory .card {
width: calc((100% / 3) - 40px);
}
}
@media (max-width: 1280px) {
.page-territory .card {
width: calc((100% / 2) - 40px);
}
}
@media (max-width: 650px) {
.page-territory .card {
width: 100%;
}
}
@media(hover: hover) {
.page-territory .card:hover {
opacity: 0.8;
}
}
.page-territory .card>i {
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 1;
filter: blur(2px);
/* background-repeat: round; */
background-size: cover;
background-position: center;
background-image: url(https://tm.rozenrod.com/web/img/bg.webp);
}
.page-territory .card>a {
position: absolute;
width: 100%;
height: 100%;
z-index: 10;
}
.page-territory .contents {
position: absolute;
z-index: 2;
background: rgb(64 64 64 / 0.7);
width: 100%;
height: 100%;
font-size: 40px;
font-weight: 500;
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
.page-territory .group {
width: calc(100% - 20px);
max-height: 50px;
border-radius: 7px;
padding: 10px 0;
margin-top: 10px;
display: flex;
background: var(--PrimaryColor);
align-items: center;
flex-direction: column;
justify-content: space-around;
}
.page-territory .group>span {
color: #fff;
font-size: 14px;
font-weight: 400;
}
.page-territory .info {
width: calc(100% - 20px);
margin-bottom: 10px;
}
.page-territory .info>div {
width: 100%;
height: 35px;
margin-top: 5px;
display: flex;
background: var(--ColorThemes0);
align-items: center;
justify-content: center;
font-size: 12px;
color: var(--ColorThemes3);
border-radius: 7px;
position: relative;
overflow: hidden;
}
.page-territory .progress {
background: var(--PrimaryColor);
height: 100%;
position: absolute;
z-index: 1;
left: 0;
}
.page-territory .info>div>span {
color: var(--ColorThemes3);
font-size: 14px;
font-weight: 300;
z-index: 2;
}
.page-territory .info>div>p {
color: var(--ColorThemes3);
font-size: 14px;
font-weight: 400;
padding: 10px;
z-index: 2;
}

View File

@@ -0,0 +1,75 @@
<div class="page-territory_manager">
<div id="territory-info">
<div class="territory-info-image" id="CardPicture">
<img id="info-picture" src="" alt="" style="display: none" />
<div id="map_territory_manager"></div>
<div class="menu-picture">
<a id="menu-picture-error" title="Зображення не знайдено!">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" style="fill: #c14d4d;">
<path
d="M 3.7070312 2.2929688 L 2.2929688 3.7070312 L 26.292969 27.707031 L 27.707031 26.292969 L 26.375 24.960938 C 27.299776 24.784872 28 23.976233 28 23 L 28 7 C 28 5.895 27.105 5 26 5 L 6.4140625 5 L 3.7070312 2.2929688 z M 2.1367188 6.2851562 C 2.0517188 6.5071563 2 6.747 2 7 L 2 23 C 2 24.105 2.895 25 4 25 L 20.851562 25 L 17.851562 22 L 5 22 L 5 15 L 7.2890625 12.710938 C 7.5140625 12.485938 7.7747812 12.317219 8.0507812 12.199219 L 2.1367188 6.2851562 z M 23 8 C 24.105 8 25 8.895 25 10 C 25 11.105 24.105 12 23 12 C 21.895 12 21 11.105 21 10 C 21 8.895 21.895 8 23 8 z M 19 14.001953 C 19.61925 14.001953 20.238437 14.238437 20.710938 14.710938 L 25 19 L 25 22 L 23.414062 22 L 16.707031 15.292969 L 17.289062 14.710938 C 17.761563 14.238437 18.38075 14.001953 19 14.001953 z"
/>
</svg>
</a>
<a id="menu-picture-ok" style="display: none;" title="Зображення знайдено" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" style="fill: #8BC34A;">
<path
d="M 4 5 C 2.895 5 2 5.895 2 7 L 2 23 C 2 24.105 2.895 25 4 25 L 26 25 C 27.105 25 28 24.105 28 23 L 28 7 C 28 5.895 27.105 5 26 5 L 4 5 z M 23 8 C 24.105 8 25 8.895 25 10 C 25 11.105 24.105 12 23 12 C 21.895 12 21 11.105 21 10 C 21 8.895 21.895 8 23 8 z M 9 13.001953 C 9.61925 13.001953 10.238437 13.238437 10.710938 13.710938 L 13.972656 16.972656 L 15 18 L 16.15625 19.15625 C 16.57825 19.57825 17.259641 19.574344 17.681641 19.152344 C 18.104641 18.730344 18.104641 18.044094 17.681641 17.621094 L 16.529297 16.470703 L 17.289062 15.710938 C 18.234063 14.765937 19.765937 14.765937 20.710938 15.710938 L 25 20 L 25 22 L 5 22 L 5 16 L 7.2890625 13.710938 C 7.7615625 13.238437 8.38075 13.001953 9 13.001953 z"
/>
</svg>
</a>
<button title="Створити нове зображення" onclick="Territory_Manager.getScreen()">
<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"/></svg>
</button>
</div>
</div>
<div class="territory-info-text">
<h1>Вулиця:</h1>
<h2 id="info-title">--</h2>
</div>
<div class="territory-info-text">
<h1>Номер:</h1>
<h2 id="info-number">--</h2>
</div>
<div class="territory-info-text">
<h1>Населений пункт:</h1>
<h2 id="info-settlement">--</h2>
</div>
<div class="territory-info-text">
<textarea
name="info-description"
id="info-description"
placeholder="Примітка"
></textarea>
</div>
<a id="editor_button" style="display: none" data-route>Змінити територію</a>
</div>
<div id="territory-entrance"></div>
</div>
<div id="territory-new" style="display: none; opacity: 0">
<div class="mess">
<span>Налаштування</span>
<select id="new-worker-name" name="new-worker-name" required="">
<option value="" selected="" disabled="">Оберіть...</option>
</select>
<div>
<button onclick="Territory_Manager.mess.close()">Закрити</button>
<button
id="new-worker-button"
onclick="Territory_Manager.newWorker()"
style="background: var(--PrimaryColor); color: var(--PrimaryColorText)"
>
Призначити
</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,506 @@
let map_territory, type_territory;
const Territory_Manager = {
init: async (type, id) => {
let html = await fetch('/lib/pages/territory_manager/index.html').then((response) => response.text());
app.innerHTML = html;
type_territory = type;
let sheeps_list = [];
if (Sheeps.sheeps_list.list.length == 0) {
sheeps_list = await Sheeps.sheeps_list.loadAPI();
} else {
sheeps_list = Sheeps.sheeps_list.list;
}
let editor_button = document.getElementById('editor_button');
if (USER.administrator.uuid || (USER.moderator.uuid && USER.moderator.can_add_territory)){
editor_button.style.display = "";
editor_button.setAttribute("href", `/territory/editor/${type}/${id}`)
}
await Territory_Manager.info.setHTML(type, id);
Territory_Manager.entrances.setHTML(type, id);
for (let i = 0; i < sheeps_list.length; i++) {
const element = sheeps_list[i];
if (Territory_Manager.info.list.group_id == element.group_id) {
document.getElementById('new-worker-name').innerHTML += `
<option value="${element.name}">${element.name}</option>
`;
}
}
},
info: {
list: {},
loadAPI: async (url) => {
let uuid = localStorage.getItem("uuid");
Territory_Manager.info.list = await fetch(url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
}).then((response) => response.json());
return Territory_Manager.info.list;
},
setHTML: async (type, id) => {
if (type == "house") {
let url = `${CONFIG.api}house/${id}`;
let data = await Territory_Manager.info.loadAPI(url);
Territory_Manager.map(type, data);
let info_picture = document.getElementById('info-picture');
let info_title = document.getElementById('info-title');
let info_number = document.getElementById('info-number');
let info_settlement = document.getElementById('info-settlement');
let info_description = document.getElementById('info-description');
info_picture.addEventListener("click", (event) => {
let state = info_picture.getAttribute('data-state')
if (state == 'active') info_picture.setAttribute('data-state', '')
else info_picture.setAttribute('data-state', 'active')
});
info_picture.setAttribute("src", ``);
info_title.innerText = data.title;
info_number.innerText = data.number;
info_settlement.innerText = data.settlement;
info_description.value = data.description;
} else if (type == "homestead") {
let url = `${CONFIG.api}homestead/${id}`;
let data = await Territory_Manager.info.loadAPI(url);
Territory_Manager.map(type, data);
let info_picture = document.getElementById('info-picture');
let info_title = document.getElementById('info-title');
let info_number = document.getElementById('info-number');
let info_settlement = document.getElementById('info-settlement');
let info_description = document.getElementById('info-description');
info_picture.addEventListener("click", (event) => {
let state = info_picture.getAttribute('data-state')
if (state == 'active') info_picture.setAttribute('data-state', '')
else info_picture.setAttribute('data-state', 'active')
});
info_picture.setAttribute("src", ``);
info_title.innerText = data.title;
info_number.innerText = data.number;
info_settlement.innerText = data.settlement;
info_description.value = data.description;
}
urlImage = `https://sheep-service.com/cards/${type}/${type == "house" ? "T" : "H"}${id}.webp`;
const checkImage = await fetch(urlImage);
if(checkImage.ok) {
document.getElementById('menu-picture-error').style.display = 'none';
document.getElementById('menu-picture-ok').style.display = '';
document.getElementById('menu-picture-ok').setAttribute("href", urlImage);
} else {
document.getElementById('menu-picture-error').style.display = '';
document.getElementById('menu-picture-ok').style.display = 'none';
}
}
},
entrances: {
list: [],
loadAPI: async (url) => {
let uuid = localStorage.getItem("uuid");
Territory_Manager.entrances.list = await fetch(url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
}).then((response) => response.json());
return Territory_Manager.entrances.list;
},
setHTML: async (type, id) => {
if (type == "house") {
let url = `${CONFIG.api}house/${id}/entrances`;
let listEntrances = await Territory_Manager.entrances.loadAPI(url);
document.getElementById('territory-entrance').innerHTML = "";
for (let i = 0; i < listEntrances.length; i++) {
const element = listEntrances[i];
let name = () => {
if (element.history.name == "Групова")
return `<p>${element.history.name + " " + element.history.group_id}</p>`;
else {
let sheeps_list = Sheeps.sheeps_list.list;
let filtered = sheeps_list.filter(item => item.group_id === Territory_Manager.info.list.group_id);
let sheep = filtered.find(item => item.name === element.history.name);
return `<a href="/sheeps/${sheep.uuid}" data-route="">${element.history.name}</a>`;
}
}
if (element.working) {
document.getElementById('territory-entrance').innerHTML += `
<div class="entrance" data-state="working">
<div id="title">
<h1>${element.title}</h1>
<a href="/territory/card/${type}/${id}" title="Редактор квартир" data-route>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><path d="M 12.5 6 C 8.9280619 6 6 8.9280619 6 12.5 L 6 35.5 C 6 39.071938 8.9280619 42 12.5 42 L 35.5 42 C 39.071938 42 42 39.071938 42 35.5 L 42 12.5 C 42 8.9280619 39.071938 6 35.5 6 L 12.5 6 z M 12.5 9 L 14 9 L 14 15 L 9 15 L 9 12.5 C 9 10.549938 10.549938 9 12.5 9 z M 17 9 L 35.5 9 C 37.450062 9 39 10.549938 39 12.5 L 39 15 L 17 15 L 17 9 z M 9 18 L 14 18 L 14 23 L 9 23 L 9 18 z M 17 18 L 39 18 L 39 23 L 17 23 L 17 18 z M 9 26 L 14 26 L 14 31 L 9 31 L 9 26 z M 17 26 L 39 26 L 39 31 L 17 31 L 17 26 z M 9 34 L 14 34 L 14 39 L 12.5 39 C 10.549938 39 9 37.450062 9 35.5 L 9 34 z M 17 34 L 39 34 L 39 35.5 C 39 37.450062 37.450062 39 35.5 39 L 17 39 L 17 34 z"/></svg>
</a>
</div>
<div>
<h1>Територію опрацьовує: </h1>
${name()}
</div>
<div>
<h1>Територія видана: </h1>
<h2>${formattedDate(element.history.date.start)}</h2>
</div>
<div>
<h1>Варто забрати: </h1>
<h2>${formattedDate(element.history.date.end) ?? formattedDate(element.history.date.start + (1000 * 2629743 * 4))}</h2>
</div>
<div class="edit_working">
<button onclick="Territory_Manager.endWorker('${type}', ${id}, ${element.history.id})" id="end-working-button" style="color: #121214;background: #c14d4d;">Забрати</button>
<button onclick="Territory_Manager.share('${type}', ${i})">Відправити посилання</button>
</div>
</div>
`;
} else {
document.getElementById('territory-entrance').innerHTML += `
<div class="entrance">
<div id="title">
<h1>${element.title}</h1>
</div>
<div>
<h1>Територія не опрацьовується</h1>
</div>
<div>
<h1>Останнє опрацювання: </h1>
<h2>${formattedDate(element.history.date.end) ?? "..."}</h2>
</div>
<div class="edit">
<button onclick="Territory_Manager.newWorker('${type}',${id}, ${i}, 'Групова')" id="group-working-button" style="color: var(--ColorThemes0);background: var(--ColorThemes3);">Призначити груповою</button>
<button onclick="Territory_Manager.mess.open('${type}', ${id}, ${i})">Призначити вісника</button>
</div>
</div>
`;
}
console.log(element);
}
} else if (type == "homestead") {
let url = `${CONFIG.api}homestead/${id}`;
let element = await Territory_Manager.entrances.loadAPI(url);
document.getElementById('territory-entrance').innerHTML = "";
let name = () => {
if (element.history.name == "Групова")
return `<p>${element.history.name + " " + element.history.group_id}</p>`;
else
return `<a href="/sheeps/${element.history.name}" data-route="">${element.history.name}</a>`;
}
if (element.working) {
document.getElementById('territory-entrance').innerHTML += `
<div class="entrance" data-state="working">
<div>
<h1>Територію опрацьовує: </h1>
${name()}
</div>
<div>
<h1>Територія видана: </h1>
<h2>${formattedDate(element.history.date.start)}</h2>
</div>
<div>
<h1>Варто забрати: </h1>
<h2>${formattedDate(element.history.date.end) ?? formattedDate(element.history.date.start + (1000 * 2629743 * 4))}</h2>
</div>
<div class="edit_working">
<button onclick="Territory_Manager.endWorker('${type}', ${id}, ${element.history.id})" id="end-working-button" style="color: #121214;background: #c14d4d;">Забрати</button>
<button onclick="Territory_Manager.share('${type}', ${i})">Відправити посилання</button>
</div>
</div>
`;
} else {
document.getElementById('territory-entrance').innerHTML += `
<div class="entrance">
<div>
<h1>Територія не опрацьовується</h1>
</div>
<div>
<h1>Останнє опрацювання: </h1>
<h2>${formattedDate(element.history.date.end) ?? "..."}</h2>
</div>
<div class="edit">
<button onclick="Territory_Manager.newWorker('${type}',${id}, ${i}, 'Групова')" id="group-working-button" style="color: var(--ColorThemes0);background: var(--ColorThemes3);">Призначити груповою</button>
<button onclick="Territory_Manager.mess.open('${type}',${id}, ${i})">Призначити вісника</button>
</div>
</div>
`;
}
console.log(element);
}
}
},
map: (type, data) => {
map_territory = {};
let center = { lat: 49.5629016, lng: 25.6145625 };
let zoom = 19;
let mytile = L.tileLayer('https://tm.rozenrod.com/webp/{z}/{x}/{y}.webp', {
maxZoom: 20,
minZoom: 15,
tms: true
});
map_territory = L.map('map_territory_manager', {
renderer: L.canvas(),
center,
zoom,
zoomControl: false,
layers: [
mytile
],
});
map_territory.pm.setLang("ua");
if (type == "homestead") {
if (data.geo.lat) map_territory.setView([data.geo.lat, data.geo.lng], 15);
else map_territory.setView([data.points[0][0][0].lat, data.points[0][0][0].lng], 15);
L.polygon(data.points, {
color: colorGroup(data.group_id),
radius: 500,
fillOpacity: 0.3,
dashArray: '20, 15',
dashOffset: '20',
}).on('click', function (e) {
}).addTo(map_territory);
} else if (type == "house") {
map_territory.setView([data.geo.lat, data.geo.lng], 17);
L.polygon(data.points, {
color: "#585858",
fillColor: colorGroup(data.group_id),
fillOpacity: 0.8,
tm_id: `house_${i}`
}).on('click', function (e) {
}).addTo(map_territory);
}
},
mess: {
open: (type, id, number) => {
const block = document.getElementById('territory-new');
const new_worker_button = document.getElementById('new-worker-button');
block.style.display = "";
setTimeout(() => {
block.style.opacity = "1";
}, 100)
new_worker_button.setAttribute("onclick", `Territory_Manager.newWorker('${type}', ${id}, ${number})`);
},
close: () => {
const block = document.getElementById('territory-new');
block.style.opacity = "0";
setTimeout(() => {
block.style.display = "none";
}, 200)
}
},
newWorker: async (type, id, number, name) => {
const uuid = localStorage.getItem('uuid');
const new_worker_button = document.getElementById('new-worker-button');
const group_worker_button = document.getElementById('group-working-button');
let URL, territory_id, data;
const pos = Sheeps.sheeps_list.list.map(e => e.name).indexOf(name ?? document.getElementById("new-worker-name").value);
let sheep = Sheeps.sheeps_list.list[pos];
if (type == "house") {
territory_id = Territory_Manager.entrances.list[number].id;
data = {
name: name ?? document.getElementById("new-worker-name").value,
group_id: Territory_Manager.info.list.group_id,
sheep_id: sheep ? sheep.id : null
}
URL = `${CONFIG.api}history/entrance/${territory_id}`;
}
else if (type == "homestead") {
territory_id = Territory_Manager.info.list.id;
data = {
name: name ?? document.getElementById("new-worker-name").value,
group_id: Territory_Manager.info.list.group_id,
sheep_id: sheep ? sheep.id : null
}
URL = `${CONFIG.api}history/homestead/${territory_id}`;
}
console.log(territory_id, data);
await fetch(URL, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
body: JSON.stringify(data)
})
.then(response => {
if (response.status == 200) {
Territory_Manager.mess.close();
Territory_Manager.entrances.list = [];
Territory_Manager.entrances.setHTML(type, id);
return response.json()
} else {
console.log('err');
new_worker_button.innerText = "Помилка";
group_worker_button.innerText = "Помилка";
return
}
})
},
endWorker: async (type, id, territory_id) => {
const end_working_button = document.getElementById('end-working-button');
let uuid = localStorage.getItem('uuid');
let URL = "";
if (type == "house") {
URL = `${CONFIG.api}history/entrance/${territory_id}`;
}
else if (type == "homestead") {
URL = `${CONFIG.api}history/homestead/${territory_id}`;
}
await fetch(URL, {
method: 'PUT',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
data: JSON.stringify({})
})
.then(response => {
if (response.status == 200) {
Territory_Manager.entrances.list = [];
Territory_Manager.entrances.setHTML(type, id);
return response.json()
} else {
console.log('err');
end_working_button.innerText = "Помилка";
return
}
})
},
share: async (type, number) => {
let territory, id;
if (type == "house") {
territory = Territory_Manager.entrances.list[number];
id = Territory_Manager.entrances.list[number].id;
}
else if (type == "homestead") {
territory = Territory_Manager.info.list;
id = Territory_Manager.info.list.id;
}
let description = () => {
if (Territory_Manager.info.list.description && Territory_Manager.info.list.description.length > 0) {
return `\n\nДодатково:\n${Territory_Manager.info.list.description}`;
} else {
return '';
}
}
let sheepUuid = () => {
const pos = Sheeps.sheeps_list.list.map(e => e.name).indexOf(territory.history.name);
let sheep = Sheeps.sheeps_list.list[pos];
if (sheep) {
return `\n\nПосилання на Sheep-Service:\n• https://sheep-service.com/?uuid=${sheep.uuid}`;
} else {
return '';
}
}
if (navigator.share) {
console.log("Congrats! Your browser supports Web Share API")
try {
let url;
if(type == 'house') url = `https://sheep-service.com/cards/house/T${Territory_Manager.info.list.id}.webp`;
if(type == 'homestead') url = `https://sheep-service.com/cards/homestead/H${Territory_Manager.info.list.id}.webp`;
const response = await fetch(url);
const blob = await response.blob();
const file = new File([blob], `${type == "house" ? "E" + id : "H" + id}.webp`, { type: blob.type });
await navigator.share({
text: `Територія:\n${type == "house" ? "E" + id : "H" + id}\n\nНаселений пункт:\n${Territory_Manager.info.list.settlement}\n\nВулиця:\n${Territory_Manager.info.list.title + " " + Territory_Manager.info.list.number}${description()}\n\nПризначення:\nЗ ${formattedDate(territory.history.date.start)}\n • До ${formattedDate(territory.history.date.end) ?? formattedDate(territory.history.date.start + (1000 * 2629743 * 4))}${sheepUuid()}`,
files: [file]
});
console.log('Успешно отправлено!');
} catch (error) {
console.error('Ошибка при отправке:', error);
}
} else {
console.log("Sorry! Your browser does not support Web Share API")
}
},
getScreen: async () => {
const center = map_territory.getCenter();
console.log(center.lat, center.lng);
let lat = center.lat;
let lng = center.lng;
let wayId = Territory_Manager.info.list.osm_id;
let zoom = map_territory.getZoom() + 2 ?? 17;
let address = Territory_Manager.info.list.title;
let number = Territory_Manager.info.list.number;
let id = Territory_Manager.info.list.id;
let url = `https://sheep-service.com/api/generator/cards?lat=${lat}&lng=${lng}&type=${type_territory}&wayId=${wayId}&zoom=${zoom}&address=${address}&number=${number}&id=${id}`;
let uuid = localStorage.getItem("uuid");
let result = await fetch(url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
}).then((response) => response.json());
urlImage = `https://sheep-service.com/cards/${type}/${type == "house" ? "T" : "H"}${id}.webp`;
if(result) {
document.getElementById('menu-picture-error').style.display = 'none';
document.getElementById('menu-picture-ok').style.display = '';
document.getElementById('menu-picture-ok').setAttribute("href", urlImage);
} else {
document.getElementById('menu-picture-error').style.display = '';
document.getElementById('menu-picture-ok').style.display = 'none';
}
}
}

View File

@@ -0,0 +1,418 @@
.page-territory_manager {
width: calc(100% - 18px);
display: flex;
flex-direction: row;
margin: 20px 9px 0 9px;
justify-content: space-between;
position: relative;
}
#territory-info,
#territory-entrance {
width: 100%;
min-height: calc(100vh - 40px - 50px);
height: fit-content;
margin: 0 10px 15px;
border-radius: 15px;
display: flex;
flex-direction: column;
overflow: hidden;
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
transition: all .2sease 0s;
}
#territory-info {
position: sticky;
overflow: auto;
top: 20px;
}
#territory-info>.territory-info-image {
width: calc(100% - 20px);
min-height: 200px;
margin: 10px;
border-radius: 10px;
background: var(--ColorThemes0);
position: relative;
display: flex;
overflow: hidden;
background-position: 50%;
background-size: cover;
justify-content: center;
aspect-ratio: 1147 / 751;
max-height: 300px;
}
#territory-info>.territory-info-image>img {
width: 100%;
object-fit: cover;
z-index: 3;
cursor: pointer;
}
#territory-info>.territory-info-image>#map_territory_manager {
width: 100%;
height: 100%;
}
#territory-info>.territory-info-image>img[data-state="active"] {
object-fit: contain;
}
#territory-info>.territory-info-image>.menu-picture {
position: absolute;
right: 10px;
top: 10px;
z-index: 999;
background: var(--ColorThemes0);
border: 1px solid var(--ColorThemes2);
padding: 5px;
border-radius: 6px;
min-width: 70px;
display: flex;
justify-content: space-between;
}
#territory-info>.territory-info-image>.menu-picture>a {
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
#territory-info>.territory-info-image>.menu-picture>a>svg {
width: 22px;
height: 22px;
}
#territory-info>.territory-info-image>.menu-picture>button {
width: 30px;
height: 30px;
border-radius: 2px;
background: var(--PrimaryColor);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
#territory-info>.territory-info-image>.menu-picture>button>svg {
width: 20px;
height: 20px;
fill: var(--PrimaryColorText);
}
#territory-info>.territory-info-text {
width: calc(100% - 40px);
display: flex;
flex-direction: row;
margin: 10px;
border-radius: 10px;
position: relative;
align-items: center;
background: var(--ColorThemes0);
padding: 10px;
}
#territory-info>.territory-info-text>h1 {
font-size: 14px;
font-weight: 400;
color: var(--ColorThemes3);
}
#territory-info>.territory-info-text>h2 {
font-size: 14px;
font-weight: 300;
color: var(--ColorThemes3);
opacity: 0.8;
margin: 0 7px;
}
#territory-info>.territory-info-text>textarea {
width: 100%;
min-height: 100px;
background: var(--ColorThemes0);
color: var(--ColorThemes3);
font-size: 14px;
resize: vertical;
border-radius: 0;
}
#territory-info>#editor_button {
width: calc(100% - 20px);
font-size: 13px;
font-weight: 400;
min-height: 40px;
display: flex;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
cursor: pointer;
border: 0;
position: relative;
border-radius: 8px;
margin: 20px 10px 10px 10px;
align-items: center;
justify-content: center;
}
#territory-entrance>.entrance {
width: calc(100% - 40px);
color: var(--ColorThemes3);
background-color: var(--ColorThemes2);
border: 1px solid var(--ColorThemes0);
box-shadow: var(--shadow-l1);
display: flex;
flex-direction: column;
align-items: flex-start;
margin: 10px;
padding: 10px;
min-height: 100px;
justify-content: center;
position: relative;
border-radius: 10px;
}
#territory-entrance>.entrance[data-state="working"] {
border: 1px solid var(--PrimaryColor);
}
#territory-entrance>.entrance>#title {
display: flex;
align-items: center;
height: 30px;
margin-bottom: 10px;
justify-content: space-between;
}
#territory-entrance>.entrance>#title>h1 {
width: 100%;
height: 30px;
background: var(--ColorThemes0);
color: var(--ColorThemes3);
border-radius: 6px;
font-size: 16px;
font-weight: 400;
display: flex;
align-items: center;
justify-content: center;
}
#territory-entrance>.entrance>#title>a {
min-height: 30px;
min-width: 30px;
padding: 0;
margin: 0 0 0 10px;
background: var(--PrimaryColor);
display: flex;
align-items: center;
justify-content: center;
}
#territory-entrance>.entrance>#title>a>svg {
width: 25px;
height: 25px;
fill: var(--PrimaryColorText);
}
#territory-entrance>.entrance>div {
display: flex;
margin-bottom: 10px;
flex-direction: row;
align-items: center;
width: 100%;
}
#territory-entrance>.entrance>div>h1 {
font-size: 14px;
font-weight: 400;
color: var(--ColorThemes3);
}
#territory-entrance>.entrance>div>h2,
#territory-entrance>.entrance>div>a,
#territory-entrance>.entrance>div>p {
font-size: 14px;
font-weight: 300;
color: var(--ColorThemes3);
opacity: 0.8;
margin: 0 7px;
display: flex;
align-items: flex-start;
}
#territory-entrance>.entrance>div>a,
#territory-entrance>.entrance>div>p {
color: var(--ColorThemes0);
background: var(--ColorThemes3);
border-radius: 6px;
padding: 2px 5px;
font-weight: 400;
opacity: 1;
}
.date_old {
background: var(--ColorThemes1) !important;
color: var(--ColorThemes3) !important;
font-size: 18px !important;
padding: 5px 10px !important;
}
#territory-entrance>.entrance>.edit_working,
#territory-entrance>.entrance>.edit {
margin-bottom: 0;
}
#territory-entrance>.entrance>.edit_working>button,
#territory-entrance>.entrance>.edit>button {
width: 100%;
font-size: 13px;
font-weight: 400;
min-height: 40px;
display: flex;
padding: 2px 10px;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
cursor: pointer;
border: 0;
position: relative;
border-radius: 8px;
align-items: center;
justify-content: center;
}
#territory-entrance>.entrance>.edit_working>button:nth-child(2n),
#territory-entrance>.entrance>.edit>button:nth-child(2n) {
margin-left: 5px;
}
#territory-entrance>.entrance>.edit_working>button:nth-child(2n-1),
#territory-entrance>.entrance>.edit>button:nth-child(2n-1) {
margin-right: 5px;
}
#territory-entrance>.entrance>button>span {
display: none;
}
#territory-entrance>.entrance>button>svg {
width: 20px;
height: 20px;
fill: var(--PrimaryColorText);
}
#territory-entrance>.entrance>.apartment_button {
width: 100%;
font-size: 13px;
font-weight: 400;
min-height: 40px;
display: flex;
background: var(--PrimaryColor);
color: var(--PrimaryColorText);
cursor: pointer;
border: 0;
position: relative;
border-radius: 8px;
margin: 20px 0 2px 0;
align-items: center;
justify-content: center;
}
#territory-new {
display: flex;
width: 100%;
height: 100%;
left: 0;
top: 0;
position: fixed;
z-index: 999;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
background: rgb(31 31 33 / 41%);
transition: all .2s ease 0s;
}
#territory-new>.mess {
display: flex;
width: 300px;
height: 200px;
border: 1px solid var(--ColorThemes2);
background: var(--ColorThemes0);
box-shadow: 0px 2px 3px rgb(0 0 0 / 5%), 0px 5px 15px rgb(0 0 0 / 5%), 0px 4px 8px rgb(0 0 0 / 5%), 0px 0px 1px rgb(0 0 0 / 5%);
position: absolute;
top: 50%;
left: 50%;
margin-top: -110px;
margin-left: -160px;
flex-direction: column;
align-items: center;
z-index: 9999;
padding: 10px;
border-radius: 15px;
justify-content: space-between;
}
#territory-new>.mess>span {
color: var(--ColorThemes3);
font-size: 18px;
text-align: center;
}
#territory-new>.mess>select {
width: 100%;
min-width: 140px;
padding: 0 5px;
border-radius: 6px;
height: 30px;
background-color: var(--ColorThemes2);
color: var(--ColorThemes3);
}
#territory-new>.mess>div {
width: 100%;
display: flex;
justify-content: space-between;
flex-direction: row;
}
#territory-new>.mess>div>button {
text-decoration: none;
font-size: 15px;
font-weight: 400;
height: 35px;
color: var(--ColorThemes0);
padding: 2px 10px;
background: var(--ColorThemes3);
cursor: pointer;
border: 0;
margin-bottom: 5px;
margin-left: 0;
width: calc(50% - 5px);
border-radius: 10px;
}
@media (max-width: 1000px),
(max-height: 540px) {
.page-territory_manager {
flex-direction: column;
justify-content: flex-start;
}
#territory-info,
#territory-entrance {
width: calc(100% - 20px);
height: fit-content;
min-height: 100px;
}
#territory-info {
position: relative;
top: auto;
}
}