fix: improve recipient picker - load all users with avatars
RecipientPicker (заявления): - Added pagination support to load ALL users (not just first page) - Added avatar images from media URLs instead of base64 - Falls back to initials when no avatar available - Loads users in batches of 100 per page RecipientPickerHandler (чаты): - Added pagination support for full user list - Keeps existing avatar display logic - Loads 100 users per page when not searching - Limits to 20 results when searching CSS improvements: - Added styles for avatar images (.user-avatar-img) - Added placeholder styles for initials - Proper object-fit and border-radius for avatars
This commit is contained in:
parent
62e03ad538
commit
ff9b094dd7
@ -182,6 +182,28 @@
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Изображение аватара */
|
||||
.user-avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Плейсхолдер с инициалами */
|
||||
.user-avatar-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bs-primary);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* Информация о пользователе */
|
||||
|
||||
@ -52,16 +52,11 @@ export class RecipientPicker {
|
||||
async loadData() {
|
||||
this.state.loading = true;
|
||||
try {
|
||||
const [usersRes, deptsRes] = await Promise.all([
|
||||
fetch(this.options.apiUsersUrl),
|
||||
fetch(this.options.apiDepartmentsUrl)
|
||||
]);
|
||||
|
||||
if (usersRes.ok) {
|
||||
const usersData = await usersRes.json();
|
||||
this.users = usersData.results || usersData;
|
||||
}
|
||||
|
||||
// Загружаем пользователей с пагинацией (все страницы)
|
||||
this.users = await this.loadAllUsers();
|
||||
|
||||
// Загружаем отделы
|
||||
const deptsRes = await fetch(this.options.apiDepartmentsUrl);
|
||||
if (deptsRes.ok) {
|
||||
const deptsData = await deptsRes.json();
|
||||
this.departments = deptsData.results || deptsData;
|
||||
@ -73,6 +68,37 @@ export class RecipientPicker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузка всех пользователей с автоматической обработкой пагинации
|
||||
*/
|
||||
async loadAllUsers() {
|
||||
const allUsers = [];
|
||||
let nextUrl = this.options.apiUsersUrl + '?page_size=100&active=true';
|
||||
|
||||
while (nextUrl) {
|
||||
try {
|
||||
const response = await fetch(nextUrl);
|
||||
if (!response.ok) break;
|
||||
|
||||
const data = await response.json();
|
||||
const users = data.results || data;
|
||||
|
||||
if (Array.isArray(users)) {
|
||||
allUsers.push(...users);
|
||||
}
|
||||
|
||||
// Проверяем наличие следующей страницы
|
||||
nextUrl = data.next || null;
|
||||
} catch (error) {
|
||||
console.error('RecipientPicker: Error loading users page:', error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`RecipientPicker: Loaded ${allUsers.length} users`);
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.container.innerHTML = `
|
||||
<div class="recipient-picker-email">
|
||||
@ -255,9 +281,17 @@ export class RecipientPicker {
|
||||
const displayName = `${user.last_name} ${user.first_name}`;
|
||||
const subtitle = user.position || user.email || '';
|
||||
|
||||
// Аватар или инициалы
|
||||
let avatarHtml;
|
||||
if (user.avatar) {
|
||||
avatarHtml = `<img src="${user.avatar}" alt="${displayName}" class="user-avatar-img">`;
|
||||
} else {
|
||||
avatarHtml = `<div class="user-avatar-placeholder">${this.getInitials(user)}</div>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="dropdown-item-user" data-action="add-${type}" data-user-id="${user.id}">
|
||||
<div class="user-avatar-small">${this.getInitials(user)}</div>
|
||||
<div class="user-avatar-small">${avatarHtml}</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">${displayName}</div>
|
||||
${subtitle ? `<div class="user-subtitle">${subtitle}</div>` : ''}
|
||||
|
||||
@ -127,21 +127,39 @@ export class RecipientPicker {
|
||||
|
||||
if (this.searchQuery) {
|
||||
url.searchParams.set('search', this.searchQuery);
|
||||
// При поиске ограничиваем результаты
|
||||
url.searchParams.set('page_size', '20');
|
||||
} else {
|
||||
// Без поиска загружаем все страницы
|
||||
url.searchParams.set('page_size', '100');
|
||||
url.searchParams.set('active', 'true');
|
||||
}
|
||||
|
||||
url.searchParams.set('ordering', 'last_name,first_name');
|
||||
url.searchParams.set('page_size', '20');
|
||||
|
||||
try {
|
||||
const response = await fetch(url.toString(), { headers: this.headers });
|
||||
let allItems = [];
|
||||
let nextUrl = url.toString();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('HTTP ' + response.status);
|
||||
}
|
||||
// Загружаем все страницы пагинации
|
||||
while (nextUrl) {
|
||||
const response = await fetch(nextUrl, { headers: this.headers });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('HTTP ' + response.status);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const items = Array.isArray(data) ? data : (data.results || []);
|
||||
this._renderResults(items);
|
||||
const data = await response.json();
|
||||
const items = Array.isArray(data) ? data : (data.results || []);
|
||||
allItems.push(...items);
|
||||
|
||||
// Проверяем наличие следующей страницы
|
||||
// При поиске загружаем только первую страницу
|
||||
nextUrl = (!this.searchQuery && data.next) ? data.next : null;
|
||||
}
|
||||
|
||||
console.log(`RecipientPickerHandler: Loaded ${allItems.length} employees`);
|
||||
this._renderResults(allItems);
|
||||
} catch (error) {
|
||||
this.elResults.innerHTML = '<div class="rp-empty">Не удалось загрузить сотрудников</div>';
|
||||
console.error('RecipientPicker fetch error:', error);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user