Merge branch 'develop'
This commit is contained in:
commit
9edb7ba6af
@ -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<HTMLInputElement>(null);
|
||||
const loadMoreRef = useRef<HTMLDivElement>(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() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{nextPage ? (
|
||||
<div className="mt-4 flex justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleLoadMore}
|
||||
disabled={loadingMore}
|
||||
className="rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-60"
|
||||
>
|
||||
{loadingMore ? "Загружаем..." : "Загрузить ещё"}
|
||||
</button>
|
||||
{/* Элемент-наблюдатель для автоматической подгрузки */}
|
||||
{nextPage && (
|
||||
<div ref={loadMoreRef} className="mt-4 flex justify-center py-4">
|
||||
{loadingMore && (
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500">
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-sky-500"></div>
|
||||
<span>Загрузка...</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
|
||||
@ -551,13 +551,30 @@ class ApiClient {
|
||||
}
|
||||
|
||||
// Заявки
|
||||
async getRequests(params?: { status?: string; type?: string; search?: string; page?: number; limit?: number }): Promise<any> {
|
||||
async getRequests(params?: {
|
||||
status?: string;
|
||||
type?: string;
|
||||
search?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
view?: string;
|
||||
addressed_to_me?: string;
|
||||
employee_id?: string | number;
|
||||
date_from?: string;
|
||||
date_to?: string;
|
||||
[key: string]: any;
|
||||
}): Promise<any> {
|
||||
const queryParams = new URLSearchParams();
|
||||
if (params?.status) queryParams.append('status', params.status);
|
||||
if (params?.type) queryParams.append('type', params.type);
|
||||
if (params?.search) queryParams.append('search', params.search);
|
||||
if (params?.page) queryParams.append('page', params.page.toString());
|
||||
if (params?.limit) queryParams.append('limit', params.limit.toString());
|
||||
if (params?.view) queryParams.append('view', params.view);
|
||||
if (params?.addressed_to_me) queryParams.append('addressed_to_me', params.addressed_to_me);
|
||||
if (params?.employee_id) queryParams.append('employee_id', params.employee_id.toString());
|
||||
if (params?.date_from) queryParams.append('date_from', params.date_from);
|
||||
if (params?.date_to) queryParams.append('date_to', params.date_to);
|
||||
|
||||
const url = `/api/v1/requests/${queryParams.toString() ? '?' + queryParams.toString() : ''}`;
|
||||
return this.request(url);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user