185 lines
7.8 KiB
JavaScript
185 lines
7.8 KiB
JavaScript
var items = [];
|
||
var filterText = "";
|
||
|
||
async function load() {
|
||
items = await fetch("/api/items").then(function(r) { return r.json(); });
|
||
render();
|
||
}
|
||
|
||
function isComplete(r) {
|
||
return r.city && r.summa && r.date && r.fileSection && r.fio && r.docSection && r.payment &&
|
||
r.fileAttachment && r.docAttachment;
|
||
}
|
||
|
||
function paymentBadge(val) {
|
||
if (val === "оплачено") return '<span class="badge badge-paid">💰 ' + val + '</span>';
|
||
if (val === "в ожидании") return '<span class="badge badge-pending">⏳ ' + val + '</span>';
|
||
return '<span class="badge badge-unpaid">❌ ' + (val || "") + '</span>';
|
||
}
|
||
|
||
function render() {
|
||
var q = filterText.toLowerCase();
|
||
var filtered = items.filter(function(i) {
|
||
return !q ||
|
||
(i.city || "").toLowerCase().indexOf(q) !== -1 ||
|
||
(i.fio || "").toLowerCase().indexOf(q) !== -1 ||
|
||
(i.fileSection || "").toLowerCase().indexOf(q) !== -1 ||
|
||
(i.docSection || "").toLowerCase().indexOf(q) !== -1 ||
|
||
(i.payment || "").toLowerCase().indexOf(q) !== -1;
|
||
});
|
||
|
||
var tbody = document.getElementById("tbody");
|
||
var empty = document.getElementById("empty");
|
||
|
||
if (filtered.length === 0) {
|
||
tbody.innerHTML = "";
|
||
empty.style.display = "block";
|
||
} else {
|
||
empty.style.display = "none";
|
||
tbody.innerHTML = filtered.map(function(r) {
|
||
var fileLink = r.fileAttachment ? '<a href="' + esc(r.fileAttachment.path) + '" target="_blank" class="file-link">📎 ' + esc(r.fileAttachment.name) + '</a>' : '';
|
||
var docLink = r.docAttachment ? '<a href="' + esc(r.docAttachment.path) + '" target="_blank" class="file-link">📎 ' + esc(r.docAttachment.name) + '</a>' : '';
|
||
var payLink = r.paymentAttachment ? '<br><a href="' + esc(r.paymentAttachment.path) + '" target="_blank" class="file-link">📎 ' + esc(r.paymentAttachment.name) + '</a>' : '';
|
||
var rowClass = isComplete(r) ? ' class="row-complete"' : '';
|
||
return "<tr" + rowClass + ">" +
|
||
"<td>" + esc(r.city) + "</td>" +
|
||
"<td>" + (r.summa ? Number(r.summa).toLocaleString("ru-RU") : "") + " ₸</td>" +
|
||
"<td>" + esc(r.date) + "</td>" +
|
||
"<td><div>" + esc(r.fileSection) + "</div>" + fileLink + "</td>" +
|
||
"<td>" + esc(r.fio) + "</td>" +
|
||
"<td><div>" + esc(r.docSection) + "</div>" + docLink + "</td>" +
|
||
"<td>" + paymentBadge(r.payment) + payLink + "</td>" +
|
||
'<td class="actions">' +
|
||
'<button class="btn-sm btn-edit" onclick="editItem(' + r.id + ')">✏️</button>' +
|
||
'<button class="btn-sm btn-del" onclick="deleteItem(' + r.id + ')">🗑️</button>' +
|
||
"</td>" +
|
||
"</tr>";
|
||
}).join("");
|
||
}
|
||
|
||
updateStats(filtered);
|
||
}
|
||
|
||
function updateStats(filtered) {
|
||
var total = items.length;
|
||
var shown = filtered.length;
|
||
var sum = filtered.reduce(function(s, r) { return s + (parseFloat(r.summa) || 0); }, 0);
|
||
document.getElementById("stats").innerHTML =
|
||
'<span>Всего: <b>' + total + '</b></span>' +
|
||
'<span>Показано: <b>' + shown + '</b></span>' +
|
||
'<span>Сумма: <b>' + sum.toLocaleString("ru-RU") + ' ₸</b></span>';
|
||
}
|
||
|
||
function esc(s) { return (s || "").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); }
|
||
|
||
function openForm() {
|
||
document.getElementById("modal").style.display = "flex";
|
||
document.getElementById("formTitle").textContent = "Новая запись";
|
||
document.getElementById("form").reset();
|
||
document.getElementById("editId").value = "";
|
||
document.getElementById("previewFile").innerHTML = "";
|
||
document.getElementById("previewDoc").innerHTML = "";
|
||
document.getElementById("previewPayment").innerHTML = "";
|
||
}
|
||
function closeForm() { document.getElementById("modal").style.display = "none"; }
|
||
|
||
async function uploadFile(inputName) {
|
||
var input = document.querySelector("[name='" + inputName + "']");
|
||
if (!input || !input.files || !input.files[0]) return null;
|
||
var formData = new FormData();
|
||
formData.append("file", input.files[0]);
|
||
var resp = await fetch("/api/upload", { method: "POST", body: formData });
|
||
var result = await resp.json();
|
||
if (result.ok && result.files && result.files[0]) return result.files[0];
|
||
return null;
|
||
}
|
||
|
||
async function saveItem(e) {
|
||
e.preventDefault();
|
||
var form = e.target;
|
||
|
||
var fileData = await uploadFile("fileAttachment");
|
||
var docData = await uploadFile("docAttachment");
|
||
var payData = await uploadFile("paymentAttachment");
|
||
|
||
var data = {
|
||
city: form.city.value,
|
||
summa: form.summa.value,
|
||
date: form.date.value,
|
||
fileSection: form.fileSection.value,
|
||
fio: form.fio.value,
|
||
docSection: form.docSection.value,
|
||
payment: form.payment.value
|
||
};
|
||
|
||
var editId = document.getElementById("editId").value;
|
||
|
||
if (editId) {
|
||
var existing = items.find(function(i) { return i.id === parseInt(editId); });
|
||
data.fileAttachment = fileData || (existing ? existing.fileAttachment : null);
|
||
data.docAttachment = docData || (existing ? existing.docAttachment : null);
|
||
data.paymentAttachment = payData || (existing ? existing.paymentAttachment : null);
|
||
await fetch("/api/items/" + editId, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) });
|
||
} else {
|
||
data.fileAttachment = fileData;
|
||
data.docAttachment = docData;
|
||
data.paymentAttachment = payData;
|
||
await fetch("/api/items", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) });
|
||
}
|
||
closeForm();
|
||
load();
|
||
}
|
||
|
||
async function editItem(id) {
|
||
var item = items.find(function(i) { return i.id === id; });
|
||
if (!item) return;
|
||
document.getElementById("modal").style.display = "flex";
|
||
document.getElementById("formTitle").textContent = "Изменить запись";
|
||
document.getElementById("editId").value = id;
|
||
document.querySelector("[name=city]").value = item.city || "";
|
||
document.querySelector("[name=summa]").value = item.summa || "";
|
||
document.querySelector("[name=date]").value = item.date || "";
|
||
document.querySelector("[name=fileSection]").value = item.fileSection || "";
|
||
document.querySelector("[name=fio]").value = item.fio || "";
|
||
document.querySelector("[name=docSection]").value = item.docSection || "";
|
||
document.querySelector("[name=payment]").value = item.payment || "";
|
||
|
||
document.getElementById("previewFile").innerHTML = item.fileAttachment
|
||
? '<span class="attached">📎 ' + esc(item.fileAttachment.name) + '</span>'
|
||
: '';
|
||
document.getElementById("previewDoc").innerHTML = item.docAttachment
|
||
? '<span class="attached">📎 ' + esc(item.docAttachment.name) + '</span>'
|
||
: '';
|
||
document.getElementById("previewPayment").innerHTML = item.paymentAttachment
|
||
? '<span class="attached">📎 ' + esc(item.paymentAttachment.name) + '</span>'
|
||
: '';
|
||
}
|
||
|
||
async function deleteItem(id) {
|
||
if (!confirm("Удалить запись?")) return;
|
||
await fetch("/api/items/" + id, { method: "DELETE" });
|
||
load();
|
||
}
|
||
|
||
document.getElementById("search").addEventListener("input", function(e) {
|
||
filterText = e.target.value;
|
||
render();
|
||
});
|
||
|
||
function exportCsv() {
|
||
if (items.length === 0) { alert("Нет данных для выгрузки"); return; }
|
||
var head = ["Город","Сумма","Дата","Раздел для файла","Файл","ФИО","Раздел для документов","Документ","Платёж","Файл оплаты"];
|
||
var body = items.map(function(r) {
|
||
return [r.city, r.summa, r.date, r.fileSection, r.fileAttachment ? r.fileAttachment.name : "", r.fio, r.docSection, r.docAttachment ? r.docAttachment.name : "", r.payment, r.paymentAttachment ? r.paymentAttachment.name : ""]
|
||
.map(function(v) { return '"' + String(v || "").replace(/"/g, '""') + '"'; })
|
||
.join(";");
|
||
});
|
||
var csv = "\uFEFF" + [head.join(";"), ...body].join("\n");
|
||
var a = document.createElement("a");
|
||
a.href = URL.createObjectURL(new Blob([csv], { type: "text/csv" }));
|
||
a.download = "otchet.csv";
|
||
a.click();
|
||
}
|
||
|
||
load();
|