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 NotFound from 'component/common/NotFound/NotFound'; | ||||||
| import { AdminIndex } from './AdminIndex'; | import { AdminIndex } from './AdminIndex'; | ||||||
| import { AdminTabsMenu } from './menu/AdminTabsMenu'; | import { AdminTabsMenu } from './menu/AdminTabsMenu'; | ||||||
| import { InstanceHealth } from './instance-health/InstanceHealth'; |  | ||||||
| 
 | 
 | ||||||
| export const Admin = () => { | export const Admin = () => { | ||||||
|     return ( |     return ( | ||||||
| @ -35,7 +34,6 @@ export const Admin = () => { | |||||||
|                 <Route path="groups/*" element={<GroupsAdmin />} /> |                 <Route path="groups/*" element={<GroupsAdmin />} /> | ||||||
|                 <Route path="roles/*" element={<Roles />} /> |                 <Route path="roles/*" element={<Roles />} /> | ||||||
|                 <Route path="instance" element={<InstanceAdmin />} /> |                 <Route path="instance" element={<InstanceAdmin />} /> | ||||||
|                 <Route path="instance-health" element={<InstanceHealth />} /> |  | ||||||
|                 <Route path="network/*" element={<Network />} /> |                 <Route path="network/*" element={<Network />} /> | ||||||
|                 <Route path="maintenance" element={<MaintenanceAdmin />} /> |                 <Route path="maintenance" element={<MaintenanceAdmin />} /> | ||||||
|                 <Route path="cors" element={<CorsAdmin />} /> |                 <Route path="cors" element={<CorsAdmin />} /> | ||||||
|  | |||||||
| @ -80,13 +80,6 @@ export const adminRoutes: INavigationMenuItem[] = [ | |||||||
|         menu: { adminSettings: true }, |         menu: { adminSettings: true }, | ||||||
|         group: 'instance', |         group: 'instance', | ||||||
|     }, |     }, | ||||||
|     { |  | ||||||
|         path: '/admin/instance-health', |  | ||||||
|         title: 'Instance health', |  | ||||||
|         menu: { adminSettings: true }, |  | ||||||
|         group: 'instance', |  | ||||||
|         flag: 'instanceHealthDashboard', |  | ||||||
|     }, |  | ||||||
|     { |     { | ||||||
|         path: '/admin/instance-privacy', |         path: '/admin/instance-privacy', | ||||||
|         title: 'Instance privacy', |         title: 'Instance privacy', | ||||||
|  | |||||||
| @ -30,24 +30,9 @@ export const InstanceStats: VFC = () => { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const rows = [ |     const rows = [ | ||||||
|         { title: 'Instance Id', value: stats?.instanceId }, |         { title: 'Instance Id', value: stats?.instanceId, offset: false }, | ||||||
|         { title: versionTitle, value: version }, |         { title: versionTitle, value: version }, | ||||||
|         { title: 'Users', value: stats?.users }, |         { 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: 'Feature toggles', value: stats?.featureToggles }, | ||||||
|         { title: 'Projects', value: stats?.projects }, |         { title: 'Projects', value: stats?.projects }, | ||||||
|         { title: 'Environments', value: stats?.environments }, |         { title: 'Environments', value: stats?.environments }, | ||||||
| @ -80,22 +65,16 @@ export const InstanceStats: VFC = () => { | |||||||
|                         {rows.map(row => ( |                         {rows.map(row => ( | ||||||
|                             <TableRow key={row.title}> |                             <TableRow key={row.title}> | ||||||
|                                 <TableCell component="th" scope="row"> |                                 <TableCell component="th" scope="row"> | ||||||
|                                     <ConditionallyRender |                                     <Box | ||||||
|                                         condition={Boolean(row.offset)} |                                         component="span" | ||||||
|                                         show={ |                                         sx={theme => ({ | ||||||
|                                             <Box |                                             marginLeft: row.offset | ||||||
|                                                 component="span" |                                                 ? theme.spacing(2) | ||||||
|                                                 sx={theme => ({ |                                                 : 0, | ||||||
|                                                     marginLeft: row.offset |                                         })} | ||||||
|                                                         ? theme.spacing(2) |                                     > | ||||||
|                                                         : 0, |                                         {row.title} | ||||||
|                                                 })} |                                     </Box> | ||||||
|                                             > |  | ||||||
|                                                 {row.title} |  | ||||||
|                                             </Box> |  | ||||||
|                                         } |  | ||||||
|                                         elseShow={row.title} |  | ||||||
|                                     /> |  | ||||||
|                                 </TableCell> |                                 </TableCell> | ||||||
|                                 <TableCell align="right">{row.value}</TableCell> |                                 <TableCell align="right">{row.value}</TableCell> | ||||||
|                             </TableRow> |                             </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; |     integrationsRework?: boolean; | ||||||
|     multipleRoles?: boolean; |     multipleRoles?: boolean; | ||||||
|     doraMetrics?: boolean; |     doraMetrics?: boolean; | ||||||
|     instanceHealthDashboard?: boolean; |  | ||||||
|     [key: string]: boolean | Variant | undefined; |     [key: string]: boolean | Variant | undefined; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -28,8 +28,7 @@ export type IFlagKey = | |||||||
|     | 'newApplicationList' |     | 'newApplicationList' | ||||||
|     | 'integrationsRework' |     | 'integrationsRework' | ||||||
|     | 'multipleRoles' |     | 'multipleRoles' | ||||||
|     | 'doraMetrics' |     | 'doraMetrics'; | ||||||
|     | 'instanceHealthDashboard'; |  | ||||||
| 
 | 
 | ||||||
| export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; | export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -42,7 +42,6 @@ process.nextTick(async () => { | |||||||
|                         lastSeenByEnvironment: true, |                         lastSeenByEnvironment: true, | ||||||
|                         newApplicationList: true, |                         newApplicationList: true, | ||||||
|                         doraMetrics: true, |                         doraMetrics: true, | ||||||
|                         instanceHealthDashboard: true, |  | ||||||
|                     }, |                     }, | ||||||
|                 }, |                 }, | ||||||
|                 authentication: { |                 authentication: { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user