mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
feat: update health report (#541)
* fix: align styling with other pages * feat: add last updat to heath report * add feature status to last seen in health overview * fix: add mobile style * fix:add background color on table hover Co-authored-by: Fredrik Strand Oseberg <fredrik.no@gmail.com>
This commit is contained in:
parent
bb38df5293
commit
31d88da08b
@ -1,4 +1,3 @@
|
|||||||
import React from 'react';
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Paper } from '@material-ui/core';
|
import { Paper } from '@material-ui/core';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
@ -8,12 +7,14 @@ import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined'
|
|||||||
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
|
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
|
||||||
|
|
||||||
import styles from './ReportCard.module.scss';
|
import styles from './ReportCard.module.scss';
|
||||||
|
import ReactTimeAgo from 'react-timeago';
|
||||||
|
|
||||||
const ReportCard = ({
|
const ReportCard = ({
|
||||||
health,
|
health,
|
||||||
activeCount,
|
activeCount,
|
||||||
staleCount,
|
staleCount,
|
||||||
potentiallyStaleCount,
|
potentiallyStaleCount,
|
||||||
|
lastUpdate,
|
||||||
}) => {
|
}) => {
|
||||||
const healthLessThan50 = health < 50;
|
const healthLessThan50 = health < 50;
|
||||||
const healthLessThan75 = health < 75;
|
const healthLessThan75 = health < 75;
|
||||||
@ -52,11 +53,22 @@ const ReportCard = ({
|
|||||||
<div className={styles.reportCardHealthInnerContainer}>
|
<div className={styles.reportCardHealthInnerContainer}>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={health > -1}
|
condition={health > -1}
|
||||||
show={<p className={healthClasses}>{health}%</p>}
|
show={
|
||||||
|
<div>
|
||||||
|
<p className={healthClasses}>{health}%</p>
|
||||||
|
<p className={styles.lastUpdate}>
|
||||||
|
Last updated:{' '}
|
||||||
|
<ReactTimeAgo
|
||||||
|
date={lastUpdate}
|
||||||
|
live={false}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.reportCardListContainer}>
|
<div className={styles.reportCardToggle}>
|
||||||
<h2 className={styles.header}>Toggle report</h2>
|
<h2 className={styles.header}>Toggle report</h2>
|
||||||
<ul className={styles.reportCardList}>
|
<ul className={styles.reportCardList}>
|
||||||
<li>
|
<li>
|
||||||
@ -65,28 +77,49 @@ const ReportCard = ({
|
|||||||
show={renderActiveToggles}
|
show={renderActiveToggles}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={activeCount}
|
||||||
|
show={
|
||||||
|
<p className={styles.reportCardActionText}>
|
||||||
|
Also includes potentially stale toggles.
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={staleCount}
|
condition={staleCount}
|
||||||
show={renderStaleToggles}
|
show={renderStaleToggles}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={potentiallyStaleCount}
|
|
||||||
show={renderPotentiallyStaleToggles}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.reportCardAction}>
|
<div className={styles.reportCardAction}>
|
||||||
<h2 className={styles.header}>Potential actions</h2>
|
<h2 className={styles.header}>Potential actions</h2>
|
||||||
<div className={styles.reportCardActionContainer}>
|
<div className={styles.reportCardActionContainer}>
|
||||||
<p className={styles.reportCardActionText}>
|
<ul className={styles.reportCardList}>
|
||||||
Review your feature toggles and delete unused
|
<li>
|
||||||
toggles.
|
<ConditionallyRender
|
||||||
</p>
|
condition={potentiallyStaleCount}
|
||||||
|
show={renderPotentiallyStaleToggles}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={potentiallyStaleCount}
|
||||||
|
show={
|
||||||
|
<p className={styles.reportCardActionText}>
|
||||||
|
Review your feature toggles and delete
|
||||||
|
unused toggles.
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
elseShow={
|
||||||
|
<p className={styles.reportCardNoActionText}>
|
||||||
|
No action is required
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: var(--card-padding);
|
padding: var(--card-padding);
|
||||||
margin: var(--card-margin-y) 0;
|
margin: var(--card-margin-y) 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@ -14,9 +16,19 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
.reportCardHealth {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.reportCardAction {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.reportCardToggle {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.reportCardHealthInnerContainer {
|
.reportCardHealthInnerContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -70,6 +82,12 @@
|
|||||||
.reportCardActionText {
|
.reportCardActionText {
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
font-size: var(--p-size);
|
font-size: var(--p-size);
|
||||||
|
margin-left: 35px;
|
||||||
|
}
|
||||||
|
.reportCardNoActionText {
|
||||||
|
max-width: 300px;
|
||||||
|
font-size: var(--p-size);
|
||||||
|
margin-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reportCardBtn {
|
.reportCardBtn {
|
||||||
@ -83,3 +101,16 @@
|
|||||||
.healthWarning {
|
.healthWarning {
|
||||||
color: var(--warning);
|
color: var(--warning);
|
||||||
}
|
}
|
||||||
|
.lastUpdate {
|
||||||
|
color: #585858;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.reportCardContainer {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.reportCardToggle {
|
||||||
|
margin: 10px 5px;
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
.reportToggleList {
|
.reportToggleList {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: var(--card-margin-y) 0;
|
margin: var(--card-margin-y) 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bulkAction {
|
.bulkAction {
|
||||||
@ -65,7 +67,31 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tableRow:hover {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 800px) {
|
||||||
|
.hideColumn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width: 550px) {
|
||||||
|
.hideColumnStatus {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 425px) {
|
||||||
|
.hideColumnLastSeen {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,11 +5,23 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender';
|
import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender';
|
||||||
|
|
||||||
import { NAME, LAST_SEEN, CREATED, EXPIRED, STATUS, REPORT } from '../../constants';
|
import {
|
||||||
|
NAME,
|
||||||
|
LAST_SEEN,
|
||||||
|
CREATED,
|
||||||
|
EXPIRED,
|
||||||
|
STATUS,
|
||||||
|
REPORT,
|
||||||
|
} from '../../constants';
|
||||||
|
|
||||||
import styles from '../ReportToggleList.module.scss';
|
import styles from '../ReportToggleList.module.scss';
|
||||||
|
|
||||||
const ReportToggleListHeader = ({ handleCheckAll, checkAll, setSortData, bulkActionsOn }) => {
|
const ReportToggleListHeader = ({
|
||||||
|
handleCheckAll,
|
||||||
|
checkAll,
|
||||||
|
setSortData,
|
||||||
|
bulkActionsOn,
|
||||||
|
}) => {
|
||||||
const handleSort = type => {
|
const handleSort = type => {
|
||||||
setSortData(prev => ({
|
setSortData(prev => ({
|
||||||
sortKey: type,
|
sortKey: type,
|
||||||
@ -34,27 +46,56 @@ const ReportToggleListHeader = ({ handleCheckAll, checkAll, setSortData, bulkAct
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<th role="button" tabIndex={0} style={{ width: '150px' }} onClick={() => handleSort(NAME)}>
|
<th
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
style={{ width: '150px' }}
|
||||||
|
onClick={() => handleSort(NAME)}
|
||||||
|
>
|
||||||
Name
|
Name
|
||||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||||
</th>
|
</th>
|
||||||
<th role="button" tabIndex={0} onClick={() => handleSort(LAST_SEEN)}>
|
<th
|
||||||
|
role="button"
|
||||||
|
className={styles.hideColumnLastSeen}
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => handleSort(LAST_SEEN)}
|
||||||
|
>
|
||||||
Last seen
|
Last seen
|
||||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||||
</th>
|
</th>
|
||||||
<th role="button" tabIndex={0} onClick={() => handleSort(CREATED)}>
|
<th
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={styles.hideColumn}
|
||||||
|
onClick={() => handleSort(CREATED)}
|
||||||
|
>
|
||||||
Created
|
Created
|
||||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||||
</th>
|
</th>
|
||||||
<th role="button" tabIndex={0} onClick={() => handleSort(EXPIRED)}>
|
<th
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={styles.hideColumn}
|
||||||
|
onClick={() => handleSort(EXPIRED)}
|
||||||
|
>
|
||||||
Expired
|
Expired
|
||||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||||
</th>
|
</th>
|
||||||
<th role="button" tabIndex={0} onClick={() => handleSort(STATUS)}>
|
<th
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={styles.hideColumnStatus}
|
||||||
|
onClick={() => handleSort(STATUS)}
|
||||||
|
>
|
||||||
Status
|
Status
|
||||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||||
</th>
|
</th>
|
||||||
<th role="button" tabIndex={0} onClick={() => handleSort(REPORT)}>
|
<th
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => handleSort(REPORT)}
|
||||||
|
>
|
||||||
Report
|
Report
|
||||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||||
</th>
|
</th>
|
||||||
|
@ -7,6 +7,7 @@ import { Checkbox } from '@material-ui/core';
|
|||||||
import CheckIcon from '@material-ui/icons/Check';
|
import CheckIcon from '@material-ui/icons/Check';
|
||||||
import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined';
|
import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined';
|
||||||
import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender';
|
import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import FeatureStatus from '../../../feature/FeatureView2/FeatureStatus/FeatureStatus';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
pluralize,
|
pluralize,
|
||||||
@ -75,20 +76,13 @@ const ReportToggleListItem = ({
|
|||||||
|
|
||||||
return pluralize(result, 'day');
|
return pluralize(result, 'day');
|
||||||
}
|
}
|
||||||
|
return 'N/A';
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatLastSeenAt = () => {
|
const formatLastSeenAt = () => {
|
||||||
if (!lastSeenAt) return 'Never';
|
return (
|
||||||
|
<FeatureStatus lastSeenAt={lastSeenAt} tooltipPlacement="bottom" />
|
||||||
const [date, now] = getDates(lastSeenAt);
|
);
|
||||||
const diff = getDiffInDays(date, now);
|
|
||||||
if (diff === 0) return '1 day';
|
|
||||||
|
|
||||||
if (diff) {
|
|
||||||
return pluralize(diff, 'day');
|
|
||||||
}
|
|
||||||
|
|
||||||
return '1 day';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderStatus = (icon, text) => (
|
const renderStatus = (icon, text) => (
|
||||||
@ -126,7 +120,7 @@ const ReportToggleListItem = ({
|
|||||||
history.push(getTogglePath(project, name));
|
history.push(getTogglePath(project, name));
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusClasses = classnames(styles.active, {
|
const statusClasses = classnames(styles.active, styles.hideColumnStatus, {
|
||||||
[styles.stale]: stale,
|
[styles.stale]: stale,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -151,9 +145,11 @@ const ReportToggleListItem = ({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<td>{name}</td>
|
<td>{name}</td>
|
||||||
<td>{formatLastSeenAt()}</td>
|
<td className={styles.hideColumnLastSeen}>{formatLastSeenAt()}</td>
|
||||||
<td>{formatCreatedAt()}</td>
|
<td className={styles.hideColumn}>{formatCreatedAt()}</td>
|
||||||
<td className={styles.expired}>{formatExpiredAt()}</td>
|
<td className={`${styles.expired} ${styles.hideColumn}`}>
|
||||||
|
{formatExpiredAt()}
|
||||||
|
</td>
|
||||||
<td className={statusClasses}>{stale ? 'Stale' : 'Active'}</td>
|
<td className={statusClasses}>{stale ? 'Stale' : 'Active'}</td>
|
||||||
<td>{formatReportStatus()}</td>
|
<td>{formatReportStatus()}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -58,7 +58,10 @@ const FeatureToggleListItem = ({
|
|||||||
className={classnames(styles.listItem, rest.className)}
|
className={classnames(styles.listItem, rest.className)}
|
||||||
>
|
>
|
||||||
<span className={styles.listItemMetric}>
|
<span className={styles.listItemMetric}>
|
||||||
<FeatureStatus lastSeenAt={lastSeenAt} />
|
<FeatureStatus
|
||||||
|
lastSeenAt={lastSeenAt}
|
||||||
|
tooltipPlacement="left"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={classnames(
|
className={classnames(
|
||||||
|
@ -83,7 +83,10 @@ const FeatureToggleListNewItem = ({
|
|||||||
align="left"
|
align="left"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<FeatureStatus lastSeenAt={lastSeenAt} />
|
<FeatureStatus
|
||||||
|
lastSeenAt={lastSeenAt}
|
||||||
|
tooltipPlacement="left"
|
||||||
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell
|
<TableCell
|
||||||
className={classNames(
|
className={classNames(
|
||||||
|
@ -47,16 +47,20 @@ function getColor(unit?: string): string {
|
|||||||
|
|
||||||
interface FeatureStatusProps {
|
interface FeatureStatusProps {
|
||||||
lastSeenAt?: Date;
|
lastSeenAt?: Date;
|
||||||
|
tooltipPlacement?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeatureStatus = ({ lastSeenAt }: FeatureStatusProps) => {
|
const FeatureStatus = ({
|
||||||
|
lastSeenAt,
|
||||||
|
tooltipPlacement,
|
||||||
|
}: FeatureStatusProps) => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
|
||||||
const Wrapper = (
|
const Wrapper = (
|
||||||
props: React.PropsWithChildren<{ color: string; toolTip: string }>
|
props: React.PropsWithChildren<{ color: string; toolTip: string }>
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<Tooltip title={props.toolTip} arrow placement="left">
|
<Tooltip title={props.toolTip} arrow placement={tooltipPlacement}>
|
||||||
<div
|
<div
|
||||||
className={styles.container}
|
className={styles.container}
|
||||||
style={{ background: props.color, fontSize: '0.8rem' }}
|
style={{ background: props.color, fontSize: '0.8rem' }}
|
||||||
@ -83,7 +87,9 @@ const FeatureStatus = ({ lastSeenAt }: FeatureStatusProps) => {
|
|||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<Wrapper
|
<Wrapper
|
||||||
toolTip={`Last usage reported ${value} ${unit}${value !== 1 ? 's' : ''} ${suffix}`}
|
toolTip={`Last usage reported ${value} ${unit}${
|
||||||
|
value !== 1 ? 's' : ''
|
||||||
|
} ${suffix}`}
|
||||||
color={getColor(unit)}
|
color={getColor(unit)}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
@ -94,7 +100,10 @@ const FeatureStatus = ({ lastSeenAt }: FeatureStatusProps) => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
elseShow={
|
elseShow={
|
||||||
<Wrapper toolTip="No usage reported from connected applications" color={getColor()}>
|
<Wrapper
|
||||||
|
toolTip="No usage reported from connected applications"
|
||||||
|
color={getColor()}
|
||||||
|
>
|
||||||
<span style={{ fontSize: '1.4rem' }}>⊕</span>
|
<span style={{ fontSize: '1.4rem' }}>⊕</span>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import useHealthReport from '../../../../hooks/api/getters/useHealthReport/useHealthReport';
|
import useHealthReport from '../../../../hooks/api/getters/useHealthReport/useHealthReport';
|
||||||
import ApiError from '../../../common/ApiError/ApiError';
|
import ApiError from '../../../common/ApiError/ApiError';
|
||||||
import ConditionallyRender from '../../../common/ConditionallyRender';
|
import ConditionallyRender from '../../../common/ConditionallyRender';
|
||||||
import ReportCardContainer from '../../../Reporting/ReportCard/ReportCardContainer'
|
import ReportCardContainer from '../../../Reporting/ReportCard/ReportCardContainer';
|
||||||
import ReportToggleList from '../../../Reporting/ReportToggleList/ReportToggleList'
|
import ReportToggleList from '../../../Reporting/ReportToggleList/ReportToggleList';
|
||||||
|
|
||||||
interface ProjectHealthProps {
|
interface ProjectHealthProps {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
@ -25,18 +25,19 @@ const ProjectHealth = ({ projectId }: ProjectHealthProps) => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ReportCardContainer
|
<ReportCardContainer
|
||||||
health={project?.health}
|
health={project?.health}
|
||||||
staleCount={project?.staleCount}
|
staleCount={project?.staleCount}
|
||||||
activeCount={project?.activeCount}
|
activeCount={project?.activeCount}
|
||||||
potentiallyStaleCount={project?.potentiallyStaleCount}
|
potentiallyStaleCount={project?.potentiallyStaleCount}
|
||||||
selectedProject={project.name}
|
selectedProject={project.name}
|
||||||
|
lastUpdate={project.updatedAt}
|
||||||
/>
|
/>
|
||||||
<ReportToggleList
|
<ReportToggleList
|
||||||
features={project.features}
|
features={project.features}
|
||||||
selectedProject={projectId}
|
selectedProject={projectId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default ProjectHealth;
|
export default ProjectHealth;
|
@ -24,4 +24,5 @@ export interface IProjectHealthReport extends IProject {
|
|||||||
staleCount: number;
|
staleCount: number;
|
||||||
potentiallyStaleCount: number;
|
potentiallyStaleCount: number;
|
||||||
activeCount: number;
|
activeCount: number;
|
||||||
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user