mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
Merge branch 'master' into fix/environment-guidance
This commit is contained in:
commit
e2a2944450
@ -1,4 +1,3 @@
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Paper } from '@material-ui/core';
|
||||
import PropTypes from 'prop-types';
|
||||
@ -8,12 +7,14 @@ import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined'
|
||||
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
import styles from './ReportCard.module.scss';
|
||||
import ReactTimeAgo from 'react-timeago';
|
||||
|
||||
const ReportCard = ({
|
||||
health,
|
||||
activeCount,
|
||||
staleCount,
|
||||
potentiallyStaleCount,
|
||||
lastUpdate,
|
||||
}) => {
|
||||
const healthLessThan50 = health < 50;
|
||||
const healthLessThan75 = health < 75;
|
||||
@ -52,11 +53,22 @@ const ReportCard = ({
|
||||
<div className={styles.reportCardHealthInnerContainer}>
|
||||
<ConditionallyRender
|
||||
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 className={styles.reportCardListContainer}>
|
||||
<div className={styles.reportCardToggle}>
|
||||
<h2 className={styles.header}>Toggle report</h2>
|
||||
<ul className={styles.reportCardList}>
|
||||
<li>
|
||||
@ -65,28 +77,49 @@ const ReportCard = ({
|
||||
show={renderActiveToggles}
|
||||
/>
|
||||
</li>
|
||||
<ConditionallyRender
|
||||
condition={activeCount}
|
||||
show={
|
||||
<p className={styles.reportCardActionText}>
|
||||
Also includes potentially stale toggles.
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
|
||||
<li>
|
||||
<ConditionallyRender
|
||||
condition={staleCount}
|
||||
show={renderStaleToggles}
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<ConditionallyRender
|
||||
condition={potentiallyStaleCount}
|
||||
show={renderPotentiallyStaleToggles}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={styles.reportCardAction}>
|
||||
<h2 className={styles.header}>Potential actions</h2>
|
||||
<div className={styles.reportCardActionContainer}>
|
||||
<ul className={styles.reportCardList}>
|
||||
<li>
|
||||
<ConditionallyRender
|
||||
condition={potentiallyStaleCount}
|
||||
show={renderPotentiallyStaleToggles}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<ConditionallyRender
|
||||
condition={potentiallyStaleCount}
|
||||
show={
|
||||
<p className={styles.reportCardActionText}>
|
||||
Review your feature toggles and delete unused
|
||||
toggles.
|
||||
Review your feature toggles and delete
|
||||
unused toggles.
|
||||
</p>
|
||||
}
|
||||
elseShow={
|
||||
<p className={styles.reportCardNoActionText}>
|
||||
No action is required
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,6 +2,8 @@
|
||||
width: 100%;
|
||||
padding: var(--card-padding);
|
||||
margin: var(--card-margin-y) 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
@ -14,9 +16,19 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.reportCardHealth {
|
||||
padding: 10px;
|
||||
}
|
||||
.reportCardAction {
|
||||
padding: 10px;
|
||||
}
|
||||
.reportCardToggle {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.reportCardHealthInnerContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -70,6 +82,12 @@
|
||||
.reportCardActionText {
|
||||
max-width: 300px;
|
||||
font-size: var(--p-size);
|
||||
margin-left: 35px;
|
||||
}
|
||||
.reportCardNoActionText {
|
||||
max-width: 300px;
|
||||
font-size: var(--p-size);
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.reportCardBtn {
|
||||
@ -83,3 +101,16 @@
|
||||
.healthWarning {
|
||||
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 {
|
||||
width: 100%;
|
||||
margin: var(--card-margin-y) 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.bulkAction {
|
||||
@ -65,7 +67,31 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tableRow:hover {
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
margin: 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 { 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';
|
||||
|
||||
const ReportToggleListHeader = ({ handleCheckAll, checkAll, setSortData, bulkActionsOn }) => {
|
||||
const ReportToggleListHeader = ({
|
||||
handleCheckAll,
|
||||
checkAll,
|
||||
setSortData,
|
||||
bulkActionsOn,
|
||||
}) => {
|
||||
const handleSort = type => {
|
||||
setSortData(prev => ({
|
||||
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
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
</th>
|
||||
<th role="button" tabIndex={0} onClick={() => handleSort(LAST_SEEN)}>
|
||||
<th
|
||||
role="button"
|
||||
className={styles.hideColumnLastSeen}
|
||||
tabIndex={0}
|
||||
onClick={() => handleSort(LAST_SEEN)}
|
||||
>
|
||||
Last seen
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
</th>
|
||||
<th role="button" tabIndex={0} onClick={() => handleSort(CREATED)}>
|
||||
<th
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={styles.hideColumn}
|
||||
onClick={() => handleSort(CREATED)}
|
||||
>
|
||||
Created
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
</th>
|
||||
<th role="button" tabIndex={0} onClick={() => handleSort(EXPIRED)}>
|
||||
<th
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={styles.hideColumn}
|
||||
onClick={() => handleSort(EXPIRED)}
|
||||
>
|
||||
Expired
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
</th>
|
||||
<th role="button" tabIndex={0} onClick={() => handleSort(STATUS)}>
|
||||
<th
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={styles.hideColumnStatus}
|
||||
onClick={() => handleSort(STATUS)}
|
||||
>
|
||||
Status
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
</th>
|
||||
<th role="button" tabIndex={0} onClick={() => handleSort(REPORT)}>
|
||||
<th
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => handleSort(REPORT)}
|
||||
>
|
||||
Report
|
||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||
</th>
|
||||
|
@ -7,6 +7,7 @@ import { Checkbox } from '@material-ui/core';
|
||||
import CheckIcon from '@material-ui/icons/Check';
|
||||
import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined';
|
||||
import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender';
|
||||
import FeatureStatus from '../../../feature/FeatureView2/FeatureStatus/FeatureStatus';
|
||||
|
||||
import {
|
||||
pluralize,
|
||||
@ -75,20 +76,13 @@ const ReportToggleListItem = ({
|
||||
|
||||
return pluralize(result, 'day');
|
||||
}
|
||||
return 'N/A';
|
||||
};
|
||||
|
||||
const formatLastSeenAt = () => {
|
||||
if (!lastSeenAt) return 'Never';
|
||||
|
||||
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';
|
||||
return (
|
||||
<FeatureStatus lastSeenAt={lastSeenAt} tooltipPlacement="bottom" />
|
||||
);
|
||||
};
|
||||
|
||||
const renderStatus = (icon, text) => (
|
||||
@ -126,7 +120,7 @@ const ReportToggleListItem = ({
|
||||
history.push(getTogglePath(project, name));
|
||||
};
|
||||
|
||||
const statusClasses = classnames(styles.active, {
|
||||
const statusClasses = classnames(styles.active, styles.hideColumnStatus, {
|
||||
[styles.stale]: stale,
|
||||
});
|
||||
|
||||
@ -151,9 +145,11 @@ const ReportToggleListItem = ({
|
||||
}
|
||||
/>
|
||||
<td>{name}</td>
|
||||
<td>{formatLastSeenAt()}</td>
|
||||
<td>{formatCreatedAt()}</td>
|
||||
<td className={styles.expired}>{formatExpiredAt()}</td>
|
||||
<td className={styles.hideColumnLastSeen}>{formatLastSeenAt()}</td>
|
||||
<td className={styles.hideColumn}>{formatCreatedAt()}</td>
|
||||
<td className={`${styles.expired} ${styles.hideColumn}`}>
|
||||
{formatExpiredAt()}
|
||||
</td>
|
||||
<td className={statusClasses}>{stale ? 'Stale' : 'Active'}</td>
|
||||
<td>{formatReportStatus()}</td>
|
||||
</tr>
|
||||
|
@ -58,7 +58,10 @@ const FeatureToggleListItem = ({
|
||||
className={classnames(styles.listItem, rest.className)}
|
||||
>
|
||||
<span className={styles.listItemMetric}>
|
||||
<FeatureStatus lastSeenAt={lastSeenAt} />
|
||||
<FeatureStatus
|
||||
lastSeenAt={lastSeenAt}
|
||||
tooltipPlacement="left"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className={classnames(
|
||||
|
@ -95,7 +95,10 @@ const FeatureToggleListNewItem = ({
|
||||
align="left"
|
||||
onClick={onClick}
|
||||
>
|
||||
<FeatureStatus lastSeenAt={lastSeenAt} />
|
||||
<FeatureStatus
|
||||
lastSeenAt={lastSeenAt}
|
||||
tooltipPlacement="left"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classNames(
|
||||
|
@ -47,16 +47,20 @@ function getColor(unit?: string): string {
|
||||
|
||||
interface FeatureStatusProps {
|
||||
lastSeenAt?: Date;
|
||||
tooltipPlacement?: string;
|
||||
}
|
||||
|
||||
const FeatureStatus = ({ lastSeenAt }: FeatureStatusProps) => {
|
||||
const FeatureStatus = ({
|
||||
lastSeenAt,
|
||||
tooltipPlacement,
|
||||
}: FeatureStatusProps) => {
|
||||
const styles = useStyles();
|
||||
|
||||
const Wrapper = (
|
||||
props: React.PropsWithChildren<{ color: string; toolTip: string }>
|
||||
) => {
|
||||
return (
|
||||
<Tooltip title={props.toolTip} arrow placement="left">
|
||||
<Tooltip title={props.toolTip} arrow placement={tooltipPlacement}>
|
||||
<div
|
||||
className={styles.container}
|
||||
style={{ background: props.color, fontSize: '0.8rem' }}
|
||||
@ -83,7 +87,9 @@ const FeatureStatus = ({ lastSeenAt }: FeatureStatusProps) => {
|
||||
) => {
|
||||
return (
|
||||
<Wrapper
|
||||
toolTip={`Last usage reported ${value} ${unit}${value !== 1 ? 's' : ''} ${suffix}`}
|
||||
toolTip={`Last usage reported ${value} ${unit}${
|
||||
value !== 1 ? 's' : ''
|
||||
} ${suffix}`}
|
||||
color={getColor(unit)}
|
||||
>
|
||||
{value}
|
||||
@ -94,7 +100,10 @@ const FeatureStatus = ({ lastSeenAt }: FeatureStatusProps) => {
|
||||
/>
|
||||
}
|
||||
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>
|
||||
</Wrapper>
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import useHealthReport from '../../../../hooks/api/getters/useHealthReport/useHealthReport';
|
||||
import ApiError from '../../../common/ApiError/ApiError';
|
||||
import ConditionallyRender from '../../../common/ConditionallyRender';
|
||||
import ReportCardContainer from '../../../Reporting/ReportCard/ReportCardContainer'
|
||||
import ReportToggleList from '../../../Reporting/ReportToggleList/ReportToggleList'
|
||||
import ReportCardContainer from '../../../Reporting/ReportCard/ReportCardContainer';
|
||||
import ReportToggleList from '../../../Reporting/ReportToggleList/ReportToggleList';
|
||||
|
||||
interface ProjectHealthProps {
|
||||
projectId: string;
|
||||
@ -30,13 +30,14 @@ const ProjectHealth = ({ projectId }: ProjectHealthProps) => {
|
||||
activeCount={project?.activeCount}
|
||||
potentiallyStaleCount={project?.potentiallyStaleCount}
|
||||
selectedProject={project.name}
|
||||
lastUpdate={project.updatedAt}
|
||||
/>
|
||||
<ReportToggleList
|
||||
features={project.features}
|
||||
selectedProject={projectId}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectHealth;
|
@ -24,4 +24,5 @@ export interface IProjectHealthReport extends IProject {
|
||||
staleCount: number;
|
||||
potentiallyStaleCount: number;
|
||||
activeCount: number;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user