From ef1383b0ad1b6daca7cb48e342a888e23f4dcc03 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, 28 Oct 2025 11:00:54 +0300 Subject: [PATCH] docs: Create comprehensive templates guide - Complete architecture documentation - JavaScript utilities and components usage - CSS components structure - Best practices and naming conventions - Migration history and metrics - Refactoring Phase 6 documentation --- backend/templates/TEMPLATES_GUIDE.md | 511 +++++++++++++++++++++++++++ 1 file changed, 511 insertions(+) create mode 100644 backend/templates/TEMPLATES_GUIDE.md diff --git a/backend/templates/TEMPLATES_GUIDE.md b/backend/templates/TEMPLATES_GUIDE.md new file mode 100644 index 00000000..d6d916ad --- /dev/null +++ b/backend/templates/TEMPLATES_GUIDE.md @@ -0,0 +1,511 @@ +# Руководство по шаблонам 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` - Детали сотрудника +- `feed-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('