Версия 1.2.7 Добавлен поиск по имени при выборе авторов и фотографов статьи.

This commit is contained in:
anibilag 2025-10-31 12:19:33 +03:00
parent 790f8aa8e7
commit 898fc159c9
2 changed files with 72 additions and 13 deletions

View File

@ -1,7 +1,7 @@
{
"name": "vite-react-typescript-starter",
"private": true,
"version": "1.2.6",
"version": "1.2.7",
"type": "module",
"scripts": {
"dev": "vite",

View File

@ -1,6 +1,6 @@
import React, { useState, useMemo } from 'react';
import React, { useState, useMemo, useEffect } from 'react';
import { Author, AuthorRole } from '../types';
import { X, ChevronLeft, ChevronRight } from 'lucide-react';
import { X, ChevronLeft, ChevronRight, Search } from 'lucide-react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
interface AuthorSelectionModalProps {
@ -28,6 +28,7 @@ const AuthorSelectionModal: React.FC<AuthorSelectionModalProps> = ({
const [activeTab, setActiveTab] = useState<AuthorRole>(AuthorRole.WRITER);
const [selectedAuthorId, setSelectedAuthorId] = useState<string | null>(null);
const [currentPage, setCurrentPage] = useState(1);
const [searchTerm, setSearchTerm] = useState('');
const itemsPerPage = 10;
// Используем useMemo для оптимальной фильтрации и сортировки авторов
@ -50,31 +51,48 @@ const AuthorSelectionModal: React.FC<AuthorSelectionModalProps> = ({
return { popularAuthors, otherAuthors };
}, [authors, existingAuthors, activeTab]);
// Вычисляем данные для пагинации только для остальных авторов
const paginationData = useMemo(() => {
const showPagination = otherAuthors.length > itemsPerPage;
// Фильтруем остальных авторов по поисковому запросу
const filteredOtherAuthors = useMemo(() => {
if (!searchTerm.trim()) {
return otherAuthors;
}
// Если пагинация не нужна, показываем всех остальных авторов
const searchLower = searchTerm.toLowerCase().trim();
return otherAuthors.filter(author =>
author.displayName.toLowerCase().startsWith(searchLower)
);
}, [otherAuthors, searchTerm]);
// Вычисляем данные для пагинации только для отфильтрованных остальных авторов
const paginationData = useMemo(() => {
const showPagination = filteredOtherAuthors.length > itemsPerPage;
// Если пагинация не нужна, показываем всех отфильтрованных остальных авторов
if (!showPagination) {
return {
showPagination: false,
displayedOtherAuthors: otherAuthors,
displayedOtherAuthors: filteredOtherAuthors,
totalPages: 1
};
}
// Если пагинация нужна
const totalPages = Math.ceil(otherAuthors.length / itemsPerPage);
const totalPages = Math.ceil(filteredOtherAuthors.length / itemsPerPage);
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const displayedOtherAuthors = otherAuthors.slice(startIndex, endIndex);
const displayedOtherAuthors = filteredOtherAuthors.slice(startIndex, endIndex);
return {
showPagination: true,
displayedOtherAuthors,
totalPages
};
}, [otherAuthors, currentPage, itemsPerPage]);
}, [filteredOtherAuthors, currentPage, itemsPerPage]);
// Сбрасываем страницу при изменении поискового запроса
useEffect(() => {
setCurrentPage(1);
}, [searchTerm]);
const handleAddAuthor = () => {
if (selectedAuthorId) {
@ -87,6 +105,7 @@ const AuthorSelectionModal: React.FC<AuthorSelectionModalProps> = ({
const handleTabChange = (role: AuthorRole) => {
setActiveTab(role);
setCurrentPage(1);
setSearchTerm('');
setSelectedAuthorId(null);
};
@ -96,6 +115,14 @@ const AuthorSelectionModal: React.FC<AuthorSelectionModalProps> = ({
document.getElementById('other-authors-section')?.scrollIntoView({ behavior: 'smooth' });
};
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(e.target.value);
};
const clearSearch = () => {
setSearchTerm('');
};
if (!isOpen) return null;
const { showPagination, displayedOtherAuthors, totalPages } = paginationData;
@ -174,7 +201,7 @@ const AuthorSelectionModal: React.FC<AuthorSelectionModalProps> = ({
</div>
)}
{/* Все остальные авторы с пагинацией */}
{/* Все остальные авторы с поиском и пагинацией */}
<div id="other-authors-section">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-semibold text-gray-700">
@ -187,6 +214,35 @@ const AuthorSelectionModal: React.FC<AuthorSelectionModalProps> = ({
)}
</div>
{/* Поле поиска */}
<div className="mb-6">
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Search size={20} className="text-gray-400" />
</div>
<input
type="text"
placeholder="Поиск по имени..."
className="w-full pl-10 pr-10 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value={searchTerm}
onChange={handleSearchChange}
/>
{searchTerm && (
<button
onClick={clearSearch}
className="absolute inset-y-0 right-0 pr-3 flex items-center"
>
<X size={20} className="text-gray-400 hover:text-gray-600" />
</button>
)}
</div>
{searchTerm && (
<p className="mt-2 text-sm text-gray-500">
Найдено: {filteredOtherAuthors.length} {filteredOtherAuthors.length === 1 ? 'автор' : 'авторов'}
</p>
)}
</div>
{displayedOtherAuthors.length > 0 ? (
<>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
@ -248,7 +304,10 @@ const AuthorSelectionModal: React.FC<AuthorSelectionModalProps> = ({
</>
) : (
<p className="text-gray-500 text-center py-8">
Нет доступных
{searchTerm
? `Нет авторов, начинающихся с "${searchTerm}"`
: `Нет доступных ${roleLabels[activeTab].toLowerCase()}`
}
</p>
)}
</div>