- Migrated all 28 CSS components to SCSS architecture
- Deleted 29 source CSS files (kept only compiled app.css & bootstrap-custom.css)
- Created 30 SCSS component modules in scss/components/
- Cleaned 24 templates from duplicate CSS links
- Updated main.scss with new imports (layout-spacing, spacing-utils, rightbar-calendar)
- Filled empty _navbar.scss with content
- Refactored css/ folder: removed obsolete files, updated README.md
- Created comprehensive documentation (migration complete, quick reference, reports)
Migration Statistics:
- Components migrated: 28 (~4,768 lines SCSS)
- CSS files deleted: 29
- Templates cleaned: 24
- app.css size: 95 KB (compiled from SCSS)
- bootstrap-custom.css: 301 KB
- Total: 396 KB
Phase 6 Complete: 100% SCSS architecture achieved ✅
273 lines
12 KiB
HTML
273 lines
12 KiB
HTML
{# backend/templates/employees/_department_controls.html #}
|
||
{# Контекст: dept, head_choices, roles, dept_perm_choices, can_manage, can_assign_roles, can_change_head #}
|
||
|
||
{% load static %}
|
||
|
||
{% if can_manage %}
|
||
<div class="ios-acc mb-3">
|
||
<div class="d-flex align-items-center gap-2 px-3 py-3">
|
||
<i class="bi-pencil-square"></i>
|
||
<div class="ios-section-title mb-0">Управление отделом</div>
|
||
</div>
|
||
|
||
<div class="ios-acc-body">
|
||
<form method="post" action="{% url 'employees:department_edit' dept.id %}">
|
||
{% csrf_token %}
|
||
<div class="row g-3 px-2">
|
||
<div class="col-12">
|
||
<label class="form-label mb-1" for="dept_name">Название</label>
|
||
<input id="dept_name" name="name" class="form-control"
|
||
value="{{ dept.name|default_if_none:'' }}" required>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label class="form-label mb-1" for="dept_desc">Описание</label>
|
||
<textarea id="dept_desc" name="description" class="form-control"
|
||
rows="3" data-autogrow data-max-height="240"
|
||
style="overflow:hidden;">{{ dept.description|default_if_none:'' }}</textarea>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<button type="submit" class="btn btn-primary btn-sm">
|
||
<i class="bi-check2 me-1"></i>Сохранить
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
|
||
{% if can_change_head %}
|
||
<div class="ios-acc mb-3">
|
||
<div class="d-flex align-items-center gap-2 px-3 py-3">
|
||
<i class="bi-person-check"></i>
|
||
<div class="ios-section-title mb-0">Руководитель отдела</div>
|
||
</div>
|
||
|
||
<div class="ios-acc-body">
|
||
<form id="deptHeadForm" method="post" action="{% url 'employees:department_set_head' dept.id %}">
|
||
{% csrf_token %}
|
||
<div class="row g-3 px-2">
|
||
<div class="col-12">
|
||
<label class="form-label mb-1" for="dept_head_text">Руководитель</label>
|
||
<div class="position-relative"
|
||
data-head-picker
|
||
data-choices='[
|
||
{% for m in head_choices %}
|
||
{"id":"{{ m.id }}","name":"{{ m.name|escapejs }}","email":"{{ m.email|default_if_none:''|escapejs }}"}{% if not forloop.last %},{% endif %}
|
||
{% endfor %}
|
||
]'>
|
||
<input id="dept_head_text" type="text" class="form-control"
|
||
placeholder="ФИО или email" autocomplete="off"
|
||
value="{% if dept.head %}{{ dept.head.name|default_if_none:'' }}{% endif %}">
|
||
<input id="dept_head_id" type="hidden" name="head_id"
|
||
value="{% if dept.head %}{{ dept.head.id }}{% endif %}">
|
||
<div class="dropdown-menu w-100 shadow" style="max-height:240px; overflow:auto;"></div>
|
||
<div class="invalid-feedback">Выберите сотрудника из списка, введите точный email или очистите поле.</div>
|
||
</div>
|
||
<div class="form-text">Очистите поле и сохраните, чтобы снять руководителя.</div>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<button type="submit" class="btn btn-primary btn-sm">
|
||
<i class="bi-check2 me-1"></i>Сохранить
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
|
||
{% if can_assign_roles %}
|
||
<div class="ios-acc mb-3">
|
||
<div class="d-flex align-items-center gap-2 px-3 py-3">
|
||
<i class="bi-list-check"></i>
|
||
<div class="ios-section-title mb-0">Роли отдела</div>
|
||
</div>
|
||
|
||
<div class="ios-acc-body">
|
||
<div class="row g-3 px-2">
|
||
<!-- Редактировать существующую роль -->
|
||
<div class="col-12">
|
||
<form id="roleEditForm" method="post" action="{% url 'employees:edit_department_role' dept.id %}">
|
||
{% csrf_token %}
|
||
<input type="hidden" name="op" value="update">
|
||
<input type="hidden" name="role_id" id="edit_role_id" value="">
|
||
<div id="edit_role_perms_hidden" class="d-none"></div>
|
||
|
||
<div class="row g-3">
|
||
<div class="col-12">
|
||
<label class="form-label mb-1" for="role_name_text">Роль</label>
|
||
<div class="position-relative"
|
||
data-role-picker
|
||
data-choices='[
|
||
{% for r in roles %}
|
||
{"id":"{{ r.id }}",
|
||
"name":"{{ r.name|escapejs }}",
|
||
"perms":"{% if r.permissions %}{% for p in r.permissions %}{% if not forloop.first %},{% endif %}{{ p }}{% endfor %}{% endif %}"}
|
||
{% if not forloop.last %},{% endif %}
|
||
{% endfor %}
|
||
]'>
|
||
<input id="role_name_text" name="name" type="text" class="form-control"
|
||
placeholder="Начните вводить название роли" autocomplete="off">
|
||
<input type="hidden" id="role_id_hidden">
|
||
<div class="dropdown-menu w-100 shadow" style="max-height:240px; overflow:auto;"></div>
|
||
<div class="invalid-feedback">Выберите роль из списка или введите новое название для создания.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<button type="button"
|
||
class="btn btn-outline-secondary btn-sm me-2"
|
||
data-bs-toggle="modal"
|
||
data-bs-target="#rolePermsEditModal"
|
||
{% if not dept_perm_choices %}disabled{% endif %}>
|
||
Выбрать права
|
||
</button>
|
||
<div id="edit_role_perms_chips" class="chips mt-2"></div>
|
||
</div>
|
||
|
||
<div class="col-12 d-flex flex-wrap gap-2">
|
||
<button type="submit" class="btn btn-primary btn-sm" id="roleSaveBtn" disabled>
|
||
<i class="bi-check2 me-1"></i>Сохранить
|
||
</button>
|
||
<button type="button" class="btn btn-outline-danger btn-sm" id="roleDeleteBtn" disabled>
|
||
<i class="bi-trash me-1"></i>Удалить
|
||
</button>
|
||
<button type="button"
|
||
id="toggleNewRole"
|
||
class="btn btn-outline-secondary btn-sm"
|
||
data-toggle-target="#roleCreateForm">
|
||
<i class="bi-plus-lg me-1"></i>Новая роль
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
{# ——— УБРАНО <hr class="my-0"> ——— #}
|
||
|
||
<!-- Форма создания роли (скрыта по умолчанию) -->
|
||
<div class="col-12">
|
||
<form id="roleCreateForm" class="d-none" method="post" action="{% url 'employees:edit_department_role' dept.id %}">
|
||
{% csrf_token %}
|
||
<input type="hidden" name="op" value="create">
|
||
<div id="new_role_perms_hidden" class="d-none"></div>
|
||
|
||
<div class="row g-3">
|
||
<div class="col-12">
|
||
<label class="form-label mb-1" for="new_role_name">Название новой роли</label>
|
||
<input id="new_role_name" name="name" class="form-control"
|
||
placeholder="Напр. Руководитель смены" required>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<button type="button"
|
||
class="btn btn-outline-secondary btn-sm me-2"
|
||
data-bs-toggle="modal"
|
||
data-bs-target="#rolePermsCreateModal"
|
||
{% if not dept_perm_choices %}disabled{% endif %}>
|
||
Выбрать права
|
||
</button>
|
||
<div id="new_role_perms_chips" class="chips mt-2"></div>
|
||
</div>
|
||
|
||
<div class="col-12 d-flex gap-2">
|
||
<button type="submit" class="btn btn-success btn-sm">
|
||
<i class="bi-plus-lg me-1"></i>Создать
|
||
</button>
|
||
<button type="button" id="cancelNewRole" class="btn btn-ghost btn-sm">
|
||
<i class="bi-x-lg me-1"></i>Отмена
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
|
||
{% if not can_manage and not can_assign_roles and not can_change_head %}
|
||
<div class="alert alert-light border small mb-3">У вас нет прав для управления этим отделом.</div>
|
||
{% endif %}
|
||
|
||
{# ===== Модальные окна выбора прав ===== #}
|
||
<div class="modal fade" id="rolePermsEditModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fullscreen-sm-down">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">Права роли</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
{% if dept_perm_choices %}
|
||
<div id="permListEdit" class="list-group">
|
||
{% for p in dept_perm_choices %}
|
||
<label class="list-group-item d-flex align-items-center gap-2 py-2">
|
||
<input class="form-check-input me-2 perm-check-edit" type="checkbox" value="{{ p.id }}">
|
||
<span class="flex-grow-1">{{ p.name }}</span>
|
||
<span class="text-muted small">{{ p.code }}</span>
|
||
</label>
|
||
{% endfor %}
|
||
</div>
|
||
{% else %}
|
||
<div class="text-muted">Нет доступных прав.</div>
|
||
{% endif %}
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-primary btn-sm" id="applyPermsEdit" data-bs-dismiss="modal">
|
||
<i class="bi-check2 me-1"></i>Готово
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal fade" id="rolePermsCreateModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fullscreen-sm-down">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title">Права новой роли</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
{% if dept_perm_choices %}
|
||
<div id="permListCreate" class="list-group">
|
||
{% for p in dept_perm_choices %}
|
||
<label class="list-group-item d-flex align-items-center gap-2 py-2">
|
||
<input class="form-check-input me-2 perm-check-create" type="checkbox" value="{{ p.id }}">
|
||
<span class="flex-grow-1">{{ p.name }}</span>
|
||
<span class="text-muted small">{{ p.code }}</span>
|
||
</label>
|
||
{% endfor %}
|
||
</div>
|
||
{% else %}
|
||
<div class="text-muted">Нет доступных прав.</div>
|
||
{% endif %}
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-primary btn-sm" id="applyPermsCreate" data-bs-dismiss="modal">
|
||
<i class="bi-check2 me-1"></i>Готово
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{# ===== Форма удаления роли ===== #}
|
||
<form id="roleDeleteForm" method="post" action="{% url 'employees:edit_department_role' dept.id %}" class="d-none">
|
||
{% csrf_token %}
|
||
<input type="hidden" name="op" value="delete">
|
||
<input type="hidden" name="role_id" id="delete_role_id">
|
||
</form>
|
||
|
||
{# ===== JS: пикеры / роли ===== #}
|
||
{# Autogrow теперь в base.html через textareaAutogrow.js #}
|
||
{# Скрипты управления отделами вынесены в модули #}
|
||
{% include "employees/components/department_controls_scripts.html" %} |