283 lines
12 KiB
JavaScript
283 lines
12 KiB
JavaScript
// Глобальный объект для состояния системы
|
|
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();
|
|
}); |