+ {% if reset_notice %}
+
{{ reset_notice }}
+ {% endif %}
{% if error %}
{{ error }}
{% endif %}
diff --git a/templates/pages/employee-detail.html b/templates/pages/employee-detail.html
deleted file mode 100644
index 280ba28..0000000
--- a/templates/pages/employee-detail.html
+++ /dev/null
@@ -1,257 +0,0 @@
-{% load common_tags %}
-{% load i18n %}
-
-
-
-
-
-
-
-
- {% if employee.photo %}
-
- {% else %}
-
{{ employee|initials }}
- {% endif %}
-
-
-
{{ employee.last_name }} {{ employee.first_name }}{% if employee.middle_name %} {{ employee.middle_name }}{% endif %}
- {% if employee.is_active %}
- {% trans "Активный" %}
- {% else %}
- {% trans "Не активный" %}
- {% endif %}
- {% if can_view_pii and employee.is_on_probation %}{% trans "Испытательный срок" %} {% endif %}
-
-
- {% if employee.post %}{{ employee.post.name }}{% else %}{{ employee.groups.first.name|default:"Посада не вказана" }}{% endif %}
- {% if employee.department %} · {{ employee.department.name }}{% elif employee.user_pharmacy %} · Аптека №{{ employee.user_pharmacy.pharmacy_number }}{% endif %}
-
- {% if employee.user_organization %}
{{ employee.user_organization.name }}
{% endif %}
-
-
-
-
-
-
-
-
-
-
-
-
{% trans "Контакты" %}
-
-
-
{% trans "Рабочий email" %}
-
{{ employee.email|default:"—" }}
-
-
-
{% trans "Рабочий телефон" %}
-
{{ employee.phone|default:"—" }}
-
-
-
{% trans "Внутренний номер" %}
-
{{ employee.internal_number|default:"—" }}
-
- {% if can_view_pii %}
-
-
{% trans "Личный телефон" %}
-
{{ employee.personal_phone|default:"—" }}
-
-
-
{% trans "Личный email" %}
-
{{ employee.personal_email|default:"—" }}
-
- {% endif %}
-
-
-
-
-
-
{% trans "Работа" %}
-
-
-
{% trans "Руководитель" %}
-
-
-
-
{% trans "Подчинённых" %}
-
{{ employee.subordinate_count }}
-
- {% if can_view_pii %}
-
-
{% trans "Дата приёма" %}
-
{{ employee.hire_date|date:"d.m.Y" }} ({{ employee.work_experience }} р.)
-
-
-
{% trans "Дата увольнения" %}
-
{{ employee.termination_date|date:"d.m.Y"|default:"—" }}
-
- {% endif %}
-
-
-
- {% if can_view_pii %}
-
-
-
{% trans "Личные данные" %}
-
-
-
{% trans "Дата рождения" %}
-
{{ employee.birth_date|date:"d.m.Y"|default:"—" }}{% if employee.age %} ({{ employee.age }} р.) {% endif %}
-
-
-
{% trans "ИНН" %}
-
{{ employee.inn_code|default:"—" }}
-
-
- {% if employee.emergency_contact_name %}
-
-
{% trans "Экстренный контакт" %}
-
{{ employee.emergency_contact_name }}{% if employee.emergency_contact_relation %} ({{ employee.emergency_contact_relation }}){% endif %}
- {% if employee.emergency_contact_phone %}
{{ employee.emergency_contact_phone }}
{% endif %}
-
- {% endif %}
-
-
-
-
-
{% trans "Интеграции" %}
-
-
- Mattermost{% if employee.mattermost_username %} ({{ employee.mattermost_username }}){% endif %}
- {% if employee.mattermost_sync_status == 'synced' %}{% trans "Синхронизировано" %}
- {% elif employee.mattermost_sync_status == 'error' %}{% trans "Ошибка" %}
- {% elif employee.mattermost_sync_status == 'disabled' %}{% trans "Отключено" %}
- {% else %}{% trans "Ожидает" %} {% endif %}
-
-
- Active Directory
- {% if employee.ad_sync_status == 'synced' %}{% trans "Синхронизировано" %}
- {% elif employee.ad_sync_status == 'error' %}{% trans "Ошибка" %}
- {% elif employee.ad_sync_status == 'disabled' %}{% trans "Отключено" %}
- {% else %}{% trans "Не синхронизировано" %} {% endif %}
-
-
-
-
-
-
- {% endif %}
-
-
-
-
-
- {% if manager_chain %}
-
-
{% trans "Цепочка руководства" %}
-
-
- {% endif %}
-
- {% if subordinates %}
-
-
Підлеглі · {{ subordinates|length }}
-
-
- {% endif %}
-
- {% if can_view_pii and last_attendance %}
-
-
{% trans "Последнее посещение" %}
-
- {% if last_attendance.direction == 'in' %}Прихід{% else %}Вихід{% endif %} · {{ last_attendance.timestamp|date:"d.m.Y H:i" }}
-
-
{{ last_attendance.location.name }}
-
- {% endif %}
-
- {% if employee.groups.all %}
-
-
{% trans "Группы доступа" %}
-
- {% for group in employee.groups.all %}
- {{ group.name }}
- {% endfor %}
-
-
- {% endif %}
-
-
-
-
diff --git a/templates/pages/employee-profile.html b/templates/pages/employee-profile.html
new file mode 100644
index 0000000..6de46d1
--- /dev/null
+++ b/templates/pages/employee-profile.html
@@ -0,0 +1,190 @@
+{% load common_tags %}
+{% load i18n %}
+
+{% with tab=profile_active_tab|default:'overview' %}
+
+ {% if saved %}
+
+ {% trans "Изменения сохранены" %}
+
+ {% endif %}
+ {% if errors %}
+
+ {% for err in errors %}
{{ err }}
{% endfor %}
+
+ {% endif %}
+ {% if hr_request_created %}
+
+ {% trans "Заявку подано" %}: {{ hr_request_created.summary }}
+
+ {% endif %}
+ {% if hr_request_errors %}
+
+
{% trans "Не удалось подать заявку" %}
+ {% for err in hr_request_errors %}
{{ err }}
{% endfor %}
+
+ {% endif %}
+
+
+
+
+ {% include "partials/employee-profile-aside.html" %}
+
+
+
+
+ {% trans "Общая информация" %}
+ {% if is_own_profile or can_view_pii %}
+ {% trans "Контакты" %}
+ {% endif %}
+ {% trans "Работа" %}
+ {% if is_own_profile %}
+ {% trans "Настройки" %}
+ {% endif %}
+
+
+
+
+ {% include "partials/employee-profile-overview.html" %}
+
+
+
+ {% if is_own_profile or can_view_pii %}
+
+ {% endif %}
+
+
+
+
+
+
{% trans "Работа" %}
+
+
{% trans "Организация" %}
{{ employee.user_organization.name|default:"—" }}
+
{% trans "Отдел" %}
{{ employee.department.name|default:"—" }}
+
{% trans "Должность" %}
{% if employee.post %}{{ employee.post.name }}{% else %}—{% endif %}
+
{% trans "Внутренний номер" %}
{{ employee.internal_number|default:"—" }}
+ {% if can_view_pii %}
+
{% trans "Дата приёма" %}
{{ employee.hire_date|date:"d.m.Y"|default:"—" }}{% if employee.hire_date %} ({{ employee.work_experience }} {% trans "л." %}) {% endif %}
+
{% trans "Дата увольнения" %}
{{ employee.termination_date|date:"d.m.Y"|default:"—" }}
+
{% trans "Дата рождения" %}
{{ employee.birth_date|date:"d.m.Y"|default:"—" }}{% if employee.age %} ({{ employee.age }}){% endif %}
+
{% trans "ИНН" %}
{{ employee.inn_code|default:"—" }}
+ {% endif %}
+
+
+ {% if can_view_pii %}
+
+
{% trans "Интеграции" %}
+
+
Mattermost
+ {% if employee.mattermost_sync_status == 'synced' %}OK {% elif employee.mattermost_sync_status == 'error' %}{% trans "Ошибка" %} {% else %}— {% endif %}
+
+
Active Directory
+ {% if employee.ad_sync_status == 'synced' %}OK {% elif employee.ad_sync_status == 'error' %}{% trans "Ошибка" %} {% else %}— {% endif %}
+
+
+
+ {% endif %}
+
+
+
+
+ {% if is_own_profile %}
+
+
+
+ {% include "partials/employee-profile-2fa.html" %}
+
+
+ {% endif %}
+
+
+
+
+{% endwith %}
+
diff --git a/templates/pages/profile.html b/templates/pages/profile.html
deleted file mode 100644
index b548018..0000000
--- a/templates/pages/profile.html
+++ /dev/null
@@ -1,107 +0,0 @@
-{% load common_tags %}
-{% load i18n %}
-
-
-
-
{% trans "Мой профиль" %}
-
{% trans "Личные контактные данные и настройки интерфейса" %}
-
-
- {% if saved %}
-
- Зміни збережено
-
- {% endif %}
-
- {% if errors %}
-
- {% for err in errors %}
{{ err }}
{% endfor %}
-
- {% endif %}
-
-
-
-
diff --git a/templates/partials/employee-profile-2fa.html b/templates/partials/employee-profile-2fa.html
new file mode 100644
index 0000000..470dc29
--- /dev/null
+++ b/templates/partials/employee-profile-2fa.html
@@ -0,0 +1,113 @@
+{% load i18n %}
+
+
{% trans "Двухфакторная аутентификация (2FA)" %}
+
+ {% if twofa_mandatory %}
+ {% trans "Для вашего аккаунта 2FA обязательна." %}
+ {% else %}
+ {% trans "Дополнительная защита входа через приложение-аутентификатор." %}
+ {% endif %}
+
+
+ {% if twofa_message %}
+
{{ twofa_message }}
+ {% endif %}
+ {% if twofa_error %}
+
{{ twofa_error }}
+ {% endif %}
+
+
+ {% trans "Статус" %}
+ {% if twofa_enabled %}
+ {% trans "Включена" %}
+ {% else %}
+ {% trans "Выключена" %}
+ {% endif %}
+ {% if twofa_enabled %}
+
+ {% blocktrans count counter=twofa_backup_remaining %}Осталось {{ counter }} резервный код{% plural %}Осталось {{ counter }} резервных кодов{% endblocktrans %}
+
+ {% endif %}
+
+
+ {% if twofa_backup_codes %}
+
+
{% trans "Резервные коды (сохраните сейчас — больше не будут показаны)" %}
+
+ {% for code in twofa_backup_codes %}
+ {{ code }}
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% if twofa_show_setup %}
+
+ {% if twofa_reset_notice %}
+
{{ twofa_reset_notice }}
+ {% endif %}
+ {% if twofa_setup_error %}
+
{{ twofa_setup_error }}
+ {% endif %}
+
+
+
+
+
{% trans "Ключ вручную:" %}
+
{{ twofa_setup_secret }}
+
+
+
+
+
+
+
+
+
+ {% elif not twofa_enabled %}
+
+ {% trans "Настроить 2FA" %}
+
+
+ {% else %}
+
+
+
+ {% if twofa_can_disable %}
+
+ {% elif twofa_mandatory %}
+
{% trans "Отключение недоступно — 2FA обязательна для вашего аккаунта." %}
+ {% endif %}
+
+ {% endif %}
+
diff --git a/templates/partials/employee-profile-aside.html b/templates/partials/employee-profile-aside.html
new file mode 100644
index 0000000..92b32be
--- /dev/null
+++ b/templates/partials/employee-profile-aside.html
@@ -0,0 +1,90 @@
+{% load common_tags %}
+{% load i18n %}
+
+
+ {% if is_own_profile %}
+
+ {% else %}
+ {% if employee.photo %}
+
+ {% else %}
+
{{ employee|initials }}
+ {% endif %}
+ {% endif %}
+
+
+
+ {{ employee.last_name }} {{ employee.first_name }}{% if employee.middle_name %} {{ employee.middle_name }}{% endif %}
+
+
+
+ {% if employee.is_active %}{% trans "Активный" %} {% else %}{% trans "Не активный" %} {% endif %}
+ {% if can_view_pii and employee.is_on_probation %}{% trans "Испытательный срок" %} {% endif %}
+
+
+
+
{% if employee.post %}{{ employee.post.name }}{% else %}{{ employee.groups.first.name|default:"—" }}{% endif %}
+ {% if employee.department %}
+
{{ employee.department.name }}
+ {% elif employee.user_pharmacy %}
+
{% trans "Аптека" %} №{{ employee.user_pharmacy.pharmacy_number }}
+ {% endif %}
+
+
+
+
+ {% trans "Логин" %}
+ {{ employee.username }}
+
+ {% if can_view_pii or is_own_profile %}
+ {% if employee.email %}
+
+ Email
+ {{ employee.email }}
+
+ {% endif %}
+ {% if employee.phone %}
+
+ {% trans "Телефон" %}
+ {{ employee.phone }}
+
+ {% endif %}
+ {% endif %}
+ {% if employee.internal_number %}
+
+ {% trans "Внутр." %}
+ {{ employee.internal_number }}
+
+ {% endif %}
+
+
+
+
diff --git a/templates/partials/employee-profile-hr-requests.html b/templates/partials/employee-profile-hr-requests.html
new file mode 100644
index 0000000..3373959
--- /dev/null
+++ b/templates/partials/employee-profile-hr-requests.html
@@ -0,0 +1,99 @@
+{% load i18n %}
+
+
+
{% trans "Заявки и отпуска" %}
+
+ {% trans "Отпуск" %}
+ {% trans "Доп." %}
+ {% trans "Справка" %}
+ {% trans "Больничный" %}
+ {% trans "Отгул" %}
+
+
+
+
+
+
+
+
{% trans "Мои заявки" %}
+ {% for req in my_hr_requests|slice:":8" %}
+
+
+
{{ req.summary }}
+
{{ req.created_at|date:"d.m.Y" }}{% if req.day_count %} · {{ req.day_count }} {% trans "дн." %}{% endif %}
+
+
+ {% if req.status == 'pending' %}
+ {{ req.get_status_display }}
+
+ {% csrf_token %}
+
+ {% trans "Отменить" %}
+
+ {% elif req.status == 'approved' %}
+ {{ req.get_status_display }}
+ {% elif req.status == 'rejected' %}
+ {{ req.get_status_display }}
+ {% else %}
+ {{ req.get_status_display }}
+ {% endif %}
+
+
+ {% empty %}
+
{% trans "Заявок пока нет" %}
+ {% endfor %}
+
+
+
{% trans "Баланс отпусков" %} — {{ leave_balance_year }}
+
+ {% for row in leave_balance %}
+
+
+ {{ row.label }}
+ {{ row.used }}/{{ row.entitlement }} {% trans "дн." %}
+
+
+
{% trans "Остаток" %}: {{ row.remaining }}{% if row.pending %} · {% trans "ожидает" %}: {{ row.pending }}{% endif %}
+
+ {% endfor %}
+ {% if next_vacation_request %}
+
+
{% trans "Следующий отпуск" %}
+
+ {{ next_vacation_request.summary }}
+ {{ next_vacation_request.get_status_display }}
+
+
+ {% endif %}
+
+
+
+
diff --git a/templates/partials/employee-profile-overview.html b/templates/partials/employee-profile-overview.html
new file mode 100644
index 0000000..98dd8e3
--- /dev/null
+++ b/templates/partials/employee-profile-overview.html
@@ -0,0 +1,123 @@
+{% load common_tags %}
+{% load i18n %}
+
+{% if is_own_profile %}
+{% include "partials/employee-profile-hr-requests.html" %}
+{% endif %}
+
+
+
+ {% if employee.manager or manager_chain or subordinates or employee.groups.all %}
+
+
{% trans "Структура" %}
+
+ {% if employee.manager %}
+
+ {% endif %}
+ {% if employee.groups.all %}
+
+
{% trans "Группы доступа" %}
+
+ {% for group in employee.groups.all %}
+ {{ group.name }}
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% if subordinates %}
+
+
{% trans "Подчинённые" %} · {{ subordinates|length }}
+
+
+ {% elif employee.is_manager %}
+
{% trans "Нет подчинённых" %}
+ {% endif %}
+ {% if manager_chain|length > 1 %}
+
+
{% trans "Цепочка руководства" %}
+
+ {% for mgr in manager_chain %}
+ {% if not forloop.first %}
→ {% endif %}
+
{{ mgr.get_full_name }}
+ {% endfor %}
+
+
+ {% endif %}
+
+ {% endif %}
+
+ {% if can_view_pii %}
+
+ {% if employee.hire_date %}
+
+
{% trans "Стаж" %}
+
{% trans "с" %} {{ employee.hire_date|date:"d.m.Y" }} · {{ employee.work_experience }} {% trans "л." %}
+
+ {% endif %}
+ {% if last_attendance %}
+
+
{% trans "Последнее посещение" %}
+
{% if last_attendance.direction == 'in' %}{% trans "Приход" %}{% else %}{% trans "Выход" %}{% endif %} · {{ last_attendance.timestamp|date:"d.m.Y H:i" }} · {{ last_attendance.location.name }}
+
+ {% endif %}
+
+ Mattermost
+ {% if employee.mattermost_sync_status == 'synced' %}OK {% elif employee.mattermost_sync_status == 'error' %}{% trans "Ошибка" %} {% else %}— {% endif %}
+ AD
+ {% if employee.ad_sync_status == 'synced' %}OK {% elif employee.ad_sync_status == 'error' %}{% trans "Ошибка" %} {% else %}— {% endif %}
+
+
{% trans "Кадровые данные" %}
+
+ {% elif not is_own_profile and not can_view_pii %}
+
+ {% trans "Контакты доступны руководителю и HR." %}
+ {% trans "Место работы" %}
+
+ {% endif %}
+
+
+{% if recent_tasks %}
+
+{% endif %}
+
+{% if is_own_profile %}
+
{% trans "Служебные данные (должность, отдел, рабочий email) редактирует администратор." %}
+{% endif %}
diff --git a/templates/partials/employee-profile-sidebar.html b/templates/partials/employee-profile-sidebar.html
new file mode 100644
index 0000000..f24b85d
--- /dev/null
+++ b/templates/partials/employee-profile-sidebar.html
@@ -0,0 +1,53 @@
+{% load common_tags %}
+{% load i18n %}
+{% if manager_chain %}
+
+
{% trans "Цепочка руководства" %}
+
+
+{% endif %}
+
+{% if subordinates %}
+
+
{% trans "Подчинённые" %} · {{ subordinates|length }}
+
+
+{% endif %}
+
+{% if can_view_pii and last_attendance %}
+
+
{% trans "Последнее посещение" %}
+
{% if last_attendance.direction == 'in' %}{% trans "Приход" %}{% else %}{% trans "Выход" %}{% endif %} · {{ last_attendance.timestamp|date:"d.m.Y H:i" }}
+
{{ last_attendance.location.name }}
+
+{% endif %}
+
+{% if employee.groups.all %}
+
+
{% trans "Группы доступа" %}
+
+ {% for group in employee.groups.all %}
+ {{ group.name }}
+ {% endfor %}
+
+
+{% endif %}
diff --git a/templates/password-setup.html b/templates/password-setup.html
new file mode 100644
index 0000000..2f21d37
--- /dev/null
+++ b/templates/password-setup.html
@@ -0,0 +1,78 @@
+{% load static %}
+
+
+
+
+
+
АКС — Встановлення пароля
+
+
+
+
+
+
+
+
+
+
+
+ {% if invalid %}Посилання недійсне{% else %}Встановлення пароля{% endif %}
+
+ {% if invalid %}
+
+ Посилання прострочене, вже використане або не існує. Зверніться до HR або IT для нового запрошення.
+
+
← На сторінку входу
+ {% else %}
+
+ {{ employee.last_name }} {{ employee.first_name }} · логін {{ employee.username }}
+
+ {% if expires_at %}
+
+ Дійсне до {{ expires_at|date:"d.m.Y H:i" }}
+
+ {% endif %}
+ {% endif %}
+
+
+ {% if not invalid %}
+
+ {% csrf_token %}
+ {% if errors %}
+
+ {% for err in errors %}
{{ err }}
{% endfor %}
+
+ {% endif %}
+
+
+ Новий пароль
+
+
+
+ Підтвердження пароля
+
+
+
+ {% if show_ad_sync %}
+
+
+ Також змінити пароль у домені Active Directory
+ {{ employee.ad_user_dn }}
+
+
+ {% elif ad_sync_hint %}
+
+ {{ ad_sync_hint }}
+
+ {% endif %}
+
+
+ Зберегти пароль
+
+
+ {% endif %}
+
+
+