Додані повідомлення та перепрацьована структура застосунку та api

This commit is contained in:
2026-03-15 00:25:10 +02:00
parent 85483b85bb
commit 4bc9c11512
101 changed files with 5763 additions and 2546 deletions

View File

@@ -2,7 +2,7 @@
<form id="page-auth-form">
<div>
<input
type="text"
type="password"
name="uuid"
id="auth-forms-uuid"
placeholder="UUID"

View File

@@ -6,12 +6,10 @@ const Auth = {
document.getElementById("page-auth-form").addEventListener("submit", async function (event) {
event.preventDefault();
let uuid = document.getElementById("auth-forms-uuid").value;
uuid = uuid.replace("https://sheep-service.com/?uuid=", "");
uuid = uuid.replace("https://sheep-service.com?uuid=", "");
uuid = uuid.replace("https://sheep-service.com?/hash=", "");
uuid = uuid.replace("https://sheep-service.com?hash=", "");
const uuid = document
.getElementById("auth-forms-uuid")
.value
.replace(/^https?:\/\/sheep-service\.com\/?\?(uuid|hash)=/, "");
console.log(uuid);
@@ -38,11 +36,47 @@ const Auth = {
console.log("USER Info: ", USER);
if (USER.possibilities.can_view_sheeps) document.getElementById("li-sheeps").style.display = "";
if (USER.possibilities.can_add_schedule) document.getElementById("li-schedule").style.display = "";
if (USER.possibilities.can_manager_territory) document.getElementById("li-territory").style.display = "";
if (USER.possibilities.can_view_stand) document.getElementById("li-stand").style.display = "";
document.getElementById("li-options").style.display = "";
if (USER.possibilities.can_view_stand) {
newMenuItems({
id: 'menu-stand',
title: 'Графік стенду',
icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M 6.9707031 4 C 6.8307031 4 6.6807813 4.039375 6.5507812 4.109375 L 2.5507812 6.109375 C 2.0607813 6.349375 1.859375 6.9492188 2.109375 7.4492188 C 2.349375 7.9392188 2.9492187 8.140625 3.4492188 7.890625 L 6.4902344 6.3691406 L 12.5 20.650391 C 12.73 21.180391 13.040156 21.650547 13.410156 22.060547 C 12.040156 22.340547 11 23.56 11 25 C 11 26.65 12.35 28 14 28 C 15.65 28 17 26.65 17 25 C 17 24.52 16.869922 24.070156 16.669922 23.660156 C 17.479922 23.740156 18.319141 23.639062 19.119141 23.289062 L 26.400391 20.099609 C 26.910391 19.889609 27.159219 19.310781 26.949219 18.800781 C 26.749219 18.290781 26.160391 18.040234 25.650391 18.240234 C 25.630391 18.250234 25.619609 18.259531 25.599609 18.269531 L 18.320312 21.460938 C 16.770312 22.130938 14.999609 21.429141 14.349609 19.869141 L 7.9199219 4.609375 C 7.7599219 4.229375 7.3807031 3.99 6.9707031 4 z M 21.359375 8.0605469 C 21.229375 8.0605469 21.100703 8.090625 20.970703 8.140625 L 13.609375 11.269531 C 13.099375 11.479531 12.860078 12.070078 13.080078 12.580078 L 16.029297 19.179688 C 16.249297 19.689688 16.829844 19.930937 17.339844 19.710938 L 24.710938 16.589844 C 25.210938 16.369844 25.450234 15.789297 25.240234 15.279297 L 22.279297 8.6699219 C 22.119297 8.2899219 21.749375 8.0605469 21.359375 8.0605469 z M 14 24 C 14.56 24 15 24.44 15 25 C 15 25.56 14.56 26 14 26 C 13.44 26 13 25.56 13 25 C 13 24.44 13.44 24 14 24 z"/></svg>`,
href: '/stand'
});
}
if (USER.possibilities.can_view_schedule) {
newMenuItems({
id: 'menu-schedule',
title: 'Графіки зібрань',
icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M47 23c3.314 0 6 2.686 6 6v17c0 3.309-2.691 6-6 6H17c-3.309 0-6-2.691-6-6V29c0-3.314 2.686-6 6-6H47zM22 45v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C21.552 46 22 45.552 22 45zM22 38v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C21.552 39 22 38.552 22 38zM30 45v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C29.552 46 30 45.552 30 45zM30 38v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C29.552 39 30 38.552 30 38zM30 31v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C29.552 32 30 31.552 30 31zM38 45v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C37.552 46 38 45.552 38 45zM38 38v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C37.552 39 38 38.552 38 38zM38 31v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C37.552 32 38 31.552 38 31zM46 38v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C45.552 39 46 38.552 46 38zM46 31v-2c0-.552-.448-1-1-1h-2c-.552 0-1 .448-1 1v2c0 .552.448 1 1 1h2C45.552 32 46 31.552 46 31zM17 20c-2.308 0-4.407.876-6 2.305V18c0-3.309 2.691-6 6-6h30c3.309 0 6 2.691 6 6v4.305C51.407 20.876 49.308 20 47 20H17z"/></svg>`,
href: '/schedule'
});
}
if (USER.possibilities.can_view_sheeps) {
newMenuItems({
id: 'menu-sheeps',
title: 'Вісники',
icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M 42.5 14 C 37.813 14 34 18.038 34 23 C 34 27.962 37.813 32 42.5 32 C 47.187 32 51 27.962 51 23 C 51 18.038 47.187 14 42.5 14 z M 21.5 17 C 16.813 17 13 21.038 13 26 C 13 30.962 16.813 35 21.5 35 C 26.187 35 30 30.962 30 26 C 30 21.038 26.187 17 21.5 17 z M 42.5 18 C 44.981 18 47 20.243 47 23 C 47 25.757 44.981 28 42.5 28 C 40.019 28 38 25.757 38 23 C 38 20.243 40.019 18 42.5 18 z M 42.498047 34.136719 C 37.579021 34.136719 33.07724 35.947963 30.054688 38.962891 C 27.67058 37.796576 24.915421 37.136719 22 37.136719 C 14.956 37.136719 8.8129375 40.942422 6.7109375 46.607422 C 5.7409375 49.220422 7.7121406 52 10.494141 52 L 33.505859 52 C 35.43112 52 36.95694 50.674804 37.404297 49 L 53.431641 49 C 56.437641 49 59.121453 45.844281 57.564453 42.613281 C 55.084453 37.463281 49.169047 34.136719 42.498047 34.136719 z M 42.5 38.136719 C 47.565 38.136719 52.171937 40.633609 53.960938 44.349609 C 54.119938 44.687609 53.741687 45 53.429688 45 L 36.544922 45 C 35.777257 43.585465 34.746773 42.317451 33.503906 41.234375 C 35.78496 39.306575 39.034912 38.136719 42.5 38.136719 z" /></svg>`,
href: '/sheeps',
hidden: true
});
await Sheeps.sheeps_list.loadAPI();
}
if (USER.possibilities.can_manager_territory) {
newMenuItems({
id: 'menu-territory',
title: 'Території',
icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M24 2H14c-.55 0-1 .45-1 1v4l3.6 2.7c.25.19.4.49.4.8V14h8V3C25 2.45 24.55 2 24 2zM15.5 7C15.22 7 15 6.78 15 6.5v-2C15 4.22 15.22 4 15.5 4h2C17.78 4 18 4.22 18 4.5v2C18 6.78 17.78 7 17.5 7h-1.17H15.5zM23 4.5v2C23 6.78 22.78 7 22.5 7h-2C20.22 7 20 6.78 20 6.5v-2C20 4.22 20.22 4 20.5 4h2C22.78 4 23 4.22 23 4.5zM22.5 12h-2c-.28 0-.5-.22-.5-.5v-2C20 9.22 20.22 9 20.5 9h2C22.78 9 23 9.22 23 9.5v2C23 11.78 22.78 12 22.5 12zM1 11.51V27c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V11.51c0-.32-.16-.62-.42-.81l-6-4.28C8.41 6.29 8.2 6.23 8 6.23S7.59 6.29 7.42 6.42l-6 4.28C1.16 10.89 1 11.19 1 11.51zM6.5 20h-2C4.22 20 4 19.78 4 19.5v-2C4 17.22 4.22 17 4.5 17h2C6.78 17 7 17.22 7 17.5v2C7 19.78 6.78 20 6.5 20zM7 22.5v2C7 24.78 6.78 25 6.5 25h-2C4.22 25 4 24.78 4 24.5v-2C4 22.22 4.22 22 4.5 22h2C6.78 22 7 22.22 7 22.5zM6.5 15h-2C4.22 15 4 14.78 4 14.5v-2C4 12.22 4.22 12 4.5 12h2C6.78 12 7 12.22 7 12.5v2C7 14.78 6.78 15 6.5 15zM9.5 17h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2C9.22 20 9 19.78 9 19.5v-2C9 17.22 9.22 17 9.5 17zM9 14.5v-2C9 12.22 9.22 12 9.5 12h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2C9.22 15 9 14.78 9 14.5zM9.5 22h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2C9.22 25 9 24.78 9 24.5v-2C9 22.22 9.22 22 9.5 22zM17 17v10c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V17c0-.55-.45-1-1-1H18C17.45 16 17 16.45 17 17zM19.5 18h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2c-.28 0-.5-.22-.5-.5v-2C19 18.22 19.22 18 19.5 18zM27 18.5v2c0 .28-.22.5-.5.5h-2c-.28 0-.5-.22-.5-.5v-2c0-.28.22-.5.5-.5h2C26.78 18 27 18.22 27 18.5zM26.5 26h-2c-.28 0-.5-.22-.5-.5v-2c0-.28.22-.5.5-.5h2c.28 0 .5.22.5.5v2C27 25.78 26.78 26 26.5 26zM19.5 23h2c.28 0 .5.22.5.5v2c0 .28-.22.5-.5.5h-2c-.28 0-.5-.22-.5-.5v-2C19 23.22 19.22 23 19.5 23z"/></svg>`,
href: '/territory',
hidden: true
});
}
newMenuItems({
id: 'menu-options',
title: 'Опції',
icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 172 172"><path d="M75.18001,14.33333c-3.43283,0 -6.36736,2.42659 -7.02669,5.79492l-2.39355,12.28971c-5.8821,2.22427 -11.32102,5.33176 -16.097,9.25228l-11.78581,-4.05924c-3.2465,-1.118 -6.81841,0.22441 -8.53841,3.19141l-10.80599,18.72852c-1.71283,2.97417 -1.08945,6.74999 1.49772,9.00033l9.44824,8.21647c-0.49137,3.0197 -0.81185,6.09382 -0.81185,9.25228c0,3.15846 0.32048,6.23258 0.81185,9.25228l-9.44824,8.21647c-2.58717,2.25033 -3.21055,6.02616 -1.49772,9.00032l10.80599,18.72852c1.71283,2.97417 5.29191,4.31623 8.53841,3.2054l11.78581,-4.05924c4.77441,3.91806 10.21756,7.01501 16.097,9.23828l2.39355,12.28972c0.65933,3.36833 3.59386,5.79492 7.02669,5.79492h21.63998c3.43283,0 6.36735,-2.42659 7.02669,-5.79492l2.39356,-12.28972c5.88211,-2.22427 11.32102,-5.33176 16.097,-9.25227l11.78581,4.05924c3.2465,1.118 6.81841,-0.21724 8.53841,-3.1914l10.80599,-18.74252c1.71284,-2.97417 1.08945,-6.73599 -1.49772,-8.98633l-9.44824,-8.21647c0.49137,-3.0197 0.81185,-6.09382 0.81185,-9.25228c0,-3.15846 -0.32048,-6.23258 -0.81185,-9.25228l9.44824,-8.21647c2.58717,-2.25033 3.21056,-6.02616 1.49772,-9.00033l-10.80599,-18.72852c-1.71283,-2.97417 -5.29191,-4.31624 -8.53841,-3.2054l-11.78581,4.05924c-4.7744,-3.91806 -10.21755,-7.01501 -16.097,-9.23828l-2.39356,-12.28971c-0.65933,-3.36833 -3.59385,-5.79492 -7.02669,-5.79492zM86,57.33333c15.83117,0 28.66667,12.8355 28.66667,28.66667c0,15.83117 -12.8355,28.66667 -28.66667,28.66667c-15.83117,0 -28.66667,-12.8355 -28.66667,-28.66667c0,-15.83117 12.8355,-28.66667 28.66667,-28.66667z"/></svg>`,
href: '/options'
});
});
}
}

