diff --git a/src/actions/projects.js b/src/actions/projects.js index 2d88e519..56f803ee 100644 --- a/src/actions/projects.js +++ b/src/actions/projects.js @@ -32,7 +32,7 @@ import { fetchMemberProjects, updateProjectApi } from '../services/projects' -import { checkAdmin } from '../util/tc' +import { checkAdmin, checkManager } from '../util/tc' function _loadProjects (projectNameOrIdFilter = '', paramFilters = {}) { return (dispatch, getState) => { @@ -54,7 +54,7 @@ function _loadProjects (projectNameOrIdFilter = '', paramFilters = {}) { } } - if (!checkAdmin(getState().auth.token)) { + if (!checkAdmin(getState().auth.token) && !checkManager(getState().auth.token)) { filters['memberOnly'] = true } diff --git a/src/config/constants.js b/src/config/constants.js index 6aa99441..1310acb4 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -308,6 +308,10 @@ export const COPILOT_ROLES = [ 'copilot' ] +export const MANAGER_ROLES = [ + 'project manager' +] + export const downloadAttachmentURL = (challengeId, attachmentId, token) => `${CHALLENGE_API_URL}/${challengeId}/attachments/${attachmentId}/download?token=${token}` diff --git a/src/containers/Projects/index.js b/src/containers/Projects/index.js index 733c044c..97a5e189 100644 --- a/src/containers/Projects/index.js +++ b/src/containers/Projects/index.js @@ -5,7 +5,7 @@ import { withRouter, Link } from 'react-router-dom' import { connect } from 'react-redux' import PropTypes from 'prop-types' import Loader from '../../components/Loader' -import { checkAdminOrCopilot } from '../../util/tc' +import { checkAdminOrCopilot, checkManager } from '../../util/tc' import { PrimaryButton } from '../../components/Buttons' import Select from '../../components/Select' import ProjectCard from '../../components/ProjectCard' @@ -18,11 +18,21 @@ import styles from './styles.module.scss' const Projects = ({ projects, auth, isLoading, projectsCount, loadProjects, loadMoreProjects, unloadProjects }) => { const [search, setSearch] = useState() const [projectStatus, setProjectStatus] = useState('') + const [showOnlyMyProjects, setOnlyMyProjects] = useState(true) const selectedStatus = useMemo(() => PROJECT_STATUSES.find(s => s.value === projectStatus)) + const isProjectManager = checkManager(auth.token) useEffect(() => { - loadProjects(search, projectStatus ? { status: projectStatus } : {}) - }, [search, projectStatus]) + const params = {} + if (projectStatus) { + params.status = projectStatus + } + + if (isProjectManager) { + params.memberOnly = showOnlyMyProjects + } + loadProjects(search, params) + }, [search, projectStatus, showOnlyMyProjects, isProjectManager]) // unload projects on dismount useEffect(() => () => unloadProjects, []) @@ -46,7 +56,7 @@ const Projects = ({ projects, auth, isLoading, projectsCount, loadProjects, load )}
-
+
@@ -61,7 +71,7 @@ const Projects = ({ projects, auth, isLoading, projectsCount, loadProjects, load />
-
+
@@ -76,6 +86,25 @@ const Projects = ({ projects, auth, isLoading, projectsCount, loadProjects, load />
+
+ { + checkManager(auth.token) && ( +
+ setOnlyMyProjects(!showOnlyMyProjects)} + /> + +
+ ) + } +
{projects.length > 0 ? ( <> diff --git a/src/containers/Projects/styles.module.scss b/src/containers/Projects/styles.module.scss index d80341cc..ef18461d 100644 --- a/src/containers/Projects/styles.module.scss +++ b/src/containers/Projects/styles.module.scss @@ -43,6 +43,7 @@ display: flex; gap: 10px; margin-bottom: 20px; + align-items: end; .searchInput { width: 100%; height: 40px; @@ -51,4 +52,85 @@ border: 1px solid $light-gray; background-color: $lighter-gray; } + + .tcCheckbox { + @include tc-checkbox; + + .tc-checkbox-label { + @include roboto-light(); + + line-height: 17px; + font-weight: 300; + margin-left: 21px; + user-select: none; + cursor: pointer; + width: 195px; + font-size: 14px; + color: #3d3d3d; + } + + height: 18px; + width: 210px; + margin: 0; + padding: 0; + vertical-align: bottom; + position: relative; + display: inline-block; + margin-bottom: 4px; + margin-left: 8px; + + input[type=checkbox] { + display: none; + } + + label { + @include roboto-light(); + + line-height: 17px; + font-weight: 300; + cursor: pointer; + position: absolute; + display: inline-block; + width: 18px; + height: 18px; + top: 0; + left: 0; + border: none; + box-shadow: none; + background: $tc-gray-30; + transition: all 0.15s ease-in-out; + + &::after { + opacity: 0; + content: ''; + position: absolute; + width: 9px; + height: 5px; + background: transparent; + top: 5px; + left: 5px; + border-top: none; + border-right: none; + transform: rotate(-45deg); + transition: all 0.15s ease-in-out; + } + + &:hover::after { + opacity: 0.3; + } + + div { + margin-left: 24px; + width: 300px; + } + } + + input[type=checkbox]:checked ~ label { + background: $tc-blue-20; + } + + input[type=checkbox]:checked + label::after { + border-color: $white; + } + } } \ No newline at end of file diff --git a/src/util/tc.js b/src/util/tc.js index 576e9df4..d5917e3b 100644 --- a/src/util/tc.js +++ b/src/util/tc.js @@ -10,7 +10,8 @@ import { SUBMITTER_ROLE_UUID, READ_ONLY_ROLES, ALLOWED_DOWNLOAD_SUBMISSIONS_ROLES, - ALLOWED_EDIT_RESOURCE_ROLES + ALLOWED_EDIT_RESOURCE_ROLES, + MANAGER_ROLES } from '../config/constants' import _ from 'lodash' import { decodeToken } from 'tc-auth-lib' @@ -200,6 +201,11 @@ export const checkAdmin = (token) => { return roles.some(val => ADMIN_ROLES.indexOf(val.toLowerCase()) > -1) } +export const checkManager = (token) => { + const tokenData = decodeToken(token) + const roles = _.get(tokenData, 'roles') + return roles.some(val => MANAGER_ROLES.indexOf(val.toLowerCase()) > -1) +} /** * Checks if token has any of the copilot roles * @param token