mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 11:10:54 +00:00
[UI] Refactor useQuery hooks (#9894)
* Improvements for table loading - Retry table queries on failure - Properly store failure modes - Increasing standoff time on query failure * Add error messages * Better error extraction * Simplify error handling * Update NotesEditor * Update dashboard items * Tweak table refetch * Refactor notifications query * Fix for calendar querty * Other fixes * Allow retry for search query * Further adjustments * Improved dashboard * Upate more useQuery hooks * Fix broken URL (was used for testing) * Remove custom delay * Revert change to noRecordsText
This commit is contained in:
@ -72,9 +72,6 @@ export function PrintingActions({
|
||||
.then((response: any) => {
|
||||
return extractAvailableFields(response, 'POST') || {};
|
||||
})
|
||||
.catch(() => {
|
||||
return {};
|
||||
})
|
||||
});
|
||||
|
||||
const labelFields: ApiFormFieldSet = useMemo(() => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Alert, Card, Center, Divider, Loader, Text } from '@mantine/core';
|
||||
import { useDisclosure, useHotkeys } from '@mantine/hooks';
|
||||
import { IconInfoCircle } from '@tabler/icons-react';
|
||||
import { IconExclamationCircle, IconInfoCircle } from '@tabler/icons-react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { type Layout, Responsive, WidthProvider } from 'react-grid-layout';
|
||||
|
||||
@ -220,6 +220,11 @@ export default function DashboardLayout() {
|
||||
removing={removing}
|
||||
/>
|
||||
<Divider p='xs' />
|
||||
{availableWidgets.error && (
|
||||
<Alert color='red' title={t`Error`} icon={<IconExclamationCircle />}>
|
||||
{t`Failed to load dashboard widgets.`}
|
||||
</Alert>
|
||||
)}
|
||||
{layouts && loaded && availableWidgets.loaded ? (
|
||||
<>
|
||||
{widgetLabels.length == 0 ? (
|
||||
|
@ -47,8 +47,7 @@ function QueryCountWidget({
|
||||
limit: 1
|
||||
}
|
||||
})
|
||||
.then((res) => res.data)
|
||||
.catch(() => {});
|
||||
.then((res) => res.data);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -196,19 +196,14 @@ function NameBadge({
|
||||
|
||||
const url = apiUrl(path, pk);
|
||||
|
||||
return api
|
||||
.get(url)
|
||||
.then((response) => {
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
return response.data;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
return {};
|
||||
});
|
||||
return api.get(url).then((response) => {
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
return response.data;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -356,9 +351,6 @@ function TableAnchorValue(props: Readonly<FieldProps>) {
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
return {};
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -92,11 +92,9 @@ export default function NotesEditor({
|
||||
|
||||
const dataQuery = useQuery({
|
||||
queryKey: ['notes-editor', noteUrl, modelType, modelId],
|
||||
retry: 5,
|
||||
queryFn: () =>
|
||||
api
|
||||
.get(noteUrl)
|
||||
.then((response) => response.data?.notes ?? '')
|
||||
.catch(() => ''),
|
||||
api.get(noteUrl).then((response) => response.data?.notes ?? ''),
|
||||
enabled: true
|
||||
});
|
||||
|
||||
|
@ -254,19 +254,14 @@ export function ApiForm({
|
||||
props.pathParams
|
||||
],
|
||||
queryFn: async () => {
|
||||
return await api
|
||||
.get(url)
|
||||
.then((response: any) => {
|
||||
// Process API response
|
||||
const fetchedData: any = processFields(fields, response.data);
|
||||
return await api.get(url).then((response: any) => {
|
||||
// Process API response
|
||||
const fetchedData: any = processFields(fields, response.data);
|
||||
|
||||
// Update form values, but only for the fields specified for this form
|
||||
form.reset(fetchedData);
|
||||
return fetchedData;
|
||||
})
|
||||
.catch(() => {
|
||||
return {};
|
||||
});
|
||||
// Update form values, but only for the fields specified for this form
|
||||
form.reset(fetchedData);
|
||||
return fetchedData;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -179,10 +179,6 @@ export function RelatedModelField({
|
||||
setData(values);
|
||||
dataRef.current = values;
|
||||
return response;
|
||||
})
|
||||
.catch((error) => {
|
||||
setData([]);
|
||||
return error;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -57,10 +57,7 @@ export function LicenseModal() {
|
||||
queryKey: ['license'],
|
||||
refetchOnMount: true,
|
||||
queryFn: () =>
|
||||
api
|
||||
.get(apiUrl(ApiEndpoints.license))
|
||||
.then((res) => res.data ?? {})
|
||||
.catch(() => {})
|
||||
api.get(apiUrl(ApiEndpoints.license)).then((res) => res.data ?? {})
|
||||
});
|
||||
|
||||
const packageKeys = useMemo(() => {
|
||||
|
@ -78,23 +78,17 @@ export function Header() {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
return api
|
||||
.get(apiUrl(ApiEndpoints.notifications_list), {
|
||||
params: {
|
||||
read: false,
|
||||
limit: 1
|
||||
}
|
||||
};
|
||||
const response = await api
|
||||
.get(apiUrl(ApiEndpoints.notifications_list), params)
|
||||
.catch(() => {
|
||||
return null;
|
||||
});
|
||||
setNotificationCount(response?.data?.count ?? 0);
|
||||
return response?.data ?? null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.then((response: any) => {
|
||||
setNotificationCount(response?.data?.count ?? 0);
|
||||
return response.data ?? null;
|
||||
});
|
||||
},
|
||||
// Refetch every minute, *if* the tab is visible
|
||||
refetchInterval: 60 * 1000,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
Alert,
|
||||
Anchor,
|
||||
Divider,
|
||||
Drawer,
|
||||
@ -15,6 +16,7 @@ import {
|
||||
import {
|
||||
IconChevronDown,
|
||||
IconChevronRight,
|
||||
IconExclamationCircle,
|
||||
IconSitemap
|
||||
} from '@tabler/icons-react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
@ -29,6 +31,7 @@ import {
|
||||
getDetailUrl,
|
||||
navigateToLink
|
||||
} from '@lib/functions/Navigation';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useApi } from '../../contexts/ApiContext';
|
||||
import { ApiIcon } from '../items/ApiIcon';
|
||||
import { StylishText } from '../items/StylishText';
|
||||
@ -67,10 +70,6 @@ export default function NavigationTree({
|
||||
}
|
||||
})
|
||||
.then((response) => response.data ?? [])
|
||||
.catch((error) => {
|
||||
console.error(`Error fetching ${modelType} tree`);
|
||||
return [];
|
||||
})
|
||||
});
|
||||
|
||||
const follow = useCallback(
|
||||
@ -96,7 +95,7 @@ export default function NavigationTree({
|
||||
const nodes: Record<number, any> = {};
|
||||
const tree: TreeNodeData[] = [];
|
||||
|
||||
if (!query?.data?.length) {
|
||||
if (!query || !query?.data?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -207,7 +206,13 @@ export default function NavigationTree({
|
||||
<Stack gap='xs'>
|
||||
<Divider />
|
||||
<LoadingOverlay visible={query.isFetching || query.isLoading} />
|
||||
<Tree data={data} tree={treeState} renderNode={renderNode} />
|
||||
{query.isError ? (
|
||||
<Alert color='red' title={t`Error`} icon={<IconExclamationCircle />}>
|
||||
{t`Error loading navigation tree.`}
|
||||
</Alert>
|
||||
) : (
|
||||
<Tree data={data} tree={treeState} renderNode={renderNode} />
|
||||
)}
|
||||
</Stack>
|
||||
</Drawer>
|
||||
);
|
||||
|
@ -122,10 +122,7 @@ export function NotificationDrawer({
|
||||
ordering: '-creation'
|
||||
}
|
||||
})
|
||||
.then((response) => response.data)
|
||||
.catch((error) => {
|
||||
return error;
|
||||
}),
|
||||
.then((response) => response.data),
|
||||
refetchOnMount: false
|
||||
});
|
||||
|
||||
|
@ -397,11 +397,7 @@ export function SearchDrawer({
|
||||
|
||||
return api
|
||||
.post(apiUrl(ApiEndpoints.api_search), params)
|
||||
.then((response) => response.data)
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
return [];
|
||||
});
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
// Search query manager
|
||||
|
@ -137,10 +137,7 @@ export function RenderRemoteInstance({
|
||||
queryFn: async () => {
|
||||
const url = apiUrl(ModelInformationDict[model].api_endpoint, pk);
|
||||
|
||||
return api
|
||||
.get(url)
|
||||
.then((response) => response.data)
|
||||
.catch(() => null);
|
||||
return api.get(url).then((response) => response.data);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1100,10 +1100,7 @@ function useStockOperationModal({
|
||||
.get(url, {
|
||||
params: params
|
||||
})
|
||||
.then((response) => response.data ?? [])
|
||||
.catch(() => {
|
||||
return [];
|
||||
});
|
||||
.then((response) => response.data ?? []);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1368,7 +1365,7 @@ export function useFindSerialNumberForm({
|
||||
}
|
||||
},
|
||||
checkClose: (data, form) => {
|
||||
if (data.length == 0) {
|
||||
if (!data || data?.length == 0) {
|
||||
form.setError('serial', { message: t`No matching items` });
|
||||
return false;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ export function extractErrorMessage({
|
||||
field?: string;
|
||||
defaultMessage?: string;
|
||||
}): string {
|
||||
const error_data = error.response?.data ?? null;
|
||||
const error_data = error.response?.data ?? error.data ?? null;
|
||||
|
||||
let message = '';
|
||||
|
||||
@ -25,7 +25,7 @@ export function extractErrorMessage({
|
||||
|
||||
// No message? Look at the response status codes
|
||||
if (!message) {
|
||||
const status = error.response?.status ?? null;
|
||||
const status = error.status ?? error.response?.status ?? null;
|
||||
|
||||
if (status) {
|
||||
switch (status) {
|
||||
@ -48,8 +48,11 @@ export function extractErrorMessage({
|
||||
message = t`Internal server error`;
|
||||
break;
|
||||
default:
|
||||
message = t`Unknown error`;
|
||||
break;
|
||||
}
|
||||
|
||||
message = `${status} - ${message}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,12 +80,14 @@ export function showApiErrorMessage({
|
||||
error,
|
||||
title,
|
||||
message,
|
||||
field
|
||||
field,
|
||||
id
|
||||
}: {
|
||||
error: any;
|
||||
title: string;
|
||||
message?: string;
|
||||
field?: string;
|
||||
id?: string;
|
||||
}) {
|
||||
const errorMessage = extractErrorMessage({
|
||||
error: error,
|
||||
@ -93,7 +95,10 @@ export function showApiErrorMessage({
|
||||
defaultMessage: message
|
||||
});
|
||||
|
||||
notifications.hide(id ?? 'api-error');
|
||||
|
||||
notifications.show({
|
||||
id: id ?? 'api-error',
|
||||
title: title,
|
||||
message: errorMessage,
|
||||
color: 'red'
|
||||
|
@ -103,6 +103,14 @@ export default function useCalendar({
|
||||
const query = useQuery({
|
||||
enabled: !!startDate && !!endDate,
|
||||
queryKey: ['calendar', name, endpoint, queryFilters],
|
||||
throwOnError: (error: any) => {
|
||||
showApiErrorMessage({
|
||||
error: error,
|
||||
title: 'Error fetching calendar data'
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
queryFn: async () => {
|
||||
// Fetch data from the API
|
||||
return api
|
||||
@ -111,12 +119,6 @@ export default function useCalendar({
|
||||
})
|
||||
.then((response) => {
|
||||
return response.data ?? [];
|
||||
})
|
||||
.catch((error) => {
|
||||
showApiErrorMessage({
|
||||
error: error,
|
||||
title: 'Error fetching calendar data'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -173,6 +175,6 @@ export default function useCalendar({
|
||||
setEndDate,
|
||||
exportModal,
|
||||
query: query,
|
||||
data: query.data
|
||||
data: query.data ?? []
|
||||
};
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import { useUserState } from '../states/UserState';
|
||||
interface DashboardLibraryProps {
|
||||
items: DashboardWidgetProps[];
|
||||
loaded: boolean;
|
||||
error: any;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,13 +52,7 @@ export function useDashboardItems(): DashboardLibraryProps {
|
||||
feature_type: PluginUIFeatureType.dashboard
|
||||
});
|
||||
|
||||
return api
|
||||
.get(url)
|
||||
.then((response: any) => response.data)
|
||||
.catch((_error: any) => {
|
||||
console.error('ERR: Failed to fetch plugin dashboard items');
|
||||
return [];
|
||||
});
|
||||
return api.get(url).then((response: any) => response.data);
|
||||
}
|
||||
});
|
||||
|
||||
@ -90,7 +85,7 @@ export function useDashboardItems(): DashboardLibraryProps {
|
||||
};
|
||||
}) ?? []
|
||||
);
|
||||
}, [pluginQuery, inventreeContext]);
|
||||
}, [pluginQuery.data, inventreeContext]);
|
||||
|
||||
const items: DashboardWidgetProps[] = useMemo(() => {
|
||||
const widgets = [...builtin, ...pluginDashboardItems];
|
||||
@ -113,6 +108,7 @@ export function useDashboardItems(): DashboardLibraryProps {
|
||||
|
||||
return {
|
||||
items: items,
|
||||
loaded: loaded
|
||||
loaded: loaded,
|
||||
error: pluginQuery.error
|
||||
};
|
||||
}
|
||||
|
@ -72,9 +72,6 @@ export default function useDataExport({
|
||||
.then((response: any) => {
|
||||
return extractAvailableFields(response, 'GET') || {};
|
||||
})
|
||||
.catch(() => {
|
||||
return {};
|
||||
})
|
||||
});
|
||||
|
||||
// Construct a field set for the export form
|
||||
|
@ -39,8 +39,7 @@ export function useFilters(props: UseFilterProps) {
|
||||
}
|
||||
|
||||
return data;
|
||||
})
|
||||
.catch((error) => []);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -85,6 +85,13 @@ export function useGenerator(props: GeneratorProps): GeneratorState {
|
||||
],
|
||||
refetchOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
throwOnError: (error: any) => {
|
||||
console.error(
|
||||
`Error generating ${props.key} @ ${props.endpoint}:`,
|
||||
error
|
||||
);
|
||||
return false;
|
||||
},
|
||||
queryFn: async () => {
|
||||
const generatorQuery = {
|
||||
...(props.initialQuery ?? {}),
|
||||
@ -105,14 +112,6 @@ export function useGenerator(props: GeneratorProps): GeneratorState {
|
||||
props.onGenerate?.(value);
|
||||
|
||||
return response;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
`Error generating ${props.key} @ ${props.endpoint}:`,
|
||||
error
|
||||
);
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -53,6 +53,10 @@ export function usePluginPanels({
|
||||
const pluginQuery = useQuery({
|
||||
enabled: pluginPanelsEnabled && !!model && id !== undefined,
|
||||
queryKey: ['custom-plugin-panels', model, id],
|
||||
throwOnError: (error: any) => {
|
||||
console.error('ERR: Failed to fetch plugin panels');
|
||||
return false;
|
||||
},
|
||||
queryFn: async () => {
|
||||
if (!pluginPanelsEnabled || !model) {
|
||||
return Promise.resolve([]);
|
||||
@ -69,11 +73,7 @@ export function usePluginPanels({
|
||||
target_id: id
|
||||
}
|
||||
})
|
||||
.then((response: any) => response.data)
|
||||
.catch((_error: any) => {
|
||||
console.error('ERR: Failed to fetch plugin panels');
|
||||
return [];
|
||||
});
|
||||
.then((response: any) => response.data);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -723,8 +723,7 @@ export default function PartDetail() {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
});
|
||||
|
||||
return revisions;
|
||||
}
|
||||
|
@ -491,9 +491,6 @@ export default function StockDetail() {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -504,7 +501,7 @@ export default function StockDetail() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (trackedBomItemQuery.data != null) {
|
||||
if (!!trackedBomItemQuery.data) {
|
||||
return trackedBomItemQuery.data;
|
||||
}
|
||||
|
||||
|
@ -28,12 +28,12 @@ import { navigateToLink } from '@lib/functions/Navigation';
|
||||
import type { TableFilter } from '@lib/types/Filters';
|
||||
import type { ApiFormFieldSet } from '@lib/types/Forms';
|
||||
import type { TableState } from '@lib/types/Tables';
|
||||
import { hideNotification, showNotification } from '@mantine/notifications';
|
||||
import { IconArrowRight } from '@tabler/icons-react';
|
||||
import { Boundary } from '../components/Boundary';
|
||||
import { useApi } from '../contexts/ApiContext';
|
||||
import { resolveItem } from '../functions/conversion';
|
||||
import { extractAvailableFields, mapFields } from '../functions/forms';
|
||||
import { showApiErrorMessage } from '../functions/notifications';
|
||||
import { useLocalState } from '../states/LocalState';
|
||||
import type { TableColumn } from './Column';
|
||||
import InvenTreeTableHeader from './InvenTreeTableHeader';
|
||||
@ -199,7 +199,16 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
tableProps.params,
|
||||
props.enableColumnCaching
|
||||
],
|
||||
retry: 3,
|
||||
retry: 5,
|
||||
retryDelay: (attempt: number) => (1 + attempt) * 250,
|
||||
throwOnError: (error: any) => {
|
||||
showApiErrorMessage({
|
||||
error: error,
|
||||
title: t`Error loading table options`
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
refetchOnMount: true,
|
||||
gcTime: 5000,
|
||||
queryFn: async () => {
|
||||
@ -240,17 +249,6 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
setTableColumnNames(cacheKey)(names);
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
.catch(() => {
|
||||
hideNotification('table-options-error');
|
||||
showNotification({
|
||||
id: 'table-options-error',
|
||||
title: t`API Error`,
|
||||
message: t`Failed to load table options`,
|
||||
color: 'red'
|
||||
});
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
@ -270,9 +268,7 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
setFieldNames(cachedNames);
|
||||
return;
|
||||
}
|
||||
|
||||
tableOptionQuery.refetch();
|
||||
}, [cacheKey, url, props.params, props.enableColumnCaching]);
|
||||
}, []);
|
||||
|
||||
const enableSelection: boolean = useMemo(() => {
|
||||
return tableProps.enableSelection || tableProps.enableBulkDelete || false;
|
||||
@ -555,54 +551,24 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
return api
|
||||
.get(url, {
|
||||
params: queryParams,
|
||||
timeout: 5 * 1000
|
||||
timeout: 10 * 1000
|
||||
})
|
||||
.then((response) => {
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
setMissingRecordsText(
|
||||
tableProps.noRecordsText ?? t`No records found`
|
||||
);
|
||||
let results = response.data?.results ?? response.data ?? [];
|
||||
|
||||
let results = response.data?.results ?? response.data ?? [];
|
||||
|
||||
if (props.dataFormatter) {
|
||||
// Custom data formatter provided
|
||||
results = props.dataFormatter(results);
|
||||
}
|
||||
|
||||
if (!Array.isArray(results)) {
|
||||
setMissingRecordsText(t`Server returned incorrect data type`);
|
||||
results = [];
|
||||
}
|
||||
|
||||
tableState.setRecordCount(response.data?.count ?? results.length);
|
||||
|
||||
return results;
|
||||
case 400:
|
||||
setMissingRecordsText(t`Bad request`);
|
||||
break;
|
||||
case 401:
|
||||
setMissingRecordsText(t`Unauthorized`);
|
||||
break;
|
||||
case 403:
|
||||
setMissingRecordsText(t`Forbidden`);
|
||||
break;
|
||||
case 404:
|
||||
setMissingRecordsText(t`Not found`);
|
||||
break;
|
||||
default:
|
||||
setMissingRecordsText(
|
||||
`${t`Unknown error`}: ${response.statusText}`
|
||||
);
|
||||
break;
|
||||
if (props.dataFormatter) {
|
||||
// Custom data formatter provided
|
||||
results = props.dataFormatter(results);
|
||||
}
|
||||
|
||||
return [];
|
||||
})
|
||||
.catch((error) => {
|
||||
setMissingRecordsText(`${t`Error`}: ${error.message}`);
|
||||
return [];
|
||||
if (!Array.isArray(results)) {
|
||||
setMissingRecordsText(t`Server returned incorrect data type`);
|
||||
results = [];
|
||||
}
|
||||
|
||||
tableState.setRecordCount(response.data?.count ?? results.length);
|
||||
|
||||
return results;
|
||||
});
|
||||
};
|
||||
|
||||
@ -626,9 +592,18 @@ export function InvenTreeTable<T extends Record<string, any>>({
|
||||
tableState.storedDataLoaded,
|
||||
tableState.searchTerm
|
||||
],
|
||||
retry: 5,
|
||||
retryDelay: (attempt: number) => (1 + attempt) * 250,
|
||||
throwOnError: (error: any) => {
|
||||
showApiErrorMessage({
|
||||
error: error,
|
||||
title: t`Error loading table data`
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
enabled: !!url && !tableData && tableState.storedDataLoaded,
|
||||
queryFn: fetchTableData,
|
||||
refetchOnMount: true
|
||||
queryFn: fetchTableData
|
||||
});
|
||||
|
||||
// Refetch data when the query parameters change
|
||||
|
@ -53,8 +53,7 @@ export default function BuildOrderTestTable({
|
||||
required: true
|
||||
}
|
||||
})
|
||||
.then((res) => res.data)
|
||||
.catch((err) => []);
|
||||
.then((res) => res.data);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -160,8 +160,7 @@ export default function BuildOutputTable({
|
||||
required: true
|
||||
}
|
||||
})
|
||||
.then((response) => response.data)
|
||||
.catch(() => []);
|
||||
.then((response) => response.data);
|
||||
}
|
||||
});
|
||||
|
||||
@ -184,8 +183,7 @@ export default function BuildOutputTable({
|
||||
tracked: true
|
||||
}
|
||||
})
|
||||
.then((response) => response.data)
|
||||
.catch(() => []);
|
||||
.then((response) => response.data);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -113,8 +113,7 @@ export default function ParametricPartTable({
|
||||
category: categoryId
|
||||
}
|
||||
})
|
||||
.then((response) => response.data)
|
||||
.catch((_error) => []);
|
||||
.then((response) => response.data);
|
||||
},
|
||||
refetchOnMount: true
|
||||
});
|
||||
@ -290,7 +289,7 @@ export default function ParametricPartTable({
|
||||
);
|
||||
|
||||
const parameterColumns: TableColumn[] = useMemo(() => {
|
||||
const data = categoryParameters.data ?? [];
|
||||
const data = categoryParameters?.data || [];
|
||||
|
||||
return data.map((template: any) => {
|
||||
let title = template.name;
|
||||
|
@ -137,6 +137,11 @@ export function PartThumbTable({ pk, setImage }: Readonly<ThumbTableProps>) {
|
||||
// Fetch thumbnails from API
|
||||
const thumbQuery = useQuery({
|
||||
queryKey: [ApiEndpoints.part_thumbs_list, page, searchText],
|
||||
throwOnError: (error: any) => {
|
||||
setTotalPages(1);
|
||||
setPage(1);
|
||||
return true;
|
||||
},
|
||||
queryFn: async () => {
|
||||
const offset = Math.max(0, page - 1) * limit;
|
||||
|
||||
@ -152,11 +157,6 @@ export function PartThumbTable({ pk, setImage }: Readonly<ThumbTableProps>) {
|
||||
const records = response?.data?.count ?? 1;
|
||||
setTotalPages(Math.ceil(records / limit));
|
||||
return response.data?.results ?? response.data;
|
||||
})
|
||||
.catch((error) => {
|
||||
setTotalPages(1);
|
||||
setPage(1);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -172,7 +172,7 @@ export function PartThumbTable({ pk, setImage }: Readonly<ThumbTableProps>) {
|
||||
spacing='xs'
|
||||
>
|
||||
{!thumbQuery.isFetching
|
||||
? thumbQuery?.data.map((data: ImageElement, index: number) => (
|
||||
? thumbQuery?.data?.map((data: ImageElement, index: number) => (
|
||||
<PartThumbComponent
|
||||
element={data}
|
||||
key={index}
|
||||
|
@ -72,8 +72,7 @@ export default function StockItemTestResultTable({
|
||||
enabled: true
|
||||
}
|
||||
})
|
||||
.then((response) => response.data)
|
||||
.catch((_error) => []);
|
||||
.then((response) => response.data);
|
||||
}
|
||||
});
|
||||
|
||||
@ -85,13 +84,14 @@ export default function StockItemTestResultTable({
|
||||
const formatRecords = useCallback(
|
||||
(records: any[]): any[] => {
|
||||
// Construct a list of test templates
|
||||
const results = testTemplates.map((template: any) => {
|
||||
return {
|
||||
...template,
|
||||
templateId: template.pk,
|
||||
results: []
|
||||
};
|
||||
});
|
||||
const results =
|
||||
testTemplates?.map((template: any) => {
|
||||
return {
|
||||
...template,
|
||||
templateId: template.pk,
|
||||
results: []
|
||||
};
|
||||
}) ?? [];
|
||||
|
||||
// If any of the tests results point to templates which we do not have, add them in
|
||||
records.forEach((record) => {
|
||||
|
Reference in New Issue
Block a user