release
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
// Глобальный объект для состояния системы
|
||||
let systemStatus = {
|
||||
medods: { configured: false, details: {} },
|
||||
n3health: { configured: false, details: {} },
|
||||
password: { configured: true }
|
||||
};
|
||||
|
||||
// Глобальные переменные для статистики сотрудников
|
||||
let expirationAlertDays = 0;
|
||||
let allPractitioners = [];
|
||||
|
||||
// Загрузка состояния системы
|
||||
async function loadSystemStatus() {
|
||||
try {
|
||||
const response = await fetch('/settings/get', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
|
||||
// Сохраняем порог предупреждения
|
||||
expirationAlertDays = data.expirationAlert || 0;
|
||||
|
||||
// Обновляем статус Медодс
|
||||
const medodsConfigured = data.medodsApiHost && data.medodsApiPort &&
|
||||
data.medodsApiIdentity && data.medodsApiSecretKey;
|
||||
systemStatus.medods.configured = medodsConfigured;
|
||||
systemStatus.medods.details = {
|
||||
host: data.medodsApiHost || 'Не задан',
|
||||
port: data.medodsApiPort || 'Не задан',
|
||||
hasCredentials: !!(data.medodsApiIdentity && data.medodsApiSecretKey)
|
||||
};
|
||||
|
||||
// Обновляем статус N3Health
|
||||
const n3healthConfigured = data.n3healthHost && data.n3healthAutorization && data.n3healthXIdLpu;
|
||||
systemStatus.n3health.configured = n3healthConfigured;
|
||||
systemStatus.n3health.details = {
|
||||
host: data.n3healthHost || 'Не задан',
|
||||
hasToken: !!data.n3healthAutorization,
|
||||
hasLpuId: !!data.n3healthXIdLpu
|
||||
};
|
||||
|
||||
// Обновляем UI
|
||||
updateStatusUI();
|
||||
updateReadinessIndicator();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при загрузке статуса:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Загрузка данных сотрудников
|
||||
async function loadStaffData() {
|
||||
try {
|
||||
const response = await fetch('/staff/getAll', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
const respJson = await response.json();
|
||||
if (response.ok && respJson.status === "ok") {
|
||||
allPractitioners = respJson.data || [];
|
||||
updateStaffStatistics();
|
||||
} else {
|
||||
allPractitioners = [];
|
||||
console.error('Ошибка при загрузке сотрудников:', respJson.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при загрузке сотрудников:', error);
|
||||
allPractitioners = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Расчет дней до истечения
|
||||
function getDaysUntilExpiry(expiryDate) {
|
||||
const now = new Date();
|
||||
const expiry = new Date(expiryDate);
|
||||
const diffTime = expiry - now;
|
||||
return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||
}
|
||||
|
||||
// Форматирование даты
|
||||
function formatDate(dateString) {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('ru-RU');
|
||||
}
|
||||
|
||||
|
||||
// Обновление статистики сотрудников
|
||||
function updateStaffStatistics() {
|
||||
// Общая статистика
|
||||
const totalCount = allPractitioners.length;
|
||||
const ukepCount = allPractitioners.filter(p => p.esiaAuth).length;
|
||||
const mchdCount = allPractitioners.filter(p => !p.esiaAuth).length;
|
||||
|
||||
// Сотрудники с истекающими подписями
|
||||
const expiringEmployees = allPractitioners.filter(p => {
|
||||
if (!p.expired_at) return false;
|
||||
const daysLeft = getDaysUntilExpiry(p.expired_at);
|
||||
return daysLeft <= expirationAlertDays && daysLeft >= 0;
|
||||
}).sort((a, b) => {
|
||||
const daysA = getDaysUntilExpiry(a.expired_at);
|
||||
const daysB = getDaysUntilExpiry(b.expired_at);
|
||||
return daysA - daysB;
|
||||
});
|
||||
|
||||
const expiringCount = expiringEmployees.length;
|
||||
|
||||
// Обновляем счетчики
|
||||
document.getElementById('totalEmployeesCount').textContent = totalCount;
|
||||
document.getElementById('ukepEmployeesCount').textContent = ukepCount;
|
||||
document.getElementById('mchdEmployeesCount').textContent = mchdCount;
|
||||
document.getElementById('expiringEmployeesCount').textContent = expiringCount;
|
||||
|
||||
// Обновляем контейнер с истекающими сотрудниками
|
||||
const container = document.getElementById('expiringEmployeesContainer');
|
||||
const noExpiringMessage = document.getElementById('noExpiringEmployeesMessage');
|
||||
const thresholdDaysSpan = document.getElementById('expiringThresholdDays');
|
||||
const noExpiringThresholdSpan = document.getElementById('noExpiringThresholdDays');
|
||||
|
||||
// Обновляем отображение порога предупреждения
|
||||
const daysText = expirationAlertDays % 10 === 1 && expirationAlertDays % 100 !== 11 ? 'день' :
|
||||
expirationAlertDays % 10 >= 2 && expirationAlertDays % 10 <= 4 &&
|
||||
(expirationAlertDays % 100 < 10 || expirationAlertDays % 100 >= 20) ? 'дня' : 'дней';
|
||||
|
||||
thresholdDaysSpan.textContent = `(менее ${expirationAlertDays} ${daysText})`;
|
||||
noExpiringThresholdSpan.textContent = expirationAlertDays;
|
||||
|
||||
if (expiringCount > 0) {
|
||||
// Показываем таблицу
|
||||
container.style.display = 'block';
|
||||
noExpiringMessage.style.display = 'none';
|
||||
|
||||
// Заполняем таблицу
|
||||
const tbody = document.getElementById('expiringEmployeesTableBody');
|
||||
let rows = '';
|
||||
|
||||
expiringEmployees.forEach(p => {
|
||||
const daysLeft = getDaysUntilExpiry(p.expired_at);
|
||||
const signatureType = p.esiaAuth ?
|
||||
'<span class="badge bg-success">УКЭП</span>' :
|
||||
'<span class="badge bg-warning text-dark">МЧД</span>';
|
||||
|
||||
let badgeClass = 'bg-warning';
|
||||
if (daysLeft < 0) {
|
||||
badgeClass = 'bg-danger';
|
||||
} else if (daysLeft <= 3) {
|
||||
badgeClass = 'bg-danger';
|
||||
} else if (daysLeft <= 7) {
|
||||
badgeClass = 'bg-warning';
|
||||
} else {
|
||||
badgeClass = 'bg-info';
|
||||
}
|
||||
|
||||
rows += `
|
||||
<tr>
|
||||
<td>${p.name}</td>
|
||||
<td>${signatureType}</td>
|
||||
<td>${formatDate(p.expired_at)}</td>
|
||||
<td>
|
||||
<span class="badge ${badgeClass}">${daysLeft} дн.</span>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
tbody.innerHTML = rows;
|
||||
} else {
|
||||
// Показываем сообщение об отсутствии
|
||||
container.style.display = 'none';
|
||||
noExpiringMessage.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление UI на основе статуса
|
||||
function updateStatusUI() {
|
||||
// Медодс
|
||||
const medodsBadge = document.getElementById('medodsStatusBadge');
|
||||
const medodsDetails = document.getElementById('medodsDetails');
|
||||
|
||||
if (systemStatus.medods.configured) {
|
||||
medodsBadge.className = 'badge bg-success';
|
||||
medodsBadge.textContent = 'Настроено';
|
||||
|
||||
medodsDetails.innerHTML = `
|
||||
<div class="text-success mb-1"><i class="bi bi-check-circle"></i> Хост: ${systemStatus.medods.details.host}</div>
|
||||
<div class="text-success mb-1"><i class="bi bi-check-circle"></i> Порт: ${systemStatus.medods.details.port}</div>
|
||||
<div class="text-success"><i class="bi bi-check-circle"></i> Учетные данные: есть</div>
|
||||
`;
|
||||
} else {
|
||||
medodsBadge.className = 'badge bg-danger';
|
||||
medodsBadge.textContent = 'Требуется настройка';
|
||||
|
||||
medodsDetails.innerHTML = `
|
||||
<div class="text-danger mb-1"><i class="bi bi-exclamation-circle"></i> Хост: ${systemStatus.medods.details.host}</div>
|
||||
<div class="text-danger mb-1"><i class="bi bi-exclamation-circle"></i> Порт: ${systemStatus.medods.details.port}</div>
|
||||
<div class="text-danger"><i class="bi bi-exclamation-circle"></i> Учетные данные: нет</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// N3Health
|
||||
const n3healthBadge = document.getElementById('n3healthStatusBadge');
|
||||
const n3healthDetails = document.getElementById('n3healthDetails');
|
||||
|
||||
if (systemStatus.n3health.configured) {
|
||||
n3healthBadge.className = 'badge bg-success';
|
||||
n3healthBadge.textContent = 'Настроено';
|
||||
|
||||
n3healthDetails.innerHTML = `
|
||||
<div class="text-success mb-1"><i class="bi bi-check-circle"></i> Хост: ${systemStatus.n3health.details.host}</div>
|
||||
<div class="text-success mb-1"><i class="bi bi-check-circle"></i> Токен доступа: есть</div>
|
||||
<div class="text-success"><i class="bi bi-check-circle"></i> Идентификатор МО: есть</div>
|
||||
`;
|
||||
} else {
|
||||
n3healthBadge.className = 'badge bg-danger';
|
||||
n3healthBadge.textContent = 'Требуется настройка';
|
||||
|
||||
n3healthDetails.innerHTML = `
|
||||
<div class="text-danger mb-1"><i class="bi bi-exclamation-circle"></i> Хост: ${systemStatus.n3health.details.host}</div>
|
||||
<div class="text-danger mb-1"><i class="bi bi-exclamation-circle"></i> Токен доступа: нет</div>
|
||||
<div class="text-danger"><i class="bi bi-exclamation-circle"></i> Идентификатор МО: нет</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление индикатора готовности и управление кнопкой
|
||||
function updateReadinessIndicator() {
|
||||
const progressBar = document.querySelector('#systemReadiness .progress-bar');
|
||||
const readinessDesc = document.getElementById('readinessDescription');
|
||||
const settingsButtonContainer = document.getElementById('settingsButtonContainer');
|
||||
|
||||
let configuredCount = 0;
|
||||
if (systemStatus.password.configured) configuredCount++;
|
||||
if (systemStatus.medods.configured) configuredCount++;
|
||||
if (systemStatus.n3health.configured) configuredCount++;
|
||||
|
||||
const readinessPercentage = Math.round((configuredCount / 3) * 100);
|
||||
|
||||
progressBar.style.width = `${readinessPercentage}%`;
|
||||
progressBar.setAttribute('aria-valuenow', readinessPercentage);
|
||||
progressBar.textContent = `${readinessPercentage}%`;
|
||||
|
||||
// Обновляем цвет прогресс-бара
|
||||
if (readinessPercentage === 100) {
|
||||
progressBar.className = 'progress-bar progress-bar-striped progress-bar-animated bg-success';
|
||||
readinessDesc.textContent = 'Все системы настроены и готовы к работе!';
|
||||
// Скрываем кнопку настроек при 100% готовности
|
||||
settingsButtonContainer.style.display = 'none';
|
||||
} else if (readinessPercentage >= 66) {
|
||||
progressBar.className = 'progress-bar progress-bar-striped progress-bar-animated bg-info';
|
||||
readinessDesc.textContent = 'Большинство систем настроены';
|
||||
settingsButtonContainer.style.display = 'block';
|
||||
} else if (readinessPercentage >= 33) {
|
||||
progressBar.className = 'progress-bar progress-bar-striped progress-bar-animated bg-warning';
|
||||
readinessDesc.textContent = 'Требуется дополнительная настройка';
|
||||
settingsButtonContainer.style.display = 'block';
|
||||
} else {
|
||||
progressBar.className = 'progress-bar progress-bar-striped progress-bar-animated bg-danger';
|
||||
readinessDesc.textContent = 'Все интеграции требуют настройки';
|
||||
settingsButtonContainer.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация при загрузке страницы
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Загружаем статус системы и данные сотрудников
|
||||
Promise.all([
|
||||
loadSystemStatus(),
|
||||
loadStaffData()
|
||||
]).then(() => {
|
||||
// Обновляем статистику после загрузки всех данных
|
||||
updateStaffStatistics();
|
||||
});
|
||||
|
||||
// Обновляем индикатор готовности и управление кнопкой
|
||||
updateReadinessIndicator();
|
||||
});
|
||||
Reference in New Issue
Block a user