zhurnal-ucheta-zayavok/script.js

338 lines
11 KiB
JavaScript
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.

// Демо-данные для первого запуска
const DEMO_DATA = [
{ id: 1, submitter: "Алия К.", description: "Закупка СИЗ для склада", deadline: "2026-09-10", status: "Новая", dateCreated: "2026-09-01" },
{ id: 2, submitter: "Ержан М.", description: "Ремонт серверной", deadline: "2026-09-15", status: "В работе", dateCreated: "2026-09-02" },
{ id: 3, submitter: "Данияр С.", description: "Выдача пропуска", deadline: "2026-09-05", status: "Выполнено", dateCreated: "2026-09-01" },
{ id: 4, submitter: "Гульнара Т.", description: "Обновление ПО на компьютерах", deadline: "2026-09-20", status: "Новая", dateCreated: "2026-09-03" },
{ id: 5, submitter: "Серик Б.", description: "Замена картриджа в принтере", deadline: "2026-09-08", status: "В работе", dateCreated: "2026-09-02" }
];
// Ключи localStorage
const KEY_REQUESTS = "requests_data";
const KEY_HISTORY = "requests_history";
// Состояние
let requests = [];
let history = [];
let currentFilter = "all";
let searchQuery = "";
// Инициализация
document.addEventListener("DOMContentLoaded", () => {
loadData();
setupEventListeners();
render();
});
// Загрузка данных
function loadData() {
const storedRequests = localStorage.getItem(KEY_REQUESTS);
if (storedRequests) {
requests = JSON.parse(storedRequests);
} else {
requests = [...DEMO_DATA];
saveData();
}
const storedHistory = localStorage.getItem(KEY_HISTORY);
if (storedHistory) {
history = JSON.parse(storedHistory);
}
}
// Сохранение данных
function saveData() {
localStorage.setItem(KEY_REQUESTS, JSON.stringify(requests));
}
function saveHistory() {
localStorage.setItem(KEY_HISTORY, JSON.stringify(history));
}
// Настройка обработчиков
function setupEventListeners() {
document.getElementById("btn-add").addEventListener("click", openModal);
document.getElementById("modal-close").addEventListener("click", closeModal);
document.getElementById("form-cancel").addEventListener("click", closeModal);
document.getElementById("modal-overlay").addEventListener("click", (e) => {
if (e.target === document.getElementById("modal-overlay")) closeModal();
});
document.getElementById("request-form").addEventListener("submit", handleFormSubmit);
document.getElementById("search-input").addEventListener("input", (e) => {
searchQuery = e.target.value.toLowerCase();
renderTable();
});
document.getElementById("btn-export").addEventListener("click", exportToExcel);
document.getElementById("btn-history").addEventListener("click", openHistory);
document.getElementById("history-close").addEventListener("click", closeHistory);
document.getElementById("history-overlay").addEventListener("click", (e) => {
if (e.target === document.getElementById("history-overlay")) closeHistory();
});
document.querySelectorAll(".chip").forEach(chip => {
chip.addEventListener("click", () => {
document.querySelectorAll(".chip").forEach(c => c.classList.remove("active"));
chip.classList.add("active");
currentFilter = chip.dataset.status;
renderTable();
});
});
}
// Открытие модального окна
function openModal(editId = null) {
const modal = document.getElementById("modal-overlay");
const title = document.getElementById("modal-title");
const form = document.getElementById("request-form");
form.reset();
document.getElementById("form-id").value = "";
if (editId) {
const req = requests.find(r => r.id === editId);
if (req) {
title.textContent = "Изменить заявку";
document.getElementById("form-id").value = req.id;
document.getElementById("form-submitter").value = req.submitter;
document.getElementById("form-description").value = req.description;
document.getElementById("form-deadline").value = req.deadline;
document.getElementById("form-status").value = req.status;
}
} else {
title.textContent = "Добавить заявку";
}
modal.classList.add("active");
}
// Закрытие модального окна
function closeModal() {
document.getElementById("modal-overlay").classList.remove("active");
}
// Обработка формы
function handleFormSubmit(e) {
e.preventDefault();
const id = document.getElementById("form-id").value;
const submitter = document.getElementById("form-submitter").value.trim();
const description = document.getElementById("form-description").value.trim();
const deadline = document.getElementById("form-deadline").value;
const status = document.getElementById("form-status").value;
if (id) {
// Редактирование
const index = requests.findIndex(r => r.id === parseInt(id));
if (index !== -1) {
requests[index] = {
...requests[index],
submitter,
description,
deadline,
status
};
addToHistory(`Изменена заявка #${id}: ${description}`, "Текущий пользователь");
}
} else {
// Создание
const newId = Date.now();
requests.push({
id: newId,
submitter,
description,
deadline,
status,
dateCreated: new Date().toISOString().split("T")[0]
});
addToHistory(`Добавлена заявка #${newId}: ${description}`, "Текущий пользователь");
}
saveData();
closeModal();
render();
}
// Добавление в историю
function addToHistory(action, who) {
const now = new Date();
history.unshift({
datetime: now.toLocaleString("ru-RU"),
action,
who
});
saveHistory();
}
// Удаление заявки
function deleteRequest(id) {
const req = requests.find(r => r.id === id);
if (!req) return;
if (confirm(`Вы уверены, что хотите удалить заявку #${id}?\n\n${req.description}`)) {
requests = requests.filter(r => r.id !== id);
addToHistory(`Удалена заявка #${id}: ${req.description}`, "Текущий пользователь");
saveData();
render();
}
}
// Фильтрация данных
function getFilteredData() {
let filtered = [...requests];
// Фильтр по статусу
if (currentFilter !== "all") {
filtered = filtered.filter(r => r.status === currentFilter);
}
// Поиск
if (searchQuery) {
filtered = filtered.filter(r =>
r.submitter.toLowerCase().includes(searchQuery) ||
r.description.toLowerCase().includes(searchQuery)
);
}
return filtered;
}
// Рендеринг
function render() {
renderStats();
renderTable();
}
function renderStats() {
const total = requests.length;
const newCount = requests.filter(r => r.status === "Новая").length;
const progressCount = requests.filter(r => r.status === "В работе").length;
const doneCount = requests.filter(r => r.status === "Выполнено").length;
const rejectedCount = requests.filter(r => r.status === "Отклонено").length;
document.getElementById("stat-total").textContent = total;
document.getElementById("stat-new").textContent = newCount;
document.getElementById("stat-progress").textContent = progressCount;
document.getElementById("stat-done").textContent = doneCount;
document.getElementById("stat-rejected").textContent = rejectedCount;
}
function renderTable() {
const tbody = document.getElementById("table-body");
const filtered = getFilteredData();
if (filtered.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="6" class="empty-state">
<p>Нет заявок для отображения</p>
</td>
</tr>
`;
return;
}
tbody.innerHTML = filtered.map((req, index) => {
const statusClass = getStatusClass(req.status);
const formattedDate = formatDate(req.deadline);
return `
<tr>
<td>${index + 1}</td>
<td>${escapeHtml(req.submitter)}</td>
<td>${escapeHtml(req.description)}</td>
<td>${formattedDate}</td>
<td><span class="status-badge ${statusClass}">${req.status}</span></td>
<td>
<div class="action-buttons">
<button class="btn btn-small btn-edit" onclick="openModal(${req.id})">Изменить</button>
<button class="btn btn-small btn-delete" onclick="deleteRequest(${req.id})">Удалить</button>
</div>
</td>
</tr>
`;
}).join("");
}
function getStatusClass(status) {
switch (status) {
case "Новая": return "status-new";
case "В работе": return "status-progress";
case "Выполнено": return "status-done";
case "Отклонено": return "status-rejected";
default: return "";
}
}
function formatDate(dateStr) {
if (!dateStr) return "";
const [year, month, day] = dateStr.split("-");
return `${day}.${month}.${year}`;
}
function escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
// Экспорт в Excel (CSV)
function exportToExcel() {
const filtered = getFilteredData();
if (filtered.length === 0) {
alert("Нет данных для экспорта");
return;
}
const headers = ["№", "Кто подал", "Что нужно", "Срок", "Статус"];
const rows = filtered.map((req, index) => [
index + 1,
req.submitter,
req.description,
formatDate(req.deadline),
req.status
]);
const csvContent = "\uFEFF" + [
headers.join(";"),
...rows.map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(";"))
].join("\n");
const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = `zhurnal_zayavok_${new Date().toISOString().split("T")[0]}.csv`;
link.click();
URL.revokeObjectURL(link.href);
addToHistory("Экспорт данных в Excel", "Текущий пользователь");
}
// История
function openHistory() {
const tbody = document.getElementById("history-body");
if (history.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="3" class="empty-state">
<p>История пуста</p>
</td>
</tr>
`;
} else {
tbody.innerHTML = history.map(h => `
<tr>
<td>${h.datetime}</td>
<td>${escapeHtml(h.action)}</td>
<td>${escapeHtml(h.who)}</td>
</tr>
`).join("");
}
document.getElementById("history-overlay").classList.add("active");
}
function closeHistory() {
document.getElementById("history-overlay").classList.remove("active");
}