diff --git a/backend/static/js/components/employeesList.js b/backend/static/js/components/employeesList.js index 3687c592..832887d2 100644 --- a/backend/static/js/components/employeesList.js +++ b/backend/static/js/components/employeesList.js @@ -15,11 +15,13 @@ export class EmployeesList { this.apiUrl = '/api/v1/employees/'; this.employees = []; this.loading = false; + this.currentPage = 1; + this.hasMore = true; + this.nextUrl = null; // Параметры из 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') || '', @@ -37,6 +39,7 @@ export class EmployeesList { try { await this.loadEmployees(); this.render(); + this.setupInfiniteScroll(); } catch (error) { console.error('Failed to load employees:', error); this.renderError(); @@ -46,11 +49,17 @@ export class EmployeesList { /** * Загрузка сотрудников через API */ - async loadEmployees() { - if (this.loading) return; + async loadEmployees(append = false) { + if (this.loading || (!this.hasMore && append)) return; this.loading = true; + // Показываем loader + if (append) { + this.showLoadMoreSpinner(); + } + const params = new URLSearchParams(); + params.append('page', this.currentPage); Object.entries(this.params).forEach(([key, value]) => { if (value) params.append(key, value); }); @@ -61,8 +70,106 @@ export class EmployeesList { } const data = await response.json(); - this.employees = data.results || data; + const newEmployees = data.results || data; + + if (append) { + this.employees = [...this.employees, ...newEmployees]; + } else { + this.employees = newEmployees; + } + + // Обновляем информацию о пагинации + this.nextUrl = data.next; + this.hasMore = !!data.next; + this.loading = false; + + // Убираем loader + if (append) { + this.hideLoadMoreSpinner(); + } + } + + /** + * Настройка бесконечной прокрутки + */ + setupInfiniteScroll() { + const observer = new IntersectionObserver( + (entries) => { + const lastEntry = entries[0]; + if (lastEntry.isIntersecting && this.hasMore && !this.loading) { + this.loadMore(); + } + }, + { + root: null, + rootMargin: '100px', + threshold: 0.1 + } + ); + + // Создаем маркер для отслеживания + const sentinel = document.createElement('div'); + sentinel.id = 'empListSentinel'; + sentinel.style.height = '1px'; + this.container.parentElement.appendChild(sentinel); + + observer.observe(sentinel); + this.infiniteScrollObserver = observer; + } + + /** + * Загрузка следующей страницы + */ + async loadMore() { + if (!this.hasMore || this.loading) return; + + this.currentPage++; + await this.loadEmployees(true); + this.appendNewEmployees(); + } + + /** + * Добавление новых сотрудников в конец списка + */ + appendNewEmployees() { + // Находим индекс последнего отрендеренного сотрудника + const currentCount = this.container.querySelectorAll('.emp-row').length; + const newEmployees = this.employees.slice(currentCount); + + if (newEmployees.length === 0) return; + + const newItemsHtml = newEmployees.map(emp => this.createEmployeeCard(emp)).join(''); + this.container.insertAdjacentHTML('beforeend', newItemsHtml); + } + + /** + * Показать spinner загрузки + */ + showLoadMoreSpinner() { + let spinner = document.getElementById('empListSpinner'); + if (!spinner) { + spinner = document.createElement('div'); + spinner.id = 'empListSpinner'; + spinner.className = 'text-center py-3'; + spinner.innerHTML = ` +
+ Загрузка... +
+ `; + this.container.parentElement.appendChild(spinner); + } + spinner.style.display = 'block'; + } + + /** + * Скрыть spinner загрузки + */ + hideLoadMoreSpinner() { + const spinner = document.getElementById('empListSpinner'); + if (spinner) { + spinner.style.display = 'none'; + } } /** diff --git a/backend/templates/employees/employees_list.html b/backend/templates/employees/employees_list.html index f8a9219d..2b5fb016 100644 --- a/backend/templates/employees/employees_list.html +++ b/backend/templates/employees/employees_list.html @@ -31,25 +31,6 @@ - {# Пагинация #} - {% if next_url or prev_url %} -
- - Назад - - {% if count %} - Всего: {{ count }} - {% endif %} - - Вперёд - -
- {% endif %} -