updated pagination commit to carry
This commit is contained in:
parent
ac344773c5
commit
2d418644bb
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,12 +18,9 @@ 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) => {
|
||||||
|
|
@ -35,15 +38,18 @@ export const PaginationToolsComponent: React.FC<PaginationToolsComponentProps> =
|
||||||
{(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(
|
-{" "}
|
||||||
|
{Math.min(
|
||||||
pagination.page * pagination.size,
|
pagination.page * pagination.size,
|
||||||
pagination.totalCount || pagination.allCount || 0
|
pagination.totalCount || pagination.allCount || 0
|
||||||
)} {t.of} {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 &&
|
||||||
|
pagination.totalCount !== (pagination.allCount || 0) && (
|
||||||
<div>
|
<div>
|
||||||
{t.total}: {pagination.allCount || 0} {t.items} ({t.filtered}: {pagination.totalCount} {t.items})
|
{t.total}: {pagination.allCount || 0} {t.items} ({t.filtered}:{" "}
|
||||||
|
{pagination.totalCount} {t.items})
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -54,25 +60,40 @@ 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
|
{
|
||||||
|
length: Math.min(
|
||||||
|
5,
|
||||||
|
Math.max(
|
||||||
|
1,
|
||||||
|
Math.ceil(
|
||||||
|
(pagination.totalCount &&
|
||||||
|
pagination.totalCount !== pagination.allCount
|
||||||
? pagination.totalCount
|
? pagination.totalCount
|
||||||
: (pagination.allCount || 0)) / pagination.size
|
: pagination.allCount || 0) / pagination.size
|
||||||
))) }, (_, i) => {
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
(_, i) => {
|
||||||
// Show pages around current page
|
// Show pages around current page
|
||||||
let pageNum;
|
let pageNum;
|
||||||
const calculatedTotalPages = Math.max(1, Math.ceil(
|
const calculatedTotalPages = Math.max(
|
||||||
(pagination.totalCount && pagination.totalCount !== pagination.allCount
|
1,
|
||||||
|
Math.ceil(
|
||||||
|
(pagination.totalCount &&
|
||||||
|
pagination.totalCount !== pagination.allCount
|
||||||
? pagination.totalCount
|
? pagination.totalCount
|
||||||
: (pagination.allCount || 0)) / pagination.size
|
: pagination.allCount || 0) / pagination.size
|
||||||
));
|
)
|
||||||
|
);
|
||||||
if (calculatedTotalPages <= 5) {
|
if (calculatedTotalPages <= 5) {
|
||||||
pageNum = i + 1;
|
pageNum = i + 1;
|
||||||
} else if (pagination.page <= 3) {
|
} else if (pagination.page <= 3) {
|
||||||
|
|
@ -95,29 +116,31 @@ export const PaginationToolsComponent: React.FC<PaginationToolsComponentProps> =
|
||||||
{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(
|
||||||
|
1,
|
||||||
|
Math.ceil(
|
||||||
|
(pagination.totalCount &&
|
||||||
|
pagination.totalCount !== pagination.allCount
|
||||||
? pagination.totalCount
|
? pagination.totalCount
|
||||||
: (pagination.allCount || 0)) / pagination.size
|
: 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
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +105,11 @@ export function useApplicationData() {
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -109,7 +119,7 @@ export function useApplicationData() {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +130,8 @@ export function useApplicationData() {
|
||||||
totalPages: 0,
|
totalPages: 0,
|
||||||
pageCount: 0,
|
pageCount: 0,
|
||||||
allCount: 0,
|
allCount: 0,
|
||||||
|
next: true,
|
||||||
|
back: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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]);
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue