Додані повідомлення та перепрацьована структура застосунку та api
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user