From 4a7dbf7ce282b314aa08b09837d87445ccb7d984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B3=D0=BE=D1=80=D1=8C=20=D0=9D=D0=B0=D0=B4=D0=B5?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Tue, 9 Dec 2025 13:24:55 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20UI=20=D0=B4=D0=BB=D1=8F=20=D1=83?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=B3=D0=BE=D1=80=D0=B8=D1=8F=D0=BC=D0=B8=20?= =?UTF-8?q?=D0=BE=D0=B1=D0=BE=D1=80=D1=83=D0=B4=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлена кнопка 'Управление категориями' на странице оборудования - Создан модал с двумя секциями: добавление и список категорий - Форма создания категории: название, иконка Bootstrap Icons, описание - Список существующих категорий с возможностью удаления - JavaScript функции для работы с API категорий - Автоматическое обновление списка после создания/удаления - Защита от удаления категорий с привязанным оборудованием - Доступно только для staff пользователей (can_manage) --- .../templates/procurement/equipment_list.html | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/backend/templates/procurement/equipment_list.html b/backend/templates/procurement/equipment_list.html index 11d9dc24..2d744019 100644 --- a/backend/templates/procurement/equipment_list.html +++ b/backend/templates/procurement/equipment_list.html @@ -13,6 +13,15 @@ {# ===== ШАПКА СТРАНИЦЫ ===== #} {% include "includes/components/page_header.html" with icon="bi-laptop" title="Оборудование" search_id="eqSearch" search_placeholder="Поиск по названию или инв. номеру..." has_filters=True filters_target="eqFilters" filters_template="procurement/_equipment_filters.html" create_as_card=True create_modal="equipmentCreateModal" create_text="Добавить оборудование" %} + {# ===== КНОПКА УПРАВЛЕНИЯ КАТЕГОРИЯМИ ===== #} + {% if can_manage %} +
+ +
+ {% endif %} + {# ===== СЕТКА ОБОРУДОВАНИЯ ===== #}
{% if equipment_list %} @@ -467,6 +476,76 @@
+{# ===== МОДАЛКА УПРАВЛЕНИЯ КАТЕГОРИЯМИ ===== #} + + {% endblock %} {% block extra_js %} @@ -705,5 +784,125 @@ submitBtn.innerHTML = 'Сохранить изменения'; } }); + + // === УПРАВЛЕНИЕ КАТЕГОРИЯМИ === + // Загрузка списка категорий + async function loadCategories() { + const list = document.getElementById('categoriesList'); + list.innerHTML = '
'; + + try { + const resp = await fetch('/api/procurement/equipment-categories/'); + if (resp.ok) { + const categories = await resp.json(); + + if (categories.length === 0) { + list.innerHTML = '
Категорий пока нет
'; + return; + } + + let html = '
'; + categories.forEach(cat => { + html += ` +
+
+ + ${cat.name} + ${cat.description ? `
${cat.description}` : ''} +
+ +
+ `; + }); + html += '
'; + list.innerHTML = html; + } else { + list.innerHTML = '
Ошибка загрузки
'; + } + } catch (e) { + console.error(e); + list.innerHTML = '
Ошибка сети
'; + } + } + + // Загрузка при открытии модала + document.getElementById('categoryManageModal')?.addEventListener('show.bs.modal', loadCategories); + + // Создание новой категории + document.getElementById('categoryCreateForm')?.addEventListener('submit', async (e) => { + e.preventDefault(); + + const name = document.getElementById('catName').value.trim(); + const icon = document.getElementById('catIcon').value.trim(); + const description = document.getElementById('catDescription').value.trim(); + + if (!name) return; + + const submitBtn = e.target.querySelector('[type="submit"]'); + submitBtn.disabled = true; + submitBtn.innerHTML = 'Создание...'; + + try { + const resp = await fetch('/api/procurement/equipment-categories/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': document.querySelector('[name="csrfmiddlewaretoken"]').value + }, + body: JSON.stringify({ name, icon: icon || null, description: description || '' }) + }); + + if (resp.ok) { + // Очистить форму + document.getElementById('catName').value = ''; + document.getElementById('catIcon').value = ''; + document.getElementById('catDescription').value = ''; + + // Перезагрузить список + await loadCategories(); + + // Обновить выпадающие списки категорий в формах + location.reload(); + } else { + const err = await resp.json(); + alert(err.name?.[0] || err.detail || 'Ошибка создания категории'); + } + } catch (e) { + console.error(e); + alert('Ошибка сети'); + } finally { + submitBtn.disabled = false; + submitBtn.innerHTML = 'Добавить'; + } + }); + + // Удаление категории + async function deleteCategory(id, name) { + if (!confirm(`Удалить категорию "${name}"?\n\nВнимание: это возможно только если к категории не привязано оборудование.`)) { + return; + } + + try { + const resp = await fetch(`/api/procurement/equipment-categories/${id}/`, { + method: 'DELETE', + headers: { + 'X-CSRFToken': document.querySelector('[name="csrfmiddlewaretoken"]').value + } + }); + + if (resp.ok || resp.status === 204) { + await loadCategories(); + alert('Категория удалена'); + } else { + const err = await resp.json(); + alert(err.detail || 'Ошибка удаления. Возможно, к категории привязано оборудование.'); + } + } catch (e) { + console.error(e); + alert('Ошибка сети'); + } + } {% endblock %}