Skip to content

Commit

Permalink
changes to tasks and mapping views
Browse files Browse the repository at this point in the history
  • Loading branch information
willemarcel committed Dec 11, 2019
1 parent 9d41120 commit f3ce768
Show file tree
Hide file tree
Showing 31 changed files with 554 additions and 117 deletions.
5 changes: 4 additions & 1 deletion frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { ListTeams, CreateTeam, EditTeam } from './views/teams';
import { ListCampaigns, CreateCampaign, EditCampaign } from './views/campaigns';
import { NotFound } from './views/notFound';
import { SelectTask } from './views/taskSelection';
import { MapTask } from './views/taskAction';
import { EmailVerification } from './views/verifyEmail';
import { ProjectEdit } from './views/projectEdit';

Expand Down Expand Up @@ -63,7 +64,9 @@ function App() {
<ProjectEdit path="manage/projects/:id" />
<ManageProjectsPage path="manage/projects/" />
<ManageProjectsPage path="projects/" />
<SelectTask path="projects/:id/map" />
<SelectTask path="projects/:id/tasks" />
<MapTask path="projects/:id/map" />
{/* <ValidateTask path="projects/:id/validate" /> */}
<ProjectDetailPage path="projects/:id" />
<Redirect from="project/:id" to="projects/:id" noThrow />
<NotFound default />
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/assets/styles/_extra.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
.vh-minus-200-ns {
height: calc(100vh - 200px);
}
.vh-minus-122-ns {
height: calc(100vh - 122px);
}
}

