1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: simple project view (#295)

Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com>
This commit is contained in:
Ivar Conradi Østhus 2021-05-18 12:13:52 +02:00 committed by GitHub
parent 960801b8aa
commit e1034a458b
6 changed files with 175 additions and 6 deletions

View File

@ -18,6 +18,7 @@ import LogoutFeatures from '../../page/user/logout';
import ListProjects from '../../page/project';
import CreateProject from '../../page/project/create';
import EditProject from '../../page/project/edit';
import ViewProject from '../../page/project/view';
import EditProjectAccess from '../../page/project/access';
import ListTagTypes from '../../page/tag-types';
import CreateTagType from '../../page/tag-types/create';
@ -197,6 +198,14 @@ export const routes = [
type: 'protected',
layout: 'main',
},
{
path: '/projects/view/:id',
parent: '/projects',
title: ':id',
component: ViewProject,
type: 'protected',
layout: 'main',
},
{
path: '/projects/:id/access',
parent: '/projects',

View File

@ -48,7 +48,7 @@ const ProjectList = ({ projects, fetchProjects, removeProject, history }) => {
);
const projectLink = ({ id, name }) => (
<Link to={`/projects/edit/${id}`}>
<Link to={`/projects/view/${id}`}>
<strong>{name}</strong>
</Link>
);

View File

@ -0,0 +1,90 @@
import { useContext, useEffect } from 'react';
import { Typography, Button, List } from '@material-ui/core';
import { Link } from 'react-router-dom';
import AccessContext from '../../../contexts/AccessContext';
import HeaderTitle from '../../common/HeaderTitle';
import PageContent from '../../common/PageContent';
import FeatureToggleListItem from '../../feature/FeatureToggleList/FeatureToggleListItem';
import ConditionallyRender from '../../common/ConditionallyRender';
const ViewProject = ({
project,
features,
settings,
toggleFeature,
featureMetrics,
revive,
fetchFeatureToggles,
}) => {
const { hasAccess } = useContext(AccessContext);
useEffect(() => {
fetchFeatureToggles();
/* eslint-disable-next-line */
}, []);
const renderProjectFeatures = () => {
return features.map(feature => {
return (
<FeatureToggleListItem
key={feature.name}
settings={settings}
metricsLastHour={featureMetrics.lastHour[feature.name]}
metricsLastMinute={featureMetrics.lastMinute[feature.name]}
feature={feature}
toggleFeature={toggleFeature}
revive={revive}
hasAccess={hasAccess}
/>
);
});
};
return (
<div>
<PageContent
headerContent={
<HeaderTitle
title={`${project.name}`}
actions={
<>
<Button
component={Link}
to={`/projects/edit/${project.id}`}
>
Edit
</Button>
<Button
component={Link}
to={`/projects/${project.id}/access`}
>
Manage access
</Button>
</>
}
/>
}
>
<ConditionallyRender
condition={project.description}
show={
<div style={{ marginBottom: '2rem' }}>
<Typography variant="subtitle2">
Description
</Typography>
<Typography>{project.description}</Typography>
</div>
}
/>
<Typography variant="subtitle2">
Feature toggles in this project
</Typography>
<List>{renderProjectFeatures()}</List>
</PageContent>
</div>
);
};
export default ViewProject;

View File

@ -0,0 +1,39 @@
import { connect } from 'react-redux';
import {
fetchFeatureToggles,
toggleFeature,
} from '../../../store/feature-toggle/actions';
import ViewProject from './ViewProject';
const mapStateToProps = (state, props) => {
const projectBase = { id: '', name: '', description: '' };
const realProject = state.projects
.toJS()
.find(n => n.id === props.projectId);
const project = Object.assign(projectBase, realProject);
const features = state.features
.toJS()
.filter(feature => feature.project === project.id);
const settings = state.settings.toJS();
const featureMetrics = state.featureMetrics.toJS();
return {
project,
features,
settings,
featureMetrics,
};
};
const mapDispatchToProps = {
toggleFeature,
fetchFeatureToggles,
};
const FormAddContainer = connect(
mapStateToProps,
mapDispatchToProps
)(ViewProject);
export default FormAddContainer;

View File

@ -24,7 +24,9 @@ function AddUserComponent({ roles, addUserToRole }) {
useEffect(() => {
if (roles.length > 0) {
const regularRole = roles.find(r => r.name.toLowerCase() === 'regular');
const regularRole = roles.find(
r => r.name.toLowerCase() === 'regular'
);
setRole(regularRole || roles[0]);
}
}, [roles]);
@ -80,7 +82,9 @@ function AddUserComponent({ roles, addUserToRole }) {
filterOptions={o => o}
getOptionLabel={option => {
if (option) {
return `${option.name || '(Empty name)'} <${option.email || option.username}>`;
return `${option.name || '(Empty name)'} <${
option.email || option.username
}>`;
} else return '';
}}
options={options}
@ -100,7 +104,12 @@ function AddUserComponent({ roles, addUserToRole }) {
),
endAdornment: (
<React.Fragment>
{loading ? <CircularProgress color="inherit" size={20} /> : null}
{loading ? (
<CircularProgress
color="inherit"
size={20}
/>
) : null}
{params.InputProps.endAdornment}
</React.Fragment>
),
@ -111,7 +120,9 @@ function AddUserComponent({ roles, addUserToRole }) {
</Grid>
<Grid item>
<FormControl>
<InputLabel id="add-user-select-role-label">Role</InputLabel>
<InputLabel id="add-user-select-role-label">
Role
</InputLabel>
<Select
labelId="add-user-select-role-label"
id="add-user-select-role"
@ -128,13 +139,19 @@ function AddUserComponent({ roles, addUserToRole }) {
</FormControl>
</Grid>
<Grid item>
<Button variant="contained" color="primary" disabled={!user} onClick={handleSubmit}>
<Button
variant="contained"
color="primary"
disabled={!user}
onClick={handleSubmit}
>
Add user
</Button>
</Grid>
</Grid>
);
}
AddUserComponent.propTypes = {
roles: PropTypes.array.isRequired,
addUserToRole: PropTypes.func.isRequired,

View File

@ -0,0 +1,14 @@
import React from 'react';
import ViewProject from '../../component/project/ViewProject';
import PropTypes from 'prop-types';
const render = ({ match: { params }, history }) => (
<ViewProject projectId={params.id} title="View project" history={history} />
);
render.propTypes = {
match: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
export default render;