From 8fee8177ad3970d0edbd52516b023b4d1ecf5a19 Mon Sep 17 00:00:00 2001 From: lizerk Date: Mon, 9 Mar 2026 08:48:08 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0?= =?UTF-8?q?=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B0=D1=8F=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0=20=D0=B7=D0=B0=D1=8F?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=BF=D1=80=D0=B8=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=BA=D1=80=D1=83=D1=82=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Удалена кнопка 'Загрузить ещё' - Реализована автоматическая подгрузка через Intersection Observer API - Данные начинают загружаться за 100px до конца списка - Учитываются все фильтры и поиск - Добавлен индикатор загрузки при подгрузке данных --- frontend/src/app/requests/page.tsx | 54 ++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/requests/page.tsx b/frontend/src/app/requests/page.tsx index ce74892d..33a326e2 100644 --- a/frontend/src/app/requests/page.tsx +++ b/frontend/src/app/requests/page.tsx @@ -4,7 +4,7 @@ import { AppShell } from "../../components/AppShell"; import { apiClient } from "@/lib/api"; import { useUser } from "@/contexts/UserContext"; import { canManageRequests, canProcessRequests } from "@/lib/permissions"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import Link from "next/link"; import type { Request, RequestComment, User, Department } from "@/types/api"; import { Ban, Check, ChevronDown, FileSignature, Filter, MessageSquare, Paperclip, Pencil, Plus, Search, ThumbsDown, ThumbsUp, Trash2, X } from "lucide-react"; @@ -313,7 +313,7 @@ export default function RequestsPage() { } }; - const handleLoadMore = async () => { + const handleLoadMore = useCallback(async () => { if (!nextPage || loadingMore) return; try { setLoadingMore(true); @@ -331,7 +331,7 @@ export default function RequestsPage() { } finally { setLoadingMore(false); } - }; + }, [nextPage, loadingMore, view, typeFilter, statusFilter, employeeFilter, dateFromFilter, dateToFilter]); const handleApprove = async (id: number) => { try { @@ -431,6 +431,33 @@ export default function RequestsPage() { const isFinal = (status?: string) => ["approved", "rejected", "cancelled"].includes(String(status || "").toLowerCase()); const fileInputRef = useRef(null); + const loadMoreRef = useRef(null); + + // Автоматическая подгрузка при прокрутке + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + const target = entries[0]; + if (target.isIntersecting && nextPage && !loadingMore && !loading) { + handleLoadMore(); + } + }, + { + rootMargin: '100px', // Начинаем загрузку за 100px до конца + } + ); + + const currentRef = loadMoreRef.current; + if (currentRef) { + observer.observe(currentRef); + } + + return () => { + if (currentRef) { + observer.unobserve(currentRef); + } + }; + }, [nextPage, loadingMore, loading, handleLoadMore]); /* ---- Searchable multi-select dropdown ---- */ const SearchableSelect = ({ label, items, selectedIds, onToggle, placeholder }: { @@ -916,18 +943,17 @@ export default function RequestsPage() { )} - {nextPage ? ( -
- + {/* Элемент-наблюдатель для автоматической подгрузки */} + {nextPage && ( +
+ {loadingMore && ( +
+
+ Загрузка... +
+ )}
- ) : null} + )} )}