mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-19 01:17:18 +02:00
Merge branch 'master' into feat/API-token-improvement
This commit is contained in:
commit
338c74e955
@ -49,6 +49,7 @@
|
|||||||
"@types/react": "17.0.27",
|
"@types/react": "17.0.27",
|
||||||
"@types/react-dom": "17.0.9",
|
"@types/react-dom": "17.0.9",
|
||||||
"@types/react-router-dom": "5.3.1",
|
"@types/react-router-dom": "5.3.1",
|
||||||
|
"@types/react-timeago": "^4.1.3",
|
||||||
"@welldone-software/why-did-you-render": "6.2.1",
|
"@welldone-software/why-did-you-render": "6.2.1",
|
||||||
"array-move": "3.0.1",
|
"array-move": "3.0.1",
|
||||||
"classnames": "2.3.1",
|
"classnames": "2.3.1",
|
||||||
|
@ -3,20 +3,22 @@ import PropTypes from 'prop-types';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Switch, IconButton, ListItem } from '@material-ui/core';
|
import { IconButton, ListItem } from '@material-ui/core';
|
||||||
import { Undo } from '@material-ui/icons';
|
import { Undo } from '@material-ui/icons';
|
||||||
|
|
||||||
import TimeAgo from 'react-timeago';
|
import TimeAgo from 'react-timeago';
|
||||||
import Progress from '../../ProgressWheel';
|
|
||||||
import Status from '../../status-component';
|
import Status from '../../status-component';
|
||||||
import FeatureToggleListItemChip from './FeatureToggleListItemChip';
|
import FeatureToggleListItemChip from './FeatureToggleListItemChip';
|
||||||
import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender';
|
import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender';
|
||||||
|
|
||||||
import { UPDATE_FEATURE } from '../../../AccessProvider/permissions';
|
import { UPDATE_FEATURE } from '../../../AccessProvider/permissions';
|
||||||
import { calc, styles as commonStyles } from '../../../common';
|
import { styles as commonStyles } from '../../../common';
|
||||||
|
|
||||||
import { useStyles } from './styles';
|
import { useStyles } from './styles';
|
||||||
import { getTogglePath } from '../../../../utils/route-path-helpers';
|
import { getTogglePath } from '../../../../utils/route-path-helpers';
|
||||||
|
import FeatureStatus from '../../FeatureView2/FeatureStatus/FeatureStatus';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const FeatureToggleListItem = ({
|
const FeatureToggleListItem = ({
|
||||||
feature,
|
feature,
|
||||||
@ -30,25 +32,9 @@ const FeatureToggleListItem = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
|
|
||||||
const { name, description, enabled, type, stale, createdAt, project } =
|
const { name, description, type, stale, createdAt, project, lastSeenAt } =
|
||||||
feature;
|
feature;
|
||||||
const { showLastHour = false } = settings;
|
|
||||||
const isStale = showLastHour
|
|
||||||
? metricsLastHour.isFallback
|
|
||||||
: metricsLastMinute.isFallback;
|
|
||||||
const percent =
|
|
||||||
1 *
|
|
||||||
(showLastHour
|
|
||||||
? calc(
|
|
||||||
metricsLastHour.yes,
|
|
||||||
metricsLastHour.yes + metricsLastHour.no,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
: calc(
|
|
||||||
metricsLastMinute.yes,
|
|
||||||
metricsLastMinute.yes + metricsLastMinute.no,
|
|
||||||
0
|
|
||||||
));
|
|
||||||
const featureUrl =
|
const featureUrl =
|
||||||
toggleFeature === undefined
|
toggleFeature === undefined
|
||||||
? `/projects/${feature.project}/archived/${name}/metrics`
|
? `/projects/${feature.project}/archived/${name}/metrics`
|
||||||
@ -60,33 +46,7 @@ const FeatureToggleListItem = ({
|
|||||||
className={classnames(styles.listItem, rest.className)}
|
className={classnames(styles.listItem, rest.className)}
|
||||||
>
|
>
|
||||||
<span className={styles.listItemMetric}>
|
<span className={styles.listItemMetric}>
|
||||||
<Progress
|
<FeatureStatus lastSeenAt={lastSeenAt} />
|
||||||
strokeWidth={15}
|
|
||||||
percentage={percent}
|
|
||||||
isFallback={isStale}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span className={styles.listItemToggle}>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={hasAccess(UPDATE_FEATURE, project)}
|
|
||||||
show={
|
|
||||||
<Switch
|
|
||||||
disabled={toggleFeature === undefined}
|
|
||||||
title={`Toggle ${name}`}
|
|
||||||
key="left-actions"
|
|
||||||
onChange={() => toggleFeature(!enabled, name)}
|
|
||||||
checked={enabled}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
elseShow={
|
|
||||||
<Switch
|
|
||||||
disabled
|
|
||||||
title={`Toggle ${name}`}
|
|
||||||
key="left-actions"
|
|
||||||
checked={enabled}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
<span className={classnames(styles.listItemLink)}>
|
<span className={classnames(styles.listItemLink)}>
|
||||||
<Link
|
<Link
|
||||||
|
@ -8,55 +8,33 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
<span
|
<span
|
||||||
className="makeStyles-listItemMetric-2"
|
className="makeStyles-listItemMetric-2"
|
||||||
>
|
>
|
||||||
<svg
|
<div
|
||||||
viewBox="0 0 24 24"
|
aria-describedby={null}
|
||||||
>
|
className="makeStyles-container-6"
|
||||||
<path
|
|
||||||
d="M17.3,18C19,16.5 20,14.4 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12C4,14.4 5,16.5 6.7,18C8.2,16.7 10,16 12,16C14,16 15.9,16.7 17.3,18M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M7,9A1,1 0 0,1 8,10A1,1 0 0,1 7,11A1,1 0 0,1 6,10A1,1 0 0,1 7,9M10,6A1,1 0 0,1 11,7A1,1 0 0,1 10,8A1,1 0 0,1 9,7A1,1 0 0,1 10,6M17,9A1,1 0 0,1 18,10A1,1 0 0,1 17,11A1,1 0 0,1 16,10A1,1 0 0,1 17,9M14.4,6.1C14.9,6.3 15.1,6.9 15,7.4L13.6,10.8C13.8,11.1 14,11.5 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12C10,11 10.7,10.1 11.7,10L13.1,6.7C13.3,6.1 13.9,5.9 14.4,6.1Z"
|
|
||||||
fill="#E0E0E0"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<span
|
|
||||||
className="MuiSwitch-root"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
aria-disabled={false}
|
|
||||||
className="MuiButtonBase-root MuiIconButton-root PrivateSwitchBase-root-6 MuiSwitch-switchBase MuiSwitch-colorSecondary"
|
|
||||||
onBlur={[Function]}
|
onBlur={[Function]}
|
||||||
onDragLeave={[Function]}
|
|
||||||
onFocus={[Function]}
|
onFocus={[Function]}
|
||||||
onKeyDown={[Function]}
|
|
||||||
onKeyUp={[Function]}
|
|
||||||
onMouseDown={[Function]}
|
|
||||||
onMouseLeave={[Function]}
|
onMouseLeave={[Function]}
|
||||||
onMouseUp={[Function]}
|
onMouseOver={[Function]}
|
||||||
onTouchEnd={[Function]}
|
onTouchEnd={[Function]}
|
||||||
onTouchMove={[Function]}
|
|
||||||
onTouchStart={[Function]}
|
onTouchStart={[Function]}
|
||||||
tabIndex={null}
|
style={
|
||||||
title="Toggle Another"
|
Object {
|
||||||
|
"background": "#EDF0F1",
|
||||||
|
"fontSize": "0.8rem",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
title="No usage reported"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="MuiIconButton-label"
|
style={
|
||||||
|
Object {
|
||||||
|
"fontSize": "1.4rem",
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<input
|
⊕
|
||||||
checked={false}
|
|
||||||
className="PrivateSwitchBase-input-9 MuiSwitch-input"
|
|
||||||
disabled={false}
|
|
||||||
onChange={[Function]}
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="MuiSwitch-thumb"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className="MuiSwitch-track"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className="makeStyles-listItemLink-4"
|
className="makeStyles-listItemLink-4"
|
||||||
@ -105,58 +83,33 @@ exports[`renders correctly with one feature without permission 1`] = `
|
|||||||
<span
|
<span
|
||||||
className="makeStyles-listItemMetric-2"
|
className="makeStyles-listItemMetric-2"
|
||||||
>
|
>
|
||||||
<svg
|
<div
|
||||||
viewBox="0 0 24 24"
|
aria-describedby={null}
|
||||||
>
|
className="makeStyles-container-6"
|
||||||
<path
|
|
||||||
d="M17.3,18C19,16.5 20,14.4 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12C4,14.4 5,16.5 6.7,18C8.2,16.7 10,16 12,16C14,16 15.9,16.7 17.3,18M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M7,9A1,1 0 0,1 8,10A1,1 0 0,1 7,11A1,1 0 0,1 6,10A1,1 0 0,1 7,9M10,6A1,1 0 0,1 11,7A1,1 0 0,1 10,8A1,1 0 0,1 9,7A1,1 0 0,1 10,6M17,9A1,1 0 0,1 18,10A1,1 0 0,1 17,11A1,1 0 0,1 16,10A1,1 0 0,1 17,9M14.4,6.1C14.9,6.3 15.1,6.9 15,7.4L13.6,10.8C13.8,11.1 14,11.5 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12C10,11 10.7,10.1 11.7,10L13.1,6.7C13.3,6.1 13.9,5.9 14.4,6.1Z"
|
|
||||||
fill="#E0E0E0"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
<span
|
|
||||||
className="MuiSwitch-root"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
aria-disabled={false}
|
|
||||||
className="MuiButtonBase-root MuiIconButton-root PrivateSwitchBase-root-6 MuiSwitch-switchBase MuiSwitch-colorSecondary"
|
|
||||||
onBlur={[Function]}
|
onBlur={[Function]}
|
||||||
onDragLeave={[Function]}
|
|
||||||
onFocus={[Function]}
|
onFocus={[Function]}
|
||||||
onKeyDown={[Function]}
|
|
||||||
onKeyUp={[Function]}
|
|
||||||
onMouseDown={[Function]}
|
|
||||||
onMouseLeave={[Function]}
|
onMouseLeave={[Function]}
|
||||||
onMouseUp={[Function]}
|
onMouseOver={[Function]}
|
||||||
onTouchEnd={[Function]}
|
onTouchEnd={[Function]}
|
||||||
onTouchMove={[Function]}
|
|
||||||
onTouchStart={[Function]}
|
onTouchStart={[Function]}
|
||||||
tabIndex={null}
|
style={
|
||||||
title="Toggle Another"
|
Object {
|
||||||
|
"background": "#EDF0F1",
|
||||||
|
"fontSize": "0.8rem",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
title="No usage reported"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="MuiIconButton-label"
|
style={
|
||||||
|
Object {
|
||||||
|
"fontSize": "1.4rem",
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<input
|
⊕
|
||||||
checked={false}
|
|
||||||
className="PrivateSwitchBase-input-9 MuiSwitch-input"
|
|
||||||
disabled={false}
|
|
||||||
onChange={[Function]}
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="MuiSwitch-thumb"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className="MuiTouchRipple-root"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
className="MuiSwitch-track"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className="makeStyles-listItemLink-4"
|
className="makeStyles-listItemLink-4"
|
||||||
|
@ -19,20 +19,24 @@ export const useStyles = makeStyles(theme => ({
|
|||||||
display: 'none',
|
display: 'none',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tableCellStatus: {
|
||||||
|
width: '50px',
|
||||||
|
},
|
||||||
tableCellName: {
|
tableCellName: {
|
||||||
width: '250px',
|
width: '250px',
|
||||||
|
display: 'flex',
|
||||||
},
|
},
|
||||||
tableCellEnv: {
|
tableCellEnv: {
|
||||||
width: '20px',
|
width: '20px',
|
||||||
},
|
},
|
||||||
tableCellType: {
|
tableCellType: {
|
||||||
display: 'flex',
|
width: '32px',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
[theme.breakpoints.down('sm')]: {
|
[theme.breakpoints.down('sm')]: {
|
||||||
display: 'none',
|
display: 'none',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
marginRight: '0.3rem',
|
color: theme.palette.grey[600],
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -11,7 +11,10 @@ import FeatureToggleListNewItem from './FeatureToggleListNewItem/FeatureToggleLi
|
|||||||
import usePagination from '../../../hooks/usePagination';
|
import usePagination from '../../../hooks/usePagination';
|
||||||
|
|
||||||
import loadingFeatures from './FeatureToggleListNewItem/loadingFeatures';
|
import loadingFeatures from './FeatureToggleListNewItem/loadingFeatures';
|
||||||
import { IFeatureToggleListItem } from '../../../interfaces/featureToggle';
|
import {
|
||||||
|
IFeatureToggle,
|
||||||
|
IFeatureToggleListItem,
|
||||||
|
} from '../../../interfaces/featureToggle';
|
||||||
import PaginateUI from '../../common/PaginateUI/PaginateUI';
|
import PaginateUI from '../../common/PaginateUI/PaginateUI';
|
||||||
interface IFeatureToggleListNewProps {
|
interface IFeatureToggleListNewProps {
|
||||||
features: IFeatureToggleListItem[];
|
features: IFeatureToggleListItem[];
|
||||||
@ -26,7 +29,7 @@ const FeatureToggleListNew = ({
|
|||||||
}: IFeatureToggleListNewProps) => {
|
}: IFeatureToggleListNewProps) => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } =
|
const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } =
|
||||||
usePagination(features, 9);
|
usePagination(features, 50);
|
||||||
|
|
||||||
const getEnvironments = () => {
|
const getEnvironments = () => {
|
||||||
if (features.length > 0) {
|
if (features.length > 0) {
|
||||||
@ -57,7 +60,7 @@ const FeatureToggleListNew = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return page.map((feature: IFeatureToggleListItem) => {
|
return page.map((feature: IFeatureToggle) => {
|
||||||
return (
|
return (
|
||||||
<FeatureToggleListNewItem
|
<FeatureToggleListNewItem
|
||||||
key={feature.name}
|
key={feature.name}
|
||||||
@ -65,6 +68,7 @@ const FeatureToggleListNew = ({
|
|||||||
type={feature.type}
|
type={feature.type}
|
||||||
environments={feature.environments}
|
environments={feature.environments}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
|
lastSeenAt={feature.lastSeenAt}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -78,12 +82,12 @@ const FeatureToggleListNew = ({
|
|||||||
<TableCell
|
<TableCell
|
||||||
className={classnames(
|
className={classnames(
|
||||||
styles.tableCell,
|
styles.tableCell,
|
||||||
styles.tableCellName,
|
styles.tableCellStatus,
|
||||||
styles.tableCellHeader
|
styles.tableCellHeader
|
||||||
)}
|
)}
|
||||||
align="left"
|
align="left"
|
||||||
>
|
>
|
||||||
<span data-loading>Name</span>
|
<span data-loading>Status</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell
|
<TableCell
|
||||||
className={classnames(
|
className={classnames(
|
||||||
@ -91,10 +95,20 @@ const FeatureToggleListNew = ({
|
|||||||
styles.tableCellHeader,
|
styles.tableCellHeader,
|
||||||
styles.typeHeader
|
styles.typeHeader
|
||||||
)}
|
)}
|
||||||
align="left"
|
align="center"
|
||||||
>
|
>
|
||||||
<span data-loading>Type</span>
|
<span data-loading>Type</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
className={classnames(
|
||||||
|
styles.tableCell,
|
||||||
|
styles.tableCellName,
|
||||||
|
styles.tableCellHeader
|
||||||
|
)}
|
||||||
|
align="left"
|
||||||
|
>
|
||||||
|
<span data-loading>Name</span>
|
||||||
|
</TableCell>
|
||||||
{getEnvironments().map((env: any) => {
|
{getEnvironments().map((env: any) => {
|
||||||
return (
|
return (
|
||||||
<TableCell
|
<TableCell
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableRow,
|
TableRow,
|
||||||
|
Tooltip,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from '@material-ui/core';
|
} from '@material-ui/core';
|
||||||
@ -16,16 +17,19 @@ import useToast from '../../../../hooks/useToast';
|
|||||||
import { getTogglePath } from '../../../../utils/route-path-helpers';
|
import { getTogglePath } from '../../../../utils/route-path-helpers';
|
||||||
import { SyntheticEvent } from 'react-router/node_modules/@types/react';
|
import { SyntheticEvent } from 'react-router/node_modules/@types/react';
|
||||||
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
import FeatureStatus from '../../FeatureView2/FeatureStatus/FeatureStatus';
|
||||||
|
|
||||||
interface IFeatureToggleListNewItemProps {
|
interface IFeatureToggleListNewItemProps {
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
environments: IEnvironments[];
|
environments: IFeatureEnvironment[];
|
||||||
projectId: string;
|
projectId: string;
|
||||||
|
lastSeenAt?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeatureToggleListNewItem = ({
|
const FeatureToggleListNewItem = ({
|
||||||
name,
|
name,
|
||||||
|
lastSeenAt,
|
||||||
type,
|
type,
|
||||||
environments,
|
environments,
|
||||||
projectId,
|
projectId,
|
||||||
@ -35,7 +39,7 @@ const FeatureToggleListNewItem = ({
|
|||||||
const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
const smallScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
const { toggleFeatureByEnvironment } = useToggleFeatureByEnv(
|
const { toggleFeatureByEnvironment } = useToggleFeatureByEnv(
|
||||||
projectId,
|
projectId,
|
||||||
name
|
name,
|
||||||
);
|
);
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
|
|
||||||
@ -74,22 +78,25 @@ const FeatureToggleListNewItem = ({
|
|||||||
<>
|
<>
|
||||||
<TableRow className={styles.tableRow}>
|
<TableRow className={styles.tableRow}>
|
||||||
<TableCell className={styles.tableCell} align="left" onClick={onClick}>
|
<TableCell className={styles.tableCell} align="left" onClick={onClick}>
|
||||||
<span data-loading>{name}</span>
|
<FeatureStatus lastSeenAt={lastSeenAt} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={!smallScreen}
|
condition={!smallScreen}
|
||||||
show={
|
show={
|
||||||
<TableCell className={styles.tableCell} align="left" onClick={onClick}>
|
<TableCell className={styles.tableCell} align="center" onClick={onClick}>
|
||||||
<div className={styles.tableCellType}>
|
<Tooltip arrow placement="right" title={type}>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
data-loading
|
data-loading
|
||||||
className={styles.icon}
|
className={styles.icon}
|
||||||
/>{' '}
|
/>
|
||||||
<span data-loading>{type}</span>
|
</Tooltip>
|
||||||
</div>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<TableCell className={styles.tableCell} align="left" onClick={onClick}>
|
||||||
|
<span data-loading>{name}</span>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
|
||||||
{environments.map((env: IEnvironments) => {
|
{environments.map((env: IEnvironments) => {
|
||||||
return (
|
return (
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
|
||||||
|
export const useStyles = makeStyles(theme => ({
|
||||||
|
container: {
|
||||||
|
width: '42px',
|
||||||
|
height: '42px',
|
||||||
|
fontSize: '0.7em',
|
||||||
|
background: 'gray',
|
||||||
|
borderRadius: '3px',
|
||||||
|
textAlign: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '13px 10px',
|
||||||
|
},
|
||||||
|
}));
|
@ -0,0 +1,105 @@
|
|||||||
|
import { useStyles } from './FeatureStatus.styles';
|
||||||
|
import TimeAgo from 'react-timeago';
|
||||||
|
import ConditionallyRender from '../../../common/ConditionallyRender';
|
||||||
|
import { Tooltip } from '@material-ui/core';
|
||||||
|
|
||||||
|
function generateUnit(unit?: string): string {
|
||||||
|
switch (unit) {
|
||||||
|
case 'second':
|
||||||
|
return 's';
|
||||||
|
case 'minute':
|
||||||
|
return 'm';
|
||||||
|
case 'hour':
|
||||||
|
return 'h';
|
||||||
|
case 'day':
|
||||||
|
return 'D';
|
||||||
|
case 'week':
|
||||||
|
return 'W';
|
||||||
|
case 'month':
|
||||||
|
return 'M';
|
||||||
|
case 'year':
|
||||||
|
return 'Y';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColor(unit?: string): string {
|
||||||
|
switch (unit) {
|
||||||
|
case 'second':
|
||||||
|
return '#98E3AF';
|
||||||
|
case 'minute':
|
||||||
|
return '#98E3AF';
|
||||||
|
case 'hour':
|
||||||
|
return '#98E3AF';
|
||||||
|
case 'day':
|
||||||
|
return '#98E3AF';
|
||||||
|
case 'week':
|
||||||
|
return '#ECD875';
|
||||||
|
case 'month':
|
||||||
|
return '#F5A69A';
|
||||||
|
case 'year':
|
||||||
|
return '#F5A69A';
|
||||||
|
default:
|
||||||
|
return '#EDF0F1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FeatureStatusProps {
|
||||||
|
lastSeenAt?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FeatureStatus = ({ lastSeenAt }: FeatureStatusProps) => {
|
||||||
|
const styles = useStyles();
|
||||||
|
|
||||||
|
const Wrapper = (
|
||||||
|
props: React.PropsWithChildren<{ color: string; toolTip: string }>
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<Tooltip title={props.toolTip} arrow placement="left">
|
||||||
|
<div
|
||||||
|
className={styles.container}
|
||||||
|
style={{ background: props.color, fontSize: '0.8rem' }}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={!!lastSeenAt}
|
||||||
|
show={
|
||||||
|
//@ts-ignore
|
||||||
|
<TimeAgo
|
||||||
|
date={lastSeenAt}
|
||||||
|
title=""
|
||||||
|
live={false}
|
||||||
|
formatter={(
|
||||||
|
value: number,
|
||||||
|
unit: string,
|
||||||
|
suffix: string
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<Wrapper
|
||||||
|
toolTip={`Last usage reported ${value} ${unit} ${suffix}`}
|
||||||
|
color={getColor(unit)}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
{generateUnit(unit)}
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
elseShow={
|
||||||
|
<Wrapper toolTip="No usage reported" color={getColor()}>
|
||||||
|
<span style={{ fontSize: '1.4rem' }}>⊕</span>
|
||||||
|
</Wrapper>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FeatureStatus;
|
@ -8,6 +8,7 @@ export const useStyles = makeStyles(theme => ({
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
projectToggles: { width: '100%', minHeight: '100%' },
|
||||||
header: {
|
header: {
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderRadius: '10px',
|
borderRadius: '10px',
|
||||||
|
@ -3,9 +3,11 @@ import { makeStyles } from '@material-ui/core/styles';
|
|||||||
export const useStyles = makeStyles(theme => ({
|
export const useStyles = makeStyles(theme => ({
|
||||||
container: {
|
container: {
|
||||||
boxShadow: 'none',
|
boxShadow: 'none',
|
||||||
marginLeft: '2rem',
|
marginLeft: '1rem',
|
||||||
width: '100%',
|
minHeight: '100%',
|
||||||
|
width: 'calc(100% - 1rem)',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
paddingBottom: '4rem',
|
||||||
[theme.breakpoints.down('sm')]: {
|
[theme.breakpoints.down('sm')]: {
|
||||||
marginLeft: '0',
|
marginLeft: '0',
|
||||||
paddingBottom: '4rem',
|
paddingBottom: '4rem',
|
||||||
|
@ -14,6 +14,11 @@ export const useStyles = makeStyles(theme => ({
|
|||||||
marginBottom: '1rem',
|
marginBottom: '1rem',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
percentageContainer: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
margin: '1rem 0',
|
||||||
|
},
|
||||||
projectIcon: {
|
projectIcon: {
|
||||||
margin: '2rem 0',
|
margin: '2rem 0',
|
||||||
[theme.breakpoints.down('sm')]: {
|
[theme.breakpoints.down('sm')]: {
|
||||||
@ -35,7 +40,7 @@ export const useStyles = makeStyles(theme => ({
|
|||||||
infoSection: {
|
infoSection: {
|
||||||
margin: '0',
|
margin: '0',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
marginBottom: '1.5rem',
|
marginBottom: '1rem',
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderRadius: '10px',
|
borderRadius: '10px',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
@ -3,9 +3,9 @@ import { Link } from 'react-router-dom';
|
|||||||
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
|
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
import { ReactComponent as ProjectIcon } from '../../../../assets/icons/projectIcon.svg';
|
|
||||||
import { useCommonStyles } from '../../../../common.styles';
|
import { useCommonStyles } from '../../../../common.styles';
|
||||||
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
import PercentageCircle from '../../../common/PercentageCircle/PercentageCircle';
|
||||||
|
|
||||||
interface IProjectInfoProps {
|
interface IProjectInfoProps {
|
||||||
id: string;
|
id: string;
|
||||||
@ -34,8 +34,8 @@ const ProjectInfo = ({
|
|||||||
<aside>
|
<aside>
|
||||||
<div className={styles.projectInfo}>
|
<div className={styles.projectInfo}>
|
||||||
<div className={styles.infoSection}>
|
<div className={styles.infoSection}>
|
||||||
<div data-loading>
|
<div data-loading className={styles.percentageContainer}>
|
||||||
<ProjectIcon className={styles.projectIcon} />
|
<PercentageCircle percentage={health} />
|
||||||
</div>
|
</div>
|
||||||
<p className={styles.subtitle} data-loading>
|
<p className={styles.subtitle} data-loading>
|
||||||
Overall health rating
|
Overall health rating
|
||||||
|
@ -21,9 +21,13 @@ const ProjectOverview = ({projectId}: ProjectOverviewProps) => {
|
|||||||
health={health}
|
health={health}
|
||||||
featureCount={features?.length}
|
featureCount={features?.length}
|
||||||
/>
|
/>
|
||||||
<ProjectFeatureToggles features={features} loading={loading} />
|
<div className={styles.projectToggles}>
|
||||||
|
<ProjectFeatureToggles
|
||||||
|
features={features}
|
||||||
|
loading={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,6 @@ export interface IFeatureToggleListItem {
|
|||||||
|
|
||||||
export interface IEnvironments {
|
export interface IEnvironments {
|
||||||
name: string;
|
name: string;
|
||||||
displayName: string;
|
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,7 +15,7 @@ export interface IFeatureToggle {
|
|||||||
stale: boolean;
|
stale: boolean;
|
||||||
archived: boolean;
|
archived: boolean;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
lastSeenAt: string;
|
lastSeenAt: Date;
|
||||||
description: string;
|
description: string;
|
||||||
environments: IFeatureEnvironment[];
|
environments: IFeatureEnvironment[];
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -2176,6 +2176,13 @@
|
|||||||
"@types/history" "*"
|
"@types/history" "*"
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-timeago@^4.1.3":
|
||||||
|
version "4.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-timeago/-/react-timeago-4.1.3.tgz#957baaa9f8ea98457ee63b6a57b57a616066ba35"
|
||||||
|
integrity sha512-XaaMBzuXLw7lxPPDs/fenlohcf3NDqM5qP4oOL/Meu+Hb1QChW4Igw/SruS1llEqch18RQB3wDTIwvqq4nivvw==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-transition-group@^4.2.0":
|
"@types/react-transition-group@^4.2.0":
|
||||||
version "4.4.1"
|
version "4.4.1"
|
||||||
resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz"
|
resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz"
|
||||||
|
Loading…
Reference in New Issue
Block a user