mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +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,9 +65,6 @@ 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
|
|
||||||
condition={Boolean(row.offset)}
|
|
||||||
show={
|
|
||||||
<Box
|
<Box
|
||||||
component="span"
|
component="span"
|
||||||
sx={theme => ({
|
sx={theme => ({
|
||||||
@ -93,9 +75,6 @@ export const InstanceStats: VFC = () => {
|
|||||||
>
|
>
|
||||||
{row.title}
|
{row.title}
|
||||||
</Box>
|
</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