diff --git a/package.json b/package.json index 8fb9b3c..1352344 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vite-react-typescript-starter", "private": true, - "version": "1.0.5", + "version": "1.0.6", "type": "module", "scripts": { "dev": "vite", diff --git a/src/components/ArticleList.tsx b/src/components/ArticleList.tsx index 68675ea..d4fc594 100644 --- a/src/components/ArticleList.tsx +++ b/src/components/ArticleList.tsx @@ -6,8 +6,8 @@ import { Pencil, Trash2, ImagePlus, ToggleLeft, ToggleRight, Plus } from 'lucide import MinutesWord from './Words/MinutesWord'; import { useAuthStore } from '../stores/authStore'; import { usePermissions } from '../hooks/usePermissions'; -import {Pagination} from "./Pagination.tsx"; -import {useSearchParams} from "react-router-dom"; +import { Pagination } from "./Pagination"; +import { useSearchParams } from "react-router-dom"; interface ArticleListProps { articles: Article[]; @@ -32,15 +32,16 @@ export const ArticleList = React.memo(function ArticleList({ }: ArticleListProps) { const { user } = useAuthStore(); const { availableCategoryIds, availableCityIds, isAdmin } = usePermissions(); - const [searchParams, setSearchParams] = useSearchParams(); - + + // Получаем значения фильтров из URL + const currentPage = Math.max(1, parseInt(searchParams.get('page') || '1', 10)); + const filterCategoryId = parseInt(searchParams.get('categoryId') || '0', 10); + const filterCityId = parseInt(searchParams.get('cityId') || '0', 10); + const showDraftOnly = searchParams.get('draftOnly') === 'true'; + const [totalPages, setTotalPages] = useState(1); const [totalArticles, setTotalArticles] = useState(0); - const currentPage = Math.max(1, parseInt(searchParams.get('page') || '1', 10)); - const [filterCategoryId, setFilterCategoryId] = useState(0); - const [filterCityId, setFilterCityId] = useState(0); - const [showDraftOnly, setShowDraftOnly] = useState(false); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); @@ -49,7 +50,14 @@ export const ArticleList = React.memo(function ArticleList({ setLoading(true); try { const response = await axios.get('/api/articles/', { - params: { page: currentPage, categoryId: filterCategoryId, cityId: filterCityId, isDraft: showDraftOnly, userId: user?.id, isAdmin }, + params: { + page: currentPage, + categoryId: filterCategoryId, + cityId: filterCityId, + isDraft: showDraftOnly, + userId: user?.id, + isAdmin + }, }); setArticles(response.data.articles); setTotalPages(response.data.totalPages); @@ -69,14 +77,22 @@ export const ArticleList = React.memo(function ArticleList({ if (article) { try { await axios.put( - `/api/articles/active/${id}`, - { isActive: !article.isActive }, - { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } } + `/api/articles/active/${id}`, + { isActive: !article.isActive }, + { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } } ); setArticles(prev => prev.map(a => (a.id === id ? { ...a, isActive: !a.isActive } : a))); + // Обновляем список с сервера после изменения статуса const response = await axios.get('/api/articles/', { - params: { page: currentPage, categoryId: filterCategoryId, cityId: filterCityId, isDraft: showDraftOnly, userId: user?.id, isAdmin }, + params: { + page: currentPage, + categoryId: filterCategoryId, + cityId: filterCityId, + isDraft: showDraftOnly, + userId: user?.id, + isAdmin + }, }); setArticles(response.data.articles); setTotalPages(response.data.totalPages); @@ -88,179 +104,204 @@ export const ArticleList = React.memo(function ArticleList({ }; const handlePageChange = (page: number) => { - searchParams.set('page', page.toString()); - setSearchParams(searchParams); + const newParams = new URLSearchParams(searchParams); + newParams.set('page', page.toString()); + setSearchParams(newParams); window.scrollTo({ top: 0, behavior: 'smooth' }); }; + // Обработчики изменения фильтров + const handleCategoryChange = (categoryId: number) => { + const newParams = new URLSearchParams(searchParams); + if (categoryId === 0) { + newParams.delete('categoryId'); + } else { + newParams.set('categoryId', categoryId.toString()); + } + newParams.set('page', '1'); + setSearchParams(newParams); + }; + + const handleCityChange = (cityId: number) => { + const newParams = new URLSearchParams(searchParams); + if (cityId === 0) { + newParams.delete('cityId'); + } else { + newParams.set('cityId', cityId.toString()); + } + newParams.set('page', '1'); + setSearchParams(newParams); + }; + + const handleDraftOnlyChange = (checked: boolean) => { + const newParams = new URLSearchParams(searchParams); + if (checked) { + newParams.set('draftOnly', 'true'); + } else { + newParams.delete('draftOnly'); + } + newParams.set('page', '1'); + setSearchParams(newParams); + }; + const hasNoPermissions = availableCategoryIds.length === 0 || availableCityIds.length === 0; return ( -
-
-
-

Статьи

-
- handleCategoryChange(Number(e.target.value))} + className="rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 text-sm" + > + + {availableCategoryIds.map(cat => ( + + ))} + + + + + + + {!hasNoPermissions && ( + - )} -
-
-

- Статьи {Math.min((currentPage - 1) * ARTICLES_PER_PAGE + 1, totalArticles)} - - - {Math.min(currentPage * ARTICLES_PER_PAGE, totalArticles)} из {totalArticles} -

-
+ + Новая статья + + )} +
+
+

+ Статьи {Math.min((currentPage - 1) * ARTICLES_PER_PAGE + 1, totalArticles)} - {Math.min(currentPage * ARTICLES_PER_PAGE, totalArticles)} из {totalArticles} +

- {loading ? ( -
-
-
- ) : hasNoPermissions ? ( -
-

Недостаточно прав

-

У вас нет прав на создание и редактирование статей. Свяжитесь с администратором.

-
- ) : ( - <> - - {totalPages > 1 && ( - - )} - - - )} - {error && isAdmin && ( -
{error}
- )}
+ + {loading ? ( +
+
+
+ ) : hasNoPermissions ? ( +
+

Недостаточно прав

+

У вас нет прав на создание и редактирование статей. Свяжитесь с администратором.

+
+ ) : ( + <> + + + {totalPages > 1 && ( + + )} + + + )} + + {error && isAdmin && ( +
{error}
+ )} + ); }); \ No newline at end of file