241 lines
9.4 KiB
JavaScript
241 lines
9.4 KiB
JavaScript
// Элементы DOM
|
|||
|
|
const statusIndicator = document.getElementById('statusIndicator');
|
||
|
|
const statusInfo = document.getElementById('statusInfo');
|
||
|
|
const btnOn = document.getElementById('btnOn');
|
||
|
|
const btnOff = document.getElementById('btnOff');
|
||
|
|
const notification = document.getElementById('notification');
|
||
|
|
const notificationMessage = document.getElementById('notificationMessage');
|
||
|
|
|
||
|
|
// Конфигурация API
|
||
|
|
const API_BASE_URL = window.location.origin;
|
||
|
|
const API_STATUS_ENDPOINT = `${API_BASE_URL}/api/v1/client/status`;
|
||
|
|
const API_TOGGLE_ENDPOINT = `${API_BASE_URL}/api/v1/client/toggle`;
|
||
|
|
|
||
|
|
// Переменные состояния
|
||
|
|
let currentStatus = null;
|
||
|
|
let isRequestInProgress = false;
|
||
|
|
let hasIntegration = false;
|
||
|
|
|
||
|
|
// Инициализация при загрузке страницы
|
||
|
|
document.addEventListener('DOMContentLoaded', function () {
|
||
|
|
loadInitialStatus();
|
||
|
|
|
||
|
|
btnOn.addEventListener('click', () => sendCommand(true));
|
||
|
|
btnOff.addEventListener('click', () => sendCommand(false));
|
||
|
|
});
|
||
|
|
|
||
|
|
// Функция загрузки начального статуса
|
||
|
|
async function loadInitialStatus() {
|
||
|
|
try {
|
||
|
|
updateUIForLoading();
|
||
|
|
|
||
|
|
const response = await fetch(API_STATUS_ENDPOINT);
|
||
|
|
const data = await response.json();
|
||
|
|
|
||
|
|
if (data.success) {
|
||
|
|
hasIntegration = true;
|
||
|
|
currentStatus = data.data.enabled;
|
||
|
|
updateUIForStatus(currentStatus);
|
||
|
|
showNotification(`Доступ разрешен. Ваш IP: ${data.data.ip}.`, 'success', true);
|
||
|
|
} else {
|
||
|
|
hasIntegration = false;
|
||
|
|
updateUIForNoIntegration(data.error || 'Нет интеграции с сервисом');
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Ошибка при загрузке статуса:', error);
|
||
|
|
updateUIForError('Ошибка соединения с сервером API');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Функция отправки команды на сервер
|
||
|
|
async function sendCommand(enabled) {
|
||
|
|
if (!hasIntegration || isRequestInProgress) return;
|
||
|
|
|
||
|
|
// Прерываем, если запрашиваемое состояние уже активно
|
||
|
|
if (enabled === currentStatus) return;
|
||
|
|
|
||
|
|
isRequestInProgress = true;
|
||
|
|
const action = enabled ? 'включения' : 'выключения';
|
||
|
|
|
||
|
|
updateUIForSendingCommand(enabled);
|
||
|
|
|
||
|
|
try {
|
||
|
|
const response = await fetch(API_TOGGLE_ENDPOINT, {
|
||
|
|
method: 'PUT',
|
||
|
|
headers: { 'Content-Type': 'application/json' }
|
||
|
|
});
|
||
|
|
const data = await response.json();
|
||
|
|
|
||
|
|
if (data.success) {
|
||
|
|
currentStatus = data.data.enabled;
|
||
|
|
updateUIForStatus(currentStatus);
|
||
|
|
showNotification(`Команда ${action} успешно выполнена.`, 'success');
|
||
|
|
provideVisualFeedback(currentStatus);
|
||
|
|
} else {
|
||
|
|
showNotification(`Ошибка: ${data.error}`, 'error');
|
||
|
|
updateUIForStatus(currentStatus);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Ошибка при отправке команды:', error);
|
||
|
|
showNotification('Ошибка сети при отправке команды', 'error');
|
||
|
|
updateUIForStatus(currentStatus);
|
||
|
|
} finally {
|
||
|
|
isRequestInProgress = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Обновление UI при загрузке
|
||
|
|
function updateUIForLoading() {
|
||
|
|
statusIndicator.className = 'status-indicator status-loading';
|
||
|
|
statusIndicator.innerHTML = '<i class="fas fa-sync-alt fa-spin"></i> Загрузка...';
|
||
|
|
statusInfo.textContent = 'Получение текущего состояния с сервера...';
|
||
|
|
|
||
|
|
btnOn.classList.add('btn-disabled');
|
||
|
|
btnOff.classList.add('btn-disabled');
|
||
|
|
btnOn.disabled = true;
|
||
|
|
btnOff.disabled = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Обновление UI на основе текущего статуса
|
||
|
|
function updateUIForStatus(enabled) {
|
||
|
|
if (enabled) {
|
||
|
|
statusIndicator.className = 'status-indicator status-on';
|
||
|
|
statusIndicator.innerHTML = '<i class="fas fa-check-circle"></i> Включено';
|
||
|
|
statusInfo.textContent = 'Устройство активно и работает в нормальном режиме';
|
||
|
|
|
||
|
|
btnOn.classList.add('btn-disabled');
|
||
|
|
btnOn.disabled = true;
|
||
|
|
|
||
|
|
btnOff.classList.remove('btn-disabled');
|
||
|
|
btnOff.disabled = false;
|
||
|
|
} else {
|
||
|
|
statusIndicator.className = 'status-indicator status-off';
|
||
|
|
statusIndicator.innerHTML = '<i class="fas fa-power-off"></i> Выключено';
|
||
|
|
statusInfo.textContent = 'Устройство отключено и не выполняет свои функции';
|
||
|
|
|
||
|
|
btnOff.classList.add('btn-disabled');
|
||
|
|
btnOff.disabled = true;
|
||
|
|
|
||
|
|
btnOn.classList.remove('btn-disabled');
|
||
|
|
btnOn.disabled = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Обновление UI при отсутствии интеграции
|
||
|
|
function updateUIForNoIntegration(message) {
|
||
|
|
statusIndicator.className = 'status-indicator status-disabled';
|
||
|
|
statusIndicator.innerHTML = '<i class="fas fa-ban"></i> Нет доступа';
|
||
|
|
statusInfo.textContent = 'Ваш IP-адрес не найден в списке доверенных адресов';
|
||
|
|
btnOn.classList.add('btn-disabled');
|
||
|
|
btnOff.classList.add('btn-disabled');
|
||
|
|
btnOn.disabled = true;
|
||
|
|
btnOff.disabled = true;
|
||
|
|
showNotification(message, 'error', true);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Обновление UI при ошибке
|
||
|
|
function updateUIForError(errorMessage) {
|
||
|
|
statusIndicator.className = 'status-indicator status-disabled';
|
||
|
|
statusIndicator.innerHTML = '<i class="fas fa-exclamation-triangle"></i> Ошибка';
|
||
|
|
statusInfo.textContent = 'Не удалось получить информацию о статусе устройства';
|
||
|
|
btnOn.classList.add('btn-disabled');
|
||
|
|
btnOff.classList.add('btn-disabled');
|
||
|
|
btnOn.disabled = true;
|
||
|
|
btnOff.disabled = true;
|
||
|
|
showNotification(errorMessage, 'error', true);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Обновление UI при отправке команды
|
||
|
|
function updateUIForSendingCommand(enabled) {
|
||
|
|
const action = enabled ? 'включения' : 'выключения';
|
||
|
|
statusIndicator.className = 'status-indicator status-loading';
|
||
|
|
statusIndicator.innerHTML = '<i class="fas fa-sync-alt fa-spin"></i> Отправка...';
|
||
|
|
statusInfo.textContent = `Отправка команды ${action} на сервер...`;
|
||
|
|
btnOn.classList.add('btn-disabled');
|
||
|
|
btnOff.classList.add('btn-disabled');
|
||
|
|
btnOn.disabled = true;
|
||
|
|
btnOff.disabled = true;
|
||
|
|
showNotification(`Отправка команды ${action}...`, 'warning');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Переменные для управления уведомлениями
|
||
|
|
let baseNotificationContent = null;
|
||
|
|
let notificationTimeout = null;
|
||
|
|
|
||
|
|
// Показать уведомление
|
||
|
|
function showNotification(message, type = 'info', isPersistent = false) {
|
||
|
|
if (notificationTimeout) {
|
||
|
|
clearTimeout(notificationTimeout);
|
||
|
|
notificationTimeout = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
notification.className = 'notification';
|
||
|
|
let icon = 'fa-info-circle';
|
||
|
|
let title = 'Информация:';
|
||
|
|
|
||
|
|
if (type === 'error') {
|
||
|
|
notification.classList.add('notification-error');
|
||
|
|
icon = 'fa-exclamation-circle';
|
||
|
|
title = 'Ошибка:';
|
||
|
|
} else if (type === 'success') {
|
||
|
|
notification.classList.add('notification-success');
|
||
|
|
icon = 'fa-check-circle';
|
||
|
|
title = 'Успешно:';
|
||
|
|
} else if (type === 'warning') {
|
||
|
|
notification.classList.add('notification-warning');
|
||
|
|
icon = 'fa-sync-alt fa-spin';
|
||
|
|
title = 'Обработка:';
|
||
|
|
}
|
||
|
|
|
||
|
|
const content = `<h3><i class="fas ${icon}"></i> ${title}</h3><div class="notification-message">${message}</div>`;
|
||
|
|
notification.innerHTML = content;
|
||
|
|
|
||
|
|
if (isPersistent) {
|
||
|
|
baseNotificationContent = content;
|
||
|
|
}
|
||
|
|
|
||
|
|
notification.style.opacity = '0';
|
||
|
|
notification.style.display = 'block';
|
||
|
|
|
||
|
|
// Плавное появление
|
||
|
|
setTimeout(() => {
|
||
|
|
notification.style.transition = 'opacity 0.3s ease';
|
||
|
|
notification.style.opacity = '1';
|
||
|
|
}, 10);
|
||
|
|
|
||
|
|
// Авто-возврат к базовому уведомлению через 5 секунд (если это не само базовое и не "в обработке")
|
||
|
|
if (!isPersistent && baseNotificationContent && type !== 'warning') {
|
||
|
|
notificationTimeout = setTimeout(() => {
|
||
|
|
notification.style.opacity = '0';
|
||
|
|
setTimeout(() => {
|
||
|
|
notification.className = 'notification';
|
||
|
|
notification.innerHTML = baseNotificationContent;
|
||
|
|
notification.style.opacity = '1';
|
||
|
|
}, 300);
|
||
|
|
}, 5000);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Визуальная обратная связь
|
||
|
|
function provideVisualFeedback(enabled) {
|
||
|
|
if (enabled) {
|
||
|
|
document.body.style.background = 'linear-gradient(135deg, #2ecc71 0%, #27ae60 100%)';
|
||
|
|
setTimeout(() => {
|
||
|
|
document.body.style.background = 'linear-gradient(135deg, #1a2980 0%, #26d0ce 100%)';
|
||
|
|
}, 800);
|
||
|
|
} else {
|
||
|
|
document.body.style.background = 'linear-gradient(135deg, #e74c3c 0%, #c0392b 100%)';
|
||
|
|
setTimeout(() => {
|
||
|
|
document.body.style.background = 'linear-gradient(135deg, #1a2980 0%, #26d0ce 100%)';
|
||
|
|
}, 800);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Дополнительная функция для ручного обновления статуса
|
||
|
|
function refreshStatus() {
|
||
|
|
if (!isRequestInProgress) {
|
||
|
|
loadInitialStatus();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
statusIndicator.addEventListener('dblclick', refreshStatus);
|