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:
parent
960801b8aa
commit
e1034a458b
@ -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',
|
||||
|
@ -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>
|
||||
);
|
||||
|
90
frontend/src/component/project/ViewProject/ViewProject.js
Normal file
90
frontend/src/component/project/ViewProject/ViewProject.js
Normal 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;
|
39
frontend/src/component/project/ViewProject/index.js
Normal file
39
frontend/src/component/project/ViewProject/index.js
Normal 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;
|
@ -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,
|
||||
|
14
frontend/src/page/project/view.js
Normal file
14
frontend/src/page/project/view.js
Normal 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;
|
Loading…
Reference in New Issue
Block a user