feat: переделана страница профиля на AJAX загрузку
- Создан новый JS модуль employeeProfile.js - Аватар сотрудника загружается через API с глобальными URL - Посты сотрудника загружаются через FeedList с правильными URL медиа - Добавлены ID контейнеров для динамической загрузки (#employeeAvatar, #employeeName, #employeePosts) - Подключены скрипты для лайков, комментариев и модала деталей - Добавлен emoji-picker для форм комментариев - Добавлен модал для просмотра публикаций - Исправлена проблема с localhost URL в аватарах и медиа постов
This commit is contained in:
parent
6ae6516db7
commit
5d65b71c21
136
backend/static/js/components/employeeProfile.js
Normal file
136
backend/static/js/components/employeeProfile.js
Normal file
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Модуль для загрузки и отображения профиля сотрудника
|
||||
* Использует API для получения данных с корректными глобальными URL
|
||||
*/
|
||||
|
||||
export class EmployeeProfile {
|
||||
constructor(employeeId, options = {}) {
|
||||
this.employeeId = employeeId;
|
||||
this.apiUrl = `/api/v1/employees/${employeeId}/`;
|
||||
this.postsApiUrl = '/api/v1/posts/';
|
||||
|
||||
// Селекторы контейнеров
|
||||
this.avatarContainer = options.avatarContainer || '#employeeAvatar';
|
||||
this.nameContainer = options.nameContainer || '#employeeName';
|
||||
this.postsContainer = options.postsContainer || '#employeePosts';
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация - загрузка и рендеринг
|
||||
*/
|
||||
async init() {
|
||||
try {
|
||||
// Загружаем данные сотрудника
|
||||
const employee = await this.fetchEmployee();
|
||||
if (employee) {
|
||||
this.renderAvatar(employee);
|
||||
this.renderName(employee);
|
||||
}
|
||||
|
||||
// Загружаем посты сотрудника
|
||||
await this.loadPosts();
|
||||
} catch (error) {
|
||||
console.error('Failed to load employee profile:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузка данных сотрудника через API
|
||||
*/
|
||||
async fetchEmployee() {
|
||||
const response = await fetch(this.apiUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Рендеринг аватара
|
||||
*/
|
||||
renderAvatar(employee) {
|
||||
const container = document.querySelector(this.avatarContainer);
|
||||
if (!container) return;
|
||||
|
||||
const avatarHtml = this.createAvatarHtml(employee);
|
||||
container.innerHTML = avatarHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создание HTML аватара
|
||||
*/
|
||||
createAvatarHtml(employee) {
|
||||
if (employee.avatar) {
|
||||
return `
|
||||
<img src="${employee.avatar}"
|
||||
alt="Аватар ${employee.last_name} ${employee.first_name}"
|
||||
class="ios-ava" style="object-fit:cover;">
|
||||
`;
|
||||
} else {
|
||||
const initial = (employee.first_name || employee.last_name || '?').charAt(0).toUpperCase();
|
||||
return `
|
||||
<div class="ios-ava--ph">
|
||||
${initial}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Рендеринг имени (опционально, если нужно обновить)
|
||||
*/
|
||||
renderName(employee) {
|
||||
const container = document.querySelector(this.nameContainer);
|
||||
if (!container) return;
|
||||
|
||||
let fullName = `${employee.last_name} ${employee.first_name}`;
|
||||
if (employee.patronymic) {
|
||||
fullName += ` ${employee.patronymic}`;
|
||||
}
|
||||
container.textContent = fullName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузка постов сотрудника
|
||||
*/
|
||||
async loadPosts() {
|
||||
const container = document.querySelector(this.postsContainer);
|
||||
if (!container) return;
|
||||
|
||||
try {
|
||||
// Динамический импорт FeedList
|
||||
const { FeedList } = await import('/static/js/components/feedList.js');
|
||||
|
||||
// Создаем экземпляр FeedList для постов сотрудника
|
||||
const feedList = new FeedList({
|
||||
containerId: this.postsContainer.replace('#', ''),
|
||||
apiUrl: this.postsApiUrl,
|
||||
params: {
|
||||
author: this.employeeId,
|
||||
ordering: '-created_at'
|
||||
},
|
||||
showLoadMore: false, // Не показываем кнопку "загрузить еще"
|
||||
postsPerPage: 10
|
||||
});
|
||||
|
||||
await feedList.init();
|
||||
|
||||
// Если постов нет, показываем сообщение
|
||||
if (feedList.posts.length === 0) {
|
||||
container.innerHTML = '<div class="text-secondary small">Публикаций пока нет.</div>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load employee posts:', error);
|
||||
container.innerHTML = '<div class="text-danger small">Не удалось загрузить публикации.</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация профиля сотрудника
|
||||
*/
|
||||
export function initEmployeeProfile(employeeId, options) {
|
||||
const profile = new EmployeeProfile(employeeId, options);
|
||||
profile.init();
|
||||
return profile;
|
||||
}
|
||||
@ -1,13 +1,28 @@
|
||||
{% load static %}
|
||||
{# === JavaScript для страницы детального просмотра сотрудника === #}
|
||||
|
||||
<script src="{% static 'js/components/postDetailModal.js' %}"></script>
|
||||
<script src="{% static 'js/components/feedLikes.js' %}"></script>
|
||||
<script src="{% static 'js/components/feedComments.js' %}"></script>
|
||||
|
||||
<script type="module">
|
||||
import { initSkillsManager } from '{% static "js/components/skillsManager.js" %}';
|
||||
import { initDeptRoleEditor } from '{% static "js/components/deptRoleEditor.js" %}';
|
||||
import { initEmployeeForm } from '{% static "js/components/employeeFormHandler.js" %}';
|
||||
import { smoothScrollTo } from '{% static "js/utils/scroll.js" %}';
|
||||
import { initEmployeeProfile } from '{% static "js/components/employeeProfile.js" %}';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 0. Загрузка данных профиля через AJAX (аватар и посты с правильными URL)
|
||||
const employeeId = '{{ emp.id }}';
|
||||
if (employeeId) {
|
||||
initEmployeeProfile(employeeId, {
|
||||
avatarContainer: '#employeeAvatar',
|
||||
nameContainer: '#employeeName',
|
||||
postsContainer: '#employeePosts'
|
||||
});
|
||||
}
|
||||
|
||||
// 1. Обработчик формы редактирования профиля
|
||||
const employeeForm = initEmployeeForm();
|
||||
console.log('[employee_detail_scripts] employeeForm initialized:', employeeForm);
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<div class="p-3 text-center">
|
||||
|
||||
<!-- Avatar -->
|
||||
<div class="ava-wrap mb-3">
|
||||
<div class="ava-wrap mb-3" id="employeeAvatar">
|
||||
{% if emp.avatar %}
|
||||
<img src="{{ emp.avatar }}"
|
||||
alt="Аватар {{ emp.last_name }} {{ emp.first_name }}"
|
||||
@ -43,7 +43,7 @@
|
||||
|
||||
<!-- Name + edit button -->
|
||||
<div class="d-flex align-items-center justify-content-center gap-2 mb-1">
|
||||
<div class="ios-name">
|
||||
<div class="ios-name" id="employeeName">
|
||||
{{ emp.last_name }} {{ emp.first_name }}{% if emp.patronymic %} {{ emp.patronymic }}{% endif %}
|
||||
</div>
|
||||
{% if can_edit %}
|
||||
@ -412,7 +412,8 @@
|
||||
<i class="bi-activity"></i>
|
||||
<div class="ios-section-title mb-0">Активность</div>
|
||||
</div>
|
||||
<div>
|
||||
<div id="employeePosts">
|
||||
{# Посты загружаются через JavaScript из employeeProfile.js #}
|
||||
{% if posts %}
|
||||
{% include "feed/_feed_cards.html" with items=posts %}
|
||||
{% else %}
|
||||
@ -424,9 +425,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Модальное окно для просмотра публикации -->
|
||||
{% include "feed/components/modal_post_detail.html" %}
|
||||
{% 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 %}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user