@@ -5,6 +5,7 @@ import { Link } from 'react-router-dom'
55import _get from 'lodash/get'
66import _map from 'lodash/map'
77import _compact from 'lodash/compact'
8+ import _isEmpty from 'lodash/isEmpty'
89import AsManager from '../../../../interactions/User/AsManager'
910import { generateWidgetId, WidgetDataTarget, widgetDescriptor }
1011 from '../../../../services/Widget/Widget'
@@ -22,23 +23,18 @@ import WithFilteredClusteredTasks
2223 from '../../HOCs/WithFilteredClusteredTasks/WithFilteredClusteredTasks'
2324import WithChallengeMetrics
2425 from '../../HOCs/WithChallengeMetrics/WithChallengeMetrics'
25- import WithDeactivateOnOutsideClick
26- from '../../../HOCs/WithDeactivateOnOutsideClick/WithDeactivateOnOutsideClick'
2726import WidgetWorkspace from '../../../WidgetWorkspace/WidgetWorkspace'
2827import RebuildTasksControl from '../RebuildTasksControl/RebuildTasksControl'
2928import TaskUploadingProgress
3029 from '../TaskUploadingProgress/TaskUploadingProgress'
31- import DropdownButton from '../../../Bulma/DropdownButton'
32- import BusySpinner from '../../../BusySpinner/BusySpinner'
30+ import Dropdown from '../../../Dropdown/Dropdown'
3331import SvgSymbol from '../../../SvgSymbol/SvgSymbol'
32+ import BusySpinner from '../../../BusySpinner/BusySpinner'
3433import ConfirmAction from '../../../ConfirmAction/ConfirmAction'
3534import manageMessages from '../Messages'
3635import messages from './Messages'
3736import './ChallengeDashboard.scss'
3837
39- // Setup child components with needed HOCs.
40- const DeactivatableDropdownButton = WithDeactivateOnOutsideClick(DropdownButton)
41-
4238// The name of this dashboard.
4339const DASHBOARD_NAME = "challenge"
4440
@@ -98,113 +94,139 @@ export class ChallengeDashboard extends Component {
9894
9995 const manager = AsManager(this.props.user)
10096 const projectId = _get(this.props, 'challenge.parent.id')
97+ const status = _get(this.props, 'challenge.status', ChallengeStatus.none)
98+ const hasTasks = _get(this.props, 'challenge.actions.total', 0) > 0
10199
102- const managedProjectOptions = _compact(_map(this.props.projects, project => {
103- if (project.id === projectId || !manager.canWriteProject(project)) {
104- return null
105- }
100+ const pageHeader = (
101+ <div className="admin__manage__header admin__manage__header--flush">
102+ <nav className="breadcrumb" aria-label="breadcrumbs">
103+ <ul>
104+ <li>
105+ <Link to='/admin/projects'>
106+ <FormattedMessage {...manageMessages.manageHeader} />
107+ </Link>
108+ </li>
109+ <li>
110+ <Link to={`/admin/project/${projectId}`}>
111+ {_get(this.props, 'challenge.parent.displayName') ||
112+ _get(this.props, 'challenge.parent.name')}
113+ </Link>
114+ </li>
115+ <li className="is-active">
116+ {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
117+ <a aria-current="page">
118+ {this.props.challenge.name}
119+ {this.props.loadingChallenge && <BusySpinner inline />}
120+ </a>
121+ </li>
122+ </ul>
123+ </nav>
106124
107- return {
108- key: `project-${project.id}`,
109- text: project.displayName ? project.displayName : project.name,
110- projectId: project.id,
111- }
112- }))
125+ <div className="admin__manage__controls mr-flex">
126+ {hasTasks && isUsableChallengeStatus(status, true) &&
127+ <Link to={`/challenge/${this.props.challenge.id}`}
128+ className="mr-text-green-lighter hover:mr-text-white mr-mr-4">
129+ <FormattedMessage {...messages.startChallengeLabel} />
130+ </Link>
131+ }
113132
114- const status = _get(this.props, 'challenge.status', ChallengeStatus.none)
115- const hasTasks = _get(this.props, 'challenge.actions.total', 0) > 0
133+ {manager.canWriteProject(this.props.challenge.parent) &&
134+ <React.Fragment>
135+ <Link to={`/admin/project/${projectId}/` +
136+ `challenge/${this.props.challenge.id}/edit`}
137+ className="mr-text-green-lighter hover:mr-text-white mr-mr-4">
138+ <FormattedMessage {...messages.editChallengeLabel } />
139+ </Link>
116140
117- return (
118- <div className="admin__manage challenge-dashboard">
119- <div className="admin__manage__header">
120- <nav className="breadcrumb" aria-label="breadcrumbs">
121- <ul>
122- <li>
123- <Link to='/admin/projects'>
124- <FormattedMessage {...manageMessages.manageHeader} />
125- </Link>
126- </li>
127- <li>
128- <Link to={`/admin/project/${projectId}`}>
129- {_get(this.props, 'challenge.parent.displayName') ||
130- _get(this.props, 'challenge.parent.name')}
131- </Link>
132- </li>
133- <li className="is-active">
141+ {_get(this.props, 'projects.length', 0) > 1 &&
142+ <Dropdown
143+ className="mr-dropdown--fixed"
144+ dropdownButton={dropdown => (
145+ // eslint-disable-next-line jsx-a11y/anchor-is-valid
146+ <a onClick={dropdown.toggleDropdownVisible}
147+ className="mr-text-green-lighter hover:mr-text-white mr-mr-4 mr-flex mr-items-center"
148+ >
149+ <FormattedMessage {...messages.moveChallengeLabel} />
150+ <SvgSymbol
151+ sym="icon-cheveron-down"
152+ viewBox="0 0 20 20"
153+ className="mr-fill-current mr-w-5 mr-h-5"
154+ />
155+ </a>
156+ )}
157+ dropdownContent={dropdown =>
158+ <ListManagedProjectItems
159+ {...this.props}
160+ currentProjectId={projectId}
161+ manager={manager}
162+ />
163+ }
164+ />
165+ }
166+
167+ {this.props.challenge.isRebuildable() &&
168+ <RebuildTasksControl {...this.props} />
169+ }
170+
171+ <Link to={{pathname: `/admin/project/${projectId}/` +
172+ `challenge/${this.props.challenge.id}/clone`,
173+ state: {cloneChallenge: true}}}
174+ className="mr-text-green-lighter hover:mr-text-white mr-mr-4">
175+ <FormattedMessage {...messages.cloneChallengeLabel } />
176+ </Link>
177+
178+ <ConfirmAction>
134179 {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
135- <a aria-current="page">
136- {this.props.challenge.name}
137- {this.props.loadingChallenge && <BusySpinner inline />}
180+ <a onClick={this.deleteChallenge}
181+ className="mr-text-green-lighter hover:mr-text-white mr-mr-4">
182+ <FormattedMessage {...messages.deleteChallengeLabel } />
138183 </a>
139- </li>
140- </ul>
141- </nav>
142-
143- <div className="columns admin__manage__controls">
144- {hasTasks && isUsableChallengeStatus(status, true) &&
145- <div className="column is-narrow admin__manage__controls--control">
146- <Link to={`/challenge/${this.props.challenge.id}`}>
147- <FormattedMessage {...messages.startChallengeLabel} />
148- </Link>
149- </div>
150- }
151-
152- {manager.canWriteProject(this.props.challenge.parent) &&
153- <React.Fragment>
154- <div className="column is-narrow admin__manage__controls--control">
155- <Link to={`/admin/project/${projectId}/` +
156- `challenge/${this.props.challenge.id}/edit`}>
157- <FormattedMessage {...messages.editChallengeLabel } />
158- </Link>
159- </div>
160-
161- {_get(this.props, 'projects.length', 0) > 1 &&
162- <div className="column is-narrow admin__manage__controls--control">
163- <DeactivatableDropdownButton options={managedProjectOptions}
164- onSelect={this.moveChallenge}
165- emptyContent={<FormattedMessage {...messages.noProjects} />}>
166- {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
167- <a>
168- <FormattedMessage {...messages.moveChallengeLabel} />
169- <div className="basic-dropdown-indicator" />
170- </a>
171- </DeactivatableDropdownButton>
172- </div>
173- }
174-
175- {this.props.challenge.isRebuildable() &&
176- <div className="column is-narrow admin__manage__controls--control">
177- <RebuildTasksControl {...this.props} />
178- </div>
179- }
180-
181- <div className="column is-narrow admin__manage__controls--control">
182- <Link to={{pathname: `/admin/project/${projectId}/` +
183- `challenge/${this.props.challenge.id}/clone`,
184- state: {cloneChallenge: true}}}>
185- <FormattedMessage {...messages.cloneChallengeLabel } />
186- </Link>
187- </div>
188-
189- <div className="column is-narrow admin__manage__controls--control">
190- <ConfirmAction>
191- {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
192- <a className='button is-clear' onClick={this.deleteChallenge}>
193- <SvgSymbol sym='trash-icon' className='icon' viewBox='0 0 20 20' />
194- </a>
195- </ConfirmAction>
196- </div>
197- </React.Fragment>
198- }
199- </div>
184+ </ConfirmAction>
185+ </React.Fragment>
186+ }
200187 </div>
188+ </div>
189+ )
201190
202- <WidgetWorkspace {...this.props} />
191+ return (
192+ <div className="admin__manage challenge-dashboard">
193+ <WidgetWorkspace
194+ {...this.props}
195+ className="mr-mt-4"
196+ workspaceEyebrow={pageHeader}
197+ />
203198 </div>
204199 )
205200 }
206201}
207202
203+ const ListManagedProjectItems = function(props) {
204+ const projectItems = _compact(_map(props.projects, project => {
205+ if (project.id === props.currentProjectId ||
206+ !props.manager.canWriteProject(project)) {
207+ return null
208+ }
209+
210+ return (
211+ <li key={`project-${project.id}`}>
212+ {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
213+ <a
214+ onClick={() => props.moveChallenge(props.challenge.id, project.id)}
215+ >
216+ {project.displayName ? project.displayName : project.name}
217+ </a>
218+ </li>
219+ )
220+ }))
221+
222+ return _isEmpty(projectItems) ?
223+ <FormattedMessage {...messages.noProjects} /> : (
224+ <ol className="mr-list-dropdown">
225+ {projectItems}
226+ </ol>
227+ )
228+ }
229+
208230ChallengeDashboard.propTypes = {
209231 /** The parent project of the challenge */
210232 project: PropTypes.object,
0 commit comments