diff options
Diffstat (limited to 'opendc-web')
16 files changed, 98 insertions, 159 deletions
diff --git a/opendc-web/opendc-web-ui/src/actions/modals/projects.js b/opendc-web/opendc-web-ui/src/actions/modals/projects.js deleted file mode 100644 index d1043cbb..00000000 --- a/opendc-web/opendc-web-ui/src/actions/modals/projects.js +++ /dev/null @@ -1,14 +0,0 @@ -export const OPEN_NEW_PROJECT_MODAL = 'OPEN_NEW_PROJECT_MODAL' -export const CLOSE_NEW_PROJECT_MODAL = 'CLOSE_PROJECT_MODAL' - -export function openNewProjectModal() { - return { - type: OPEN_NEW_PROJECT_MODAL, - } -} - -export function closeNewProjectModal() { - return { - type: CLOSE_NEW_PROJECT_MODAL, - } -} diff --git a/opendc-web/opendc-web-ui/src/actions/projects.js b/opendc-web/opendc-web-ui/src/actions/projects.js index add0f242..15158164 100644 --- a/opendc-web/opendc-web-ui/src/actions/projects.js +++ b/opendc-web/opendc-web-ui/src/actions/projects.js @@ -1,17 +1,9 @@ -export const SET_AUTH_VISIBILITY_FILTER = 'SET_AUTH_VISIBILITY_FILTER' export const ADD_PROJECT = 'ADD_PROJECT' export const ADD_PROJECT_SUCCEEDED = 'ADD_PROJECT_SUCCEEDED' export const DELETE_PROJECT = 'DELETE_PROJECT' export const DELETE_PROJECT_SUCCEEDED = 'DELETE_PROJECT_SUCCEEDED' export const OPEN_PROJECT_SUCCEEDED = 'OPEN_PROJECT_SUCCEEDED' -export function setAuthVisibilityFilter(filter) { - return { - type: SET_AUTH_VISIBILITY_FILTER, - filter, - } -} - export function addProject(name) { return (dispatch, getState) => { const { auth } = getState() diff --git a/opendc-web/opendc-web-ui/src/components/projects/FilterButton.js b/opendc-web/opendc-web-ui/src/components/projects/FilterButton.js deleted file mode 100644 index 664f9b46..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/FilterButton.js +++ /dev/null @@ -1,24 +0,0 @@ -import classNames from 'classnames' -import PropTypes from 'prop-types' -import React from 'react' - -const FilterButton = ({ active, children, onClick }) => ( - <button - className={classNames('btn btn-secondary', { active: active })} - onClick={() => { - if (!active) { - onClick() - } - }} - > - {children} - </button> -) - -FilterButton.propTypes = { - active: PropTypes.bool.isRequired, - children: PropTypes.node.isRequired, - onClick: PropTypes.func.isRequired, -} - -export default FilterButton diff --git a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js b/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js index 89b483fb..5129c013 100644 --- a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js +++ b/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js @@ -1,13 +1,28 @@ import React from 'react' -import FilterLink from '../../containers/projects/FilterLink' +import PropTypes from 'prop-types' +import { Button, ButtonGroup } from 'reactstrap' import { filterPanel } from './FilterPanel.module.scss' -const FilterPanel = () => ( - <div className={`btn-group ${filterPanel} mb-2`}> - <FilterLink filter="SHOW_ALL">All Projects</FilterLink> - <FilterLink filter="SHOW_OWN">My Projects</FilterLink> - <FilterLink filter="SHOW_SHARED">Shared with me</FilterLink> - </div> +export const FILTERS = { SHOW_ALL: 'All Projects', SHOW_OWN: 'My Projects', SHOW_SHARED: 'Shared with me' } + +const FilterPanel = ({ onSelect, activeFilter = 'SHOW_ALL' }) => ( + <ButtonGroup className={`${filterPanel} mb-2`}> + {Object.keys(FILTERS).map((filter) => ( + <Button + color="secondary" + key={filter} + onClick={() => activeFilter === filter || onSelect(filter)} + active={activeFilter === filter} + > + {FILTERS[filter]} + </Button> + ))} + </ButtonGroup> ) +FilterPanel.propTypes = { + onSelect: PropTypes.func.isRequired, + activeFilter: PropTypes.string, +} + export default FilterPanel diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewProjectButtonComponent.js b/opendc-web/opendc-web-ui/src/components/projects/NewProjectButtonComponent.js deleted file mode 100644 index 312671c6..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/NewProjectButtonComponent.js +++ /dev/null @@ -1,17 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' - -const NewProjectButtonComponent = ({ onClick }) => ( - <div className="bottom-btn-container"> - <div className="btn btn-primary float-right" onClick={onClick}> - <span className="fa fa-plus mr-2" /> - New Project - </div> - </div> -) - -NewProjectButtonComponent.propTypes = { - onClick: PropTypes.func.isRequired, -} - -export default NewProjectButtonComponent diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectActionButtons.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectActionButtons.js index 48cce019..96970fd9 100644 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectActionButtons.js +++ b/opendc-web/opendc-web-ui/src/components/projects/ProjectActionButtons.js @@ -1,24 +1,29 @@ import PropTypes from 'prop-types' import React from 'react' import Link from 'next/link' +import { Button } from 'reactstrap' const ProjectActionButtons = ({ projectId, onViewUsers, onDelete }) => ( <td className="text-right"> <Link href={`/projects/${projectId}`}> - <a className="btn btn-outline-primary btn-sm mr-2" title="Open this project"> + <Button color="primary" outline size="sm" className="mr-2" title="Open this project"> <span className="fa fa-play" /> - </a> + </Button> </Link> - <div - className="btn btn-outline-success btn-sm disabled mr-2" + <Button + color="success" + outline + size="sm" + disabled + className="mr-2" title="View and edit collaborators (not supported currently)" onClick={() => onViewUsers(projectId)} > <span className="fa fa-users" /> - </div> - <div className="btn btn-outline-danger btn-sm" title="Delete this project" onClick={() => onDelete(projectId)}> + </Button> + <Button color="danger" outline size="sm" title="Delete this project" onClick={() => onDelete(projectId)}> <span className="fa fa-trash" /> - </div> + </Button> </td> ) diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthList.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js index 15147e08..90d42326 100644 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthList.js +++ b/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js @@ -1,9 +1,9 @@ import PropTypes from 'prop-types' import React from 'react' import { Authorization } from '../../shapes' -import ProjectAuthRow from './ProjectAuthRow' +import ProjectRow from './ProjectRow' -const ProjectAuthList = ({ authorizations }) => { +const ProjectList = ({ authorizations }) => { return ( <div className="vertically-expanding-container"> {authorizations.length === 0 ? ( @@ -23,7 +23,7 @@ const ProjectAuthList = ({ authorizations }) => { </thead> <tbody> {authorizations.map((authorization) => ( - <ProjectAuthRow projectAuth={authorization} key={authorization.project._id} /> + <ProjectRow projectAuth={authorization} key={authorization.project._id} /> ))} </tbody> </table> @@ -32,8 +32,8 @@ const ProjectAuthList = ({ authorizations }) => { ) } -ProjectAuthList.propTypes = { +ProjectList.propTypes = { authorizations: PropTypes.arrayOf(Authorization).isRequired, } -export default ProjectAuthList +export default ProjectList diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthRow.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js index 1c1d5cd8..bc63c805 100644 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthRow.js +++ b/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js @@ -5,7 +5,7 @@ import { Authorization } from '../../shapes/index' import { AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP } from '../../util/authorizations' import { parseAndFormatDateTime } from '../../util/date-time' -const ProjectAuthRow = ({ projectAuth }) => ( +const ProjectRow = ({ projectAuth }) => ( <tr> <td className="pt-3">{projectAuth.project.name}</td> <td className="pt-3">{parseAndFormatDateTime(projectAuth.project.datetimeLastEdited)}</td> @@ -17,8 +17,8 @@ const ProjectAuthRow = ({ projectAuth }) => ( </tr> ) -ProjectAuthRow.propTypes = { +ProjectRow.propTypes = { projectAuth: Authorization.isRequired, } -export default ProjectAuthRow +export default ProjectRow diff --git a/opendc-web/opendc-web-ui/src/containers/modals/NewProjectModal.js b/opendc-web/opendc-web-ui/src/containers/modals/NewProjectModal.js deleted file mode 100644 index e63ba76b..00000000 --- a/opendc-web/opendc-web-ui/src/containers/modals/NewProjectModal.js +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { closeNewProjectModal } from '../../actions/modals/projects' -import { addProject } from '../../actions/projects' -import TextInputModal from '../../components/modals/TextInputModal' - -const NewProjectModal = (props) => { - const visible = useSelector((state) => state.modals.newProjectModalVisible) - const dispatch = useDispatch() - const callback = (text) => { - if (text) { - dispatch(addProject(text)) - } - dispatch(closeNewProjectModal()) - } - return <TextInputModal title="New Project" label="Project title" show={visible} callback={callback} {...props} /> -} - -export default NewProjectModal diff --git a/opendc-web/opendc-web-ui/src/containers/projects/FilterLink.js b/opendc-web/opendc-web-ui/src/containers/projects/FilterLink.js deleted file mode 100644 index 26f95c55..00000000 --- a/opendc-web/opendc-web-ui/src/containers/projects/FilterLink.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { setAuthVisibilityFilter } from '../../actions/projects' -import FilterButton from '../../components/projects/FilterButton' - -const FilterLink = (props) => { - const active = useSelector((state) => state.projectList.authVisibilityFilter === props.filter) - const dispatch = useDispatch() - - return <FilterButton {...props} onClick={() => dispatch(setAuthVisibilityFilter(props.filter))} active={active} /> -} - -export default FilterLink diff --git a/opendc-web/opendc-web-ui/src/containers/projects/NewProjectButtonContainer.js b/opendc-web/opendc-web-ui/src/containers/projects/NewProjectButtonContainer.js deleted file mode 100644 index b8f6fef5..00000000 --- a/opendc-web/opendc-web-ui/src/containers/projects/NewProjectButtonContainer.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { openNewProjectModal } from '../../actions/modals/projects' -import NewProjectButtonComponent from '../../components/projects/NewProjectButtonComponent' - -const NewProjectButtonContainer = (props) => { - const dispatch = useDispatch() - return <NewProjectButtonComponent {...props} onClick={() => dispatch(openNewProjectModal())} /> -} - -export default NewProjectButtonContainer diff --git a/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js b/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js new file mode 100644 index 00000000..5a8a2dcf --- /dev/null +++ b/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js @@ -0,0 +1,33 @@ +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' +import { addProject } from '../../actions/projects' +import TextInputModal from '../../components/modals/TextInputModal' +import { Button } from 'reactstrap' + +/** + * A container for creating a new project. + */ +const NewProjectContainer = () => { + const [isVisible, setVisible] = useState(false) + const dispatch = useDispatch() + const callback = (text) => { + if (text) { + dispatch(addProject(text)) + } + setVisible(false) + } + + return ( + <> + <div className="bottom-btn-container"> + <Button color="primary" className="float-right" onClick={() => setVisible(true)}> + <span className="fa fa-plus mr-2" /> + New Project + </Button> + </div> + <TextInputModal title="New Project" label="Project title" show={isVisible} callback={callback} /> + </> + ) +} + +export default NewProjectContainer diff --git a/opendc-web/opendc-web-ui/src/containers/projects/VisibleProjectAuthList.js b/opendc-web/opendc-web-ui/src/containers/projects/VisibleProjectAuthList.js index b869775c..8e1d063b 100644 --- a/opendc-web/opendc-web-ui/src/containers/projects/VisibleProjectAuthList.js +++ b/opendc-web/opendc-web-ui/src/containers/projects/VisibleProjectAuthList.js @@ -1,6 +1,7 @@ import React from 'react' +import PropTypes from 'prop-types' import { useSelector } from 'react-redux' -import ProjectList from '../../components/projects/ProjectAuthList' +import ProjectList from '../../components/projects/ProjectList' const getVisibleProjectAuths = (projectAuths, filter) => { switch (filter) { @@ -15,7 +16,7 @@ const getVisibleProjectAuths = (projectAuths, filter) => { } } -const VisibleProjectAuthList = (props) => { +const VisibleProjectAuthList = ({ filter }) => { const authorizations = useSelector((state) => { const denormalizedAuthorizations = state.projectList.authorizationsOfCurrentUser.map((authorizationIds) => { const authorization = state.objects.authorization[authorizationIds] @@ -24,9 +25,13 @@ const VisibleProjectAuthList = (props) => { return authorization }) - return getVisibleProjectAuths(denormalizedAuthorizations, state.projectList.authVisibilityFilter) + return getVisibleProjectAuths(denormalizedAuthorizations, filter) }) - return <ProjectList {...props} authorizations={authorizations} /> + return <ProjectList authorizations={authorizations} /> +} + +VisibleProjectAuthList.propTypes = { + filter: PropTypes.string.isRequired, } export default VisibleProjectAuthList diff --git a/opendc-web/opendc-web-ui/src/pages/projects/index.js b/opendc-web/opendc-web-ui/src/pages/projects/index.js index bea9ad93..dd61751f 100644 --- a/opendc-web/opendc-web-ui/src/pages/projects/index.js +++ b/opendc-web/opendc-web-ui/src/pages/projects/index.js @@ -1,19 +1,21 @@ -import React, { useEffect } from 'react' +import React, { useEffect, useState } from 'react' import Head from 'next/head' import { useDispatch } from 'react-redux' import { fetchAuthorizationsOfCurrentUser } from '../../actions/users' import ProjectFilterPanel from '../../components/projects/FilterPanel' -import NewProjectModal from '../../containers/modals/NewProjectModal' -import NewProjectButtonContainer from '../../containers/projects/NewProjectButtonContainer' +import NewProjectContainer from '../../containers/projects/NewProjectContainer' import VisibleProjectList from '../../containers/projects/VisibleProjectAuthList' import AppNavbarContainer from '../../containers/navigation/AppNavbarContainer' import { useRequireAuth } from '../../auth/hook' +import { Container } from 'reactstrap' function Projects() { + useRequireAuth() + const dispatch = useDispatch() + const [filter, setFilter] = useState('SHOW_ALL') - useRequireAuth() - useEffect(() => dispatch(fetchAuthorizationsOfCurrentUser())) + useEffect(() => dispatch(fetchAuthorizationsOfCurrentUser()), []) return ( <> @@ -22,12 +24,11 @@ function Projects() { </Head> <div className="full-height"> <AppNavbarContainer fullWidth={false} /> - <div className="container text-page-container full-height"> - <ProjectFilterPanel /> - <VisibleProjectList /> - <NewProjectButtonContainer /> - </div> - <NewProjectModal /> + <Container className="text-page-container"> + <ProjectFilterPanel onSelect={setFilter} activeFilter={filter} /> + <VisibleProjectList filter={filter} /> + <NewProjectContainer /> + </Container> </div> </> ) diff --git a/opendc-web/opendc-web-ui/src/reducers/modals.js b/opendc-web/opendc-web-ui/src/reducers/modals.js index e600d556..a58d8708 100644 --- a/opendc-web/opendc-web-ui/src/reducers/modals.js +++ b/opendc-web/opendc-web-ui/src/reducers/modals.js @@ -1,5 +1,4 @@ import { combineReducers } from 'redux' -import { CLOSE_NEW_PROJECT_MODAL, OPEN_NEW_PROJECT_MODAL } from '../actions/modals/projects' import { CLOSE_NEW_TOPOLOGY_MODAL, CLOSE_DELETE_MACHINE_MODAL, @@ -31,7 +30,6 @@ function modal(openAction, closeAction) { } export const modals = combineReducers({ - newProjectModalVisible: modal(OPEN_NEW_PROJECT_MODAL, CLOSE_NEW_PROJECT_MODAL), changeTopologyModalVisible: modal(OPEN_NEW_TOPOLOGY_MODAL, CLOSE_NEW_TOPOLOGY_MODAL), editRoomNameModalVisible: modal(OPEN_EDIT_ROOM_NAME_MODAL, CLOSE_EDIT_ROOM_NAME_MODAL), deleteRoomModalVisible: modal(OPEN_DELETE_ROOM_MODAL, CLOSE_DELETE_ROOM_MODAL), diff --git a/opendc-web/opendc-web-ui/src/reducers/project-list.js b/opendc-web/opendc-web-ui/src/reducers/project-list.js index 1f1aa8d0..ad803db0 100644 --- a/opendc-web/opendc-web-ui/src/reducers/project-list.js +++ b/opendc-web/opendc-web-ui/src/reducers/project-list.js @@ -1,5 +1,5 @@ import { combineReducers } from 'redux' -import { ADD_PROJECT_SUCCEEDED, DELETE_PROJECT_SUCCEEDED, SET_AUTH_VISIBILITY_FILTER } from '../actions/projects' +import { ADD_PROJECT_SUCCEEDED, DELETE_PROJECT_SUCCEEDED } from '../actions/projects' import { FETCH_AUTHORIZATIONS_OF_CURRENT_USER_SUCCEEDED } from '../actions/users' export function authorizationsOfCurrentUser(state = [], action) { @@ -15,16 +15,4 @@ export function authorizationsOfCurrentUser(state = [], action) { } } -export function authVisibilityFilter(state = 'SHOW_ALL', action) { - switch (action.type) { - case SET_AUTH_VISIBILITY_FILTER: - return action.filter - default: - return state - } -} - -export const projectList = combineReducers({ - authorizationsOfCurrentUser, - authVisibilityFilter, -}) +export const projectList = combineReducers({ authorizationsOfCurrentUser }) |
