mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	[React] Tree navigation (#5800)
* Adds placehold "PartCategoryTree" - Global navigation (within the "Part" context) for part categories - Replaces multiple tree views from legacy UI * FIx for ApiImage component - Do not continuously request if the user is unauthorized * Add StockLocation tree * Add initial data to nav tree panels * Display data * Render tree view * Fix unused variables
This commit is contained in:
		| @@ -26,6 +26,7 @@ | ||||
|         "@mantine/hooks": "<7", | ||||
|         "@mantine/modals": "<7", | ||||
|         "@mantine/notifications": "<7", | ||||
|         "@naisutech/react-tree": "^3.1.0", | ||||
|         "@sentry/react": "^7.74.1", | ||||
|         "@tabler/icons-react": "^2.39.0", | ||||
|         "@tanstack/react-query": "^5.0.0", | ||||
| @@ -41,6 +42,7 @@ | ||||
|         "react-router-dom": "^6.17.0", | ||||
|         "react-select": "^5.7.7", | ||||
|         "react-simplemde-editor": "^5.2.0", | ||||
|         "styled-components": "^6.1.0", | ||||
|         "zustand": "^4.4.3" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|   | ||||
| @@ -23,11 +23,17 @@ import { api } from '../../App'; | ||||
| export function ApiImage(props: ImageProps) { | ||||
|   const [image, setImage] = useState<string>(''); | ||||
|  | ||||
|   const [authorized, setAuthorized] = useState<boolean>(true); | ||||
|  | ||||
|   const queryKey = useId(); | ||||
|  | ||||
|   const imgQuery = useQuery({ | ||||
|     queryKey: ['image', queryKey, props.src], | ||||
|     enabled: props.src != undefined && props.src != null && props.src != '', | ||||
|     enabled: | ||||
|       authorized && | ||||
|       props.src != undefined && | ||||
|       props.src != null && | ||||
|       props.src != '', | ||||
|     queryFn: async () => { | ||||
|       if (!props.src) { | ||||
|         return null; | ||||
| @@ -37,11 +43,21 @@ export function ApiImage(props: ImageProps) { | ||||
|           responseType: 'blob' | ||||
|         }) | ||||
|         .then((response) => { | ||||
|           let img = new Blob([response.data], { | ||||
|             type: response.headers['content-type'] | ||||
|           }); | ||||
|           let url = URL.createObjectURL(img); | ||||
|           setImage(url); | ||||
|           switch (response.status) { | ||||
|             case 200: | ||||
|               let img = new Blob([response.data], { | ||||
|                 type: response.headers['content-type'] | ||||
|               }); | ||||
|               let url = URL.createObjectURL(img); | ||||
|               setImage(url); | ||||
|               break; | ||||
|             default: | ||||
|               // User is not authorized to view this image | ||||
|               setAuthorized(false); | ||||
|               console.error(`Error fetching image ${props.src}:`, response); | ||||
|               break; | ||||
|           } | ||||
|  | ||||
|           return response; | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|   | ||||
| @@ -1,4 +1,12 @@ | ||||
| import { Anchor, Breadcrumbs, Paper, Text } from '@mantine/core'; | ||||
| import { | ||||
|   ActionIcon, | ||||
|   Anchor, | ||||
|   Breadcrumbs, | ||||
|   Group, | ||||
|   Paper, | ||||
|   Text | ||||
| } from '@mantine/core'; | ||||
| import { IconMenu2 } from '@tabler/icons-react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| export type Breadcrumb = { | ||||
| @@ -9,23 +17,36 @@ export type Breadcrumb = { | ||||
| /** | ||||
|  * Construct a breadcrumb list, with integrated navigation. | ||||
|  */ | ||||
| export function BreadcrumbList({ breadcrumbs }: { breadcrumbs: Breadcrumb[] }) { | ||||
| export function BreadcrumbList({ | ||||
|   breadcrumbs, | ||||
|   navCallback | ||||
| }: { | ||||
|   breadcrumbs: Breadcrumb[]; | ||||
|   navCallback?: () => void; | ||||
| }) { | ||||
|   const navigate = useNavigate(); | ||||
|  | ||||
|   return ( | ||||
|     <Paper p="3" radius="xs"> | ||||
|       <Breadcrumbs separator=">"> | ||||
|         {breadcrumbs.map((breadcrumb, index) => { | ||||
|           return ( | ||||
|             <Anchor | ||||
|               key={`breadcrumb-${index}`} | ||||
|               onClick={() => breadcrumb.url && navigate(breadcrumb.url)} | ||||
|             > | ||||
|               <Text size="sm">{breadcrumb.name}</Text> | ||||
|             </Anchor> | ||||
|           ); | ||||
|         })} | ||||
|       </Breadcrumbs> | ||||
|       <Group spacing="xs"> | ||||
|         {navCallback && ( | ||||
|           <ActionIcon key="nav-action" onClick={navCallback}> | ||||
|             <IconMenu2 /> | ||||
|           </ActionIcon> | ||||
|         )} | ||||
|         <Breadcrumbs key="breadcrumbs" separator=">"> | ||||
|           {breadcrumbs.map((breadcrumb, index) => { | ||||
|             return ( | ||||
|               <Anchor | ||||
|                 key={`breadcrumb-${index}`} | ||||
|                 onClick={() => breadcrumb.url && navigate(breadcrumb.url)} | ||||
|               > | ||||
|                 <Text size="sm">{breadcrumb.name}</Text> | ||||
|               </Anchor> | ||||
|             ); | ||||
|           })} | ||||
|         </Breadcrumbs> | ||||
|       </Group> | ||||
|     </Paper> | ||||
|   ); | ||||
| } | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { api } from '../../App'; | ||||
| import { ApiPaths, apiUrl } from '../../states/ApiState'; | ||||
| import { StylishText } from '../items/StylishText'; | ||||
|  | ||||
| /** | ||||
|  * Construct a notification drawer. | ||||
| @@ -65,7 +66,7 @@ export function NotificationDrawer({ | ||||
|       }} | ||||
|       title={ | ||||
|         <Group position="apart" noWrap={true}> | ||||
|           <Text size="lg">{t`Notifications`}</Text> | ||||
|           <StylishText size="lg">{t`Notifications`}</StylishText> | ||||
|           <ActionIcon | ||||
|             onClick={() => { | ||||
|               onClose(); | ||||
|   | ||||
| @@ -17,6 +17,7 @@ export function PageDetail({ | ||||
|   detail, | ||||
|   imageUrl, | ||||
|   breadcrumbs, | ||||
|   breadcrumbAction, | ||||
|   actions | ||||
| }: { | ||||
|   title?: string; | ||||
| @@ -24,13 +25,17 @@ export function PageDetail({ | ||||
|   imageUrl?: string; | ||||
|   detail?: ReactNode; | ||||
|   breadcrumbs?: Breadcrumb[]; | ||||
|   breadcrumbAction?: () => void; | ||||
|   actions?: ReactNode[]; | ||||
| }) { | ||||
|   return ( | ||||
|     <Stack spacing="xs"> | ||||
|       {breadcrumbs && breadcrumbs.length > 0 && ( | ||||
|         <Paper p="xs" radius="xs" shadow="xs"> | ||||
|           <BreadcrumbList breadcrumbs={breadcrumbs} /> | ||||
|           <BreadcrumbList | ||||
|             navCallback={breadcrumbAction} | ||||
|             breadcrumbs={breadcrumbs} | ||||
|           /> | ||||
|         </Paper> | ||||
|       )} | ||||
|       <Paper p="xs" radius="xs" shadow="xs"> | ||||
|   | ||||
							
								
								
									
										94
									
								
								src/frontend/src/components/nav/PartCategoryTree.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/frontend/src/components/nav/PartCategoryTree.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| import { t } from '@lingui/macro'; | ||||
| import { Drawer, Group, LoadingOverlay, Stack, Text } from '@mantine/core'; | ||||
| import { ReactTree } from '@naisutech/react-tree'; | ||||
| import { IconSitemap } from '@tabler/icons-react'; | ||||
| import { useQuery } from '@tanstack/react-query'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { api } from '../../App'; | ||||
| import { ApiPaths, apiUrl } from '../../states/ApiState'; | ||||
| import { StylishText } from '../items/StylishText'; | ||||
|  | ||||
| export function PartCategoryTree({ | ||||
|   opened, | ||||
|   onClose, | ||||
|   selectedCategory | ||||
| }: { | ||||
|   opened: boolean; | ||||
|   onClose: () => void; | ||||
|   selectedCategory?: number | null; | ||||
| }) { | ||||
|   const navigate = useNavigate(); | ||||
|  | ||||
|   const treeQuery = useQuery({ | ||||
|     enabled: opened, | ||||
|     queryKey: ['part_category_tree', opened], | ||||
|     queryFn: async () => | ||||
|       api | ||||
|         .get(apiUrl(ApiPaths.category_tree), {}) | ||||
|         .then((response) => | ||||
|           response.data.map((category: any) => { | ||||
|             return { | ||||
|               id: category.pk, | ||||
|               label: category.name, | ||||
|               parentId: category.parent | ||||
|             }; | ||||
|           }) | ||||
|         ) | ||||
|         .catch((error) => { | ||||
|           console.error('Error fetching part categpry tree:', error); | ||||
|           return error; | ||||
|         }), | ||||
|     refetchOnMount: true | ||||
|   }); | ||||
|  | ||||
|   function renderNode({ node }: { node: any }) { | ||||
|     return ( | ||||
|       <Group | ||||
|         position="apart" | ||||
|         key={node.id} | ||||
|         noWrap={true} | ||||
|         onClick={() => { | ||||
|           onClose(); | ||||
|           navigate(`/part/category/${node.id}`); | ||||
|         }} | ||||
|       > | ||||
|         <Text>{node.label}</Text> | ||||
|       </Group> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <Drawer | ||||
|       opened={opened} | ||||
|       size="md" | ||||
|       position="left" | ||||
|       onClose={onClose} | ||||
|       withCloseButton={true} | ||||
|       styles={{ | ||||
|         header: { | ||||
|           width: '100%' | ||||
|         }, | ||||
|         title: { | ||||
|           width: '100%' | ||||
|         } | ||||
|       }} | ||||
|       title={ | ||||
|         <Group position="left" p="ms" spacing="md" noWrap={true}> | ||||
|           <IconSitemap /> | ||||
|           <StylishText size="lg">{t`Part Categories`}</StylishText> | ||||
|         </Group> | ||||
|       } | ||||
|     > | ||||
|       <Stack spacing="xs"> | ||||
|         <LoadingOverlay visible={treeQuery.isFetching} /> | ||||
|         <ReactTree | ||||
|           nodes={treeQuery.data ?? []} | ||||
|           RenderNode={renderNode} | ||||
|           defaultSelectedNodes={selectedCategory ? [selectedCategory] : []} | ||||
|           showEmptyItems={false} | ||||
|         /> | ||||
|       </Stack> | ||||
|     </Drawer> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										94
									
								
								src/frontend/src/components/nav/StockLocationTree.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/frontend/src/components/nav/StockLocationTree.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| import { t } from '@lingui/macro'; | ||||
| import { Drawer, Group, LoadingOverlay, Stack, Text } from '@mantine/core'; | ||||
| import { ReactTree } from '@naisutech/react-tree'; | ||||
| import { IconSitemap } from '@tabler/icons-react'; | ||||
| import { useQuery } from '@tanstack/react-query'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
|  | ||||
| import { api } from '../../App'; | ||||
| import { ApiPaths, apiUrl } from '../../states/ApiState'; | ||||
| import { StylishText } from '../items/StylishText'; | ||||
|  | ||||
| export function StockLocationTree({ | ||||
|   opened, | ||||
|   onClose, | ||||
|   selectedLocation | ||||
| }: { | ||||
|   opened: boolean; | ||||
|   onClose: () => void; | ||||
|   selectedLocation?: number | null; | ||||
| }) { | ||||
|   const navigate = useNavigate(); | ||||
|  | ||||
|   const treeQuery = useQuery({ | ||||
|     enabled: opened, | ||||
|     queryKey: ['stock_location_tree', opened], | ||||
|     queryFn: async () => | ||||
|       api | ||||
|         .get(apiUrl(ApiPaths.stock_location_tree), {}) | ||||
|         .then((response) => | ||||
|           response.data.map((location: any) => { | ||||
|             return { | ||||
|               id: location.pk, | ||||
|               label: location.name, | ||||
|               parentId: location.parent | ||||
|             }; | ||||
|           }) | ||||
|         ) | ||||
|         .catch((error) => { | ||||
|           console.error('Error fetching stock location tree:', error); | ||||
|           return error; | ||||
|         }), | ||||
|     refetchOnMount: true | ||||
|   }); | ||||
|  | ||||
|   function renderNode({ node }: { node: any }) { | ||||
|     return ( | ||||
|       <Group | ||||
|         position="apart" | ||||
|         key={node.id} | ||||
|         noWrap={true} | ||||
|         onClick={() => { | ||||
|           onClose(); | ||||
|           navigate(`/stock/location/${node.id}`); | ||||
|         }} | ||||
|       > | ||||
|         <Text>{node.label}</Text> | ||||
|       </Group> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <Drawer | ||||
|       opened={opened} | ||||
|       size="md" | ||||
|       position="left" | ||||
|       onClose={onClose} | ||||
|       withCloseButton={true} | ||||
|       styles={{ | ||||
|         header: { | ||||
|           width: '100%' | ||||
|         }, | ||||
|         title: { | ||||
|           width: '100%' | ||||
|         } | ||||
|       }} | ||||
|       title={ | ||||
|         <Group position="left" noWrap={true} spacing="md" p="md"> | ||||
|           <IconSitemap /> | ||||
|           <StylishText size="lg">{t`Stock Locations`}</StylishText> | ||||
|         </Group> | ||||
|       } | ||||
|     > | ||||
|       <Stack spacing="xs"> | ||||
|         <LoadingOverlay visible={treeQuery.isFetching} /> | ||||
|         <ReactTree | ||||
|           nodes={treeQuery.data ?? []} | ||||
|           showEmptyItems={false} | ||||
|           RenderNode={renderNode} | ||||
|           defaultSelectedNodes={selectedLocation ? [selectedLocation] : []} | ||||
|         /> | ||||
|       </Stack> | ||||
|     </Drawer> | ||||
|   ); | ||||
| } | ||||
| @@ -5,12 +5,13 @@ import { | ||||
|   IconListDetails, | ||||
|   IconSitemap | ||||
| } from '@tabler/icons-react'; | ||||
| import { useMemo } from 'react'; | ||||
| import { useMemo, useState } from 'react'; | ||||
| import { useParams } from 'react-router-dom'; | ||||
|  | ||||
| import { PlaceholderPanel } from '../../components/items/Placeholder'; | ||||
| import { PageDetail } from '../../components/nav/PageDetail'; | ||||
| import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | ||||
| import { PartCategoryTree } from '../../components/nav/PartCategoryTree'; | ||||
| import { PartCategoryTable } from '../../components/tables/part/PartCategoryTable'; | ||||
| import { PartListTable } from '../../components/tables/part/PartTable'; | ||||
| import { useInstance } from '../../hooks/UseInstance'; | ||||
| @@ -24,6 +25,8 @@ import { ApiPaths } from '../../states/ApiState'; | ||||
| export default function CategoryDetail({}: {}) { | ||||
|   const { id } = useParams(); | ||||
|  | ||||
|   const [treeOpen, setTreeOpen] = useState(false); | ||||
|  | ||||
|   const { | ||||
|     instance: category, | ||||
|     refreshInstance, | ||||
| @@ -88,10 +91,20 @@ export default function CategoryDetail({}: {}) { | ||||
|   return ( | ||||
|     <Stack spacing="xs"> | ||||
|       <LoadingOverlay visible={instanceQuery.isFetching} /> | ||||
|       <PartCategoryTree | ||||
|         opened={treeOpen} | ||||
|         onClose={() => { | ||||
|           setTreeOpen(false); | ||||
|         }} | ||||
|         selectedCategory={category?.pk} | ||||
|       /> | ||||
|       <PageDetail | ||||
|         title={t`Part Category`} | ||||
|         detail={<Text>{category.name ?? 'Top level'}</Text>} | ||||
|         breadcrumbs={breadcrumbs} | ||||
|         breadcrumbAction={() => { | ||||
|           setTreeOpen(true); | ||||
|         }} | ||||
|       /> | ||||
|       <PanelGroup pageKey="partcategory" panels={categoryPanels} /> | ||||
|     </Stack> | ||||
|   | ||||
| @@ -27,12 +27,13 @@ import { | ||||
|   IconUnlink, | ||||
|   IconVersions | ||||
| } from '@tabler/icons-react'; | ||||
| import { useMemo } from 'react'; | ||||
| import { useMemo, useState } from 'react'; | ||||
| import { useParams } from 'react-router-dom'; | ||||
|  | ||||
| import { ActionDropdown } from '../../components/items/ActionDropdown'; | ||||
| import { PageDetail } from '../../components/nav/PageDetail'; | ||||
| import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | ||||
| import { PartCategoryTree } from '../../components/nav/PartCategoryTree'; | ||||
| import { AttachmentTable } from '../../components/tables/general/AttachmentTable'; | ||||
| import { PartParameterTable } from '../../components/tables/part/PartParameterTable'; | ||||
| import { PartVariantTable } from '../../components/tables/part/PartVariantTable'; | ||||
| @@ -52,6 +53,8 @@ export default function PartDetail() { | ||||
|  | ||||
|   const user = useUserState(); | ||||
|  | ||||
|   const [treeOpen, setTreeOpen] = useState(false); | ||||
|  | ||||
|   const { | ||||
|     instance: part, | ||||
|     refreshInstance, | ||||
| @@ -289,12 +292,22 @@ export default function PartDetail() { | ||||
|     <> | ||||
|       <Stack spacing="xs"> | ||||
|         <LoadingOverlay visible={instanceQuery.isFetching} /> | ||||
|         <PartCategoryTree | ||||
|           opened={treeOpen} | ||||
|           onClose={() => { | ||||
|             setTreeOpen(false); | ||||
|           }} | ||||
|           selectedCategory={part?.category} | ||||
|         /> | ||||
|         <PageDetail | ||||
|           title={t`Part` + ': ' + part.full_name} | ||||
|           subtitle={part.description} | ||||
|           imageUrl={part.image} | ||||
|           detail={partDetail} | ||||
|           breadcrumbs={breadcrumbs} | ||||
|           breadcrumbAction={() => { | ||||
|             setTreeOpen(true); | ||||
|           }} | ||||
|           actions={partActions} | ||||
|         /> | ||||
|         <PanelGroup pageKey="part" panels={partPanels} /> | ||||
|   | ||||
| @@ -1,11 +1,12 @@ | ||||
| import { t } from '@lingui/macro'; | ||||
| import { LoadingOverlay, Stack, Text } from '@mantine/core'; | ||||
| import { IconPackages, IconSitemap } from '@tabler/icons-react'; | ||||
| import { useMemo } from 'react'; | ||||
| import { useMemo, useState } from 'react'; | ||||
| import { useParams } from 'react-router-dom'; | ||||
|  | ||||
| import { PageDetail } from '../../components/nav/PageDetail'; | ||||
| import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | ||||
| import { StockLocationTree } from '../../components/nav/StockLocationTree'; | ||||
| import { StockItemTable } from '../../components/tables/stock/StockItemTable'; | ||||
| import { StockLocationTable } from '../../components/tables/stock/StockLocationTable'; | ||||
| import { useInstance } from '../../hooks/UseInstance'; | ||||
| @@ -14,6 +15,8 @@ import { ApiPaths } from '../../states/ApiState'; | ||||
| export default function Stock() { | ||||
|   const { id } = useParams(); | ||||
|  | ||||
|   const [treeOpen, setTreeOpen] = useState(false); | ||||
|  | ||||
|   const { | ||||
|     instance: location, | ||||
|     refreshInstance, | ||||
| @@ -70,10 +73,18 @@ export default function Stock() { | ||||
|     <> | ||||
|       <Stack> | ||||
|         <LoadingOverlay visible={instanceQuery.isFetching} /> | ||||
|         <StockLocationTree | ||||
|           opened={treeOpen} | ||||
|           onClose={() => setTreeOpen(false)} | ||||
|           selectedLocation={location?.pk} | ||||
|         /> | ||||
|         <PageDetail | ||||
|           title={t`Stock Items`} | ||||
|           detail={<Text>{location.name ?? 'Top level'}</Text>} | ||||
|           breadcrumbs={breadcrumbs} | ||||
|           breadcrumbAction={() => { | ||||
|             setTreeOpen(true); | ||||
|           }} | ||||
|         /> | ||||
|         <PanelGroup pageKey="stocklocation" panels={locationPanels} /> | ||||
|       </Stack> | ||||
|   | ||||
| @@ -9,12 +9,13 @@ import { | ||||
|   IconPaperclip, | ||||
|   IconSitemap | ||||
| } from '@tabler/icons-react'; | ||||
| import { useMemo } from 'react'; | ||||
| import { useMemo, useState } from 'react'; | ||||
| import { useParams } from 'react-router-dom'; | ||||
|  | ||||
| import { PlaceholderPanel } from '../../components/items/Placeholder'; | ||||
| import { PageDetail } from '../../components/nav/PageDetail'; | ||||
| import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | ||||
| import { StockLocationTree } from '../../components/nav/StockLocationTree'; | ||||
| import { AttachmentTable } from '../../components/tables/general/AttachmentTable'; | ||||
| import { NotesEditor } from '../../components/widgets/MarkdownEditor'; | ||||
| import { useInstance } from '../../hooks/UseInstance'; | ||||
| @@ -23,6 +24,8 @@ import { ApiPaths, apiUrl } from '../../states/ApiState'; | ||||
| export default function StockDetail() { | ||||
|   const { id } = useParams(); | ||||
|  | ||||
|   const [treeOpen, setTreeOpen] = useState(false); | ||||
|  | ||||
|   const { | ||||
|     instance: stockitem, | ||||
|     refreshInstance, | ||||
| @@ -110,6 +113,11 @@ export default function StockDetail() { | ||||
|   return ( | ||||
|     <Stack> | ||||
|       <LoadingOverlay visible={instanceQuery.isFetching} /> | ||||
|       <StockLocationTree | ||||
|         opened={treeOpen} | ||||
|         onClose={() => setTreeOpen(false)} | ||||
|         selectedLocation={stockitem?.location} | ||||
|       /> | ||||
|       <PageDetail | ||||
|         title={t`Stock Items`} | ||||
|         subtitle={stockitem.part_detail?.full_name ?? 'name goes here'} | ||||
| @@ -119,6 +127,9 @@ export default function StockDetail() { | ||||
|           </Alert> | ||||
|         } | ||||
|         breadcrumbs={breadcrumbs} | ||||
|         breadcrumbAction={() => { | ||||
|           setTreeOpen(true); | ||||
|         }} | ||||
|       /> | ||||
|       <PanelGroup pageKey="stockitem" panels={stockPanels} /> | ||||
|     </Stack> | ||||
|   | ||||
| @@ -76,6 +76,7 @@ export enum ApiPaths { | ||||
|   // Part URLs | ||||
|   part_list = 'api-part-list', | ||||
|   category_list = 'api-category-list', | ||||
|   category_tree = 'api-category-tree', | ||||
|   related_part_list = 'api-related-part-list', | ||||
|   part_attachment_list = 'api-part-attachment-list', | ||||
|   part_parameter_list = 'api-part-parameter-list', | ||||
| @@ -89,6 +90,7 @@ export enum ApiPaths { | ||||
|   // Stock Item URLs | ||||
|   stock_item_list = 'api-stock-item-list', | ||||
|   stock_location_list = 'api-stock-location-list', | ||||
|   stock_location_tree = 'api-stock-location-tree', | ||||
|   stock_attachment_list = 'api-stock-attachment-list', | ||||
|  | ||||
|   // Purchase Order URLs | ||||
| @@ -165,6 +167,8 @@ export function apiEndpoint(path: ApiPaths): string { | ||||
|       return 'part/parameter/template/'; | ||||
|     case ApiPaths.category_list: | ||||
|       return 'part/category/'; | ||||
|     case ApiPaths.category_tree: | ||||
|       return 'part/category/tree/'; | ||||
|     case ApiPaths.related_part_list: | ||||
|       return 'part/related/'; | ||||
|     case ApiPaths.part_attachment_list: | ||||
| @@ -179,6 +183,8 @@ export function apiEndpoint(path: ApiPaths): string { | ||||
|       return 'stock/'; | ||||
|     case ApiPaths.stock_location_list: | ||||
|       return 'stock/location/'; | ||||
|     case ApiPaths.stock_location_tree: | ||||
|       return 'stock/location/tree/'; | ||||
|     case ApiPaths.stock_attachment_list: | ||||
|       return 'stock/attachment/'; | ||||
|     case ApiPaths.purchase_order_list: | ||||
|   | ||||
| @@ -389,6 +389,13 @@ | ||||
|   resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" | ||||
|   integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== | ||||
|  | ||||
| "@emotion/is-prop-valid@^1.2.0", "@emotion/is-prop-valid@^1.2.1": | ||||
|   version "1.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc" | ||||
|   integrity sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw== | ||||
|   dependencies: | ||||
|     "@emotion/memoize" "^0.8.1" | ||||
|  | ||||
| "@emotion/memoize@^0.8.1": | ||||
|   version "0.8.1" | ||||
|   resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" | ||||
| @@ -424,7 +431,7 @@ | ||||
|   resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" | ||||
|   integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== | ||||
|  | ||||
| "@emotion/unitless@^0.8.1": | ||||
| "@emotion/unitless@^0.8.0", "@emotion/unitless@^0.8.1": | ||||
|   version "0.8.1" | ||||
|   resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" | ||||
|   integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== | ||||
| @@ -963,6 +970,15 @@ | ||||
|   dependencies: | ||||
|     moo "^0.5.1" | ||||
|  | ||||
| "@naisutech/react-tree@^3.1.0": | ||||
|   version "3.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/@naisutech/react-tree/-/react-tree-3.1.0.tgz#a83820425b53a1ec7a39804ff8bd9024f0a953f4" | ||||
|   integrity sha512-6p1l3ZIaTmbgiAf/mpFELvqwl51LDhr+09f7L+C27DBLWjtleezCMoUuiSLhrJgpixCPNL13PuI3q2yn+0AGvA== | ||||
|   dependencies: | ||||
|     "@emotion/is-prop-valid" "^1.2.0" | ||||
|     nanoid "^4.0.0" | ||||
|     react-draggable "^4.4.5" | ||||
|  | ||||
| "@playwright/test@^1.39.0": | ||||
|   version "1.39.0" | ||||
|   resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.39.0.tgz#d10ba8e38e44104499e25001945f07faa9fa91cd" | ||||
| @@ -1302,6 +1318,11 @@ | ||||
|   resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.4.tgz#fedc3e5b15c26dc18faae96bf1317487cb3658cf" | ||||
|   integrity sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ== | ||||
|  | ||||
| "@types/stylis@^4.0.2": | ||||
|   version "4.2.2" | ||||
|   resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.2.tgz#baabb6b3aa6787e90a6bd6cd75cd8fb9a4f256a3" | ||||
|   integrity sha512-Rm17MsTpQQP5Jq4BF7CdrxJsDufoiL/q5IbJZYZmOZAJALyijgF7BzLgobXUqraNcQdqFYLYGeglDp6QzaxPpg== | ||||
|  | ||||
| "@types/tern@*": | ||||
|   version "0.23.5" | ||||
|   resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.5.tgz#8d369a06749ea83956885cb734788ec208a0e900" | ||||
| @@ -1478,6 +1499,11 @@ camelcase@^6.2.0: | ||||
|   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" | ||||
|   integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== | ||||
|  | ||||
| camelize@^1.0.0: | ||||
|   version "1.0.1" | ||||
|   resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" | ||||
|   integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== | ||||
|  | ||||
| caniuse-lite@^1.0.30001541: | ||||
|   version "1.0.30001549" | ||||
|   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz#7d1a3dce7ea78c06ed72c32c2743ea364b3615aa" | ||||
| @@ -1653,12 +1679,26 @@ cosmiconfig@^8.0.0: | ||||
|     parse-json "^5.2.0" | ||||
|     path-type "^4.0.0" | ||||
|  | ||||
| css-color-keywords@^1.0.0: | ||||
|   version "1.0.0" | ||||
|   resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" | ||||
|   integrity sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg== | ||||
|  | ||||
| css-to-react-native@^3.2.0: | ||||
|   version "3.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32" | ||||
|   integrity sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ== | ||||
|   dependencies: | ||||
|     camelize "^1.0.0" | ||||
|     css-color-keywords "^1.0.0" | ||||
|     postcss-value-parser "^4.0.2" | ||||
|  | ||||
| csstype@3.0.9: | ||||
|   version "3.0.9" | ||||
|   resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b" | ||||
|   integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw== | ||||
|  | ||||
| csstype@^3.0.2: | ||||
| csstype@^3.0.2, csstype@^3.1.2: | ||||
|   version "3.1.2" | ||||
|   resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" | ||||
|   integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== | ||||
| @@ -2246,6 +2286,11 @@ nanoid@^3.3.6: | ||||
|   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" | ||||
|   integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== | ||||
|  | ||||
| nanoid@^4.0.0: | ||||
|   version "4.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e" | ||||
|   integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw== | ||||
|  | ||||
| node-releases@^2.0.13: | ||||
|   version "2.0.13" | ||||
|   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" | ||||
| @@ -2392,7 +2437,12 @@ pofile@^1.1.4: | ||||
|   resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.1.4.tgz#eab7e29f5017589b2a61b2259dff608c0cad76a2" | ||||
|   integrity sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g== | ||||
|  | ||||
| postcss@^8.4.27: | ||||
| postcss-value-parser@^4.0.2: | ||||
|   version "4.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" | ||||
|   integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== | ||||
|  | ||||
| postcss@^8.4.27, postcss@^8.4.31: | ||||
|   version "8.4.31" | ||||
|   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" | ||||
|   integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== | ||||
| @@ -2691,6 +2741,11 @@ semver@^6.3.1: | ||||
|   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" | ||||
|   integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== | ||||
|  | ||||
| shallowequal@^1.1.0: | ||||
|   version "1.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" | ||||
|   integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== | ||||
|  | ||||
| signal-exit@^3.0.2: | ||||
|   version "3.0.7" | ||||
|   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" | ||||
| @@ -2736,11 +2791,31 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: | ||||
|   dependencies: | ||||
|     ansi-regex "^5.0.1" | ||||
|  | ||||
| styled-components@^6.1.0: | ||||
|   version "6.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.1.0.tgz#228e3ab9c1ee1daa4b0a06aae30df0ed14fda274" | ||||
|   integrity sha512-VWNfYYBuXzuLS/QYEeoPgMErP26WL+dX9//rEh80B2mmlS1yRxRxuL5eax4m6ybYEUoHWlTy2XOU32767mlMkg== | ||||
|   dependencies: | ||||
|     "@emotion/is-prop-valid" "^1.2.1" | ||||
|     "@emotion/unitless" "^0.8.0" | ||||
|     "@types/stylis" "^4.0.2" | ||||
|     css-to-react-native "^3.2.0" | ||||
|     csstype "^3.1.2" | ||||
|     postcss "^8.4.31" | ||||
|     shallowequal "^1.1.0" | ||||
|     stylis "^4.3.0" | ||||
|     tslib "^2.5.0" | ||||
|  | ||||
| stylis@4.2.0: | ||||
|   version "4.2.0" | ||||
|   resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" | ||||
|   integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== | ||||
|  | ||||
| stylis@^4.3.0: | ||||
|   version "4.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.0.tgz#abe305a669fc3d8777e10eefcfc73ad861c5588c" | ||||
|   integrity sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ== | ||||
|  | ||||
| supports-color@^5.3.0: | ||||
|   version "5.5.0" | ||||
|   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" | ||||
| @@ -2801,7 +2876,7 @@ tslib@^1.9.0: | ||||
|   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" | ||||
|   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== | ||||
|  | ||||
| tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0, "tslib@^2.4.1 || ^1.9.3": | ||||
| tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0, "tslib@^2.4.1 || ^1.9.3", tslib@^2.5.0: | ||||
|   version "2.6.2" | ||||
|   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" | ||||
|   integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== | ||||
|   | ||||
		Reference in New Issue
	
	Block a user