From 1891a6f3963d3ddeae0ea093f9a7e3608a97b4d7 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Thu, 13 May 2021 16:35:01 +0200 Subject: ui: Simplify projects page This change simplifies the logic and components of the projects page and reduces its dependency on Redux for simple operations. --- .../opendc-web-ui/src/actions/modals/projects.js | 14 -------- opendc-web/opendc-web-ui/src/actions/projects.js | 8 ----- .../src/components/projects/FilterButton.js | 24 ------------- .../src/components/projects/FilterPanel.js | 29 ++++++++++++---- .../projects/NewProjectButtonComponent.js | 17 ---------- .../components/projects/ProjectActionButtons.js | 19 +++++++---- .../src/components/projects/ProjectAuthList.js | 39 ---------------------- .../src/components/projects/ProjectAuthRow.js | 24 ------------- .../src/components/projects/ProjectList.js | 39 ++++++++++++++++++++++ .../src/components/projects/ProjectRow.js | 24 +++++++++++++ .../src/containers/modals/NewProjectModal.js | 19 ----------- .../src/containers/projects/FilterLink.js | 13 -------- .../projects/NewProjectButtonContainer.js | 11 ------ .../src/containers/projects/NewProjectContainer.js | 33 ++++++++++++++++++ .../containers/projects/VisibleProjectAuthList.js | 13 +++++--- .../opendc-web-ui/src/pages/projects/index.js | 23 +++++++------ opendc-web/opendc-web-ui/src/reducers/modals.js | 2 -- .../opendc-web-ui/src/reducers/project-list.js | 16 ++------- 18 files changed, 153 insertions(+), 214 deletions(-) delete mode 100644 opendc-web/opendc-web-ui/src/actions/modals/projects.js delete mode 100644 opendc-web/opendc-web-ui/src/components/projects/FilterButton.js delete mode 100644 opendc-web/opendc-web-ui/src/components/projects/NewProjectButtonComponent.js delete mode 100644 opendc-web/opendc-web-ui/src/components/projects/ProjectAuthList.js delete mode 100644 opendc-web/opendc-web-ui/src/components/projects/ProjectAuthRow.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/ProjectList.js create mode 100644 opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/modals/NewProjectModal.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/projects/FilterLink.js delete mode 100644 opendc-web/opendc-web-ui/src/containers/projects/NewProjectButtonContainer.js create mode 100644 opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js (limited to 'opendc-web/opendc-web-ui/src') 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 }) => ( - -) - -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 = () => ( -
- All Projects - My Projects - Shared with me -
+export const FILTERS = { SHOW_ALL: 'All Projects', SHOW_OWN: 'My Projects', SHOW_SHARED: 'Shared with me' } + +const FilterPanel = ({ onSelect, activeFilter = 'SHOW_ALL' }) => ( + + {Object.keys(FILTERS).map((filter) => ( + + ))} + ) +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 }) => ( -
-
- - New Project -
-
-) - -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 }) => ( - + -
onViewUsers(projectId)} > -
-
onDelete(projectId)}> + +
+ ) diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthList.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthList.js deleted file mode 100644 index 15147e08..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthList.js +++ /dev/null @@ -1,39 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Authorization } from '../../shapes' -import ProjectAuthRow from './ProjectAuthRow' - -const ProjectAuthList = ({ authorizations }) => { - return ( -
- {authorizations.length === 0 ? ( -
- - No projects here yet... Add some with the 'New Project' button! -
- ) : ( - - - - - - - - - - {authorizations.map((authorization) => ( - - ))} - -
Project nameLast editedAccess rights -
- )} -
- ) -} - -ProjectAuthList.propTypes = { - authorizations: PropTypes.arrayOf(Authorization).isRequired, -} - -export default ProjectAuthList diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthRow.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthRow.js deleted file mode 100644 index 1c1d5cd8..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectAuthRow.js +++ /dev/null @@ -1,24 +0,0 @@ -import classNames from 'classnames' -import React from 'react' -import ProjectActions from '../../containers/projects/ProjectActions' -import { Authorization } from '../../shapes/index' -import { AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP } from '../../util/authorizations' -import { parseAndFormatDateTime } from '../../util/date-time' - -const ProjectAuthRow = ({ projectAuth }) => ( - - {projectAuth.project.name} - {parseAndFormatDateTime(projectAuth.project.datetimeLastEdited)} - - - {AUTH_DESCRIPTION_MAP[projectAuth.authorizationLevel]} - - - -) - -ProjectAuthRow.propTypes = { - projectAuth: Authorization.isRequired, -} - -export default ProjectAuthRow diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js new file mode 100644 index 00000000..90d42326 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js @@ -0,0 +1,39 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Authorization } from '../../shapes' +import ProjectRow from './ProjectRow' + +const ProjectList = ({ authorizations }) => { + return ( +
+ {authorizations.length === 0 ? ( +
+ + No projects here yet... Add some with the 'New Project' button! +
+ ) : ( + + + + + + + + + + {authorizations.map((authorization) => ( + + ))} + +
Project nameLast editedAccess rights +
+ )} +
+ ) +} + +ProjectList.propTypes = { + authorizations: PropTypes.arrayOf(Authorization).isRequired, +} + +export default ProjectList diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js new file mode 100644 index 00000000..bc63c805 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js @@ -0,0 +1,24 @@ +import classNames from 'classnames' +import React from 'react' +import ProjectActions from '../../containers/projects/ProjectActions' +import { Authorization } from '../../shapes/index' +import { AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP } from '../../util/authorizations' +import { parseAndFormatDateTime } from '../../util/date-time' + +const ProjectRow = ({ projectAuth }) => ( + + {projectAuth.project.name} + {parseAndFormatDateTime(projectAuth.project.datetimeLastEdited)} + + + {AUTH_DESCRIPTION_MAP[projectAuth.authorizationLevel]} + + + +) + +ProjectRow.propTypes = { + projectAuth: Authorization.isRequired, +} + +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 -} - -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 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 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 ( + <> +
+ +
+ + + ) +} + +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 + return +} + +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() {
-
- - - -
- + + + + +
) 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 }) -- cgit v1.2.3