302 lines
9.4 KiB
TypeScript
302 lines
9.4 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
import React, { useCallback, useEffect } from 'react';
|
|
|
|
import type { APIOptions } from '../../utils/types';
|
|
import type { GridlerColumn } from '../Column';
|
|
|
|
import {
|
|
type FetchAPIOperation,
|
|
GoAPIHeaders,
|
|
type GoAPIOperation,
|
|
} from '../../utils/golang-restapi-v2';
|
|
import { useGridlerStore } from '../GridlerStore';
|
|
|
|
export interface GlidlerAPIAdaptorForGoLangv2Props<T = unknown> extends APIOptions {
|
|
filter?: string;
|
|
hotfields?: Array<string>;
|
|
initialData?: Array<T>;
|
|
options?: Array<GoAPIOperation>;
|
|
}
|
|
|
|
function _GlidlerAPIAdaptorForGoLangv2<T = unknown>(props: GlidlerAPIAdaptorForGoLangv2Props<T>) {
|
|
const [setStateFN, setState, getState, addError, mounted] = useGridlerStore((s) => [
|
|
s.setStateFN,
|
|
s.setState,
|
|
s.getState,
|
|
s.addError,
|
|
s.mounted,
|
|
]);
|
|
|
|
const useAPIQuery: (index: number) => Promise<any> = useCallback(
|
|
async (index: number) => {
|
|
const columns = getState('columns');
|
|
const colSort = getState('colSort');
|
|
const pageSize = getState('pageSize');
|
|
const colFilters = getState('colFilters');
|
|
const searchStr = getState('searchStr');
|
|
const searchFields = getState('searchFields');
|
|
const _active_requests = getState('_active_requests');
|
|
const keyField = getState('keyField');
|
|
setState('loadingData', true);
|
|
try {
|
|
//console.log('APIAdaptorGoLangv2', { _active_requests, index, pageSize, props });
|
|
if (props && props.url) {
|
|
const head = new Headers();
|
|
|
|
head.set('Authorization', `Token ${props.authtoken}`);
|
|
const ops: FetchAPIOperation[] = [
|
|
{ type: 'limit', value: String(pageSize ?? 50) },
|
|
{ type: 'offset', value: String((pageSize ?? 50) * index) },
|
|
];
|
|
|
|
if (colSort?.length && colSort.length > 0) {
|
|
ops.push({
|
|
type: 'sort',
|
|
value: colSort
|
|
?.map((sort: any) => `${sort.id} ${sort.direction}`)
|
|
.reduce((acc: any, val: any) => `${acc},${val}`),
|
|
});
|
|
}
|
|
|
|
colFilters
|
|
?.filter((f) => f.value?.length > 0)
|
|
?.forEach((filter: any) => {
|
|
if (filter.value && filter.value !== '') {
|
|
ops.push({
|
|
name: `${filter.id}`,
|
|
op: filter.operator,
|
|
type: 'searchop',
|
|
value: filter.value,
|
|
});
|
|
}
|
|
});
|
|
|
|
if (searchStr && searchStr !== '') {
|
|
columns
|
|
?.filter(
|
|
(f) =>
|
|
!f.disableFilter &&
|
|
!f.disableSearch &&
|
|
!f.virtual &&
|
|
f.id &&
|
|
((searchFields ?? []).length == 0 || searchFields?.includes(f.id))
|
|
)
|
|
?.forEach((filter: any) => {
|
|
ops.push({
|
|
name: `${filter.id ?? ""}`,
|
|
op: 'contains',
|
|
type: 'searchor',
|
|
value: searchStr,
|
|
});
|
|
});
|
|
}
|
|
|
|
if (props.filter && props.filter !== '') {
|
|
ops.push({
|
|
name: 'sql_filter',
|
|
type: 'custom-sql-w',
|
|
value: props.filter,
|
|
});
|
|
}
|
|
|
|
if ((props.options ?? []).length > 0) {
|
|
ops.push(...(props.options ?? []));
|
|
}
|
|
|
|
const col_ids =
|
|
columns
|
|
?.filter((col) => !col.virtual)
|
|
?.map((col: GridlerColumn) => {
|
|
return col.id;
|
|
}) ?? [];
|
|
|
|
if (props.hotfields && props.hotfields.length > 0) {
|
|
col_ids?.push(props.hotfields.join(','));
|
|
}
|
|
|
|
if (keyField) {
|
|
if (!col_ids.includes(keyField)) {
|
|
col_ids.push(keyField);
|
|
}
|
|
}
|
|
|
|
if (col_ids && col_ids.length > 0) {
|
|
ops.push({
|
|
type: 'select-fields',
|
|
value: col_ids.join(','),
|
|
});
|
|
}
|
|
|
|
if (ops && ops.length > 0) {
|
|
const optionHeaders = GoAPIHeaders(ops);
|
|
for (const oh in GoAPIHeaders(ops)) {
|
|
head.set(oh, optionHeaders[oh]);
|
|
}
|
|
}
|
|
|
|
const currentRequestIndex = _active_requests?.findIndex((f) => f.page === index) ?? -1;
|
|
_active_requests?.forEach((r) => {
|
|
if ((r.page >= 0 && r.page < index - 2) || (index >= 0 && r.page > index + 2)) {
|
|
r.controller?.abort?.();
|
|
}
|
|
});
|
|
|
|
if (
|
|
_active_requests &&
|
|
currentRequestIndex >= 0 &&
|
|
_active_requests[currentRequestIndex]
|
|
) {
|
|
//console.log(`Already queued ${index}`, index, s._active_requests);
|
|
setState('loadingData', false);
|
|
return undefined;
|
|
}
|
|
|
|
const controller = new AbortController();
|
|
await setStateFN('_active_requests', (cv) => [
|
|
...(cv ?? []),
|
|
{ controller, page: index },
|
|
]);
|
|
|
|
const res = await fetch(
|
|
`${props.url}?x-limit=${String(pageSize ?? 50)}&x-offset=${String((pageSize ?? 50) * index)}`,
|
|
{
|
|
headers: head,
|
|
method: 'GET',
|
|
signal: controller?.signal,
|
|
}
|
|
);
|
|
|
|
if (res.ok) {
|
|
const cr = res.headers.get('Content-Range')?.split('/');
|
|
if (cr?.[1] && parseInt(cr[1], 10) > 0) {
|
|
setState('total_rows', parseInt(cr[1], 10));
|
|
}
|
|
|
|
const data = await res.json();
|
|
setState('loadingData', false);
|
|
return data ?? [];
|
|
}
|
|
addError(`${res.status} ${res.statusText}`, 'api', props.url);
|
|
|
|
await setStateFN('_active_requests', (cv) => [
|
|
...(cv ?? []).filter((f) => f.page !== index),
|
|
]);
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
} catch (_e) {
|
|
//console.log('APIAdaptorGoLangv2 error', e);
|
|
//addError(`Error: ${e}`, 'api', props.url);
|
|
}
|
|
setState('loadingData', false);
|
|
return [];
|
|
},
|
|
[
|
|
getState,
|
|
props.authtoken,
|
|
props.url,
|
|
props.filter,
|
|
JSON.stringify(props.options),
|
|
setState,
|
|
setStateFN,
|
|
addError,
|
|
]
|
|
);
|
|
|
|
const askAPIRowNumber: (key: string) => Promise<number> = useCallback(
|
|
async (key: string) => {
|
|
const colFilters = getState('colFilters');
|
|
|
|
//console.log('APIAdaptorGoLangv2', { _active_requests, index, pageSize, props });
|
|
if (props && props.url) {
|
|
const head = new Headers();
|
|
const ops: FetchAPIOperation[] = [
|
|
{ type: 'limit', value: String(10) },
|
|
{ type: 'fetch-rownumber', value: key },
|
|
];
|
|
|
|
head.set('Authorization', `Token ${props.authtoken}`);
|
|
|
|
if (colFilters?.length && colFilters.length > 0) {
|
|
colFilters
|
|
?.filter((f) => f.value?.length > 0)
|
|
?.forEach((filter: any) => {
|
|
if (filter.value && filter.value !== '') {
|
|
head.set(`x-searchop-${filter.operator}-${filter.id}`, `${filter.value}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (props.filter && props.filter !== '') {
|
|
ops.push({
|
|
name: 'sql_filter',
|
|
type: 'custom-sql-w',
|
|
value: `(${props.filter})`,
|
|
});
|
|
}
|
|
|
|
if (props.options && props.options.length > 0) {
|
|
const optionHeaders = GoAPIHeaders(props.options);
|
|
for (const oh in optionHeaders) {
|
|
head.set(oh, optionHeaders[oh]);
|
|
}
|
|
}
|
|
|
|
if (ops && ops.length > 0) {
|
|
const optionHeaders = GoAPIHeaders(ops);
|
|
for (const oh in GoAPIHeaders(ops)) {
|
|
head.set(oh, optionHeaders[oh]);
|
|
}
|
|
}
|
|
|
|
const controller = new AbortController();
|
|
|
|
const res = await fetch(`${props.url}?x-fetch-rownumber=${key}}`, {
|
|
headers: head,
|
|
method: 'GET',
|
|
signal: controller?.signal,
|
|
});
|
|
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
|
|
return data?.[0]?._rownumber ?? data?._rownumber ?? 0;
|
|
}
|
|
addError(`${res.status} ${res.statusText}`, 'api', props.url);
|
|
}
|
|
return [];
|
|
},
|
|
[props.url, props.authtoken, props.filter, props.options, getState, addError]
|
|
);
|
|
|
|
//Reset the function in the store.
|
|
useEffect(() => {
|
|
setState('useAPIQuery', useAPIQuery);
|
|
setState('askAPIRowNumber', askAPIRowNumber);
|
|
const isValuesInPages = getState('isValuesInPages');
|
|
|
|
const _refresh = getState('_refresh');
|
|
if (!isValuesInPages) {
|
|
setState('values', []);
|
|
}
|
|
|
|
//Reset the loaded pages to new rules
|
|
_refresh?.().then(() => {
|
|
const onChange = getState('onChange');
|
|
const getGridSelectedRows = getState('getGridSelectedRows');
|
|
if (onChange && typeof onChange === 'function') {
|
|
const buffers = getGridSelectedRows?.();
|
|
onChange(buffers);
|
|
}
|
|
});
|
|
}, [props.url, props.authtoken, props.filter, JSON.stringify(props.options), mounted, setState]);
|
|
|
|
return <></>;
|
|
}
|
|
|
|
|
|
|
|
//The computer component does not need to be recalculated on every render, so we use React.memo to prevent unnecessary re-renders.
|
|
export const GlidlerAPIAdaptorForGoLangv2 = React.memo(_GlidlerAPIAdaptorForGoLangv2);
|
|
|
|
GlidlerAPIAdaptorForGoLangv2.displayName = 'Gridler-GlidlerAPIAdaptorForGoLangv2';
|