From 4d04aafdb92df93650cda1659918db848ea99443 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 08:54:38 +0300 Subject: [PATCH] refactor: Replace inline CSS with components (Phase 3) - Replaced feed-* inline styles in base.html with feed-cards.css component (~60 lines) - Replaced ios-search inline styles in 3 templates with ios-search.css component * employees/employees_list.html * search/results.html * employees/department_list.html - Eliminated ~150 lines of duplicate CSS across templates - All 17 tests passing --- backend/README_EUSRR.md | 16 +- backend/analyze_templates.py | 151 ++ backend/api/v1/employees/views.py | 94 +- backend/check_templates.py | 170 ++ backend/diagnose_page.py | 133 ++ backend/diagnostic_summary.py | 55 + backend/employees/ldap/README.md | 241 +++ backend/employees/ldap/__init__.py | 106 + backend/employees/ldap/connections.py | 54 - backend/employees/ldap/directory_service.py | 1847 +---------------- backend/employees/ldap/domain/__init__.py | 18 + backend/employees/ldap/domain/dtos.py | 154 ++ backend/employees/ldap/dto.py | 91 - backend/employees/ldap/helpers.py | 152 -- .../employees/ldap/infrastructure/__init__.py | 12 + .../ldap/infrastructure/connections.py | 87 + backend/employees/ldap/repo_db.py | 204 -- .../employees/ldap/repositories/__init__.py | 29 + .../ldap/repositories/employee_repository.py | 293 +++ .../ldap/repositories/ldap_repository.py | 277 +++ .../repositories/sync_state_repository.py | 160 ++ backend/employees/ldap/services/__init__.py | 16 + .../ldap/services/department_service.py | 1052 ++++++++++ .../employees/ldap/services/group_service.py | 400 ++++ .../ldap/services/position_service.py | 389 ++++ .../employees/ldap/services/sync_service.py | 485 +++++ .../employees/ldap/services/user_service.py | 645 ++++++ backend/employees/ldap/sync_service.py | 660 ++---- backend/employees/ldap/tests/README.md | 264 +++ backend/employees/ldap/tests/__init__.py | 3 + backend/employees/ldap/tests/conftest.py | 295 +++ .../employees/ldap/tests/fixtures/__init__.py | 3 + .../ldap/tests/fixtures/sample_data.py | 309 +++ .../ldap/tests/integration/__init__.py | 3 + .../tests/integration/test_integration.py | 428 ++++ .../ldap/tests/test_ldap_connection.py | 31 + backend/employees/ldap/tests/unit/__init__.py | 3 + .../tests/unit/test_department_service.py | 483 +++++ .../ldap/tests/unit/test_group_service.py | 332 +++ .../ldap/tests/unit/test_position_service.py | 355 ++++ .../ldap/tests/unit/test_user_service.py | 461 ++++ backend/employees/ldap/utils/__init__.py | 72 + .../ldap/{dn.py => utils/dn_utils.py} | 192 +- .../ldap/{groups.py => utils/group_utils.py} | 4 +- backend/employees/ldap/utils/image_utils.py | 73 + .../ldap/{utils.py => utils/ldap_utils.py} | 413 ++-- backend/employees/ldap/utils/text_utils.py | 126 ++ ...t_uniq_employee_per_department_and_more.py | 26 + ...t_uniq_department_per_employee_and_more.py | 22 + backend/employees/models.py | 123 +- backend/eusrr_backend/auth_backends.py | 2 +- backend/eusrr_backend/ууу.js | 400 ---- backend/render_test_page.py | 56 + backend/requests_app/views_front.py | 2 +- backend/templates/auth/logout_confirm.html | 33 - .../templates/auth/password_reset_form.html | 7 - backend/templates/base.html | 57 +- .../templates/employees/_employee_edit.html | 1380 +----------- .../employees/_employee_edit.html.backup | 1391 +++++++++++++ .../components/department_header.html | 64 + .../employees/components/department_info.html | 40 + .../components/department_modals.html | 197 ++ .../components/department_scripts.html | 513 +++++ .../components/department_sidebar.html | 48 + .../components/department_styles.html | 58 + .../components/department_team_circle.html | 76 + .../components/employee_form_actions.html | 10 + .../components/employee_form_contact.html | 52 + .../components/employee_form_personal.html | 50 + .../components/employee_form_position.html | 359 ++++ .../components/employee_form_scripts.html | 907 ++++++++ .../employees/department_detail.html | 1017 +-------- .../employees/department_detail.html.backup | 1025 +++++++++ .../templates/employees/department_list.html | 10 +- .../templates/employees/employees_list.html | 27 +- .../includes/calendar/calendar_styles.html | 133 ++ .../includes/rightbar_calendar.html.backup | 1130 ++++++++++ .../request_list_full.html} | 2 +- backend/templates/search/results.html | 27 +- backend/templates/test_dropdown.html | 47 + backend/tests/test_template_components.py | 164 ++ 81 files changed, 15200 insertions(+), 6096 deletions(-) create mode 100644 backend/analyze_templates.py create mode 100644 backend/check_templates.py create mode 100644 backend/diagnose_page.py create mode 100644 backend/diagnostic_summary.py create mode 100644 backend/employees/ldap/README.md delete mode 100644 backend/employees/ldap/connections.py create mode 100644 backend/employees/ldap/domain/__init__.py create mode 100644 backend/employees/ldap/domain/dtos.py delete mode 100644 backend/employees/ldap/dto.py delete mode 100644 backend/employees/ldap/helpers.py create mode 100644 backend/employees/ldap/infrastructure/__init__.py create mode 100644 backend/employees/ldap/infrastructure/connections.py delete mode 100644 backend/employees/ldap/repo_db.py create mode 100644 backend/employees/ldap/repositories/__init__.py create mode 100644 backend/employees/ldap/repositories/employee_repository.py create mode 100644 backend/employees/ldap/repositories/ldap_repository.py create mode 100644 backend/employees/ldap/repositories/sync_state_repository.py create mode 100644 backend/employees/ldap/services/__init__.py create mode 100644 backend/employees/ldap/services/department_service.py create mode 100644 backend/employees/ldap/services/group_service.py create mode 100644 backend/employees/ldap/services/position_service.py create mode 100644 backend/employees/ldap/services/sync_service.py create mode 100644 backend/employees/ldap/services/user_service.py create mode 100644 backend/employees/ldap/tests/README.md create mode 100644 backend/employees/ldap/tests/__init__.py create mode 100644 backend/employees/ldap/tests/conftest.py create mode 100644 backend/employees/ldap/tests/fixtures/__init__.py create mode 100644 backend/employees/ldap/tests/fixtures/sample_data.py create mode 100644 backend/employees/ldap/tests/integration/__init__.py create mode 100644 backend/employees/ldap/tests/integration/test_integration.py create mode 100644 backend/employees/ldap/tests/test_ldap_connection.py create mode 100644 backend/employees/ldap/tests/unit/__init__.py create mode 100644 backend/employees/ldap/tests/unit/test_department_service.py create mode 100644 backend/employees/ldap/tests/unit/test_group_service.py create mode 100644 backend/employees/ldap/tests/unit/test_position_service.py create mode 100644 backend/employees/ldap/tests/unit/test_user_service.py create mode 100644 backend/employees/ldap/utils/__init__.py rename backend/employees/ldap/{dn.py => utils/dn_utils.py} (50%) rename backend/employees/ldap/{groups.py => utils/group_utils.py} (98%) create mode 100644 backend/employees/ldap/utils/image_utils.py rename backend/employees/ldap/{utils.py => utils/ldap_utils.py} (55%) create mode 100644 backend/employees/ldap/utils/text_utils.py create mode 100644 backend/employees/migrations/0032_remove_employeedepartment_uniq_employee_per_department_and_more.py create mode 100644 backend/employees/migrations/0033_remove_employeedepartment_uniq_department_per_employee_and_more.py delete mode 100644 backend/eusrr_backend/ууу.js create mode 100644 backend/render_test_page.py delete mode 100644 backend/templates/auth/logout_confirm.html delete mode 100644 backend/templates/auth/password_reset_form.html create mode 100644 backend/templates/employees/_employee_edit.html.backup create mode 100644 backend/templates/employees/components/department_header.html create mode 100644 backend/templates/employees/components/department_info.html create mode 100644 backend/templates/employees/components/department_modals.html create mode 100644 backend/templates/employees/components/department_scripts.html create mode 100644 backend/templates/employees/components/department_sidebar.html create mode 100644 backend/templates/employees/components/department_styles.html create mode 100644 backend/templates/employees/components/department_team_circle.html create mode 100644 backend/templates/employees/components/employee_form_actions.html create mode 100644 backend/templates/employees/components/employee_form_contact.html create mode 100644 backend/templates/employees/components/employee_form_personal.html create mode 100644 backend/templates/employees/components/employee_form_position.html create mode 100644 backend/templates/employees/components/employee_form_scripts.html create mode 100644 backend/templates/employees/department_detail.html.backup create mode 100644 backend/templates/includes/calendar/calendar_styles.html create mode 100644 backend/templates/includes/rightbar_calendar.html.backup rename backend/templates/{requests/request_list.html => requests_app/request_list_full.html} (99%) create mode 100644 backend/templates/test_dropdown.html create mode 100644 backend/tests/test_template_components.py diff --git a/backend/README_EUSRR.md b/backend/README_EUSRR.md index c0fea691..2f7d7e48 100644 --- a/backend/README_EUSRR.md +++ b/backend/README_EUSRR.md @@ -10,25 +10,25 @@ python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -r backend/requirements.txt cp backend/.env backend/.env.local || true -# edit backend/.env.local if needed (USE_SQLITE=True by default) +# Edit backend/.env.local if needed (USE_SQLITE=True by default) python backend/manage.py migrate python backend/manage.py createsuperuser python backend/manage.py runserver 0.0.0.0:9000 ``` -Open http://127.0.0.1:9000/ +Open [http://127.0.0.1:9000/](http://127.0.0.1:9000/) ## Full stack with Docker (Postgres + Redis) 1. Put your secrets into `backend/.env` (use `.env.example` as a template). **Never commit real secrets.** -2. Fix Dockerfile to use `eusrr_backend.asgi` (or use the provided `Dockerfile.fixed`). +2. Ensure the `Dockerfile` uses `eusrr_backend.asgi` (or use the provided `Dockerfile.fixed`). 3. Launch: ```bash docker compose up --build ``` -The app will be available on http://127.0.0.1:9000/ +The app will be available at [http://127.0.0.1:9000/](http://127.0.0.1:9000/) ## Useful commands ```bash -# Telegram bot +# Start Telegram bot python backend/manage.py start_telegram_bot # Run tests @@ -36,6 +36,6 @@ pytest -q backend ``` ## Notes -- Set `API_BASE_URL` to match where the app is served (default http://127.0.0.1:9000/api). -- For Channels websockets, keep Redis reachable at `${REDIS_HOST}:${REDIS_PORT}`. -- The `hikcentral` DB is configured as a secondary Postgres. Externalize its DSN via envs if you plan to use it. +- Set `API_BASE_URL` to match where the app is served (default: [http://127.0.0.1:9000/api](http://127.0.0.1:9000/api)). +- For Channels websockets, ensure Redis is reachable at `${REDIS_HOST}:${REDIS_PORT}`. +- The `hikcentral` DB is configured as a secondary Postgres. Externalize its DSN via environment variables if you plan to use it. diff --git a/backend/analyze_templates.py b/backend/analyze_templates.py new file mode 100644 index 00000000..51d4d03d --- /dev/null +++ b/backend/analyze_templates.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +""" +Анализ шаблонов: поиск встроенных стилей и скриптов. +Помогает понять масштаб рефакторинга. +""" +import os +import re +from pathlib import Path +from collections import defaultdict + +# Путь к шаблонам +TEMPLATES_DIR = Path(__file__).parent / 'templates' + + +def analyze_template(filepath): + """Анализирует один шаблон""" + try: + content = filepath.read_text(encoding='utf-8') + except Exception as e: + return None + + lines = len(content.splitlines()) + + # Подсчёт тегов + style_tags = len(re.findall(r']*>', content)) + script_tags = len(re.findall(r']*>', content)) + + # Размер встроенных стилей и скриптов + style_blocks = re.findall(r']*>(.*?)', content, re.DOTALL) + script_blocks = re.findall(r']*>(.*?)', content, re.DOTALL) + + style_lines = sum(len(block.splitlines()) for block in style_blocks) + script_lines = sum(len(block.splitlines()) for block in script_blocks) + + # Поиск CSS классов + css_classes = set(re.findall(r'\.([a-z][a-z0-9\-_]*)\s*{', content, re.IGNORECASE)) + + # Поиск функций JavaScript + js_functions = set(re.findall(r'function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(', content)) + + return { + 'path': str(filepath.relative_to(TEMPLATES_DIR)), + 'total_lines': lines, + 'style_tags': style_tags, + 'script_tags': script_tags, + 'style_lines': style_lines, + 'script_lines': script_lines, + 'css_classes': css_classes, + 'js_functions': js_functions, + 'has_blocks': bool(re.search(r'{%\s*block\s+extra_(css|js)\s*%}', content)), + } + + +def main(): + """Главная функция анализа""" + print("=" * 80) + print("АНАЛИЗ ШАБЛОНОВ: встроенные стили и скрипты") + print("=" * 80) + + # Находим все HTML файлы + html_files = list(TEMPLATES_DIR.rglob('*.html')) + + results = [] + for filepath in sorted(html_files): + # Пропускаем backup файлы + if '.backup' in str(filepath): + continue + + result = analyze_template(filepath) + if result and (result['style_tags'] > 0 or result['script_tags'] > 0): + results.append(result) + + # Сортируем по количеству встроенного кода + results.sort(key=lambda x: x['style_lines'] + x['script_lines'], reverse=True) + + print(f"\nНайдено {len(results)} шаблонов со встроенными стилями/скриптами\n") + + # Таблица результатов + print(f"{'Файл':<60} {'Строк':<8} {' + +
+ {% csrf_token %} + + {# === ЛИЧНЫЕ ДАННЫЕ === #} +
+
+
+ +
Личные данные
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+ + {# === КОНТАКТЫ === #} +
+
+
+ +
Контакты
+
+
+
+
+ + +
+
+
+ + +
+
+ +
+ + +
+
+
+ + +
+
+
+ + +
+
+
+
+
+
+ +{# === ДОЛЖНОСТЬ === #} +
+ {% if can_edit_position or request.user.is_staff or request.user.is_superuser %} +
+
+ +
Должность
+
+ +
+
+ + {# ТЕКУЩАЯ ДОЛЖНОСТЬ #} +
+ + {% if all_positions %} + + {% else %} + +
Передайте в контекст all_positions для выпадающего списка.
+ {% endif %} +
+ + {# СОЗДАТЬ НОВУЮ #} + {% if can_create_position or request.user.is_staff or request.user.is_superuser %} +
+ + +
+ +
+
+
+
+ + +
+
+ + +
+ + {% if can_assign_position_groups or request.user.is_staff or request.user.is_superuser %} +
+ {% if all_groups %} +
+
+ +
+ + {# Модал выбора групп для новой должности #} + + {% else %} +
+ + +
+ {% endif %} +
+ {% endif %} + +
+ + + +
+ +
+
+
+
+
+
+ {% endif %} + + {# РЕДАКТИРОВАТЬ ВЫБРАННУЮ #} +
+
+ +
+ +
+
+
+ + +
+
+ + +
+ + {% if can_assign_position_groups or request.user.is_staff or request.user.is_superuser %} +
+ +
+ +
+ + +
+
+ + {# Модал выбора групп для редактирования должности #} + + {% endif %} + +
+ + + +
+ +
+
+
+
+
+
+ +
{# /.row #} +
{# /.ios-acc-body #} +
+ {% else %} +
+
+ +
Должность
+
+
+ +
+
+ {% endif %} +
+ + +{# === ПЕРСОНАЛЬНЫЕ ГРУППЫ ПОЛНОМОЧИЙ === #} +
+ {% if request.user.is_staff or request.user.is_superuser %} +
+
+ +
Дополнительные полномочия
+
+
+
+ +
+
+
+
+ + {# <<< НОВОЕ: вывод уже назначенных групп сотруднику в виде «пилюль» >>> #} +
+ +
+ {% for g in emp_personal_groups %} + {% if g.id and g.name %} + {{ g.name }} + {% endif %} + {% empty %} + Нет персональных групп + {% endfor %} +
+
+ +
+
+ + + +
+ +
+
+ +
+ +
+ +
+ + +
+
+
+ +
+ +
+ + +
+
Удаляется и LDAP-группа, и объект в БД.
+
+
+
+
+
+ + {# Модал выбора групп для персонального назначения #} + + {% endif %} +
+ + + + {# === КНОПКИ === #} +
+ + +
+
+ + + + + + + + + + + diff --git a/backend/templates/employees/components/department_header.html b/backend/templates/employees/components/department_header.html new file mode 100644 index 00000000..6f86c873 --- /dev/null +++ b/backend/templates/employees/components/department_header.html @@ -0,0 +1,64 @@ + {# ===== Заголовок ===== #} +
+ +

{{ dept.name }}

+ {% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} +
+ + {# Добавить участника #} + + + {# Назначить роль #} + {% if roles %} + {% if user.is_superuser or user.is_staff or can_assign_roles or dept.head.id == user.id %} + + {% endif %} + {% endif %} + + {# Удалить участника #} + + +
+ {% endif %} + +
+ {% url 'feed:post_create' as post_create_url %} + {% if user.is_authenticated and user.id in members_exclude_ids %} + + Создать публикацию + + {% endif %} + + +
+
+ + {# ===== Две колонки как в employee_detail: слева основное, справа сайдбар ===== #} +
+ + {# ===== ЛЕВАЯ КОЛОНКА (основная, как в профиле): команда + инфо ===== #} diff --git a/backend/templates/employees/components/department_info.html b/backend/templates/employees/components/department_info.html new file mode 100644 index 00000000..3cfcb27b --- /dev/null +++ b/backend/templates/employees/components/department_info.html @@ -0,0 +1,40 @@ + {# --- Инфо-блок отдела (под командой) --- #} + {% if dept.description or dept.head %} +
+
+ {% if dept.description %} +
+ {{ dept.description }} +
+ {% endif %} + + {% if dept.head %} +
+ Руководитель: + {% url 'employees:employee_detail' dept.head.id as head_url %} + {% if head_url %} + + {{ dept.head.last_name }}{% if dept.head.first_name %} {{ dept.head.first_name }}{% endif %} + + {% else %} + {{ dept.head.last_name }}{% if dept.head.first_name %} {{ dept.head.first_name }}{% endif %} + {% endif %} +
+ {% endif %} + + {% if user.is_staff or dept.head.id == user.id or can_manage or can_assign_roles or can_change_head %} + + {% endif %} +
+
+ {% endif %} diff --git a/backend/templates/employees/components/department_modals.html b/backend/templates/employees/components/department_modals.html new file mode 100644 index 00000000..38ddfeff --- /dev/null +++ b/backend/templates/employees/components/department_modals.html @@ -0,0 +1,197 @@ +{# ---------- МОДАЛ «Удалить участника» ---------- #} +{% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} + +{% endif %} + +{# ---------- МОДАЛ «Добавить участника» ---------- #} +{% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} + {% with exclude_ids=members_exclude_ids %} + + {% endwith %} +{% endif %} + +{# ---------- МОДАЛ «Назначить роль» ---------- #} +{% if roles %} + {% if user.is_superuser or user.is_staff or can_assign_roles or dept.head.id == user.id %} + + {% endif %} +{% endif %} + diff --git a/backend/templates/employees/components/department_scripts.html b/backend/templates/employees/components/department_scripts.html new file mode 100644 index 00000000..4f6d9ad2 --- /dev/null +++ b/backend/templates/employees/components/department_scripts.html @@ -0,0 +1,513 @@ +{# ==== JS: круг с автоскроллом/ручным управлением и индикатором ==== #} + + +{# ===== Переключатель панели настроек (скролл к блоку при открытии) ===== #} + + +{# ===== Модал «Добавить участника»: AJAX-поиск ===== #} + + +{# ===== Модал «Удалить участника»: локальный выбор ===== #} + + +{# ===== Модал «Назначить роль»: подстановка empId в action ===== #} + + diff --git a/backend/templates/employees/components/department_sidebar.html b/backend/templates/employees/components/department_sidebar.html new file mode 100644 index 00000000..d8b2530e --- /dev/null +++ b/backend/templates/employees/components/department_sidebar.html @@ -0,0 +1,48 @@ +{# ===== ПРАВАЯ КОЛОНКА: настройки + лента ===== #} +{% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} +
+
+
+
+ +
Настройки отдела
+
+ +
+
+ +
+ {% include "employees/_department_controls.html" with dept=dept edit_form=edit_form head_choices=head_choices %} +
+
+
+
+{% endif %} + +
+
+ +
+
Публикации отдела
+
+
+
+ {% if posts %} + {% include "feed/_feed_cards.html" with items=posts %} + {% else %} +
Публикаций пока нет.
+ {% endif %} +
+
+ +{# Простая пагинация (если нужно) #} +{% if page|default:0 %} +
+ {% if page|add:"-1" %} + ← Назад + {% endif %} +
+{% endif %} diff --git a/backend/templates/employees/components/department_styles.html b/backend/templates/employees/components/department_styles.html new file mode 100644 index 00000000..10615611 --- /dev/null +++ b/backend/templates/employees/components/department_styles.html @@ -0,0 +1,58 @@ +{# ==== СТИЛИ (можно вынести в base.html, чтобы не дублировать) ==== #} + + diff --git a/backend/templates/employees/components/department_team_circle.html b/backend/templates/employees/components/department_team_circle.html new file mode 100644 index 00000000..77e82d43 --- /dev/null +++ b/backend/templates/employees/components/department_team_circle.html @@ -0,0 +1,76 @@ + {# --- Команда отдела (круг) --- #} +
+
+ + +
+
{% url 'employees:employees_list' as employees_list_url %} + {% if employees_list_url %} + + Команда отдела → + + {% endif %}
+
+
+ +
+
+ + {# КРУГ #} +
+
+
+
+
+
+
+
+
+ + {# Слой фокуса #} +
+
+ + +
+
+ + {# ПОЛУКРУЖНЫЙ ИНДИКАТОР СКРОЛЛА (справа по дуге) #} + +
+ + {# Источник данных для круга #} +
+ {% for li in links %} + {% with emp=li.employee %} +
+ {% endwith %} + {% empty %} + {% endfor %} +
+ + {# Текст под кругом #} +
+
+
+
+
+
+
+ +
+
+
diff --git a/backend/templates/employees/components/employee_form_actions.html b/backend/templates/employees/components/employee_form_actions.html new file mode 100644 index 00000000..912eea65 --- /dev/null +++ b/backend/templates/employees/components/employee_form_actions.html @@ -0,0 +1,10 @@ +{# employees/components/employee_form_actions.html #} +{# Кнопки сохранения и отмены формы #} + +
+ + +
diff --git a/backend/templates/employees/components/employee_form_contact.html b/backend/templates/employees/components/employee_form_contact.html new file mode 100644 index 00000000..fb27c979 --- /dev/null +++ b/backend/templates/employees/components/employee_form_contact.html @@ -0,0 +1,52 @@ +{# employees/components/employee_form_contact.html #} +{# Секция "Контакты": Email, телефон, мессенджеры #} + +
+
+
+ +
Контакты
+
+
+
+
+ + +
+
+
+ + +
+
+ +
+ + +
+
+
+ + +
+
+
+ + +
+
+
+
+
+
diff --git a/backend/templates/employees/components/employee_form_personal.html b/backend/templates/employees/components/employee_form_personal.html new file mode 100644 index 00000000..e4b25007 --- /dev/null +++ b/backend/templates/employees/components/employee_form_personal.html @@ -0,0 +1,50 @@ +{# employees/components/employee_form_personal.html #} +{# Секция "Личные данные": ФИО, пол, дата рождения #} + +
+
+
+ +
Личные данные
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
diff --git a/backend/templates/employees/components/employee_form_position.html b/backend/templates/employees/components/employee_form_position.html new file mode 100644 index 00000000..be04f442 --- /dev/null +++ b/backend/templates/employees/components/employee_form_position.html @@ -0,0 +1,359 @@ +{# === ДОЛЖНОСТЬ === #} +
+ {% if can_edit_position or request.user.is_staff or request.user.is_superuser %} +
+
+ +
Должность
+
+ +
+
+ + {# ТЕКУЩАЯ ДОЛЖНОСТЬ #} +
+ + {% if all_positions %} + + {% else %} + +
Передайте в контекст all_positions для выпадающего списка.
+ {% endif %} +
+ + {# СОЗДАТЬ НОВУЮ #} + {% if can_create_position or request.user.is_staff or request.user.is_superuser %} +
+ + +
+ +
+
+
+
+ + +
+
+ + +
+ + {% if can_assign_position_groups or request.user.is_staff or request.user.is_superuser %} +
+ {% if all_groups %} +
+
+ +
+ + {# Модал выбора групп для новой должности #} + + {% else %} +
+ + +
+ {% endif %} +
+ {% endif %} + +
+ + + +
+ +
+
+
+
+
+
+ {% endif %} + + {# РЕДАКТИРОВАТЬ ВЫБРАННУЮ #} +
+
+ +
+ +
+
+
+ + +
+
+ + +
+ + {% if can_assign_position_groups or request.user.is_staff or request.user.is_superuser %} +
+ +
+ +
+ + +
+
+ + {# Модал выбора групп для редактирования должности #} + + {% endif %} + +
+ + + +
+ +
+
+
+
+
+
+ +
{# /.row #} +
{# /.ios-acc-body #} +
+ {% else %} +
+
+ +
Должность
+
+
+ +
+
+ {% endif %} +
+ + +{# === ПЕРСОНАЛЬНЫЕ ГРУППЫ ПОЛНОМОЧИЙ === #} +
+ {% if request.user.is_staff or request.user.is_superuser %} +
+
+ +
Дополнительные полномочия
+
+
+
+ +
+
+
+
+ + {# <<< НОВОЕ: вывод уже назначенных групп сотруднику в виде «пилюль» >>> #} +
+ +
+ {% for g in emp_personal_groups %} + {% if g.id and g.name %} + {{ g.name }} + {% endif %} + {% empty %} + Нет персональных групп + {% endfor %} +
+
+ +
+
+ + + +
+ +
+
+ +
+ +
+ +
+ + +
+
+
+ +
+ +
+ + +
+
Удаляется и LDAP-группа, и объект в БД.
+
+
+
+
+
+ + {# Модал выбора групп для персонального назначения #} + + {% endif %} +
+ + + diff --git a/backend/templates/employees/components/employee_form_scripts.html b/backend/templates/employees/components/employee_form_scripts.html new file mode 100644 index 00000000..ceaf9a33 --- /dev/null +++ b/backend/templates/employees/components/employee_form_scripts.html @@ -0,0 +1,907 @@ + + + + + + + + diff --git a/backend/templates/employees/department_detail.html b/backend/templates/employees/department_detail.html index 64be2a33..cebdd602 100644 --- a/backend/templates/employees/department_detail.html +++ b/backend/templates/employees/department_detail.html @@ -1,4 +1,5 @@ {# backend/templates/employees/department_detail.html #} +{# Детальная страница отдела - рефакторированная версия #} {% extends 'base.html' %} {% load static django_bootstrap5 %} @@ -8,1018 +9,38 @@ {% with dept_id=dept.id %}
- {# ===== Заголовок ===== #} -
- -

{{ dept.name }}

- {% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} -
+ {# ===== ЗАГОЛОВОК ===== #} + {% include "employees/components/department_header.html" %} - {# Добавить участника #} - - - {# Назначить роль #} - {% if roles %} - {% if user.is_superuser or user.is_staff or can_assign_roles or dept.head.id == user.id %} - - {% endif %} - {% endif %} - - {# Удалить участника #} - - -
- {% endif %} - -
- {% url 'feed:post_create' as post_create_url %} - {% if user.is_authenticated and user.id in members_exclude_ids %} - - Создать публикацию - - {% endif %} - - -
-
- - {# ===== Две колонки как в employee_detail: слева основное, справа сайдбар ===== #} + {# ===== ДВЕ КОЛОНКИ ===== #}
- {# ===== ЛЕВАЯ КОЛОНКА (основная, как в профиле): команда + инфо ===== #} + {# ===== ЛЕВАЯ КОЛОНКА: команда + инфо ===== #}
- {# --- Команда отдела (круг) --- #} -
-
- - -
-
{% url 'employees:employees_list' as employees_list_url %} - {% if employees_list_url %} - - Команда отдела → - - {% endif %}
-
-
- -
-
- - {# КРУГ #} -
-
-
-
-
-
-
-
-
- - {# Слой фокуса #} -
-
- - -
-
- - {# ПОЛУКРУЖНЫЙ ИНДИКАТОР СКРОЛЛА (справа по дуге) #} - -
- - {# Источник данных для круга #} -
- {% for li in links %} - {% with emp=li.employee %} -
- {% endwith %} - {% empty %} - {% endfor %} -
- - {# Текст под кругом #} -
-
-
-
-
-
-
- -
-
-
- - {# --- Инфо-блок отдела (под командой) --- #} - {% if dept.description or dept.head %} -
-
- {% if dept.description %} -
- {{ dept.description }} -
- {% endif %} - - {% if dept.head %} -
- Руководитель: - {% url 'employees:employee_detail' dept.head.id as head_url %} - {% if head_url %} - - {{ dept.head.last_name }}{% if dept.head.first_name %} {{ dept.head.first_name }}{% endif %} - - {% else %} - {{ dept.head.last_name }}{% if dept.head.first_name %} {{ dept.head.first_name }}{% endif %} - {% endif %} -
- {% endif %} - - {% if user.is_staff or dept.head.id == user.id or can_manage or can_assign_roles or can_change_head %} - - {% endif %} -
-
- {% endif %} - + {% include "employees/components/department_team_circle.html" %} + {% include "employees/components/department_info.html" %}
- {# ===== ПРАВАЯ КОЛОНКА (сайдбар): публикации/активность ===== #} + {# ===== ПРАВАЯ КОЛОНКА: настройки + лента ===== #}
- {% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} -
-
-
-
- -
Настройки отдела
-
- -
-
- -
- {% include "employees/_department_controls.html" with dept=dept edit_form=edit_form head_choices=head_choices %} -
-
-
-
- {% endif %} -
-
- -
-
Публикации отдела
-
-
-
- {% if posts %} - {% include "feed/_feed_cards.html" with items=posts %} - {% else %} -
Публикаций пока нет.
- {% endif %} -
-
- - {# Простая пагинация (если нужно) #} - {% if page|default:0 %} -
- {% if page|add:"-1" %} - ← Назад - {% endif %} -
- {% endif %} + {% include "employees/components/department_sidebar.html" %}
-{# ---------- МОДАЛ «Удалить участника» ---------- #} -{% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} - -{% endif %} - -{# ---------- МОДАЛ «Добавить участника» ---------- #} -{% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} - {% with exclude_ids=members_exclude_ids %} - - {% endwith %} -{% endif %} - -{# ---------- МОДАЛ «Назначить роль» ---------- #} -{% if roles %} - {% if user.is_superuser or user.is_staff or can_assign_roles or dept.head.id == user.id %} - - {% endif %} -{% endif %} +{# ===== МОДАЛЬНЫЕ ОКНА ===== #} +{% include "employees/components/department_modals.html" %} {% endwith %} {% endblock %} -{% block extra_js %} - -{% url 'employees:employees_list' as employees_list_url %} - -{# ==== СТИЛИ (можно вынести в base.html, чтобы не дублировать) ==== #} - - -{# ==== JS: круг с автоскроллом/ручным управлением и индикатором ==== #} - - -{# ===== Переключатель панели настроек (скролл к блоку при открытии) ===== #} - - -{# ===== Модал «Добавить участника»: AJAX-поиск ===== #} - - -{# ===== Модал «Удалить участника»: локальный выбор ===== #} - - -{# ===== Модал «Назначить роль»: подстановка empId в action ===== #} - - - +{# ===== СТИЛИ в для правильного рендеринга ===== #} +{% block extra_css %} +{% include "employees/components/department_styles.html" %} +{% endblock %} + +{# ===== СКРИПТЫ ===== #} +{% block extra_js %} +{% include "employees/components/department_scripts.html" %} {% endblock %} diff --git a/backend/templates/employees/department_detail.html.backup b/backend/templates/employees/department_detail.html.backup new file mode 100644 index 00000000..64be2a33 --- /dev/null +++ b/backend/templates/employees/department_detail.html.backup @@ -0,0 +1,1025 @@ +{# backend/templates/employees/department_detail.html #} +{% extends 'base.html' %} +{% load static django_bootstrap5 %} + +{% block title %}{{ dept.name }}{% endblock %} + +{% block content %} +{% with dept_id=dept.id %} +
+ + {# ===== Заголовок ===== #} +
+ +

{{ dept.name }}

+ {% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} +
+ + {# Добавить участника #} + + + {# Назначить роль #} + {% if roles %} + {% if user.is_superuser or user.is_staff or can_assign_roles or dept.head.id == user.id %} + + {% endif %} + {% endif %} + + {# Удалить участника #} + + +
+ {% endif %} + +
+ {% url 'feed:post_create' as post_create_url %} + {% if user.is_authenticated and user.id in members_exclude_ids %} + + Создать публикацию + + {% endif %} + + +
+
+ + {# ===== Две колонки как в employee_detail: слева основное, справа сайдбар ===== #} +
+ + {# ===== ЛЕВАЯ КОЛОНКА (основная, как в профиле): команда + инфо ===== #} +
+ {# --- Команда отдела (круг) --- #} +
+
+ + +
+
{% url 'employees:employees_list' as employees_list_url %} + {% if employees_list_url %} + + Команда отдела → + + {% endif %}
+
+
+ +
+
+ + {# КРУГ #} +
+
+
+
+
+
+
+
+
+ + {# Слой фокуса #} +
+
+ + +
+
+ + {# ПОЛУКРУЖНЫЙ ИНДИКАТОР СКРОЛЛА (справа по дуге) #} + +
+ + {# Источник данных для круга #} +
+ {% for li in links %} + {% with emp=li.employee %} +
+ {% endwith %} + {% empty %} + {% endfor %} +
+ + {# Текст под кругом #} +
+
+
+
+
+
+
+ +
+
+
+ + {# --- Инфо-блок отдела (под командой) --- #} + {% if dept.description or dept.head %} +
+
+ {% if dept.description %} +
+ {{ dept.description }} +
+ {% endif %} + + {% if dept.head %} +
+ Руководитель: + {% url 'employees:employee_detail' dept.head.id as head_url %} + {% if head_url %} + + {{ dept.head.last_name }}{% if dept.head.first_name %} {{ dept.head.first_name }}{% endif %} + + {% else %} + {{ dept.head.last_name }}{% if dept.head.first_name %} {{ dept.head.first_name }}{% endif %} + {% endif %} +
+ {% endif %} + + {% if user.is_staff or dept.head.id == user.id or can_manage or can_assign_roles or can_change_head %} + + {% endif %} +
+
+ {% endif %} + +
+ + {# ===== ПРАВАЯ КОЛОНКА (сайдбар): публикации/активность ===== #} +
+ {% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} +
+
+
+
+ +
Настройки отдела
+
+ +
+
+ +
+ {% include "employees/_department_controls.html" with dept=dept edit_form=edit_form head_choices=head_choices %} +
+
+
+
+ {% endif %} +
+
+ +
+
Публикации отдела
+
+
+
+ {% if posts %} + {% include "feed/_feed_cards.html" with items=posts %} + {% else %} +
Публикаций пока нет.
+ {% endif %} +
+
+ + {# Простая пагинация (если нужно) #} + {% if page|default:0 %} +
+ {% if page|add:"-1" %} + ← Назад + {% endif %} +
+ {% endif %} +
+ +
+
+ +{# ---------- МОДАЛ «Удалить участника» ---------- #} +{% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} + +{% endif %} + +{# ---------- МОДАЛ «Добавить участника» ---------- #} +{% if user.is_staff or user.is_superuser or can_manage or dept.head.id == user.id %} + {% with exclude_ids=members_exclude_ids %} + + {% endwith %} +{% endif %} + +{# ---------- МОДАЛ «Назначить роль» ---------- #} +{% if roles %} + {% if user.is_superuser or user.is_staff or can_assign_roles or dept.head.id == user.id %} + + {% endif %} +{% endif %} + +{% endwith %} +{% endblock %} + +{% block extra_js %} + +{% url 'employees:employees_list' as employees_list_url %} + +{# ==== СТИЛИ (можно вынести в base.html, чтобы не дублировать) ==== #} + + +{# ==== JS: круг с автоскроллом/ручным управлением и индикатором ==== #} + + +{# ===== Переключатель панели настроек (скролл к блоку при открытии) ===== #} + + +{# ===== Модал «Добавить участника»: AJAX-поиск ===== #} + + +{# ===== Модал «Удалить участника»: локальный выбор ===== #} + + +{# ===== Модал «Назначить роль»: подстановка empId в action ===== #} + + + +{% endblock %} diff --git a/backend/templates/employees/department_list.html b/backend/templates/employees/department_list.html index 3f3b9c9f..971e5696 100644 --- a/backend/templates/employees/department_list.html +++ b/backend/templates/employees/department_list.html @@ -4,15 +4,9 @@ {% block title %}Отделы{% endblock %} {% block extra_css %} + \ No newline at end of file diff --git a/backend/templates/includes/rightbar_calendar.html.backup b/backend/templates/includes/rightbar_calendar.html.backup new file mode 100644 index 00000000..86586eb4 --- /dev/null +++ b/backend/templates/includes/rightbar_calendar.html.backup @@ -0,0 +1,1130 @@ +{% load static %} + + + +{# десктопная правая колонка #} +
+
+
+ +
+
Календарь
+ + {# Выпадающий выбор: Компания / Отделы #} + +
+ +
+ События + +
+
+ +
+
+ +
+ На этой неделе + +
+
+
+
+
+ +{# мобилка: offcanvas справа + плавающая кнопка #} + + +
+
+
Календарь
+ +
+
+
+ +
+ На этой неделе + +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/backend/templates/requests/request_list.html b/backend/templates/requests_app/request_list_full.html similarity index 99% rename from backend/templates/requests/request_list.html rename to backend/templates/requests_app/request_list_full.html index 4048463f..837bc07e 100644 --- a/backend/templates/requests/request_list.html +++ b/backend/templates/requests_app/request_list_full.html @@ -311,7 +311,7 @@ Скрыть {% if obj.attachment %} - + Вложение {% endif %} diff --git a/backend/templates/search/results.html b/backend/templates/search/results.html index 94e46bf6..ab51c3c5 100644 --- a/backend/templates/search/results.html +++ b/backend/templates/search/results.html @@ -4,32 +4,9 @@ {% block title %}Поиск{% endblock %} {% block extra_css %} +