tehnicheskoe-zadanie-informa/card.html

287 lines
12 KiB
HTML
Raw Permalink 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 id="pageTitle">Карточка поручения</h1>
</div>
<nav class="nav" id="mainNav">
<a href="index.html">Дашборд</a>
<a href="registry.html">Реестр</a>
<a href="card.html?id=new" id="newBtn" style="display:none">Новое поручение</a>
</nav>
</header>
<main class="card-page">
<div class="card-form">
<h2 id="formTitle">Новое поручение</h2>
<form id="instructionForm">
<input type="hidden" id="instructionId">
<div class="form-group">
<label for="title">Поручение *</label>
<textarea id="title" name="title" required placeholder="Формулировка поручения СД"></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label for="status">Статус *</label>
<select id="status" name="status" required>
<option value="В работе">В работе</option>
<option value="На контроле">На контроле</option>
<option value="Выполнено">Выполнено</option>
<option value="Просрочено">Просрочено</option>
</select>
</div>
<div class="form-group">
<label for="rm">РМ (Ответственный менеджер)</label>
<input type="text" id="rm" name="rm" placeholder="ФИО руководителя проекта">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="issueDate">Дата выдачи *</label>
<input type="date" id="issueDate" name="issueDate" required>
</div>
<div class="form-group">
<label for="planDate">Плановый срок *</label>
<input type="date" id="planDate" name="planDate" required>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="factDate">Фактический срок</label>
<input type="date" id="factDate" name="factDate">
</div>
<div class="form-group">
<label for="delegate">Исполнитель (делегирование)</label>
<input type="text" id="delegate" name="delegate" placeholder="Подразделение или ФИО">
</div>
</div>
<div class="form-group">
<label for="note">Примечание</label>
<textarea id="note" name="note" placeholder="Комментарии и уточнения"></textarea>
</div>
<div class="btn-group">
<button type="submit" class="btn btn-primary" id="saveBtn">Сохранить</button>
<button type="button" class="btn btn-secondary" onclick="window.location='registry.html'">Отмена</button>
<button type="button" class="btn btn-danger" id="deleteBtn" onclick="deleteInstruction()" style="display:none">Удалить</button>
</div>
</form>
</div>
<div class="answers-section" id="answersSection" style="display:none">
<h3>Промежуточные ответы</h3>
<div class="answers-list" id="answersList"></div>
<div style="margin-top:16px;display:flex;gap:12px">
<input type="text" id="answerText" placeholder="Добавить комментарий о ходе выполнения" style="flex:1;padding:10px;border:1px solid #ddd;border-radius:6px">
<input type="text" id="answerAuthor" placeholder="Автор" style="width:200px;padding:10px;border:1px solid #ddd;border-radius:6px">
<button class="btn btn-primary" onclick="addAnswer()">Добавить</button>
</div>
</div>
<div class="history-section" id="historySection" style="display:none">
<h3>История изменений</h3>
<div class="history-list" id="historyList"></div>
</div>
<div class="history-section" id="timelineSection" style="display:none">
<h3>Временная шкала исполнения</h3>
<div class="timeline" id="timeline"></div>
</div>
</main>
<script src="auth.js"></script>
<script>
const user = requireAuth();
renderUserMenu();
// Права на редактирование
const canEditAccess = canEdit();
const canDeleteAccess = canDelete();
if (canEditAccess) {
document.getElementById("newBtn").style.display = "inline-block";
}
const urlParams = new URLSearchParams(window.location.search);
const instructionId = urlParams.get("id");
let instruction = null;
// Блокируем форму для тех, кто не может редактировать
if (!canEditAccess && instructionId !== "new") {
document.getElementById("title").disabled = true;
document.getElementById("status").disabled = true;
document.getElementById("rm").disabled = true;
document.getElementById("issueDate").disabled = true;
document.getElementById("planDate").disabled = true;
document.getElementById("factDate").disabled = true;
document.getElementById("delegate").disabled = true;
document.getElementById("note").disabled = true;
document.getElementById("saveBtn").style.display = "none";
}
async function load() {
if (instructionId === "new") {
if (!canEditAccess) {
alert("У вас нет прав на создание поручений");
window.location = "index.html";
return;
}
document.getElementById("formTitle").textContent = "Новое поручение";
document.getElementById("deleteBtn").style.display = "none";
const today = new Date().toISOString().slice(0, 10);
document.getElementById("issueDate").value = today;
document.getElementById("answerAuthor").value = user.name;
return;
}
const res = await fetch(`/api/instructions/${instructionId}`);
if (!res.ok) {
alert("Поручение не найдено");
window.location = "registry.html";
return;
}
instruction = await res.json();
document.getElementById("formTitle").textContent = `Поручение №${instruction.id}`;
document.getElementById("pageTitle").textContent = `Поручение №${instruction.id}`;
document.getElementById("instructionId").value = instruction.id;
document.getElementById("title").value = instruction.title || "";
document.getElementById("status").value = instruction.status || "В работе";
document.getElementById("rm").value = instruction.rm || "";
document.getElementById("issueDate").value = instruction.issueDate || "";
document.getElementById("planDate").value = instruction.planDate || "";
document.getElementById("factDate").value = instruction.factDate || "";
document.getElementById("delegate").value = instruction.delegate || "";
document.getElementById("note").value = instruction.note || "";
if (canDeleteAccess) {
document.getElementById("deleteBtn").style.display = "inline-block";
}
document.getElementById("answerAuthor").value = user.name;
// Answers
if (instruction.answers && instruction.answers.length) {
document.getElementById("answersSection").style.display = "block";
document.getElementById("answersList").innerHTML = instruction.answers.map(a => `
<div class="answer-item">
<div class="author">${a.author || "Автор"} — ${a.date || ""}</div>
<div class="text">${a.text || ""}</div>
</div>
`).join("");
}
// History
if (instruction.history && instruction.history.length) {
document.getElementById("historySection").style.display = "block";
document.getElementById("historyList").innerHTML = instruction.history.map(h => `
<div class="history-item">
<div class="date">${h.date}</div>
<div><strong>${h.user}</strong>: ${h.field} → ${h.newValue}</div>
</div>
`).join("");
}
// Timeline
if (instruction.history && instruction.history.length) {
document.getElementById("timelineSection").style.display = "block";
const sorted = [...instruction.history].sort((a, b) => new Date(b.date) - new Date(a.date));
document.getElementById("timeline").innerHTML = sorted.map(h => `
<div class="timeline-item ${h.field === 'Создано' ? 'done' : h.field === 'Статус' && h.newValue === 'Просрочено' ? 'overdue' : ''}">
<div class="timeline-date">${h.date}</div>
<div class="timeline-title">${h.user}: ${h.field}</div>
<div class="timeline-desc">${h.newValue}</div>
</div>
`).join("");
}
}
document.getElementById("instructionForm").addEventListener("submit", async (e) => {
e.preventDefault();
if (!canEditAccess) {
alert("У вас нет прав на редактирование");
return;
}
const data = {
title: document.getElementById("title").value,
status: document.getElementById("status").value,
rm: document.getElementById("rm").value,
issueDate: document.getElementById("issueDate").value,
planDate: document.getElementById("planDate").value,
factDate: document.getElementById("factDate").value,
delegate: document.getElementById("delegate").value,
note: document.getElementById("note").value,
user: user.name
};
const url = instructionId === "new" ? "/api/instructions" : `/api/instructions/${instructionId}`;
const method = instructionId === "new" ? "POST" : "PUT";
const res = await fetch(url, {
method,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
if (res.ok) {
const result = await res.json();
window.location = instructionId === "new" ? `card.html?id=${result.id}` : "registry.html";
} else {
alert("Ошибка сохранения");
}
});
async function addAnswer() {
if (!instructionId || instructionId === "new") return;
const text = document.getElementById("answerText").value;
const author = document.getElementById("answerAuthor").value || user.name;
if (!text) return;
const res = await fetch(`/api/instructions/${instructionId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
answer: { text, author },
user: user.name
})
});
if (res.ok) {
document.getElementById("answerText").value = "";
load();
} else {
alert("Ошибка добавления ответа");
}
}
async function deleteInstruction() {
if (!canDeleteAccess) {
alert("У вас нет прав на удаление");
return;
}
if (!confirm("Удалить это поручение?")) return;
const res = await fetch(`/api/instructions/${instructionId}`, { method: "DELETE" });
if (res.ok) {
window.location = "registry.html";
} else {
alert("Ошибка удаления");
}
}
load();
</script>
</body>
</html>