@media screen and (min-width: 66rem) {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/button.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';

export function Button({ onClick, children, className }: Object) {
export function Button({ onClick, children, className, disabled }: Object) {
return (
<button
onClick={onClick}
aria-pressed="false"
focusindex="0"
className={`${className || ''} br1 f5 bn pointer`}
className={`${className || ''} br1 f5 bn ${disabled ? 'o-50' : 'pointer'}`}
style={{ padding: '.75rem 1.5rem' }}
>
{children}
Expand Down
16 changes: 8 additions & 8 deletions frontend/src/components/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ const socialNetworks = [
];

export function Footer({ location }: Object) {
if (
location.pathname.endsWith('/map') ||
location.pathname.endsWith('/map/') ||
location.pathname.endsWith('/validate') ||
location.pathname.endsWith('/validate/') ||
location.pathname.endsWith('/new') ||
location.pathname.endsWith('/new/')
) {
const noFooterViews = [
'tasks',
'map',
'validate',
'new'
];
const activeView = location.pathname.split('/').filter(i => i !== "").splice(-1)[0];
if (noFooterViews.includes(activeView)) {
return <></>;
} else {
return (
Expand Down
13 changes: 3 additions & 10 deletions frontend/src/components/projectDetail/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import DueDateBox from '../projectcard/dueDateBox';
import { MappingLevelMessage } from '../mappingLevel';

import { TasksMap } from '../taskSelection/map.js';
import { HeaderLine } from '../taskSelection';
import { HeaderLine, TagLine } from '../taskSelection';
import { MappingTypes } from '../mappingTypes';
import { Imagery } from '../taskSelection/imagery';

import { htmlFromMarkdown } from './htmlFromMarkdown';
import { htmlFromMarkdown } from '../../utils/htmlFromMarkdown';
import { NewMapperFlow } from './newMapperFlow';
import { ShowReadMoreButton } from './showReadMoreButton';
import { ProjectDetailFooter } from './projectDetailFooter';
Expand Down Expand Up @@ -81,7 +81,6 @@ const ProjectDetailMap = props => {
taskBordersMap={taskBordersGeoJSON}
taskCentroidMap={centroidGeoJSON}
taskBordersOnly={taskBordersOnly}
projectId={props.project.projectId}
disableScrollZoom={true}
navigate={props.navigate}
type={props.type}
Expand Down Expand Up @@ -138,13 +137,7 @@ export const ProjectDetailLeft = props => {
<h3 className="f2 fw6 mt2 mb3 ttu barlow-condensed blue-dark">
{props.project.projectInfo && props.project.projectInfo.name}
</h3>
<span className="blue-light">{props.project.campaignTag}</span>
{props.project.countryTag && (
<span className="blue-light">
<span className="ph2">&#183;</span>
{props.project.countryTag.map(country => country).join(', ')}
</span>
)}
< TagLine campaigns={props.project.campaigns} countries={props.project.countryTag} />
</div>
<section className={`lh-copy h5 overflow-x-scroll`}>
<div className="pr2" dangerouslySetInnerHTML={htmlShortDescription} />
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/projectDetail/newMapperFlow.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import { TaskSelectionIcon, AreaIcon, SubmitWorkIcon } from '../svgIcons';
import messages from './messages';
import { FormattedMessage } from 'react-intl';

import messages from './messages';
import { TaskSelectionIcon, AreaIcon, SubmitWorkIcon } from '../svgIcons';

function MappingCard({ image, title, description }: Object) {
return (
<div className="db ph2-l pb3 w-100">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const ProjectDetailFooter = props => {
<AddToFavorites projectId={props.projectId} />
</div>
<div className="dib w-40 tr fr">
<Link to={`./map`} className="">
<Link to={`./tasks`} className="">
<Button className="white bg-red h3 w-100">
<FormattedMessage {...messages.contribute} />
</Button>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/projectcard/dueDateBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import humanizeDuration from 'humanize-duration';
import { ClockIcon } from '../svgIcons';
import messages from './messages';

export function DueDateBox({ intl, dueDate }: Object) {
function DueDateBox({ intl, dueDate, align="right" }: Object) {
if (dueDate === undefined) {
return null;
} else if (new Date(dueDate) === undefined) {
Expand All @@ -16,7 +16,7 @@ export function DueDateBox({ intl, dueDate }: Object) {

if (milliDifference > 0) {
return (
<span className="fr relative w-40 lh-solid f7 tr br1 link ph1 pv2 bg-grey-light blue-grey truncate mw4">
<span className={`relative w-40 lh-solid f7 tr br1 link ph1 pv2 bg-grey-light blue-grey truncate mw4 ${align === 'right' ? 'fr' : 'fl'}`}>
<span>
<ClockIcon className="absolute pl1 top-0 pt1 left-0" />
</span>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/projectcard/projectCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function ProjectCard({
<FormattedMessage {...messages.editProject} />
</Link>
<Link
to={`/projects/${projectId}/map`}
to={`/projects/${projectId}/tasks`}
className={`fr f6 di w-50 tc bg-red white bn ${linkCombo}`}
>
<FormattedMessage {...messages.projectTasks} />
Expand Down
242 changes: 242 additions & 0 deletions frontend/src/components/taskSelection/action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { navigate } from '@reach/router';
import ReactPlaceholder from 'react-placeholder';
import { FormattedMessage } from 'react-intl';

import messages from './messages';
import { ProjectInstructions } from './instructions';
import { HeaderLine } from './index';
import { TasksMap } from './map';
import { Button } from '../button';
import DueDateBox from '../projectcard/dueDateBox';
import { CheckIcon, CloseIcon } from '../svgIcons';
import { pushToLocalJSONAPI } from '../../network/genericJSONRequest';

export function TaskMapAction({ project, tasks, action, editor }) {
const [activeSection, setActiveSection] = useState('completion');
const tasksIds = tasks && tasks.features ? tasks.features.map(i => i.properties.taskId) : [];

return (
<div className="cf vh-minus-122-ns overflow-y-hidden">
<div className="w-70 fl h-100 relative">
<ReactPlaceholder
showLoadingAnimation={true}
type="media"
rows={26}
delay={10}
ready={tasks && tasks.features && tasks.features.length}
>
<TasksMap
mapResults={tasks}
className="dib w-100 fl h-100-ns vh-75"
taskBordersOnly={false}
/>
</ReactPlaceholder>
</div>
<div className="w-30 fr pt3 ph3 h-100 overflow-y-scroll">
<ReactPlaceholder
showLoadingAnimation={true}
rows={3}
ready={typeof project.projectId === 'number' && project.projectId > 0}
>
<HeaderLine author={project.author} projectId={project.projectId} />
<div className="cf pb3">
<h3 className="f2 fw6 mt2 mb3 ttu barlow-condensed blue-dark">
{project.projectInfo && project.projectInfo.name}
<span className="pl2">&#183;</span>
{tasksIds.map(task => (
<span className="red ph2">{`#${task}`}</span>
))}
</h3>
<div>
<DueDateBox dueDate={project.dueDate} align="left" />
</div>
</div>
<div className="cf">
<div className="cf ttu barlow-condensed f4 pv2 blue-dark">
<span
className={`mr4 pb2 pointer ${activeSection === 'completion' && 'bb b--blue-dark'}`}
onClick={() => setActiveSection('completion')}
>
<FormattedMessage {...messages.completion} />
</span>
<span
className={`mr4 pb2 pointer ${activeSection === 'instructions' &&
'bb b--blue-dark'}`}
onClick={() => setActiveSection('instructions')}
>
<FormattedMessage {...messages.instructions} />
</span>
<span
className={`mr4 pb2 pointer ${activeSection === 'history' && 'bb b--blue-dark'}`}
onClick={() => setActiveSection('history')}
>
<FormattedMessage {...messages.history} />
</span>
</div>
</div>
<div className="pt3">
{activeSection === 'completion' && <CompletionTab project={project} tasks={tasksIds} />}
{activeSection === 'instructions' && (
<ProjectInstructions
instructions={project.projectInfo && project.projectInfo.instructions}
/>
)}
{activeSection === 'history' && <div></div>}
</div>
</ReactPlaceholder>
</div>
</div>
);
}

function CompletionTab({ project, tasks, action }: Object) {
const token = useSelector(state => state.auth.get('token'));
const [selectedStatus, setSelectedStatus] = useState();
const [taskComment, setTaskComment] = useState('');
const radioInput = 'radio-input input-reset pointer v-mid dib h2 w2 mr2 br-100 ba b--blue-light';

const splitTask = () => {
pushToLocalJSONAPI(`projects/${project.projectId}/tasks/${tasks[0]}/actions/split/`, null, token)
.then(r => navigate(`../tasks/`));
}

const stopMapping = () => {
pushToLocalJSONAPI(
`projects/${project.projectId}/tasks/actions/stop-mapping/${tasks[0]}/`,
'{}',
token
).then(r => navigate(`../tasks/`));
}

const submitTask = () => {
if (selectedStatus) {
let url;
let payload = {comment: taskComment};
if (selectedStatus === 'MAPPED') {
url = `projects/${project.projectId}/tasks/actions/set-as-mapped/${tasks[0]}/`;
payload.status = 'MAPPED';
}
if (selectedStatus === 'READY') {
url = `projects/${project.projectId}/tasks/actions/stop-mapping/${tasks[0]}/`;
}
if (selectedStatus === 'BADIMAGERY') {
url = `projects/${project.projectId}/tasks/actions/stop-mapping/${tasks[0]}/`;
}
pushToLocalJSONAPI(
url,
JSON.stringify(payload),
token
).then(r => navigate(`../tasks/`));
}
}

return (
<div>
<CompletionInstructions />
<div className="cf">
<h4 className="ttu blue-grey f5">
<FormattedMessage {...messages.editStatus} />
</h4>
<p>
<input
type="radio"
value="MAPPED"
className={radioInput}
checked={selectedStatus === 'MAPPED'}
onClick={() => setSelectedStatus('MAPPED')}
/>
<label for="MAPPED">
<FormattedMessage {...messages.completelyMapped} />
</label>
</p>
<p>
<input
type="radio"
value="READY"
className={radioInput}
checked={selectedStatus === 'READY'}
onClick={() => setSelectedStatus('READY')}
/>
<label for="READY">
<FormattedMessage {...messages.incomplete} />
</label>
</p>
<p>
<input
type="radio"
value="BADIMAGERY"
className={radioInput}
checked={selectedStatus === 'BADIMAGERY'}
onClick={() => setSelectedStatus('BADIMAGERY')}
/>
<label for="BADIMAGERY">
<FormattedMessage {...messages.badImagery} />
</label>
</p>
</div>
<div className="cf">
<h4 className="ttu blue-grey f5">
<FormattedMessage {...messages.comment} />
</h4>
<p>
<textarea onChange={e => setTaskComment(e.target.value)} rows="2" className="w-100 pa2" />
</p>
</div>
<div className="cf">
<Button className="bg-blue-dark white w-50 fl" onClick={() => splitTask()}>
<FormattedMessage {...messages.splitTask} />
</Button>
<Button className="blue-dark bg-white w-50 fl" onClick={() => stopMapping()}>
<FormattedMessage {...messages.selectAnotherTask} />
</Button>
</div>
<div className="cf mv2">
<Button className="bg-red white w-100 fl" onClick={() => submitTask()} disabled={!selectedStatus}>
<FormattedMessage {...messages.submitTask} />
</Button>
</div>
</div>
);
}

function CompletionInstructions() {
const [active, setActive] = useState(true);
return (
<>
<div className={active ? 'dib ph4-l w-100 cf' : 'dn'}>
<h4 className="fw8 f5 blue-dark di">
<FormattedMessage {...messages.finishMappingTitle} />
</h4>
<span
className="br-100 bg-grey-light white h1 w1 fr pointer tc v-mid di"
onClick={() => setActive(false)}
>
<CloseIcon className="pv1" />
</span>
<div className="blue-grey">
<p>
<CheckBall />
<FormattedMessage {...messages.instructionsSelect} />
</p>
<p>
<CheckBall />
<FormattedMessage {...messages.instructionsComment} />
</p>
<p>
<CheckBall />
<FormattedMessage {...messages.instructionsSubmit} />
</p>
</div>
</div>
<div className={active ? 'bb b--grey-light w-100' : 'dn'}></div>
</>
);
}

const CheckBall = () => (
<span className="br-100 bg-red white h1 w1 ph1 mr2">
<CheckIcon height="10px" width="10px" />
</span>
);
Loading

0 comments on commit f3ce768

Please sign in to comment.