добавление поиска
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2025-09-29 21:45:50 +03:00
parent dc7713ce1f
commit 6bb675ea78
+269 -2
View File
@@ -357,6 +357,31 @@
.scrollable-table-body {
box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);
}
/* Стили для подсветки результатов поиска */
.search-highlight {
background-color: #fff3cd !important;
border-left: 4px solid #ffc107 !important;
animation: searchPulse 0.5s ease-in-out;
}
@keyframes searchPulse {
0% { background-color: #fff3cd; }
50% { background-color: #ffeaa7; }
100% { background-color: #fff3cd; }
}
/* Стили для полей поиска */
.input-group .form-control:focus {
border-color: #86b7fe;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.input-group .btn-outline-secondary:hover {
background-color: #6c757d;
border-color: #6c757d;
color: white;
}
</style>
</head>
<body>
@@ -444,7 +469,16 @@
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Список постов</h5>
<div>
<div class="d-flex align-items-center">
<div class="input-group me-2" style="width: 200px;">
<input type="text" class="form-control form-control-sm" id="posts-search" placeholder="Поиск...">
<button class="btn btn-outline-secondary btn-sm" type="button" id="posts-search-prev">
<i class="fas fa-chevron-up"></i>
</button>
<button class="btn btn-outline-secondary btn-sm" type="button" id="posts-search-next">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<button class="btn btn-sm btn-success" id="add-post-btn">
<i class="fas fa-plus"></i> Добавить
</button>
@@ -528,7 +562,16 @@
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Список событий</h5>
<div>
<div class="d-flex align-items-center">
<div class="input-group me-2" style="width: 200px;">
<input type="text" class="form-control form-control-sm" id="events-search" placeholder="Поиск...">
<button class="btn btn-outline-secondary btn-sm" type="button" id="events-search-prev">
<i class="fas fa-chevron-up"></i>
</button>
<button class="btn btn-outline-secondary btn-sm" type="button" id="events-search-next">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<button class="btn btn-sm btn-success" id="add-event-btn-sm">
<i class="fas fa-plus"></i> Добавить
</button>
@@ -3034,6 +3077,230 @@ document.addEventListener('DOMContentLoaded', () => {
alert('Ошибка при сохранении изменений');
}
});
// Функции поиска
let postsSearchResults = [];
let eventsSearchResults = [];
let currentPostsSearchIndex = -1;
let currentEventsSearchIndex = -1;
// Поиск по постам
function searchPosts(query) {
if (!query.trim()) {
postsSearchResults = [];
currentPostsSearchIndex = -1;
clearPostsSearchHighlight();
return;
}
const posts = document.querySelectorAll('.post-row');
postsSearchResults = [];
posts.forEach((post, index) => {
const postId = post.dataset.id;
const shortname = post.querySelector('.shortname-preview')?.textContent || '';
const textPreview = post.querySelector('.post-text-preview')?.textContent || '';
const searchText = `${shortname} ${textPreview}`.toLowerCase();
if (searchText.includes(query.toLowerCase())) {
postsSearchResults.push({
element: post,
index: index,
id: postId
});
}
});
currentPostsSearchIndex = postsSearchResults.length > 0 ? 0 : -1;
highlightSearchResults('posts');
}
// Поиск по событиям (расширенный с тэгами)
async function searchEvents(query) {
if (!query.trim()) {
eventsSearchResults = [];
currentEventsSearchIndex = -1;
clearEventsSearchHighlight();
return;
}
try {
// Получаем все события из API для поиска по тэгам
const response = await fetch(`${PREFIX}/api/events`);
const allEvents = await response.json();
const events = document.querySelectorAll('.event-row');
eventsSearchResults = [];
events.forEach((event, index) => {
const eventId = event.dataset.id;
const name = event.querySelector('.event-name-preview')?.textContent || '';
const unitName = event.querySelector('.shortname-preview')?.textContent || '';
// Находим полные данные события для поиска по тэгам
const eventData = allEvents.find(e => e.id == eventId);
const tags = eventData?.tags || '';
const searchText = `${name} ${unitName} ${tags}`.toLowerCase();
if (searchText.includes(query.toLowerCase())) {
eventsSearchResults.push({
element: event,
index: index,
id: eventId
});
}
});
currentEventsSearchIndex = eventsSearchResults.length > 0 ? 0 : -1;
highlightSearchResults('events');
} catch (error) {
console.error('Ошибка при поиске событий:', error);
// Fallback к простому поиску без тэгов
searchEventsSimple(query);
}
}
// Простой поиск по событиям (без тэгов)
function searchEventsSimple(query) {
const events = document.querySelectorAll('.event-row');
eventsSearchResults = [];
events.forEach((event, index) => {
const eventId = event.dataset.id;
const name = event.querySelector('.event-name-preview')?.textContent || '';
const unitName = event.querySelector('.shortname-preview')?.textContent || '';
const searchText = `${name} ${unitName}`.toLowerCase();
if (searchText.includes(query.toLowerCase())) {
eventsSearchResults.push({
element: event,
index: index,
id: eventId
});
}
});
currentEventsSearchIndex = eventsSearchResults.length > 0 ? 0 : -1;
highlightSearchResults('events');
}
// Подсветка результатов поиска
function highlightSearchResults(type) {
if (type === 'posts') {
clearPostsSearchHighlight();
if (currentPostsSearchIndex >= 0 && currentPostsSearchIndex < postsSearchResults.length) {
const result = postsSearchResults[currentPostsSearchIndex];
result.element.classList.add('search-highlight');
result.element.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
// Выделяем найденный пост
highlightSelectedPost(result.id);
}
} else if (type === 'events') {
clearEventsSearchHighlight();
if (currentEventsSearchIndex >= 0 && currentEventsSearchIndex < eventsSearchResults.length) {
const result = eventsSearchResults[currentEventsSearchIndex];
result.element.classList.add('search-highlight');
result.element.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
// Выделяем найденное событие
highlightSelectedEvent(result.id);
}
}
}
// Очистка подсветки поиска постов
function clearPostsSearchHighlight() {
document.querySelectorAll('.post-row.search-highlight').forEach(row => {
row.classList.remove('search-highlight');
});
}
// Очистка подсветки поиска событий
function clearEventsSearchHighlight() {
document.querySelectorAll('.event-row.search-highlight').forEach(row => {
row.classList.remove('search-highlight');
});
}
// Навигация по результатам поиска постов
function navigatePostsSearch(direction) {
if (postsSearchResults.length === 0) return;
if (direction === 'next') {
currentPostsSearchIndex = (currentPostsSearchIndex + 1) % postsSearchResults.length;
} else if (direction === 'prev') {
currentPostsSearchIndex = currentPostsSearchIndex <= 0 ? postsSearchResults.length - 1 : currentPostsSearchIndex - 1;
}
highlightSearchResults('posts');
}
// Навигация по результатам поиска событий
function navigateEventsSearch(direction) {
if (eventsSearchResults.length === 0) return;
if (direction === 'next') {
currentEventsSearchIndex = (currentEventsSearchIndex + 1) % eventsSearchResults.length;
} else if (direction === 'prev') {
currentEventsSearchIndex = currentEventsSearchIndex <= 0 ? eventsSearchResults.length - 1 : currentEventsSearchIndex - 1;
}
highlightSearchResults('events');
}
// Обработчики событий для поиска постов
document.getElementById('posts-search').addEventListener('input', (e) => {
searchPosts(e.target.value);
});
document.getElementById('posts-search-prev').addEventListener('click', () => {
navigatePostsSearch('prev');
});
document.getElementById('posts-search-next').addEventListener('click', () => {
navigatePostsSearch('next');
});
// Обработчики событий для поиска событий
document.getElementById('events-search').addEventListener('input', (e) => {
searchEvents(e.target.value);
});
document.getElementById('events-search-prev').addEventListener('click', () => {
navigateEventsSearch('prev');
});
document.getElementById('events-search-next').addEventListener('click', () => {
navigateEventsSearch('next');
});
// Обработчики клавиш для поиска
document.getElementById('posts-search').addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
navigatePostsSearch('next');
} else if (e.key === 'ArrowUp') {
e.preventDefault();
navigatePostsSearch('prev');
} else if (e.key === 'ArrowDown') {
e.preventDefault();
navigatePostsSearch('next');
}
});
document.getElementById('events-search').addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
navigateEventsSearch('next');
} else if (e.key === 'ArrowUp') {
e.preventDefault();
navigateEventsSearch('prev');
} else if (e.key === 'ArrowDown') {
e.preventDefault();
navigateEventsSearch('next');
}
});
});
// Убедимся, что функция доступна глобально, если потребуется вызвать из HTML
window.resetLayout = resetLayout;