View File

@@ -28,4 +28,12 @@
<div id="home-group-territory-list"></div>
</details>
<details id="details-joint-territory" open style="display: none">
<summary>
<span>Тимчасові території</span>
</summary>
<div id="home-joint-territory-list"></div>
</details>
</div>

View File

@@ -9,6 +9,8 @@ const Home = {
Home.group.house.setHTML();
Home.group.homestead.setHTML();
Home.joint.homestead.setHTML();
}
},
personal: {
@@ -119,6 +121,34 @@ const Home = {
}
}
},
joint: {
homestead: {
list: [],
loadAPI: async () => {
const uuid = localStorage.getItem("uuid");
const URL = `${CONFIG.api}homestead/list?mode=joint`;
const res = await fetch(URL, {
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
});
Home.joint.homestead.list = await res.json();
return Home.joint.homestead.list;
},
setHTML: async () => {
const list = Home.joint.homestead.list.length > 0
? Home.joint.homestead.list
: await Home.joint.homestead.loadAPI();
if (USER.possibilities.can_view_territory && list.length)
document.getElementById('details-joint-territory').style.display = "";
list.sort((a, b) => b.id - a.id);
Home.renderCards(list, "homestead", "joint");
}
}
},
renderCards: (list, type, block) => {
const container = document.getElementById(`home-${block}-territory-list`);
const fragment = document.createDocumentFragment();
@@ -126,7 +156,7 @@ const Home = {
for (const el of list) {
const card = document.createElement('app-territory-card');
card.image = `${CONFIG.web}cards/${type}/${type === "house" ? "T" : "H"}${el.id}.webp`;
card.address = `${el.title} ${el.number})`;
card.address = `${el.title} ${el.number}`;
card.link = `/territory/card/${type}/${el.id}`;
fragment.appendChild(card);
}

View File

@@ -91,8 +91,9 @@
.page-home #home-personal-territory-list,
.page-home #home-group-territory-list {
width: 100%;
.page-home #home-group-territory-list,
.page-home #home-joint-territory-list {
width: calc(100% - 20px);
margin: 0;
display: flex;
flex-wrap: wrap;

View File

@@ -1,3 +1,3 @@
<div class="page-schedule-constructor">
</div>
</div>

View File

@@ -3,6 +3,12 @@ const Schedule_constructor = {
let html = await fetch('/lib/pages/schedule/constructor/index.html').then((response) => response.text());
app.innerHTML = html;
const newItem = document.createElement('nav-item');
const uniqueId = `nav-dynamic-${Date.now()}`;
newItem.setAttribute('id', uniqueId);
newItem.setAttribute('title', 'Динамічний Пункт');
newItem.setAttribute('icon', '/img/0.svg');
newItem.setAttribute('click', `alert('${uniqueId} clicked!')`);
document.querySelector('navigation-container').appendChild(newItem);
}
}

View File

@@ -121,7 +121,7 @@
id="sheep-editor-can_view_sheeps"
type="checkbox"
/>
<label for="sheep-editor-can_view_sheeps"> View Sheeps </label>
<label for="sheep-editor-can_view_sheeps"> Перегляд списку вісників </label>
</div>
<div class="checkbox">
<input
@@ -130,7 +130,16 @@
id="sheep-editor-can_add_sheeps"
type="checkbox"
/>
<label for="sheep-editor-can_add_sheeps"> Create Sheeps </label>
<label for="sheep-editor-can_add_sheeps"> Додавання вісників </label>
</div>
<div class="checkbox">
<input
name="can_manager_sheeps"
class="custom-checkbox"
id="sheep-editor-can_manager_sheeps"
type="checkbox"
/>
<label for="sheep-editor-can_manager_sheeps"> Керування дозволами вісників </label>
</div>
<div class="checkbox">
<input
@@ -140,7 +149,7 @@
type="checkbox"
/>
<label for="sheep-editor-can_add_territory">
Create Territory
Створення територій
</label>
</div>
<div class="checkbox">
@@ -151,7 +160,18 @@
type="checkbox"
/>
<label for="sheep-editor-can_manager_territory">
Manager Territory
Керування територіями
</label>
</div>
<div class="checkbox">
<input
name="can_joint_territory"
class="custom-checkbox"
id="sheep-editor-can_joint_territory"
type="checkbox"
/>
<label for="sheep-editor-can_joint_territory">
Спільний доступ до території
</label>
</div>
<div class="checkbox">
@@ -161,7 +181,7 @@
id="sheep-editor-can_add_stand"
type="checkbox"
/>
<label for="sheep-editor-can_add_stand"> Create Stand </label>
<label for="sheep-editor-can_add_stand"> Створення стендів </label>
</div>
<div class="checkbox">
<input
@@ -170,7 +190,7 @@
id="sheep-editor-can_manager_stand"
type="checkbox"
/>
<label for="sheep-editor-can_manager_stand"> Manager Stand </label>
<label for="sheep-editor-can_manager_stand"> Керування стендами </label>
</div>
<div class="checkbox">
<input
@@ -179,7 +199,7 @@
id="sheep-editor-can_add_schedule"
type="checkbox"
/>
<label for="sheep-editor-can_add_schedule"> Create Schedule </label>
<label for="sheep-editor-can_add_schedule"> Створення розкладу зібрань </label>
</div>
</div>
</div>
@@ -194,7 +214,7 @@
id="sheep-editor-can_view_schedule"
type="checkbox"
/>
<label for="sheep-editor-can_view_schedule"> View Schedule </label>
<label for="sheep-editor-can_view_schedule"> Перегляд розкладу зібрань </label>
</div>
<div class="checkbox">
<input
@@ -203,7 +223,7 @@
id="sheep-editor-can_view_stand"
type="checkbox"
/>
<label for="sheep-editor-can_view_stand"> View Stand </label>
<label for="sheep-editor-can_view_stand"> Перегляд стендів </label>
</div>
<div class="checkbox">
<input
@@ -213,7 +233,7 @@
type="checkbox"
/>
<label for="sheep-editor-can_view_territory">
View Territory
Перегляд територій
</label>
</div>
</div>

View File

@@ -40,6 +40,8 @@ const SheepsEvents = {
const sheepEditorButton = document.getElementById('sheep-editor-button');
const form = event.target;
const formData = new FormData(form);
console.log(formData);
const uuidValue = form.elements["uuid"].value;
const sheep = Sheeps.sheeps_list.list.find(item => item.uuid === uuidValue);
@@ -50,7 +52,7 @@ const SheepsEvents = {
sheep.name = form.elements["name"].value;
sheep.group_id = Number(formData.get("group_id"));
sheep.mode = formData.get("mode");
sheep.mode = formData.get("mode") || sheep.mode;
sheep.mode_title = ["Користувач", "Модератор", "Адміністратор"][sheep.mode] || "Користувач";
const permKeys = [
@@ -59,8 +61,10 @@ const SheepsEvents = {
"can_view_stand",
"can_view_territory",
"can_add_sheeps",
"can_manager_sheeps",
"can_add_territory",
"can_manager_territory",
"can_joint_territory",
"can_add_stand",
"can_manager_stand",
"can_add_schedule"
@@ -73,8 +77,7 @@ const SheepsEvents = {
try {
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}sheep`;
const response = await fetch(URL, {
const response = await fetch(`${CONFIG.api}sheep`, {
method: 'PUT',
headers: {
"Content-Type": "application/json",
@@ -84,26 +87,23 @@ const SheepsEvents = {
});
if (response.ok) {
sheepEditorButton.innerText = "Успішно збережено";
sheepEditorButton.innerText = "Зберегти";
Notifier.success("Успішно збережено!", {timeout: 2000})
const data = await response.json();
console.log(data);
Sheeps.sheeps_list.list = [];
await Sheeps.sheeps_list.setHTML();
setTimeout(() => {
sheepEditorButton.innerText = "Зберегти";
}, 3000);
} else {
sheepEditorButton.innerText = "Зберегти";
console.error('Помилка збереження');
sheepEditorButton.innerText = "Помилка збереження";
Notifier.error("Помилка збереження!", {timeout: 3000});
}
} catch (err) {
console.error(err);
sheepEditorButton.innerText = "Помилка збереження";
Notifier.error("Помилка збереження!", {timeout: 3000});
}
// тот же код, что был в _onSheepEditorSubmit, но обращаемся к editorForm
return;
}
@@ -120,8 +120,7 @@ const SheepsEvents = {
try {
const uuid = localStorage.getItem('uuid');
const URL = `${CONFIG.api}sheep`;
const response = await fetch(URL, {
const response = await fetch(`${CONFIG.api}sheep`, {
method: 'POST',
headers: {
"Content-Type": "application/json",
@@ -131,7 +130,7 @@ const SheepsEvents = {
});
if (response.ok) {
sheepAddedsButton.innerText = "Вісника додано";
sheepAddedsButton.innerText = "Додати";
const data = await response.json();
console.log(data);
@@ -141,17 +140,14 @@ const SheepsEvents = {
Sheeps.addeds.close();
await Sheeps.editor.setHTML(data.id, randomNumber);
setTimeout(() => {
sheepAddedsButton.innerText = "Додати";
}, 3000);
} else {
sheepEditorButton.innerText = "Додати";
console.error('Помилка додавання');
sheepAddedsButton.innerText = "Помилка додавання";
Notifier.error("Помилка додавання!", {timeout: 3000});
}
} catch (err) {
console.error(err);
sheepAddedsButton.innerText = "Помилка додавання";
Notifier.error("Помилка додавання!", {timeout: 3000});
}
return;
}
@@ -176,9 +172,7 @@ const Sheeps = {
loadAPI: async () => {
let uuid = localStorage.getItem("uuid");
const URL = `${CONFIG.api}sheeps/list`;
Sheeps.sheeps_list.list = await fetch(URL, {
Sheeps.sheeps_list.list = await fetch(`${CONFIG.api}sheeps/list`, {
method: 'GET',
headers: {
"Content-Type": "application/json",
@@ -238,9 +232,11 @@ const Sheeps = {
if (p.can_view_territory) perms.push("View Territory");
if (p.can_add_sheeps) perms.push("Create Sheeps");
if (p.can_add_territory) perms.push("Create Territory");
if (p.can_manager_sheeps) perms.push("Manager Sheeps");
if (p.can_manager_territory) perms.push("Manager Territory");
if (p.can_add_stand) perms.push("Create Stand");
if (p.can_manager_stand) perms.push("Manager Stand");
if (p.can_joint_territory) perms.push("Joint Territory");
if (p.can_add_stand) perms.push("Create Stand");
if (p.can_add_schedule) perms.push("Create Schedule");
return perms.map(p => `<b>${p}</b>`).join('');
};
@@ -274,8 +270,7 @@ const Sheeps = {
loadAPI: async (id) => {
let uuid = localStorage.getItem("uuid");
const URL = `${CONFIG.api}sheep?id=${id}`;
return await fetch(URL, {
return await fetch(`${CONFIG.api}sheep?id=${id}`, {
method: 'GET',
headers: {
"Content-Type": "application/json",
@@ -327,8 +322,11 @@ const Sheeps = {
let sheep_editor_can_view_sheeps = document.getElementById('sheep-editor-can_view_sheeps');
let sheep_editor_can_add_sheeps = document.getElementById('sheep-editor-can_add_sheeps');
let sheep_editor_can_manager_sheeps = document.getElementById('sheep-editor-can_manager_sheeps');
let sheep_editor_can_add_territory = document.getElementById('sheep-editor-can_add_territory');
let sheep_editor_can_manager_territory = document.getElementById('sheep-editor-can_manager_territory');
let sheep_editor_can_joint_territory = document.getElementById('sheep-editor-can_joint_territory');
let sheep_editor_can_add_stand = document.getElementById('sheep-editor-can_add_stand');
let sheep_editor_can_manager_stand = document.getElementById('sheep-editor-can_manager_stand');
let sheep_editor_can_add_schedule = document.getElementById('sheep-editor-can_add_schedule');
@@ -367,8 +365,10 @@ const Sheeps = {
sheep_editor_can_view_sheeps.checked = sheep.possibilities.can_view_sheeps;
sheep_editor_can_add_sheeps.checked = sheep.possibilities.can_add_sheeps;
sheep_editor_can_manager_sheeps.checked = sheep.possibilities.can_manager_sheeps;
sheep_editor_can_add_territory.checked = sheep.possibilities.can_add_territory;
sheep_editor_can_manager_territory.checked = sheep.possibilities.can_manager_territory;
sheep_editor_can_joint_territory.checked = sheep.possibilities.can_joint_territory;
sheep_editor_can_add_stand.checked = sheep.possibilities.can_add_stand;
sheep_editor_can_manager_stand.checked = sheep.possibilities.can_manager_stand;
sheep_editor_can_add_schedule.checked = sheep.possibilities.can_add_schedule;
@@ -387,6 +387,9 @@ const Sheeps = {
if (USER.mode == 2) {
document.getElementById('sheep-editor-button').style.display = "";
sheep_editor_mode.disabled = false;
} else if (USER.possibilities.can_manager_sheeps) {
document.getElementById('sheep-editor-button').style.display = "";
sheep_editor_mode.disabled = true;
} else {
sheep_editor_mode.disabled = true;
}
@@ -458,9 +461,9 @@ const Sheeps = {
}
},
territory: {
async loadAPI(URL) {
async loadAPI(url) {
const uuid = localStorage.getItem("uuid");
const res = await fetch(URL, {
const res = await fetch(url, {
headers: {
"Content-Type": "application/json",
"Authorization": uuid
@@ -470,8 +473,7 @@ const Sheeps = {
},
async house(id) {
const URL = `${CONFIG.api}house/list?mode=admin&sheep_id=${id}`;
const list = await Sheeps.territory.loadAPI(URL);
const list = await Sheeps.territory.loadAPI(`${CONFIG.api}house/list?mode=admin&sheep_id=${id}`);
if ((USER.possibilities.can_view_territory || USER.mode == 2) && list.length > 0) {
document.getElementById('editor-blocks-territory').style.display = "";
@@ -480,8 +482,7 @@ const Sheeps = {
},
async homestead(id) {
const URL = `${CONFIG.api}homestead/list?mode=admin&sheep_id=${id}`;
const list = await Sheeps.territory.loadAPI(URL);
const list = await Sheeps.territory.loadAPI(`${CONFIG.api}homestead/list?mode=admin&sheep_id=${id}`);
if ((USER.possibilities.can_view_territory || USER.mode == 2) && list.length > 0) {
document.getElementById('editor-blocks-territory').style.display = "";

View File

@@ -54,15 +54,15 @@
}
#stand-info div span {
opacity: 0.8;
font-weight: 400;
font-size: var(--FontSize2);
font-weight: 500;
font-size: var(--FontSize3);
margin-right: 5px;
}
#stand-info div p {
font-weight: 300;
font-size: var(--FontSize4);
opacity: 0.9;
font-weight: 400;
font-size: var(--FontSize3);
}
#stand-info img {
@@ -143,11 +143,11 @@
}
#stand-schedule>.block-day h3 {
font-size: var(--FontSize5);
font-size: var(--FontSize6);
font-weight: 500;
padding: 10px;
padding: 20px;
text-transform: capitalize;
width: calc(100% - 20px);
width: calc(100% - 40px);
text-align: center;
}
@@ -181,6 +181,7 @@
#stand-schedule>.block-day div select:disabled {
opacity: 0.9 !important;
background-image: url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%2217%2010%2038%2050%22%3E%3Cpath%20fill%3D%22%23F2BD53%22%20d%3D%22M%2036%2010%20C%2028.28%2010%2022%2016.28%2022%2024%20L%2022%2028.587891%20C%2019.069798%2029.775473%2017%2032.643974%2017%2036%20L%2017%2052%20C%2017%2056.418%2020.582%2060%2025%2060%20L%2047%2060%20C%2051.418%2060%2055%2056.418%2055%2052%20L%2055%2036%20C%2055%2032.643974%2052.930202%2029.775473%2050%2028.587891%20L%2050%2024%20C%2050%2016.28%2043.72%2010%2036%2010%20z%20M%2036%2018%20C%2039.309%2018%2042%2020.691%2042%2024%20L%2042%2028%20L%2030%2028%20L%2030%2024%20C%2030%2020.691%2032.691%2018%2036%2018%20z%22%2F%3E%3C%2Fsvg%3E);
}
#stand-schedule>.block-day div:nth-child(2n) {

View File

@@ -52,6 +52,9 @@ const Stand_constructor = {
button.innerText = "Стенд додано";
Notifier.success('Стенд створено');
Stand_list.list = [];
Stand_list.loadAPI();
return response.json()
} else {
console.log('err');

View File

@@ -85,6 +85,9 @@ const Stand_editor = {
button.innerText = "Стенд відредаговано";
Notifier.success('Стенд відредаговано');
Stand_list.list = [];
Stand_list.loadAPI();
return response.json()
} else {
console.log('err');

View File

@@ -1,206 +1,11 @@
<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;
}
#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: var(--FontSize3);
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 > p {
font-size: var(--FontSize5);
text-align: center;
color: var(--ColorThemes3);
padding: 10px;
}
.card_info {
display: flex;
font-size: var(--FontSize3);
border-radius: 8px;
margin: 10px 10px 15px 10px;
flex-direction: column;
align-items: stretch;
border: 1px solid var(--ColorThemes3);
background: var(--ColorThemes2);
}
.card_info_homestead {
width: 100%;
margin: 0;
}
.card_info > .info {
display: flex;
font-size: var(--FontSize3);
align-items: center;
justify-content: space-between;
border-radius: 8px;
}
.card_info > .info > span {
min-width: 40px;
font-size: var(--FontSize1);
position: relative;
margin: 5px;
}
.card_info > .info > select {
color: #3d3d3d;
border-radius: 6px;
border: 1px solid #eaebef;
margin: 5px;
background-color: var(--ColorThemes3);
min-width: 110px;
width: 100%;
padding: 4px;
height: 30px;
}
.card_info > .info > input,
.card_info > .info > button {
font-size: var(--FontSize4);
font-weight: 400;
-webkit-appearance: none;
-moz-appearance: none;
border-radius: 6px;
margin: 5px;
background-color: var(--ColorThemes0);
border: 1px solid var(--ColorThemes1);
color: var(--ColorThemes3);
width: 100%;
max-width: 170px;
min-width: 70px;
padding: 0 4px;
height: 30px;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}
.card_info > .info > button > svg {
width: 15px;
height: 15px;
fill: var(--ColorThemes3);
}
.card_info > textarea {
border-radius: 6px;
font-size: var(--FontSize3);
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;
}
.card_info > textarea::placeholder {
color: var(--ColorThemes3);
opacity: 0.5;
}
.card_info > textarea::-webkit-input-placeholder {
color: var(--ColorThemes3);
opacity: 0.5;
}
#map_card {
display: none;
width: 100%;
height: calc(100vh - 100px);
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
border-radius: var(--border-radius);
}
</style>
<div class="page-card">
<div class="list-controls" id="page-card-controls" style="display: none">
<div id="page-card-sort">
<button id="sort_1" onclick="Territory_card.sort('2')" data-state="active">
<button
id="sort_1"
onclick="Territory_card.sort('2')"
data-state="active"
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path
d="M 32.476562 5.9785156 A 1.50015 1.50015 0 0 0 31 7.5 L 31 37.878906 L 26.560547 33.439453 A 1.50015 1.50015 0 1 0 24.439453 35.560547 L 31.439453 42.560547 A 1.50015 1.50015 0 0 0 33.560547 42.560547 L 40.560547 35.560547 A 1.50015 1.50015 0 1 0 38.439453 33.439453 L 34 37.878906 L 34 7.5 A 1.50015 1.50015 0 0 0 32.476562 5.9785156 z M 14.375 8.0058594 C 14.257547 8.01575 14.139641 8.0379219 14.025391 8.0761719 L 11.025391 9.0761719 C 10.239391 9.3381719 9.8141719 10.188609 10.076172 10.974609 C 10.338172 11.760609 11.190609 12.188828 11.974609 11.923828 L 13 11.580078 L 13 20.5 C 13 21.329 13.671 22 14.5 22 C 15.329 22 16 21.329 16 20.5 L 16 9.5 C 16 9.018 15.767953 8.5652031 15.376953 8.2832031 C 15.082953 8.0717031 14.727359 7.9761875 14.375 8.0058594 z M 14 27 C 11.344 27 9.387625 28.682109 9.015625 31.287109 C 8.898625 32.107109 9.4671094 32.867375 10.287109 32.984375 C 11.106109 33.102375 11.867375 32.533891 11.984375 31.712891 C 12.096375 30.931891 12.537 30 14 30 C 15.103 30 16 30.897 16 32 C 16 33.103 15.103 34 14 34 C 11.592 34 9 35.721 9 39.5 C 9 40.329 9.672 41 10.5 41 L 17.5 41 C 18.329 41 19 40.329 19 39.5 C 19 38.671 18.329 38 17.5 38 L 12.308594 38 C 12.781594 37.093 13.664 37 14 37 C 16.757 37 19 34.757 19 32 C 19 29.243 16.757 27 14 27 z"
@@ -394,6 +199,8 @@
</div>
</div>
<div id="page-card-info" style="display: none"></div>
<div id="list"></div>
<div id="map_card"></div>
</div>
@@ -403,4 +210,4 @@
<button id="card-new-date-button">Оновити дату</button>
<input type="datetime-local" id="card-new-date-input" placeholder="Дата" />
</div>
</div>
</div>

View File

@@ -32,7 +32,6 @@ const Territory_card = {
// Застосовуємо режим сортування
this.sort(localStorage.getItem('territory_card_sort'), false);
this.getEntrances({ update: false });
} else if (type === "homestead") {
this.getHomestead.map({});
}
@@ -40,7 +39,7 @@ const Territory_card = {
const ids = ['cloud_1', 'cloud_2', 'cloud_3'];
ids.forEach((id, idx) => {
const el = document.getElementById(id);
if(!el) return;
if (!el) return;
el.setAttribute('data-state', ['sync', 'ok', 'err'].indexOf(Cloud.status) === idx ? 'active' : '');
});
@@ -134,10 +133,18 @@ const Territory_card = {
if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) {
Cloud.socket.send(JSON.stringify(message));
} else {
if (confirm("З'єднання розірвано! Перепідключитись?")) {
Cloud.start();
Territory_card.getEntrances({ update: true });
}
Notifier.click({
title: `Запис не додано!`,
text: `Натисніть, щоб перепідключитись!`
}, {
type: 'warn',
f: () => {
Cloud.reconnecting = true;
Cloud.reconnectAttempts = 0;
Cloud.start();
},
timeout: 0
});
}
},
@@ -181,9 +188,18 @@ const Territory_card = {
if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) {
Cloud.socket.send(JSON.stringify(message));
} else {
if (confirm("З'єднання розірвано! Перепідключитись?")) {
Territory_card.cloud.start();
}
Notifier.click({
title: `Запис не додано!`,
text: `Натисніть, щоб перепідключитись!`
}, {
type: 'warn',
f: () => {
Cloud.reconnecting = true;
Cloud.reconnectAttempts = 0;
Cloud.start();
},
timeout: 0
});
}
}
},
@@ -211,12 +227,14 @@ const Territory_card = {
return;
}
Territory_card.info(data);
const fragment = document.createDocumentFragment();
const canManage = USER.mode === 2 || (USER.mode === 1 && USER.possibilities.can_manager_territory);
for (const element of data) {
const { id, entrance_number, title, history, working } = element;
const { id, entrance_number, title, history, working, address } = element;
const isMy = ((history.name === "Групова" && history.group_id == USER.group_id) || history.name === USER.name);
const show = (isMy && working) ? "open" : canManage ? "close" : null;
@@ -310,8 +328,9 @@ const Territory_card = {
div.style = style;
div.innerHTML = `
<span>Квартира ${apt.title}</span>
<hr>
<div class="info">
<span>кв.${apt.title}</span>
<select id="status_${apt.id}" onchange="Territory_card.cloud.messApartment({number:${number},id:${apt.id},update:true})" style="${style}">
${statusOptions(apt.status)}
</select>
@@ -350,9 +369,6 @@ const Territory_card = {
async map({ homestead_id = Territory_card.id }) {
let data = await this.loadAPI({ url: `${CONFIG.api}homestead/${homestead_id}` });
console.log(data);
let lat = data.geo?.lat ?? data.points?.[0]?.[0]?.[0]?.lat ?? 49.5629016;
let lng = data.geo?.lng ?? data.points?.[0]?.[0]?.[0]?.lng ?? 25.6145625;
let zoom = 15;
@@ -483,10 +499,116 @@ const Territory_card = {
Territory_card.getHomestead.markers[element.id] = marker; // сохраним ссылку на маркер
}
if((USER.possibilities.can_joint_territory && data.history.sheep_id == USER.id) || USER.mode == 2){
this.joint.setHTML(homestead_id);
}
},
joint: {
async setHTML(homestead_id){
let lest = await this.getJoint(homestead_id);
let block_info = document.getElementById('page-card-info');
block_info.style.display = "flex";
block_info.innerHTML = `
<h2>Надати спільний доступ:</h2>
<smart-select type="number" id="joint-${homestead_id}" onchange="Territory_card.getHomestead.joint.setJoint('${homestead_id}')" max="30" placeholder="Оберіть вісників..." title="Оберіть вісників, з якими хочете поділитись територією">
${Sheeps.sheeps_list.list.map(p => {
const isSelected = lest.some(item => item.sheep_id === p.id);
if(USER.id === Number(p.id) && USER.mode != 2) return
return `<div
slot="option"
data-value="${Number(p.id)}"
${isSelected ? 'data-selected' : ''}>
${p.name}
</div>`;
}).join('')}
</smart-select>
`;
},
setJoint(homestead_id){
const select = document.getElementById(`joint-${homestead_id}`);
if (!select) return;
console.log(select.getClick);
if(select.getClick.state == "add"){
this.addSheep(homestead_id, select.getClick.value);
} else if(select.getClick.state == "delete"){
this.delSheep(homestead_id, select.getClick.value);
}
},
async getJoint(homestead_id){
let uuid = localStorage.getItem("uuid");
return await fetch(`${CONFIG.api}homestead/joint/${homestead_id}`, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
}
}).then((response) => response.json());
},
async addSheep(homestead_id, sheep_id){
const uuid = localStorage.getItem('uuid');
if (!homestead_id) {
console.warn("Невірні дані для наданя доступу.");
return;
}
try {
const response = await fetch(`${CONFIG.api}homestead/joint/${homestead_id}`, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
body: JSON.stringify({"sheep_id": sheep_id})
});
if (!response.ok) throw new Error("Failed to assign");
Notifier.success('Віснику успішно надано доступ.');
} catch (err) {
console.error('❌ Error:', err);
Notifier.error('Помилка надання доступу.');
}
},
async delSheep(homestead_id, sheep_id){
const uuid = localStorage.getItem('uuid');
if (!homestead_id) {
console.warn("Невірні дані для відкликання доступу.");
return;
}
try {
const response = await fetch(`${CONFIG.api}homestead/joint/${homestead_id}`, {
method: 'DELETE',
headers: {
"Content-Type": "application/json",
"Authorization": uuid
},
body: JSON.stringify({"sheep_id": sheep_id})
});
if (!response.ok) throw new Error("Failed to assign");
Notifier.success('Доступ успішно відкликанно.');
} catch (err) {
console.error('❌ Error:', err);
Notifier.error('Помилка при відкликанні доступу.');
}
}
}
},
async reload(){
async reload() {
Territory_card.getEntrances({ update: true });
},
@@ -547,5 +669,39 @@ const Territory_card = {
}
this.close();
}
},
info(data) {
let block_info = document.getElementById('page-card-info');
block_info.style.display = "flex";
block_info.innerHTML = `
<a href="https://www.google.com/maps?q=${data[0].address.points_number.lat},${data[0].address.points_number.lng}">${data[0].address.title} ${data[0].address.number}</a>
<hr>
<h2>Терміни опрацювання:</h2>
`
for (let index = 0; index < data.length; index++) {
const element = data[index];
const canManage = USER.mode === 2 || (USER.mode === 1 && USER.possibilities.can_manager_territory);
const isMy = ((element.history.name === "Групова" && element.history.group_id == USER.group_id) || element.history.name === USER.name);
let date_start = element.history.date.start;
let date_end = date_start + (1000 * 2629743 * 4);
let red = () => {
if(Date.now() > date_end) return `color: #ec2d2d;`
return
}
if (element.working && (isMy || canManage)) {
block_info.innerHTML += `
<div>
<h3>${element.title}</h3>
<h4>${formattedDate(date_start)} — <span style="${red()}">${formattedDate(date_end)}</span></h4>
</div>
`;
}
}
}
}

View File

@@ -88,6 +88,48 @@
}
#page-card-info {
padding: 10px;
margin: 0px 0 10px 0;
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
border-radius: var(--border-radius);
/* overflow: auto; */
display: flex;
justify-content: space-between;
flex-direction: column;
gap: 10px;
}
#page-card-info>a {
font-size: var(--FontSize5);
font-weight: 500;
}
#page-card-info>h2 {
font-size: var(--FontSize4);
}
#page-card-info>div {
display: flex;
align-items: flex-end;
gap: 10px;
}
#page-card-info>div>h3 {
font-size: var(--FontSize3);
font-weight: 500;
opacity: 0.9;
}
#page-card-info>div>h4 {
font-size: var(--FontSize2);
font-weight: 400;
opacity: 0.8;
}
#card-new-date {
display: flex;
width: 100%;
@@ -149,4 +191,221 @@
font-weight: 400;
-webkit-appearance: none;
-moz-appearance: none;
}
.page-card *[disabled] {
opacity: 0.5;
}
.page-card #list {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.page-card details {
color: var(--ColorThemes3);
width: 100%;
min-width: 320px;
background: var(--ColorThemes1);
border-radius: var(--border-radius);
overflow: hidden;
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
}
@media (min-width: 900px) {
.page-card #list {
align-items: flex-start;
justify-content: space-between;
flex-direction: row;
flex-wrap: wrap;
}
.page-card details {
width: calc(50% - 10px);
}
}
.page-card details[disabled] .page-card details summary,
.page-card details.disabled .page-card details summary {
pointer-events: none;
user-select: none;
}
.page-card details .page-card details summary::-webkit-.page-card details-marker,
.page-card details .page-card details summary::marker {
display: none;
content: "";
}
.page-card details summary {
cursor: pointer;
height: 45px;
font-weight: 500;
display: flex;
align-items: center;
justify-content: space-between;
}
.page-card details summary p {
padding: 0 10px;
font-size: var(--FontSize3);
font-weight: 500;
}
.page-card details summary svg {
width: 25px;
height: 25px;
padding: 0 10px;
fill: currentColor;
}
.page-card .apartments_list {
padding: 10px;
gap: 15px;
display: flex;
flex-direction: column;
}
.page-card .apartments_list>p {
font-size: var(--FontSize5);
text-align: center;
color: var(--ColorThemes3);
padding: 10px;
}
.page-card .card_info {
display: flex;
font-size: var(--FontSize3);
border-radius: 8px;
flex-direction: column;
align-items: stretch;
border: 1px solid var(--ColorThemes3);
background-color: var(--ColorThemes2);
gap: 6px;
padding: 6px;
}
.card_info_homestead {
border-radius: 6px !important;
width: calc(100% - 15px);
margin: 0;
}
.page-card .card_info>span {
font-size: var(--FontSize3);
font-weight: 500;
position: relative;
}
.page-card .card_info>hr {
height: 1px;
background-color: currentColor;
opacity: 0.1;
margin-bottom: 4px;
}
.page-card .card_info>.info {
display: flex;
font-size: var(--FontSize3);
align-items: center;
justify-content: space-between;
border-radius: 8px;
gap: 5px;
}
.page-card .card_info>.info>select {
color: #3d3d3d;
border-radius: 6px;
border: 1px solid #eaebef;
background-color: var(--ColorThemes3);
min-width: 110px;
width: 50%;
padding: 4px;
height: 30px;
}
.page-card .card_info>.info>input,
.page-card .card_info>.info>button {
font-size: var(--FontSize2);
font-weight: 400;
-webkit-appearance: none;
-moz-appearance: none;
border-radius: 6px;
background-color: var(--ColorThemes0);
border: 1px solid var(--ColorThemes1);
color: var(--ColorThemes3);
width: 50%;
min-width: 70px;
padding: 0 4px;
height: 30px;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
}
.page-card .card_info>.info>button>svg {
width: 15px;
height: 15px;
fill: var(--ColorThemes3);
}
.page-card .card_info>textarea {
border-radius: 6px;
font-size: var(--FontSize3);
background-color: var(--ColorThemes0);
border: 0;
color: var(--ColorThemes3);
width: calc(100% - 10px);
min-width: 70px;
padding: 5px;
min-height: 40px;
appearance: none;
resize: vertical;
-webkit-appearance: none;
}
.page-card .card_info>textarea::placeholder {
color: var(--ColorThemes3);
opacity: 0.5;
}
.page-card .card_info>textarea::-webkit-input-placeholder {
color: var(--ColorThemes3);
opacity: 0.5;
}
.page-card #map_card {
display: none;
width: 100%;
height: calc(100vh - 100px);
background: var(--ColorThemes1);
color: var(--ColorThemes3);
border: 1px solid var(--ColorThemes2);
box-shadow: var(--shadow-l1);
border-radius: var(--border-radius);
margin-bottom: 15px;
}
smart-select {
--smart-select-option-color: var(--ColorThemes3);
--smart-select-border-color: var(--ColorThemes2);
--smart-select-background: var(--ColorThemes0);
--smart-select-color: var(--PrimaryColor);
--smart-select-chip-fill: var(--PrimaryColorText);
--smart-select-chip-background: var(--PrimaryColor);
--smart-select-chip-color: var(--ColorThemes3);
--smart-select-search-color: var(--ColorThemes3);
--smart-select-hover-background: var(--ColorThemes2);
--smart-select-hover-color: var(--ColorThemes3);
--smart-select-selected-background: var(--PrimaryColor);
--smart-select-selected-color: var(--PrimaryColorText);
--smart-select-font-size-1: var(--FontSize1);
--smart-select-font-size-2: var(--FontSize3);
--smart-select-border-radius-1: 8px;
--smart-select-border-radius-2: 4px;
}

