Corp/backend/templates/employees/employee_detail.html
Игорь Надеин 1f811fde2a feat: add avatar zoom modal on click
- Added Bootstrap modal for enlarged avatar view
- Made avatar image clickable with cursor pointer
- Added modal trigger to both static and dynamically loaded avatars
- Modal displays full-size avatar centered with dark backdrop
- Close button positioned in top-right corner
2025-12-08 11:07:45 +03:00

459 lines
24 KiB
HTML
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.

{# templates/employees/employee_detail.html #}
{% extends "base.html" %}
{% load static django_bootstrap5 %}
{% block title %}
{% if is_me %}Мой профиль{% else %}Профиль сотрудника{% endif %}
{% endblock %}
{% block extra_css %}
{% endblock %}
{% block content %}
<div class="container">
<div class="ios-header section-header d-flex align-items-center gap-2 flex-wrap">
<i class="bi-person-badge fs-2 text-primary"></i>
<h2 class="title ios-title mb-0">
{% if is_me %}Мой профиль{% else %}Профиль сотрудника{% endif %}
</h2>
<div class="ms-auto"></div>
</div>
<div class="row g-3 align-items-start">
<!-- LEFT -->
<div class="col-md-4">
<article class="ios-card">
<div class="p-3 text-center">
<!-- Avatar -->
<div class="ava-wrap mb-3" id="employeeAvatar">
{% if emp.avatar %}
<img src="{{ emp.avatar }}"
alt="Аватар {{ emp.last_name }} {{ emp.first_name }}"
class="ios-ava"
style="object-fit:cover; cursor: pointer;"
data-bs-toggle="modal"
data-bs-target="#avatarModal"
data-avatar-url="{{ emp.avatar }}">
{% else %}
<div class="ios-ava--ph">
{% firstof emp.first_name emp.last_name "?" as initial %}
{{ initial|slice:":1"|upper }}
</div>
{% endif %}
</div>
<!-- Name + edit button -->
<div class="d-flex align-items-center justify-content-center gap-2 mb-1">
<div class="ios-name" id="employeeName">
{{ emp.last_name }} {{ emp.first_name }}{% if emp.patronymic %} {{ emp.patronymic }}{% endif %}
</div>
{% if can_edit %}
<button class="btn-icon"
type="button"
data-bs-toggle="collapse"
data-bs-target="#editFormWrap"
aria-expanded="false"
aria-controls="editFormWrap"
title="Редактировать профиль" aria-label="Редактировать профиль">
<i class="bi-pencil-square"></i>
</button>
<noscript>
<a class="btn btn-link p-0" href="{% url 'employees:employee_edit' emp.id %}">Редактировать</a>
</noscript>
{% endif %}
</div>
<div class="ios-role">
{% if emp.position.name %}
{{ emp.position.name }}
{% elif emp.position_label %}
{{ emp.position_label }}
{% elif emp.position %}
Должность #{{ emp.position }}
{% endif %}
</div>
<!-- Contacts -->
<div class="mt-3 text-start">
<div class="ios-acc">
<button class="ios-collapse-trigger"
type="button"
data-bs-toggle="collapse"
data-bs-target="#contactsCollapse"
aria-expanded="false"
aria-controls="contactsCollapse">
<span class="title"><i class="bi-person-lines-fill me-1"></i>Контакты</span>
<i class="bi-chevron-down ios-collapse-caret"></i>
</button>
<div id="contactsCollapse" class="collapse">
<div class="ios-acc-body">
<div class="ios-list">
{% if emp.email %}
<div class="ios-cell">
<i class="bi-envelope"></i>
<a href="mailto:{{ emp.email }}">{{ emp.email }}</a>
</div>
{% endif %}
{% if emp.phone_number %}
<div class="ios-cell">
<i class="bi-telephone"></i>
<a href="tel:{{ emp.phone_number }}">{{ emp.phone_number }}</a>
</div>
{% endif %}
{% if emp.telegram %}<div class="ios-cell"><i class="bi-telegram"></i><span>{{ emp.telegram }}</span></div>{% endif %}
{% if emp.whatsapp %}<div class="ios-cell"><i class="bi-whatsapp"></i><span>{{ emp.whatsapp }}</span></div>{% endif %}
{% if emp.wechat %}<div class="ios-cell"><i class="bi-wechat"></i><span>{{ emp.wechat }}</span></div>{% endif %}
{% if not emp.email and not emp.phone_number and not emp.telegram and not emp.whatsapp and not emp.wechat %}
<div class="ios-cell"><span class="text-secondary">Контакты не указаны</span></div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{# === ОТДЕЛЫ: роль скрыта; редактор по кнопке-карандашу (только для начальников) === #}
<div class="mt-3 text-start">
<div class="ios-acc">
<button class="ios-collapse-trigger"
type="button"
data-bs-toggle="collapse"
data-bs-target="#departmentsCollapse"
aria-expanded="false"
aria-controls="departmentsCollapse">
<span class="title"><i class="bi-diagram-3 me-1"></i>Отделы</span>
<i class="bi-chevron-down ios-collapse-caret"></i>
</button>
<div id="departmentsCollapse" class="collapse">
<div class="ios-acc-body">
<div class="ios-list">
{% if emp_depts and emp_depts|length %}
{% for d in emp_depts %}
{% if d.name %}
<div class="ios-cell dept-item"
data-dept-id="{{ d.id|default:'' }}"
data-role-id="{{ d.role_id|default:'' }}"
data-is-head="{{ d.is_head|yesno:'1,0' }}">
<div class="dept-line">
{% if d.id %}
<a class="text-decoration-none dept-name" href="{% url 'employees:department_detail' d.id %}">{{ d.name }}</a>
{% else %}
<span class="dept-name">{{ d.name }}</span>
{% endif %}
<span class="dept-role">
{% if d.role_name %}— {{ d.role_name }}{% endif %}
</span>
</div>
<div class="d-flex align-items-center gap-2">
{% if d.is_head %}
<span class="badge-head" data-bs-toggle="tooltip" data-bs-title="Руководитель отдела">
<i class="bi-award-fill" aria-hidden="true"></i><span class="lbl">Руководитель</span>
</span>
{% endif %}
{% if d.can_edit_role %}
<button class="badge-gear" type="button"
data-bs-toggle="collapse" data-bs-target="#roleEditor-{{ d.id }}"
aria-expanded="false" aria-controls="roleEditor-{{ d.id }}"
title="Изменить роль сотрудника в этом отделе" aria-label="Изменить роль">
<i class="bi-gear-fill"></i><span class="lbl">Роль</span>
</button>
{% endif %}
</div>
{% if d.can_edit_role %}
<div id="roleEditor-{{ d.id }}" class="collapse dept-editor-wrap">
<div class="dept-editor">
<form method="post"
action="{% url 'employees:set_member_role' emp_id=emp.id dept_id=d.id %}">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.get_full_path }}">
<select name="role_id" class="form-select form-select-sm dept-role-select">
<option value="" {% if not d.role_id %}selected{% endif %}>Без роли</option>
{% for r in d.roles_choices %}
{% if r.name %}
<option value="{{ r.id }}" {% if d.role_id == r.id %}selected{% endif %}>{{ r.name }}</option>
{% endif %}
{% endfor %}
</select>
<button type="submit" class="btn btn-sm btn-primary dept-role-save" disabled>OK</button>
<button type="button" class="btn btn-sm btn-ghost"
data-bs-toggle="collapse" data-bs-target="#roleEditor-{{ d.id }}">Отмена</button>
</form>
</div>
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
{% else %}
<div class="ios-cell"><span class="text-secondary">Отделы не указаны</span></div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{# === КАДРОВЫЕ СОБЫТИЯ (для аутентифицированных) === #}
{% if request.user.is_authenticated %}
{% with acts_src=employee_actions|default:emp.actions %}
{% with acts=acts_src.results|default:acts_src %}
{% if acts %}
<div class="mt-3 text-start">
<div class="ios-acc">
<button class="ios-collapse-trigger" type="button"
data-bs-toggle="collapse" data-bs-target="#actionsCollapse"
aria-expanded="false" aria-controls="actionsCollapse">
<span class="title"><i class="bi-journal-text me-1"></i>Кадровые события</span>
<i class="bi-chevron-down ios-collapse-caret"></i>
</button>
<div id="actionsCollapse" class="collapse">
<div class="ios-acc-body">
<div class="ios-list">
{% for a in acts %}
<div class="ios-cell">
<i class="bi-clipboard-check" style="width:1.25rem;text-align:center;opacity:.9;"></i>
<div class="flex-grow-1">
<div class="fw-bold">{% firstof a.action_display a.action %}</div>
{% if a.comment %}<div class="text-secondary">{{ a.comment }}</div>{% endif %}
<div class="small text-secondary">
<time>{% firstof a.date_display a.date|date:"d.m.Y H:i" %}</time>
</div>
{% if a.extra %}
<div class="small text-secondary mt-1">
{% for k, v in a.extra.items %}
<div>{{ k }}: {{ v }}</div>
{% endfor %}
</div>
{% endif %}
{% if a.history and a.history|length %}
<details class="mt-2 small">
<summary class="text-secondary">История</summary>
<div class="mt-2">
{% for h in a.history %}
{% with new_action=h.changes.action.new|default:a.action %}
<div class="py-2 border-top">
<div class="d-flex align-items-baseline gap-2">
<span class="fw-semibold">
{% if new_action == 'hired' %}Принят
{% elif new_action == 'dismissed' %}Уволен
{% elif new_action == 'on_leave' %}В отпуске
{% elif new_action == 'returned_from_leave' %}Вернулся из отпуска
{% elif new_action == 'on_maternity' %}В декрете
{% elif new_action == 'returned_from_maternity' %}Вернулся из декрета
{% elif new_action == 'transferred' %}Переведен
{% elif new_action == 'rehired' %}Восстановлен
{% else %}{% firstof a.action_display a.action %}{% endif %}
</span>
<span class="text-secondary">
{% firstof h.history_date_display h.history_date %}{% if h.history_user %} • {{ h.history_user }}{% endif %}
</span>
</div>
{% if h.changes %}
<div class="ms-3 mt-1">
{% for field, diff in h.changes.items %}
{% if field != 'action' %}
<div>
<span class="text-secondary">
{% if field == 'date' %}Дата события
{% elif field == 'comment' %}Комментарий
{% elif field == 'extra' %}Дополнительно
{% elif field == 'employee_id' %}Сотрудник
{% else %}{{ field }}{% endif %}
:
</span>
{% if field == 'extra' and diff.new %}
{% if diff.new.items %}
{% for ek, ev in diff.new.items %}
{% if not forloop.first %}, {% endif %}{{ ek }}: {{ ev }}
{% endfor %}
{% else %}
{{ diff.new }}
{% endif %}
{% else %}
{{ diff.new }}
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
{% endwith %}
{% endfor %}
</div>
</details>
{% endif %}
</div>
</div>
{% empty %}
<div class="ios-cell"><span class="text-secondary">Событий нет.</span></div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endwith %}
{% endwith %}
{% endif %}
{# ====== НАВЫКИ ====== #}
<div class="mt-3 text-start" id="skillsBlock"
{% if perms.employees.manage_employee_skills or request.user.is_staff or request.user.is_superuser or is_me %}
data-editable="1"
{% endif %}>
<div class="ios-section-title mb-2"><i class="bi bi-stars me-1"></i>Навыки</div>
<div class="chips align-items-center">
{% with skills=emp.skills %}
{% if skills %}
{% for s in skills %}
{% firstof s.name s as sk_name %}
{% if sk_name %}
<span class="chip chip-skill"
data-skill-id="{{ s.id|default:'' }}"
data-skill-name="{{ sk_name }}">
<span class="lbl">{{ sk_name }}</span>
{% if perms.employees.manage_employee_skills or request.user.is_staff or request.user.is_superuser or is_me %}
<button type="button" class="chip-del-btn" title="Убрать навык" aria-label="Убрать навык">
<i class="bi bi-dash-lg"></i>
</button>
{% endif %}
</span>
{% endif %}
{% endfor %}
{% else %}
<span class="skills-empty"></span>
{% endif %}
{% endwith %}
{% if perms.employees.manage_employee_skills or request.user.is_staff or request.user.is_superuser or is_me %}
<button type="button" class="chip chip-add"
data-bs-toggle="collapse" data-bs-target="#skillsForm"
aria-expanded="false" aria-controls="skillsForm" title="Добавить навык">
<i class="bi bi-plus-lg"></i><span class="d-none d-sm-inline"></span>
</button>
{% endif %}
</div>
{% if perms.employees.manage_employee_skills or request.user.is_staff or request.user.is_superuser or is_me %}
<div id="skillsForm" class="collapse mt-2">
<form id="skillAddForm" class="skill-form"
action="{% url 'employees:skill_add' emp.id %}" method="post">
{% csrf_token %}
<div class="row g-2">
<div class="col-12">
<input class="form-control" name="skill_name" list="skillsAll"
placeholder="Выберите или введите новый навык" required>
<datalist id="skillsAll">
{% for sk in all_skills %}
{% if sk.name %}<option value="{{ sk.name }}"></option>{% endif %}
{% endfor %}
</datalist>
</div>
<div class="col-12 d-flex gap-2">
<button type="submit" class="btn btn-primary btn-sm">
<i class="bi bi-plus-lg me-1"></i>Добавить
</button>
<button type="button" class="btn btn-ghost btn-sm" data-bs-toggle="collapse" data-bs-target="#skillsForm">
Отмена
</button>
</div>
</div>
</form>
</div>
{% endif %}
</div>
</div>
</article>
</div>
<!-- RIGHT -->
<div class="col-md-8">
{% if can_edit %}
<div id="editFormWrap" class="collapse mb-3">
<article class="ios-card">
<div class="p-3">
<div class="d-flex align-items-center gap-2">
<i class="bi-pencil-square"></i>
<div class="ios-section-title mb-0">Редактирование профиля</div>
<div class="ms-auto">
<button type="button" class="btn btn-sm btn-ghost"
data-bs-toggle="collapse" data-bs-target="#editFormWrap">Скрыть</button>
</div>
</div>
<div class="mt-3">
{% if is_me %}
{% url 'employees:employee_edit_me' as edit_endpoint %}
{% else %}
{% url 'employees:employee_edit' emp.id as edit_endpoint %}
{% endif %}
{% include "employees/_employee_edit.html" with emp=emp endpoint=edit_endpoint %}
</div>
</div>
</article>
</div>
{% endif %}
<section class="ios-card mb-3">
<div class="p-3">
<div class="d-flex align-items-center gap-2 mb-3">
<i class="bi-activity"></i>
<div class="ios-section-title mb-0">Активность</div>
</div>
<div id="employeePosts">
{# Посты загружаются через JavaScript из employeeProfile.js #}
{% if posts %}
{% include "feed/_feed_cards.html" with items=posts %}
{% else %}
<div class="text-secondary small">Публикаций пока нет.</div>
{% endif %}
</div>
</div>
</section>
</div>
</div>
</div>
<!-- Модальное окно для просмотра публикации -->
{% include "feed/components/modal_post_detail.html" %}
<!-- Модальное окно для увеличенного просмотра аватара -->
<div class="modal fade" id="avatarModal" tabindex="-1" aria-labelledby="avatarModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content bg-transparent border-0">
<div class="modal-header border-0 pb-0">
<button type="button" class="btn-close btn-close-white ms-auto" data-bs-dismiss="modal" aria-label="Закрыть"></button>
</div>
<div class="modal-body text-center p-0">
<img id="avatarModalImage" src="" alt="Аватар" class="img-fluid rounded" style="max-height: 80vh; object-fit: contain;">
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
{# === JavaScript логика === #}
{% include "employees/components/employee_detail_scripts.html" %}
<!-- Emoji Picker Library -->
<script type="module" src="https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js"></script>
{% endblock %}