Corp/backend/docs/TEMPLATES_GUIDE.md
Игорь Надеин 263a7cbe90 Исправлена разметка и стили страницы профиля
- Заменен лишний div.container на стандартный container для единообразия
- Заменен класс .card на .ios-card в секции активности для устранения конфликтов стилей
- Добавлен card-list.css в base.html для корректного отображения карточек новостей
- Добавлены CSS правила для правильного поведения .row и колонок в grid-layout
- Исправлена проблема с выходом календаря за границы страницы
- Убраны видимые скроллбары у modal-body и modal-content для чистого UI
2025-11-29 13:52:21 +03:00

736 lines
22 KiB
Markdown
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.

# 📄 Руководство по шаблонам EUSRR
**Версия**: 1.0
**Дата**: 4 ноября 2025 г.
---
## 📚 Содержание
1. [Структура шаблонов](#структура-шаблонов)
2. [Базовый шаблон](#базовый-шаблон)
3. [Компоненты сотрудников](#компоненты-сотрудников)
4. [Компоненты календаря](#компоненты-календаря)
5. [Компоненты отделов](#компоненты-отделов)
6. [Best Practices](#best-practices)
---
## 🏗️ Структура шаблонов
### Общая организация
```
templates/
├── base.html # Базовый шаблон (631 строка)
├── includes/ # Общие компоненты
│ ├── navbar.html # Навигационная панель
│ ├── sidebar.html # Левая боковая панель
│ ├── rightbar_calendar.html # Правая панель календаря (17 строк)
│ └── components/ # Компоненты для includes
│ ├── calendar_desktop.html # Десктопная версия календаря
│ ├── calendar_mobile.html # Мобильная offcanvas версия
│ ├── calendar_modal_create.html # Модал создания события
│ ├── calendar_modal_details.html # Модал деталей события
│ └── calendar_scripts.html # JS инициализация календаря
├── employees/ # Шаблоны сотрудников
│ ├── employees_list.html # Список сотрудников
│ ├── employee_detail.html # Детали сотрудника (432 строки)
│ ├── _employee_edit.html # Форма редактирования (24 строки!)
│ ├── _department_controls.html # Управление отделом (275 строк)
│ └── components/ # Компоненты сотрудников
│ ├── employee_form_personal.html
│ ├── employee_form_contacts.html
│ ├── employee_form_position.html
│ ├── employee_form_employment.html
│ ├── employee_form_photo.html
│ ├── employee_form_actions.html
│ ├── employee_form_scripts.html
│ ├── employee_detail_scripts.html
│ ├── department_controls_scripts.html
│ └── department_scripts.html
├── departments/
│ ├── department_detail.html # Детали отдела (46 строк!)
│ └── components/
│ ├── department_header.html
│ ├── department_wheel.html
│ ├── department_members.html
│ └── department_sidebar.html
├── documents/
│ └── document_list.html # Список документов (705 строк)
├── search/
│ └── results.html # Результаты поиска (335 строк)
└── requests_app/
└── request_list_full.html # Список заявок (673 строки)
```
---
## 🎨 Базовый шаблон
### `base.html`
Основной шаблон приложения с трёхколоночной структурой.
#### Структура
```django
<!doctype html>
<html lang="ru">
<head>
<!-- Meta-теги -->
<title>{% block title %}HiRo{% endblock %}</title>
<!-- Bootstrap, Icons, FullCalendar -->
<link href="bootstrap@5.3.3" rel="stylesheet">
<link href="bootstrap-icons@1.11.3" rel="stylesheet">
<!-- CSS Components -->
<link rel="stylesheet" href="{% static 'css/variables.css' %}">
<link rel="stylesheet" href="{% static 'css/components/common.css' %}">
<link rel="stylesheet" href="{% static 'css/components/cards.css' %}">
<link rel="stylesheet" href="{% static 'css/components/ios-search.css' %}">
<link rel="stylesheet" href="{% static 'css/components/base-app.css' %}">
<link rel="stylesheet" href="{% static 'css/components/ios-components.css' %}">
{% block extra_css %}{% endblock %}
</head>
<body>
{% include "includes/navbar.html" %}
<div class="container-xxl">
<div class="app-layout with-sidebar with-rightbar">
{# Левая боковая панель #}
{% if user.is_authenticated %}
{% include "includes/sidebar.html" %}
{% endif %}
{# Основной контент #}
<main class="main">
{% block content %}{% endblock %}
</main>
{# Правая панель с календарём #}
{% if user.is_authenticated %}
{% include "includes/rightbar_calendar.html" %}
{% endif %}
</div>
</div>
<!-- JS Scripts -->
<script src="bootstrap@5.3.3/bootstrap.bundle.min.js" defer></script>
<script src="fullcalendar@6.1.8" defer></script>
<!-- Global JS Modules -->
<script type="module">
// Утилиты
import { esc, norm, escAttr, truncate, getInitials } from '...stringUtils.js';
import { debounce, throttle, delay } from '...timing.js';
import { smoothScrollTo, scrollToTop, isElementInViewport } from '...scroll.js';
// Глобальные компоненты
import { initNavbarHeight } from '...navbarHeight.js';
import { initLikeHandler } from '...likeHandler.js';
import { initTextareaAutogrow } from '...textareaAutogrow.js';
document.addEventListener('DOMContentLoaded', () => {
// Инициализация глобальных компонентов
const navbarHeight = initNavbarHeight();
const likeHandler = initLikeHandler();
const textareaAutogrow = initTextareaAutogrow();
// Экспорт в window для совместимости
if (navbarHeight) window.navbarHeight = navbarHeight;
if (likeHandler) window.likeHandler = likeHandler;
if (textareaAutogrow) window.textareaAutogrow = textareaAutogrow;
});
</script>
{% block extra_js %}{% endblock %}
</body>
</html>
```
#### Блоки для переопределения
- `{% block title %}` - заголовок страницы
- `{% block extra_css %}` - дополнительные стили
- `{% block content %}` - основной контент
- `{% block extra_js %}` - дополнительные скрипты
---
## 👥 Компоненты сотрудников
### Форма редактирования сотрудника
#### `_employee_edit.html` (24 строки)
Главный шаблон формы, объединяющий все компоненты.
```django
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container ios-page">
<form method="post" enctype="multipart/form-data" id="employeeForm">
{% csrf_token %}
<div class="row g-3">
{# Компоненты формы #}
{% include "employees/components/employee_form_personal.html" %}
{% include "employees/components/employee_form_contacts.html" %}
{% include "employees/components/employee_form_position.html" %}
{% include "employees/components/employee_form_employment.html" %}
{% include "employees/components/employee_form_photo.html" %}
{% include "employees/components/employee_form_actions.html" %}
</div>
</form>
</div>
{# JavaScript инициализация #}
{% include "employees/components/employee_form_scripts.html" %}
{% endblock %}
```
---
### Компоненты формы
#### `employee_form_personal.html`
Личные данные сотрудника.
**Поля:**
- Фамилия, Имя, Отчество
- Пол
- Дата рождения
**Особенности:**
- iOS-стайлинг аккордеона
- Валидация обязательных полей
- Автофокус на первое поле
---
#### `employee_form_contacts.html`
Контактная информация.
**Поля:**
- Email (корпоративная почта)
- Телефон (рабочий)
- Внутренний номер
**Особенности:**
- Форматирование телефона
- Валидация email
- Опциональные поля
---
#### `employee_form_position.html` (360 строк)
Управление должностью сотрудника.
**Разделы:**
1. Текущая должность (select/input)
2. Создание новой должности (collapse)
3. Редактирование должности (модал)
4. Назначение групп должностей
**JavaScript:**
- `positionManager.js` - CRUD должностей
- `groupPickers.js` - выбор групп
**Особенности:**
- Динамические модалы
- AJAX операции
- Валидация прав доступа
---
#### `employee_form_employment.html`
Информация о трудоустройстве.
**Поля:**
- Дата приёма на работу
- Статус (работает/уволен)
- Дата увольнения
**Особенности:**
- Datepicker интеграция
- Условное отображение (статус)
- Автозаполнение текущей даты
---
#### `employee_form_photo.html`
Загрузка фотографии.
**Поля:**
- Превью текущей фотографии
- File input для загрузки
- Кнопка удаления фото
**Особенности:**
- Предпросмотр перед загрузкой
- Валидация размера/формата
- Crop функционал (опционально)
---
#### `employee_form_actions.html`
Кнопки действий формы.
**Кнопки:**
- Сохранить изменения
- Отмена (возврат назад)
- Удалить сотрудника (для админов)
**Особенности:**
- Sticky позиционирование на мобильных
- Подтверждение удаления
- Отслеживание изменений
---
#### `employee_form_scripts.html` (22 строки)
JavaScript инициализация для формы.
```django
{% load static %}
<script type="module">
import { initEmployeeForm } from '{% static "js/components/employeeFormHandler.js" %}';
import { initPositionManager } from '{% static "js/components/positionManager.js" %}';
import { initPositionGroupPicker, initPositionEditGroupPicker } from '{% static "js/components/groupPickers.js" %}';
import { initEmployeeGroupsManager } from '{% static "js/components/employeeGroupsManager.js" %}';
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initAll);
} else {
initAll();
}
function initAll() {
const employeeForm = initEmployeeForm();
const positionMgr = initPositionManager();
const groupPicker = initPositionGroupPicker();
const editGroupPicker = initPositionEditGroupPicker();
const groupsMgr = initEmployeeGroupsManager();
// Экспорт для отладки
if (employeeForm) window.employeeForm = employeeForm;
if (positionMgr) window.positionManager = positionMgr;
if (groupPicker) window.groupPicker = groupPicker;
if (editGroupPicker) window.editGroupPicker = editGroupPicker;
if (groupsMgr) window.employeeGroupsManager = groupsMgr;
}
</script>
```
---
### Детали сотрудника
#### `employee_detail.html` (432 строки)
Страница просмотра профиля сотрудника.
**Разделы:**
1. Шапка с фото и основной информацией
2. Контактная информация
3. Должность и отделы
4. Навыки (skills)
5. История изменений
6. Форма редактирования (collapse)
**JavaScript:**
```django
{% include "employees/components/employee_detail_scripts.html" %}
```
#### `employee_detail_scripts.html` (47 строк)
```django
{% load static %}
<script type="module">
import { initSkillsManager } from '{% static "js/components/skillsManager.js" %}';
import { initDeptRoleEditor } from '{% static "js/components/deptRoleEditor.js" %}';
import { smoothScrollTo } from '{% static "js/utils/scroll.js" %}';
document.addEventListener('DOMContentLoaded', () => {
// 1. Менеджер навыков
const skillsManager = initSkillsManager({
blockId: 'skillsBlock',
formId: 'skillAddForm',
collapseId: 'skillsForm',
removeUrl: "{% url 'employees:skill_remove' emp.id %}"
});
// 2. Редакторы ролей в отделах
const deptRoleEditor = initDeptRoleEditor();
// 3. Smooth scroll к форме редактирования
const editWrap = document.getElementById('editFormWrap');
if (editWrap) {
const isMobile = () => window.matchMedia('(max-width: 767.98px)').matches;
editWrap.addEventListener('shown.bs.collapse', () => {
if (isMobile()) {
smoothScrollTo(editWrap, { offset: getHeaderOffset() });
}
});
}
// Экспорт в window
if (skillsManager) window.skillsManager = skillsManager;
if (deptRoleEditor) window.deptRoleEditor = deptRoleEditor;
});
</script>
```
---
## 📅 Компоненты календаря
### `rightbar_calendar.html` (17 строк)
Основной файл правой панели с календарём.
```django
{% load static %}
{# Правая колонка с календарём - рефакторированная версия #}
<link rel="stylesheet" href="{% static 'css/components/rightbar-calendar.css' %}">
{# Десктопная версия #}
{% include "includes/components/calendar_desktop.html" %}
{# Мобильная версия #}
{% include "includes/components/calendar_mobile.html" %}
{# Модальные окна #}
{% include "includes/components/calendar_modal_create.html" %}
{% include "includes/components/calendar_modal_details.html" %}
{# JavaScript инициализация #}
{% include "includes/components/calendar_scripts.html" %}
```
---
### `calendar_desktop.html` (40 строк)
Десктопная версия календаря в правой панели.
**Элементы:**
- Заголовок "События"
- Контейнер для FullCalendar
- Кнопка создания события
- Список предстоящих событий
**Классы:**
- `.rightbar-calendar` - основной контейнер
- `.calendar-widget` - виджет календаря
- `.event-list` - список событий
---
### `calendar_mobile.html` (23 строки)
Мобильная offcanvas версия календаря.
**Структура:**
```html
<div class="offcanvas offcanvas-end" id="calendarOffcanvas">
<div class="offcanvas-header">
<h5>События</h5>
<button type="button" class="btn-close"></button>
</div>
<div class="offcanvas-body">
<!-- Содержимое календаря -->
</div>
</div>
```
---
### `calendar_modal_create.html` (132 строки)
Модал для создания нового события.
**Поля формы:**
- Название события
- Описание
- Дата начала
- Дата окончания
- Цвет события
- Тип (личное/рабочее)
**Особенности:**
- Валидация дат
- Color picker
- AJAX отправка
---
### `calendar_modal_details.html` (33 строки)
Модал для просмотра деталей события.
**Отображаемая информация:**
- Название
- Описание
- Дата и время
- Участники
- Кнопки: Редактировать, Удалить
---
### `calendar_scripts.html` (11 строк)
Инициализация виджета календаря.
```django
{% load static %}
<script type="module">
import { initCalendarWidget } from '{% static "js/components/calendarWidget.js" %}';
const widget = initCalendarWidget();
if (widget) {
window.calendarWidget = widget;
}
</script>
```
---
## 🏢 Компоненты отделов
### `department_detail.html` (46 строк)
Страница деталей отдела.
```django
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container ios-page">
<div class="row g-3">
{# Заголовок отдела #}
{% include "departments/components/department_header.html" %}
{# Колесо команды #}
{% include "departments/components/department_wheel.html" %}
{# Список участников #}
{% include "departments/components/department_members.html" %}
{# Боковая панель с настройками #}
{% include "departments/components/department_sidebar.html" %}
</div>
</div>
{# JavaScript инициализация #}
{% include "employees/components/department_scripts.html" %}
{% endblock %}
```
---
### `_department_controls.html` (275 строк)
Управление отделом (настройки, роли, права).
**Разделы:**
1. Выбор руководителя отдела (headPicker)
2. Управление ролями отдела (roleManager)
3. Создание/редактирование ролей
4. Назначение прав (permissions)
**JavaScript:**
```django
{% include "employees/components/department_controls_scripts.html" %}
```
#### `department_controls_scripts.html` (19 строк)
```django
{% load static %}
<script type="module">
import { initHeadPicker } from '{% static "js/components/headPicker.js" %}';
import { initRoleManager } from '{% static "js/components/roleManager.js" %}';
document.addEventListener('DOMContentLoaded', () => {
const headPicker = initHeadPicker();
const roleManager = initRoleManager();
if (headPicker) window.headPicker = headPicker;
if (roleManager) window.roleManager = roleManager;
});
</script>
```
---
## ✅ Best Practices
### 1. Структура компонентов
```
✅ ХОРОШО: Малый размер, одна ответственность
employees/components/
├── employee_form_personal.html # ~80 строк
├── employee_form_contacts.html # ~60 строк
└── employee_form_scripts.html # ~22 строки
❌ ПЛОХО: Монолитный файл
employees/
└── employee_edit.html # 1391 строка!
```
### 2. Именование файлов
```
✅ ХОРОШО: Префиксы, понятные имена
calendar_modal_create.html
employee_form_personal.html
department_controls_scripts.html
❌ ПЛОХО: Неясные имена
modal.html
form.html
scripts.html
```
### 3. Подключение компонентов
```django
✅ ХОРОШО: Явное включение с комментариями
{# Личные данные #}
{% include "employees/components/employee_form_personal.html" %}
{# Контакты #}
{% include "employees/components/employee_form_contacts.html" %}
❌ ПЛОХО: Неясный порядок без комментариев
{% include "comp1.html" %}
{% include "comp2.html" %}
```
### 4. Разделение JavaScript
```django
✅ ХОРОШО: Отдельный файл инициализации
{% include "employees/components/employee_form_scripts.html" %}
❌ ПЛОХО: Встроенный <script> в HTML
<script>
// 500 строк JavaScript прямо в шаблоне
</script>
```
### 5. Передача контекста
```django
✅ ХОРОШО: Явная передача параметров
{% include "component.html" with employee=emp can_edit=True %}
❌ ПЛОХО: Неявный контекст
{% include "component.html" %}
{# Компонент зависит от глобальных переменных #}
```
### 6. Блоки и наследование
```django
✅ ХОРОШО: Расширение базового шаблона
{% extends "base.html" %}
{% block content %}
<!-- Ваш контент -->
{% endblock %}
❌ ПЛОХО: Дублирование структуры
<!DOCTYPE html>
<html>
<!-- Полная структура заново -->
</html>
```
### 7. CSS подключение
```django
✅ ХОРОШО: Модульные стили в extra_css
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/components/custom.css' %}">
{% endblock %}
❌ ПЛОХО: Inline стили
<div style="color: red; font-size: 14px;">...</div>
```
### 8. Условное включение
```django
✅ ХОРОШО: Проверка прав перед включением
{% if user.is_authenticated %}
{% include "includes/rightbar_calendar.html" %}
{% endif %}
❌ ПЛОХО: Проверка внутри компонента
{# В компоненте: #}
{% if user.is_authenticated %}
<!-- Весь компонент -->
{% endif %}
```
---
## 🔧 Отладка шаблонов
### Вывод отладочной информации
```django
{# Проверка контекста #}
{% if debug %}
<pre>{{ employee|pprint }}</pre>
{% endif %}
{# Отладка переменных #}
{{ variable|default:"NOT SET" }}
```
### Комментарии для разработчиков
```django
{# === СЕКЦИЯ: Личные данные === #}
{# TODO: Добавить валидацию email #}
{# FIXME: Баг с датой рождения #}
{# NOTE: Этот компонент используется в 3 местах #}
```
---
## 📞 Поддержка
При создании новых шаблонов:
1. Следуйте структуре существующих компонентов
2. Используйте префиксы в именах файлов
3. Документируйте сложную логику
4. Разделяйте HTML и JavaScript
5. Тестируйте на разных экранах
---
**Дата актуализации**: 4 ноября 2025 г.
**Версия**: 1.0