View File

@@ -13,12 +13,20 @@
name="address"
required
value=""
onchange="Territory_editor.info.title=this.value"
/>
</div>
<div>
<label for="info-number">Номер будинку / частини</label>
<input type="text" id="info-number" name="number" required value="" />
<input
type="text"
id="info-number"
name="number"
required
value=""
onchange="Territory_editor.info.number=this.value"
/>
</div>
<div>
@@ -29,6 +37,7 @@
name="settlement"
required
value="Тернопіль"
onchange="Territory_editor.info.settlement=this.value"
/>
</div>
</form>
@@ -66,14 +75,18 @@
</div>
</div>
<span>або</span>
<button onclick="Territory_editor.osm.newPoligon()">Обрати на карті</button>
<button onclick="Territory_editor.osm.newPoligon()">
Обрати на карті
</button>
</div>
<div class="block-map">
<div id="map"></div>
</div>
<button type="button" id="part-2-button" onclick="Territory_editor.save()">Зберегти</button>
<button type="button" id="part-2-button" onclick="Territory_editor.save()">
Зберегти
</button>
</div>
<div id="part-3" class="part_block" style="display: none">

View File

@@ -40,10 +40,11 @@ const Territory_list = {
},
house: {
url: null,
list: [],
loadAPI: async function (url) {
const uuid = localStorage.getItem("uuid");
const response = await fetch(url, {
const response = await fetch(url ?? Territory_list.house.url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
@@ -60,6 +61,7 @@ const Territory_list = {
const territory_list_filter = Number(localStorage.getItem("territory_list_filter") ?? 0);
const url = `${CONFIG.api}houses/list${territory_entrances ? '/entrances' : ''}`;
Territory_list.house.url = url;
let list = this.list.length > 0 ? this.list : await this.loadAPI(url);
const isEnd = territory_list_filter === "2";
@@ -94,11 +96,13 @@ const Territory_list = {
const person = working
? `${element.history.name === 'Групова' ? 'Група ' + element.history.group_id : element.history.name}`
: ``;
const overdue = working && (element.history.date.start + (1000 * 2629743 * 4)) <= Date.now();
card.image = `${CONFIG.web}cards/house/T${element.house.id}.webp`;
card.address = `${element.house.title} ${element.house.number} (${element.title})`;
card.link = `/territory/manager/house/${element.house.id}`;
card.sheep = person;
card.overdue = overdue;
block.appendChild(card);
} else {
const qty = element.entrance.quantity;
@@ -154,8 +158,8 @@ const Territory_list = {
}
});
this.list = await response.json();
return this.list;
Territory_list.homestead.list = await response.json();
return Territory_list.homestead.list;
},
setHTML: async function () {
const block = document.getElementById('list-homestead');
@@ -186,12 +190,15 @@ const Territory_list = {
const person = working
? `${element.history.name === 'Групова' ? 'Група ' + element.history.group_id : element.history.name}`
: ``;
const overdue = working && (element.history.date.start + (1000 * 2629743 * 4)) <= Date.now();
const card = document.createElement('app-territory-card');
card.image = `${CONFIG.web}cards/homestead/H${element.id}.webp`;
card.address = `${element.title} ${element.number}`;
card.link = `/territory/manager/homestead/${element.id}`;
card.sheep = person;
card.overdue = overdue;
block.appendChild(card);
}
}

View File

@@ -358,6 +358,11 @@ const Territory_Manager = {
Territory_Manager.mess.close();
Territory_Manager.entrances.list = [];
await Territory_Manager.entrances.setHTML(type, id);
Territory_list.house.list = [];
Territory_list.homestead.list = [];
Territory_list.house.loadAPI();
Territory_list.homestead.loadAPI();
} catch (err) {
console.error('❌ Error:', err);
Notifier.error('Помилка призначення території');
@@ -389,6 +394,11 @@ const Territory_Manager = {
Territory_Manager.entrances.list = [];
await Territory_Manager.entrances.setHTML(type, id);
Territory_list.house.list = [];
Territory_list.homestead.list = [];
Territory_list.house.loadAPI();
Territory_list.homestead.loadAPI();
Notifier.success('Територія забрана успішно');
} catch (error) {
console.error("❌ Помилка зняття призначення:", error);

View File

@@ -1,7 +1,7 @@
let map_all;
let map_all, free_entrance, free_homesteads;
const Territory_Map = {
init: async () => {
async init() {
let html = await fetch('/lib/pages/territory/map/index.html').then((response) => response.text());
app.innerHTML = html;
@@ -9,7 +9,7 @@ const Territory_Map = {
Territory_Map.info.setHTML();
},
info: {
loadAPI: async (url) => {
async loadAPI(url) {
const uuid = localStorage.getItem("uuid");
const response = await fetch(url, {
@@ -23,22 +23,22 @@ const Territory_Map = {
return await response.json();
},
setHTML: async () => {
async setHTML() {
const houses = await Territory_Map.info.loadAPI(`${CONFIG.api}houses/list`);
const homestead = await Territory_Map.info.loadAPI(`${CONFIG.api}homestead/list`);
Territory_Map.map.added({ type: "houses", data: houses });
Territory_Map.map.added({ type: "house", data: houses });
Territory_Map.map.added({ type: "homestead", data: homestead });
}
},
map: {
polygons: [],
init: () => {
init() {
if (map_all && map_all.remove) map_all.remove();
const mapElement = document.getElementById('map');
if (!mapElement) return;
let firstLocate = true;
let googleHybrid = L.tileLayer('http://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', {
maxZoom: 20,
@@ -55,6 +55,9 @@ const Territory_Map = {
tms: true
});
free_entrance = new L.FeatureGroup();
free_homesteads = new L.FeatureGroup();
map_all = L.map(mapElement, {
renderer: L.canvas(),
center: [49.5629016, 25.6145625],
@@ -63,22 +66,72 @@ const Territory_Map = {
layers: [
googleHybrid,
osm,
mytile
mytile,
free_entrance,
free_homesteads
]
});
map_all.locate({
setView: true, // 🔥 сразу центрирует карту
maxZoom: 16
});
let baseMaps = {
"Google Hybrid": googleHybrid,
"OpenStreetMap": osm,
"Sheep Service Map": mytile,
};
let layerControl = L.control.layers(baseMaps, [], { position: 'bottomright' }).addTo(map_all);
let baseLayer = {
"Вільні під'їзди": free_entrance,
"Вільні райони": free_homesteads
};
L.control.layers(baseMaps, baseLayer, { position: 'bottomright' }).addTo(map_all);
map_all.pm.setLang("ua");
map_all.on('zoomend', () => {
const z = map_all.getZoom();
if (z <= 15) {
map_all.removeLayer(free_homesteads);
} else {
map_all.addLayer(free_homesteads);
}
if (z <= 16) {
map_all.removeLayer(free_entrance);
} else {
map_all.addLayer(free_entrance);
}
});
// слежение в реальном времени
map_all.locate({ setView: false, watch: true, enableHighAccuracy: true });
map_all.on('locationfound', (e) => {
if (firstLocate) map_all.setView(e.latlng, 16);
if (!map_all._userMarker) {
map_all._userMarker = L.marker(e.latlng).addTo(map_all).bindPopup("");
map_all._userMarker.on("popupopen", () => {
const div = document.createElement("div");
div.className = 'marker_popup'
div.innerHTML = `<p>Ви тут!</p>`;
map_all._userMarker.setPopupContent(div);
});
} else {
map_all._userMarker.setLatLng(e.latlng);
}
firstLocate = false;
});
},
added: ({ type, data }) => {
added({ type, data }) {
for (let index = 0; index < data.length; index++) {
const element = data[index];
let posPersonal, posGroup;
@@ -108,6 +161,13 @@ const Territory_Map = {
}
}
this.marker({
id: element.id,
type: 'homestead',
free: element.working ? 0 : 1,
geo: element.geo
})
} else {
posPersonal = Home.personal.house.list.map(e => e.id).indexOf(element.id);
posGroup = Home.group.house.list.map(e => e.id).indexOf(element.id);
@@ -119,6 +179,13 @@ const Territory_Map = {
fillOpacity: 0.8
}
}
this.marker({
id: element.id,
type: 'house',
free: element.entrance.quantity - element.entrance.working,
geo: element.geo
})
}
const polygon = L.polygon(element.points, polygonOptions).addTo(map_all);
@@ -129,13 +196,14 @@ const Territory_Map = {
polygon.on("popupopen", () => {
const div = document.createElement("div");
let text = () => {
if (posPersonal != -1) return "<span>Моя територія</span>"
else if (posGroup != -1) return "<span>Групова територія</span>"
return ""
if (posPersonal != -1) return `<span>Моя територія</span> <p>${element.title} ${element.number}</p> <a href="/territory/card/${type}/${element.id}" data-route>Перейти до території</a>`
else if (posGroup != -1) return `<span>Групова територія</span> <p>${element.title} ${element.number}</p> <a href="/territory/card/${type}/${element.id}" data-route>Перейти до території</a>`
return `<p>${element.title} ${element.number}</p> `
}
div.innerHTML = `${text()} ${element.title} ${element.number}`;
div.className = "leaflet_drop"
div.className = 'marker_popup'
div.innerHTML = `${text()}`;
if (USER.possibilities.can_manager_territory || USER.mode == 2) div.innerHTML += `<a href="/territory/manager/${type}/${element.id}" data-route>Керування</a>`;
polygon.setPopupContent(div);
});
@@ -144,40 +212,29 @@ const Territory_Map = {
}
},
marker: ({ data, personal = false, group = false }) => {
console.log(data);
marker({ id, type, free, geo }) {
if (!USER.possibilities.can_manager_territory || USER.mode != 2) return;
if (free <= 0) return;
for (let index = 0; index < data.length; index++) {
const element = data[index];
const redDot = L.divIcon({
className: "marker",
html: `${free}`,
iconSize: [30, 30],
iconAnchor: [15, 15]
});
// создаём маркер
const marker = L.marker([geo.lat, geo.lng], { icon: redDot }).addTo(type == 'homestead' ? free_homesteads : free_entrance);
marker.bindPopup("");
console.log(element);
// при открытии popup генерим div заново
marker.on("popupopen", () => {
const div = document.createElement("div");
div.className = 'marker_popup'
div.innerHTML = `<a href="/territory/manager/${type}/${id}" data-route>Перейти до території</a>`;
const redDot = L.divIcon({
className: "leaflet_drop",
html: `<div id="redDot_${element.id}"></div>`,
iconSize: [16, 16],
iconAnchor: [8, 8]
});
// создаём маркер
const marker = L.marker([element.geo.lat, element.geo.lng], { icon: redDot }).addTo(map_all);
marker.bindPopup("");
// при открытии popup генерим div заново
marker.on("popupopen", () => {
const div = document.createElement("div");
let text = () => {
if (personal) return "Моя територія"
else if (group) return "Групова територія"
return ""
}
div.innerHTML = text();
marker.setPopupContent(div);
});
}
marker.setPopupContent(div);
});
}
}
}

View File

@@ -1,11 +1,53 @@
.page-territory_map {
width: 100%;
width: 100%;
display: flex;
position: relative;
}
.page-territory_map>#map {
margin: 20px;
width: calc(100% - 40px);
height: calc(100% - 40px);
border-radius: calc(var(--border-radius) - 5px);
}
.page-territory_map .marker {
background: var(--ColorThemes2);
color: var(--ColorThemes3);
font-size: var(--FontSize1);
border-radius: var(--border-radius);
border: 2px solid var(--ColorThemes3);
display: flex;
align-items: center;
justify-content: center;
}
.page-territory_map .marker_popup {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
margin: 0 2px;
}
.page-territory_map .marker_popup>p {
margin: 0;
}
.page-territory_map .marker_popup>span {
margin: 0;
}
.page-territory_map .marker_popup>a {
color: var(--ColorThemes3);
cursor: pointer;
border-radius: calc(var(--border-radius) - 8px);
padding: 5px 10px;
min-width: fit-content;
background: var(--PrimaryColor);
display: flex;
align-items: center;
justify-content: center;
font-weight: 400;
font-size: var(--FontSize1);
min-width: calc(100% - 15px);
}