[PUI] Instance hook (#5564)

* Custom hook for fetching instance data from the server

* Refactor pages to use new hookr

* Allow useInstance hook to handle parameters
This commit is contained in:
Oliver 2023-09-18 21:07:01 +10:00 committed by GitHub
parent fe68598c1b
commit ad8df52b73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 82 deletions

View File

@ -0,0 +1,58 @@
import { useQuery } from '@tanstack/react-query';
import { useCallback, useState } from 'react';
import { api } from '../App';
/**
* Custom hook for loading a single instance of an instance from the API
*
* - Queries the API for a single instance of an object, and returns the result.
* - Provides a callback function to refresh the instance
*
* To use this hook:
* const { instance, refreshInstance } = useInstance(url: string, pk: number)
*/
export function useInstance(
url: string,
pk: string | undefined,
params: any = {}
) {
const [instance, setInstance] = useState<any>({});
const instanceQuery = useQuery({
queryKey: ['instance', url, pk, params],
enabled: pk != null && pk != undefined && pk.length > 0,
queryFn: async () => {
return api
.get(url + pk + '/', {
params: params
})
.then((response) => {
switch (response.status) {
case 200:
setInstance(response.data);
return response.data;
default:
setInstance({});
return null;
}
})
.catch((error) => {
setInstance({});
console.error(`Error fetching instance ${url}${pk}:`, error);
return null;
});
},
refetchOnMount: false,
refetchOnWindowFocus: false
});
const refreshInstance = useCallback(
function () {
instanceQuery.refetch();
},
[instanceQuery]
);
return { instance, refreshInstance, instanceQuery };
}

View File

@ -26,6 +26,7 @@ import { AttachmentTable } from '../../components/tables/AttachmentTable';
import { BuildOrderTable } from '../../components/tables/build/BuildOrderTable';
import { StockItemTable } from '../../components/tables/stock/StockItemTable';
import { NotesEditor } from '../../components/widgets/MarkdownEditor';
import { useInstance } from '../../hooks/UseInstance';
/**
* Detail page for a single Build Order
@ -33,30 +34,12 @@ import { NotesEditor } from '../../components/widgets/MarkdownEditor';
export default function BuildDetail() {
const { id } = useParams();
// Build data
const [build, setBuild] = useState<any>({});
useEffect(() => {
setBuild({});
}, [id]);
// Query hook for fetching build data
const buildQuery = useQuery(['build', id ?? -1], async () => {
let url = `/build/${id}/`;
return api
.get(url, {
params: {
part_detail: true
}
})
.then((response) => {
setBuild(response.data);
})
.catch((error) => {
console.error(error);
setBuild({});
});
const {
instance: build,
refreshInstance,
instanceQuery
} = useInstance('/build/', id, {
part_detail: true
});
const buildPanels: PanelType[] = useMemo(() => {
@ -162,7 +145,7 @@ export default function BuildDetail() {
]}
actions={[<PlaceholderPill key="1" />]}
/>
<LoadingOverlay visible={buildQuery.isFetching} />
<LoadingOverlay visible={instanceQuery.isFetching} />
<PanelGroup panels={buildPanels} />
</Stack>
</>

View File

@ -15,6 +15,7 @@ import { PageDetail } from '../../components/nav/PageDetail';
import { PanelGroup, PanelType } from '../../components/nav/PanelGroup';
import { PartCategoryTable } from '../../components/tables/part/PartCategoryTable';
import { PartListTable } from '../../components/tables/part/PartTable';
import { useInstance } from '../../hooks/UseInstance';
/**
* Detail view for a single PartCategory instance.
@ -24,27 +25,10 @@ import { PartListTable } from '../../components/tables/part/PartTable';
export default function CategoryDetail({}: {}) {
const { id } = useParams();
const [category, setCategory] = useState<any>({});
useEffect(() => {
setCategory({});
}, [id]);
const categoryQuery = useQuery({
enabled: id != null && id != undefined,
queryKey: ['category', id],
queryFn: async () => {
return api
.get(`/part/category/${id}/`)
.then((response) => {
setCategory(response.data);
return response.data;
})
.catch((error) => {
console.error('Error fetching category data:', error);
});
}
});
const { instance: category, refreshInstance } = useInstance(
'/part/category/',
id
);
const categoryPanels: PanelType[] = useMemo(
() => [
@ -90,7 +74,7 @@ export default function CategoryDetail({}: {}) {
title={t`Part Category`}
detail={<Text>{category.name ?? 'Top level'}</Text>}
breadcrumbs={
id
category.pk
? [
{ name: t`Parts`, url: '/part' },
{ name: '...', url: '' },

View File

@ -1,13 +1,5 @@
import { t } from '@lingui/macro';
import {
Alert,
Button,
Group,
LoadingOverlay,
Space,
Stack,
Text
} from '@mantine/core';
import { Alert, Button, LoadingOverlay, Stack, Text } from '@mantine/core';
import {
IconBuilding,
IconCurrencyDollar,
@ -41,6 +33,7 @@ import {
NotesEditor
} from '../../components/widgets/MarkdownEditor';
import { editPart } from '../../functions/forms/PartForms';
import { useInstance } from '../../hooks/UseInstance';
/**
* Detail view for a single Part instance
@ -48,12 +41,11 @@ import { editPart } from '../../functions/forms/PartForms';
export default function PartDetail() {
const { id } = useParams();
// Part data
const [part, setPart] = useState<any>({});
useEffect(() => {
setPart({});
}, [id]);
const {
instance: part,
refreshInstance,
instanceQuery
} = useInstance('/part/', id);
// Part data panels (recalculate when part data changes)
const partPanels: PanelType[] = useMemo(() => {
@ -153,22 +145,6 @@ export default function PartDetail() {
];
}, [part]);
// Query hook for fetching part data
const partQuery = useQuery(['part', id], async () => {
let url = `/part/${id}/`;
return api
.get(url)
.then((response) => {
setPart(response.data);
return response.data;
})
.catch((error) => {
setPart({});
return null;
});
});
function partAttachmentsTab(): React.ReactNode {
return (
<AttachmentTable
@ -226,9 +202,7 @@ export default function PartDetail() {
part.pk &&
editPart({
part_id: part.pk,
callback: () => {
partQuery.refetch();
}
callback: refreshInstance
})
}
>
@ -236,7 +210,7 @@ export default function PartDetail() {
</Button>
]}
/>
<LoadingOverlay visible={partQuery.isFetching} />
<LoadingOverlay visible={instanceQuery.isFetching} />
<PanelGroup panels={partPanels} />
</Stack>
</>