# Руководство по шаблонам EUSRR ## Статус рефакторинга **Версия**: 2.1 **Дата обновления**: январь 2025 **Завершено**: Фазы 0-5 (85%) --- ## Архитектура шаблонов ### Базовая структура ``` templates/ ├── base.html # Корневой шаблон с navbar/sidebar ├── includes/ # Общие компоненты │ ├── navbar.html # Верхняя навигация │ ├── sidebar.html # Боковое меню │ ├── rightbar_calendar.html # Календарь (237 строк, было 996) │ └── messages.html # Django messages │ ├── admin/ # Административные override ├── auth/ # Авторизация, регистрация ├── communications/ # Чаты ├── documents/ # Документы ├── emails/ # Email-шаблоны ├── employees/ # Сотрудники, отделы │ ├── components/ # Переиспользуемые компоненты │ │ ├── department_controls.html │ │ ├── department_info.html │ │ ├── department_modals.html │ │ └── department_sidebar.html │ ├── _employee_edit.html # 24 строки (было 1391) │ ├── department_detail.html # 46 строк (было 1025) │ ├── employees_list.html │ └── ... │ ├── feed/ # Лента новостей ├── requests_app/ # Заявления сотрудников └── search/ # Поиск ``` --- ## Статические файлы ### JavaScript **Утилиты** (`static/js/utils/`): - `stringUtils.js` (115 строк) - esc(), norm(), escAttr(), truncate(), getInitials() - `timing.js` (173 строки) - debounce(), throttle(), delay(), poll(), once() - `scroll.js` (205 строк) - smoothScrollTo(), scrollToTop(), isElementInViewport() - `index.js` - Центральная точка экспорта **Компоненты** (`static/js/components/`): - `listFilter.js` (279 строк) - Универсальная фильтрация списков - `teamWheel.js` (308 строк) - Вращающийся виджет команды - `calendarWidget.js` (1002 строки) - FullCalendar + CRUD событий - `index.js` - Центральная точка экспорта (v1.1.0) ### CSS **Компоненты** (`static/css/components/`): - `chat-detail.css` - Чат - `department-list.css` - Список отделов - `department-sidebar.css` - Боковая панель отдела - `document-list.css` - Список документов - `employee-detail.css` - Детали сотрудника - `cards.css` - Карточки в ленте - `login.css` - Страница входа - `logout.css` - Страница выхода - `navbar.css` - Верхняя навигация - `password-forms.css` - Формы паролей - `register.css` - Регистрация - `request-list.css` - Список заявлений - `rightbar-calendar.css` - Календарь - `search-results.css` - Результаты поиска - `sidebar.css` - Боковое меню - `team-wheel.css` - Виджет команды - И другие... **Общие стили**: - `common.css` (245 строк) - Переиспользуемые классы - `variables.css` - CSS-переменные и цвета --- ## Использование компонентов ### JavaScript утилиты #### В base.html (глобальный импорт) ```django ``` #### В шаблонах ```django {% block extra_js %} {% endblock %} ``` ### CSS компоненты #### Подключение в шаблоне ```django {% block extra_css %} {% endblock %} ``` #### Использование общих классов (common.css) ```html
📋
Документов пока нет

Удалить сотрудника?

Это действие необратимо

Иван Петров
2 минуты назад

Текст поста...

