diff --git a/.genkit/traces_idx/genkit.metadata b/.genkit/traces_idx/genkit.metadata new file mode 100644 index 0000000..d297703 --- /dev/null +++ b/.genkit/traces_idx/genkit.metadata @@ -0,0 +1 @@ +{"version":"1.21.0"} \ No newline at end of file diff --git a/frontend/src/components/base/Pagination.tsx b/frontend/src/components/base/Pagination.tsx index f251453..7616658 100644 --- a/frontend/src/components/base/Pagination.tsx +++ b/frontend/src/components/base/Pagination.tsx @@ -1,7 +1,7 @@ -import type { Component } from 'solid-js'; -import { Show } from 'solid-js'; -import { FiChevronLeft, FiChevronRight } from 'solid-icons/fi'; -import type { PaginationProps } from '@globalInterfaces'; +import type { Component } from "solid-js"; +import { Show, Match, Switch } from "solid-js"; +import { FiChevronLeft, FiChevronRight } from "solid-icons/fi"; // Убедитесь, что иконки установлены +import type { PaginationProps } from "@globalInterfaces"; interface Props { page: PaginationProps; @@ -17,41 +17,59 @@ const Pagination: Component = (props) => { class="mt-12 flex items-center justify-center gap-6 border-t border-neutral-200 pt-8 dark:border-neutral-800" aria-label="Навигация по страницам" > - {/* Кнопка "Назад" */} - - - + + + + + + + + + + {/* Индикатор текущей страницы */} - Страница {props.page.currentPage} из {props.page.lastPage} + Страница{" "} + + {props.page.currentPage} + {" "} + из {props.page.lastPage} - {/* Кнопка "Вперед" */} - - - + + + + + + + + + + ); }; -export default Pagination; \ No newline at end of file +export default Pagination; diff --git a/frontend/src/pages/api/search.json.ts b/frontend/src/pages/api/search.json.ts index 45389db..7ee994b 100644 --- a/frontend/src/pages/api/search.json.ts +++ b/frontend/src/pages/api/search.json.ts @@ -1,10 +1,10 @@ // src/pages/api/search.json.ts import type { APIRoute } from "astro"; -import { pb } from '@lib/pocketbase'; +import { pb } from "@lib/pocketbase"; export const GET: APIRoute = async ({ url }): Promise => { try { - const query = url.searchParams.get('q')?.trim() || ''; + const query = url.searchParams.get("q")?.trim() || ""; if (!query) { return new Response(JSON.stringify([]), { @@ -13,19 +13,17 @@ export const GET: APIRoute = async ({ url }): Promise => { }); } - // 1. САНИТИЗАЦИЯ: Экранируем кавычки, чтобы запрос не сломал синтаксис фильтра PB - // Если пользователь введет: React "Hero", мы превратим это в: React \"Hero\" - const safeQuery = query.replace(/"/g, '\\"'); + // УЛУЧШЕНИЕ: Используем параметризованный фильтр для безопасности и надежности. + // PocketBase SDK автоматически экранирует значения, предотвращая ошибки. + const filterString = `isActive = true && (title ~ {:query} || description ~ {:query} || content ~ {:query})`; - // 2. СБОРКА ФИЛЬТРА: Собираем строку вручную (это самый надежный способ) - // Мы ищем совпадения в заголовке, описании ИЛИ контенте - const filterString = `isActive = true && (title ~ "${safeQuery}" || description ~ "${safeQuery}" || content ~ "${safeQuery}")`; - - const result = await pb.collection('posts').getList(1, 15, { + const result = await pb.collection("posts").getList(1, 15, { filter: filterString, - sort: '-publishDate', + // Передаем значение query в качестве параметра + filterParams: { query }, + sort: "-publishDate", // Запрашиваем только нужные поля (без content, чтобы не грузить сеть) - fields: 'id,title,description,slug', + fields: "id,title,description,slug", }); const searchData = result.items.map((post) => ({ @@ -39,17 +37,16 @@ export const GET: APIRoute = async ({ url }): Promise => { status: 200, headers: { "Content-Type": "application/json", - "Cache-Control": "public, max-age=60" + "Cache-Control": "public, max-age=60", }, }); - } catch (error) { // Логируем ошибку подробно, чтобы видеть причину в консоли - console.error('Search API error:', error); + console.error("Search API error:", error); - return new Response(JSON.stringify({ error: 'Internal server error' }), { + return new Response(JSON.stringify({ error: "Internal server error" }), { status: 500, - headers: { 'Content-Type': 'application/json' }, + headers: { "Content-Type": "application/json" }, }); } -}; \ No newline at end of file +};