Додан моніторінг застосунку
Додани веб компоненти карточок територій та повідомлень
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
const CONFIG = {
|
||||
"web": "https://test.sheep-service.com/",
|
||||
"api": "https://test.sheep-service.com/api/",
|
||||
"wss": "wss://test.sheep-service.com/ws"
|
||||
"wss": "wss://test.sheep-service.com/ws",
|
||||
"metrics": "wss://test.sheep-service.com/metrics"
|
||||
}
|
||||
@@ -73,6 +73,7 @@
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
outline: none;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
*:disabled {
|
||||
@@ -419,76 +420,6 @@ body.modal-open {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Уведомление и кнопка обновления приложения */
|
||||
#update_banner {
|
||||
height: 55px;
|
||||
transition: .3s ease;
|
||||
}
|
||||
|
||||
#update_banner .content {
|
||||
margin: 0 auto;
|
||||
max-width: 340px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#update_banner .headline {
|
||||
font-weight: 800;
|
||||
font-size: var(--FontSize4);
|
||||
color: var(--PrimaryColorText);
|
||||
font-family: 'Roboto', sans-serif;
|
||||
margin-bottom: 5px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
#update_banner .subhead {
|
||||
font-size: var(--FontSize2);
|
||||
text-align: center;
|
||||
color: var(--PrimaryColorText);
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
#update_banner[data-state="noupdate"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#update_banner[data-state="updateavailable"] {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
background-color: var(--PrimaryColor);
|
||||
color: var(--PrimaryColorText);
|
||||
transition: .3s ease;
|
||||
opacity: 0.95;
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
width: 350px;
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
border-radius: var(--border-radius);
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#update_banner_icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#update_banner_icon svg {
|
||||
padding: 0px 50px;
|
||||
width: 25px;
|
||||
margin: -4px;
|
||||
fill: var(--PrimaryColorText);
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
#update_banner[data-state="updateavailable"] {
|
||||
width: calc(100% - 30px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Стили для меню */
|
||||
#navigation {
|
||||
position: fixed;
|
||||
|
||||
@@ -41,6 +41,10 @@
|
||||
<!-- Конфигурация SW -->
|
||||
<script src="/sw.js"></script>
|
||||
|
||||
<!-- Кастомні елементи -->
|
||||
<script src="/lib/customElements/notification.js" defer></script>
|
||||
<script src="/lib/customElements/territoryCard.js" defer></script>
|
||||
|
||||
<link rel="stylesheet" href="/css/main.css" />
|
||||
|
||||
<script src="/config.js" defer></script>
|
||||
@@ -59,6 +63,8 @@
|
||||
|
||||
<script src="/lib/components/cloud.js" defer></script>
|
||||
|
||||
<script src="/lib/components/metrics.js" defer></script>
|
||||
|
||||
<script src="/lib/components/clipboard.js" defer></script>
|
||||
<script src="/lib/components/colorGroup.js" defer></script>
|
||||
<script src="/lib/components/makeid.js" defer></script>
|
||||
@@ -117,6 +123,9 @@
|
||||
<script src="/lib/pages/schedule/list/script.js" defer></script>
|
||||
<link href="/lib/pages/schedule/list/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/pages/schedule/constructor/script.js" defer></script>
|
||||
<link href="/lib/pages/schedule/constructor/style.css" rel="stylesheet" />
|
||||
|
||||
<script src="/lib/app.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -169,39 +178,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Кнопка сповіщення та оновлення додатка -->
|
||||
<div id="update_banner" data-state="noupdate">
|
||||
<div class="content">
|
||||
<div class="headline"></div>
|
||||
<div class="subhead"></div>
|
||||
<div id="update_banner_icon">
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.0"
|
||||
width="64px"
|
||||
height="64px"
|
||||
viewBox="0 0 128 128"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M64 9.75A54.25 54.25 0 0 0 9.75 64H0a64 64 0 0 1 128 0h-9.75A54.25 54.25 0 0 0 64 9.75z"
|
||||
></path>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
type="rotate"
|
||||
from="0 64 64"
|
||||
to="360 64 64"
|
||||
dur="1400ms"
|
||||
repeatCount="indefinite"
|
||||
></animateTransform>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-notification-container
|
||||
id="notif-manager"
|
||||
position="top-right"
|
||||
max-visible="5"
|
||||
timeout="4000">
|
||||
</app-notification-container>
|
||||
|
||||
<!-- Анімація оновлення сторінки свайпом -->
|
||||
<div id="swipe_updater">
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
let USER = {};
|
||||
let page = "Home";
|
||||
let swRegistration = null;
|
||||
|
||||
// Реєструємо CustomElements
|
||||
const Notifier = document.getElementById('notif-manager');
|
||||
|
||||
// Определение ID главного блока
|
||||
let app = document.getElementById('app');
|
||||
|
||||
@@ -59,7 +63,7 @@ window.addEventListener('load', async function () {
|
||||
return response.json();
|
||||
});
|
||||
|
||||
console.log("USER Info: ", USER);
|
||||
console.log("[APP] USER Info: ", USER);
|
||||
|
||||
|
||||
if (USER.possibilities.can_view_sheeps) document.getElementById("li-sheeps").style.display = "";
|
||||
@@ -72,13 +76,39 @@ window.addEventListener('load', async function () {
|
||||
|
||||
if (Cloud.socket) Cloud.socket.close(1000, "Перезапуск з'єднання");
|
||||
Cloud.start();
|
||||
|
||||
|
||||
editFontStyle();
|
||||
|
||||
Router.check().listen().delegateLinks();
|
||||
|
||||
setupFrontendMetrics();
|
||||
}
|
||||
});
|
||||
|
||||
let offlineNode = null;
|
||||
window.addEventListener("offline", () => {
|
||||
console.log("[APP] Інтернет зник");
|
||||
offlineNode = Notifier.error({
|
||||
title: 'Оффлайн',
|
||||
text: 'Втрачено з\'єднання з інтернетом'
|
||||
}, { timeout: 0, lock: true });
|
||||
});
|
||||
|
||||
window.addEventListener("online", () => {
|
||||
console.log("[APP] Інтернет з'явився");
|
||||
if (offlineNode) {
|
||||
Notifier._removeNode(offlineNode);
|
||||
offlineNode = null;
|
||||
}
|
||||
Notifier.success({
|
||||
title: 'Онлайн',
|
||||
text: 'Інтернет знову працює'
|
||||
}, { timeout: 3000 });
|
||||
|
||||
if (Cloud.socket) Cloud.socket.close(1000, "Перезапуск з'єднання");
|
||||
Cloud.start();
|
||||
});
|
||||
|
||||
function editFontStyle() {
|
||||
let fontSize = localStorage.getItem("fontSize")
|
||||
? localStorage.getItem("fontSize")
|
||||
@@ -153,7 +183,7 @@ document.getElementById("pwa-install-button").addEventListener("click", async ()
|
||||
|
||||
deferredPrompt.prompt();
|
||||
const { outcome } = await deferredPrompt.userChoice;
|
||||
console.log(`Результат встановлення PWA: ${outcome}`);
|
||||
console.log(`[APP] Результат встановлення PWA: ${outcome}`);
|
||||
|
||||
closePopup();
|
||||
});
|
||||
@@ -172,28 +202,26 @@ function closePopup() {
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
let refreshing = false;
|
||||
let updateNode = null;
|
||||
|
||||
const showUpdateBanner = sw => {
|
||||
const banner = document.getElementById('update_banner');
|
||||
if (!banner) return;
|
||||
const updateCache = sw => {
|
||||
if (updateNode) {
|
||||
Notifier._removeNode(updateNode);
|
||||
updateNode = null;
|
||||
}
|
||||
|
||||
banner.dataset.state = 'updateavailable';
|
||||
banner.querySelector('.headline').textContent = 'Доступне оновлення';
|
||||
banner.querySelector('.subhead').textContent = 'Натисніть, щоб оновити додаток до останньої версії!';
|
||||
Notifier.warn({ title: `Завантаження оновлення`, text: `Додаток буде перезавантажено!` }, { timeout: 3000 });
|
||||
|
||||
banner.addEventListener('click', () => {
|
||||
banner.querySelector('.headline').textContent = '';
|
||||
banner.querySelector('.subhead').textContent = '';
|
||||
banner.querySelector('#update_banner_icon').style.display = 'block';
|
||||
|
||||
sw.postMessage('skipWaiting'); // активує новий SW
|
||||
sw.postMessage('updateCache'); // оновлює кеш
|
||||
});
|
||||
sw.postMessage('skipWaiting'); // активує новий SW
|
||||
sw.postMessage('updateCache'); // оновлює кеш
|
||||
};
|
||||
|
||||
|
||||
navigator.serviceWorker.register('/sw.js').then(reg => {
|
||||
// якщо є waiting SW, показуємо банер
|
||||
if (reg.waiting) showUpdateBanner(reg.waiting);
|
||||
if (reg.waiting) {
|
||||
updateNode = Notifier.click({ title: `Доступне оновлення`, text: `Натисніть, щоб оновити додаток до останньої версії!` }, { type: 'info', f: () => updateCache(reg.waiting), timeout: 0 });
|
||||
}
|
||||
|
||||
// слідкуємо за новим воркером
|
||||
reg.addEventListener('updatefound', () => {
|
||||
@@ -202,7 +230,7 @@ if ('serviceWorker' in navigator) {
|
||||
|
||||
newWorker.addEventListener('statechange', () => {
|
||||
if (newWorker.state === 'installed' && reg.waiting) {
|
||||
showUpdateBanner(reg.waiting); // лише показ банера
|
||||
updateNode = Notifier.click({ title: `Доступне оновлення`, text: `Натисніть, щоб оновити додаток до останньої версії!` }, { type: 'info', f: () => updateCache(reg.waiting), timeout: 0 });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -213,5 +241,5 @@ if ('serviceWorker' in navigator) {
|
||||
window.location.reload(); // відбувається ТІЛЬКИ після skipWaiting
|
||||
});
|
||||
|
||||
}).catch(err => console.error('Помилка реєстрації SW:', err));
|
||||
}).catch(err => console.error('[ServiceWorker] Помилка реєстрації SW:', err));
|
||||
}
|
||||
@@ -9,11 +9,13 @@ const Cloud = {
|
||||
Cloud.status = 'sync';
|
||||
const uuid = localStorage.getItem("uuid");
|
||||
|
||||
if(!navigator.onLine) alert("[APP] Інтернет з'єднання відсутнє!")
|
||||
|
||||
if (Cloud.socket && Cloud.socket.readyState <= 1) return;
|
||||
|
||||
const ws = new WebSocket(CONFIG.wss, uuid);
|
||||
Cloud.socket = ws;
|
||||
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log("[WebSocket] З'єднання встановлено");
|
||||
Cloud.status = 'ok';
|
||||
@@ -25,10 +27,10 @@ const Cloud = {
|
||||
}
|
||||
}));
|
||||
|
||||
if(Cloud.reconnecting == true) {
|
||||
Router.navigate(location.pathname);
|
||||
if (Cloud.reconnecting == true) {
|
||||
Cloud.reconnect();
|
||||
}
|
||||
|
||||
|
||||
Cloud.reconnecting = true;
|
||||
Cloud.reconnectAttempts = 0;
|
||||
clearTimeout(Cloud.reconnectTimeout);
|
||||
@@ -37,7 +39,7 @@ const Cloud = {
|
||||
ws.onmessage = (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
if (data.event === 'user_connected' && data.user.id !== USER.id) {
|
||||
console.log(`Новий користувач: ${data.user.name}`);
|
||||
console.log(`[WebSocket] Новий користувач: ${data.user.name}`);
|
||||
}
|
||||
if (data.event === 'message') {
|
||||
switch (data.type) {
|
||||
@@ -54,7 +56,7 @@ const Cloud = {
|
||||
case "stand_update":
|
||||
Stand_card.cloud.update(data);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -65,7 +67,7 @@ const Cloud = {
|
||||
console.warn("[WebSocket] З'єднання розірвано");
|
||||
Cloud.status = 'err';
|
||||
|
||||
if (!Cloud.reconnecting) return; // защита от дублирования
|
||||
if (!Cloud.reconnecting) return; // захист від дублювання
|
||||
|
||||
if (Cloud.reconnectAttempts < 5) {
|
||||
Cloud.reconnectAttempts++;
|
||||
@@ -73,7 +75,7 @@ const Cloud = {
|
||||
|
||||
Cloud.reconnectTimeout = setTimeout(() => {
|
||||
Cloud.start();
|
||||
}, 1000);
|
||||
}, 500);
|
||||
} else {
|
||||
Cloud.reconnecting = false;
|
||||
|
||||
@@ -90,6 +92,19 @@ const Cloud = {
|
||||
ws.onerror = (err) => {
|
||||
console.error("[WebSocket] Помилка", err);
|
||||
Cloud.status = 'err';
|
||||
ws.close();
|
||||
};
|
||||
},
|
||||
|
||||
async reconnect(){
|
||||
switch (page) {
|
||||
case "Territory_card":
|
||||
Territory_card.reload();
|
||||
break;
|
||||
|
||||
default:
|
||||
Router.navigate(location.pathname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
56
web/lib/components/metrics.js
Normal file
56
web/lib/components/metrics.js
Normal file
@@ -0,0 +1,56 @@
|
||||
let mws;
|
||||
const RECONNECT_INTERVAL = 3000;
|
||||
let isConnectedMetrics = false;
|
||||
|
||||
function setupFrontendMetrics() {
|
||||
console.log("[Metrics] Спроба підключення до метрик...");
|
||||
mws = new WebSocket(CONFIG.metrics);
|
||||
|
||||
mws.onopen = () => {
|
||||
console.log("[Metrics] З'єднання встановлено");
|
||||
isConnectedMetrics = true;
|
||||
|
||||
// Відправляємо один раз навігацію та ресурси
|
||||
sendMetrics();
|
||||
};
|
||||
|
||||
mws.onclose = () => {
|
||||
console.warn("[Metrics] З'єднання розірвано");
|
||||
console.log(`[Metrics] Спроба перепідключення`);
|
||||
isConnectedMetrics = false;
|
||||
// Спроба перепідключення через заданий інтервал
|
||||
setTimeout(setupFrontendMetrics, RECONNECT_INTERVAL);
|
||||
};
|
||||
|
||||
mws.onerror = (err) => {
|
||||
console.error("[Metrics] Помилка", err);
|
||||
mws.close(); // Примусово закриваємо для запуску логіки перепідключення
|
||||
};
|
||||
}
|
||||
|
||||
async function sendMetrics() {
|
||||
if (isConnectedMetrics) {
|
||||
const perf = performance;
|
||||
|
||||
const payload = {
|
||||
type: "frontend_metrics",
|
||||
memory: perf.memory || null,
|
||||
resources: perf.getEntriesByType("resource").slice(0, 500),
|
||||
id: USER.id
|
||||
};
|
||||
mws.send(JSON.stringify(payload));
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('popstate', () => {
|
||||
console.log("[Metrics] Запуск sendMetrics після popstate");
|
||||
sendMetrics();
|
||||
});
|
||||
|
||||
window.addEventListener('click', (e) => {
|
||||
const target = e.target.closest('[data-route]');
|
||||
if (!target || !target.href) return;
|
||||
|
||||
console.log("[Metrics] Запуск sendMetrics після click");
|
||||
sendMetrics();
|
||||
});
|
||||
@@ -2,7 +2,7 @@
|
||||
const webPush = {
|
||||
async init() {
|
||||
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
|
||||
console.error('Push повідомлення не підтримуються');
|
||||
console.error('[WebPush] Push повідомлення не підтримуються');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ const webPush = {
|
||||
// Запитуємо дозвіл
|
||||
const permission = await Notification.requestPermission();
|
||||
if (permission !== 'granted') {
|
||||
console.warn('Push повідомлення заборонено користувачем');
|
||||
console.warn('[WebPush] Push повідомлення заборонено користувачем');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,15 +63,15 @@ const webPush = {
|
||||
body: JSON.stringify({ subscription, device: deviceInfo })
|
||||
});
|
||||
|
||||
console.log('Push підписка готова:', subscription);
|
||||
console.log('[WebPush] Push підписка готова:', subscription);
|
||||
|
||||
console.log('Створено нову підписку');
|
||||
console.log('[WebPush] Створено нову підписку');
|
||||
} else {
|
||||
console.log('Підписка вже існує');
|
||||
console.log('[WebPush] Підписка вже існує');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error('Помилка ініціалізації push:', err);
|
||||
console.error('[WebPush] Помилка ініціалізації push:', err);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -85,7 +85,7 @@ const webPush = {
|
||||
const success = await subscription.unsubscribe();
|
||||
|
||||
if (success) {
|
||||
console.log("Локальна підписка скасована");
|
||||
console.log("[WebPush] Локальна підписка скасована");
|
||||
|
||||
// повідомляємо сервер
|
||||
await fetch(`${CONFIG.api}push//unsubscribe`, {
|
||||
@@ -98,7 +98,7 @@ const webPush = {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("Підписки немає");
|
||||
console.log("[WebPush] Підписки немає");
|
||||
}
|
||||
}
|
||||
}
|
||||
326
web/lib/customElements/notification.js
Normal file
326
web/lib/customElements/notification.js
Normal file
@@ -0,0 +1,326 @@
|
||||
class AppNotificationContainer extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
|
||||
// Настройки по умолчанию
|
||||
this._timeout = 4000;
|
||||
this._maxVisible = 5;
|
||||
this._position = 'top-right';
|
||||
this._mobileBottomEnabled = false;
|
||||
|
||||
this._container = document.createElement('div');
|
||||
this._container.className = 'app-notification-container';
|
||||
this.shadowRoot.appendChild(this._container);
|
||||
|
||||
this._insertStyles();
|
||||
|
||||
this._icons = {
|
||||
info: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M 15 3 C 13.895 3 13 3.895 13 5 L 13 5.2929688 C 10.109011 6.1538292 8 8.8293311 8 12 L 8 14.757812 C 8 17.474812 6.921 20.079 5 22 A 1 1 0 0 0 4 23 A 1 1 0 0 0 5 24 L 25 24 A 1 1 0 0 0 26 23 A 1 1 0 0 0 25 22 C 23.079 20.079 22 17.474812 22 14.757812 L 22 12 C 22 8.8293311 19.890989 6.1538292 17 5.2929688 L 17 5 C 17 3.895 16.105 3 15 3 z M 3.9550781 7.9882812 A 1.0001 1.0001 0 0 0 3.1054688 8.5527344 C 3.1054688 8.5527344 2 10.666667 2 13 C 2 15.333333 3.1054687 17.447266 3.1054688 17.447266 A 1.0001165 1.0001165 0 0 0 4.8945312 16.552734 C 4.8945312 16.552734 4 14.666667 4 13 C 4 11.333333 4.8945313 9.4472656 4.8945312 9.4472656 A 1.0001 1.0001 0 0 0 3.9550781 7.9882812 z M 26.015625 7.9882812 A 1.0001 1.0001 0 0 0 25.105469 9.4472656 C 25.105469 9.4472656 26 11.333333 26 13 C 26 14.666667 25.105469 16.552734 25.105469 16.552734 A 1.0001163 1.0001163 0 1 0 26.894531 17.447266 C 26.894531 17.447266 28 15.333333 28 13 C 28 10.666667 26.894531 8.5527344 26.894531 8.5527344 A 1.0001 1.0001 0 0 0 26.015625 7.9882812 z M 12 26 C 12 27.657 13.343 29 15 29 C 16.657 29 18 27.657 18 26 L 12 26 z"/></svg>`,
|
||||
success: `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' style="width: 17px;height: 17px;"><path d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/></svg>`,
|
||||
warn: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"> <path d="M 15 3 C 14.168432 3 13.456063 3.5067238 13.154297 4.2285156 L 2.3007812 22.947266 L 2.3007812 22.949219 A 2 2 0 0 0 2 24 A 2 2 0 0 0 4 26 A 2 2 0 0 0 4.140625 25.994141 L 4.1445312 26 L 15 26 L 25.855469 26 L 25.859375 25.992188 A 2 2 0 0 0 26 26 A 2 2 0 0 0 28 24 A 2 2 0 0 0 27.699219 22.947266 L 27.683594 22.919922 A 2 2 0 0 0 27.681641 22.917969 L 16.845703 4.2285156 C 16.543937 3.5067238 15.831568 3 15 3 z M 13.787109 11.359375 L 16.212891 11.359375 L 16.011719 17.832031 L 13.988281 17.832031 L 13.787109 11.359375 z M 15.003906 19.810547 C 15.825906 19.810547 16.318359 20.252813 16.318359 21.007812 C 16.318359 21.748812 15.825906 22.189453 15.003906 22.189453 C 14.175906 22.189453 13.679688 21.748813 13.679688 21.007812 C 13.679688 20.252813 14.174906 19.810547 15.003906 19.810547 z"/></svg>`,
|
||||
error: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30"><path d="M 10.03125 2.9042969 A 1.0001 1.0001 0 0 0 9.0605469 4.2578125 C 9.0605469 4.2578125 9.7494688 5.9996671 11.488281 7.0800781 C 11.080693 7.2039808 10.414387 7.5446908 9.8027344 8.4570312 C 9.4322834 8.3403526 9.1037156 8.2185154 8.8496094 8.0976562 C 8.3997876 7.8837103 8.118603 7.6807271 7.9589844 7.546875 C 7.7993657 7.4130229 7.8046875 7.40625 7.8046875 7.40625 A 1.0001 1.0001 0 0 0 7 7 L 5 7 A 1.0001 1.0001 0 1 0 5 9 L 6.5957031 9 C 6.6368531 9.038228 6.6271651 9.038995 6.6738281 9.078125 C 6.961397 9.3192729 7.3888062 9.6162897 7.9902344 9.9023438 C 9.1930908 10.474451 11.083447 11 13.935547 11 A 1.0001 1.0001 0 0 0 14.140625 10.980469 C 14.430223 10.987386 14.697172 11 15.017578 11 C 15.326932 11 15.582943 10.988887 15.863281 10.982422 A 1.0001 1.0001 0 0 0 16.064453 11 C 18.916553 11 20.806909 10.474451 22.009766 9.9023438 C 22.611194 9.6162897 23.038603 9.3192729 23.326172 9.078125 C 23.372834 9.0389949 23.363147 9.0382279 23.404297 9 L 25 9 A 1.0001 1.0001 0 1 0 25 7 L 23 7 A 1.0001 1.0001 0 0 0 22.195312 7.40625 C 22.195312 7.40625 22.200612 7.41302 22.041016 7.546875 C 21.881397 7.6807271 21.600212 7.8837103 21.150391 8.0976562 C 20.891444 8.2208175 20.55751 8.3444389 20.177734 8.4628906 C 19.558423 7.539139 18.907199 7.1978378 18.517578 7.0761719 C 20.252095 5.9954925 20.939453 4.2578125 20.939453 4.2578125 A 1.0001 1.0001 0 0 0 20.039062 2.9042969 A 1.0001 1.0001 0 0 0 19.060547 3.5761719 C 19.060547 3.5761719 18.556779 5.088719 16.882812 5.6757812 C 16.36708 5.2573881 15.71568 5 15 5 C 14.284868 5 13.632808 5.2576596 13.117188 5.6757812 C 11.443221 5.088719 10.939453 3.5761719 10.939453 3.5761719 A 1.0001 1.0001 0 0 0 10.03125 2.9042969 z M 5.9628906 11 A 1.0001 1.0001 0 0 0 5.6835938 11.050781 L 2.6835938 12.050781 A 1.0005646 1.0005646 0 0 0 3.3164062 13.949219 L 5.9707031 13.064453 C 6.0672386 13.111686 6.3494962 13.235909 6.5917969 13.34375 C 6.2888038 14.107541 6 15.15686 6 16.425781 C 6 17.948961 6.3086267 19.289595 6.7949219 20.453125 L 6.1601562 22.357422 L 3.4453125 24.167969 A 1.0001 1.0001 0 1 0 4.5546875 25.832031 L 7.5546875 23.832031 A 1.0001 1.0001 0 0 0 7.9492188 23.316406 L 8.1367188 22.751953 C 9.8000084 24.902319 11.988204 26 13 26 C 13.742 26 14 25.42 14 25 L 14 16 C 14 15.447 14.448 15 15 15 C 15.552 15 16 15.447 16 16 L 16 25 C 16 25.42 16.258 26 17 26 C 18.011796 26 20.199992 24.902319 21.863281 22.751953 L 22.050781 23.316406 A 1.0001 1.0001 0 0 0 22.445312 23.832031 L 25.445312 25.832031 A 1.0001 1.0001 0 1 0 26.554688 24.167969 L 23.839844 22.357422 L 23.205078 20.453125 C 23.691373 19.289595 24 17.948961 24 16.425781 C 24 15.15686 23.711196 14.107541 23.408203 13.34375 C 23.650504 13.235909 23.932762 13.111686 24.029297 13.064453 L 26.683594 13.949219 A 1.0005646 1.0005646 0 1 0 27.316406 12.050781 L 24.316406 11.050781 A 1.0001 1.0001 0 0 0 24.021484 11 A 1.0001 1.0001 0 0 0 23.552734 11.105469 C 23.552734 11.105469 20.660591 12.508607 17.513672 12.896484 C 16.740098 12.956149 15.915495 13 15 13 C 14.084505 13 13.259902 12.956149 12.486328 12.896484 C 9.3394093 12.508607 6.4472656 11.105469 6.4472656 11.105469 A 1.0001 1.0001 0 0 0 5.9628906 11 z"/></svg>`
|
||||
};
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return ['timeout', 'max-visible', 'position', 'mobile-position'];
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this._updateSettings();
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (oldValue !== newValue) {
|
||||
this._updateSettings();
|
||||
}
|
||||
}
|
||||
|
||||
_updateSettings() {
|
||||
this._position = this.getAttribute('position') || 'top-right';
|
||||
this._maxVisible = parseInt(this.getAttribute('max-visible')) || 5;
|
||||
this._timeout = parseInt(this.getAttribute('timeout')) || 4000;
|
||||
|
||||
const mobilePosAttr = this.getAttribute('mobile-position');
|
||||
// Если атрибут установлен в 'bottom' или присутствует (как пустая строка, если это булевый атрибут)
|
||||
this._mobileBottomEnabled = mobilePosAttr === 'bottom' || mobilePosAttr === '';
|
||||
|
||||
this._container.setAttribute('data-position', this._position);
|
||||
|
||||
this._applyMobileStyles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Динамически применяет класс, который активирует мобильные стили "только снизу".
|
||||
*/
|
||||
_applyMobileStyles() {
|
||||
if (this._mobileBottomEnabled) {
|
||||
this._container.classList.add('mobile-bottom');
|
||||
} else {
|
||||
this._container.classList.remove('mobile-bottom');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Публичный метод для изменения настройки мобильной позиции во время выполнения.
|
||||
* @param {boolean} enable - true, чтобы принудительно устанавливать позицию снизу на мобильных, false, чтобы использовать обычные @media стили.
|
||||
*/
|
||||
setMobileBottom(enable) {
|
||||
this._mobileBottomEnabled = !!enable;
|
||||
this._applyMobileStyles();
|
||||
}
|
||||
|
||||
show(message, options = {}) {
|
||||
const {
|
||||
type = 'info',
|
||||
timeout = this._timeout,
|
||||
title,
|
||||
onClick,
|
||||
lock
|
||||
} = options;
|
||||
|
||||
const content = typeof message === 'string'
|
||||
? { title: title || '', text: message }
|
||||
: message;
|
||||
|
||||
while (this._container.children.length >= this._maxVisible) {
|
||||
const first = this._container.firstElementChild;
|
||||
if (first) first.remove();
|
||||
else break;
|
||||
}
|
||||
|
||||
const node = document.createElement('div');
|
||||
node.className = `app-notification ${type}`;
|
||||
if (onClick) node.style.cursor = "pointer"
|
||||
|
||||
const icon = document.createElement('div');
|
||||
icon.className = 'icon';
|
||||
icon.innerHTML = this._icons[type] || this._icons.info;
|
||||
|
||||
const body = document.createElement('div');
|
||||
body.className = 'body';
|
||||
if (content.title) {
|
||||
const t = document.createElement('div');
|
||||
t.className = 'title';
|
||||
t.textContent = content.title;
|
||||
body.appendChild(t);
|
||||
}
|
||||
const txt = document.createElement('div');
|
||||
txt.className = 'text';
|
||||
txt.textContent = content.text || '';
|
||||
body.appendChild(txt);
|
||||
|
||||
node.appendChild(icon);
|
||||
node.appendChild(body);
|
||||
|
||||
if (!onClick && !lock) {
|
||||
const closeDiv = document.createElement('div');
|
||||
closeDiv.className = 'blockClose';
|
||||
node.appendChild(closeDiv);
|
||||
|
||||
const closeBtn = document.createElement('button');
|
||||
closeBtn.className = 'close';
|
||||
closeBtn.setAttribute('aria-label', 'Закрыть уведомление');
|
||||
closeBtn.innerHTML = '<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>';
|
||||
closeDiv.appendChild(closeBtn);
|
||||
closeBtn.addEventListener('click', () => this._removeNode(node));
|
||||
}
|
||||
|
||||
this._container.appendChild(node);
|
||||
requestAnimationFrame(() => node.classList.add('show'));
|
||||
|
||||
let timer = null;
|
||||
const startTimer = () => {
|
||||
if (timeout === 0) return;
|
||||
timer = setTimeout(() => this._removeNode(node), timeout);
|
||||
};
|
||||
const clearTimer = () => { if (timer) { clearTimeout(timer); timer = null; } };
|
||||
|
||||
node.addEventListener('mouseenter', clearTimer);
|
||||
node.addEventListener('mouseleave', startTimer);
|
||||
|
||||
if (typeof onClick === 'function') {
|
||||
node.addEventListener('click', () => {
|
||||
try { onClick(); } catch (e) { }
|
||||
this._removeNode(node);
|
||||
});
|
||||
}
|
||||
|
||||
startTimer();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
_removeNode(node) {
|
||||
if (!node || !node.parentElement) return;
|
||||
node.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
if (node && node.parentElement) node.parentElement.removeChild(node);
|
||||
}, 200);
|
||||
}
|
||||
|
||||
clearAll() {
|
||||
if (!this._container) return;
|
||||
Array.from(this._container.children).forEach(n => this._removeNode(n));
|
||||
}
|
||||
|
||||
info(message, opts = {}) { return this.show(message, { ...opts, type: 'info' }); }
|
||||
success(message, opts = {}) { return this.show(message, { ...opts, type: 'success' }); }
|
||||
warn(message, opts = {}) { return this.show(message, { ...opts, type: 'warn' }); }
|
||||
error(message, opts = {}) { return this.show(message, { ...opts, type: 'error' }); }
|
||||
click(message, opts = {}) { return this.show(message, { ...opts, onClick: opts.f }); }
|
||||
|
||||
|
||||
_insertStyles() {
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.app-notification-container {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
}
|
||||
.app-notification-container[data-position="top-right"] { top: 8px; right: 8px; align-items: flex-end; }
|
||||
.app-notification-container[data-position="top-left"] { top: 8px; left: 8px; align-items: flex-start; }
|
||||
.app-notification-container[data-position="bottom-right"] { bottom: 8px; right: 8px; align-items: flex-end; }
|
||||
.app-notification-container[data-position="bottom-left"] { bottom: 8px; left: 8px; align-items: flex-start; }
|
||||
|
||||
.app-notification {
|
||||
pointer-events: auto;
|
||||
min-width: 220px;
|
||||
max-width: 360px;
|
||||
background: #111;
|
||||
color: #fff;
|
||||
padding: 10px 12px 10px 12px;
|
||||
border-radius: var(--border-radius, 8px);
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
|
||||
font-size: var(--FontSize2, 14px);
|
||||
line-height: 1.2;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transform: translateY(-6px) scale(0.995);
|
||||
transition: opacity .18s ease, transform .18s ease;
|
||||
position: relative;
|
||||
}
|
||||
.app-notification.show {
|
||||
opacity: 0.95;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
.app-notification .icon {
|
||||
font-size: 18px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
border-radius: calc(var(--border-radius, 8px) - 5px);
|
||||
padding: 8px;
|
||||
}
|
||||
.app-notification .icon svg{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: #fff;
|
||||
}
|
||||
.app-notification .body { flex:1; }
|
||||
.app-notification .title { font-weight: 600; margin-bottom: 4px; font-size: 13px; }
|
||||
.app-notification .blockClose {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.app-notification .blockClose .close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
margin-left: 8px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
.app-notification .blockClose .close svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
fill: #fff;
|
||||
opacity: 0.8;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.app-notification.info {
|
||||
background: var(--ColorThemes3, #2196F3);
|
||||
color: var(--ColorThemes0, #ffffff);
|
||||
}
|
||||
.app-notification.info .icon { background: var(--ColorThemes0, #ffffff); }
|
||||
.app-notification.info .icon svg{fill: var(--ColorThemes3, #2196F3);}
|
||||
.app-notification.info .close svg{fill: var(--ColorThemes0, #ffffff);}
|
||||
|
||||
.app-notification.success { background: #52ac56; }
|
||||
.app-notification.success .icon { background: #6dc450; }
|
||||
.app-notification.success .close svg{fill: #fff;}
|
||||
|
||||
.app-notification.warn { background: #d18515; }
|
||||
.app-notification.warn .icon { background: #eaad57; }
|
||||
.app-notification.warn .close svg{fill: #fff;}
|
||||
|
||||
.app-notification.error { background: #9c2424; }
|
||||
.app-notification.error .icon { background: #c45050; }
|
||||
.app-notification.error .close svg{fill: #fff;}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.app-notification-container {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: calc(100% - 24px);
|
||||
align-items: center !important;
|
||||
}
|
||||
.app-notification-container .app-notification {
|
||||
max-width: 95%;
|
||||
min-width: 95%;
|
||||
}
|
||||
|
||||
.app-notification-container.mobile-bottom {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
this.shadowRoot.appendChild(style);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('app-notification-container', AppNotificationContainer);
|
||||
|
||||
/* <app-notification-container
|
||||
id="notif-manager"
|
||||
position="top-right"
|
||||
max-visible="5"
|
||||
timeout="4000"
|
||||
mobile-position>
|
||||
</app-notification-container> */
|
||||
// const Notifier = document.getElementById('notif-manager');
|
||||
|
||||
// 💡 Включить принудительную позицию снизу для мобильных
|
||||
// Notifier.setMobileBottom(true);
|
||||
|
||||
// 💡 Отключить принудительную позицию снизу (вернется к поведению @media или position)
|
||||
// Notifier.setMobileBottom(false);
|
||||
|
||||
|
||||
// Пример использования
|
||||
|
||||
// Notifier.info('Настройки мобильной позиции изменены.');
|
||||
// Notifier.info('Привет! Это ваше первое уведомление через Web Component.', {
|
||||
// title: 'Успешная инициализация',
|
||||
// onClick: () => alert('Вы кликнули!'),
|
||||
// lock: false
|
||||
// });
|
||||
// Notifier.success('Успешная операция.');
|
||||
// Notifier.error('Критическая ошибка!', { timeout: 0, lock: true });
|
||||
// Notifier.warn({ title: `Metrics`, text: `З'єднання встановлено` }, { timeout: 0 });
|
||||
323
web/lib/customElements/territoryCard.js
Normal file
323
web/lib/customElements/territoryCard.js
Normal file
@@ -0,0 +1,323 @@
|
||||
const appTerritoryCardStyles = new CSSStyleSheet();
|
||||
appTerritoryCardStyles.replaceSync(`
|
||||
:host {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
}
|
||||
@media (max-width: 2300px) {
|
||||
:host {
|
||||
width: calc((100% / 5) - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1960px) {
|
||||
:host {
|
||||
width: calc((100% / 4) - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1640px) {
|
||||
:host {
|
||||
width: calc((100% / 3) - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
:host {
|
||||
width: calc((100% / 2) - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
:host {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.card {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background-color: var(--ColorThemes2, #525151);
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
border-radius: calc(var(--border-radius, 15px) - 5px);
|
||||
}
|
||||
@media(hover: hover) {
|
||||
.card:hover {
|
||||
opacity: 0.8;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
filter: blur(3px);
|
||||
}
|
||||
.contents {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
background: rgb(64 64 64 / 0.7);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
justify-content: space-between;
|
||||
border-radius: calc(var(--border-radius, 15px) - 5px);
|
||||
}
|
||||
.address {
|
||||
margin: 10px;
|
||||
height: 35px;
|
||||
display: flex;
|
||||
background: var(--ColorThemes0, #1c1c19);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--FontSize3, 14px);
|
||||
color: var(--ColorThemes3, #f3f3f3);
|
||||
border-radius: calc(var(--border-radius, 15px) - 5px - 4px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Стили для режима 'sheep' */
|
||||
.sheep {
|
||||
margin: 10px;
|
||||
max-height: 50px;
|
||||
border-radius: calc(var(--border-radius, 15px) - 5px - 4px);
|
||||
padding: 10px 0;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
background: var(--PrimaryColor, #cb9e44);
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.sheep span {
|
||||
color: var(--PrimaryColorText, #2e2e2e);
|
||||
font-size: var(--FontSize3, 14px);
|
||||
font-weight: 400;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.sheep p {
|
||||
color: var(--PrimaryColorText, #2e2e2e);
|
||||
font-size: var(--FontSize4, 15px);
|
||||
font-weight: 400;
|
||||
margin: 5px 0 0 0;
|
||||
}
|
||||
|
||||
|
||||
/* Стили для режима 'info' (прогресс) */
|
||||
.info {
|
||||
margin: 10px;
|
||||
}
|
||||
.info > div {
|
||||
position: relative;
|
||||
background-color: var(--ColorThemes0, #1c1c19);;
|
||||
border-radius: calc(var(--border-radius, 15px) - 5px - 4px);
|
||||
overflow: hidden;
|
||||
padding: 5px 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-height: 25px;
|
||||
}
|
||||
.info span {
|
||||
z-index: 2;
|
||||
font-size: var(--FontSize1, 12px);
|
||||
color: var(--ColorThemes3, #f3f3f3);
|
||||
}
|
||||
.info p {
|
||||
z-index: 2;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
font-size: var(--FontSize3, 14px);
|
||||
color: var(--ColorThemes3, #f3f3f3);
|
||||
}
|
||||
.progress {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
background-color: var(--PrimaryColor, #cb9e44);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
a {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
`);
|
||||
|
||||
class AppTerritoryCard extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
|
||||
if (this.shadowRoot.adoptedStyleSheets) {
|
||||
this.shadowRoot.adoptedStyleSheets = [appTerritoryCardStyles];
|
||||
}
|
||||
}
|
||||
|
||||
// Определяем, какие атрибуты будем отслеживать
|
||||
static get observedAttributes() {
|
||||
return ['image', 'address', 'sheep', 'link', 'atWork', 'quantity'];
|
||||
}
|
||||
|
||||
get image() {
|
||||
return this.getAttribute('image');
|
||||
}
|
||||
set image(newValue) {
|
||||
if (newValue === null) {
|
||||
this.removeAttribute('image');
|
||||
} else {
|
||||
this.setAttribute('image', newValue);
|
||||
}
|
||||
}
|
||||
|
||||
get address() {
|
||||
return this.getAttribute('address');
|
||||
}
|
||||
set address(newValue) {
|
||||
if (newValue === null) {
|
||||
this.removeAttribute('address');
|
||||
} else {
|
||||
this.setAttribute('address', newValue);
|
||||
}
|
||||
}
|
||||
|
||||
get sheep() {
|
||||
return this.getAttribute('sheep');
|
||||
}
|
||||
set sheep(newValue) {
|
||||
if (newValue === null) {
|
||||
this.removeAttribute('sheep');
|
||||
} else {
|
||||
this.setAttribute('sheep', newValue);
|
||||
}
|
||||
}
|
||||
|
||||
get link() {
|
||||
return this.getAttribute('link');
|
||||
}
|
||||
set link(newValue) {
|
||||
if (newValue === null) {
|
||||
this.removeAttribute('link');
|
||||
} else {
|
||||
this.setAttribute('link', newValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get atWork() {
|
||||
return this.getAttribute('atWork');
|
||||
}
|
||||
set atWork(newValue) {
|
||||
if (newValue === null) {
|
||||
this.removeAttribute('atWork');
|
||||
} else {
|
||||
this.setAttribute('atWork', String(newValue));
|
||||
}
|
||||
}
|
||||
|
||||
get quantity() {
|
||||
return this.getAttribute('quantity');
|
||||
}
|
||||
set quantity(newValue) {
|
||||
if (newValue === null) {
|
||||
this.removeAttribute('quantity');
|
||||
} else {
|
||||
this.setAttribute('quantity', String(newValue));
|
||||
}
|
||||
}
|
||||
|
||||
// Вызывается при добавлении элемента в DOM
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
// Вызывается при изменении одного из отслеживаемых атрибутов
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (oldValue !== newValue) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const image = this.getAttribute('image') || '';
|
||||
const address = this.getAttribute('address') || '';
|
||||
const sheep = this.getAttribute('sheep'); // Может быть null или ""
|
||||
const link = this.getAttribute('link') || '#';
|
||||
const atWork = this.getAttribute('atWork'); // Может быть null
|
||||
const quantity = this.getAttribute('quantity'); // Может быть null
|
||||
|
||||
// --- Логика определения контента ---
|
||||
let contentHTML = '';
|
||||
const isProgressMode = atWork !== null && quantity !== null && !isNaN(parseInt(atWork)) && !isNaN(parseInt(quantity));
|
||||
const hasSheep = sheep !== null && sheep !== '';
|
||||
|
||||
if (isProgressMode) {
|
||||
// Режим прогресса (свободные подъезды)
|
||||
const atWorkNum = parseInt(atWork);
|
||||
const quantityNum = parseInt(quantity);
|
||||
|
||||
const free = quantityNum - atWorkNum;
|
||||
const progressPercent = quantityNum > 0 ? (atWorkNum / quantityNum) * 100 : 100;
|
||||
|
||||
contentHTML = `
|
||||
<div class="info">
|
||||
<div>
|
||||
<div class="progress" style="width: ${progressPercent}%"></div>
|
||||
<span>Вільні під'їзди:</span>
|
||||
<p>${free} / ${quantityNum}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else if (sheep !== null && sheep !== '') {
|
||||
// Режим ответственного
|
||||
contentHTML = `
|
||||
<div class="sheep">
|
||||
<span>Територію опрацьовує:</span>
|
||||
<p>${sheep}</p>
|
||||
</div>
|
||||
`;
|
||||
} else if (sheep !== null) {
|
||||
// Режим "не опрацьовується"
|
||||
contentHTML = `
|
||||
<div class="sheep">
|
||||
<span>Територія не опрацьовується</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// --- Сборка всего шаблона ---
|
||||
this.shadowRoot.innerHTML = `
|
||||
<div class="card">
|
||||
<img src="${image}" alt="${address}" />
|
||||
<div class="contents">
|
||||
<h1 class="address">${address}</h1>
|
||||
${contentHTML}
|
||||
</div>
|
||||
<a href="${link}" data-route=""></a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Регистрируем веб-компонент
|
||||
customElements.define('app-territory-card', AppTerritoryCard);
|
||||
|
||||
// document.getElementById('app-territory-card-1').setAttribute('sheep', 'test')
|
||||
|
||||
@@ -27,8 +27,8 @@ const Home = {
|
||||
return Home.personal.house.list;
|
||||
},
|
||||
setHTML: async () => {
|
||||
const list = Home.personal.house.list.length > 0
|
||||
? Home.personal.house.list
|
||||
const list = Home.personal.house.list.length > 0
|
||||
? Home.personal.house.list
|
||||
: await Home.personal.house.loadAPI();
|
||||
|
||||
if (USER.possibilities.can_view_territory && list.length)
|
||||
@@ -53,8 +53,8 @@ const Home = {
|
||||
return Home.personal.homestead.list;
|
||||
},
|
||||
setHTML: async () => {
|
||||
const list = Home.personal.homestead.list.length > 0
|
||||
? Home.personal.homestead.list
|
||||
const list = Home.personal.homestead.list.length > 0
|
||||
? Home.personal.homestead.list
|
||||
: await Home.personal.homestead.loadAPI();
|
||||
|
||||
if (USER.possibilities.can_view_territory && list.length)
|
||||
@@ -81,8 +81,8 @@ const Home = {
|
||||
return Home.group.house.list;
|
||||
},
|
||||
setHTML: async () => {
|
||||
const list = Home.group.house.list.length > 0
|
||||
? Home.group.house.list
|
||||
const list = Home.group.house.list.length > 0
|
||||
? Home.group.house.list
|
||||
: await Home.group.house.loadAPI();
|
||||
|
||||
if (USER.possibilities.can_view_territory && list.length)
|
||||
@@ -107,8 +107,8 @@ const Home = {
|
||||
return Home.group.homestead.list;
|
||||
},
|
||||
setHTML: async () => {
|
||||
const list = Home.group.homestead.list.length > 0
|
||||
? Home.group.homestead.list
|
||||
const list = Home.group.homestead.list.length > 0
|
||||
? Home.group.homestead.list
|
||||
: await Home.group.homestead.loadAPI();
|
||||
|
||||
if (USER.possibilities.can_view_territory && list.length)
|
||||
@@ -124,18 +124,10 @@ const Home = {
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
for (const el of list) {
|
||||
const card = document.createElement("div");
|
||||
card.className = "card";
|
||||
|
||||
card.innerHTML = `
|
||||
<i style="background-image: url(${CONFIG.web}cards/${type}/${type === "house" ? "T" : "H"}${el.id}.webp);"></i>
|
||||
<div class="contents">
|
||||
<div class="info">
|
||||
<div><p>${el.title} ${el.number}</p></div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/territory/card/${type}/${el.id}" data-route></a>
|
||||
`;
|
||||
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.link = `/territory/card/${type}/${el.id}`;
|
||||
fragment.appendChild(card);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,123 +102,6 @@
|
||||
overflow-y: auto;
|
||||
align-items: flex-start;
|
||||
transition: .3s ease;
|
||||
}
|
||||
|
||||
.page-home .card {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
background-color: var(--ColorThemes2);
|
||||
margin: 10px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
border-radius: calc(var(--border-radius) - 5px);
|
||||
}
|
||||
|
||||
@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-color: var(--PrimaryColor);
|
||||
border-radius: calc(var(--border-radius) - 5px);
|
||||
}
|
||||
|
||||
.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: stretch;
|
||||
justify-content: space-between;
|
||||
border-radius: calc(var(--border-radius) - 5px);
|
||||
}
|
||||
|
||||
.page-home .info {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.page-home .info>div {
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
display: flex;
|
||||
background: var(--ColorThemes0);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--FontSize1);
|
||||
color: var(--ColorThemes3);
|
||||
border-radius: calc(var(--border-radius) - 5px - 4px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.page-home .info>div>span {
|
||||
color: var(--ColorThemes3);
|
||||
font-size: var(--FontSize3);
|
||||
font-weight: 300;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.page-home .info>div>p {
|
||||
color: var(--ColorThemes3);
|
||||
font-size: var(--FontSize3);
|
||||
font-weight: 400;
|
||||
gap: 20px;
|
||||
padding: 10px;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
3
web/lib/pages/schedule/constructor/index.html
Normal file
3
web/lib/pages/schedule/constructor/index.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="page-schedule-constructor">
|
||||
|
||||
</div>
|
||||
8
web/lib/pages/schedule/constructor/script.js
Normal file
8
web/lib/pages/schedule/constructor/script.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const Schedule_constructor = {
|
||||
init: async () => {
|
||||
let html = await fetch('/lib/pages/schedule/constructor/index.html').then((response) => response.text());
|
||||
app.innerHTML = html;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
6
web/lib/pages/schedule/constructor/style.css
Normal file
6
web/lib/pages/schedule/constructor/style.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.page-schedule-constructor {
|
||||
width: calc(100% - 40px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 20px 20px 0 20px;
|
||||
}
|
||||
@@ -86,6 +86,7 @@
|
||||
<option value="5">Група 5</option>
|
||||
<option value="6">Група 6</option>
|
||||
<option value="7">Група 7</option>
|
||||
<option value="8">Група 8</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="editor-blocks-inputs">
|
||||
@@ -335,6 +336,7 @@
|
||||
<option value="5">Група 5</option>
|
||||
<option value="6">Група 6</option>
|
||||
<option value="7">Група 7</option>
|
||||
<option value="8">Група 8</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -285,7 +285,6 @@ const Sheeps = {
|
||||
},
|
||||
setHTML: async (id, randomNumber) => {
|
||||
let sheep = await Sheeps.editor.loadAPI(id);
|
||||
console.log(sheep);
|
||||
|
||||
Router.navigate(`sheeps/${id}`, true, false);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<span>Розташування:</span>
|
||||
<p id="stand-info-title">--</p>
|
||||
</div>
|
||||
<div>
|
||||
<div id="stand-info-block-geo" >
|
||||
<span>Геолокація:</span>
|
||||
<a id="stand-info-geo" target="_blank">--</a>
|
||||
</div>
|
||||
|
||||
@@ -23,14 +23,23 @@ const Stand_card = {
|
||||
|
||||
info: {
|
||||
list: [],
|
||||
sheeps: [],
|
||||
|
||||
async setHTML() {
|
||||
const url = `${CONFIG.api}stand/${Stand_card.id}`;
|
||||
this.list = await Stand_card.loadAPI(url);
|
||||
this.list = await Stand_card.loadAPI(`${CONFIG.api}stand/${Stand_card.id}`);
|
||||
this.sheeps = await Stand_card.loadAPI(`${CONFIG.api}sheeps/list/stand`);
|
||||
|
||||
|
||||
document.getElementById('stand-info-title').innerText = this.list.title;
|
||||
document.getElementById('stand-info-geo').innerHTML = 'Відкрити Google Maps';
|
||||
document.getElementById('stand-info-geo').href = `https://www.google.com/maps?q=${this.list.geo[0]},${this.list.geo[1]}`;
|
||||
|
||||
if(this.list.geo[0]>0){
|
||||
document.getElementById('stand-info-geo').innerHTML = 'Відкрити Google Maps';
|
||||
document.getElementById('stand-info-geo').href = `https://www.google.com/maps?q=${this.list.geo[0]},${this.list.geo[1]}`;
|
||||
document.getElementById('stand-info-block-geo').style.display = "";
|
||||
} else {
|
||||
document.getElementById('stand-info-block-geo').style.display = "none";
|
||||
}
|
||||
|
||||
document.getElementById('stand-info-image').setAttribute('src', '');
|
||||
|
||||
Stand_card.schedule.setHTML();
|
||||
@@ -42,7 +51,7 @@ const Stand_card = {
|
||||
update(msg) {
|
||||
const { type, data, user } = msg;
|
||||
const el = document.getElementById(`name-${data?.id}`);
|
||||
if (!el) return; // если элемент не найден — выходим
|
||||
if (!el) return; // якщо елемент не знайдено - виходимо
|
||||
|
||||
const isSelf = user.id == USER.id;
|
||||
|
||||
@@ -56,7 +65,7 @@ const Stand_card = {
|
||||
break;
|
||||
|
||||
case "stand_unlocking":
|
||||
// Разблокируем только если событие от другого пользователя
|
||||
// Розблокуємо лише якщо подія від іншого користувача
|
||||
if (!isSelf) {
|
||||
el.style.border = "";
|
||||
el.style.backgroundColor = "";
|
||||
@@ -70,18 +79,18 @@ const Stand_card = {
|
||||
const sid = data.sheep_id;
|
||||
const sname = data.sheep_name ?? "";
|
||||
|
||||
// Менеджеру показываем весь список
|
||||
// Менеджеру показуємо весь перелік
|
||||
if (USER.possibilities.can_manager_stand && Array.isArray(Sheeps?.sheeps_list?.list)) {
|
||||
el.innerHTML = "";
|
||||
|
||||
// пустой вариант
|
||||
// порожній варіант
|
||||
el.appendChild(Object.assign(document.createElement("option"), {
|
||||
value: "",
|
||||
textContent: " "
|
||||
}));
|
||||
|
||||
// заполняем всех овечек
|
||||
Sheeps.sheeps_list.list.forEach(s => {
|
||||
// заповнюємо всіх овечок
|
||||
Stand_card.info.sheeps.forEach(s => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = s.id;
|
||||
opt.textContent = s.name;
|
||||
@@ -91,7 +100,7 @@ const Stand_card = {
|
||||
|
||||
el.removeAttribute("disabled");
|
||||
} else {
|
||||
// Обычное поведение для обычных пользователей
|
||||
// Звичайна поведінка для звичайних користувачів
|
||||
if (sid == USER.id) {
|
||||
el.innerHTML = `<option value=""></option><option selected value="${USER.id}">${USER.name}</option>`;
|
||||
el.removeAttribute("disabled");
|
||||
@@ -130,7 +139,7 @@ const Stand_card = {
|
||||
}
|
||||
};
|
||||
|
||||
if (Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
Cloud.socket.send(JSON.stringify(message));
|
||||
} else {
|
||||
if (confirm("З'єднання розірвано! Перепідключитись?")) {
|
||||
@@ -152,7 +161,7 @@ const Stand_card = {
|
||||
}
|
||||
};
|
||||
|
||||
if (Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
Cloud.socket.send(JSON.stringify(message));
|
||||
} else {
|
||||
if (confirm("З'єднання розірвано! Перепідключитись?")) {
|
||||
@@ -177,14 +186,14 @@ const Stand_card = {
|
||||
};
|
||||
|
||||
if (USER.possibilities.can_manager_stand) {
|
||||
const pos = Sheeps.sheeps_list.list.map(e => e.id).indexOf(Number(sheep_id));
|
||||
const pos = Stand_card.info.sheeps.map(e => e.id).indexOf(Number(sheep_id));
|
||||
if (pos != -1) {
|
||||
let name = Sheeps.sheeps_list.list[pos].name;
|
||||
let name = Stand_card.info.sheeps[pos].name;
|
||||
message.data.sheep_name = name;
|
||||
}
|
||||
}
|
||||
|
||||
if (Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
Cloud.socket.send(JSON.stringify(message));
|
||||
} else {
|
||||
if (confirm("З'єднання розірвано! Перепідключитись?")) {
|
||||
@@ -272,10 +281,10 @@ const Stand_card = {
|
||||
textContent: " "
|
||||
}));
|
||||
|
||||
// если есть права менеджера — добавляем всех пользователей
|
||||
if (USER.possibilities.can_manager_stand && Array.isArray(Sheeps?.sheeps_list?.list)) {
|
||||
Sheeps.sheeps_list.list.sort((a, b) => a.name.localeCompare(b.name, 'uk'));
|
||||
Sheeps.sheeps_list.list.forEach(s => {
|
||||
// якщо є права менеджера - додаємо всіх користувачів
|
||||
if (USER.possibilities.can_manager_stand && Array.isArray(Stand_card.info.sheeps)) {
|
||||
Stand_card.info.sheeps.sort((a, b) => a.name.localeCompare(b.name, 'uk'));
|
||||
Stand_card.info.sheeps.forEach(s => {
|
||||
const option = document.createElement("option");
|
||||
option.value = s.id;
|
||||
option.textContent = s.name;
|
||||
@@ -283,7 +292,7 @@ const Stand_card = {
|
||||
select.appendChild(option);
|
||||
});
|
||||
} else {
|
||||
// если есть владелец — показываем его
|
||||
// якщо є власник - показуємо його
|
||||
const opt = document.createElement("option");
|
||||
if (sheep.sheep_id) {
|
||||
opt.value = sheep.sheep_id;
|
||||
@@ -296,13 +305,13 @@ const Stand_card = {
|
||||
select.appendChild(opt);
|
||||
}
|
||||
|
||||
// если занят другим пользователем — блокируем
|
||||
// якщо зайнятий іншим користувачем - блокуємо
|
||||
if (sheep.sheep_id && sheep.sheep_id !== USER.id && !USER.possibilities.can_manager_stand) {
|
||||
select.disabled = true;
|
||||
select.value = sheep.sheep_id;
|
||||
}
|
||||
|
||||
// --- обработчики ---
|
||||
// --- обробники ---
|
||||
select.addEventListener("mousedown", () => Stand_card.cloud.mess.locking({ id: sheep.id }));
|
||||
select.addEventListener("change", () => Stand_card.cloud.mess.update({ sheep_id: select.value, id: sheep.id }));
|
||||
select.addEventListener("blur", () => Stand_card.cloud.mess.unlocking({ id: sheep.id }));
|
||||
@@ -347,7 +356,7 @@ const Stand_card = {
|
||||
if (USER.possibilities.can_add_stand) {
|
||||
const btn = Object.assign(document.createElement("button"), {
|
||||
id: "stand-new-button",
|
||||
textContent: "Додати стенд(и)",
|
||||
textContent: "Додати день",
|
||||
onclick: () => Stand_card.addStand()
|
||||
});
|
||||
fragment.appendChild(btn);
|
||||
@@ -407,12 +416,14 @@ const Stand_card = {
|
||||
.then(response => {
|
||||
if (response.status == 200) {
|
||||
console.log({ 'setPack': 'ok' });
|
||||
button.innerText = "Стенд(и) додано";
|
||||
button.innerText = "День додано";
|
||||
Notifier.success('День додано');
|
||||
|
||||
return response.json()
|
||||
} else {
|
||||
console.log('err');
|
||||
button.innerText = "Помилка запису";
|
||||
Notifier.error('Помилка додавання');
|
||||
|
||||
return
|
||||
}
|
||||
@@ -423,16 +434,12 @@ const Stand_card = {
|
||||
Stand_card.schedule.setHTML();
|
||||
|
||||
setTimeout(() => {
|
||||
button.innerText = "Додати стенд(и)";
|
||||
button.innerText = "Додати день";
|
||||
}, 3000);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
button.innerText = "Помилка запису";
|
||||
})
|
||||
},
|
||||
|
||||
edit({ sheep_id, stand_id }) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -51,14 +51,12 @@
|
||||
type="text"
|
||||
id="info-geo_lat"
|
||||
name="geo_lat"
|
||||
required
|
||||
placeholder="49.5601856455014"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="info-geo_lng"
|
||||
name="geo_lng"
|
||||
required
|
||||
placeholder="25.625626212730406"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -50,11 +50,13 @@ const Stand_constructor = {
|
||||
if (response.status == 200) {
|
||||
console.log({ 'setPack': 'ok' });
|
||||
button.innerText = "Стенд додано";
|
||||
Notifier.success('Стенд створено');
|
||||
|
||||
return response.json()
|
||||
} else {
|
||||
console.log('err');
|
||||
button.innerText = "Помилка запису";
|
||||
Notifier.error('Помилка створення');
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -51,14 +51,12 @@
|
||||
type="text"
|
||||
id="info-geo_lat"
|
||||
name="geo_lat"
|
||||
required
|
||||
placeholder="49.5601856455014"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
id="info-geo_lng"
|
||||
name="geo_lng"
|
||||
required
|
||||
placeholder="25.625626212730406"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -83,11 +83,13 @@ const Stand_editor = {
|
||||
if (response.status == 200) {
|
||||
console.log({ 'setPack': 'ok' });
|
||||
button.innerText = "Стенд відредаговано";
|
||||
Notifier.success('Стенд відредаговано');
|
||||
|
||||
return response.json()
|
||||
} else {
|
||||
console.log('err');
|
||||
button.innerText = "Помилка при редагуванні";
|
||||
Notifier.error('Помилка при редагуванні');
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ const Stand_list = {
|
||||
},
|
||||
|
||||
renderCard: ({ element }) => {
|
||||
const imagesList = ['1.png', '2.png', '3.png', '4.png', '5.png', '6.png', '7.png', '8.png'];
|
||||
const imagesList = ['1.png', '5.png', '8.png', '7.png', '2.png', '3.png', '4.png', '6.png'];
|
||||
// const randomImage = images[Math.floor(Math.random() * images.length)];
|
||||
const image = imagesList[Stand_list.renderIndex % imagesList.length];
|
||||
Stand_list.renderIndex++;
|
||||
@@ -85,5 +85,5 @@ const Stand_list = {
|
||||
<a href="/stand/card/${element.id}" data-route></a>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -246,7 +246,8 @@
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
filter: brightness(0.9) contrast(80%) saturate(110%) drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.3));
|
||||
/* filter: brightness(0.9) contrast(80%) saturate(110%) drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.3)); */
|
||||
filter: blur(1px) brightness(0.7) contrast(80%) saturate(110%) drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.3));
|
||||
border-radius: calc(var(--border-radius) - 5px);
|
||||
}
|
||||
|
||||
@@ -266,7 +267,7 @@
|
||||
|
||||
.page-stand-list>details>#list>.card>.contents>.info>div {
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
background: var(--ColorThemes0);
|
||||
align-items: center;
|
||||
@@ -288,15 +289,15 @@
|
||||
|
||||
.page-stand-list>details>#list>.card>.contents>.info>div>p {
|
||||
color: var(--ColorThemes3);
|
||||
font-size: var(--FontSize3);
|
||||
font-weight: 400;
|
||||
font-size: var(--FontSize4);
|
||||
font-weight: 500;
|
||||
padding: 10px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.page-stand-list>details>#list>.card>.contents>.info>.button-edit {
|
||||
min-width: 35px;
|
||||
height: 35px;
|
||||
min-width: 40px;
|
||||
height: 40px;
|
||||
border-radius: calc(var(--border-radius) - 5px - 4px);
|
||||
background: var(--PrimaryColor);
|
||||
cursor: pointer;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
let map_card;
|
||||
let map_card, Territory_reconnecting;
|
||||
|
||||
const Territory_card = {
|
||||
// Глобальні змінні стану
|
||||
@@ -131,12 +131,12 @@ const Territory_card = {
|
||||
data: apt
|
||||
};
|
||||
|
||||
if (Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
Cloud.socket.send(JSON.stringify(message));
|
||||
} else {
|
||||
if (confirm("З'єднання розірвано! Перепідключитись?")) {
|
||||
Territory_card.getEntrances({ update: true });
|
||||
Cloud.start();
|
||||
Territory_card.getEntrances({ update: true });
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -178,11 +178,10 @@ const Territory_card = {
|
||||
data: apt
|
||||
};
|
||||
|
||||
if (Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
if (navigator.onLine && Cloud.socket?.readyState === WebSocket.OPEN) {
|
||||
Cloud.socket.send(JSON.stringify(message));
|
||||
} else {
|
||||
if (confirm("З'єднання розірвано! Перепідключитись?")) {
|
||||
Territory_card.getEntrances({ update: true });
|
||||
Territory_card.cloud.start();
|
||||
}
|
||||
}
|
||||
@@ -230,9 +229,9 @@ const Territory_card = {
|
||||
const details = document.createElement('details');
|
||||
if (show === "open") details.setAttribute('open', '');
|
||||
details.innerHTML = `
|
||||
<summary><p>${title}</p>${icon}</summary>
|
||||
<div id="apartments_${id}" class="apartments_list"></div>
|
||||
`;
|
||||
<summary><p>${title}</p>${icon}</summary>
|
||||
<div id="apartments_${id}" class="apartments_list"></div>
|
||||
`;
|
||||
fragment.appendChild(details);
|
||||
|
||||
this.getApartment({ id, number: entrance_number, update: false });
|
||||
@@ -487,6 +486,10 @@ const Territory_card = {
|
||||
},
|
||||
},
|
||||
|
||||
async reload(){
|
||||
Territory_card.getEntrances({ update: true });
|
||||
},
|
||||
|
||||
// Сортування
|
||||
sort(mode, load) {
|
||||
const idx = Math.max(1, Math.min(4, Number(mode) || 1));
|
||||
|
||||
@@ -54,7 +54,7 @@ const Territory_list = {
|
||||
return Territory_list.house.list;
|
||||
},
|
||||
setHTML: async function () {
|
||||
const block_house = document.getElementById('list-house');
|
||||
const block = document.getElementById('list-house');
|
||||
const territory_entrances = localStorage.getItem('territory_list_entrances') === 'true';
|
||||
const sort_mode = localStorage.getItem('territory_list_sort') ?? "1";
|
||||
const territory_list_filter = Number(localStorage.getItem("territory_list_filter") ?? 0);
|
||||
@@ -74,7 +74,7 @@ const Territory_list = {
|
||||
|
||||
list.sort(compare);
|
||||
|
||||
let html = "";
|
||||
block.innerHTML = '';
|
||||
for (const element of list) {
|
||||
const qty = element.entrance?.quantity ?? 0;
|
||||
const work = element.entrance?.working ?? 0;
|
||||
@@ -87,11 +87,33 @@ const Territory_list = {
|
||||
(territory_list_filter === 2 && element.working === false);
|
||||
|
||||
if (statusMatch) {
|
||||
html += this.renderCard({ element, territory_entrances });
|
||||
const card = document.createElement('app-territory-card');
|
||||
|
||||
if (territory_entrances) {
|
||||
const working = element.working;
|
||||
const person = working
|
||||
? `${element.history.name === 'Групова' ? 'Група ' + element.history.group_id : element.history.name}`
|
||||
: ``;
|
||||
|
||||
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;
|
||||
block.appendChild(card);
|
||||
} else {
|
||||
const qty = element.entrance.quantity;
|
||||
const work = element.entrance.working;
|
||||
|
||||
card.image = `${CONFIG.web}cards/house/T${element.id}.webp`;
|
||||
card.address = `${element.title} ${element.number}`;
|
||||
card.link = `/territory/manager/house/${element.id}`;
|
||||
card.atWork = work;
|
||||
card.quantity = qty;
|
||||
}
|
||||
|
||||
block.appendChild(card);
|
||||
}
|
||||
}
|
||||
|
||||
block_house.innerHTML = html;
|
||||
},
|
||||
compareAB: (a, b, entrances, order = 'asc') => {
|
||||
const dir = order === 'asc' ? 1 : -1;
|
||||
@@ -110,57 +132,6 @@ const Territory_list = {
|
||||
|
||||
return 0;
|
||||
},
|
||||
renderCard: ({ element, territory_entrances }) => {
|
||||
if (territory_entrances) {
|
||||
const working = element.working;
|
||||
const bg = working ? `background: ${colorGroup(element.history.group_id)} color: #fff;` : "";
|
||||
|
||||
const person = working
|
||||
? `<span>Територію опрацьовує:</span> <p>${element.history.name === 'Групова' ? 'Група ' + element.history.group_id : element.history.name}</p>`
|
||||
: `<span>Територія не опрацьовується</span>`;
|
||||
|
||||
return `
|
||||
<div class="card">
|
||||
<i style="background-image: url(${CONFIG.web}cards/house/T${element.house.id}.webp);"></i>
|
||||
<div class="contents">
|
||||
<div class="info">
|
||||
<div>
|
||||
<p>${element.house.title} ${element.house.number} (${element.title})</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sheep" style="${bg}">
|
||||
${person}
|
||||
</div>
|
||||
</div>
|
||||
<a href="/territory/manager/house/${element.house.id}" data-route></a>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
const qty = element.entrance.quantity;
|
||||
const work = element.entrance.working;
|
||||
const progress = ((work / qty) * 100).toFixed(1);
|
||||
return `
|
||||
<div class="card">
|
||||
<i style="background-image: url(${CONFIG.web}cards/house/T${element.id}.webp);"></i>
|
||||
<div class="contents">
|
||||
<div class="info">
|
||||
<div>
|
||||
<p>${element.title} ${element.number}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div>
|
||||
<div class="progress" style="width: ${progress}%"></div>
|
||||
<span>Вільні під'їзди:</span>
|
||||
<p>${qty - work} / ${qty}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/territory/manager/house/${element.id}" data-route></a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
},
|
||||
territoryType: (type) => {
|
||||
localStorage.setItem('territory_list_entrances', type);
|
||||
document.getElementById('territory_entrances_true').setAttribute('data-state', type === 'false' ? 'active' : '');
|
||||
@@ -202,8 +173,7 @@ const Territory_list = {
|
||||
|
||||
list.sort(compare);
|
||||
|
||||
let html = "";
|
||||
|
||||
block.innerHTML = '';
|
||||
for (const element of list) {
|
||||
const statusMatch =
|
||||
territory_list_filter === 0 ||
|
||||
@@ -211,35 +181,20 @@ const Territory_list = {
|
||||
(territory_list_filter === 2 && !element.working);
|
||||
|
||||
if (statusMatch) {
|
||||
html += this.renderCard(element);
|
||||
const working = element.working;
|
||||
|
||||
const person = working
|
||||
? `${element.history.name === 'Групова' ? 'Група ' + element.history.group_id : element.history.name}`
|
||||
: ``;
|
||||
|
||||
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;
|
||||
block.appendChild(card);
|
||||
}
|
||||
}
|
||||
|
||||
block.innerHTML = html;
|
||||
},
|
||||
renderCard: (element) => {
|
||||
const working = element.working;
|
||||
const bg = working ? `background: ${colorGroup(element.history.group_id)} color: #fff;` : "";
|
||||
const person = working
|
||||
? `<span>Територію опрацьовує:</span> <p>${element.history.name === 'Групова' ? 'Група ' + element.history.group_id : element.history.name}</p>`
|
||||
: `<span>Територія не опрацьовується</span>`;
|
||||
|
||||
return `
|
||||
<div class="card">
|
||||
<i style="background-image: url(${CONFIG.web}cards/homestead/H${element.id}.webp);"></i>
|
||||
<div class="contents">
|
||||
<div class="info">
|
||||
<div>
|
||||
<p>${element.title} ${element.number}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sheep" style="${bg}">
|
||||
${person}
|
||||
</div>
|
||||
</div>
|
||||
<a href="/territory/manager/homestead/${element.id}" data-route></a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
},
|
||||
filter: () => {
|
||||
@@ -252,6 +207,8 @@ const Territory_list = {
|
||||
Territory_list.homestead.setHTML();
|
||||
},
|
||||
report: async () => {
|
||||
Notifier.info('Запит на генерацію звіту опрацювання відправлено');
|
||||
|
||||
const uuid = localStorage.getItem("uuid");
|
||||
const url = `${CONFIG.api}generator/report/territories`;
|
||||
await fetch(url, {
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
|
||||
.page-territory #list-house,
|
||||
.page-territory #list-homestead {
|
||||
width: 100%;
|
||||
width: calc(100% - 20px);
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -212,157 +212,6 @@
|
||||
overflow-y: auto;
|
||||
align-items: flex-start;
|
||||
transition: .3s ease;
|
||||
}
|
||||
|
||||
.page-territory .card {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
background-color: var(--ColorThemes2);
|
||||
margin: 10px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
border-radius: calc(var(--border-radius) - 5px);
|
||||
}
|
||||
|
||||
@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-color: var(--PrimaryColor);
|
||||
border-radius: calc(var(--border-radius) - 5px);
|
||||
}
|
||||
|
||||
.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: stretch;
|
||||
justify-content: space-between;
|
||||
border-radius: calc(var(--border-radius) - 5px);
|
||||
}
|
||||
|
||||
|
||||
.page-territory .info {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.page-territory .info>div {
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
display: flex;
|
||||
background: var(--ColorThemes0);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--FontSize1);
|
||||
color: var(--ColorThemes3);
|
||||
border-radius: calc(var(--border-radius) - 5px - 4px);
|
||||
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: var(--FontSize3);
|
||||
font-weight: 300;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.page-territory .info>div>p {
|
||||
color: var(--ColorThemes3);
|
||||
font-size: var(--FontSize3);
|
||||
font-weight: 400;
|
||||
gap: 20px;
|
||||
padding: 10px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.page-territory .sheep {
|
||||
margin: 10px;
|
||||
max-height: 50px;
|
||||
border-radius: calc(var(--border-radius) - 5px - 4px);
|
||||
padding: 10px 0;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
background: var(--PrimaryColor);
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.page-territory .sheep>span {
|
||||
color: var(--PrimaryColorText);
|
||||
font-size: var(--FontSize3);
|
||||
font-weight: 400;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.page-territory .sheep>p {
|
||||
color: var(--PrimaryColorText);
|
||||
font-size: var(--FontSize4);
|
||||
font-weight: 400;
|
||||
margin-top: 5px;
|
||||
}
|
||||
@@ -279,12 +279,6 @@ const Territory_Manager = {
|
||||
};
|
||||
|
||||
L.polygon(data.points, polygonOptions).addTo(map_territory);
|
||||
|
||||
console.log(data.zoom);
|
||||
|
||||
// setTimeout(() => {
|
||||
// map_territory.setZoom(data.zoom);
|
||||
// }, 200)
|
||||
},
|
||||
mess: {
|
||||
open: ({ type, id, number, mode }) => {
|
||||
@@ -359,11 +353,14 @@ const Territory_Manager = {
|
||||
|
||||
if (!response.ok) throw new Error("Failed to assign");
|
||||
|
||||
Notifier.success('Територія призначина успішно');
|
||||
|
||||
Territory_Manager.mess.close();
|
||||
Territory_Manager.entrances.list = [];
|
||||
await Territory_Manager.entrances.setHTML(type, id);
|
||||
} catch (err) {
|
||||
console.error('❌ Error:', err);
|
||||
Notifier.error('Помилка призначення території');
|
||||
const errorText = "Помилка";
|
||||
if (newButton) newButton.innerText = errorText;
|
||||
if (groupButton) groupButton.innerText = errorText;
|
||||
@@ -391,8 +388,11 @@ const Territory_Manager = {
|
||||
|
||||
Territory_Manager.entrances.list = [];
|
||||
await Territory_Manager.entrances.setHTML(type, id);
|
||||
|
||||
Notifier.success('Територія забрана успішно');
|
||||
} catch (error) {
|
||||
console.error("❌ Помилка зняття призначення:", error);
|
||||
Notifier.error('Помилка зняття призначення');
|
||||
if (button) button.innerText = "Помилка";
|
||||
}
|
||||
},
|
||||
|
||||
@@ -123,7 +123,12 @@ const Router = {
|
||||
// Делегування кліків по посиланнях з data-route
|
||||
delegateLinks() {
|
||||
window.addEventListener('click', (e) => {
|
||||
const target = e.target.closest('[data-route]');
|
||||
// const target = e.target.closest('[data-route]');
|
||||
const pathNodes = e.composedPath();
|
||||
const target = pathNodes.find(node =>
|
||||
node.tagName === 'A' && node.hasAttribute('data-route')
|
||||
);
|
||||
|
||||
if (!target || !target.href) return;
|
||||
|
||||
const path = target.href.replace(location.origin, '');
|
||||
@@ -148,7 +153,7 @@ const Router = {
|
||||
location.hash = this.clearSlashes(path);
|
||||
}
|
||||
|
||||
if(update == true) {
|
||||
if (update == true) {
|
||||
window.scrollTo(0, 0); // Скидуємо прокрутку при переході
|
||||
this.check();
|
||||
}
|
||||
|
||||
@@ -1,71 +1,93 @@
|
||||
Router
|
||||
.add('auth', function () {
|
||||
pageActive('');
|
||||
Auth.init();;
|
||||
Auth.init();
|
||||
page = "Auth";
|
||||
})
|
||||
.add('territory/map', function () {
|
||||
pageActive();
|
||||
Territory_Map.init();;
|
||||
Territory_Map.init();
|
||||
page = "Territory_Map";
|
||||
})
|
||||
.add('territory/constructor', function () {
|
||||
pageActive();
|
||||
Territory_constructor.init();;
|
||||
Territory_constructor.init();
|
||||
page = "Territory_constructor";
|
||||
})
|
||||
.add('territory/manager/(.*)/(.*)', function (type, id) {
|
||||
pageActive();
|
||||
Territory_Manager.init(type, id);
|
||||
page = "Territory_Manager";
|
||||
})
|
||||
.add('territory/editor/(.*)/(.*)', function (type, id) {
|
||||
pageActive();
|
||||
Territory_editor.init(type, id);
|
||||
page = "Territory_editor";
|
||||
})
|
||||
.add('territory/card/(.*)/(.*)', function (type, id) {
|
||||
pageActive();
|
||||
Territory_card.init(type, id);
|
||||
page = "Territory_card";
|
||||
})
|
||||
.add('territory/history', function () {
|
||||
pageActive();
|
||||
Territory_History.init();
|
||||
page = "Territory_History";
|
||||
})
|
||||
.add('territory', function () {
|
||||
pageActive('territory');
|
||||
Territory_list.init();
|
||||
page = "Territory_list";
|
||||
})
|
||||
.add('sheeps/(.*)', function (name) {
|
||||
pageActive('sheeps');
|
||||
Sheeps.init(name);;
|
||||
Sheeps.init(name);
|
||||
page = "Sheeps";
|
||||
})
|
||||
.add('sheeps', function () {
|
||||
pageActive('sheeps');
|
||||
Sheeps.init();;
|
||||
Sheeps.init();
|
||||
page = "Sheeps";
|
||||
})
|
||||
.add('home', function () {
|
||||
pageActive('home');
|
||||
Home.init();
|
||||
page = "Home";
|
||||
})
|
||||
.add('schedule/constructor', function () {
|
||||
pageActive();
|
||||
Schedule_constructor.init();
|
||||
page = "Schedule_constructor";
|
||||
})
|
||||
.add('schedule', function () {
|
||||
pageActive('schedule');
|
||||
Schedule_list.init();;
|
||||
Schedule_list.init();
|
||||
page = "Schedule_list";
|
||||
})
|
||||
.add('stand/constructor', function () {
|
||||
pageActive();
|
||||
Stand_constructor.init();;
|
||||
Stand_constructor.init();
|
||||
page = "Stand_constructor";
|
||||
})
|
||||
.add('stand/editor/(.*)', function (id) {
|
||||
pageActive();
|
||||
Stand_editor.init(id);;
|
||||
Stand_editor.init(id);
|
||||
page = "Stand_editor";
|
||||
})
|
||||
.add('stand/card/(.*)', function (id) {
|
||||
pageActive();
|
||||
Stand_card.init(id);;
|
||||
Stand_card.init(id);
|
||||
page = "Stand_card";
|
||||
})
|
||||
.add('stand', function () {
|
||||
pageActive('stand');
|
||||
Stand_list.init();;
|
||||
Stand_list.init();
|
||||
page = "Stand_list";
|
||||
})
|
||||
.add('options', function () {
|
||||
pageActive('options');
|
||||
Options.init();;
|
||||
Options.init();
|
||||
page = "Options";
|
||||
})
|
||||
.add(function () {
|
||||
page_404();;
|
||||
|
||||
22
web/sw.js
22
web/sw.js
@@ -1,4 +1,4 @@
|
||||
const STATIC_CACHE_NAME = 'v2.0.40';
|
||||
const STATIC_CACHE_NAME = 'v2.0.103';
|
||||
|
||||
const FILES_TO_CACHE = [
|
||||
'/',
|
||||
@@ -7,6 +7,8 @@ const FILES_TO_CACHE = [
|
||||
"/lib/router/router.js",
|
||||
"/lib/router/routes.js",
|
||||
|
||||
"/lib/customElements/notification.js",
|
||||
|
||||
"/lib/components/leaflet/leaflet.css",
|
||||
"/lib/components/leaflet/leaflet.js",
|
||||
|
||||
@@ -17,6 +19,8 @@ const FILES_TO_CACHE = [
|
||||
|
||||
"/lib/components/cloud.js",
|
||||
|
||||
"/lib/components/metrics.js",
|
||||
|
||||
"/lib/components/clipboard.js",
|
||||
"/lib/components/colorGroup.js",
|
||||
"/lib/components/makeid.js",
|
||||
@@ -91,6 +95,10 @@ const FILES_TO_CACHE = [
|
||||
"/lib/pages/schedule/list/style.css",
|
||||
"/lib/pages/schedule/list/index.html",
|
||||
|
||||
"/lib/pages/schedule/constructor/script.js",
|
||||
"/lib/pages/schedule/constructor/style.css",
|
||||
"/lib/pages/schedule/constructor/index.html",
|
||||
|
||||
"/lib/app.js"
|
||||
];
|
||||
|
||||
@@ -102,7 +110,7 @@ self.addEventListener('install', event => {
|
||||
fetch(url).then(res => {
|
||||
if (!res.ok) throw new Error(`${url} not found`);
|
||||
return cache.put(url, res);
|
||||
}).catch(err => console.warn(err))
|
||||
}).catch(err => console.warn('[ServiceWorker] ', err))
|
||||
));
|
||||
})
|
||||
);
|
||||
@@ -130,12 +138,12 @@ self.addEventListener('fetch', event => {
|
||||
// ----------------------- PUSH -----------------------
|
||||
self.addEventListener("push", event => {
|
||||
let data = {};
|
||||
try { data = event.data.json(); } catch { data = { title: "Уведомлення", body: event.data?.text() }; }
|
||||
try { data = event.data.json(); } catch { data = { title: "Повідомлення", body: event.data?.text() }; }
|
||||
|
||||
console.log(data);
|
||||
console.log('[ServiceWorker] ', data);
|
||||
|
||||
|
||||
const title = data.title || "Уведомлення";
|
||||
const title = data.title || "Повідомлення";
|
||||
const options = {
|
||||
body: data.body || "",
|
||||
icon: "/img/icon.png",
|
||||
@@ -177,7 +185,7 @@ async function updateALL() {
|
||||
fetch(url).then(res => {
|
||||
if (!res.ok) throw new Error(`${url} not found`);
|
||||
return cache.put(url, res);
|
||||
}).catch(err => console.warn(err))
|
||||
}).catch(err => console.warn('[ServiceWorker] ', err))
|
||||
));
|
||||
console.log('All caches updated');
|
||||
console.log('[ServiceWorker] All caches updated');
|
||||
}
|
||||
Reference in New Issue
Block a user