``` --- ## Именование файлов ### Шаблоны - `{model}_list.html` - Списки (employees_list, document_list) - `{model}_detail.html` - Детальные страницы (employee_detail, department_detail) - `{model}_form.html` - Формы создания/редактирования - `{model}_confirm_delete.html` - Подтверждение удаления - `_{partial}.html` - Партиалы (начинаются с `_`) ### Компоненты - `components/{name}.html` - Переиспользуемые компоненты приложения - `includes/{name}.html` - Глобальные компоненты --- ## Лучшие практики ### 1. Структура шаблона ```django {% extends "base.html" %} {% load static %} {% block title %}Название страницы{% endblock %} {% block extra_css %} {% endblock %} {% block content %} {# Основной контент #} {% endblock %} {% block extra_js %} {% endblock %} ``` ### 2. Использование компонентов ```django {# С параметрами #} {% include "employees/components/department_info.html" with department=dept %} {# С контекстом #} {% with user=employee %} {% include "includes/components/user_card.html" %} {% endwith %} ``` ### 3. Стили и скрипты ✅ **ПРАВИЛЬНО**: ```django {# Стили в extra_css блоке #} {% block extra_css %} {% endblock %} {# Скрипты в extra_js блоке #} {% block extra_js %} {% endblock %} ``` ❌ **НЕПРАВИЛЬНО**: ```django {# Встроенные стили в body #} {# Встроенные скрипты в body #} ``` ### 4. Безопасность ```django {# Всегда используйте CSRF-защиту #}
{% csrf_token %} {# ... поля формы ... #}
{# Экранирование HTML (по умолчанию включено) #} {{ user.bio }} {# Безопасно #} {# Если нужен HTML (используйте с осторожностью!) #} {{ user.bio|safe }} {# Только для доверенного контента! #} ``` --- ## Компоненты и их использование ### ListFilter (универсальная фильтрация) **Импорт**: ```javascript import { ListFilter, createDataAttrMatcher, createSelectorMatcher } from '{% static "js/components/listFilter.js" %}'; ``` **Базовое использование**: ```javascript new ListFilter({ listSelector: '.employees-list', itemSelector: '.employee-card', searchInputSelector: '#searchInput' }); ``` **С пользовательской функцией поиска**: ```javascript new ListFilter({ listSelector: '#empList', itemSelector: '.emp-row', searchInputSelector: '#empFilter', matchFn: createDataAttrMatcher(['name', 'email', 'department']), clearButtonSelector: '#clearBtn', emptyMessageSelector: '.no-results', debounceMs: 300 }); ``` ### TeamWheel (вращающийся виджет команды) **Импорт**: ```javascript import { initTeamWheel } from '{% static "js/components/teamWheel.js" %}'; ``` **Использование**: ```javascript initTeamWheel({ wheelId: 'teamWheel', columns: 4, speed: 30, pauseOnHover: true }); ``` ### CalendarWidget (календарь событий) **Импорт**: ```javascript import { initCalendarWidget } from '{% static "js/components/calendarWidget.js" %}'; ``` **Использование**: ```javascript window.calendarWidget = initCalendarWidget({ deskContainerId: 'calendarRight', mobContainerId: 'calendarRightMobile', apiEventsUrl: '/api/v1/calendar/events/', apiMyDeptsUrl: '/api/v1/departments/my-departments/', defaultColor: '#0d6efd' }); ``` **API виджета**: ```javascript // Обновить списки недели window.calendarWidget.updateWeekLists(); // Перезагрузить события window.calendarWidget.refetchEvents(); // Получить список отделов const departments = window.calendarWidget.getDepartments(); // Получить текущее состояние const state = window.calendarWidget.getState(); // { type: 'company'|'dept', deptId: number|null } ``` --- ## Отладка и тестирование ### Проверка подключения модулей ```javascript // В консоли браузера console.log(window.esc); // function esc(str) { ... } console.log(window.debounce); // function debounce(fn, ms) { ... } ``` ### Проверка работы компонентов ```javascript // ListFilter const filter = new ListFilter({ /* ... */ }); filter.filter('test'); // Применить фильтр filter.refresh(); // Обновить список элементов filter.clear(); // Очистить фильтр // CalendarWidget window.calendarWidget.refetchEvents(); // Перезагрузить события ``` ### Django тесты ```python from django.test import TestCase from django.template.loader import get_template class TemplateRenderingTests(TestCase): def test_employee_list_renders(self): template = get_template('employees/employees_list.html') self.assertIsNotNone(template) def test_no_inline_styles(self): """Проверка отсутствия встроенных стилей""" template = get_template('employees/employees_list.html') content = template.render({}) self.assertNotIn('