mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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