mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	[P-UI] Home page (#5344)
* Add new start page * [FR/P-UI] Home/Start page - widgets * load widgets dynamic * code cleanup * remove lodash * refactor and rename to WidgetLayout * Add CSS serving * removed unneeded complexity * clean up UI; switch to menu for controls * change signature * added hotkey * removed hover * removed dummy widget * Add translations * fix test * uses real data for getting started * adapted style for GettingStartedCard * added placeholder usage to GettingStartedCard
This commit is contained in:
		| @@ -18,6 +18,7 @@ | ||||
|         "@fortawesome/react-fontawesome": "^0.2.0", | ||||
|         "@lingui/core": "^4.3.0", | ||||
|         "@lingui/react": "^4.3.0", | ||||
|         "@mantine/carousel": "^6.0.17", | ||||
|         "@mantine/core": "^6.0.17", | ||||
|         "@mantine/dates": "^6.0.17", | ||||
|         "@mantine/dropzone": "^6.0.17", | ||||
| @@ -29,10 +30,12 @@ | ||||
|         "@tanstack/react-query": "^4.32.0", | ||||
|         "axios": "^1.4.0", | ||||
|         "dayjs": "^1.11.9", | ||||
|         "embla-carousel-react": "^8.0.0-rc11", | ||||
|         "html5-qrcode": "^2.3.8", | ||||
|         "mantine-datatable": "^2.9.0", | ||||
|         "react": "^18.2.0", | ||||
|         "react-dom": "^18.2.0", | ||||
|         "react-grid-layout": "^1.3.4", | ||||
|         "react-router-dom": "^6.14.2", | ||||
|         "zustand": "^4.3.9" | ||||
|     }, | ||||
| @@ -46,6 +49,7 @@ | ||||
|         "@types/node": "^20.4.4", | ||||
|         "@types/react": "^18.2.15", | ||||
|         "@types/react-dom": "^18.2.7", | ||||
|         "@types/react-grid-layout": "^1.3.2", | ||||
|         "@types/react-router-dom": "^5.3.3", | ||||
|         "@vitejs/plugin-react": "^4.0.3", | ||||
|         "babel-plugin-macros": "^3.1.0", | ||||
|   | ||||
							
								
								
									
										92
									
								
								src/frontend/src/components/items/GettingStartedCarousel.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/frontend/src/components/items/GettingStartedCarousel.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| import { Trans } from '@lingui/macro'; | ||||
