diff --git a/frontend/src/component/common/DisabledIndicator/DisabledIndicator.styles.ts b/frontend/src/component/common/DisabledIndicator/DisabledIndicator.styles.ts
new file mode 100644
index 0000000000..844b84e620
--- /dev/null
+++ b/frontend/src/component/common/DisabledIndicator/DisabledIndicator.styles.ts
@@ -0,0 +1,13 @@
+import { makeStyles } from '@material-ui/core/styles';
+
+export const useStyles = makeStyles(theme => ({
+ indicator: {
+ padding: '0.2rem',
+ borderRadius: '5px',
+ marginLeft: '0.5rem',
+ backgroundColor: '#000',
+ color: '#fff',
+ fontSize: '0.9rem',
+ fontWeight: 'bold',
+ },
+}));
diff --git a/frontend/src/component/common/DisabledIndicator/DisabledIndicator.tsx b/frontend/src/component/common/DisabledIndicator/DisabledIndicator.tsx
new file mode 100644
index 0000000000..1de3b301bd
--- /dev/null
+++ b/frontend/src/component/common/DisabledIndicator/DisabledIndicator.tsx
@@ -0,0 +1,16 @@
+import { useStyles } from './DisabledIndicator.styles';
+import classnames from 'classnames';
+interface IDisabledIndicator {
+ className?: string;
+}
+
+const DisabledIndicator = ({ className }: IDisabledIndicator) => {
+ const styles = useStyles();
+ return (
+
+ disabled
+
+ );
+};
+
+export default DisabledIndicator;
diff --git a/frontend/src/component/common/RolloutIcon/RolloutIcon.styles.ts b/frontend/src/component/common/RolloutIcon/RolloutIcon.styles.ts
new file mode 100644
index 0000000000..21b4e55997
--- /dev/null
+++ b/frontend/src/component/common/RolloutIcon/RolloutIcon.styles.ts
@@ -0,0 +1,26 @@
+import { makeStyles } from '@material-ui/core/styles';
+
+export const useStyles = makeStyles(theme => ({
+ container: {
+ display: 'flex',
+ alignItems: 'center',
+ position: 'relative',
+ width: '50px',
+ height: '100%',
+ },
+ vertical: {
+ borderRadius: '1px',
+ height: '50px',
+ width: '50px',
+ },
+ circle: {
+ width: '15px',
+ height: '15px',
+ },
+ pos: {
+ position: 'absolute',
+ right: 0,
+ left: 0,
+ margin: '0 auto',
+ },
+}));
diff --git a/frontend/src/component/common/RolloutIcon/RolloutIcon.tsx b/frontend/src/component/common/RolloutIcon/RolloutIcon.tsx
new file mode 100644
index 0000000000..3f117da9ba
--- /dev/null
+++ b/frontend/src/component/common/RolloutIcon/RolloutIcon.tsx
@@ -0,0 +1,24 @@
+import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
+import Remove from '@material-ui/icons/Remove';
+import { useStyles } from './RolloutIcon.styles';
+import classnames from 'classnames';
+
+interface IRolloutIconProps {
+ className?: string;
+}
+
+const RolloutIcon = ({ className }: IRolloutIconProps) => {
+ const styles = useStyles();
+ return (
+
+
+
+
+ );
+};
+
+export default RolloutIcon;
diff --git a/frontend/src/component/environments/EnvironmentList/EnvironmentListItem/EnvironmentListItem.tsx b/frontend/src/component/environments/EnvironmentList/EnvironmentListItem/EnvironmentListItem.tsx
index bd177ebdef..4aca2ad472 100644
--- a/frontend/src/component/environments/EnvironmentList/EnvironmentListItem/EnvironmentListItem.tsx
+++ b/frontend/src/component/environments/EnvironmentList/EnvironmentListItem/EnvironmentListItem.tsx
@@ -23,6 +23,7 @@ import {
} from '../../../providers/AccessProvider/permissions';
import { useDrag, useDrop, DropTargetMonitor } from 'react-dnd';
import { XYCoord } from 'dnd-core';
+import DisabledIndicator from '../../../common/DisabledIndicator/DisabledIndicator';
interface IEnvironmentListItemProps {
env: IEnvironment;
@@ -118,7 +119,7 @@ const EnvironmentListItem = ({
if (updatePermission) {
drag(drop(ref));
}
-
+
return (
{env.name}
- disabled
-
- }
+ show={}
/>
>
}
diff --git a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.styles.ts b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.styles.ts
index 858b683234..cd7b318748 100644
--- a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.styles.ts
+++ b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.styles.ts
@@ -21,7 +21,7 @@ export const useStyles = makeStyles(theme => ({
cursor: 'pointer',
},
tableCellStatus: {
- width: '50px',
+ width: '60px',
},
tableCellName: {
paddingLeft: '10px',
@@ -44,9 +44,9 @@ export const useStyles = makeStyles(theme => ({
display: 'none',
},
},
- link:{
+ link: {
textDecoration: 'none',
- color: 'inherit'
+ color: 'inherit',
},
envName: {
display: 'inline-block',
@@ -54,5 +54,5 @@ export const useStyles = makeStyles(theme => ({
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
- }
+ },
}));
diff --git a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.tsx b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.tsx
index 5f2a612c10..58c5546e94 100644
--- a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.tsx
+++ b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNew.tsx
@@ -1,4 +1,4 @@
-import { useState, useEffect} from 'react';
+import { useState, useEffect } from 'react';
import {
Table,
TableBody,
@@ -25,12 +25,12 @@ interface IFeatureToggleListNewProps {
//@ts-ignore
const sortList = (list, sortOpt) => {
- if(!list) {
+ if (!list) {
return list;
}
- if(!sortOpt.field) {
+ if (!sortOpt.field) {
return list;
- }
+ }
if (sortOpt.type === 'string') {
//@ts-ignore
return list.sort((a, b) => {
@@ -45,7 +45,7 @@ const sortList = (list, sortOpt) => {
return direction === 0 ? 1 : -1;
}
return 0;
- })
+ });
}
if (sortOpt.type === 'date') {
//@ts-ignore
@@ -60,10 +60,10 @@ const sortList = (list, sortOpt) => {
return sortOpt.direction === 0 ? -1 : 1;
}
return 0;
- })
+ });
}
return list;
-}
+};
const FeatureToggleListNew = ({
features,
@@ -76,32 +76,33 @@ const FeatureToggleListNew = ({
type: 'string',
direction: 0,
});
- const [sortedFeatures, setSortedFeatures] = useState(sortList([...features], sortOpt));
-
+ const [sortedFeatures, setSortedFeatures] = useState(
+ sortList([...features], sortOpt)
+ );
+
const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } =
usePagination(sortedFeatures, 50);
-
+
useEffect(() => {
- setSortedFeatures(sortList([...features], sortOpt))
+ setSortedFeatures(sortList([...features], sortOpt));
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [features])
+ }, [features]);
const updateSort = (field: string) => {
let newSortOpt;
- if(field === sortOpt.field) {
- newSortOpt = {...sortOpt, direction: (sortOpt.direction + 1) % 2};
- }
- else if(['createdAt', 'lastSeenAt'].includes(field)) {
+ if (field === sortOpt.field) {
+ newSortOpt = { ...sortOpt, direction: (sortOpt.direction + 1) % 2 };
+ } else if (['createdAt', 'lastSeenAt'].includes(field)) {
newSortOpt = {
field,
type: 'date',
- direction: 0
+ direction: 0,
};
} else {
newSortOpt = {
field,
type: 'string',
- direction: 0
+ direction: 0,
};
}
setSortOpt(newSortOpt);
@@ -164,44 +165,64 @@ const FeatureToggleListNew = ({
styles.tableCell,
styles.tableCellStatus,
styles.tableCellHeader,
- styles.tableCellHeaderSortable,
+ styles.tableCellHeaderSortable
)}
align="left"
>
- updateSort('lastSeenAt')}>Status
+ updateSort('lastSeenAt')}
+ >
+ Last use
+
- updateSort('type')}>Type
+ updateSort('type')}
+ >
+ Type
+
- updateSort('name')}>Name
+ updateSort('name')}
+ >
+ Name
+
- updateSort('createdAt')}>Created
+ updateSort('createdAt')}
+ >
+ Created
+
{getEnvironments().map((env: any) => {
return (
@@ -211,12 +232,15 @@ const FeatureToggleListNew = ({
styles.tableCell,
styles.tableCellEnv,
styles.tableCellHeader,
- styles.tableCellHeaderSortable,
+ styles.tableCellHeaderSortable
)}
align="center"
>
-
- {env.name}
+
+ {env.name}
);
diff --git a/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.styles.ts b/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.styles.ts
index e37c0cc22f..4874c05e0e 100644
--- a/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.styles.ts
+++ b/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.styles.ts
@@ -23,6 +23,11 @@ export const useStyles = makeStyles(theme => ({
display: 'flex',
alignItems: 'center',
},
+ disabledIndicatorPos: {
+ position: 'absolute',
+ top: '15px',
+ left: '20px',
+ },
iconContainer: {
backgroundColor: theme.palette.primary.light,
borderRadius: '50%',
@@ -118,6 +123,9 @@ export const useStyles = makeStyles(theme => ({
},
},
[theme.breakpoints.down(560)]: {
+ disabledIndicatorPos: {
+ top: '-8px',
+ },
headerTitle: {
flexDirection: 'column',
},
diff --git a/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx b/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx
index e2af79531e..cc07d9222b 100644
--- a/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx
+++ b/frontend/src/component/feature/FeatureView2/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx
@@ -10,6 +10,8 @@ import useFeatureMetrics from '../../../../../../hooks/api/getters/useFeatureMet
import { IFeatureEnvironment } from '../../../../../../interfaces/featureToggle';
import { IFeatureViewParams } from '../../../../../../interfaces/params';
import { getFeatureMetrics } from '../../../../../../utils/get-feature-metrics';
+import ConditionallyRender from '../../../../../common/ConditionallyRender';
+import DisabledIndicator from '../../../../../common/DisabledIndicator/DisabledIndicator';
import EnvironmentIcon from '../../../../../common/EnvironmentIcon/EnvironmentIcon';
import StringTruncator from '../../../../../common/StringTruncator/StringTruncator';
@@ -64,6 +66,14 @@ const FeatureOverviewEnvironment = ({
className={styles.truncator}
maxWidth="120"
/>
+
+ }
+ />
({
},
icon: {
fill: theme.palette.grey[600],
- marginRight: '0.5rem',
},
editStrategy: {
marginLeft: 'auto',
diff --git a/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordion.styles.ts b/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordion.styles.ts
index 7d98dc58a1..b3ab26e44d 100644
--- a/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordion.styles.ts
+++ b/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordion.styles.ts
@@ -40,9 +40,8 @@ export const useStyles = makeStyles(theme => ({
marginLeft: 'auto',
},
icon: {
- marginRight: '0.5rem',
fill: theme.palette.primary.main,
- minWidth: '35px',
+ minWidth: '50px',
},
rollout: {
fontSize: theme.fontSizes.smallBody,
diff --git a/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordion.tsx b/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordion.tsx
index a6b222a65a..06796ac9c2 100644
--- a/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordion.tsx
+++ b/frontend/src/component/feature/FeatureView2/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordion.tsx
@@ -65,6 +65,7 @@ const FeatureStrategyAccordion: React.FC = ({
>
+
{strategyName}
({
borderRadius: '10px',
marginBottom: '1rem',
},
- innerContainer: { padding: '2rem' },
+ innerContainer: {
+ padding: '1rem 2rem',
+ display: 'flex',
+ alignItems: 'center',
+ },
separator: {
width: '100%',
backgroundColor: theme.palette.grey[200],
diff --git a/frontend/src/component/project/Project/Project.tsx b/frontend/src/component/project/Project/Project.tsx
index b97e04c4f0..de9a6ea313 100644
--- a/frontend/src/component/project/Project/Project.tsx
+++ b/frontend/src/component/project/Project/Project.tsx
@@ -5,7 +5,7 @@ import useLoading from '../../../hooks/useLoading';
import ApiError from '../../common/ApiError/ApiError';
import ConditionallyRender from '../../common/ConditionallyRender';
import { useStyles } from './Project.styles';
-import { IconButton, Tab, Tabs } from '@material-ui/core';
+import { Tab, Tabs } from '@material-ui/core';
import { Edit } from '@material-ui/icons';
import useToast from '../../../hooks/useToast';
import useQueryParams from '../../../hooks/useQueryParams';
@@ -17,9 +17,11 @@ import EditProject from '../edit-project-container';
import ProjectEnvironment from '../ProjectEnvironment/ProjectEnvironment';
import ProjectOverview from './ProjectOverview';
import ProjectHealth from './ProjectHealth/ProjectHealth';
+import { UPDATE_PROJECT } from '../../../store/project/actions';
+import PermissionIconButton from '../../common/PermissionIconButton/PermissionIconButton';
const Project = () => {
- const { id, activeTab } = useParams<{ id: string, activeTab: string }>();
+ const { id, activeTab } = useParams<{ id: string; activeTab: string }>();
const params = useQueryParams();
const { project, error, loading, refetch } = useProject(id);
const ref = useLoading(loading);
@@ -52,18 +54,24 @@ const Project = () => {
},
{
title: 'Environments',
- component: ,
+ component: ,
path: `${basePath}/environments`,
name: 'environments',
},
{
title: 'Settings',
// @ts-ignore (fix later)
- component: ,
+ component: (
+
+ ),
path: `${basePath}/settings`,
name: 'settings',
},
- ]
+ ];
useEffect(() => {
const created = params.get('created');
@@ -85,32 +93,29 @@ const Project = () => {
useEffect(() => {
const tabIdx = tabData.findIndex(tab => tab.name === activeTab);
- if(tabIdx > 0) {
+ if (tabIdx > 0) {
setActiveTab(tabIdx);
} else {
setActiveTab(0);
}
-
+
/* eslint-disable-next-line */
}, []);
const goToTabWithName = (name: string) => {
const index = tabData.findIndex(t => t.name === name);
- if(index >= 0) {
+ if (index >= 0) {
const tab = tabData[index];
history.push(tab.path);
setActiveTab(index);
}
- }
-
-
-
+ };
const renderTabs = () => {
return tabData.map((tab, index) => {
return (
{
});
};
-
return (
-
+
Project: {project?.name}{' '}
- goToTabWithName('settings')}>
+ goToTabWithName('settings')}
+ data-loading
+ >
-
+
-
{project?.description}
({
width: 'inherit',
},
},
+
bodyClass: { padding: '0.5rem 1rem' },
header: {
padding: '1rem',
diff --git a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeatureToggles.tsx b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeatureToggles.tsx
index 24570f6047..33e2bea70a 100644
--- a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeatureToggles.tsx
+++ b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeatureToggles.tsx
@@ -39,7 +39,7 @@ const ProjectFeatureToggles = ({
headerContent={
}
/>
-
+
history.push(
- getCreateTogglePath(id, uiConfig.flags.E)
+ getCreateTogglePath(
+ id,
+ uiConfig.flags.E
+ )
)
}
maxWidth="700px"
@@ -93,7 +96,10 @@ const ProjectFeatureToggles = ({
condition={hasAccess(CREATE_FEATURE, id)}
show={
diff --git a/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.styles.ts b/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.styles.ts
index 9fb98fa658..02d6c2291e 100644
--- a/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.styles.ts
+++ b/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.styles.ts
@@ -14,6 +14,18 @@ export const useStyles = makeStyles(theme => ({
marginBottom: '1rem',
},
},
+ description: {
+ textAlign: 'left',
+ marginBottom: '0.5rem',
+ },
+ descriptionContainer: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ },
+ idContainer: {
+ display: 'flex',
+ width: '100%',
+ },
percentageContainer: {
display: 'flex',
justifyContent: 'center',
@@ -66,6 +78,9 @@ export const useStyles = makeStyles(theme => ({
color: '#635dc5',
marginLeft: '0.5rem',
},
+ permissionButtonShortDesc: {
+ transform: `translateY(-10px)`,
+ },
infoLink: {
textDecoration: 'none',
color: '#635dc5',
@@ -74,6 +89,15 @@ export const useStyles = makeStyles(theme => ({
bottom: '5px',
},
},
+ accordion: {
+ boxShadow: 'none',
+ textAlign: 'left',
+ },
+ accordionBody: { padding: '0' },
+ accordionActions: {
+ padding: '0',
+ justifyContent: 'flex-start',
+ },
linkText: {
[theme.breakpoints.down('sm')]: {
display: 'none',
diff --git a/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx b/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx
index 5529f3ab5c..ac3dcc20ff 100644
--- a/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx
+++ b/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx
@@ -2,23 +2,34 @@ import { useStyles } from './ProjectInfo.styles';
import { Link } from 'react-router-dom';
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
import classnames from 'classnames';
+import { Edit, ExpandMore } from '@material-ui/icons';
import { useCommonStyles } from '../../../../common.styles';
import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig';
import PercentageCircle from '../../../common/PercentageCircle/PercentageCircle';
+import PermissionIconButton from '../../../common/PermissionIconButton/PermissionIconButton';
+import { UPDATE_PROJECT } from '../../../../store/project/actions';
+import ConditionallyRender from '../../../common/ConditionallyRender';
+import {
+ Accordion,
+ AccordionActions,
+ AccordionDetails,
+ AccordionSummary,
+} from '@material-ui/core';
interface IProjectInfoProps {
id: string;
memberCount: number;
featureCount: number;
health: number;
+ description: string;
}
const ProjectInfo = ({
id,
memberCount,
- featureCount,
health,
+ description,
}: IProjectInfoProps) => {
const commonStyles = useCommonStyles();
const styles = useStyles();
@@ -30,9 +41,87 @@ const ProjectInfo = ({
link = `/projects/${id}/access`;
}
+ const LONG_DESCRIPTION = 100;
+
+ const permissionButtonClass = classnames({
+ [styles.permissionButtonShortDesc]:
+ description.length < LONG_DESCRIPTION,
+ });
+ const permissionButton = (
+
+
+
+ );
+
return (