tehnicheskoe-zadanie-informa/index.html

216 lines
9.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Контроль исполнения поручений СД</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="header">
<div class="header-left">
<h1>Контроль исполнения поручений Совета директоров</h1>
</div>
<nav class="nav" id="mainNav">
<a href="index.html" class="active">Дашборд</a>
<a href="registry.html">Реестр</a>
<a href="card.html?id=new" id="newBtn" style="display:none">Новое поручение</a>
</nav>
</header>
<main class="dashboard">
<section class="metrics">
<div class="metric total">
<div class="metric-value" id="total">0</div>
<div class="metric-label">Всего поручений</div>
</div>
<div class="metric done">
<div class="metric-value" id="done">0</div>
<div class="metric-label">Выполнено</div>
</div>
<div class="metric progress">
<div class="metric-value" id="progress">0</div>
<div class="metric-label">В работе</div>
</div>
<div class="metric control">
<div class="metric-value" id="control">0</div>
<div class="metric-label">На контроле</div>
</div>
<div class="metric overdue">
<div class="metric-value" id="overdue">0</div>
<div class="metric-label">Просрочено</div>
</div>
</section>
<section class="filters">
<select id="filterStatus">
<option value="">Все статусы</option>
<option value="В работе">В работе</option>
<option value="На контроле">На контроле</option>
<option value="Выполнено">Выполнено</option>
<option value="Просрочено">Просрочено</option>
</select>
<select id="filterRm">
<option value="">Все РМ</option>
</select>
<input type="date" id="filterFrom" placeholder="С даты">
<input type="date" id="filterTo" placeholder="По дату">
<button onclick="applyFilters()">Применить</button>
<button onclick="clearFilters()">Сбросить</button>
<button onclick="exportCsv()" class="export-btn">Выгрузить в Excel</button>
</section>
<section class="alerts">
<h2>⚠️ Просроченные поручения</h2>
<div id="overdueList" class="list"></div>
</section>
<section class="coming-soon">
<h2>📅 Ближайшие сроки исполнения</h2>
<div id="comingList" class="list"></div>
</section>
<section class="critical">
<h2>🔴 Критические поручения (просрочено > 3 дней)</h2>
<div id="criticalList" class="list"></div>
</section>
</main>
<script src="auth.js"></script>
<script>
// Проверка авторизации
const user = requireAuth();
renderUserMenu();
// Показываем кнопку "Новое поручение" только для admin/pmo
if (canEdit()) {
document.getElementById("newBtn").style.display = "inline-block";
}
let allInstructions = [];
async function load() {
const res = await fetch("/api/instructions");
allInstructions = await res.json();
renderMetrics();
renderFilters();
renderLists();
}
function renderMetrics() {
const total = allInstructions.length;
const done = allInstructions.filter(i => i.status === "Выполнено").length;
const progress = allInstructions.filter(i => i.status === "В работе").length;
const control = allInstructions.filter(i => i.status === "На контроле").length;
const overdue = allInstructions.filter(i => i.status === "Просрочено").length;
document.getElementById("total").textContent = total;
document.getElementById("done").textContent = done;
document.getElementById("progress").textContent = progress;
document.getElementById("control").textContent = control;
document.getElementById("overdue").textContent = overdue;
}
function renderFilters() {
const rms = [...new Set(allInstructions.map(i => i.rm).filter(Boolean))];
const select = document.getElementById("filterRm");
rms.forEach(rm => {
const opt = document.createElement("option");
opt.value = rm;
opt.textContent = rm;
select.appendChild(opt);
});
}
function renderLists() {
const today = new Date();
const overdue = allInstructions.filter(i => i.status === "Просрочено");
const critical = overdue.filter(i => {
const plan = new Date(i.planDate);
const diff = Math.floor((today - plan) / (1000 * 60 * 60 * 24));
return diff > 3;
});
const coming = allInstructions
.filter(i => i.status !== "Выполнено" && i.status !== "Просрочено")
.sort((a, b) => new Date(a.planDate) - new Date(b.planDate))
.slice(0, 5);
document.getElementById("overdueList").innerHTML = overdue.length
? overdue.map(i => `<div class="list-item overdue"><a href="card.html?id=${i.id}">${i.title}</a><span>РМ: ${i.rm || "—"}</span><span>Срок: ${i.planDate}</span></div>`).join("")
: "<p>Нет просроченных поручений</p>";
document.getElementById("criticalList").innerHTML = critical.length
? critical.map(i => `<div class="list-item critical"><a href="card.html?id=${i.id}">${i.title}</a><span>Просрочено на ${Math.floor((today - new Date(i.planDate)) / (1000 * 60 * 60 * 24))} дн.</span></div>`).join("")
: "<p>Нет критических поручений</p>";
document.getElementById("comingList").innerHTML = coming.length
? coming.map(i => `<div class="list-item"><a href="card.html?id=${i.id}">${i.title}</a><span>РМ: ${i.rm || "—"}</span><span>Срок: ${i.planDate}</span></div>`).join("")
: "<p>Нет поручений с ближайшими сроками</p>";
}
function applyFilters() {
const status = document.getElementById("filterStatus").value;
const rm = document.getElementById("filterRm").value;
const from = document.getElementById("filterFrom").value;
const to = document.getElementById("filterTo").value;
let filtered = allInstructions;
if (status) filtered = filtered.filter(i => i.status === status);
if (rm) filtered = filtered.filter(i => i.rm === rm);
if (from) filtered = filtered.filter(i => i.issueDate >= from);
if (to) filtered = filtered.filter(i => i.issueDate <= to);
const total = filtered.length;
const done = filtered.filter(i => i.status === "Выполнено").length;
const progress = filtered.filter(i => i.status === "В работе").length;
const control = filtered.filter(i => i.status === "На контроле").length;
const overdue = filtered.filter(i => i.status === "Просрочено").length;
document.getElementById("total").textContent = total;
document.getElementById("done").textContent = done;
document.getElementById("progress").textContent = progress;
document.getElementById("control").textContent = control;
document.getElementById("overdue").textContent = overdue;
const today = new Date();
const overdueList = filtered.filter(i => i.status === "Просрочено");
const critical = overdueList.filter(i => {
const plan = new Date(i.planDate);
const diff = Math.floor((today - plan) / (1000 * 60 * 60 * 24));
return diff > 3;
});
const coming = filtered
.filter(i => i.status !== "Выполнено" && i.status !== "Просрочено")
.sort((a, b) => new Date(a.planDate) - new Date(b.planDate))
.slice(0, 5);
document.getElementById("overdueList").innerHTML = overdueList.length
? overdueList.map(i => `<div class="list-item overdue"><a href="card.html?id=${i.id}">${i.title}</a><span>РМ: ${i.rm || "—"}</span><span>Срок: ${i.planDate}</span></div>`).join("")
: "<p>Нет просроченных поручений</p>";
document.getElementById("criticalList").innerHTML = critical.length
? critical.map(i => `<div class="list-item critical"><a href="card.html?id=${i.id}">${i.title}</a><span>Просрочено на ${Math.floor((today - new Date(i.planDate)) / (1000 * 60 * 60 * 24))} дн.</span></div>`).join("")
: "<p>Нет критических поручений</p>";
document.getElementById("comingList").innerHTML = coming.length
? coming.map(i => `<div class="list-item"><a href="card.html?id=${i.id}">${i.title}</a><span>РМ: ${i.rm || "—"}</span><span>Срок: ${i.planDate}</span></div>`).join("")
: "<p>Нет поручений с ближайшими сроками</p>";
}
function clearFilters() {
document.getElementById("filterStatus").value = "";
document.getElementById("filterRm").value = "";
document.getElementById("filterFrom").value = "";
document.getElementById("filterTo").value = "";
load();
}
function exportCsv() {
window.open("/api/export", "_blank");
}
load();
</script>
</body>
</html>