mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +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/hooks": "<7", | ||||||
|         "@mantine/modals": "<7", |         "@mantine/modals": "<7", | ||||||
|         "@mantine/notifications": "<7", |         "@mantine/notifications": "<7", | ||||||
|  |         "@naisutech/react-tree": "^3.1.0", | ||||||
|         "@sentry/react": "^7.74.1", |         "@sentry/react": "^7.74.1", | ||||||
|         "@tabler/icons-react": "^2.39.0", |         "@tabler/icons-react": "^2.39.0", | ||||||
|         "@tanstack/react-query": "^5.0.0", |         "@tanstack/react-query": "^5.0.0", | ||||||
| @@ -41,6 +42,7 @@ | |||||||
|         "react-router-dom": "^6.17.0", |         "react-router-dom": "^6.17.0", | ||||||
|         "react-select": "^5.7.7", |         "react-select": "^5.7.7", | ||||||
|         "react-simplemde-editor": "^5.2.0", |         "react-simplemde-editor": "^5.2.0", | ||||||
|  |         "styled-components": "^6.1.0", | ||||||
|         "zustand": "^4.4.3" |         "zustand": "^4.4.3" | ||||||
|     }, |     }, | ||||||
|     "devDependencies": { |     "devDependencies": { | ||||||
|   | |||||||
| @@ -23,11 +23,17 @@ import { api } from '../../App'; | |||||||
| export function ApiImage(props: ImageProps) { | export function ApiImage(props: ImageProps) { | ||||||
|   const [image, setImage] = useState<string>(''); |   const [image, setImage] = useState<string>(''); | ||||||
|  |  | ||||||
|  |   const [authorized, setAuthorized] = useState<boolean>(true); | ||||||
|  |  | ||||||
|   const queryKey = useId(); |   const queryKey = useId(); | ||||||
|  |  | ||||||
|   const imgQuery = useQuery({ |   const imgQuery = useQuery({ | ||||||
|     queryKey: ['image', queryKey, props.src], |     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 () => { |     queryFn: async () => { | ||||||
|       if (!props.src) { |       if (!props.src) { | ||||||
|         return null; |         return null; | ||||||
| @@ -37,11 +43,21 @@ export function ApiImage(props: ImageProps) { | |||||||
|           responseType: 'blob' |           responseType: 'blob' | ||||||
|         }) |         }) | ||||||
|         .then((response) => { |         .then((response) => { | ||||||
|           let img = new Blob([response.data], { |           switch (response.status) { | ||||||
|             type: response.headers['content-type'] |             case 200: | ||||||
|           }); |               let img = new Blob([response.data], { | ||||||
|           let url = URL.createObjectURL(img); |                 type: response.headers['content-type'] | ||||||
|           setImage(url); |               }); | ||||||
|  |               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; |           return response; | ||||||
|         }) |         }) | ||||||
|         .catch((error) => { |         .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'; | import { useNavigate } from 'react-router-dom'; | ||||||
|  |  | ||||||
| export type Breadcrumb = { | export type Breadcrumb = { | ||||||
| @@ -9,23 +17,36 @@ export type Breadcrumb = { | |||||||
| /** | /** | ||||||
|  * Construct a breadcrumb list, with integrated navigation. |  * 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(); |   const navigate = useNavigate(); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Paper p="3" radius="xs"> |     <Paper p="3" radius="xs"> | ||||||
|       <Breadcrumbs separator=">"> |       <Group spacing="xs"> | ||||||
|         {breadcrumbs.map((breadcrumb, index) => { |         {navCallback && ( | ||||||
|           return ( |           <ActionIcon key="nav-action" onClick={navCallback}> | ||||||
|             <Anchor |             <IconMenu2 /> | ||||||
|               key={`breadcrumb-${index}`} |           </ActionIcon> | ||||||
|               onClick={() => breadcrumb.url && navigate(breadcrumb.url)} |         )} | ||||||
|             > |         <Breadcrumbs key="breadcrumbs" separator=">"> | ||||||
|               <Text size="sm">{breadcrumb.name}</Text> |           {breadcrumbs.map((breadcrumb, index) => { | ||||||
|             </Anchor> |             return ( | ||||||
|           ); |               <Anchor | ||||||
|         })} |                 key={`breadcrumb-${index}`} | ||||||
|       </Breadcrumbs> |                 onClick={() => breadcrumb.url && navigate(breadcrumb.url)} | ||||||
|  |               > | ||||||
|  |                 <Text size="sm">{breadcrumb.name}</Text> | ||||||
|  |               </Anchor> | ||||||
|  |             ); | ||||||
|  |           })} | ||||||
|  |         </Breadcrumbs> | ||||||
|  |       </Group> | ||||||
|     </Paper> |     </Paper> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ import { useNavigate } from 'react-router-dom'; | |||||||
|  |  | ||||||
| import { api } from '../../App'; | import { api } from '../../App'; | ||||||
| import { ApiPaths, apiUrl } from '../../states/ApiState'; | import { ApiPaths, apiUrl } from '../../states/ApiState'; | ||||||
|  | import { StylishText } from '../items/StylishText'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Construct a notification drawer. |  * Construct a notification drawer. | ||||||
| @@ -65,7 +66,7 @@ export function NotificationDrawer({ | |||||||
|       }} |       }} | ||||||
|       title={ |       title={ | ||||||
|         <Group position="apart" noWrap={true}> |         <Group position="apart" noWrap={true}> | ||||||
|           <Text size="lg">{t`Notifications`}</Text> |           <StylishText size="lg">{t`Notifications`}</StylishText> | ||||||
|           <ActionIcon |           <ActionIcon | ||||||
|             onClick={() => { |             onClick={() => { | ||||||
|               onClose(); |               onClose(); | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ export function PageDetail({ | |||||||
|   detail, |   detail, | ||||||
|   imageUrl, |   imageUrl, | ||||||
|   breadcrumbs, |   breadcrumbs, | ||||||
|  |   breadcrumbAction, | ||||||
|   actions |   actions | ||||||
| }: { | }: { | ||||||
|   title?: string; |   title?: string; | ||||||
| @@ -24,13 +25,17 @@ export function PageDetail({ | |||||||
|   imageUrl?: string; |   imageUrl?: string; | ||||||
|   detail?: ReactNode; |   detail?: ReactNode; | ||||||
|   breadcrumbs?: Breadcrumb[]; |   breadcrumbs?: Breadcrumb[]; | ||||||
|  |   breadcrumbAction?: () => void; | ||||||
|   actions?: ReactNode[]; |   actions?: ReactNode[]; | ||||||
| }) { | }) { | ||||||
|   return ( |   return ( | ||||||
|     <Stack spacing="xs"> |     <Stack spacing="xs"> | ||||||
|       {breadcrumbs && breadcrumbs.length > 0 && ( |       {breadcrumbs && breadcrumbs.length > 0 && ( | ||||||
|         <Paper p="xs" radius="xs" shadow="xs"> |         <Paper p="xs" radius="xs" shadow="xs"> | ||||||
|           <BreadcrumbList breadcrumbs={breadcrumbs} /> |           <BreadcrumbList | ||||||
|  |             navCallback={breadcrumbAction} | ||||||
|  |             breadcrumbs={breadcrumbs} | ||||||
|  |           /> | ||||||
|         </Paper> |         </Paper> | ||||||
|       )} |       )} | ||||||
|       <Paper p="xs" radius="xs" shadow="xs"> |       <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, |   IconListDetails, | ||||||
|   IconSitemap |   IconSitemap | ||||||
| } from '@tabler/icons-react'; | } from '@tabler/icons-react'; | ||||||
| import { useMemo } from 'react'; | import { useMemo, useState } from 'react'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { PlaceholderPanel } from '../../components/items/Placeholder'; | import { PlaceholderPanel } from '../../components/items/Placeholder'; | ||||||
| import { PageDetail } from '../../components/nav/PageDetail'; | import { PageDetail } from '../../components/nav/PageDetail'; | ||||||
| import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | ||||||
|  | import { PartCategoryTree } from '../../components/nav/PartCategoryTree'; | ||||||
| import { PartCategoryTable } from '../../components/tables/part/PartCategoryTable'; | import { PartCategoryTable } from '../../components/tables/part/PartCategoryTable'; | ||||||
| import { PartListTable } from '../../components/tables/part/PartTable'; | import { PartListTable } from '../../components/tables/part/PartTable'; | ||||||
| import { useInstance } from '../../hooks/UseInstance'; | import { useInstance } from '../../hooks/UseInstance'; | ||||||
| @@ -24,6 +25,8 @@ import { ApiPaths } from '../../states/ApiState'; | |||||||
| export default function CategoryDetail({}: {}) { | export default function CategoryDetail({}: {}) { | ||||||
|   const { id } = useParams(); |   const { id } = useParams(); | ||||||
|  |  | ||||||
|  |   const [treeOpen, setTreeOpen] = useState(false); | ||||||
|  |  | ||||||
|   const { |   const { | ||||||
|     instance: category, |     instance: category, | ||||||
|     refreshInstance, |     refreshInstance, | ||||||
| @@ -88,10 +91,20 @@ export default function CategoryDetail({}: {}) { | |||||||
|   return ( |   return ( | ||||||
|     <Stack spacing="xs"> |     <Stack spacing="xs"> | ||||||
|       <LoadingOverlay visible={instanceQuery.isFetching} /> |       <LoadingOverlay visible={instanceQuery.isFetching} /> | ||||||
|  |       <PartCategoryTree | ||||||
|  |         opened={treeOpen} | ||||||
|  |         onClose={() => { | ||||||
|  |           setTreeOpen(false); | ||||||
|  |         }} | ||||||
|  |         selectedCategory={category?.pk} | ||||||
|  |       /> | ||||||
|       <PageDetail |       <PageDetail | ||||||
|         title={t`Part Category`} |         title={t`Part Category`} | ||||||
|         detail={<Text>{category.name ?? 'Top level'}</Text>} |         detail={<Text>{category.name ?? 'Top level'}</Text>} | ||||||
|         breadcrumbs={breadcrumbs} |         breadcrumbs={breadcrumbs} | ||||||
|  |         breadcrumbAction={() => { | ||||||
|  |           setTreeOpen(true); | ||||||
|  |         }} | ||||||
|       /> |       /> | ||||||
|       <PanelGroup pageKey="partcategory" panels={categoryPanels} /> |       <PanelGroup pageKey="partcategory" panels={categoryPanels} /> | ||||||
|     </Stack> |     </Stack> | ||||||
|   | |||||||
| @@ -27,12 +27,13 @@ import { | |||||||
|   IconUnlink, |   IconUnlink, | ||||||
|   IconVersions |   IconVersions | ||||||
| } from '@tabler/icons-react'; | } from '@tabler/icons-react'; | ||||||
| import { useMemo } from 'react'; | import { useMemo, useState } from 'react'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { ActionDropdown } from '../../components/items/ActionDropdown'; | import { ActionDropdown } from '../../components/items/ActionDropdown'; | ||||||
| import { PageDetail } from '../../components/nav/PageDetail'; | import { PageDetail } from '../../components/nav/PageDetail'; | ||||||
| import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | ||||||
|  | import { PartCategoryTree } from '../../components/nav/PartCategoryTree'; | ||||||
| import { AttachmentTable } from '../../components/tables/general/AttachmentTable'; | import { AttachmentTable } from '../../components/tables/general/AttachmentTable'; | ||||||
| import { PartParameterTable } from '../../components/tables/part/PartParameterTable'; | import { PartParameterTable } from '../../components/tables/part/PartParameterTable'; | ||||||
| import { PartVariantTable } from '../../components/tables/part/PartVariantTable'; | import { PartVariantTable } from '../../components/tables/part/PartVariantTable'; | ||||||
| @@ -52,6 +53,8 @@ export default function PartDetail() { | |||||||
|  |  | ||||||
|   const user = useUserState(); |   const user = useUserState(); | ||||||
|  |  | ||||||
|  |   const [treeOpen, setTreeOpen] = useState(false); | ||||||
|  |  | ||||||
|   const { |   const { | ||||||
|     instance: part, |     instance: part, | ||||||
|     refreshInstance, |     refreshInstance, | ||||||
| @@ -289,12 +292,22 @@ export default function PartDetail() { | |||||||
|     <> |     <> | ||||||
|       <Stack spacing="xs"> |       <Stack spacing="xs"> | ||||||
|         <LoadingOverlay visible={instanceQuery.isFetching} /> |         <LoadingOverlay visible={instanceQuery.isFetching} /> | ||||||
|  |         <PartCategoryTree | ||||||
|  |           opened={treeOpen} | ||||||
|  |           onClose={() => { | ||||||
|  |             setTreeOpen(false); | ||||||
|  |           }} | ||||||
|  |           selectedCategory={part?.category} | ||||||
|  |         /> | ||||||
|         <PageDetail |         <PageDetail | ||||||
|           title={t`Part` + ': ' + part.full_name} |           title={t`Part` + ': ' + part.full_name} | ||||||
|           subtitle={part.description} |           subtitle={part.description} | ||||||
|           imageUrl={part.image} |           imageUrl={part.image} | ||||||
|           detail={partDetail} |           detail={partDetail} | ||||||
|           breadcrumbs={breadcrumbs} |           breadcrumbs={breadcrumbs} | ||||||
|  |           breadcrumbAction={() => { | ||||||
|  |             setTreeOpen(true); | ||||||
|  |           }} | ||||||
|           actions={partActions} |           actions={partActions} | ||||||
|         /> |         /> | ||||||
|         <PanelGroup pageKey="part" panels={partPanels} /> |         <PanelGroup pageKey="part" panels={partPanels} /> | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| import { t } from '@lingui/macro'; | import { t } from '@lingui/macro'; | ||||||
| import { LoadingOverlay, Stack, Text } from '@mantine/core'; | import { LoadingOverlay, Stack, Text } from '@mantine/core'; | ||||||
| import { IconPackages, IconSitemap } from '@tabler/icons-react'; | import { IconPackages, IconSitemap } from '@tabler/icons-react'; | ||||||
| import { useMemo } from 'react'; | import { useMemo, useState } from 'react'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { PageDetail } from '../../components/nav/PageDetail'; | import { PageDetail } from '../../components/nav/PageDetail'; | ||||||
| import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | ||||||
|  | import { StockLocationTree } from '../../components/nav/StockLocationTree'; | ||||||
| import { StockItemTable } from '../../components/tables/stock/StockItemTable'; | import { StockItemTable } from '../../components/tables/stock/StockItemTable'; | ||||||
| import { StockLocationTable } from '../../components/tables/stock/StockLocationTable'; | import { StockLocationTable } from '../../components/tables/stock/StockLocationTable'; | ||||||
| import { useInstance } from '../../hooks/UseInstance'; | import { useInstance } from '../../hooks/UseInstance'; | ||||||
| @@ -14,6 +15,8 @@ import { ApiPaths } from '../../states/ApiState'; | |||||||
| export default function Stock() { | export default function Stock() { | ||||||
|   const { id } = useParams(); |   const { id } = useParams(); | ||||||
|  |  | ||||||
|  |   const [treeOpen, setTreeOpen] = useState(false); | ||||||
|  |  | ||||||
|   const { |   const { | ||||||
|     instance: location, |     instance: location, | ||||||
|     refreshInstance, |     refreshInstance, | ||||||
| @@ -70,10 +73,18 @@ export default function Stock() { | |||||||
|     <> |     <> | ||||||
|       <Stack> |       <Stack> | ||||||
|         <LoadingOverlay visible={instanceQuery.isFetching} /> |         <LoadingOverlay visible={instanceQuery.isFetching} /> | ||||||
|  |         <StockLocationTree | ||||||
|  |           opened={treeOpen} | ||||||
|  |           onClose={() => setTreeOpen(false)} | ||||||
|  |           selectedLocation={location?.pk} | ||||||
|  |         /> | ||||||
|         <PageDetail |         <PageDetail | ||||||
|           title={t`Stock Items`} |           title={t`Stock Items`} | ||||||
|           detail={<Text>{location.name ?? 'Top level'}</Text>} |           detail={<Text>{location.name ?? 'Top level'}</Text>} | ||||||
|           breadcrumbs={breadcrumbs} |           breadcrumbs={breadcrumbs} | ||||||
|  |           breadcrumbAction={() => { | ||||||
|  |             setTreeOpen(true); | ||||||
|  |           }} | ||||||
|         /> |         /> | ||||||
|         <PanelGroup pageKey="stocklocation" panels={locationPanels} /> |         <PanelGroup pageKey="stocklocation" panels={locationPanels} /> | ||||||
|       </Stack> |       </Stack> | ||||||
|   | |||||||
| @@ -9,12 +9,13 @@ import { | |||||||
|   IconPaperclip, |   IconPaperclip, | ||||||
|   IconSitemap |   IconSitemap | ||||||
| } from '@tabler/icons-react'; | } from '@tabler/icons-react'; | ||||||
| import { useMemo } from 'react'; | import { useMemo, useState } from 'react'; | ||||||
| import { useParams } from 'react-router-dom'; | import { useParams } from 'react-router-dom'; | ||||||
|  |  | ||||||
| import { PlaceholderPanel } from '../../components/items/Placeholder'; | import { PlaceholderPanel } from '../../components/items/Placeholder'; | ||||||
| import { PageDetail } from '../../components/nav/PageDetail'; | import { PageDetail } from '../../components/nav/PageDetail'; | ||||||
| import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | import { PanelGroup, PanelType } from '../../components/nav/PanelGroup'; | ||||||
|  | import { StockLocationTree } from '../../components/nav/StockLocationTree'; | ||||||
| import { AttachmentTable } from '../../components/tables/general/AttachmentTable'; | import { AttachmentTable } from '../../components/tables/general/AttachmentTable'; | ||||||
| import { NotesEditor } from '../../components/widgets/MarkdownEditor'; | import { NotesEditor } from '../../components/widgets/MarkdownEditor'; | ||||||
| import { useInstance } from '../../hooks/UseInstance'; | import { useInstance } from '../../hooks/UseInstance'; | ||||||
| @@ -23,6 +24,8 @@ import { ApiPaths, apiUrl } from '../../states/ApiState'; | |||||||
| export default function StockDetail() { | export default function StockDetail() { | ||||||
|   const { id } = useParams(); |   const { id } = useParams(); | ||||||
|  |  | ||||||
|  |   const [treeOpen, setTreeOpen] = useState(false); | ||||||
|  |  | ||||||
|   const { |   const { | ||||||
|     instance: stockitem, |     instance: stockitem, | ||||||
|     refreshInstance, |     refreshInstance, | ||||||
| @@ -110,6 +113,11 @@ export default function StockDetail() { | |||||||
|   return ( |   return ( | ||||||
|     <Stack> |     <Stack> | ||||||
|       <LoadingOverlay visible={instanceQuery.isFetching} /> |       <LoadingOverlay visible={instanceQuery.isFetching} /> | ||||||
|  |       <StockLocationTree | ||||||
|  |         opened={treeOpen} | ||||||
|  |         onClose={() => setTreeOpen(false)} | ||||||
|  |         selectedLocation={stockitem?.location} | ||||||
|  |       /> | ||||||
|       <PageDetail |       <PageDetail | ||||||
|         title={t`Stock Items`} |         title={t`Stock Items`} | ||||||
|         subtitle={stockitem.part_detail?.full_name ?? 'name goes here'} |         subtitle={stockitem.part_detail?.full_name ?? 'name goes here'} | ||||||
| @@ -119,6 +127,9 @@ export default function StockDetail() { | |||||||
|           </Alert> |           </Alert> | ||||||
|         } |         } | ||||||
|         breadcrumbs={breadcrumbs} |         breadcrumbs={breadcrumbs} | ||||||
|  |         breadcrumbAction={() => { | ||||||
|  |           setTreeOpen(true); | ||||||
|  |         }} | ||||||
|       /> |       /> | ||||||
|       <PanelGroup pageKey="stockitem" panels={stockPanels} /> |       <PanelGroup pageKey="stockitem" panels={stockPanels} /> | ||||||
|     </Stack> |     </Stack> | ||||||
|   | |||||||
| @@ -76,6 +76,7 @@ export enum ApiPaths { | |||||||
|   // Part URLs |   // Part URLs | ||||||
|   part_list = 'api-part-list', |   part_list = 'api-part-list', | ||||||
|   category_list = 'api-category-list', |   category_list = 'api-category-list', | ||||||
|  |   category_tree = 'api-category-tree', | ||||||
|   related_part_list = 'api-related-part-list', |   related_part_list = 'api-related-part-list', | ||||||
|   part_attachment_list = 'api-part-attachment-list', |   part_attachment_list = 'api-part-attachment-list', | ||||||
|   part_parameter_list = 'api-part-parameter-list', |   part_parameter_list = 'api-part-parameter-list', | ||||||
| @@ -89,6 +90,7 @@ export enum ApiPaths { | |||||||
|   // Stock Item URLs |   // Stock Item URLs | ||||||
|   stock_item_list = 'api-stock-item-list', |   stock_item_list = 'api-stock-item-list', | ||||||
|   stock_location_list = 'api-stock-location-list', |   stock_location_list = 'api-stock-location-list', | ||||||
|  |   stock_location_tree = 'api-stock-location-tree', | ||||||
|   stock_attachment_list = 'api-stock-attachment-list', |   stock_attachment_list = 'api-stock-attachment-list', | ||||||
|  |  | ||||||
|   // Purchase Order URLs |   // Purchase Order URLs | ||||||
| @@ -165,6 +167,8 @@ export function apiEndpoint(path: ApiPaths): string { | |||||||
|       return 'part/parameter/template/'; |       return 'part/parameter/template/'; | ||||||
|     case ApiPaths.category_list: |     case ApiPaths.category_list: | ||||||
|       return 'part/category/'; |       return 'part/category/'; | ||||||
|  |     case ApiPaths.category_tree: | ||||||
|  |       return 'part/category/tree/'; | ||||||
|     case ApiPaths.related_part_list: |     case ApiPaths.related_part_list: | ||||||
|       return 'part/related/'; |       return 'part/related/'; | ||||||
|     case ApiPaths.part_attachment_list: |     case ApiPaths.part_attachment_list: | ||||||
| @@ -179,6 +183,8 @@ export function apiEndpoint(path: ApiPaths): string { | |||||||
|       return 'stock/'; |       return 'stock/'; | ||||||
|     case ApiPaths.stock_location_list: |     case ApiPaths.stock_location_list: | ||||||
|       return 'stock/location/'; |       return 'stock/location/'; | ||||||
|  |     case ApiPaths.stock_location_tree: | ||||||
|  |       return 'stock/location/tree/'; | ||||||
|     case ApiPaths.stock_attachment_list: |     case ApiPaths.stock_attachment_list: | ||||||
|       return 'stock/attachment/'; |       return 'stock/attachment/'; | ||||||
|     case ApiPaths.purchase_order_list: |     case ApiPaths.purchase_order_list: | ||||||
|   | |||||||
| @@ -389,6 +389,13 @@ | |||||||
|   resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" |   resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" | ||||||
|   integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== |   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": | "@emotion/memoize@^0.8.1": | ||||||
|   version "0.8.1" |   version "0.8.1" | ||||||
|   resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" |   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" |   resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec" | ||||||
|   integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA== |   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" |   version "0.8.1" | ||||||
|   resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" |   resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" | ||||||
|   integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== |   integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== | ||||||
| @@ -963,6 +970,15 @@ | |||||||
|   dependencies: |   dependencies: | ||||||
|     moo "^0.5.1" |     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": | "@playwright/test@^1.39.0": | ||||||
|   version "1.39.0" |   version "1.39.0" | ||||||
|   resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.39.0.tgz#d10ba8e38e44104499e25001945f07faa9fa91cd" |   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" |   resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.4.tgz#fedc3e5b15c26dc18faae96bf1317487cb3658cf" | ||||||
|   integrity sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ== |   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@*": | "@types/tern@*": | ||||||
|   version "0.23.5" |   version "0.23.5" | ||||||
|   resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.5.tgz#8d369a06749ea83956885cb734788ec208a0e900" |   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" |   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" | ||||||
|   integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== |   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: | caniuse-lite@^1.0.30001541: | ||||||
|   version "1.0.30001549" |   version "1.0.30001549" | ||||||
|   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz#7d1a3dce7ea78c06ed72c32c2743ea364b3615aa" |   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" |     parse-json "^5.2.0" | ||||||
|     path-type "^4.0.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: | csstype@3.0.9: | ||||||
|   version "3.0.9" |   version "3.0.9" | ||||||
|   resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b" |   resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b" | ||||||
|   integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw== |   integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw== | ||||||
|  |  | ||||||
| csstype@^3.0.2: | csstype@^3.0.2, csstype@^3.1.2: | ||||||
|   version "3.1.2" |   version "3.1.2" | ||||||
|   resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" |   resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" | ||||||
|   integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== |   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" |   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" | ||||||
|   integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== |   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: | node-releases@^2.0.13: | ||||||
|   version "2.0.13" |   version "2.0.13" | ||||||
|   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" |   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" |   resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.1.4.tgz#eab7e29f5017589b2a61b2259dff608c0cad76a2" | ||||||
|   integrity sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g== |   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" |   version "8.4.31" | ||||||
|   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" |   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" | ||||||
|   integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== |   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" |   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" | ||||||
|   integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== |   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: | signal-exit@^3.0.2: | ||||||
|   version "3.0.7" |   version "3.0.7" | ||||||
|   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" |   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: |   dependencies: | ||||||
|     ansi-regex "^5.0.1" |     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: | stylis@4.2.0: | ||||||
|   version "4.2.0" |   version "4.2.0" | ||||||
|   resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" |   resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" | ||||||
|   integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== |   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: | supports-color@^5.3.0: | ||||||
|   version "5.5.0" |   version "5.5.0" | ||||||
|   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" |   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" |   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" | ||||||
|   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== |   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" |   version "2.6.2" | ||||||
|   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" |   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" | ||||||
|   integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== |   integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user