mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	refactor: instance health cleanup (#4602)
## About the changes Cleanup after hackathon. Discovered project on: https://linear.app/unleash/project/active-users-and-instance-dashboard-67352abadf49
This commit is contained in:
		
							parent
							
								
									5ae86ef196
								
							
						
					
					
						commit
						8aace7f93f
					
				| @ -18,7 +18,6 @@ import UsersAdmin from './users/UsersAdmin'; | ||||
| import NotFound from 'component/common/NotFound/NotFound'; | ||||
| import { AdminIndex } from './AdminIndex'; | ||||
| import { AdminTabsMenu } from './menu/AdminTabsMenu'; | ||||
| import { InstanceHealth } from './instance-health/InstanceHealth'; | ||||
| 
 | ||||
| export const Admin = () => { | ||||
|     return ( | ||||
| @ -35,7 +34,6 @@ export const Admin = () => { | ||||
|                 <Route path="groups/*" element={<GroupsAdmin />} /> | ||||
|                 <Route path="roles/*" element={<Roles />} /> | ||||
|                 <Route path="instance" element={<InstanceAdmin />} /> | ||||
|                 <Route path="instance-health" element={<InstanceHealth />} /> | ||||
|                 <Route path="network/*" element={<Network />} /> | ||||
|                 <Route path="maintenance" element={<MaintenanceAdmin />} /> | ||||
|                 <Route path="cors" element={<CorsAdmin />} /> | ||||
|  | ||||
| @ -80,13 +80,6 @@ export const adminRoutes: INavigationMenuItem[] = [ | ||||
|         menu: { adminSettings: true }, | ||||
|         group: 'instance', | ||||
|     }, | ||||
|     { | ||||
|         path: '/admin/instance-health', | ||||
|         title: 'Instance health', | ||||
|         menu: { adminSettings: true }, | ||||
|         group: 'instance', | ||||
|         flag: 'instanceHealthDashboard', | ||||
|     }, | ||||
|     { | ||||
|         path: '/admin/instance-privacy', | ||||
|         title: 'Instance privacy', | ||||
|  | ||||
| @ -30,24 +30,9 @@ export const InstanceStats: VFC = () => { | ||||
|     } | ||||
| 
 | ||||
|     const rows = [ | ||||
|         { title: 'Instance Id', value: stats?.instanceId }, | ||||
|         { title: 'Instance Id', value: stats?.instanceId, offset: false }, | ||||
|         { title: versionTitle, value: version }, | ||||
|         { title: 'Users', value: stats?.users }, | ||||
|         { | ||||
|             title: 'Active past 7 days', | ||||
|             value: stats?.activeUsers?.last7, | ||||
|             offset: true, | ||||
|         }, | ||||
|         { | ||||
|             title: 'Active past 30 days', | ||||
|             value: stats?.activeUsers?.last30, | ||||
|             offset: true, | ||||
|         }, | ||||
|         { | ||||
|             title: 'Active past 90 days', | ||||
|             value: stats?.activeUsers?.last90, | ||||
|             offset: true, | ||||
|         }, | ||||
|         { title: 'Feature toggles', value: stats?.featureToggles }, | ||||
|         { title: 'Projects', value: stats?.projects }, | ||||
|         { title: 'Environments', value: stats?.environments }, | ||||
| @ -80,22 +65,16 @@ export const InstanceStats: VFC = () => { | ||||
|                         {rows.map(row => ( | ||||
|                             <TableRow key={row.title}> | ||||
|                                 <TableCell component="th" scope="row"> | ||||
|                                     <ConditionallyRender | ||||
|                                         condition={Boolean(row.offset)} | ||||
|                                         show={ | ||||
|                                             <Box | ||||
|                                                 component="span" | ||||
|                                                 sx={theme => ({ | ||||
|                                                     marginLeft: row.offset | ||||
|                                                         ? theme.spacing(2) | ||||
|                                                         : 0, | ||||
|                                                 })} | ||||
|                                             > | ||||
|                                                 {row.title} | ||||
|                                             </Box> | ||||
|                                         } | ||||
|                                         elseShow={row.title} | ||||
|                                     /> | ||||
|                                     <Box | ||||
|                                         component="span" | ||||
|                                         sx={theme => ({ | ||||
|                                             marginLeft: row.offset | ||||
|                                                 ? theme.spacing(2) | ||||
|                                                 : 0, | ||||
|                                         })} | ||||
|                                     > | ||||
|                                         {row.title} | ||||
|                                     </Box> | ||||
|                                 </TableCell> | ||||
|                                 <TableCell align="right">{row.value}</TableCell> | ||||
|                             </TableRow> | ||||
|  | ||||
| @ -1,220 +0,0 @@ | ||||
| import { VFC, useMemo } from 'react'; | ||||
| import { useSortBy, useTable } from 'react-table'; | ||||
| import { styled, Typography, Box } from '@mui/material'; | ||||
| import { PageContent } from 'component/common/PageContent/PageContent'; | ||||
| import { PageHeader } from 'component/common/PageHeader/PageHeader'; | ||||
| import { useInstanceStats } from 'hooks/api/getters/useInstanceStats/useInstanceStats'; | ||||
| import useProjects from 'hooks/api/getters/useProjects/useProjects'; | ||||
| import { sortTypes } from 'utils/sortTypes'; | ||||
| import { | ||||
|     SortableTableHeader, | ||||
|     Table, | ||||
|     TableBody, | ||||
|     TableRow, | ||||
|     TableCell, | ||||
| } from 'component/common/Table'; | ||||
| import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; | ||||
| import { HelpPopper } from 'component/project/Project/ProjectStats/HelpPopper'; | ||||
| import { StatusBox } from 'component/project/Project/ProjectStats/StatusBox'; | ||||
| import { DateCell } from 'component/common/Table/cells/DateCell/DateCell'; | ||||
| 
 | ||||
| interface IInstanceHealthProps {} | ||||
| 
 | ||||
| const CardsGrid = styled('div')(({ theme }) => ({ | ||||
|     display: 'grid', | ||||
|     gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', | ||||
|     gap: theme.spacing(2), | ||||
|     paddingBottom: theme.spacing(2), | ||||
| })); | ||||
| 
 | ||||
| const Card = styled('div')(({ theme }) => ({ | ||||
|     padding: theme.spacing(2.5), | ||||
|     borderRadius: `${theme.shape.borderRadiusLarge}px`, | ||||
|     backgroundColor: `${theme.palette.background.paper}`, | ||||
|     border: `1px solid ${theme.palette.divider}`, | ||||
|     // boxShadow: theme.boxShadows.card,
 | ||||
|     display: 'flex', | ||||
|     flexDirection: 'column', | ||||
| })); | ||||
| 
 | ||||
| /** | ||||
|  * @deprecated unify with project widget | ||||
|  */ | ||||
| const StyledWidget = styled(Box)(({ theme }) => ({ | ||||
|     position: 'relative', | ||||
|     padding: theme.spacing(3), | ||||
|     backgroundColor: theme.palette.background.paper, | ||||
|     flex: 1, | ||||
|     display: 'flex', | ||||
|     flexDirection: 'column', | ||||
|     alignItems: 'center', | ||||
|     justifyContent: 'center', | ||||
|     borderRadius: `${theme.shape.borderRadiusLarge}px`, | ||||
|     [theme.breakpoints.down('lg')]: { | ||||
|         padding: theme.spacing(2), | ||||
|     }, | ||||
| })); | ||||
| 
 | ||||
| export const InstanceHealth: VFC<IInstanceHealthProps> = () => { | ||||
|     const { stats } = useInstanceStats(); | ||||
|     const { projects } = useProjects(); | ||||
|     // FIXME: loading state
 | ||||
| 
 | ||||
|     const initialState = useMemo( | ||||
|         () => ({ | ||||
|             hiddenColumns: [], | ||||
|             sortBy: [{ id: 'createdAt' }], | ||||
|         }), | ||||
|         [] | ||||
|     ); | ||||
| 
 | ||||
|     const data = useMemo(() => projects, [projects]); | ||||
| 
 | ||||
|     const dormantUsersPercentage = | ||||
|         (1 - stats?.activeUsers?.last90! / stats?.users!) * 100; | ||||
| 
 | ||||
|     const dormantUsersColor = | ||||
|         dormantUsersPercentage < 30 | ||||
|             ? 'success.main' | ||||
|             : dormantUsersPercentage < 50 | ||||
|             ? 'warning.main' | ||||
|             : 'error.main'; | ||||
| 
 | ||||
|     const COLUMNS = useMemo( | ||||
|         () => [ | ||||
|             { | ||||
|                 accessor: 'name', | ||||
|                 Header: 'Project name', | ||||
|                 Cell: TextCell, | ||||
|                 width: '80%', | ||||
|             }, | ||||
|             { | ||||
|                 Header: 'Feature toggles', | ||||
|                 accessor: 'featureCount', | ||||
|                 Cell: TextCell, | ||||
|             }, | ||||
|             { | ||||
|                 Header: 'Created at', | ||||
|                 accessor: 'createdAt', | ||||
|                 Cell: DateCell, | ||||
|             }, | ||||
|             { | ||||
|                 Header: 'Members', | ||||
|                 accessor: 'memberCount', | ||||
|                 Cell: TextCell, | ||||
|             }, | ||||
|             { | ||||
|                 Header: 'Health', | ||||
|                 accessor: 'health', | ||||
|                 Cell: ({ value }: { value: number }) => { | ||||
|                     const healthRatingColor = | ||||
|                         value < 50 | ||||
|                             ? 'error.main' | ||||
|                             : value < 75 | ||||
|                             ? 'warning.main' | ||||
|                             : 'success.main'; | ||||
|                     return ( | ||||
|                         <TextCell> | ||||
|                             <Typography | ||||
|                                 component="span" | ||||
|                                 sx={{ color: healthRatingColor }} | ||||
|                             > | ||||
|                                 {value}% | ||||
|                             </Typography> | ||||
|                         </TextCell> | ||||
|                     ); | ||||
|                 }, | ||||
|             }, | ||||
|         ], | ||||
|         [] | ||||
|     ); | ||||
| 
 | ||||
|     const { headerGroups, rows, prepareRow, getTableProps, getTableBodyProps } = | ||||
|         useTable( | ||||
|             { | ||||
|                 columns: COLUMNS as any, | ||||
|                 data: data as any, | ||||
|                 initialState, | ||||
|                 sortTypes, | ||||
|                 autoResetGlobalFilter: false, | ||||
|                 autoResetHiddenColumns: false, | ||||
|                 autoResetSortBy: false, | ||||
|                 disableSortRemove: true, | ||||
|             }, | ||||
|             useSortBy | ||||
|         ); | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|             <CardsGrid> | ||||
|                 <Card> | ||||
|                     <StatusBox | ||||
|                         title="User accounts" | ||||
|                         boxText={String(stats?.users)} | ||||
|                         customChangeElement={<></>} | ||||
|                     > | ||||
|                         {/* <HelpPopper id="user-accounts"> | ||||
|                             Sum of all configuration and state modifications in | ||||
|                             the project. | ||||
|                         </HelpPopper> */} | ||||
|                         {/* FIXME: tooltip */} | ||||
|                     </StatusBox> | ||||
|                 </Card> | ||||
|                 <Card> | ||||
|                     <StatusBox | ||||
|                         title="Dormant users" | ||||
|                         boxText={String( | ||||
|                             stats?.users! - stats?.activeUsers?.last90! | ||||
|                         )} | ||||
|                         customChangeElement={ | ||||
|                             <Typography | ||||
|                                 component="span" | ||||
|                                 sx={{ color: dormantUsersColor }} | ||||
|                             > | ||||
|                                 ({dormantUsersPercentage.toFixed(1)}%) | ||||
|                             </Typography> | ||||
|                         } | ||||
|                     > | ||||
|                         {/* <HelpPopper id="dormant-users"> | ||||
|                             Sum of all configuration and state modifications in | ||||
|                             the project. | ||||
|                         </HelpPopper> */} | ||||
|                     </StatusBox> | ||||
|                 </Card> | ||||
|                 <Card> | ||||
|                     <StatusBox | ||||
|                         title="Number of projects" | ||||
|                         boxText={String(projects?.length)} | ||||
|                         customChangeElement={<></>} | ||||
|                     ></StatusBox> | ||||
|                 </Card> | ||||
|                 <Card> | ||||
|                     <StatusBox | ||||
|                         title="Number of feature toggles" | ||||
|                         boxText={String(stats?.featureToggles)} | ||||
|                         customChangeElement={<></>} | ||||
|                     ></StatusBox> | ||||
|                 </Card> | ||||
|             </CardsGrid> | ||||
|             <PageContent header={<PageHeader title="Health per project" />}> | ||||
|                 <Table {...getTableProps()} rowHeight="standard"> | ||||
|                     <SortableTableHeader headerGroups={headerGroups} /> | ||||
|                     <TableBody {...getTableBodyProps()}> | ||||
|                         {rows.map(row => { | ||||
|                             prepareRow(row); | ||||
|                             return ( | ||||
|                                 <TableRow hover {...row.getRowProps()}> | ||||
|                                     {row.cells.map(cell => ( | ||||
|                                         <TableCell {...cell.getCellProps()}> | ||||
|                                             {cell.render('Cell')} | ||||
|                                         </TableCell> | ||||
|                                     ))} | ||||
|                                 </TableRow> | ||||
|                             ); | ||||
|                         })} | ||||
|                     </TableBody> | ||||
|                 </Table> | ||||
|             </PageContent> | ||||
|         </> | ||||
|     ); | ||||
| }; | ||||
| @ -57,7 +57,6 @@ export interface IFlags { | ||||
|     integrationsRework?: boolean; | ||||
|     multipleRoles?: boolean; | ||||
|     doraMetrics?: boolean; | ||||
|     instanceHealthDashboard?: boolean; | ||||
|     [key: string]: boolean | Variant | undefined; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -28,8 +28,7 @@ export type IFlagKey = | ||||
|     | 'newApplicationList' | ||||
|     | 'integrationsRework' | ||||
|     | 'multipleRoles' | ||||
|     | 'doraMetrics' | ||||
|     | 'instanceHealthDashboard'; | ||||
|     | 'doraMetrics'; | ||||
| 
 | ||||
| export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; | ||||
| 
 | ||||
|  | ||||
| @ -42,7 +42,6 @@ process.nextTick(async () => { | ||||
|                         lastSeenByEnvironment: true, | ||||
|                         newApplicationList: true, | ||||
|                         doraMetrics: true, | ||||
|                         instanceHealthDashboard: true, | ||||
|                     }, | ||||
|                 }, | ||||
|                 authentication: { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user