updated pagination commit to carry

This commit is contained in:
berkay 2025-04-28 13:19:01 +03:00
parent ac344773c5
commit 2d418644bb
5 changed files with 228 additions and 179 deletions

View File

@ -144,6 +144,18 @@ class Pagination:
self.orderField = "uu_id" self.orderField = "uu_id"
self.orderType = "asc" self.orderType = "asc"
@property
def next_available(self) -> bool:
if self.page < self.total_pages:
return True
return False
@property
def back_available(self) -> bool:
if self.page > 1:
return True
return False
@property @property
def as_dict(self) -> Dict[str, Any]: def as_dict(self) -> Dict[str, Any]:
"""Convert pagination state to dictionary format.""" """Convert pagination state to dictionary format."""
@ -157,6 +169,8 @@ class Pagination:
"pageCount": self.page_count, "pageCount": self.page_count,
"orderField": self.orderField, "orderField": self.orderField,
"orderType": self.orderType, "orderType": self.orderType,
"next": self.next_available,
"back": self.back_available,
} }

View File

@ -1,7 +1,13 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { PagePagination } from "./hooks"; import { PagePagination } from "./hooks";
import { getTranslation, LanguageKey } from "./language"; import { getTranslation, LanguageKey } from "./language";
@ -12,14 +18,11 @@ interface PaginationToolsComponentProps {
lang: string; lang: string;
} }
export const PaginationToolsComponent: React.FC<PaginationToolsComponentProps> = ({ export const PaginationToolsComponent: React.FC<
pagination, PaginationToolsComponentProps
updatePagination, > = ({ pagination, updatePagination, loading, lang }) => {
loading,
lang,
}) => {
const t = getTranslation(lang as LanguageKey); const t = getTranslation(lang as LanguageKey);
const handlePageChange = (newPage: number) => { const handlePageChange = (newPage: number) => {
if (newPage >= 1 && newPage <= pagination.totalPages) { if (newPage >= 1 && newPage <= pagination.totalPages) {
updatePagination({ page: newPage }); updatePagination({ page: newPage });
@ -32,20 +35,23 @@ export const PaginationToolsComponent: React.FC<PaginationToolsComponentProps> =
<div> <div>
{t.showing}{" "} {t.showing}{" "}
{/* Show the range based on filtered count when available */} {/* Show the range based on filtered count when available */}
{(pagination.totalCount || pagination.allCount || 0) > 0 {(pagination.totalCount || pagination.allCount || 0) > 0
? (pagination.page - 1) * pagination.size + 1 ? (pagination.page - 1) * pagination.size + 1
: 0}{" "} : 0}{" "}
- {Math.min( -{" "}
pagination.page * pagination.size, {Math.min(
pagination.totalCount || pagination.allCount || 0 pagination.page * pagination.size,
)} {t.of} {pagination.totalCount || pagination.allCount || 0}{" "} pagination.totalCount || pagination.allCount || 0
{t.items} )}{" "}
{t.of} {pagination.totalCount || pagination.allCount || 0} {t.items}
</div> </div>
{pagination.totalCount && pagination.totalCount !== (pagination.allCount || 0) && ( {pagination.totalCount &&
<div> pagination.totalCount !== (pagination.allCount || 0) && (
{t.total}: {pagination.allCount || 0} {t.items} ({t.filtered}: {pagination.totalCount} {t.items}) <div>
</div> {t.total}: {pagination.allCount || 0} {t.items} ({t.filtered}:{" "}
)} {pagination.totalCount} {t.items})
</div>
)}
</div> </div>
{/* Navigation buttons - center */} {/* Navigation buttons - center */}
@ -54,70 +60,87 @@ export const PaginationToolsComponent: React.FC<PaginationToolsComponentProps> =
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => handlePageChange(pagination.page - 1)} onClick={() => handlePageChange(pagination.page - 1)}
disabled={pagination.page <= 1 || loading} disabled={pagination.next}
> >
{t.previous} {t.previous}
</Button> </Button>
{/* Page number buttons */} {/* Page number buttons */}
<div className="flex items-center space-x-1"> <div className="flex items-center space-x-1">
{Array.from({ length: Math.min(5, Math.max(1, Math.ceil( {Array.from(
(pagination.totalCount && pagination.totalCount !== pagination.allCount {
? pagination.totalCount length: Math.min(
: (pagination.allCount || 0)) / pagination.size 5,
))) }, (_, i) => { Math.max(
// Show pages around current page 1,
let pageNum; Math.ceil(
const calculatedTotalPages = Math.max(1, Math.ceil( (pagination.totalCount &&
(pagination.totalCount && pagination.totalCount !== pagination.allCount pagination.totalCount !== pagination.allCount
? pagination.totalCount ? pagination.totalCount
: (pagination.allCount || 0)) / pagination.size : pagination.allCount || 0) / pagination.size
)); )
if (calculatedTotalPages <= 5) { )
pageNum = i + 1; ),
} else if (pagination.page <= 3) { },
pageNum = i + 1; (_, i) => {
} else if (pagination.page >= calculatedTotalPages - 2) { // Show pages around current page
pageNum = calculatedTotalPages - 4 + i; let pageNum;
} else { const calculatedTotalPages = Math.max(
pageNum = pagination.page - 2 + i; 1,
} Math.ceil(
(pagination.totalCount &&
pagination.totalCount !== pagination.allCount
? pagination.totalCount
: pagination.allCount || 0) / pagination.size
)
);
if (calculatedTotalPages <= 5) {
pageNum = i + 1;
} else if (pagination.page <= 3) {
pageNum = i + 1;
} else if (pagination.page >= calculatedTotalPages - 2) {
pageNum = calculatedTotalPages - 4 + i;
} else {
pageNum = pagination.page - 2 + i;
}
return ( return (
<Button <Button
key={pageNum} key={pageNum}
variant={pagination.page === pageNum ? "default" : "outline"} variant={pagination.page === pageNum ? "default" : "outline"}
size="sm" size="sm"
className="w-9 h-9 p-0" className="w-9 h-9 p-0"
onClick={() => handlePageChange(pageNum)} onClick={() => handlePageChange(pageNum)}
disabled={loading} disabled={loading}
> >
{pageNum} {pageNum}
</Button> </Button>
); );
})} }
)}
</div> </div>
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => handlePageChange(pagination.page + 1)} onClick={() => handlePageChange(pagination.page + 1)}
disabled={pagination.page >= Math.max(1, Math.ceil( disabled={pagination.back}
(pagination.totalCount && pagination.totalCount !== pagination.allCount
? pagination.totalCount
: (pagination.allCount || 0)) / pagination.size
)) || loading}
> >
{t.next} {t.next}
</Button> </Button>
{/* Page text display */} {/* Page text display */}
<span className="px-4 py-1 text-sm text-muted-foreground"> <span className="px-4 py-1 text-sm text-muted-foreground">
{t.page} {pagination.page} {t.of} {Math.max(1, Math.ceil( {t.page} {pagination.page} {t.of}{" "}
(pagination.totalCount && pagination.totalCount !== pagination.allCount {Math.max(
? pagination.totalCount 1,
: (pagination.allCount || 0)) / pagination.size Math.ceil(
))} (pagination.totalCount &&
pagination.totalCount !== pagination.allCount
? pagination.totalCount
: pagination.allCount || 0) / pagination.size
)
)}
</span> </span>
</div> </div>
@ -129,7 +152,7 @@ export const PaginationToolsComponent: React.FC<PaginationToolsComponentProps> =
onValueChange={(value) => { onValueChange={(value) => {
updatePagination({ updatePagination({
size: Number(value), size: Number(value),
page: 1 // Reset to first page when changing page size page: 1, // Reset to first page when changing page size
}); });
}} }}
> >

View File

@ -16,6 +16,8 @@ export interface ResponseMetadata {
totalPages: number; totalPages: number;
pageCount: number; pageCount: number;
allCount?: number; allCount?: number;
next: boolean;
back: boolean;
} }
export interface PagePagination extends RequestParams, ResponseMetadata {} export interface PagePagination extends RequestParams, ResponseMetadata {}
@ -39,6 +41,8 @@ export function useApplicationData() {
totalItems: 0, totalItems: 0,
totalPages: 0, totalPages: 0,
pageCount: 0, pageCount: 0,
next: true,
back: false,
}); });
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -66,6 +70,8 @@ export function useApplicationData() {
totalPages: result.pagination.totalPages || 1, totalPages: result.pagination.totalPages || 1,
pageCount: result.pagination.pageCount || 0, pageCount: result.pagination.pageCount || 0,
allCount: result.pagination.allCount || 0, allCount: result.pagination.allCount || 0,
next: result.pagination.next || true,
back: result.pagination.back || false,
}); });
} }
} }
@ -96,23 +102,27 @@ export function useApplicationData() {
// Transform query parameters to use __ilike with %value% format // Transform query parameters to use __ilike with %value% format
if (updates.query) { if (updates.query) {
const transformedQuery: Record<string, any> = {}; const transformedQuery: Record<string, any> = {};
Object.entries(updates.query).forEach(([key, value]) => { Object.entries(updates.query).forEach(([key, value]) => {
// Only transform string values that aren't already using a special operator // Only transform string values that aren't already using a special operator
if (typeof value === 'string' && !key.includes('__') && value.trim() !== '') { if (
typeof value === "string" &&
!key.includes("__") &&
value.trim() !== ""
) {
transformedQuery[`${key}__ilike`] = `%${value}%`; transformedQuery[`${key}__ilike`] = `%${value}%`;
} else { } else {
transformedQuery[key] = value; transformedQuery[key] = value;
} }
}); });
updates.query = transformedQuery; updates.query = transformedQuery;
// Always reset to page 1 when search query changes // Always reset to page 1 when search query changes
if (!updates.hasOwnProperty('page')) { if (!updates.hasOwnProperty("page")) {
updates.page = 1; updates.page = 1;
} }
// Reset response metadata when search changes to avoid stale pagination data // Reset response metadata when search changes to avoid stale pagination data
setResponseMetadata({ setResponseMetadata({
totalCount: 0, totalCount: 0,
@ -120,9 +130,11 @@ export function useApplicationData() {
totalPages: 0, totalPages: 0,
pageCount: 0, pageCount: 0,
allCount: 0, allCount: 0,
next: true,
back: false,
}); });
} }
setRequestParams((prev) => ({ setRequestParams((prev) => ({
...prev, ...prev,
...updates, ...updates,
@ -132,9 +144,9 @@ export function useApplicationData() {
// Create a combined refetch function // Create a combined refetch function
const refetch = useCallback(() => { const refetch = useCallback(() => {
// Reset pagination to page 1 when manually refetching // Reset pagination to page 1 when manually refetching
setRequestParams(prev => ({ setRequestParams((prev) => ({
...prev, ...prev,
page: 1 page: 1,
})); }));
fetchApplicationsFromApi(); fetchApplicationsFromApi();
}, [fetchApplicationsFromApi]); }, [fetchApplicationsFromApi]);

View File

@ -21,11 +21,11 @@ const ApplicationPage: React.FC<PageProps> = ({ lang = "en" }) => {
null null
); );
// State for sorting // // State for sorting
const [sortField, setSortField] = useState<string | null>(null); // const [sortField, setSortField] = useState<string | null>(null);
const [sortDirection, setSortDirection] = useState<"asc" | "desc" | null>( // const [sortDirection, setSortDirection] = useState<"asc" | "desc" | null>(
null // null
); // );
// Available options for dropdowns // Available options for dropdowns
const urlOptions = [ const urlOptions = [
@ -55,29 +55,29 @@ const ApplicationPage: React.FC<PageProps> = ({ lang = "en" }) => {
}); });
}; };
// Handle sorting // // Handle sorting
const handleSort = (field: string) => { // const handleSort = (field: string) => {
let direction: "asc" | "desc" | null = "asc"; // let direction: "asc" | "desc" | null = "asc";
if (sortField === field) { // if (sortField === field) {
// Toggle direction if same field is clicked // // Toggle direction if same field is clicked
if (sortDirection === "asc") { // if (sortDirection === "asc") {
direction = "desc"; // direction = "desc";
} else if (sortDirection === "desc") { // } else if (sortDirection === "desc") {
// Clear sorting if already desc // // Clear sorting if already desc
field = ""; // field = "";
direction = null; // direction = null;
} // }
} // }
setSortField(field || null); // setSortField(field || null);
setSortDirection(direction); // setSortDirection(direction);
updatePagination({ // updatePagination({
orderField: field ? [field] : [], // orderField: field ? [field] : [],
orderType: direction ? [direction] : [], // orderType: direction ? [direction] : [],
}); // });
}; // };
return ( return (
<div className="container mx-auto px-4 py-6"> <div className="container mx-auto px-4 py-6">
@ -99,14 +99,14 @@ const ApplicationPage: React.FC<PageProps> = ({ lang = "en" }) => {
lang={lang} lang={lang}
/> />
{/* Sorting Component */} {/* Sorting Component
<SortingComponent <SortingComponent
sortField={sortField} sortField={sortField}
sortDirection={sortDirection} sortDirection={sortDirection}
onSort={handleSort} onSort={handleSort}
translations={translations} translations={translations}
lang={lang} lang={lang}
/> /> */}
{/* Data Display Component */} {/* Data Display Component */}
<div className="mt-6"> <div className="mt-6">

View File

@ -1,17 +1,17 @@
services: services:
client_frontend: # client_frontend:
container_name: client_frontend # container_name: client_frontend
build: # build:
context: . # context: .
dockerfile: WebServices/client-frontend/Dockerfile # dockerfile: WebServices/client-frontend/Dockerfile
networks: # networks:
- wag-services # - wag-services
ports: # ports:
- "3000:3000" # - "3000:3000"
environment: # environment:
- NODE_ENV=development # - NODE_ENV=development
cpus: 1 # cpus: 1
mem_limit: 2048m # mem_limit: 2048m
# volumes: # volumes:
# - client-frontend:/WebServices/client-frontend # - client-frontend:/WebServices/client-frontend
@ -26,7 +26,7 @@ services:
- "3001:3001" - "3001:3001"
environment: environment:
- NODE_ENV=development - NODE_ENV=development
cpus: 1 cpus: 2
mem_limit: 2048m mem_limit: 2048m
auth_service: auth_service:
@ -56,59 +56,59 @@ services:
mem_limit: 512m mem_limit: 512m
cpus: 0.5 cpus: 0.5
identity_service: # identity_service:
container_name: identity_service # container_name: identity_service
build: # build:
context: . # context: .
dockerfile: ApiServices/IdentityService/Dockerfile # dockerfile: ApiServices/IdentityService/Dockerfile
networks: # networks:
- wag-services # - wag-services
depends_on: # depends_on:
- initializer_service # - initializer_service
env_file: # env_file:
- api_env.env # - api_env.env
environment: # environment:
- API_PATH=app:app # - API_PATH=app:app
- API_HOST=0.0.0.0 # - API_HOST=0.0.0.0
- API_PORT=8002 # - API_PORT=8002
- API_LOG_LEVEL=info # - API_LOG_LEVEL=info
- API_RELOAD=1 # - API_RELOAD=1
- API_APP_NAME=evyos-identity-api-gateway # - API_APP_NAME=evyos-identity-api-gateway
- API_TITLE=WAG API Identity Api Gateway # - API_TITLE=WAG API Identity Api Gateway
- API_FORGOT_LINK=https://identity_service/forgot-password # - API_FORGOT_LINK=https://identity_service/forgot-password
- API_DESCRIPTION=This api is serves as web identity api gateway only to evyos web services. # - API_DESCRIPTION=This api is serves as web identity api gateway only to evyos web services.
- API_APP_URL=https://identity_service # - API_APP_URL=https://identity_service
ports: # ports:
- "8002:8002" # - "8002:8002"
mem_limit: 512m # mem_limit: 512m
cpus: 0.5 # cpus: 0.5
building_service: # building_service:
container_name: building_service # container_name: building_service
build: # build:
context: . # context: .
dockerfile: ApiServices/BuildingService/Dockerfile # dockerfile: ApiServices/BuildingService/Dockerfile
networks: # networks:
- wag-services # - wag-services
env_file: # env_file:
- api_env.env # - api_env.env
depends_on: # depends_on:
- initializer_service # - initializer_service
environment: # environment:
- API_PATH=app:app # - API_PATH=app:app
- API_HOST=0.0.0.0 # - API_HOST=0.0.0.0
- API_PORT=8003 # - API_PORT=8003
- API_LOG_LEVEL=info # - API_LOG_LEVEL=info
- API_RELOAD=1 # - API_RELOAD=1
- API_APP_NAME=evyos-building-api-gateway # - API_APP_NAME=evyos-building-api-gateway
- API_TITLE=WAG API Building Api Gateway # - API_TITLE=WAG API Building Api Gateway
- API_FORGOT_LINK=https://building_service/forgot-password # - API_FORGOT_LINK=https://building_service/forgot-password
- API_DESCRIPTION=This api is serves as web building api gateway only to evyos web services. # - API_DESCRIPTION=This api is serves as web building api gateway only to evyos web services.
- API_APP_URL=https://building_service # - API_APP_URL=https://building_service
ports: # ports:
- "8003:8003" # - "8003:8003"
mem_limit: 512m # mem_limit: 512m
cpus: 0.5 # cpus: 0.5
management_service: management_service:
container_name: management_service container_name: management_service
@ -151,17 +151,17 @@ services:
mem_limit: 512m mem_limit: 512m
cpus: 0.5 cpus: 0.5
dealer_service: # dealer_service:
container_name: dealer_service # container_name: dealer_service
build: # build:
context: . # context: .
dockerfile: ApiServices/DealerService/Dockerfile # dockerfile: ApiServices/DealerService/Dockerfile
networks: # networks:
- wag-services # - wag-services
env_file: # env_file:
- api_env.env # - api_env.env
mem_limit: 512m # mem_limit: 512m
cpus: 0.5 # cpus: 0.5
networks: networks:
wag-services: wag-services: