mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	[PUI] Added Server Info Modal (#5810)
* updated typing to allow either link or action * fixed typing * made it possible to use an action instead of a link * added ServerInfo Modal skeleton * fixed anchor * added content to ServerInfo * Factored database lookup out * Extended status API to CUI level * extended ServerInfo to CUI level * Made modal larger * fixed default settings
This commit is contained in:
		| @@ -18,10 +18,11 @@ from InvenTree.permissions import RolePermission | |||||||
| from part.templatetags.inventree_extras import plugins_info | from part.templatetags.inventree_extras import plugins_info | ||||||
| from plugin.serializers import MetadataSerializer | from plugin.serializers import MetadataSerializer | ||||||
|  |  | ||||||
|  | from .email import is_email_configured | ||||||
| from .mixins import RetrieveUpdateAPI | from .mixins import RetrieveUpdateAPI | ||||||
| from .status import is_worker_running | from .status import check_system_health, is_worker_running | ||||||
| from .version import (inventreeApiVersion, inventreeInstanceName, | from .version import (inventreeApiVersion, inventreeDatabase, | ||||||
|                       inventreeVersion) |                       inventreeInstanceName, inventreeVersion) | ||||||
| from .views import AjaxView | from .views import AjaxView | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -48,6 +49,11 @@ class InfoView(AjaxView): | |||||||
|             'worker_pending_tasks': self.worker_pending_tasks(), |             'worker_pending_tasks': self.worker_pending_tasks(), | ||||||
|             'plugins_enabled': settings.PLUGINS_ENABLED, |             'plugins_enabled': settings.PLUGINS_ENABLED, | ||||||
|             'active_plugins': plugins_info(), |             'active_plugins': plugins_info(), | ||||||
|  |             'email_configured': is_email_configured(), | ||||||
|  |             'debug_mode': settings.DEBUG, | ||||||
|  |             'docker_mode': settings.DOCKER, | ||||||
|  |             'system_health': check_system_health() if request.user.is_staff else None, | ||||||
|  |             'database': inventreeDatabase()if request.user.is_staff else None | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return JsonResponse(data) |         return JsonResponse(data) | ||||||
|   | |||||||
| @@ -2,11 +2,14 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| # InvenTree API version | # InvenTree API version | ||||||
| INVENTREE_API_VERSION = 142 | INVENTREE_API_VERSION = 143 | ||||||
|  |  | ||||||
| """ | """ | ||||||
| Increment this API version number whenever there is a significant change to the API that any clients need to know about | Increment this API version number whenever there is a significant change to the API that any clients need to know about | ||||||
|  |  | ||||||
|  | v143 -> 2023-10-29: https://github.com/inventree/InvenTree/pull/5810 | ||||||
|  |     - Extends the status endpoint to include information about system status and health | ||||||
|  |  | ||||||
| v142 -> 2023-10-20: https://github.com/inventree/InvenTree/pull/5759 | v142 -> 2023-10-20: https://github.com/inventree/InvenTree/pull/5759 | ||||||
|     - Adds generic API endpoints for looking up status models |     - Adds generic API endpoints for looking up status models | ||||||
|  |  | ||||||
|   | |||||||
| @@ -199,3 +199,9 @@ def inventreeTarget(): | |||||||
| def inventreePlatform(): | def inventreePlatform(): | ||||||
|     """Returns the platform for the instance.""" |     """Returns the platform for the instance.""" | ||||||
|     return platform.platform(aliased=True) |     return platform.platform(aliased=True) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def inventreeDatabase(): | ||||||
|  |     """Return the InvenTree database backend e.g. 'postgresql'.""" | ||||||
|  |     db = settings.DATABASES['default'] | ||||||
|  |     return db.get('ENGINE', None).replace('django.db.backends.', '') | ||||||
|   | |||||||
| @@ -183,13 +183,7 @@ def plugins_info(*args, **kwargs): | |||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
| def inventree_db_engine(*args, **kwargs): | def inventree_db_engine(*args, **kwargs): | ||||||
|     """Return the InvenTree database backend e.g. 'postgresql'.""" |     """Return the InvenTree database backend e.g. 'postgresql'.""" | ||||||
|     db = djangosettings.DATABASES['default'] |     return version.inventreeDatabase() or _('Unknown database') | ||||||
|  |  | ||||||
|     engine = db.get('ENGINE', _('Unknown database')) |  | ||||||
|  |  | ||||||
|     engine = engine.replace('django.db.backends.', '') |  | ||||||
|  |  | ||||||
|     return engine |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @register.simple_tag() | @register.simple_tag() | ||||||
|   | |||||||
| @@ -3,37 +3,81 @@ import { Anchor, Group, SimpleGrid, Text } from '@mantine/core'; | |||||||
| import { DocTooltip } from './DocTooltip'; | import { DocTooltip } from './DocTooltip'; | ||||||
| import { PlaceholderPill } from './Placeholder'; | import { PlaceholderPill } from './Placeholder'; | ||||||
|  |  | ||||||
| export interface DocumentationLinkItem { | interface DocumentationLinkBase { | ||||||
|   id: string; |   id: string; | ||||||
|   title: string | JSX.Element; |   title: string | JSX.Element; | ||||||
|   description: string | JSX.Element; |   description: string | JSX.Element; | ||||||
|   link: string; |  | ||||||
|   placeholder?: boolean; |   placeholder?: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | interface DocumentationLinkItemLink extends DocumentationLinkBase { | ||||||
|  |   link: string; | ||||||
|  |   action?: never; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface DocumentationLinkItemAction extends DocumentationLinkBase { | ||||||
|  |   link?: never; | ||||||
|  |   action: () => void; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export type DocumentationLinkItem = | ||||||
|  |   | DocumentationLinkItemLink | ||||||
|  |   | DocumentationLinkItemAction; | ||||||
|  |  | ||||||
| export function DocumentationLinks({ | export function DocumentationLinks({ | ||||||
|   links |   links | ||||||
| }: { | }: { | ||||||
|   links: DocumentationLinkItem[]; |   links: DocumentationLinkItem[]; | ||||||
| }) { | }) { | ||||||
|  |   const DocumentationLinkRenderer = ({ | ||||||
|  |     link | ||||||
|  |   }: { | ||||||
|  |     link: DocumentationLinkItem; | ||||||
|  |   }) => { | ||||||
|  |     const content = ( | ||||||
|  |       <Text size="sm" fw={500}> | ||||||
|  |         {link.title} | ||||||
|  |       </Text> | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     const Linker = ({ children }: { children: any }) => { | ||||||
|  |       if (link.link) | ||||||
|  |         return ( | ||||||
|  |           <Anchor href={link.link} key={link.id}> | ||||||
|  |             {children} | ||||||
|  |           </Anchor> | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |       if (link.action) | ||||||
|  |         return ( | ||||||
|  |           <Anchor component="button" type="button" onClick={link.action}> | ||||||
|  |             {children} | ||||||
|  |           </Anchor> | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |       console.log('Neither link nor action found for link:', link); | ||||||
|  |       return children; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     return ( | ||||||
|  |       <Linker> | ||||||
|  |         {link.placeholder ? ( | ||||||
|  |           <Group> | ||||||
|  |             {content} | ||||||
|  |             <PlaceholderPill /> | ||||||
|  |           </Group> | ||||||
|  |         ) : ( | ||||||
|  |           content | ||||||
|  |         )} | ||||||
|  |       </Linker> | ||||||
|  |     ); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <SimpleGrid cols={2} spacing={0}> |     <SimpleGrid cols={2} spacing={0}> | ||||||
|       {links.map((link) => ( |       {links.map((link) => ( | ||||||
|         <DocTooltip key={link.id} text={link.description}> |         <DocTooltip key={link.id} text={link.description}> | ||||||
|           <Anchor href={link.link} key={link.id}> |           <DocumentationLinkRenderer link={link} /> | ||||||
|             {link.placeholder ? ( |  | ||||||
|               <Group> |  | ||||||
|                 <Text size="sm" fw={500}> |  | ||||||
|                   {link.title} |  | ||||||
|                 </Text> |  | ||||||
|                 <PlaceholderPill /> |  | ||||||
|               </Group> |  | ||||||
|             ) : ( |  | ||||||
|               <Text size="sm" fw={500}> |  | ||||||
|                 {link.title} |  | ||||||
|               </Text> |  | ||||||
|             )} |  | ||||||
|           </Anchor> |  | ||||||
|         </DocTooltip> |         </DocTooltip> | ||||||
|       ))} |       ))} | ||||||
|     </SimpleGrid> |     </SimpleGrid> | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								src/frontend/src/components/items/OnlyStaff.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/frontend/src/components/items/OnlyStaff.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | import { Trans } from '@lingui/macro'; | ||||||
|  |  | ||||||
|  | import { useUserState } from '../../states/UserState'; | ||||||
|  |  | ||||||
|  | export const OnlyStaff = ({ children }: { children: any }) => { | ||||||
|  |   const [user] = useUserState((state) => [state.user]); | ||||||
|  |  | ||||||
|  |   if (user?.is_staff) return children; | ||||||
|  |   return <Trans>This information is only available for staff users</Trans>; | ||||||
|  | }; | ||||||
							
								
								
									
										141
									
								
								src/frontend/src/components/modals/ServerInfoModal.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/frontend/src/components/modals/ServerInfoModal.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | |||||||
|  | import { Trans } from '@lingui/macro'; | ||||||
|  | import { Badge, Button, Stack, Table, Title } from '@mantine/core'; | ||||||
|  | import { ContextModalProps } from '@mantine/modals'; | ||||||
|  |  | ||||||
|  | import { useServerApiState } from '../../states/ApiState'; | ||||||
|  | import { OnlyStaff } from '../items/OnlyStaff'; | ||||||
|  |  | ||||||
|  | export function ServerInfoModal({ | ||||||
|  |   context, | ||||||
|  |   id | ||||||
|  | }: ContextModalProps<{ modalBody: string }>) { | ||||||
|  |   const [server] = useServerApiState((state) => [state.server]); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <Stack> | ||||||
|  |       <Title order={5}> | ||||||
|  |         <Trans>Server</Trans> | ||||||
|  |       </Title> | ||||||
|  |       <Table> | ||||||
|  |         <tbody> | ||||||
|  |           <tr> | ||||||
|  |             <td> | ||||||
|  |               <Trans>Instance Name</Trans> | ||||||
|  |             </td> | ||||||
|  |             <td>{server.instance}</td> | ||||||
|  |           </tr> | ||||||
|  |           <tr> | ||||||
|  |             <td> | ||||||
|  |               <Trans>Database</Trans> | ||||||
|  |             </td> | ||||||
|  |             <td> | ||||||
|  |               <OnlyStaff>{server.database}</OnlyStaff> | ||||||
|  |             </td> | ||||||
|  |           </tr> | ||||||
|  |           {server.debug_mode && ( | ||||||
|  |             <tr> | ||||||
|  |               <td> | ||||||
|  |                 <Trans>Bebug Mode</Trans> | ||||||
|  |               </td> | ||||||
|  |               <td> | ||||||
|  |                 <Trans>Server is running in debug mode</Trans> | ||||||
|  |               </td> | ||||||
|  |             </tr> | ||||||
|  |           )} | ||||||
|  |           {server.docker_mode && ( | ||||||
|  |             <tr> | ||||||
|  |               <td> | ||||||
|  |                 <Trans>Docker Mode</Trans> | ||||||
|  |               </td> | ||||||
|  |               <td> | ||||||
|  |                 <Trans>Server is deployed using docker</Trans> | ||||||
|  |               </td> | ||||||
|  |             </tr> | ||||||
|  |           )} | ||||||
|  |           <tr> | ||||||
|  |             <td> | ||||||
|  |               <Trans>Plugin Support</Trans> | ||||||
|  |             </td> | ||||||
|  |             <td> | ||||||
|  |               <Badge color={server.plugins_enabled ? 'green' : 'red'}> | ||||||
|  |                 {server.plugins_enabled ? ( | ||||||
|  |                   <Trans>Plugin support enabled</Trans> | ||||||
|  |                 ) : ( | ||||||
|  |                   <Trans>Plugin support disabled</Trans> | ||||||
|  |                 )} | ||||||
|  |               </Badge> | ||||||
|  |             </td> | ||||||
|  |           </tr> | ||||||
|  |           <tr> | ||||||
|  |             <td> | ||||||
|  |               <Trans>Server status</Trans> | ||||||
|  |             </td> | ||||||
|  |             <td> | ||||||
|  |               <OnlyStaff> | ||||||
|  |                 <Badge color={server.system_health ? 'green' : 'yellow'}> | ||||||
|  |                   {server.system_health ? ( | ||||||
|  |                     <Trans>Healthy</Trans> | ||||||
|  |                   ) : ( | ||||||
|  |                     <Trans>Issues detected</Trans> | ||||||
|  |                   )} | ||||||
|  |                 </Badge> | ||||||
|  |               </OnlyStaff> | ||||||
|  |             </td> | ||||||
|  |           </tr> | ||||||
|  |           {server.worker_running != true && ( | ||||||
|  |             <tr> | ||||||
|  |               <td> | ||||||
|  |                 <Trans>Background Worker</Trans> | ||||||
|  |               </td> | ||||||
|  |               <td> | ||||||
|  |                 <Badge color="red"> | ||||||
|  |                   <Trans>Background worker not running</Trans> | ||||||
|  |                 </Badge> | ||||||
|  |               </td> | ||||||
|  |             </tr> | ||||||
|  |           )} | ||||||
|  |           {server.email_configured != true && ( | ||||||
|  |             <tr> | ||||||
|  |               <td> | ||||||
|  |                 <Trans>Email Settings</Trans> | ||||||
|  |               </td> | ||||||
|  |               <td> | ||||||
|  |                 <Badge color="red"> | ||||||
|  |                   <Trans>Email settings not configured</Trans> | ||||||
|  |                 </Badge> | ||||||
|  |               </td> | ||||||
|  |             </tr> | ||||||
|  |           )} | ||||||
|  |         </tbody> | ||||||
|  |       </Table> | ||||||
|  |       <Title order={5}> | ||||||
|  |         <Trans>Version</Trans> | ||||||
|  |       </Title> | ||||||
|  |       <Table> | ||||||
|  |         <tbody> | ||||||
|  |           <tr> | ||||||
|  |             <td> | ||||||
|  |               <Trans>Server Version</Trans> | ||||||
|  |             </td> | ||||||
|  |             <td>{server.version}</td> | ||||||
|  |           </tr> | ||||||
|  |           <tr> | ||||||
|  |             <td> | ||||||
|  |               <Trans>API Version</Trans> | ||||||
|  |             </td> | ||||||
|  |             <td>{server.apiVersion}</td> | ||||||
|  |           </tr> | ||||||
|  |         </tbody> | ||||||
|  |       </Table> | ||||||
|  |       <Button | ||||||
|  |         color="red" | ||||||
|  |         variant="outline" | ||||||
|  |         onClick={() => { | ||||||
|  |           context.closeModal(id); | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         <Trans>Close modal</Trans> | ||||||
|  |       </Button> | ||||||
|  |     </Stack> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @@ -10,6 +10,7 @@ import { ModalsProvider } from '@mantine/modals'; | |||||||
| import { Notifications } from '@mantine/notifications'; | import { Notifications } from '@mantine/notifications'; | ||||||
|  |  | ||||||
| import { QrCodeModal } from '../components/modals/QrCodeModal'; | import { QrCodeModal } from '../components/modals/QrCodeModal'; | ||||||
|  | import { ServerInfoModal } from '../components/modals/ServerInfoModal'; | ||||||
| import { useLocalState } from '../states/LocalState'; | import { useLocalState } from '../states/LocalState'; | ||||||
|  |  | ||||||
| export function ThemeContext({ children }: { children: JSX.Element }) { | export function ThemeContext({ children }: { children: JSX.Element }) { | ||||||
| @@ -60,7 +61,7 @@ export function ThemeContext({ children }: { children: JSX.Element }) { | |||||||
|         <Notifications /> |         <Notifications /> | ||||||
|         <ModalsProvider |         <ModalsProvider | ||||||
|           labels={{ confirm: t`Submit`, cancel: t`Cancel` }} |           labels={{ confirm: t`Submit`, cancel: t`Cancel` }} | ||||||
|           modals={{ qr: QrCodeModal }} |           modals={{ qr: QrCodeModal, info: ServerInfoModal }} | ||||||
|         > |         > | ||||||
|           {children} |           {children} | ||||||
|         </ModalsProvider> |         </ModalsProvider> | ||||||
|   | |||||||
| @@ -8,7 +8,12 @@ export const emptyServerAPI = { | |||||||
|   worker_running: null, |   worker_running: null, | ||||||
|   worker_pending_tasks: null, |   worker_pending_tasks: null, | ||||||
|   plugins_enabled: null, |   plugins_enabled: null, | ||||||
|   active_plugins: [] |   active_plugins: [], | ||||||
|  |   email_configured: null, | ||||||
|  |   debug_mode: null, | ||||||
|  |   docker_mode: null, | ||||||
|  |   database: null, | ||||||
|  |   system_health: null | ||||||
| }; | }; | ||||||
|  |  | ||||||
| export interface SiteMarkProps { | export interface SiteMarkProps { | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { Trans } from '@lingui/macro'; | import { Trans } from '@lingui/macro'; | ||||||
|  | import { openContextModal } from '@mantine/modals'; | ||||||
|  |  | ||||||
| import { DocumentationLinkItem } from '../components/items/DocumentationLinks'; | import { DocumentationLinkItem } from '../components/items/DocumentationLinks'; | ||||||
| import { IS_DEV_OR_DEMO } from '../main'; | import { IS_DEV_OR_DEMO } from '../main'; | ||||||
| @@ -69,18 +70,26 @@ export const navDocLinks: DocumentationLinkItem[] = [ | |||||||
|   } |   } | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|  | function serverInfo() { | ||||||
|  |   return openContextModal({ | ||||||
|  |     modal: 'info', | ||||||
|  |     title: <Trans>System Information</Trans>, | ||||||
|  |     size: 'xl', | ||||||
|  |     innerProps: {} | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  |  | ||||||
| // TODO @matmair: Add the following pages and adjust the links | // TODO @matmair: Add the following pages and adjust the links | ||||||
| export const aboutLinks: DocumentationLinkItem[] = [ | export const aboutLinks: DocumentationLinkItem[] = [ | ||||||
|   { |   { | ||||||
|     id: 'instance', |     id: 'instance', | ||||||
|     title: <Trans>Instance</Trans>, |     title: <Trans>System Information</Trans>, | ||||||
|     description: <Trans>About this Inventree instance</Trans>, |     description: <Trans>About this Inventree instance</Trans>, | ||||||
|     link: '/instance', |     action: serverInfo | ||||||
|     placeholder: true |  | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     id: 'about', |     id: 'about', | ||||||
|     title: <Trans>InvenTree</Trans>, |     title: <Trans>About InvenTree</Trans>, | ||||||
|     description: <Trans>About the InvenTree org</Trans>, |     description: <Trans>About the InvenTree org</Trans>, | ||||||
|     link: '/about', |     link: '/about', | ||||||
|     placeholder: true |     placeholder: true | ||||||
|   | |||||||
| @@ -28,6 +28,11 @@ export interface ServerAPIProps { | |||||||
|   worker_pending_tasks: null | number; |   worker_pending_tasks: null | number; | ||||||
|   plugins_enabled: null | boolean; |   plugins_enabled: null | boolean; | ||||||
|   active_plugins: PluginProps[]; |   active_plugins: PluginProps[]; | ||||||
|  |   email_configured: null | boolean; | ||||||
|  |   debug_mode: null | boolean; | ||||||
|  |   docker_mode: null | boolean; | ||||||
|  |   database: null | string; | ||||||
|  |   system_health: null | boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
| // Type interface defining a single 'setting' object | // Type interface defining a single 'setting' object | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user