450 lines
12 KiB
TypeScript
450 lines
12 KiB
TypeScript
import {
|
|
useInfiniteQuery,
|
|
useMutation,
|
|
useQuery,
|
|
useQueryClient,
|
|
} from '@tanstack/react-query';
|
|
import { useCallback } from 'react';
|
|
import { toast } from 'react-toastify';
|
|
|
|
export function useQueryAPI({ endpoint }: { endpoint: string }) {
|
|
const callEndpoint = async (endpoint: string) => {
|
|
const url = `http://localhost:5000/trigger/${endpoint}`;
|
|
const options: object = {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': '*',
|
|
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
'Access-Control-Allow-Credentials': true,
|
|
'Sec-Fetch-Mode': 'no-cors',
|
|
'Sec-Fetch-Site': 'same-site',
|
|
},
|
|
};
|
|
|
|
const response = await fetch(url, options);
|
|
if (!response.ok) {
|
|
throw new Error(`${response.status} ${response.statusText}`);
|
|
}
|
|
return await response.json();
|
|
};
|
|
|
|
return useQuery({
|
|
queryKey: ['GET', endpoint],
|
|
queryFn: () => callEndpoint(endpoint),
|
|
});
|
|
}
|
|
|
|
export function useMutationAPI() {
|
|
const callEndpoint = async (
|
|
method: string,
|
|
endpoint: string,
|
|
body: object | undefined | null,
|
|
) => {
|
|
const url = `http://localhost:5000/trigger/${endpoint}`;
|
|
const options: any = {
|
|
method: method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': '*',
|
|
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
'Access-Control-Allow-Credentials': true,
|
|
'Sec-Fetch-Mode': 'no-cors',
|
|
'Sec-Fetch-Site': 'same-site',
|
|
},
|
|
body: JSON.stringify(body),
|
|
};
|
|
|
|
const response = await fetch(url, options);
|
|
if (!response.ok) {
|
|
const responseData = await response.json();
|
|
if (typeof responseData.detail !== 'string') {
|
|
const errorResponse = JSON.stringify({
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
method: options.method,
|
|
endpoint: endpoint,
|
|
msg: responseData.detail[0]?.msg,
|
|
loc: responseData.detail[0]?.loc,
|
|
type: responseData.detail[0]?.type,
|
|
});
|
|
|
|
throw new Error(errorResponse);
|
|
}
|
|
if (typeof responseData.detail === 'object') {
|
|
const errorResponse = JSON.stringify({
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
method: options.method,
|
|
endpoint: endpoint,
|
|
msg: responseData.detail.message,
|
|
});
|
|
|
|
throw new Error(errorResponse);
|
|
}
|
|
const errorResponse = JSON.stringify({
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
method: options.method,
|
|
endpoint: endpoint,
|
|
msg: responseData.detail,
|
|
});
|
|
|
|
throw new Error(errorResponse);
|
|
}
|
|
return await response.json();
|
|
};
|
|
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: ({
|
|
method,
|
|
endpoint,
|
|
body = undefined,
|
|
}: {
|
|
method: string;
|
|
endpoint: string;
|
|
body?: object | undefined | null;
|
|
}) => callEndpoint(method, endpoint, body),
|
|
onMutate: () => {},
|
|
onSuccess: (data, variables) => {
|
|
queryClient.invalidateQueries({ queryKey: ['GET', variables.endpoint] });
|
|
|
|
if (variables.endpoint.includes('/')) {
|
|
if ((variables.endpoint.match(/\//g) || []).length > 2) {
|
|
const GetEndpoint = variables.endpoint.split('/');
|
|
const Endpoint = `${GetEndpoint[1]}/${GetEndpoint[2]}`;
|
|
queryClient.invalidateQueries({ queryKey: ['GET', `/${Endpoint}`] });
|
|
} else {
|
|
const GetEndpoint = variables.endpoint.split('/')[1];
|
|
queryClient.invalidateQueries({
|
|
queryKey: ['GET', `/${GetEndpoint}`],
|
|
});
|
|
}
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useFileUploadAPI() {
|
|
const callEndpoint = async (
|
|
method: string,
|
|
endpoint: string,
|
|
body?: object | FormData | undefined | null,
|
|
) => {
|
|
const url = `http://localhost:5000/trigger/${endpoint}`;
|
|
|
|
const options = {
|
|
method: method,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': '*',
|
|
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
'Access-Control-Allow-Credentials': true,
|
|
'Sec-Fetch-Mode': 'no-cors',
|
|
'Sec-Fetch-Site': 'same-site',
|
|
},
|
|
body: body ?? undefined,
|
|
};
|
|
const response = await fetch(url, options as unknown as Body);
|
|
if (!response.ok) {
|
|
const responseData = await response.json();
|
|
if (typeof responseData.detail !== 'string') {
|
|
const errorResponse = JSON.stringify({
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
method: options.method,
|
|
endpoint: endpoint,
|
|
msg: responseData.detail[0].msg,
|
|
loc: responseData.detail[0].loc,
|
|
type: responseData.detail[0].type,
|
|
});
|
|
|
|
throw new Error(errorResponse);
|
|
}
|
|
const errorResponse = JSON.stringify({
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
method: options.method,
|
|
endpoint: endpoint,
|
|
msg: responseData.detail,
|
|
});
|
|
|
|
throw new Error(errorResponse);
|
|
}
|
|
return await response.json();
|
|
};
|
|
|
|
const queryClient = useQueryClient();
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
method,
|
|
endpoint,
|
|
body,
|
|
}: {
|
|
method: string;
|
|
endpoint: string;
|
|
body: object | FormData | undefined | null;
|
|
toastID?: string;
|
|
}) => callEndpoint(method, endpoint, body),
|
|
onMutate: () => {},
|
|
onSuccess: (variables) => {
|
|
queryClient.invalidateQueries({ queryKey: ['GET', variables.endpoint] });
|
|
if (variables.endpoint?.includes('/')) {
|
|
const GetEndpoint = variables.endpoint.split('/')[1];
|
|
queryClient.invalidateQueries({ queryKey: ['GET', `/${GetEndpoint}`] });
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useFetchAllRecordsAPI({ endpoint }: { endpoint: string }) {
|
|
const pageSize = 500;
|
|
const queryClient = useQueryClient();
|
|
|
|
const fetchPage = async (page: number, retryCount = 0): Promise<any> => {
|
|
// Start building the URL
|
|
const url = new URL(`http://localhost:5000/trigger/${endpoint}`);
|
|
|
|
// Check and append custom query parameters from the endpoint
|
|
const customParams = new URLSearchParams(endpoint.split('?')[1]);
|
|
for (const [key, value] of customParams) {
|
|
if (!url.searchParams.has(key)) {
|
|
url.searchParams.append(key, value);
|
|
}
|
|
}
|
|
|
|
// Add 'page' and 'size' parameters
|
|
url.searchParams.set('page', page.toString()); // Use 'set' to ensure uniqueness
|
|
url.searchParams.set('size', pageSize.toString());
|
|
|
|
const options: any = {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': '*',
|
|
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
'Access-Control-Allow-Credentials': true,
|
|
'Sec-Fetch-Mode': 'no-cors',
|
|
'Sec-Fetch-Site': 'same-site',
|
|
},
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(url.toString(), options);
|
|
|
|
if (!response.ok) {
|
|
// If the response is not ok and status is 401 (not authorized), reload the page
|
|
if (response.status === 401) {
|
|
window.location.reload();
|
|
}
|
|
throw new Error(
|
|
`Error fetching page ${page}: ${response.status} ${response.statusText}`,
|
|
);
|
|
}
|
|
return response.json();
|
|
} catch (err) {
|
|
if (retryCount < 3) {
|
|
// Retry up to 3 times with a random delay
|
|
const delay = Math.floor(Math.random() * 3000) + 1000; // Random delay between 1 to 3 seconds
|
|
console.error(
|
|
`Retrying page ${page}, attempt ${retryCount + 1} after ${delay} ms`,
|
|
);
|
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
return fetchPage(page, retryCount + 1);
|
|
}
|
|
console.error(`Error fetching page ${page} after 3 retries:`, err);
|
|
throw err; // Re-throw the error to handle it in the calling function
|
|
}
|
|
};
|
|
|
|
const callEndpoint = async () => {
|
|
try {
|
|
// Fetch the first page to get the total number of items and pages
|
|
const firstPageData = await fetchPage(1);
|
|
// If the first page doesn't have a 'pages' property, return the data as-is
|
|
if (firstPageData?.pages === undefined) {
|
|
return firstPageData;
|
|
}
|
|
if (firstPageData?.pages === 1) {
|
|
return firstPageData.items;
|
|
}
|
|
|
|
let totalPages = firstPageData.pages;
|
|
const allItems = [...firstPageData.items];
|
|
// Fetch each page sequentially
|
|
for (let page = 2; page <= totalPages; page++) {
|
|
try {
|
|
const pageData = await fetchPage(page);
|
|
allItems.push(...pageData.items);
|
|
|
|
// Check and update the totalPages if it has increased
|
|
if (pageData.pages > totalPages) {
|
|
totalPages = pageData.pages;
|
|
}
|
|
} catch (err) {
|
|
console.error(`Final error fetching page ${page}:`, err);
|
|
// Continue to the next page even if the current page fails
|
|
}
|
|
}
|
|
|
|
return allItems;
|
|
} catch (error) {
|
|
// Handling errors in fetching the first page
|
|
console.error('Error fetching the first page:', error);
|
|
return []; // Return empty array on error
|
|
}
|
|
};
|
|
|
|
const invalidate = useCallback(() => {
|
|
queryClient.invalidateQueries({ queryKey: ['GET', endpoint] });
|
|
}, [queryClient, endpoint]);
|
|
|
|
return {
|
|
...useQuery({
|
|
queryKey: ['GET', endpoint],
|
|
queryFn: callEndpoint,
|
|
}),
|
|
invalidate,
|
|
};
|
|
}
|
|
|
|
interface DownloadFileOptions {
|
|
endpoint: string;
|
|
file_name: string;
|
|
}
|
|
|
|
export function useDownloadFile() {
|
|
const downloadFile = useCallback(
|
|
async ({ endpoint, file_name }: DownloadFileOptions) => {
|
|
const url = `http://localhost:5000/trigger/${endpoint}`;
|
|
const options: any = {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': '*',
|
|
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
'Access-Control-Allow-Credentials': true,
|
|
'Sec-Fetch-Mode': 'no-cors',
|
|
'Sec-Fetch-Site': 'same-site',
|
|
},
|
|
};
|
|
const toastID = toast.loading('Processing...');
|
|
try {
|
|
const response = await fetch(url, options);
|
|
if (!response.ok) {
|
|
throw new Error('Network response was not ok');
|
|
}
|
|
|
|
const blob = await response.blob();
|
|
if (blob.type === 'application/json') {
|
|
const blob_text = await blob.text();
|
|
const cleaned_text = blob_text.replace(/"/g, '');
|
|
toast.update(toastID, {
|
|
render: cleaned_text,
|
|
type: 'info',
|
|
isLoading: false,
|
|
autoClose: 5000,
|
|
closeButton: null,
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (typeof window !== 'undefined') {
|
|
const blobURL = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
|
|
a.style.display = 'none';
|
|
a.href = blobURL;
|
|
a.download = file_name;
|
|
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
window.URL.revokeObjectURL(blobURL);
|
|
document.body.removeChild(a);
|
|
toast.update(toastID, {
|
|
render: `${file_name} Downloaded`,
|
|
type: 'success',
|
|
isLoading: false,
|
|
autoClose: 5000,
|
|
closeButton: null,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Download failed:', error);
|
|
toast.update(toastID, {
|
|
render: `Error Downloading ${file_name}. Please try again.`,
|
|
type: 'error',
|
|
isLoading: false,
|
|
autoClose: 5000,
|
|
closeButton: null,
|
|
});
|
|
}
|
|
},
|
|
[],
|
|
);
|
|
|
|
return downloadFile;
|
|
}
|
|
|
|
export const useFetchInfiniteQuery = ({
|
|
endpoint,
|
|
pageSize = 10,
|
|
}: {
|
|
endpoint: string;
|
|
pageSize?: number;
|
|
}) => {
|
|
const queryKey = ['GET', endpoint];
|
|
|
|
const fetchInfiniteQuery = async (endpoint: string) => {
|
|
const url = `http://localhost:5000/trigger/${endpoint}`;
|
|
const requestOptions: any = {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Access-Control-Allow-Origin': '*',
|
|
'Access-Control-Allow-Headers': '*',
|
|
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
'Access-Control-Allow-Credentials': true,
|
|
'Sec-Fetch-Mode': 'no-cors',
|
|
'Sec-Fetch-Site': 'same-site',
|
|
},
|
|
};
|
|
try {
|
|
const response = await fetch(url, requestOptions);
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('Error fetching transactions:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
const queryFn = ({ pageParam = 1 }) =>
|
|
fetchInfiniteQuery(`${endpoint}&page=${pageParam}&size=${pageSize}`);
|
|
const getNextPageParam = (lastPage: any, allPages: any) => {
|
|
if (lastPage.items.length < pageSize) {
|
|
return undefined;
|
|
}
|
|
return allPages.length + 1;
|
|
};
|
|
const { data, isLoading, isFetchingNextPage, fetchNextPage, isError, isFetching } =
|
|
useInfiniteQuery({
|
|
queryKey,
|
|
queryFn,
|
|
getNextPageParam,
|
|
initialPageParam: 1,
|
|
});
|
|
return {
|
|
data,
|
|
isLoading,
|
|
isFetchingNextPage,
|
|
fetchNextPage,
|
|
isError,
|
|
isFetching,
|
|
};
|
|
};
|