diff --git a/src/components/Tables/tenders/index.tsx b/src/components/Tables/tenders/index.tsx
index 13528e2..b337af5 100644
--- a/src/components/Tables/tenders/index.tsx
+++ b/src/components/Tables/tenders/index.tsx
@@ -5,7 +5,6 @@ import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
import IsVisible from "@/components/ui/IsVisible";
import ListHeader from "@/components/ui/ListHeader";
import ListWrapper from "@/components/ui/ListWrapper";
-import Pagination from "@/components/ui/pagination";
import Status from "@/components/ui/Status";
import {
Table,
@@ -49,7 +48,8 @@ const TendersTable = () => {
navigateToTenderDetails,
navigateToTenderFeedback,
handlePaginationChange,
- buildWindowedPageLabel,
+ hasNext,
+ hasPrevious,
getRowNumber,
formatDateTimeCell,
pagination,
@@ -204,16 +204,37 @@ const TendersTable = () => {
/>
-
+
);
diff --git a/src/components/Tables/tenders/useTenderListPresenter.ts b/src/components/Tables/tenders/useTenderListPresenter.ts
index c283a20..a2cab02 100644
--- a/src/components/Tables/tenders/useTenderListPresenter.ts
+++ b/src/components/Tables/tenders/useTenderListPresenter.ts
@@ -8,14 +8,20 @@ import { TCustomer } from "@/lib/api";
import {
buildQueryString,
deleteEmptyKeys,
- getPaginatedRowNumber,
isDateRangeActive,
unixToDate,
} from "@/utils/shared";
import gsap from "gsap";
import { useIsMutating } from "@tanstack/react-query";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
-import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
+import {
+ useCallback,
+ useEffect,
+ useLayoutEffect,
+ useMemo,
+ useRef,
+ useState,
+} from "react";
import { useForm } from "react-hook-form";
const COMMA_SEPARATED_FILTER_KEYS = new Set([
@@ -356,11 +362,10 @@ const useTenderListPresenter = () => {
useEffect(() => {
const newParams: Record = {
...apiDefaultParams,
- offset: 0,
- limit: 10,
+ limit: 20,
};
for (const key of searchParams.keys()) {
- if (key === "cpv_codes") {
+ if (key === "cpv_codes" || key === "include_total" || key === "cursor") {
continue;
}
newParams[key] = searchParams.get(key);
@@ -376,53 +381,21 @@ const useTenderListPresenter = () => {
const { data, error, isPending, isError } = useTendersQuery(params);
- const { pagination, handlePaginationChange, buildFirstPageParams } =
- useHybridPagination({
- meta: data?.meta,
- params,
- setParams,
- isError,
- error,
- });
+ const {
+ pagination,
+ handlePaginationChange,
+ buildFirstPageParams,
+ hasNext,
+ hasPrevious,
+ } = useHybridPagination({
+ meta: data?.meta,
+ params,
+ setParams,
+ isError,
+ error,
+ });
- const MAX_PAGE_JUMP = 10;
- const paginationWindow = useMemo(() => {
- const windowStart = Math.max(0, pagination.currentPage - MAX_PAGE_JUMP);
- const windowEnd = Math.min(
- Math.max(pagination.totalPages - 1, 0),
- pagination.currentPage + MAX_PAGE_JUMP,
- );
- return {
- windowStart,
- virtualCurrent: pagination.currentPage - windowStart,
- virtualTotal: Math.max(windowEnd - windowStart + 1, 1),
- };
- }, [pagination.currentPage, pagination.totalPages]);
-
- const handleWindowedPaginationChange = useCallback(
- (e: { selected: number }) => {
- handlePaginationChange({
- selected: e.selected + paginationWindow.windowStart,
- });
- },
- [handlePaginationChange, paginationWindow.windowStart],
- );
-
- const buildWindowedPageLabel = useCallback(
- (virtualPage: number) => String(virtualPage + paginationWindow.windowStart),
- [paginationWindow.windowStart],
- );
-
- const windowedPagination = useMemo(
- () => ({
- currentPage: paginationWindow.virtualCurrent,
- totalPages: paginationWindow.virtualTotal,
- }),
- [paginationWindow.virtualCurrent, paginationWindow.virtualTotal],
- );
-
- const shouldShowPagination =
- !isPending && (data?.meta?.pages ?? 1) * (data?.meta?.limit ?? 10) > 10;
+ const shouldShowPagination = !isPending && (hasPrevious || hasNext);
const tendersList = data?.data?.tenders ?? [];
@@ -449,12 +422,11 @@ const useTenderListPresenter = () => {
const getRowNumber = useCallback(
(index: number) =>
- getPaginatedRowNumber({
- index,
- page: data?.meta?.page,
- limit: data?.meta?.limit,
- }),
- [data?.meta?.limit, data?.meta?.page],
+ pagination.currentPage *
+ (data?.meta?.limit ?? Number(params.limit) ?? 20) +
+ index +
+ 1,
+ [data?.meta?.limit, pagination.currentPage, params.limit],
);
const formatDateTimeCell = useCallback((unix: number) => {
@@ -541,7 +513,7 @@ const useTenderListPresenter = () => {
const clearAllFilters = useCallback(() => {
const cleared = buildFirstPageParams({
...apiDefaultParams,
- limit: 10,
+ limit: 20,
});
filterFormReset(mergeTenderFilterFormState(cleared));
router.push(pathName);
@@ -629,11 +601,12 @@ const useTenderListPresenter = () => {
tendersList,
navigateToTenderDetails,
navigateToTenderFeedback,
- handlePaginationChange: handleWindowedPaginationChange,
- buildWindowedPageLabel,
+ handlePaginationChange,
+ hasNext,
+ hasPrevious,
getRowNumber,
formatDateTimeCell,
- pagination: windowedPagination,
+ pagination,
absoluteCurrentPage: pagination.currentPage,
};
};
diff --git a/src/hooks/queries/useTenders.ts b/src/hooks/queries/useTenders.ts
index d390427..5f6c3bb 100644
--- a/src/hooks/queries/useTenders.ts
+++ b/src/hooks/queries/useTenders.ts
@@ -1,9 +1,10 @@
import { API_ENDPOINTS, tendersService } from "@/lib/api";
+import { TTenderListParams } from "@/lib/api/types/Tenders";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useMemo } from "react";
import { toast } from "react-toastify";
-export const useTendersQuery = (params?: Record) => {
+export const useTendersQuery = (params?: TTenderListParams) => {
const queryKey = useMemo(
() => [API_ENDPOINTS.TENDERS.READ_ALL, "read-tenders", params],
[params],
@@ -15,7 +16,10 @@ export const useTendersQuery = (params?: Record) => {
});
};
-export const useTenderDetailQuery = (id: string, params?: Record) => {
+export const useTenderDetailQuery = (
+ id: string,
+ params?: Record,
+) => {
const queryKey = useMemo(
() => [API_ENDPOINTS.TENDERS.DETAILS(id), params],
[id, params],
@@ -52,13 +56,10 @@ export const useGetTenderDocumentsQuery = (
id: string,
options?: { enabled?: boolean },
) => {
- const queryKey = useMemo(
- () => [API_ENDPOINTS.TENDERS.DOCUMENTS(id)],
- [id],
- );
+ const queryKey = useMemo(() => [API_ENDPOINTS.TENDERS.DOCUMENTS(id)], [id]);
return useQuery({
queryKey,
queryFn: () => tendersService.getDocuments(id),
enabled: options?.enabled ?? true,
});
-};
\ No newline at end of file
+};
diff --git a/src/hooks/useHybridPagination.ts b/src/hooks/useHybridPagination.ts
index 749abc2..bd6e7bc 100644
--- a/src/hooks/useHybridPagination.ts
+++ b/src/hooks/useHybridPagination.ts
@@ -1,6 +1,5 @@
"use client";
-import { IMetaData } from "@/lib/api/types/Factory";
import { AxiosError } from "axios";
import { useCallback, useEffect, useMemo, useState } from "react";
@@ -12,7 +11,14 @@ const isInvalidCursorError = (error: unknown) => {
};
type UseHybridPaginationOptions = {
- meta?: IMetaData;
+ meta?: {
+ limit: number;
+ offset: number;
+ page?: number;
+ pages?: number;
+ has_more?: boolean;
+ next_cursor?: string;
+ };
params: Record;
setParams: React.Dispatch>>;
isError?: boolean;
@@ -91,7 +97,14 @@ export const useHybridPagination = ({
if (meta?.page != null) return meta.page - 1;
return 0;
- }, [cursorStack, params.cursor, params.offset, meta?.offset, meta?.page, limit]);
+ }, [
+ cursorStack,
+ params.cursor,
+ params.offset,
+ meta?.offset,
+ meta?.page,
+ limit,
+ ]);
const hasPrevious = cursorStack.length > 0 || currentPageIndex > 0;
@@ -129,7 +142,11 @@ export const useHybridPagination = ({
{ cursor: nextCursor, fromPage: fromPage1Based },
]);
setParams((current) => {
- const next: Record = { ...current, cursor: nextCursor, limit };
+ const next: Record = {
+ ...current,
+ cursor: nextCursor,
+ limit,
+ };
delete next.offset;
return next;
});
diff --git a/src/lib/api/services/tenders-service.ts b/src/lib/api/services/tenders-service.ts
index c7dc395..2fa8841 100644
--- a/src/lib/api/services/tenders-service.ts
+++ b/src/lib/api/services/tenders-service.ts
@@ -5,14 +5,16 @@ import { ApiResponse } from "../types";
import {
TTenderDetails,
TTenderDocumentsResponse,
+ TTenderListParams,
+ TTenderListMeta,
TTenderResponse,
TTenderTranslationResponse,
} from "../types/Tenders";
export const tendersService = {
tendersList: async (
- params?: Record,
- ): Promise> => {
+ params?: TTenderListParams,
+ ): Promise> => {
try {
return (
await api.get(API_ENDPOINTS.TENDERS.READ_ALL, {
@@ -30,7 +32,8 @@ export const tendersService = {
params?: Record,
): Promise> => {
try {
- return (await api.get(API_ENDPOINTS.TENDERS.DETAILS(id), { params })).data;
+ return (await api.get(API_ENDPOINTS.TENDERS.DETAILS(id), { params }))
+ .data;
} catch (error) {
console.error(
"ERROR caught in Tender Service => tender details: ",
@@ -53,12 +56,17 @@ export const tendersService = {
})
).data;
} catch (error) {
- console.error("ERROR caught in Tender Service => translate tender: ", error);
+ console.error(
+ "ERROR caught in Tender Service => translate tender: ",
+ error,
+ );
throw error;
}
},
- getDocuments: async (id: string): Promise> => {
+ getDocuments: async (
+ id: string,
+ ): Promise> => {
try {
return (await api.get(API_ENDPOINTS.TENDERS.DOCUMENTS(id))).data;
} catch (error) {
diff --git a/src/lib/api/types/Tenders.ts b/src/lib/api/types/Tenders.ts
index 5203058..a89d0d1 100644
--- a/src/lib/api/types/Tenders.ts
+++ b/src/lib/api/types/Tenders.ts
@@ -123,9 +123,33 @@ export const TendersResponse = z.object({
tenders: z.array(TenderSchema).or(z.null()),
});
-export type TTenderDocumentsResponse = z.infer;
+export type TTenderDocumentsResponse = z.infer<
+ typeof TenderDocumentsResponseSchema
+>;
export type TTenderResponse = z.infer;
export type TTenderDetails = z.infer;
+export interface TTenderListParams {
+ limit?: number;
+ offset?: number;
+ cursor?: string;
+ include_total?: boolean;
+ sort_by?: string;
+ sort_order?: "asc" | "desc";
+ q?: string;
+ status?: string[];
+ country?: string;
+ documents_scraped?: boolean;
+ [key: string]: unknown;
+}
+export interface TTenderListMeta {
+ limit: number;
+ offset: number;
+ has_more: boolean;
+ next_cursor?: string;
+ total?: number;
+ page?: number;
+ pages?: number;
+}
export type TTenderTranslationResponse = z.infer<
typeof TenderTranslationResponseSchema
>;
diff --git a/src/lib/api/types/shared.ts b/src/lib/api/types/shared.ts
index aee151f..a577578 100644
--- a/src/lib/api/types/shared.ts
+++ b/src/lib/api/types/shared.ts
@@ -1,9 +1,9 @@
import { IMetaData } from "./Factory";
-export interface ApiResponse {
+export interface ApiResponse {
data: T;
message: string;
code?: number;
- meta?: IMetaData;
+ meta?: TMeta;
success: boolean;
}