mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	[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:
		
							
								
								
									
										58
									
								
								src/frontend/src/hooks/UseInstance.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/frontend/src/hooks/UseInstance.tsx
									
									
									
									
									
										Normal 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 }; | ||||
| } | ||||
| @@ -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> | ||||
|     </> | ||||
|   | ||||
| @@ -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: '' }, | ||||
|   | ||||
| @@ -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> | ||||
|     </> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user