From f5feace4a8be22491e4ab35938395c9546fd790e Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Mon, 3 Mar 2025 17:56:57 +0200 Subject: [PATCH] PM-875 - move projects to new container, add filters --- src/actions/projects.js | 96 +++++++++++++++ src/actions/sidebar.js | 21 +--- src/components/ProjectCard/index.js | 6 +- src/config/constants.js | 2 + src/containers/Challenges/index.js | 45 +------ src/containers/Projects/index.js | 132 +++++++++++++++++++++ src/containers/Projects/styles.module.scss | 54 +++++++++ src/containers/TaaSList/index.js | 69 +++++++---- src/containers/Tab/index.js | 15 ++- src/reducers/projects.js | 39 +++++- src/reducers/sidebar.js | 6 +- src/routes.js | 3 +- 12 files changed, 383 insertions(+), 105 deletions(-) create mode 100644 src/containers/Projects/index.js create mode 100644 src/containers/Projects/styles.module.scss diff --git a/src/actions/projects.js b/src/actions/projects.js index 5f38015c..2d88e519 100644 --- a/src/actions/projects.js +++ b/src/actions/projects.js @@ -1,4 +1,12 @@ +import _ from 'lodash' + import { + PROJECT_TYPE_TAAS, + PROJECTS_PAGE_SIZE, + LOAD_PROJECTS_PENDING, + LOAD_PROJECTS_SUCCESS, + UNLOAD_PROJECTS_SUCCESS, + LOAD_PROJECTS_FAILURE, LOAD_PROJECT_BILLING_ACCOUNT, LOAD_CHALLENGE_MEMBERS_SUCCESS, LOAD_PROJECT_DETAILS, @@ -21,8 +29,96 @@ import { getProjectTypes, createProjectApi, fetchBillingAccounts, + fetchMemberProjects, updateProjectApi } from '../services/projects' +import { checkAdmin } from '../util/tc' + +function _loadProjects (projectNameOrIdFilter = '', paramFilters = {}) { + return (dispatch, getState) => { + dispatch({ + type: LOAD_PROJECTS_PENDING + }) + + const filters = { + sort: 'lastActivityAt desc', + perPage: PROJECTS_PAGE_SIZE, + ...paramFilters + } + + if (!_.isEmpty(projectNameOrIdFilter)) { + if (!isNaN(projectNameOrIdFilter)) { // if it is number + filters['id'] = parseInt(projectNameOrIdFilter, 10) + } else { // text search + filters['keyword'] = decodeURIComponent(projectNameOrIdFilter) + } + } + + if (!checkAdmin(getState().auth.token)) { + filters['memberOnly'] = true + } + + // eslint-disable-next-line no-debugger + const state = getState().projects + fetchMemberProjects(filters).then(({ projects, pagination }) => dispatch({ + filters, + type: LOAD_PROJECTS_SUCCESS, + projects: _.uniqBy((filters.page ? state.projects || [] : []).concat(projects), 'id'), + total: pagination.xTotal, + page: pagination.xPage + })).catch(() => dispatch({ + type: LOAD_PROJECTS_FAILURE + })) + } +} + +export function loadProjects (projectNameOrIdFilter = '', paramFilters = {}) { + return async (dispatch, getState) => { + const _filters = _.assign({}, paramFilters) + if (_.isEmpty(_filters) || !_filters.type) { + let projectTypes = getState().projects.projectTypes + + if (!projectTypes.length) { + dispatch({ + type: LOAD_PROJECTS_PENDING + }) + await loadProjectTypes()(dispatch) + projectTypes = getState().projects.projectTypes + } + + _.assign(_filters, { + type: projectTypes.filter(d => d.key !== PROJECT_TYPE_TAAS).map(d => d.key) + }) + } + + return _loadProjects(projectNameOrIdFilter, _filters)(dispatch, getState) + } +} + +/** + * Load more projects for the authenticated user + */ +export function loadMoreProjects () { + return (dispatch, getState) => { + const { projectFilters, projectsPage } = getState().projects + + loadProjects('', _.assign({}, projectFilters, { + perPage: PROJECTS_PAGE_SIZE, + page: projectsPage + 1 + }))(dispatch, getState) + } +} + +/** + * Unloads projects of the authenticated user + */ +export function unloadProjects () { + return (dispatch) => { + dispatch({ + type: UNLOAD_PROJECTS_SUCCESS + }) + } +} /** * Loads project details diff --git a/src/actions/sidebar.js b/src/actions/sidebar.js index dd521d25..7a945301 100644 --- a/src/actions/sidebar.js +++ b/src/actions/sidebar.js @@ -37,6 +37,7 @@ export function loadProjects (filterProjectName = '', paramFilters = {}) { }) const filters = { + status: 'active', sort: 'lastActivityAt desc', perPage: PROJECTS_PAGE_SIZE, ...paramFilters @@ -66,26 +67,6 @@ export function loadProjects (filterProjectName = '', paramFilters = {}) { } } -/** - * Load more projects for the authenticated user - */ -export function loadMoreProjects (filterProjectName = '', paramFilters = {}) { - return (dispatch, getState) => { - const state = getState().sidebar - - loadProjects(filterProjectName, _.assignIn({}, paramFilters, { - perPage: PROJECTS_PAGE_SIZE, - page: state.page + 1 - }))(dispatch, getState) - } -} - -export function loadTaasProjects (filterProjectName = '', paramFilters = {}) { - return loadProjects(filterProjectName, Object.assign({ - type: 'talent-as-a-service' - }, paramFilters)) -} - /** * Unloads projects of the authenticated user */ diff --git a/src/components/ProjectCard/index.js b/src/components/ProjectCard/index.js index f639259b..9d3d7675 100644 --- a/src/components/ProjectCard/index.js +++ b/src/components/ProjectCard/index.js @@ -8,13 +8,12 @@ import { PROJECT_STATUSES } from '../../config/constants' import styles from './ProjectCard.module.scss' -const ProjectCard = ({ projectName, projectStatus, projectId, selected, setActiveProject }) => { +const ProjectCard = ({ projectName, projectStatus, projectId, selected }) => { return (
setActiveProject(parseInt(projectId))} >
{projectName} @@ -29,8 +28,7 @@ ProjectCard.propTypes = { projectStatus: PT.string.isRequired, projectId: PT.number.isRequired, projectName: PT.string.isRequired, - selected: PT.bool.isRequired, - setActiveProject: PT.func + selected: PT.bool } export default ProjectCard diff --git a/src/config/constants.js b/src/config/constants.js index 106c9f76..43845e41 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -451,3 +451,5 @@ export const ATTACHMENT_TYPE_LINK = 'link' */ export const PROJECT_ASSETS_SHARED_WITH_ALL_MEMBERS = 'All Project Members' export const PROJECT_ASSETS_SHARED_WITH_ADMIN = 'Only Admins' + +export const PROJECT_TYPE_TAAS = 'talent-as-a-service' diff --git a/src/containers/Challenges/index.js b/src/containers/Challenges/index.js index 72aa0ad4..93b0ae85 100644 --- a/src/containers/Challenges/index.js +++ b/src/containers/Challenges/index.js @@ -6,9 +6,7 @@ import React, { Component, Fragment } from 'react' // import { Redirect } from 'react-router-dom' import PropTypes from 'prop-types' import { connect } from 'react-redux' -import { Link } from 'react-router-dom' import ChallengesComponent from '../../components/ChallengesComponent' -import ProjectCard from '../../components/ProjectCard' // import Loader from '../../components/Loader' import { loadChallengesByPage, @@ -18,15 +16,11 @@ import { } from '../../actions/challenges' import { loadProject, updateProject } from '../../actions/projects' import { - loadMoreProjects, loadProjects, setActiveProject, resetSidebarActiveParams } from '../../actions/sidebar' -import styles from './Challenges.module.scss' -import { checkAdmin, checkAdminOrCopilot } from '../../util/tc' -import { PrimaryButton } from '../../components/Buttons' -import InfiniteLoadTrigger from '../../components/InfiniteLoadTrigger' +import { checkAdmin } from '../../util/tc' class Challenges extends Component { constructor (props) { @@ -145,46 +139,11 @@ class Challenges extends Component { metadata } = this.props const { challengeTypes = [] } = metadata - const projectInfo = _.find(projects, { id: activeProjectId }) || {} - const projectComponents = - !dashboard && - projects.map((p) => ( -
  • - -
  • - )) return ( - {!dashboard && - (!!projectComponents.length || - (activeProjectId === -1 && !selfService)) ? ( -
    - {activeProjectId === -1 && !selfService && ( -
    -
    No project selected. Select one below
    - {checkAdminOrCopilot(auth.token) && ( - - - - )} -
    - )} -
      {projectComponents}
    - {projects && !!projects.length && ( - - )} -
    - ) : null} {(dashboard || activeProjectId !== -1 || selfService) && ( ({ const mapDispatchToProps = { loadChallengesByPage, resetSidebarActiveParams, - loadMoreProjects, loadProject, loadProjects, updateProject, diff --git a/src/containers/Projects/index.js b/src/containers/Projects/index.js new file mode 100644 index 00000000..733c044c --- /dev/null +++ b/src/containers/Projects/index.js @@ -0,0 +1,132 @@ +import React, { useEffect, useMemo, useState } from 'react' +import cn from 'classnames' +import { DebounceInput } from 'react-debounce-input' +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 { PrimaryButton } from '../../components/Buttons' +import Select from '../../components/Select' +import ProjectCard from '../../components/ProjectCard' +import InfiniteLoadTrigger from '../../components/InfiniteLoadTrigger' +import { loadProjects, loadMoreProjects, unloadProjects } from '../../actions/projects' +import { PROJECT_STATUSES } from '../../config/constants' + +import styles from './styles.module.scss' + +const Projects = ({ projects, auth, isLoading, projectsCount, loadProjects, loadMoreProjects, unloadProjects }) => { + const [search, setSearch] = useState() + const [projectStatus, setProjectStatus] = useState('') + const selectedStatus = useMemo(() => PROJECT_STATUSES.find(s => s.value === projectStatus)) + + useEffect(() => { + loadProjects(search, projectStatus ? { status: projectStatus } : {}) + }, [search, projectStatus]) + + // unload projects on dismount + useEffect(() => () => unloadProjects, []) + + if (isLoading && projects.length === 0) { + return ( +
    + +
    + ) + } + + return ( +
    +
    +

    Projects

    + {checkAdminOrCopilot(auth.token) && ( + + + + )} +
    +
    +
    +
    + +
    +
    + setSearch(e.target.value)} + value={search} + /> +
    +
    +
    +
    + +
    +
    +