- Заменен лишний div.container на стандартный container для единообразия - Заменен класс .card на .ios-card в секции активности для устранения конфликтов стилей - Добавлен card-list.css в base.html для корректного отображения карточек новостей - Добавлены CSS правила для правильного поведения .row и колонок в grid-layout - Исправлена проблема с выходом календаря за границы страницы - Убраны видимые скроллбары у modal-body и modal-content для чистого UI
299 lines
15 KiB
HTML
299 lines
15 KiB
HTML
{% extends "base.html" %}
|
||
{% load static django_bootstrap5 %}
|
||
|
||
{% block title %}Поиск{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<link rel="stylesheet" href="{% static 'css/components/page-header.css' %}">
|
||
<link rel="stylesheet" href="{% static 'css/components/ios-search.css' %}">
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container">
|
||
<div class="row gx-0">
|
||
<div class="col-12">
|
||
|
||
<!-- Шапка страницы -->
|
||
{% include "includes/components/page_header.html" with icon="bi-search" title="Поиск" count=total count_label="Найдено:" search_id="globalSearch" search_placeholder="Фильтр по заголовкам" no_create=True has_filters=True filters_target="searchFilters" filters_template="search/_filters.html" %}
|
||
|
||
{% if total > 0 %}
|
||
|
||
{# ===== ПУБЛИКАЦИИ ===== #}
|
||
{% if counts.post %}
|
||
<div class="card mb-3 section" data-sec="post">
|
||
<div class="card-header">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-journal-text"></i></div>
|
||
<div class="card-meta flex-grow-1"><h6 class="mb-0 text-secondary">Публикации</h6></div>
|
||
</div>
|
||
<div class="list-results">
|
||
{% for item in results %}
|
||
{% if item.model_name == 'post' %}
|
||
{% with post=item.object %}
|
||
<div class="result-row d-flex align-items-start gap-2 py-2 px-2" data-type="post" data-haystack="{{ post.title|default_if_none:'' }}">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-journal-text"></i></div>
|
||
<div class="flex-grow-1 position-relative">
|
||
<a href="{% url 'feed:post_detail' post.pk %}" class="card-title text-decoration-none stretched-link">{{ post.title }}</a>
|
||
<div class="card-subtitle mt-1 text-secondary">{{ post.created_at|date:"d.m.Y H:i" }}</div>
|
||
{% if post.body %}<div class="small mt-1 text-body">{{ post.body|truncatewords:35 }}</div>{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endwith %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# ===== СОТРУДНИКИ ===== #}
|
||
{% if counts.employee %}
|
||
<div class="card mb-3 section" data-sec="employee">
|
||
<div class="card-header">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-person-badge"></i></div>
|
||
<div class="card-meta flex-grow-1"><h6 class="mb-0 text-secondary">Сотрудники</h6></div>
|
||
</div>
|
||
<div class="list-results">
|
||
{% for item in results %}
|
||
{% if item.model_name == 'employee' %}
|
||
{% with emp=item.object %}
|
||
<div class="result-row d-flex align-items-start gap-2 py-2 px-2" data-type="employee" data-haystack="{{ emp.last_name }} {{ emp.first_name }} {{ emp.patronymic|default_if_none:'' }}">
|
||
<div class="card-icon" style="width:44px;height:44px;">
|
||
{% if emp.avatar %}<img src="{{ emp.avatar.url }}" alt="">{% else %}<i class="bi-person"></i>{% endif %}
|
||
</div>
|
||
<div class="flex-grow-1 position-relative">
|
||
<a href="{% url 'employees:employee_detail' emp.pk %}" class="card-title text-decoration-none stretched-link">
|
||
{{ emp.last_name }} {{ emp.first_name }}{% if emp.patronymic %} {{ emp.patronymic }}{% endif %}
|
||
</a>
|
||
<div class="card-subtitle mt-1 text-secondary">
|
||
{% if emp.position %}{{ emp.position.name }} · {% endif %}
|
||
{{ emp.email }}{% if emp.phone_number %} · {{ emp.phone_number }}{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endwith %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# ===== ОТДЕЛЫ ===== #}
|
||
{% if counts.department %}
|
||
<div class="card mb-3 section" data-sec="department">
|
||
<div class="card-header">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-diagram-3"></i></div>
|
||
<div class="card-meta flex-grow-1"><h6 class="mb-0 text-secondary">Отделы</h6></div>
|
||
</div>
|
||
<div class="list-results">
|
||
{% for item in results %}
|
||
{% if item.model_name == 'department' %}
|
||
{% with dept=item.object %}
|
||
<div class="result-row d-flex align-items-start gap-2 py-2 px-2" data-type="department" data-haystack="{{ dept.name|default_if_none:'' }}">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-diagram-3"></i></div>
|
||
<div class="flex-grow-1 position-relative">
|
||
<a href="{% url 'employees:department_detail' dept.pk %}" class="card-title text-decoration-none stretched-link">{{ dept.name }}</a>
|
||
<div class="card-subtitle mt-1 text-secondary">
|
||
{% if dept.head %}Руководитель: {{ dept.head.last_name }} {{ dept.head.first_name }}{% if dept.head.patronymic %} {{ dept.head.patronymic }}{% endif %}{% endif %}
|
||
</div>
|
||
{% if dept.description %}<div class="small mt-1 text-body">{{ dept.description|truncatewords:35 }}</div>{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endwith %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# ===== ЗАЯВЛЕНИЯ ===== #}
|
||
{% if counts.request %}
|
||
<div class="card mb-3 section" data-sec="request">
|
||
<div class="card-header">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-file-earmark-text"></i></div>
|
||
<div class="card-meta flex-grow-1"><h6 class="mb-0 text-secondary">Заявления</h6></div>
|
||
</div>
|
||
<div class="list-results">
|
||
{% for item in results %}
|
||
{% if item.model_name == 'request' %}
|
||
{% with req=item.object %}
|
||
<div class="result-row d-flex align-items-start gap-2 py-2 px-2" data-type="request" data-haystack="{% firstof req.title req.get_type_display '' %}">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-file-earmark-text"></i></div>
|
||
<div class="flex-grow-1 position-relative">
|
||
<div class="card-title">
|
||
{% firstof req.title req.get_type_display "Заявление" %}
|
||
</div>
|
||
<div class="card-subtitle mt-1 text-secondary">
|
||
Автор: {{ req.employee }}{% if req.department %} · Отдел: {{ req.department }}{% endif %} · {{ req.created_at|date:"d.m.Y H:i" }}
|
||
</div>
|
||
{% if req.comment %}<div class="small mt-1 text-body">{{ req.comment|truncatewords:35 }}</div>{% endif %}
|
||
</div>
|
||
<div class="ms-2 mt-1">
|
||
{% if req.status == 'pending' %}<span class="badge bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle">На рассмотрении</span>
|
||
{% elif req.status == 'approved' %}<span class="badge bg-success-subtle text-success-emphasis border border-success-subtle">Одобрено</span>
|
||
{% elif req.status == 'rejected' %}<span class="badge bg-danger-subtle text-danger-emphasis border border-danger-subtle">Отклонено</span>
|
||
{% elif req.status == 'cancelled' %}<span class="badge bg-dark-subtle text-dark-emphasis border border-dark-subtle">Отменено</span>
|
||
{% elif req.status == 'draft' %}<span class="badge bg-light text-body border">Черновик</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endwith %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# ===== ЧАТЫ ===== #}
|
||
{% if counts.chat %}
|
||
<div class="card mb-3 section" data-sec="chat">
|
||
<div class="card-header">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-chat-dots"></i></div>
|
||
<div class="card-meta flex-grow-1"><h6 class="mb-0 text-secondary">Чаты</h6></div>
|
||
</div>
|
||
<div class="list-results">
|
||
{% for item in results %}
|
||
{% if item.model_name == 'chat' %}
|
||
{% with chat=item.object %}
|
||
{% firstof chat.name chat.department.name chat.pk as chat_label %}
|
||
<div class="result-row d-flex align-items-start gap-2 py-2 px-2" data-type="chat" data-haystack="{{ chat_label }}">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-chat-dots"></i></div>
|
||
<div class="flex-grow-1 position-relative">
|
||
<a href="{% url 'communications:chat_detail' chat.pk %}" class="card-title text-decoration-none stretched-link">
|
||
{% if chat.type == 'global' %}
|
||
Глобальный чат
|
||
{% elif chat.type == 'department' and chat.department %}
|
||
Чат отдела: {{ chat.department.name }}
|
||
{% elif chat.type == 'private' %}
|
||
{% for u in chat.participants.all %}{% if u != request.user %}{% if not forloop.first %}, {% endif %}{{ u.get_full_name|default:u.username }}{% endif %}{% endfor %}
|
||
{% else %}
|
||
{{ chat_label }}
|
||
{% endif %}
|
||
</a>
|
||
</div>
|
||
</div>
|
||
{% endwith %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# ===== СООБЩЕНИЯ ===== #}
|
||
{% if counts.message %}
|
||
<div class="card mb-3 section" data-sec="message">
|
||
<div class="card-header">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-chat-left-text"></i></div>
|
||
<div class="card-meta flex-grow-1"><h6 class="mb-0 text-secondary">Сообщения</h6></div>
|
||
</div>
|
||
<div class="list-results">
|
||
{% for item in results %}
|
||
{% if item.model_name == 'message' %}
|
||
{% with msg=item.object %}
|
||
{% firstof msg.chat.name msg.chat.department.name msg.chat.pk as msg_chat_label %}
|
||
<div class="result-row d-flex align-items-start gap-2 py-2 px-2" data-type="message" data-haystack="{{ msg.content|default_if_none:'' }}">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-chat-left-text"></i></div>
|
||
<div class="flex-grow-1 position-relative">
|
||
<a href="{% url 'communications:chat_detail' msg.chat.pk %}" class="card-title text-decoration-none stretched-link">
|
||
{% if msg.author %}{{ msg.author.get_full_name|default:msg.author.username }}: {% endif %}
|
||
{{ msg.content|default:"(вложение)"|truncatechars:120 }}
|
||
</a>
|
||
<div class="card-subtitle mt-1 text-secondary">
|
||
{{ msg.created_at|date:"d.m.Y H:i" }} · Чат: {{ msg_chat_label }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endwith %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# ===== СОБЫТИЯ ===== #}
|
||
{% if counts.event %}
|
||
<div class="card mb-3 section" data-sec="event">
|
||
<div class="card-header">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-calendar-event"></i></div>
|
||
<div class="card-meta flex-grow-1"><h6 class="mb-0 text-secondary">События</h6></div>
|
||
</div>
|
||
<div class="list-results">
|
||
{% for item in results %}
|
||
{% if item.model_name == 'event' %}
|
||
{% with ev=item.object %}
|
||
{% firstof ev.title ev.name "Событие" as ev_title %}
|
||
<div class="result-row d-flex align-items-start gap-2 py-2 px-2" data-type="event" data-haystack="{{ ev_title }}">
|
||
<div class="card-icon" style="width:44px;height:44px;"><i class="bi-calendar-event"></i></div>
|
||
<div class="flex-grow-1 position-relative">
|
||
<div class="card-title">{{ ev_title }}</div>
|
||
<div class="card-subtitle mt-1 text-secondary">
|
||
{% if ev.start %}Начало: {{ ev.start|date:"d.m.Y H:i" }}
|
||
{% elif ev.starts_at %}Начало: {{ ev.starts_at|date:"d.m.Y H:i" }}{% endif %}
|
||
{% if ev.location %} · {{ ev.location }}{% endif %}
|
||
{% if ev.department %} · {% firstof ev.department.name ev.department " " %}{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endwith %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% else %}
|
||
<div class="alert alert-info shadow-sm mt-3">Ничего не найдено.</div>
|
||
{% endif %}
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script type="module">
|
||
import { initSearchFilter } from "{% static 'js/components/searchFilterHandler.js' %}";
|
||
|
||
// Инициализация поиска по заголовкам
|
||
initSearchFilter({
|
||
inputId: 'globalSearch',
|
||
clearSelector: '.ios-search-clear',
|
||
sectionSelector: '.section',
|
||
rowSelector: '.result-row'
|
||
});
|
||
|
||
// Фильтрация по типу через селект
|
||
const typeFilter = document.getElementById('searchTypeFilter');
|
||
const applyBtn = document.getElementById('applySearchFilter');
|
||
const resetBtn = document.getElementById('resetSearchFilter');
|
||
const sections = document.querySelectorAll('.section');
|
||
|
||
function applyTypeFilter() {
|
||
const selectedType = typeFilter.value;
|
||
|
||
sections.forEach(section => {
|
||
const sectionType = section.dataset.sec;
|
||
if (selectedType === 'all' || sectionType === selectedType) {
|
||
section.style.display = '';
|
||
} else {
|
||
section.style.display = 'none';
|
||
}
|
||
});
|
||
}
|
||
|
||
function resetFilter() {
|
||
typeFilter.value = 'all';
|
||
applyTypeFilter();
|
||
}
|
||
|
||
if (applyBtn) {
|
||
applyBtn.addEventListener('click', applyTypeFilter);
|
||
}
|
||
|
||
if (resetBtn) {
|
||
resetBtn.addEventListener('click', resetFilter);
|
||
}
|
||
|
||
// Применение при изменении селекта
|
||
if (typeFilter) {
|
||
typeFilter.addEventListener('change', applyTypeFilter);
|
||
}
|
||
</script>
|
||
{% endblock %}
|