Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 37 additions & 83 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<title>Topcoder Challenge Creation App</title>
<title>Work Manager - Topcoder</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
16 changes: 12 additions & 4 deletions src/actions/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,27 @@ export function setActiveProject (projectId) {
/**
* Loads projects of the authenticated user
*/
export function loadProjects (filterProjectName = '') {
export function loadProjects (filterProjectName = '', myProjects = true) {
return (dispatch) => {
dispatch({
type: LOAD_PROJECTS_PENDING
})

const filters = {}
if (!_.isEmpty(filterProjectName)) {
filters['name'] = `*${filterProjectName}*`
if (!isNaN(filterProjectName)) { // if it is number
filters['id'] = parseInt(filterProjectName, 10)
} else { // text search
filters['keyword'] = decodeURIComponent(filterProjectName)
}
}
filters['status'] = 'active'
filters['sort'] = 'lastActivityAt'
filters['memberOnly'] = 'true'
filters['sort'] = 'lastActivityAt desc'
// filters['perPage'] = 20
// filters['page'] = 1
if (myProjects) {
filters['memberOnly'] = true
}

fetchMemberProjects(filters).then(projects => dispatch({
type: LOAD_PROJECTS_SUCCESS,
Expand Down
31 changes: 3 additions & 28 deletions src/components/Sidebar/Sidebar.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,11 @@
height: 100vh;
background-color: $dark-gray;
overflow: auto;

ul {
list-style: none;
width: 100%;
padding: 0;
margin-top: 0;

li {
cursor: pointer;
}
}

a {
text-decoration: none;
}

.noProjects {
@include roboto;
font-weight: 400;
padding-left: 30px;
padding-right: 20px;
color: $white;
}
}

.logo {
width: calc(100% - 100px);
padding: 30px 50px 42px 50px;
padding: 30px 50px 42px 30px;
@media screen and (max-width: 300px * 4) {
width: calc(100% - 20px);
padding: 30px 10px 42px 10px;
Expand All @@ -46,10 +23,8 @@
font-weight: 400;
line-height: 31px;
color: $white;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 37px
margin-bottom: 37px;
padding-left: 30px;
}

.homeLink {
Expand Down
47 changes: 2 additions & 45 deletions src/components/Sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,71 +5,28 @@ import React from 'react'
import { Link } from 'react-router-dom'
import PropTypes from 'prop-types'
import cn from 'classnames'
import _ from 'lodash'
import ProjectCard from '../ProjectCard'
import { DebounceInput } from 'react-debounce-input'
import Loader from '../Loader'
import TopcoderLogo from '../../assets/images/topcoder-logo.png'
import styles from './Sidebar.module.scss'

const Sidebar = ({
projects, isLoading, setActiveProject,
projectId, resetSidebarActiveParams,
updateProjectsList, searchProjectName
projectId, resetSidebarActiveParams
}) => {
const projectComponents = projects.map(p => (
<li key={p.id}>
<ProjectCard
projectName={p.name}
projectId={p.id}
selected={projectId === `${p.id}`}
setActiveProject={setActiveProject}
/>
</li>
))

return (
<div className={styles.sidebar}>
<img src={TopcoderLogo} className={styles.logo} />
<div className={styles.title}>Challenge Editor</div>
{
!isLoading && !searchProjectName && _.get(projectComponents, 'length', 0) === 0 && (
<div className={styles.noProjects}>
You don't have any active projects yet!
</div>
)
}
<Link to='/'>
<div className={cn(styles.homeLink, { [styles.active]: !projectId })} onClick={resetSidebarActiveParams}>
Active challenges
</div>
</Link>
<DebounceInput
minLength={2}
debounceTimeout={300}
placeholder='Search projects'
onChange={(e) => updateProjectsList(e.target.value)}
value={searchProjectName}
/>
{
isLoading ? <Loader /> : (
<ul>
{projectComponents}
</ul>
)
}
</div>
)
}

Sidebar.propTypes = {
projects: PropTypes.arrayOf(PropTypes.shape()),
isLoading: PropTypes.bool,
setActiveProject: PropTypes.func,
projectId: PropTypes.string,
resetSidebarActiveParams: PropTypes.func,
searchProjectName: PropTypes.string,
updateProjectsList: PropTypes.func
resetSidebarActiveParams: PropTypes.func
}

export default Sidebar
51 changes: 51 additions & 0 deletions src/containers/Challenges/Challenges.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@import "../../styles/includes";

.projectSearch {
padding: 20px 20px 0px;

.projectSearchHeader {
display: flex;

label {
line-height: 40px;
margin-right: 10px;
}
}

ul {
background-color: $lighter-gray;
list-style: none;
width: 100%;
padding: 0;
margin-top: 0;
border-radius: 3px;
flex: 1;

li {
cursor: pointer;
}
}

input[type="text"] {
flex: 2;
}

input[type="checkbox"] {
margin-left: 20px;
margin-right: 10px;
height: 40px;
}

a {
color: $dark-gray;
text-decoration: none;
}

.noProjects {
@include roboto;
font-weight: 400;
padding-left: 30px;
padding-right: 20px;
color: $white;
}
}
125 changes: 102 additions & 23 deletions src/containers/Challenges/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,39 @@
* Container to render Challenges page
*/
import _ from 'lodash'
import React, { Component } from 'react'
import React, { Component, Fragment } from 'react'
// import { Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { DebounceInput } from 'react-debounce-input'
import ChallengesComponent from '../../components/ChallengesComponent'
import ProjectCard from '../../components/ProjectCard'
import Loader from '../../components/Loader'
import { loadChallengesByPage } from '../../actions/challenges'
import { loadProject } from '../../actions/projects'
import { resetSidebarActiveParams } from '../../actions/sidebar'
import { loadProjects, setActiveProject, resetSidebarActiveParams } from '../../actions/sidebar'
import {
CHALLENGE_STATUS
} from '../../config/constants'
import styles from './Challenges.module.scss'

class Challenges extends Component {
constructor (props) {
super(props)
this.state = {
searchProjectName: '',
onlyMyProjects: true
}

this.updateProjectName = this.updateProjectName.bind(this)
this.toggleMyProjects = this.toggleMyProjects.bind(this)
}

componentDidMount () {
const { activeProjectId, resetSidebarActiveParams, menu, projectId } = this.props
const { activeProjectId, resetSidebarActiveParams, menu, projectId, isLoading } = this.props
if (activeProjectId === -1 && !isLoading) {
this.props.loadProjects()
}
if (menu === 'NULL' && activeProjectId !== -1) {
resetSidebarActiveParams()
} else {
Expand All @@ -28,7 +46,9 @@ class Challenges extends Component {
}

componentWillReceiveProps (nextProps) {
this.reloadChallenges(nextProps)
if (this.props.activeProjectId !== nextProps.activeProjectId) {
this.reloadChallenges(nextProps)
}
}

reloadChallenges (props) {
Expand All @@ -43,6 +63,17 @@ class Challenges extends Component {
}
}

updateProjectName (val) {
this.setState({ searchProjectName: val })
this.props.loadProjects(val, this.state.onlyMyProjects)
}

toggleMyProjects (evt) {
this.setState({ onlyMyProjects: evt.target.checked }, () => {
this.props.loadProjects(this.state.searchProjectName, this.state.onlyMyProjects)
})
}

render () {
const {
challenges,
Expand All @@ -56,26 +87,70 @@ class Challenges extends Component {
loadChallengesByPage,
page,
perPage,
totalChallenges
totalChallenges,
setActiveProject
} = this.props
const { searchProjectName, onlyMyProjects } = this.state
const projectInfo = _.find(projects, { id: activeProjectId }) || {}
const projectComponents = projects.map(p => (
<li key={p.id}>
<ProjectCard
projectName={p.name}
projectId={p.id}
selected={activeProjectId === `${p.id}`}
setActiveProject={setActiveProject}
/>
</li>
))
return (
<ChallengesComponent
activeProject={({
...projectInfo,
...((reduxProjectInfo && reduxProjectInfo.id === activeProjectId) ? reduxProjectInfo : {})
})}
warnMessage={warnMessage}
challenges={challenges}
isLoading={isLoading}
filterChallengeName={filterChallengeName}
status={status}
activeProjectId={activeProjectId}
loadChallengesByPage={loadChallengesByPage}
page={page}
perPage={perPage}
totalChallenges={totalChallenges}
/>
<Fragment>
<div className={styles.projectSearch}>
<div className={styles.projectSearchHeader}>
<label>Swtich Project</label>
<DebounceInput
minLength={2}
debounceTimeout={300}
placeholder='Search projects'
onChange={(e) => this.updateProjectName(e.target.value)}
value={searchProjectName}
/>
<input
type='checkbox'
label='My Projects'
checked={onlyMyProjects}
onChange={this.toggleMyProjects}
/>
<label>My Projects</label>
</div>
{
activeProjectId === -1 && <div>No project selected. Select one below</div>
}
{
isLoading ? <Loader /> : (
<ul>
{projectComponents}
</ul>
)
}
</div>
{ activeProjectId !== -1 && <ChallengesComponent
activeProject={({
...projectInfo,
...((reduxProjectInfo && reduxProjectInfo.id === activeProjectId) ? reduxProjectInfo : {})
})}
warnMessage={warnMessage}
challenges={challenges}
isLoading={isLoading}
filterChallengeName={filterChallengeName}
status={status}
activeProjectId={activeProjectId}
loadChallengesByPage={loadChallengesByPage}
page={page}
perPage={perPage}
totalChallenges={totalChallenges}
/>
}
</Fragment>
)
}
}
Expand All @@ -96,7 +171,9 @@ Challenges.propTypes = {
resetSidebarActiveParams: PropTypes.func,
page: PropTypes.number.isRequired,
perPage: PropTypes.number.isRequired,
totalChallenges: PropTypes.number.isRequired
totalChallenges: PropTypes.number.isRequired,
loadProjects: PropTypes.func.isRequired,
setActiveProject: PropTypes.func.isRequired
}

const mapStateToProps = ({ challenges, sidebar, projects }) => ({
Expand All @@ -111,7 +188,9 @@ const mapStateToProps = ({ challenges, sidebar, projects }) => ({
const mapDispatchToProps = {
loadChallengesByPage,
resetSidebarActiveParams,
loadProject
loadProject,
loadProjects,
setActiveProject
}

export default connect(mapStateToProps, mapDispatchToProps)(Challenges)
Loading