| import { Carousel } from '@mantine/carousel'; | ||||
| import { | ||||
|   Anchor, | ||||
|   Button, | ||||
|   Paper, | ||||
|   Text, | ||||
|   Title, | ||||
|   createStyles, | ||||
|   rem | ||||
| } from '@mantine/core'; | ||||
|  | ||||
| import { DocumentationLinkItem } from './DocumentationLinks'; | ||||
| import { PlaceholderPill } from './Placeholder'; | ||||
|  | ||||
| const useStyles = createStyles((theme) => ({ | ||||
|   card: { | ||||
|     height: rem(170), | ||||
|     display: 'flex', | ||||
|     flexDirection: 'column', | ||||
|     justifyContent: 'space-between', | ||||
|     alignItems: 'flex-start', | ||||
|     backgroundSize: 'cover', | ||||
|     backgroundPosition: 'center' | ||||
|   }, | ||||
|  | ||||
|   title: { | ||||
|     fontWeight: 900, | ||||
|     color: | ||||
|       theme.colorScheme === 'dark' ? theme.colors.white : theme.colors.dark, | ||||
|     lineHeight: 1.2, | ||||
|     fontSize: rem(32), | ||||
|     marginTop: 0 | ||||
|   }, | ||||
|  | ||||
|   category: { | ||||
|     color: | ||||
|       theme.colorScheme === 'dark' ? theme.colors.white : theme.colors.dark, | ||||
|     opacity: 0.7, | ||||
|     fontWeight: 700 | ||||
|   } | ||||
| })); | ||||
|  | ||||
| function StartedCard({ | ||||
|   title, | ||||
|   description, | ||||
|   link, | ||||
|   placeholder | ||||
| }: DocumentationLinkItem) { | ||||
|   const { classes } = useStyles(); | ||||
|  | ||||
|   return ( | ||||
|     <Paper shadow="md" p="xl" radius="md" className={classes.card}> | ||||
|       <div> | ||||
|         <Title order={3} className={classes.title}> | ||||
|           {title} {placeholder && <PlaceholderPill />} | ||||
|         </Title> | ||||
|         <Text size="sm" className={classes.category} lineClamp={2}> | ||||
|           {description} | ||||
|         </Text> | ||||
|       </div> | ||||
|       <Anchor href={link} target="_blank"> | ||||
|         <Button> | ||||
|           <Trans>Read more</Trans> | ||||
|         </Button> | ||||
|       </Anchor> | ||||
|     </Paper> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| export function GettingStartedCarousel({ | ||||
|   items | ||||
| }: { | ||||
|   items: DocumentationLinkItem[]; | ||||
| }) { | ||||
|   const slides = items.map((item) => ( | ||||
|     <Carousel.Slide key={item.id}> | ||||
|       <StartedCard {...item} /> | ||||
|     </Carousel.Slide> | ||||
|   )); | ||||
|  | ||||
|   return ( | ||||
|     <Carousel | ||||
|       slideSize="50%" | ||||
|       breakpoints={[{ maxWidth: 'sm', slideSize: '100%', slideGap: rem(2) }]} | ||||
|       slideGap="xl" | ||||
|       align="start" | ||||
|     > | ||||
|       {slides} | ||||
|     </Carousel> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/frontend/src/components/widgets/DisplayWidget.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/frontend/src/components/widgets/DisplayWidget.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| import { Trans } from '@lingui/macro'; | ||||
| import { SimpleGrid, Title } from '@mantine/core'; | ||||
|  | ||||
| import { ColorToggle } from '../items/ColorToggle'; | ||||
| import { LanguageSelect } from '../items/LanguageSelect'; | ||||
|  | ||||
| export default function DisplayWidget() { | ||||
|   return ( | ||||
|     <span> | ||||
|       <Title order={5}> | ||||
|         <Trans>Display Settings</Trans> | ||||
|       </Title> | ||||
|       <SimpleGrid cols={2} spacing={0}> | ||||
|         <div> | ||||
|           <Trans>Color Mode</Trans> | ||||
|         </div> | ||||
|         <div> | ||||
|           <ColorToggle /> | ||||
|         </div> | ||||
|         <div> | ||||
|           <Trans>Language</Trans> | ||||
|         </div> | ||||
|         <div> | ||||
|           <LanguageSelect /> | ||||
|         </div> | ||||
|       </SimpleGrid> | ||||
|     </span> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										35
									
								
								src/frontend/src/components/widgets/FeedbackWidget.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/frontend/src/components/widgets/FeedbackWidget.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import { Trans } from '@lingui/macro'; | ||||
| import { Button, Stack, Title } from '@mantine/core'; | ||||
| import { IconExternalLink } from '@tabler/icons-react'; | ||||
|  | ||||
| export default function FeedbackWidget() { | ||||
|   return ( | ||||
|     <Stack | ||||
|       sx={(theme) => ({ | ||||
|         backgroundColor: | ||||
|           theme.colorScheme === 'dark' | ||||
|             ? theme.colors.gray[9] | ||||
|             : theme.colors.gray[1], | ||||
|         borderRadius: theme.radius.md | ||||
|       })} | ||||
|       p={15} | ||||
|     > | ||||
|       <Title order={5}> | ||||
|         <Trans>Something is new: Platform UI</Trans> | ||||
|       </Title> | ||||
|       <Trans> | ||||
|         We are building a new UI with a modern stack. What you currently see is | ||||
|         not fixed and will be redesigned but demonstrates the UI/UX | ||||
|         possibilities we will have going forward. | ||||
|       </Trans> | ||||
|       <Button | ||||
|         component="a" | ||||
|         href="https://github.com/inventree/InvenTree/discussions/5328" | ||||
|         variant="outline" | ||||
|         leftIcon={<IconExternalLink size="0.9rem" />} | ||||
|       > | ||||
|         <Trans>Provide Feedback</Trans> | ||||
|       </Button> | ||||
|     </Stack> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										16
									
								
								src/frontend/src/components/widgets/GetStartedWidget.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/frontend/src/components/widgets/GetStartedWidget.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| import { Trans } from '@lingui/macro'; | ||||
| import { Title } from '@mantine/core'; | ||||
|  | ||||
| import { navDocLinks } from '../../defaults/links'; | ||||
| import { GettingStartedCarousel } from '../items/GettingStartedCarousel'; | ||||
|  | ||||
| export default function GetStartedWidget() { | ||||
|   return ( | ||||
|     <span> | ||||
|       <Title order={5}> | ||||
|         <Trans>Getting started</Trans> | ||||
|       </Title> | ||||
|       <GettingStartedCarousel items={navDocLinks} /> | ||||
|     </span> | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										246
									
								
								src/frontend/src/components/widgets/WidgetLayout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								src/frontend/src/components/widgets/WidgetLayout.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | ||||
| import { Trans } from '@lingui/macro'; | ||||
| import { | ||||
|   ActionIcon, | ||||
|   Container, | ||||
|   Group, | ||||
|   Indicator, | ||||
|   createStyles | ||||
| } from '@mantine/core'; | ||||
| import { Menu, Text } from '@mantine/core'; | ||||
| import { useDisclosure, useHotkeys } from '@mantine/hooks'; | ||||
| import { | ||||
|   IconArrowBackUpDouble, | ||||
|   IconDotsVertical, | ||||
|   IconLayout2, | ||||
|   IconSquare, | ||||
|   IconSquareCheck | ||||
| } from '@tabler/icons-react'; | ||||
| import { useEffect, useState } from 'react'; | ||||
| import { Responsive, WidthProvider } from 'react-grid-layout'; | ||||
|  | ||||
| const ReactGridLayout = WidthProvider(Responsive); | ||||
|  | ||||
| interface LayoutStorage { | ||||
|   [key: string]: {}; | ||||
| } | ||||
|  | ||||
| const compactType = 'vertical'; | ||||
|  | ||||
| const useItemStyle = createStyles((theme) => ({ | ||||
|   backgroundItem: { | ||||
|     backgroundColor: | ||||
|       theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.white, | ||||
|     maxWidth: '100%', | ||||
|     padding: '8px', | ||||
|     boxShadow: theme.shadows.md | ||||
|   }, | ||||
|  | ||||
|   baseItem: { | ||||
|     maxWidth: '100%', | ||||
|     padding: '8px' | ||||
|   } | ||||
| })); | ||||
|  | ||||
| export interface LayoutItemType { | ||||
|   i: number; | ||||
|   val: string | JSX.Element | JSX.Element[] | (() => JSX.Element); | ||||
|   w?: number; | ||||
|   h?: number; | ||||
|   x?: number; | ||||
|   y?: number; | ||||
|   minH?: number; | ||||
| } | ||||
|  | ||||
| export function WidgetLayout({ | ||||
|   items = [], | ||||
|   className = 'layout', | ||||
|   localstorageName = 'argl', | ||||
|   rowHeight = 30 | ||||
| }: { | ||||
|   items: LayoutItemType[]; | ||||
|   className?: string; | ||||
|   localstorageName?: string; | ||||
|   rowHeight?: number; | ||||
| }) { | ||||
|   const [layouts, setLayouts] = useState({}); | ||||
|   const [editable, setEditable] = useDisclosure(false); | ||||
|   const [boxShown, setBoxShown] = useDisclosure(true); | ||||
|   const { classes } = useItemStyle(); | ||||
|  | ||||
|   useEffect(() => { | ||||
|     let layout = getFromLS('layouts') || []; | ||||
|     const new_layout = JSON.parse(JSON.stringify(layout)); | ||||
|     setLayouts(new_layout); | ||||
|   }, []); | ||||
|  | ||||
|   function getFromLS(key: string) { | ||||
|     let ls: LayoutStorage = {}; | ||||
|     if (localStorage) { | ||||
|       try { | ||||
|         ls = JSON.parse(localStorage.getItem(localstorageName) || '') || {}; | ||||
|       } catch (e) { | ||||
|         /*Ignore*/ | ||||
|       } | ||||
|     } | ||||
|     return ls[key]; | ||||
|   } | ||||
|  | ||||
|   function saveToLS(key: string, value: any) { | ||||
|     if (localStorage) { | ||||
|       localStorage.setItem( | ||||
|         localstorageName, | ||||
|         JSON.stringify({ | ||||
|           [key]: value | ||||
|         }) | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   function resetLayout() { | ||||
|     setLayouts({}); | ||||
|   } | ||||
|  | ||||
|   function onLayoutChange(layout: any, layouts: any) { | ||||
|     saveToLS('layouts', layouts); | ||||
|     setLayouts(layouts); | ||||
|   } | ||||
|  | ||||
|   return ( | ||||
|     <div> | ||||
|       <WidgetControlBar | ||||
|         editable={editable} | ||||
|         editFnc={setEditable.toggle} | ||||
|         resetLayout={resetLayout} | ||||
|         boxShown={boxShown} | ||||
|         boxFnc={setBoxShown.toggle} | ||||
|       /> | ||||
|       {layouts ? ( | ||||
|         <ReactGridLayout | ||||
|           className={className} | ||||
|           cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }} | ||||
|           rowHeight={rowHeight} | ||||
|           layouts={layouts} | ||||
|           onLayoutChange={(layout, layouts) => onLayoutChange(layout, layouts)} | ||||
|           compactType={compactType} | ||||
|           isDraggable={editable} | ||||
|           isResizable={editable} | ||||
|         > | ||||
|           {items.map((item) => { | ||||
|             return LayoutItem(item, boxShown, classes); | ||||
|           })} | ||||
|         </ReactGridLayout> | ||||
|       ) : ( | ||||
|         <div> | ||||
|           <Trans>Loading</Trans> | ||||
|         </div> | ||||
|       )} | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| function WidgetControlBar({ | ||||
|   editable, | ||||
|   editFnc, | ||||
|   resetLayout, | ||||
|   boxShown, | ||||
|   boxFnc | ||||
| }: { | ||||
|   editable: boolean; | ||||
|   editFnc: () => void; | ||||
|   resetLayout: () => void; | ||||
|   boxShown: boolean; | ||||
|   boxFnc: () => void; | ||||
| }) { | ||||
|   useHotkeys([['mod+E', () => editFnc()]]); | ||||
|  | ||||
|   return ( | ||||
|     <Group position="right"> | ||||
|       <Menu | ||||
|         shadow="md" | ||||
|         width={200} | ||||
|         openDelay={100} | ||||
|         closeDelay={400} | ||||
|         position="bottom-end" | ||||
|       > | ||||
|         <Menu.Target> | ||||
|           <Indicator | ||||
|             color="red" | ||||
|             position="bottom-start" | ||||
|             processing | ||||
|             disabled={!editable} | ||||
|           > | ||||
|             <ActionIcon variant="transparent"> | ||||
|               <IconDotsVertical /> | ||||
|             </ActionIcon> | ||||
|           </Indicator> | ||||
|         </Menu.Target> | ||||
|  | ||||
|         <Menu.Dropdown> | ||||
|           <Menu.Label> | ||||
|             <Trans>Layout</Trans> | ||||
|           </Menu.Label> | ||||
|           <Menu.Item | ||||
|             icon={<IconArrowBackUpDouble size={14} />} | ||||
|             onClick={resetLayout} | ||||
|           > | ||||
|             <Trans>Reset Layout</Trans> | ||||
|           </Menu.Item> | ||||
|           <Menu.Item | ||||
|             icon={ | ||||
|               <IconLayout2 size={14} color={editable ? 'red' : undefined} /> | ||||
|             } | ||||
|             onClick={editFnc} | ||||
|             rightSection={ | ||||
|               <Text size="xs" color="dimmed"> | ||||
|                 ⌘E | ||||
|               </Text> | ||||
|             } | ||||
|           > | ||||
|             {editable ? <Trans>Stop Edit</Trans> : <Trans>Edit Layout</Trans>} | ||||
|           </Menu.Item> | ||||
|  | ||||
|           <Menu.Divider /> | ||||
|  | ||||
|           <Menu.Label> | ||||
|             <Trans>Appearance</Trans> | ||||
|           </Menu.Label> | ||||
|           <Menu.Item | ||||
|             icon={ | ||||
|               boxShown ? ( | ||||
|                 <IconSquareCheck size={14} /> | ||||
|               ) : ( | ||||
|                 <IconSquare size={14} /> | ||||
|               ) | ||||
|             } | ||||
|             onClick={boxFnc} | ||||
|           > | ||||
|             <Trans>Show Boxes</Trans> | ||||
|           </Menu.Item> | ||||
|         </Menu.Dropdown> | ||||
|       </Menu> | ||||
|     </Group> | ||||
|   ); | ||||
| } | ||||
|  | ||||
| function LayoutItem( | ||||
|   item: any, | ||||
|   backgroundColor: boolean, | ||||
|   classes: { backgroundItem: string; baseItem: string } | ||||
| ) { | ||||
|   return ( | ||||
|     <Container | ||||
|       key={item.i} | ||||
|       data-grid={{ | ||||
|         w: item.w || 3, | ||||
|         h: item.h || 3, | ||||
|         x: item.x || 0, | ||||
|         y: item.y || 0, | ||||
|         minH: item.minH || undefined, | ||||
|         minW: item.minW || undefined | ||||
|       }} | ||||
|       className={backgroundColor ? classes.backgroundItem : classes.baseItem} | ||||
|     > | ||||
|       {item.val} | ||||
|     </Container> | ||||
|   ); | ||||
| } | ||||
| @@ -17,3 +17,8 @@ export const Loadable = (Component: any) => (props: JSX.IntrinsicAttributes) => | ||||
|       <Component {...props} /> | ||||
|     </Suspense> | ||||
|   ); | ||||
|  | ||||
| export function LoadingItem({ item }: { item: any }): JSX.Element { | ||||
|   const Itm = Loadable(item); | ||||
|   return <Itm />; | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,5 +1,7 @@ | ||||
| import React from 'react'; | ||||
| import ReactDOM from 'react-dom/client'; | ||||
| import 'react-grid-layout/css/styles.css'; | ||||
| import 'react-resizable/css/styles.css'; | ||||
|  | ||||
| import App from './App'; | ||||
|  | ||||
|   | ||||
| @@ -1,18 +1,63 @@ | ||||
| import { Trans } from '@lingui/macro'; | ||||
| import { Group } from '@mantine/core'; | ||||
| import { Title } from '@mantine/core'; | ||||
| import { lazy } from 'react'; | ||||
|  | ||||
| import { PlaceholderPill } from '../../components/items/Placeholder'; | ||||
| import { StylishText } from '../../components/items/StylishText'; | ||||
| import { | ||||
|   LayoutItemType, | ||||
|   WidgetLayout | ||||
| } from '../../components/widgets/WidgetLayout'; | ||||
| import { LoadingItem } from '../../functions/loading'; | ||||
| import { useApiState } from '../../states/ApiState'; | ||||
|  | ||||
| const vals: LayoutItemType[] = [ | ||||
|   { | ||||
|     i: 1, | ||||
|     val: ( | ||||
|       <LoadingItem | ||||
|         item={lazy(() => import('../../components/widgets/GetStartedWidget'))} | ||||
|       /> | ||||
|     ), | ||||
|     w: 12, | ||||
|     h: 6, | ||||
|     x: 0, | ||||
|     y: 0, | ||||
|     minH: 6 | ||||
|   }, | ||||
|   { | ||||
|     i: 2, | ||||
|     val: ( | ||||
|       <LoadingItem | ||||
|         item={lazy(() => import('../../components/widgets/DisplayWidget'))} | ||||
|       /> | ||||
|     ), | ||||
|     w: 3, | ||||
|     h: 3, | ||||
|     x: 0, | ||||
|     y: 7, | ||||
|     minH: 3 | ||||
|   }, | ||||
|   { | ||||
|     i: 4, | ||||
|     val: ( | ||||
|       <LoadingItem | ||||
|         item={lazy(() => import('../../components/widgets/FeedbackWidget'))} | ||||
|       /> | ||||
|     ), | ||||
|     w: 4, | ||||
|     h: 6, | ||||
|     x: 0, | ||||
|     y: 9 | ||||
|   } | ||||
| ]; | ||||
|  | ||||
| export default function Home() { | ||||
|   const [username] = useApiState((state) => [state.user?.name]); | ||||
|   return ( | ||||
|     <> | ||||
|       <Group> | ||||
|         <StylishText> | ||||
|           <Trans>Home</Trans> | ||||
|         </StylishText> | ||||
|         <PlaceholderPill /> | ||||
|       </Group> | ||||
|       <Title order={1}> | ||||
|         <Trans>Welcome to your Dashboard{username && `, ${username}`}</Trans> | ||||
|       </Title> | ||||
|       <WidgetLayout items={vals} /> | ||||
|     </> | ||||
|   ); | ||||
| } | ||||
|   | ||||
| @@ -11,5 +11,9 @@ test('Basic Platform UI test', async ({ page }) => { | ||||
|   await page.goto('./platform/'); | ||||
|  | ||||
|   await expect(page).toHaveTitle('InvenTree Demo Server'); | ||||
|   await expect(page.getByText('Home').nth(1)).toBeVisible(); | ||||
|   await expect( | ||||
|     page.getByRole('heading', { | ||||
|       name: 'Welcome to your Dashboard, Ally Access' | ||||
|     }) | ||||
|   ).toBeVisible(); | ||||
| }); | ||||
|   | ||||
| @@ -877,6 +877,13 @@ | ||||
|     "@babel/runtime" "^7.20.13" | ||||
|     "@lingui/core" "4.3.0" | ||||
|  | ||||
| "@mantine/carousel@^6.0.17": | ||||
|   version "6.0.17" | ||||
|   resolved "https://registry.yarnpkg.com/@mantine/carousel/-/carousel-6.0.17.tgz#d31fc9bc9ef14bd5ea3e9162d4a130b904cb478e" | ||||
|   integrity sha512-cKX7zGmWVXdq/mPff5QYaHLR2X6bujbR4YZ3Hs3TD8KuySTZDOHipUD9IAVH1DtYJRE0+FIRb6OeZ7X9/N2Erg== | ||||
|   dependencies: | ||||
|     "@mantine/utils" "6.0.17" | ||||
|  | ||||
| "@mantine/core@^6.0.17": | ||||
|   version "6.0.17" | ||||
|   resolved "https://registry.npmjs.org/@mantine/core/-/core-6.0.17.tgz" | ||||
| @@ -1167,6 +1174,13 @@ | ||||
|   dependencies: | ||||
|     "@types/react" "*" | ||||
|  | ||||
| "@types/react-grid-layout@^1.3.2": | ||||
|   version "1.3.2" | ||||
|   resolved "https://registry.yarnpkg.com/@types/react-grid-layout/-/react-grid-layout-1.3.2.tgz#9f195666a018a5ae2b773887e3b552cb4378d67f" | ||||
|   integrity sha512-ZzpBEOC1JTQ7MGe1h1cPKSLP4jSWuxc+yvT4TsAlEW9+EFPzAf8nxQfFd7ea9gL17Em7PbwJZAsiwfQQBUklZQ== | ||||
|   dependencies: | ||||
|     "@types/react" "*" | ||||
|  | ||||
| "@types/react-router-dom@^5.3.3": | ||||
|   version "5.3.3" | ||||
|   resolved "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz" | ||||
| @@ -1442,6 +1456,11 @@ clsx@1.1.1: | ||||
|   resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz" | ||||
|   integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== | ||||
|  | ||||
| clsx@^1.1.1: | ||||
|   version "1.2.1" | ||||
|   resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" | ||||
|   integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== | ||||
|  | ||||
| color-convert@^1.9.0: | ||||
|   version "1.9.3" | ||||
|   resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" | ||||
| @@ -1578,6 +1597,24 @@ electron-to-chromium@^1.4.431: | ||||
|   resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.468.tgz" | ||||
|   integrity sha512-6M1qyhaJOt7rQtNti1lBA0GwclPH+oKCmsra/hkcWs5INLxfXXD/dtdnaKUYQu/pjOBP/8Osoe4mAcNvvzoFag== | ||||
|  | ||||
| embla-carousel-react@^8.0.0-rc11: | ||||
|   version "8.0.0-rc11" | ||||
|   resolved "https://registry.yarnpkg.com/embla-carousel-react/-/embla-carousel-react-8.0.0-rc11.tgz#0e2fde5cafa3cae9c30721e18aee648599527994" | ||||
|   integrity sha512-hXOAUMOIa0GF5BtdTTqBuKcjgU+ipul6thTCXOZttqnu2c6VS3SIzUUT+onIIEw+AptzKJcPwGcoAByAGa9eJw== | ||||
|   dependencies: | ||||
|     embla-carousel "8.0.0-rc11" | ||||
|     embla-carousel-reactive-utils "8.0.0-rc11" | ||||
|  | ||||
| embla-carousel-reactive-utils@8.0.0-rc11: | ||||
|   version "8.0.0-rc11" | ||||
|   resolved "https://registry.yarnpkg.com/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.0.0-rc11.tgz#d5493bd2bfeb68b1cbf65d4c836a4d36779a03de" | ||||
|   integrity sha512-pDNVJNCn0dybLkHw93My+cMfkRQ5oLZff6ZCwgmrw+96aPiZUyo5ANywz8Lb70SWWgD/TNBRrtQCquvjHS31Sg== | ||||
|  | ||||
| embla-carousel@8.0.0-rc11: | ||||
|   version "8.0.0-rc11" | ||||
|   resolved "https://registry.yarnpkg.com/embla-carousel/-/embla-carousel-8.0.0-rc11.tgz#700ab6b3e4825ef9e6ac83238b81e3e1a316c3f4" | ||||
|   integrity sha512-Toeaug98PGYzSY56p/xsa+u4zbQbAXgGymwEDUc2wqT+1XCnnUsH42MClglhABJQbobwDYxOabhJrfXyJKUMig== | ||||
|  | ||||
| emoji-regex@^8.0.0: | ||||
|   version "8.0.0" | ||||
|   resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" | ||||
| @@ -1987,6 +2024,11 @@ lodash.get@^4.4.2: | ||||
|   resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" | ||||
|   integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== | ||||
|  | ||||
| lodash.isequal@^4.0.0: | ||||
|   version "4.5.0" | ||||
|   resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" | ||||
|   integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== | ||||
|  | ||||
| lodash.sortby@^4.7.0: | ||||
|   version "4.7.0" | ||||
|   resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" | ||||
| @@ -2231,7 +2273,7 @@ pretty-format@^29.6.1: | ||||
|     ansi-styles "^5.0.0" | ||||
|     react-is "^18.0.0" | ||||
|  | ||||
| prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: | ||||
| prop-types@15.x, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: | ||||
|   version "15.8.1" | ||||
|   resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" | ||||
|   integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== | ||||
| @@ -2270,6 +2312,14 @@ react-dom@^18.2.0: | ||||
|     loose-envify "^1.1.0" | ||||
|     scheduler "^0.23.0" | ||||
|  | ||||
| react-draggable@^4.0.0, react-draggable@^4.0.3: | ||||
|   version "4.4.5" | ||||
|   resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.5.tgz#9e37fe7ce1a4cf843030f521a0a4cc41886d7e7c" | ||||
|   integrity sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g== | ||||
|   dependencies: | ||||
|     clsx "^1.1.1" | ||||
|     prop-types "^15.8.1" | ||||
|  | ||||
| react-dropzone@14.2.3: | ||||
|   version "14.2.3" | ||||
|   resolved "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz" | ||||
| @@ -2279,6 +2329,17 @@ react-dropzone@14.2.3: | ||||
|     file-selector "^0.6.0" | ||||
|     prop-types "^15.8.1" | ||||
|  | ||||
| react-grid-layout@^1.3.4: | ||||
|   version "1.3.4" | ||||
|   resolved "https://registry.yarnpkg.com/react-grid-layout/-/react-grid-layout-1.3.4.tgz#4fa819be24a1ba9268aa11b82d63afc4762a32ff" | ||||
|   integrity sha512-sB3rNhorW77HUdOjB4JkelZTdJGQKuXLl3gNg+BI8gJkTScspL1myfZzW/EM0dLEn+1eH+xW+wNqk0oIM9o7cw== | ||||
|   dependencies: | ||||
|     clsx "^1.1.1" | ||||
|     lodash.isequal "^4.0.0" | ||||
|     prop-types "^15.8.1" | ||||
|     react-draggable "^4.0.0" | ||||
|     react-resizable "^3.0.4" | ||||
|  | ||||
| react-is@^16.13.1, react-is@^16.7.0: | ||||
|   version "16.13.1" | ||||
|   resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" | ||||
| @@ -2313,6 +2374,14 @@ react-remove-scroll@^2.5.5: | ||||
|     use-callback-ref "^1.3.0" | ||||
|     use-sidecar "^1.1.2" | ||||
|  | ||||
| react-resizable@^3.0.4: | ||||
|   version "3.0.5" | ||||
|   resolved "https://registry.yarnpkg.com/react-resizable/-/react-resizable-3.0.5.tgz#362721f2efbd094976f1780ae13f1ad7739786c1" | ||||
|   integrity sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w== | ||||
|   dependencies: | ||||
|     prop-types "15.x" | ||||
|     react-draggable "^4.0.3" | ||||
|  | ||||
| react-router-dom@^6.14.2: | ||||
|   version "6.14.2" | ||||
|   resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user