mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-08 01:15:49 +02:00
fix: misc UI improvements (#806)
* fix: link color in project features Co-authored-by: olav <mail@olav.io> * fix: link in health page Co-authored-by: olav <mail@olav.io> * fix: keep sorting state in project toggles list Co-authored-by: olav <mail@olav.io> * fix: style link in the toggle list project Co-authored-by: olav <mail@olav.io> * refactor: update browser list Co-authored-by: olav <mail@olav.io> Co-authored-by: olav <mail@olav.io>
This commit is contained in:
parent
cfc2338e78
commit
4589a19e03
@ -1,29 +1,26 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Paper, MenuItem } from '@material-ui/core';
|
import { Paper, MenuItem } from '@material-ui/core';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import ReportToggleListItem from './ReportToggleListItem/ReportToggleListItem';
|
import ReportToggleListItem from './ReportToggleListItem/ReportToggleListItem';
|
||||||
import ReportToggleListHeader from './ReportToggleListHeader/ReportToggleListHeader';
|
import ReportToggleListHeader from './ReportToggleListHeader/ReportToggleListHeader';
|
||||||
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
|
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
|
||||||
import DropdownMenu from '../../common/DropdownMenu/DropdownMenu';
|
import DropdownMenu from '../../common/DropdownMenu/DropdownMenu';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getObjectProperties,
|
getObjectProperties,
|
||||||
getCheckedState,
|
getCheckedState,
|
||||||
applyCheckedToFeatures,
|
applyCheckedToFeatures,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
|
import { useStyles } from './ReportToggleList.styles';
|
||||||
import useSort from '../../../hooks/useSort';
|
import { useFeaturesSort } from 'hooks/useFeaturesSort';
|
||||||
|
|
||||||
import styles from './ReportToggleList.module.scss';
|
|
||||||
|
|
||||||
/* FLAG TO TOGGLE UNFINISHED BULK ACTIONS FEATURE */
|
/* FLAG TO TOGGLE UNFINISHED BULK ACTIONS FEATURE */
|
||||||
const BULK_ACTIONS_ON = false;
|
const BULK_ACTIONS_ON = false;
|
||||||
|
|
||||||
const ReportToggleList = ({ features, selectedProject }) => {
|
const ReportToggleList = ({ features, selectedProject }) => {
|
||||||
|
const styles = useStyles();
|
||||||
const [checkAll, setCheckAll] = useState(false);
|
const [checkAll, setCheckAll] = useState(false);
|
||||||
const [localFeatures, setFeatures] = useState([]);
|
const [localFeatures, setFeatures] = useState([]);
|
||||||
const [sort, setSortData] = useSort();
|
const { setSort, sorted } = useFeaturesSort(localFeatures);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const formattedFeatures = features.map(feature => ({
|
const formattedFeatures = features.map(feature => ({
|
||||||
@ -52,7 +49,7 @@ const ReportToggleList = ({ features, selectedProject }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderListRows = () =>
|
const renderListRows = () =>
|
||||||
sort(localFeatures).map(feature => (
|
sorted.map(feature => (
|
||||||
<ReportToggleListItem
|
<ReportToggleListItem
|
||||||
key={feature.name}
|
key={feature.name}
|
||||||
{...feature}
|
{...feature}
|
||||||
@ -88,7 +85,7 @@ const ReportToggleList = ({ features, selectedProject }) => {
|
|||||||
<ReportToggleListHeader
|
<ReportToggleListHeader
|
||||||
handleCheckAll={handleCheckAll}
|
handleCheckAll={handleCheckAll}
|
||||||
checkAll={checkAll}
|
checkAll={checkAll}
|
||||||
setSortData={setSortData}
|
setSort={setSort}
|
||||||
bulkActionsOn={BULK_ACTIONS_ON}
|
bulkActionsOn={BULK_ACTIONS_ON}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
.reportToggleList {
|
|
||||||
width: 100%;
|
|
||||||
margin: var(--card-margin-y) 0;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bulkAction {
|
|
||||||
background-color: #f2f2f2;
|
|
||||||
font-size: var(--p-size);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sortIcon {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reportToggleListHeader {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
border-bottom: 1px solid #f1f1f1;
|
|
||||||
padding: 1rem var(--card-padding-x);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reportToggleListInnerContainer {
|
|
||||||
padding: var(--card-padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reportToggleListHeading {
|
|
||||||
font-size: var(--h1-size);
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 'bold';
|
|
||||||
}
|
|
||||||
|
|
||||||
.reportingToggleTable {
|
|
||||||
width: 100%;
|
|
||||||
border-spacing: 0 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reportingToggleTable th {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.expired {
|
|
||||||
color: var(--danger);
|
|
||||||
}
|
|
||||||
|
|
||||||
.active {
|
|
||||||
color: var(--success);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stale {
|
|
||||||
color: var(--danger);
|
|
||||||
}
|
|
||||||
|
|
||||||
.reportStatus {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reportIcon {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tableRow {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,103 @@
|
|||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
|
||||||
|
export const useStyles = makeStyles(theme => ({
|
||||||
|
reportToggleList: {
|
||||||
|
width: '100%',
|
||||||
|
margin: 'var(--card-margin-y) 0',
|
||||||
|
borderRadius: 10,
|
||||||
|
boxShadow: 'none',
|
||||||
|
},
|
||||||
|
bulkAction: {
|
||||||
|
backgroundColor: '#f2f2f2',
|
||||||
|
fontSize: 'var(--p-size)',
|
||||||
|
},
|
||||||
|
|
||||||
|
sortIcon: {
|
||||||
|
marginLeft: 8,
|
||||||
|
},
|
||||||
|
|
||||||
|
reportToggleListHeader: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderBottom: '1px solid #f1f1f1',
|
||||||
|
padding: '1rem var(--card-padding-x)',
|
||||||
|
},
|
||||||
|
|
||||||
|
reportToggleListInnerContainer: {
|
||||||
|
padding: 'var(--card-padding)',
|
||||||
|
},
|
||||||
|
|
||||||
|
reportToggleListHeading: {
|
||||||
|
fontSize: 'var(--h1-size)',
|
||||||
|
margin: 0,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
|
||||||
|
reportIcon: {
|
||||||
|
fontsize: '1.5rem',
|
||||||
|
marginRight: 5,
|
||||||
|
},
|
||||||
|
|
||||||
|
reportingToggleTable: {
|
||||||
|
width: ' 100%',
|
||||||
|
borderSpacing: '0 0.8rem',
|
||||||
|
'& th': {
|
||||||
|
textAlign: 'left',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expired: {
|
||||||
|
color: 'var(--danger)',
|
||||||
|
},
|
||||||
|
|
||||||
|
active: {
|
||||||
|
color: 'var(--success)',
|
||||||
|
},
|
||||||
|
|
||||||
|
stale: {
|
||||||
|
color: 'var(--danger)',
|
||||||
|
},
|
||||||
|
|
||||||
|
reportStatus: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
|
||||||
|
tableRow: {
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: '#eeeeee',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
checkbox: {
|
||||||
|
margin: 0,
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
link: {
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
textDecoration: 'none',
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
},
|
||||||
|
|
||||||
|
[theme.breakpoints.down(800)]: {
|
||||||
|
hideColumn: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
th: {
|
||||||
|
minWidth: '120px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
[theme.breakpoints.down(550)]: {
|
||||||
|
hideColumnStatus: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
[theme.breakpoints.down(425)]: {
|
||||||
|
hideColumnLastSeen: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
@ -5,27 +5,21 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender';
|
import ConditionallyRender from '../../../common/ConditionallyRender/ConditionallyRender';
|
||||||
|
|
||||||
import {
|
import { NAME, LAST_SEEN, CREATED, EXPIRED, STATUS } from '../../constants';
|
||||||
NAME,
|
|
||||||
LAST_SEEN,
|
|
||||||
CREATED,
|
|
||||||
EXPIRED,
|
|
||||||
STATUS,
|
|
||||||
REPORT,
|
|
||||||
} from '../../constants';
|
|
||||||
|
|
||||||
import styles from '../ReportToggleList.module.scss';
|
import { useStyles } from '../ReportToggleList.styles';
|
||||||
|
|
||||||
const ReportToggleListHeader = ({
|
const ReportToggleListHeader = ({
|
||||||
handleCheckAll,
|
handleCheckAll,
|
||||||
checkAll,
|
checkAll,
|
||||||
setSortData,
|
setSort,
|
||||||
bulkActionsOn,
|
bulkActionsOn,
|
||||||
}) => {
|
}) => {
|
||||||
|
const styles = useStyles();
|
||||||
const handleSort = type => {
|
const handleSort = type => {
|
||||||
setSortData(prev => ({
|
setSort(prev => ({
|
||||||
sortKey: type,
|
type,
|
||||||
ascending: !prev.ascending,
|
desc: !prev.desc,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -94,7 +88,7 @@ const ReportToggleListHeader = ({
|
|||||||
<th
|
<th
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => handleSort(REPORT)}
|
onClick={() => handleSort(EXPIRED)}
|
||||||
>
|
>
|
||||||
Report
|
Report
|
||||||
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
<UnfoldMoreOutlinedIcon className={styles.sortIcon} />
|
||||||
@ -106,7 +100,7 @@ const ReportToggleListHeader = ({
|
|||||||
|
|
||||||
ReportToggleListHeader.propTypes = {
|
ReportToggleListHeader.propTypes = {
|
||||||
checkAll: PropTypes.bool.isRequired,
|
checkAll: PropTypes.bool.isRequired,
|
||||||
setSortData: PropTypes.func.isRequired,
|
setSort: PropTypes.func.isRequired,
|
||||||
bulkActionsOn: PropTypes.bool.isRequired,
|
bulkActionsOn: PropTypes.bool.isRequired,
|
||||||
handleCheckAll: PropTypes.func.isRequired,
|
handleCheckAll: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { Checkbox } from '@material-ui/core';
|
import { Checkbox } from '@material-ui/core';
|
||||||
import CheckIcon from '@material-ui/icons/Check';
|
import CheckIcon from '@material-ui/icons/Check';
|
||||||
@ -21,7 +21,7 @@ import {
|
|||||||
PERMISSION,
|
PERMISSION,
|
||||||
} from '../../../../constants/featureToggleTypes';
|
} from '../../../../constants/featureToggleTypes';
|
||||||
|
|
||||||
import styles from '../ReportToggleList.module.scss';
|
import { useStyles } from '../ReportToggleList.styles';
|
||||||
import { getTogglePath } from '../../../../utils/routePathHelpers';
|
import { getTogglePath } from '../../../../utils/routePathHelpers';
|
||||||
|
|
||||||
const ReportToggleListItem = ({
|
const ReportToggleListItem = ({
|
||||||
@ -35,8 +35,8 @@ const ReportToggleListItem = ({
|
|||||||
bulkActionsOn,
|
bulkActionsOn,
|
||||||
setFeatures,
|
setFeatures,
|
||||||
}) => {
|
}) => {
|
||||||
|
const styles = useStyles();
|
||||||
const nameMatches = feature => feature.name === name;
|
const nameMatches = feature => feature.name === name;
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const handleChange = () => {
|
const handleChange = () => {
|
||||||
setFeatures(prevState => {
|
setFeatures(prevState => {
|
||||||
@ -116,21 +116,12 @@ const ReportToggleListItem = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const navigateToFeature = () => {
|
|
||||||
history.push(getTogglePath(project, name));
|
|
||||||
};
|
|
||||||
|
|
||||||
const statusClasses = classnames(styles.active, styles.hideColumnStatus, {
|
const statusClasses = classnames(styles.active, styles.hideColumnStatus, {
|
||||||
[styles.stale]: stale,
|
[styles.stale]: stale,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr
|
<tr className={styles.tableRow}>
|
||||||
role="button"
|
|
||||||
tabIndex={0}
|
|
||||||
onClick={navigateToFeature}
|
|
||||||
className={styles.tableRow}
|
|
||||||
>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={bulkActionsOn}
|
condition={bulkActionsOn}
|
||||||
show={
|
show={
|
||||||
@ -144,7 +135,11 @@ const ReportToggleListItem = ({
|
|||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<td>{name}</td>
|
<td>
|
||||||
|
<Link to={getTogglePath(project, name)} className={styles.link}>
|
||||||
|
{name}
|
||||||
|
</Link>
|
||||||
|
</td>
|
||||||
<td className={styles.hideColumnLastSeen}>{formatLastSeenAt()}</td>
|
<td className={styles.hideColumnLastSeen}>{formatLastSeenAt()}</td>
|
||||||
<td className={styles.hideColumn}>{formatCreatedAt()}</td>
|
<td className={styles.hideColumn}>{formatCreatedAt()}</td>
|
||||||
<td className={`${styles.expired} ${styles.hideColumn}`}>
|
<td className={`${styles.expired} ${styles.hideColumn}`}>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* SORT TYPES */
|
/* SORT TYPES */
|
||||||
export const NAME = 'name';
|
export const NAME = 'name';
|
||||||
export const LAST_SEEN = 'lastSeen';
|
export const LAST_SEEN = 'last-seen';
|
||||||
export const CREATED = 'created';
|
export const CREATED = 'created';
|
||||||
export const EXPIRED = 'expired';
|
export const EXPIRED = 'expired';
|
||||||
export const STATUS = 'status';
|
export const STATUS = 'status';
|
||||||
|
@ -16,5 +16,6 @@ export const useStyles = makeStyles(theme => ({
|
|||||||
'& > *': {
|
'& > *': {
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
},
|
},
|
||||||
|
color: theme.palette.primary.main,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -2,7 +2,6 @@ import { makeStyles } from '@material-ui/core/styles';
|
|||||||
|
|
||||||
export const useStyles = makeStyles(theme => ({
|
export const useStyles = makeStyles(theme => ({
|
||||||
tableRow: {
|
tableRow: {
|
||||||
cursor: 'pointer',
|
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: theme.palette.grey[200],
|
backgroundColor: theme.palette.grey[200],
|
||||||
},
|
},
|
||||||
@ -46,6 +45,7 @@ export const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
color: 'inherit',
|
color: theme.palette.primary.main,
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -9,15 +9,15 @@ import {
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { useStyles } from './FeatureToggleListNew.styles';
|
import { useStyles } from './FeatureToggleListNew.styles';
|
||||||
import FeatureToggleListNewItem from './FeatureToggleListNewItem/FeatureToggleListNewItem';
|
import FeatureToggleListNewItem from './FeatureToggleListNewItem/FeatureToggleListNewItem';
|
||||||
import usePagination from '../../../hooks/usePagination';
|
import usePagination from 'hooks/usePagination';
|
||||||
|
|
||||||
import loadingFeatures from './FeatureToggleListNewItem/loadingFeatures';
|
import loadingFeatures from './FeatureToggleListNewItem/loadingFeatures';
|
||||||
import {
|
import {
|
||||||
IFeatureToggle,
|
IFeatureToggle,
|
||||||
IFeatureToggleListItem,
|
IFeatureToggleListItem,
|
||||||
} from '../../../interfaces/featureToggle';
|
} from 'interfaces/featureToggle';
|
||||||
import PaginateUI from '../../common/PaginateUI/PaginateUI';
|
import PaginateUI from 'component/common/PaginateUI/PaginateUI';
|
||||||
import StringTruncator from '../../common/StringTruncator/StringTruncator';
|
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||||||
|
import { createGlobalStateHook } from 'hooks/useGlobalState';
|
||||||
interface IFeatureToggleListNewProps {
|
interface IFeatureToggleListNewProps {
|
||||||
features: IFeatureToggleListItem[];
|
features: IFeatureToggleListItem[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@ -66,17 +66,24 @@ const sortList = (list, sortOpt) => {
|
|||||||
return list;
|
return list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface ISortedState {
|
||||||
|
field: string;
|
||||||
|
type: string;
|
||||||
|
direction: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useFeatureToggLeProjectSort = createGlobalStateHook<ISortedState>(
|
||||||
|
'useFeatureToggLeProjectSort',
|
||||||
|
{ field: 'name', type: 'string', direction: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
const FeatureToggleListNew = ({
|
const FeatureToggleListNew = ({
|
||||||
features,
|
features,
|
||||||
loading,
|
loading,
|
||||||
projectId,
|
projectId,
|
||||||
}: IFeatureToggleListNewProps) => {
|
}: IFeatureToggleListNewProps) => {
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const [sortOpt, setSortOpt] = useState({
|
const [sortOpt, setSortOpt] = useFeatureToggLeProjectSort();
|
||||||
field: 'name',
|
|
||||||
type: 'string',
|
|
||||||
direction: 0,
|
|
||||||
});
|
|
||||||
const [sortedFeatures, setSortedFeatures] = useState(
|
const [sortedFeatures, setSortedFeatures] = useState(
|
||||||
sortList([...features], sortOpt)
|
sortList([...features], sortOpt)
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import React, { useRef, useState } from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import { TableCell, TableRow } from '@material-ui/core';
|
import { TableCell, TableRow } from '@material-ui/core';
|
||||||
import { useHistory } from 'react-router';
|
|
||||||
import { useStyles } from '../FeatureToggleListNew.styles';
|
import { useStyles } from '../FeatureToggleListNew.styles';
|
||||||
import useToggleFeatureByEnv from '../../../../hooks/api/actions/useToggleFeatureByEnv/useToggleFeatureByEnv';
|
import useToggleFeatureByEnv from '../../../../hooks/api/actions/useToggleFeatureByEnv/useToggleFeatureByEnv';
|
||||||
import { IEnvironments } from '../../../../interfaces/featureToggle';
|
import { IEnvironments } from '../../../../interfaces/featureToggle';
|
||||||
import useToast from '../../../../hooks/useToast';
|
import useToast from '../../../../hooks/useToast';
|
||||||
import { getTogglePath } from 'utils/routePathHelpers';
|
import { getTogglePath } from 'utils/routePathHelpers';
|
||||||
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
|
||||||
import FeatureStatus from '../../FeatureView/FeatureStatus/FeatureStatus';
|
import FeatureStatus from '../../FeatureView/FeatureStatus/FeatureStatus';
|
||||||
import FeatureType from '../../FeatureView/FeatureType/FeatureType';
|
import FeatureType from '../../FeatureView/FeatureType/FeatureType';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
@ -41,10 +39,8 @@ const FeatureToggleListNewItem = ({
|
|||||||
name
|
name
|
||||||
);
|
);
|
||||||
|
|
||||||
const { uiConfig } = useUiConfig();
|
|
||||||
const { refetch } = useProject(projectId);
|
const { refetch } = useProject(projectId);
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const history = useHistory();
|
|
||||||
const ref = useRef<HTMLButtonElement>(null);
|
const ref = useRef<HTMLButtonElement>(null);
|
||||||
const [showInfoBox, setShowInfoBox] = useState(false);
|
const [showInfoBox, setShowInfoBox] = useState(false);
|
||||||
const [environmentName, setEnvironmentName] = useState('');
|
const [environmentName, setEnvironmentName] = useState('');
|
||||||
@ -53,12 +49,6 @@ const FeatureToggleListNewItem = ({
|
|||||||
setShowInfoBox(false);
|
setShowInfoBox(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClick = (e: React.MouseEvent) => {
|
|
||||||
if (!ref.current?.contains(e.target as Node)) {
|
|
||||||
history.push(getTogglePath(projectId, name));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleToggle = (env: IEnvironments) => {
|
const handleToggle = (env: IEnvironments) => {
|
||||||
toggleFeatureByEnvironment(env.name, env.enabled)
|
toggleFeatureByEnvironment(env.name, env.enabled)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -87,7 +77,6 @@ const FeatureToggleListNewItem = ({
|
|||||||
styles.tableCellStatus
|
styles.tableCellStatus
|
||||||
)}
|
)}
|
||||||
align="left"
|
align="left"
|
||||||
onClick={onClick}
|
|
||||||
>
|
>
|
||||||
<FeatureStatus
|
<FeatureStatus
|
||||||
lastSeenAt={lastSeenAt}
|
lastSeenAt={lastSeenAt}
|
||||||
@ -100,7 +89,6 @@ const FeatureToggleListNewItem = ({
|
|||||||
styles.tableCellType
|
styles.tableCellType
|
||||||
)}
|
)}
|
||||||
align="center"
|
align="center"
|
||||||
onClick={onClick}
|
|
||||||
>
|
>
|
||||||
<FeatureType type={type} />
|
<FeatureType type={type} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@ -110,11 +98,9 @@ const FeatureToggleListNewItem = ({
|
|||||||
styles.tableCellName
|
styles.tableCellName
|
||||||
)}
|
)}
|
||||||
align="left"
|
align="left"
|
||||||
onClick={onClick}
|
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
// @ts-expect-error
|
to={getTogglePath(projectId, name)}
|
||||||
to={getTogglePath(projectId, name, uiConfig.flags.E)}
|
|
||||||
className={styles.link}
|
className={styles.link}
|
||||||
>
|
>
|
||||||
<span data-loading>{name}</span>
|
<span data-loading>{name}</span>
|
||||||
@ -126,7 +112,6 @@ const FeatureToggleListNewItem = ({
|
|||||||
styles.tableCellCreated
|
styles.tableCellCreated
|
||||||
)}
|
)}
|
||||||
align="left"
|
align="left"
|
||||||
onClick={onClick}
|
|
||||||
>
|
>
|
||||||
<CreatedAt time={createdAt} />
|
<CreatedAt time={createdAt} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -40,6 +40,7 @@ export const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
|
color: theme.palette.primary.main,
|
||||||
},
|
},
|
||||||
actionsContainer: {
|
actionsContainer: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -2,18 +2,27 @@ import { IFeatureToggle } from '../interfaces/featureToggle';
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { getBasePath } from 'utils/formatPath';
|
import { getBasePath } from 'utils/formatPath';
|
||||||
import { createPersistentGlobalStateHook } from './usePersistentGlobalState';
|
import { createPersistentGlobalStateHook } from './usePersistentGlobalState';
|
||||||
|
import { parseISO } from 'date-fns';
|
||||||
|
import {
|
||||||
|
expired,
|
||||||
|
getDiffInDays,
|
||||||
|
toggleExpiryByTypeMap,
|
||||||
|
} from 'component/Reporting/utils';
|
||||||
|
|
||||||
type FeaturesSortType =
|
type FeaturesSortType =
|
||||||
| 'name'
|
| 'name'
|
||||||
|
| 'expired'
|
||||||
| 'type'
|
| 'type'
|
||||||
| 'enabled'
|
| 'enabled'
|
||||||
| 'stale'
|
| 'stale'
|
||||||
| 'created'
|
| 'created'
|
||||||
| 'last-seen'
|
| 'last-seen'
|
||||||
|
| 'status'
|
||||||
| 'project';
|
| 'project';
|
||||||
|
|
||||||
interface IFeaturesSort {
|
interface IFeaturesSort {
|
||||||
type: FeaturesSortType;
|
type: FeaturesSortType;
|
||||||
|
desc?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFeaturesSortOutput {
|
export interface IFeaturesSortOutput {
|
||||||
@ -63,7 +72,7 @@ export const createFeaturesFilterSortOptions =
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
const sortFeatures = (
|
const sortAscendingFeatures = (
|
||||||
features: IFeatureToggle[],
|
features: IFeatureToggle[],
|
||||||
sort: IFeaturesSort
|
sort: IFeaturesSort
|
||||||
): IFeatureToggle[] => {
|
): IFeatureToggle[] => {
|
||||||
@ -82,12 +91,29 @@ const sortFeatures = (
|
|||||||
return sortByProject(features);
|
return sortByProject(features);
|
||||||
case 'type':
|
case 'type':
|
||||||
return sortByType(features);
|
return sortByType(features);
|
||||||
|
case 'expired':
|
||||||
|
return sortByExpired(features);
|
||||||
|
case 'status':
|
||||||
|
return sortByStatus(features);
|
||||||
default:
|
default:
|
||||||
console.error(`Unknown feature sort type: ${sort.type}`);
|
console.error(`Unknown feature sort type: ${sort.type}`);
|
||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sortFeatures = (
|
||||||
|
features: IFeatureToggle[],
|
||||||
|
sort: IFeaturesSort
|
||||||
|
): IFeatureToggle[] => {
|
||||||
|
const sorted = sortAscendingFeatures(features, sort);
|
||||||
|
|
||||||
|
if (sort.desc) {
|
||||||
|
return [...sorted].reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
};
|
||||||
|
|
||||||
const sortByEnabled = (
|
const sortByEnabled = (
|
||||||
features: Readonly<IFeatureToggle[]>
|
features: Readonly<IFeatureToggle[]>
|
||||||
): IFeatureToggle[] => {
|
): IFeatureToggle[] => {
|
||||||
@ -137,3 +163,43 @@ const sortByProject = (
|
|||||||
const sortByType = (features: Readonly<IFeatureToggle[]>): IFeatureToggle[] => {
|
const sortByType = (features: Readonly<IFeatureToggle[]>): IFeatureToggle[] => {
|
||||||
return [...features].sort((a, b) => a.type.localeCompare(b.type));
|
return [...features].sort((a, b) => a.type.localeCompare(b.type));
|
||||||
};
|
};
|
||||||
|
const sortByExpired = (
|
||||||
|
features: Readonly<IFeatureToggle[]>
|
||||||
|
): IFeatureToggle[] => {
|
||||||
|
return [...features].sort((a, b) => {
|
||||||
|
const now = new Date();
|
||||||
|
const dateA = parseISO(a.createdAt);
|
||||||
|
const dateB = parseISO(b.createdAt);
|
||||||
|
|
||||||
|
const diffA = getDiffInDays(dateA, now);
|
||||||
|
const diffB = getDiffInDays(dateB, now);
|
||||||
|
|
||||||
|
if (!expired(diffA, a.type) && expired(diffB, b.type)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expired(diffA, a.type) && !expired(diffB, b.type)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expiration = toggleExpiryByTypeMap as Record<string, number>;
|
||||||
|
const expiredByA = diffA - expiration[a.type];
|
||||||
|
const expiredByB = diffB - expiration[b.type];
|
||||||
|
|
||||||
|
return expiredByB - expiredByA;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortByStatus = (
|
||||||
|
features: Readonly<IFeatureToggle[]>
|
||||||
|
): IFeatureToggle[] => {
|
||||||
|
return [...features].sort((a, b) => {
|
||||||
|
if (a.stale) {
|
||||||
|
return 1;
|
||||||
|
} else if (b.stale) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user