diff --git a/backend/static/js/components/employeesList.js b/backend/static/js/components/employeesList.js
new file mode 100644
index 00000000..3687c592
--- /dev/null
+++ b/backend/static/js/components/employeesList.js
@@ -0,0 +1,223 @@
+/**
+ * Модуль для загрузки и отображения списка сотрудников
+ * Использует API для получения данных с корректными глобальными URL
+ */
+
+export class EmployeesList {
+ constructor(options = {}) {
+ this.containerId = options.containerId || 'empList';
+ this.container = document.getElementById(this.containerId);
+ if (!this.container) {
+ console.warn(`Container #${this.containerId} not found`);
+ return;
+ }
+
+ this.apiUrl = '/api/v1/employees/';
+ this.employees = [];
+ this.loading = false;
+
+ // Параметры из URL
+ const urlParams = new URLSearchParams(window.location.search);
+ this.params = {
+ page: urlParams.get('page') || 1,
+ ordering: urlParams.get('o') || 'last_name',
+ search: urlParams.get('q') || '',
+ department: urlParams.get('department') || '',
+ position: urlParams.get('position') || '',
+ is_active: urlParams.get('is_active') || ''
+ };
+ }
+
+ /**
+ * Инициализация - загрузка и рендеринг
+ */
+ async init() {
+ if (!this.container) return;
+
+ try {
+ await this.loadEmployees();
+ this.render();
+ } catch (error) {
+ console.error('Failed to load employees:', error);
+ this.renderError();
+ }
+ }
+
+ /**
+ * Загрузка сотрудников через API
+ */
+ async loadEmployees() {
+ if (this.loading) return;
+ this.loading = true;
+
+ const params = new URLSearchParams();
+ Object.entries(this.params).forEach(([key, value]) => {
+ if (value) params.append(key, value);
+ });
+
+ const response = await fetch(`${this.apiUrl}?${params}`);
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const data = await response.json();
+ this.employees = data.results || data;
+ this.loading = false;
+ }
+
+ /**
+ * Рендеринг списка сотрудников
+ */
+ render() {
+ if (this.employees.length === 0) {
+ this.container.innerHTML = '
Нет сотрудников.
';
+ return;
+ }
+
+ const itemsHtml = this.employees.map(emp => this.createEmployeeCard(emp)).join('');
+ this.container.innerHTML = itemsHtml;
+ }
+
+ /**
+ * Создание карточки сотрудника
+ */
+ createEmployeeCard(emp) {
+ const fullName = this.getFullName(emp);
+ const position = this.getPosition(emp);
+ const departments = this.getDepartments(emp);
+ const avatar = this.getAvatarHtml(emp);
+
+ return `
+
+
+
+
+
+ `;
+ }
+
+ /**
+ * Получение полного имени
+ */
+ getFullName(emp) {
+ let name = `${emp.last_name || ''} ${emp.first_name || ''}`.trim();
+ if (emp.patronymic) {
+ name += ` ${emp.patronymic}`;
+ }
+ return name || 'Сотрудник';
+ }
+
+ /**
+ * Получение должности
+ */
+ getPosition(emp) {
+ if (emp.position?.name) {
+ return emp.position.name;
+ } else if (emp.position_label) {
+ return emp.position_label;
+ } else if (emp.position) {
+ return `Должность #${emp.position}`;
+ }
+ return '';
+ }
+
+ /**
+ * Получение отделов
+ */
+ getDepartments(emp) {
+ if (!emp.departments || emp.departments.length === 0) {
+ return '';
+ }
+
+ return emp.departments
+ .filter(d => d.name)
+ .map(d => {
+ if (d.id) {
+ return `${this.escapeHtml(d.name)}`;
+ }
+ return this.escapeHtml(d.name);
+ })
+ .join(', ');
+ }
+
+ /**
+ * Получение HTML аватара
+ */
+ getAvatarHtml(emp) {
+ if (emp.avatar) {
+ return `
`;
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Получение кнопок действий
+ */
+ getActionButtons(emp) {
+ if (!emp.id) return '';
+
+ // Получаем ID текущего пользователя из глобальной переменной или data-атрибута
+ const currentUserId = window.currentUserId || document.body.dataset.userId;
+ if (currentUserId && String(currentUserId) === String(emp.id)) {
+ return ''; // Не показываем кнопки для себя
+ }
+
+ return `
+
+ Чат
+
+ `;
+ }
+
+ /**
+ * Экранирование HTML
+ */
+ escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ /**
+ * Рендеринг ошибки
+ */
+ renderError() {
+ this.container.innerHTML = `
+
+ Не удалось загрузить список сотрудников. Попробуйте обновить страницу.
+
+ `;
+ }
+}
+
+/**
+ * Инициализация списка сотрудников
+ */
+export function initEmployeesList(options) {
+ const list = new EmployeesList(options);
+ list.init();
+ return list;
+}
diff --git a/backend/templates/employees/employees_list.html b/backend/templates/employees/employees_list.html
index 96b50e9c..50fdc7f6 100644
--- a/backend/templates/employees/employees_list.html
+++ b/backend/templates/employees/employees_list.html
@@ -22,6 +22,7 @@
+ {# Список загружается через JavaScript из employeesList.js #}
{% if employees %}
{% for emp in employees %}
import { ListFilter, createDataAttrMatcher } from '{% static "js/components/listFilter.js" %}';
import { initCreateEmployeeModal } from '{% static "js/components/createEmployeeModal.js" %}';
+ import { initEmployeesList } from '{% static "js/components/employeesList.js" %}';
- // Инициализация фильтра списка
- new ListFilter({
- listSelector: '#empList',
- itemSelector: '.emp-row',
- searchInputSelector: '#empFilter',
- clearButtonSelector: '.ios-search-clear',
- matchFn: createDataAttrMatcher(['name', 'depts']),
- debounceMs: 300
- });
-
- // Инициализация модала создания сотрудника
- initCreateEmployeeModal({
- submitUrl: '{% url "employees:employee_create_modal" %}'
+ document.addEventListener('DOMContentLoaded', () => {
+ // Передаем ID текущего пользователя в глобальную переменную
+ window.currentUserId = {{ user.id|default:'null' }};
+
+ // Инициализация списка сотрудников через AJAX
+ const employeesList = initEmployeesList({
+ containerId: 'empList'
+ });
+
+ // Инициализация фильтра списка (работает с data-атрибутами)
+ new ListFilter({
+ listSelector: '#empList',
+ itemSelector: '.emp-row',
+ searchInputSelector: '#empFilter',
+ clearButtonSelector: '.ios-search-clear',
+ matchFn: createDataAttrMatcher(['name', 'depts']),
+ debounceMs: 300
+ });
+
+ // Инициализация модала создания сотрудника
+ initCreateEmployeeModal({
+ submitUrl: '{% url "employees:employee_create_modal" %}'
+ });
});
{% endblock %}