diff --git a/backend/static/css/components/request-list.css b/backend/static/css/components/request-list.css index b9805397..b0091356 100644 --- a/backend/static/css/components/request-list.css +++ b/backend/static/css/components/request-list.css @@ -285,3 +285,122 @@ } } +/* ======================================== + AJAX-режим: стили для req-item + ======================================== */ + +.req-item { + border-bottom: 1px solid var(--bs-border-color); + padding: 1rem; + transition: background-color 0.2s; +} + +.req-item:hover { + background-color: var(--bs-light); +} + +.req-item:last-child { + border-bottom: none; +} + +.req-header { + margin-bottom: 0.75rem; +} + +.req-title-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + margin-bottom: 0.5rem; +} + +.req-link { + display: flex; + align-items: center; + gap: 0.5rem; + text-decoration: none; + color: var(--bs-body-color); + font-weight: 500; + flex: 1; + min-width: 0; +} + +.req-link:hover { + color: var(--bs-primary); +} + +.req-link strong { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.req-meta { + display: flex; + flex-wrap: wrap; + gap: 1rem; + font-size: 0.875rem; + color: var(--bs-secondary-color); +} + +.req-meta > span { + display: flex; + align-items: center; + gap: 0.25rem; +} + +.req-comment { + margin: 0.5rem 0; + padding: 0.5rem; + background-color: var(--bs-light); + border-radius: 0.375rem; + font-size: 0.875rem; + color: var(--bs-secondary-color); +} + +.req-footer { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + align-items: center; + padding-top: 0.75rem; + border-top: 1px solid var(--bs-border-color-translucent); + font-size: 0.875rem; +} + +.req-approver, +.req-recipients { + display: flex; + align-items: center; + gap: 0.25rem; + color: var(--bs-secondary-color); +} + +/* Badge стили для статусов */ +.badge-warning { + background-color: #ffc107; + color: #000; +} + +.badge-success { + background-color: #198754; + color: #fff; +} + +.badge-danger { + background-color: #dc3545; + color: #fff; +} + +.badge-secondary { + background-color: #6c757d; + color: #fff; +} + +/* Спиннер загрузки */ +.load-more-trigger { + visibility: hidden; +} + + diff --git a/backend/static/js/components/requestListHandler.js b/backend/static/js/components/requestListHandler.js index 93e6df05..e1ccc471 100644 --- a/backend/static/js/components/requestListHandler.js +++ b/backend/static/js/components/requestListHandler.js @@ -22,11 +22,11 @@ export function initRequestListHandler(options) { headers = {} } = options; - const listElement = document.getElementById('requestList'); + const listElement = document.getElementById('reqList'); const searchInput = document.getElementById('reqFilter'); if (!listElement) { - console.warn('initRequestListHandler: #requestList not found'); + console.warn('initRequestListHandler: #reqList not found'); return { load: () => {}, destroy: () => {} }; } @@ -281,62 +281,177 @@ export function initRequestListHandler(options) { * @returns {string} HTML */ function renderRequestRow(req) { - const statusClass = getStatusClass(req.status); - const statusText = getStatusText(req.status); - const detailUrl = detailUrlTemplate.replace('{id}', req.id); + const type = (req.type || '').toLowerCase(); + const status = (req.status || '').toLowerCase(); + + // Иконка типа + let typeIcon = 'bi-clipboard-check text-secondary'; + if (type === 'vacation') typeIcon = 'bi-calendar-check text-primary'; + else if (type === 'sick_leave') typeIcon = 'bi-capsule text-danger'; + else if (type === 'transfer') typeIcon = 'bi-box-arrow-right text-info'; + else if (type === 'dismissal') typeIcon = 'bi-door-open text-warning'; + + // Текст типа + const typeText = { + 'vacation': 'Отпуск', + 'sick_leave': 'Больничный', + 'transfer': 'Перевод', + 'dismissal': 'Увольнение', + 'other': 'Другое' + }[type] || req.type; + + // Класс и текст статуса + let statusBadgeClass = 'text-bg-light'; + let statusText = req.status; + if (status === 'draft') { statusBadgeClass = 'text-bg-info'; statusText = 'Черновик'; } + else if (status === 'pending') { statusBadgeClass = 'text-bg-warning'; statusText = 'На рассмотр.'; } + else if (status === 'approved') { statusBadgeClass = 'text-bg-success'; statusText = 'Одобрено'; } + else if (status === 'rejected') { statusBadgeClass = 'text-bg-danger'; statusText = 'Отклонено'; } + else if (status === 'cancelled') { statusBadgeClass = 'text-bg-secondary'; statusText = 'Отменено'; } + + // URL для сотрудника + const employeeUrl = req.employee?.id === userId ? '/employees/profile/' : `/employees/${req.employee?.id}/`; + const employeeName = req.employee?.id === userId ? 'Вы' : (req.employee?.full_name || req.employee?.display_name || 'Сотрудник'); + + // Кнопки действий + let actionButtons = ''; + if (req.employee?.id !== userId && canProcess) { + actionButtons = ` + + + `; + } else if (req.employee?.id === userId) { + actionButtons = ` + + + `; + } return ` -
-
-
- - - ${escapeHtml(req.display_title || req.title || 'Заявление')} - - ${statusText} +
+
+ +
+
+ +
-
- - ${escapeHtml(req.employee?.full_name || 'Неизвестно')} - - ${req.date_from ? ` - - - ${formatDate(req.date_from)}${req.date_to ? ' — ' + formatDate(req.date_to) : ''} - - ` : ''} - - ${formatDate(req.created_at)} - + +
+
+ ${escapeHtml(req.title || 'Без заголовка')} + ${type ? `${typeText}` : ''} + ${req.comment ? `
${escapeHtml(req.comment.substring(0, 100))}
` : ''} +
-
- - ${req.comment ? ` -
${escapeHtml(req.comment)}
- ` : ''} - + + +
+
+ ${req.employee ? `${employeeName}` : ''} +
+
+ + +
+
+ ${req.date_from || req.date_to ? ` + ${req.date_from ? formatDate(req.date_from) : '—'}
+ ${req.date_to ? formatDate(req.date_to) : '—'} + ` : '—'} +
+
+ + +
+
+ ${req.status ? `${statusText}` : ''} +
+
+ + +
+
+ ${req.created_at ? formatDate(req.created_at) : '—'} +
+
+ + +
+
+ ${actionButtons} +
+
+ + + + +
+
+
+ Комментарии + +
+
+
+
+ + + +
+
+
+
`; } diff --git a/backend/templates/requests_app/request_list_full.html b/backend/templates/requests_app/request_list_full.html index 6f7c22e5..febdc4ae 100644 --- a/backend/templates/requests_app/request_list_full.html +++ b/backend/templates/requests_app/request_list_full.html @@ -49,214 +49,13 @@ - {% if requests %} - {% for obj in requests %} -
-
- -
-
- {% with tp=obj.type|lower %} - {% if tp == 'vacation' %} - {% elif tp == 'sick_leave' %} - {% elif tp == 'transfer' %} - {% elif tp == 'dismissal' %} - {% else %} - {% endif %} - {% endwith %} -
-
- - -
-
- {{ obj.title|default:"Без заголовка" }} - {% if obj.type %}{% with tp=obj.type|lower %} - - {% if tp == 'vacation' %}Отпуск - {% elif tp == 'sick_leave' %}Больничный - {% elif tp == 'transfer' %}Перевод - {% elif tp == 'dismissal' %}Увольнение - {% elif tp == 'other' %}Другое - {% else %}{{ obj.type }}{% endif %} - - {% endwith %}{% endif %} - {% if obj.comment %} -
{{ obj.comment|truncatewords:15 }}
- {% endif %} -
-
- - -
-
- {% if obj.employee %} - {% if obj.employee.id == user.id %} - Вы - {% else %} - - {{ obj.employee.full_name|default:obj.employee.display_name|default:"Сотрудник" }} - - {% endif %} - {% endif %} -
-
- - -
-
- {% if obj.date_from or obj.date_to %} - {% if obj.date_from %} - {{ obj.date_from|format_iso_date:"d.m.Y" }}
- {% else %} - —
- {% endif %} - {% if obj.date_to %} - {{ obj.date_to|format_iso_date:"d.m.Y" }} - {% else %} - — - {% endif %} - {% else %} - — - {% endif %} -
-
- - -
-
- {% if obj.status %}{% with st=obj.status|lower %} - - {% if st == 'draft' %}Черновик - {% elif st == 'pending' %}На рассмотр. - {% elif st == 'approved' %}Одобрено - {% elif st == 'rejected' %}Отклонено - {% elif st == 'cancelled' %}Отменено - {% else %}{{ obj.status }}{% endif %} - - {% endwith %}{% endif %} -
-
- - -
-
- {% if obj.created_at %} - {{ obj.created_at|format_iso_date:"d.m.Y" }} - {% else %} - — - {% endif %} -
-
- - -
-
- {% if obj.employee.id != user.id and can_process %} - - - {% elif obj.employee.id == user.id %} - - - {% endif %} -
-
-
- - - - -
-
-
- Комментарии - -
-
-
- {% csrf_token %} -
- - - - -
-
-
-
-
- {% endfor %} - {% else %} -
Нет заявлений.
- {% endif %} - - - {# Пагинация по next/prev ссылкам из API (если есть) #} - {% if next_url or prev_url %} -
- - Назад - - Всего: {{ count|default:'—' }} - - Вперёд - + {# Список загружается через JavaScript из requestListHandler.js #} +
+
+ Загрузка... +
- {% endif %} +
{# ===== Modals ===== #} @@ -558,6 +357,11 @@ {% endblock %} {% block extra_js %} +