diff --git a/src/actions/users.js b/src/actions/users.js new file mode 100644 index 00000000..708ddd1f --- /dev/null +++ b/src/actions/users.js @@ -0,0 +1,35 @@ +/** + * Sidebar related redux actions + */ +import { fetchMemberProjects } from '../services/projects' +import { + LOAD_ALL_USER_PROJECTS_PENDING, + LOAD_ALL_USER_PROJECTS_SUCCESS, + LOAD_ALL_USER_PROJECTS_FAILURE +} from '../config/constants' + +/** + * Loads projects of the authenticated user + */ +export function loadAllUserProjects (isAdmin = true) { + return (dispatch) => { + dispatch({ + type: LOAD_ALL_USER_PROJECTS_PENDING + }) + + const filters = { + status: 'active', + sort: 'lastActivityAt desc' + } + if (!isAdmin) { + filters['memberOnly'] = true + } + + fetchMemberProjects(filters).then(projects => dispatch({ + type: LOAD_ALL_USER_PROJECTS_SUCCESS, + projects + })).catch(() => dispatch({ + type: LOAD_ALL_USER_PROJECTS_FAILURE + })) + } +} diff --git a/src/components/ChallengeEditor/ChallengeView/index.js b/src/components/ChallengeEditor/ChallengeView/index.js index 6becff77..c7bfbb95 100644 --- a/src/components/ChallengeEditor/ChallengeView/index.js +++ b/src/components/ChallengeEditor/ChallengeView/index.js @@ -207,6 +207,12 @@ const ChallengeView = ({ )} )} +
+
+ Timezone: + {Intl.DateTimeFormat().resolvedOptions().timeZone} +
+
{ challenge.legacy.subTrack === 'WEB_DESIGNS' && challenge.phases.length === 8 ? phases.map((phase, index) => ( currentPhase.indexOf(phase) < 0) const exceptionHandlesDeleteList = {} - // The creator of the challenge can't be deleted - exceptionHandlesDeleteList[challenge.createdBy] = true _.forEach(submissions, (s) => { // do not allow to delete submitters who submitted exceptionHandlesDeleteList[s.createdBy] = true @@ -182,6 +180,14 @@ export default class Resources extends React.Component { ], (status) => challenge.status.toUpperCase() === status) ) { if ( + // The creator of the challenge can't be deleted + resourceItem.memberHandle === challenge.createdBy + ) { + // where the copilot has multiple roles, we should allow the additional roles to be deleted, but not the copilot role + if (`${resourceItem.role}`.toLowerCase().indexOf('copilot') >= 0) { + exceptionResourceIdDeleteList[resourceItem.id] = true + } + } else if ( // Copilots can't delete themselves from the challenge loggedInUserResource && _.some(loggedInUserResource.roles, (role) => `${role}`.toLowerCase().indexOf('copilot') >= 0) && @@ -189,6 +195,11 @@ export default class Resources extends React.Component { ) { exceptionResourceIdDeleteList[resourceItem.id] = true } + } else if ( + // The creator of the challenge can't be deleted + resourceItem.memberHandle === challenge.createdBy + ) { + exceptionResourceIdDeleteList[resourceItem.id] = true } else if ( // If the current phase is not submission or registration // then we will disable removing reviewers and copilots. diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 479dde10..f181e87b 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -1749,6 +1749,12 @@ class ChallengeEditor extends Component { )} {!isTask && ( <> +
+
+ Timezone: + {Intl.DateTimeFormat().resolvedOptions().timeZone} +
+
{ phases.map((phase, index) => ( { - return ( -
- -
- Work Manager - {isBetaMode() && beta} -
- -
- All Work -
- - { - isBetaMode() && ( - -
- Self-Service Opportunities -
- - ) - } - -
- Give Application Feedback -
-
-

- Have an urgent issue?
- E: support@topcoder.com -

-
- ) -} - -Sidebar.propTypes = { - projectId: PropTypes.string, - resetSidebarActiveParams: PropTypes.func, - selfService: PropTypes.bool -} - -export default Sidebar diff --git a/src/config/constants.js b/src/config/constants.js index d1c2c10e..6aff40cf 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -57,6 +57,10 @@ export const LOAD_PROJECTS_PENDING = 'LOAD_PROJECTS_PENDING' export const LOAD_PROJECTS_FAILURE = 'LOAD_PROJECTS_FAILURE' export const UNLOAD_PROJECTS_SUCCESS = 'UNLOAD_PROJECTS_SUCCESS' +export const LOAD_ALL_USER_PROJECTS_SUCCESS = 'LOAD_ALL_USER_PROJECTS_SUCCESS' +export const LOAD_ALL_USER_PROJECTS_PENDING = 'LOAD_ALL_USER_PROJECTS_PENDING' +export const LOAD_ALL_USER_PROJECTS_FAILURE = 'LOAD_ALL_USER_PROJECTS_FAILURE' + // project billingAccount export const LOAD_PROJECT_BILLING_ACCOUNT = 'LOAD_PROJECT_BILLING_ACCOUNT' export const LOAD_PROJECT_BILLING_ACCOUNT_PENDING = 'LOAD_PROJECT_BILLING_ACCOUNT_PENDING' diff --git a/src/containers/Sidebar/index.js b/src/containers/Sidebar/index.js deleted file mode 100644 index edb70ba9..00000000 --- a/src/containers/Sidebar/index.js +++ /dev/null @@ -1,104 +0,0 @@ -import _ from 'lodash' -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' -import Sidebar from '../../components/Sidebar' -import { loadProjects, setActiveProject, resetSidebarActiveParams, unloadProjects } from '../../actions/sidebar' - -class SidebarContainer extends Component { - constructor (props) { - super(props) - this.state = { - searchProjectName: '' - } - this.updateProjectName = this.updateProjectName.bind(this) - } - - componentDidMount () { - const { projectId, activeProjectId, isLoading, selfService } = this.props - if (!projectId && activeProjectId === -1 && !isLoading && !selfService) { - this.props.loadProjects() - } - - if (projectId && activeProjectId < 0) { - this.props.setActiveProject(parseInt(projectId)) - } - } - - componentWillReceiveProps (nextProps) { - const { projectId, isLoading, selfService, projects } = nextProps - - // if we're viewing a specific project, - // or we're viewing the self serve page, - // or if the project is already loading, - // don't load the projects - if (!!projectId || selfService || isLoading) { - // if we're not in the middle of loading, - // and we have projects to unload, - // unload them - if (!isLoading && !!projects && !!projects.length) { - this.props.unloadProjects() - } - - return - } - - // if we already have projects in the list, - // don't load the projects again - if (!!projects && !!projects.length) { - return - } - - // now it's okay to load the projects - this.props.loadProjects() - } - - updateProjectName (val) { - this.setState({ searchProjectName: val }) - this.props.loadProjects(val) - } - - render () { - const { isLoading, setActiveProject, projectId, resetSidebarActiveParams, projects, selfService, unloadProjects } = this.props - const { searchProjectName } = this.state - - return ( - - ) - } -} - -SidebarContainer.propTypes = { - projects: PropTypes.arrayOf(PropTypes.shape()), - isLoading: PropTypes.bool, - loadProjects: PropTypes.func, - unloadProjects: PropTypes.func, - activeProjectId: PropTypes.number, - setActiveProject: PropTypes.func, - projectId: PropTypes.string, - resetSidebarActiveParams: PropTypes.func, - selfService: PropTypes.bool -} - -const mapStateToProps = ({ sidebar }) => ({ - ...sidebar -}) - -const mapDispatchToProps = { - loadProjects, - unloadProjects, - setActiveProject, - resetSidebarActiveParams -} - -export default connect(mapStateToProps, mapDispatchToProps)(SidebarContainer) diff --git a/src/containers/Tab/index.js b/src/containers/Tab/index.js index 327d9362..af091a3f 100644 --- a/src/containers/Tab/index.js +++ b/src/containers/Tab/index.js @@ -17,13 +17,25 @@ class TabContainer extends Component { searchProjectName: '', currentTab: 1 } - this.updateProjectName = this.updateProjectName.bind(this) this.onTabChange = this.onTabChange.bind(this) } componentDidMount () { - const { projectId, activeProjectId, isLoading, selfService } = this.props - if (!projectId && activeProjectId === -1 && !isLoading && !selfService) { + const { + projectId, + activeProjectId, + isLoading, + selfService, + history + } = this.props + if ( + !projectId && + activeProjectId === -1 && + !isLoading && + !selfService && + // do not fetch projects for users page + history.location.pathname !== '/users' + ) { this.props.loadProjects() } @@ -46,15 +58,22 @@ class TabContainer extends Component { } else { this.setState({ currentTab: 0 }) } + if ( + isLoading || + // do not fetch projects for users page + nextProps.history.location.pathname === '/users' + ) { + return + } // if we're viewing a specific project, // or we're viewing the self serve page, // or if the project is already loading, // don't load the projects - if (!!projectId || selfService || isLoading) { + if (!!projectId || selfService) { // if we're not in the middle of loading, // and we have projects to unload, // unload them - if (!isLoading && !!projects && !!projects.length) { + if (!!projects && !!projects.length) { this.props.unloadProjects() } @@ -71,11 +90,6 @@ class TabContainer extends Component { this.props.loadProjects() } - updateProjectName (val) { - this.setState({ searchProjectName: val }) - this.props.loadProjects(val) - } - onTabChange (tab) { const { history, resetSidebarActiveParams } = this.props if (tab === 1) { diff --git a/src/containers/Users/index.js b/src/containers/Users/index.js index e1b8a3ff..8e45f089 100644 --- a/src/containers/Users/index.js +++ b/src/containers/Users/index.js @@ -5,6 +5,11 @@ import PT from 'prop-types' import UsersComponent from '../../components/Users' import { PROJECT_ROLES } from '../../config/constants' import { fetchProjectById } from '../../services/projects' +import { checkAdmin } from '../../util/tc' + +import { + loadAllUserProjects +} from '../../actions/users' class Users extends Component { constructor (props) { @@ -21,6 +26,11 @@ class Users extends Component { } componentDidMount () { + const { token, isLoading, loadAllUserProjects } = this.props + if (!isLoading) { + const isAdmin = checkAdmin(token) + loadAllUserProjects(isAdmin) + } } isEditable () { @@ -118,18 +128,27 @@ class Users extends Component { } } -const mapStateToProps = ({ sidebar, auth }) => { +const mapStateToProps = ({ users, auth }) => { return { - projects: sidebar.projects, + projects: users.allUserProjects, + isLoading: users.isLoadingAllUserProjects, auth, - loggedInUser: auth.user + loggedInUser: auth.user, + token: auth.token } } Users.propTypes = { projects: PT.arrayOf(PT.object), auth: PT.object, - loggedInUser: PT.object + loggedInUser: PT.object, + token: PT.string, + isLoading: PT.bool, + loadAllUserProjects: PT.func.isRequired +} + +const mapDispatchToProps = { + loadAllUserProjects } -export default connect(mapStateToProps)(Users) +export default connect(mapStateToProps, mapDispatchToProps)(Users) diff --git a/src/reducers/index.js b/src/reducers/index.js index 5211b302..a220cb0c 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -9,6 +9,7 @@ import projects from './projects' import challengeSubmissions from './challengeSubmissions' import sidebar from './sidebar' import members from './members' +import users from './users' export default combineReducers({ auth, @@ -17,5 +18,6 @@ export default combineReducers({ sidebar, toastr: toastrReducer, projects, - members + members, + users }) diff --git a/src/reducers/users.js b/src/reducers/users.js new file mode 100644 index 00000000..ad5e7192 --- /dev/null +++ b/src/reducers/users.js @@ -0,0 +1,26 @@ +/** + * Reducer to process actions related to sidebar + */ +import { + LOAD_ALL_USER_PROJECTS_PENDING, + LOAD_ALL_USER_PROJECTS_SUCCESS, + LOAD_ALL_USER_PROJECTS_FAILURE +} from '../config/constants' + +const initialState = { + allUserProjects: [], + isLoadProjectsSuccess: false +} + +export default function (state = initialState, action) { + switch (action.type) { + case LOAD_ALL_USER_PROJECTS_SUCCESS: + return { ...state, allUserProjects: action.projects, isLoadingAllUserProjects: false } + case LOAD_ALL_USER_PROJECTS_PENDING: + return { ...state, isLoadingAllUserProjects: true } + case LOAD_ALL_USER_PROJECTS_FAILURE: + return { ...state, isLoadingAllUserProjects: false } + default: + return state + } +}