Skip to content

Commit

Permalink
Darien classrooms table (#74)
Browse files Browse the repository at this point in the history
* Add program filter

* Refactoring to enable Darien's classrooms table

* Use non-spec program filter

* Add conditional for determine which classrooms table to use

* Add forgotten files

* Remove cruft

* Fix classroom delete regressions
  • Loading branch information
srallen authored and shaunanoordin committed Oct 24, 2017
1 parent a1176a6 commit 8777254
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 111 deletions.
2 changes: 1 addition & 1 deletion src/components/astro/AstroClassroomsTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const AstroClassroomsTable = (props) => {
return (
<Box>
<ExportModal onClose={props.onExportModalClose} assignment={props.assignmentToExport} />

{props.children}
<Table className="manager-table">
<thead className="manager-table__headers">
<TableRow>
Expand Down
20 changes: 1 addition & 19 deletions src/components/classrooms/ClassroomsManager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
} from '../../ducks/classrooms';

import ClassroomFormContainer from '../../containers/classrooms/ClassroomFormContainer';
import ConfirmationDialog from '../common/ConfirmationDialog';
import ClassroomsTableContainer from '../../containers/classrooms/ClassroomsTableContainer';

const ClassroomsManager = (props) => {
Expand All @@ -24,14 +23,6 @@ const ClassroomsManager = (props) => {
<Paragraph align="start" size="small">{props.classroomInstructions}</Paragraph>
<Button type="button" primary={true} label="Create New Classroom" onClick={props.toggleFormVisibility} />
</Box>
<ConfirmationDialog
confirmationButtonLabel="Delete"
onConfirmation={props.deleteClassroom}
onClose={props.closeConfirmationDialog}
showConfirmationDialog={props.showConfirmationDialog}
>
<Paragraph size="small">Deleting a classroom will also delete the associated assignments.</Paragraph>
</ConfirmationDialog>
{props.showForm &&
<Layer closer={true} onClose={props.toggleFormVisibility}>
<ClassroomFormContainer heading="Create Classroom" submitLabel="Create" />
Expand All @@ -43,29 +34,20 @@ const ClassroomsManager = (props) => {
{props.classrooms.length === 0 && props.classroomsStatus === CLASSROOMS_STATUS.ERROR &&
<Paragraph>Error: Classrooms could not be loaded.</Paragraph>}
{(props.classrooms.length > 0 && props.classroomsStatus === CLASSROOMS_STATUS.SUCCESS) &&
<ClassroomsTableContainer
maybeDeleteClassroom={props.maybeDeleteClassroom}
match={props.match}
/>}
<ClassroomsTableContainer match={props.match} />}
</Box>
);
};

ClassroomsManager.defaultProps = {
classroomInstructions: 'First, make sure your students have set up a Zooniverse account. Then create a classroom and share the classroom\'s unique join URL with your students to keep track of their progress as they work through each assignment. Students must be logged in to their Zooniverse accounts first to be able to use the join link. Share the URL under View Project with your students for them to complete the assignment.',
closeConfirmationDialog: () => {},
deleteClassroom: () => {},
maybeDeleteClassroom: () => {},
showForm: false,
toggleFormVisibility: Actions.classrooms.toggleFormVisibility,
...CLASSROOMS_INITIAL_STATE
};

ClassroomsManager.propTypes = {
classroomInstructions: PropTypes.string,
closeConfirmationDialog: PropTypes.func,
deleteClassroom: PropTypes.func,
maybeDeleteClassroom: PropTypes.func,
showForm: PropTypes.bool,
toggleFormVisibility: PropTypes.func,
...CLASSROOMS_PROPTYPES
Expand Down
171 changes: 171 additions & 0 deletions src/components/darien/DarienClassroomsTable.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Actions } from 'jumpstate';
import CopyToClipboard from 'react-copy-to-clipboard';
import Box from 'grommet/components/Box';
import Table from 'grommet/components/Table';
import TableRow from 'grommet/components/TableRow';
import AddIcon from 'grommet/components/icons/base/Add';
import EditIcon from 'grommet/components/icons/base/Edit';
import CloseIcon from 'grommet/components/icons/base/Close';
import Anchor from 'grommet/components/Anchor';
import Button from 'grommet/components/Button';
import Paragraph from 'grommet/components/Paragraph';
import Spinning from 'grommet/components/icons/Spinning';
import Timestamp from 'grommet/components/Timestamp';

import { config } from '../../lib/config';
import {
ASSIGNMENTS_STATUS, ASSIGNMENTS_INITIAL_STATE, ASSIGNMENTS_PROPTYPES
} from '../../ducks/assignments';
import {
CLASSROOMS_INITIAL_STATE, CLASSROOMS_PROPTYPES
} from '../../ducks/classrooms';

const DarienClassroomsTable = (props) => {
return (
<Box>
{props.children}
<Table className="manager-table">
<thead className="manager-table__headers">
<TableRow>
<th id="assignments" scope="col" className="manager-table__caption">Your Classrooms</th>
<th id="completed" scope="col" className="headers__header">Completed</th>
<th id="export" scope="col" className="headers__header">Due Date</th>
<th id="view-project" scope="col" className="headers__header">View Project</th>
</TableRow>
</thead>
{props.classrooms.map((classroom) => {
// TODO update URL once we have staging/production hosts

const joinURL = `${window.location.host}/#/${props.selectedProgram.slug}/students/classrooms/${classroom.id}/join?token=${classroom.joinToken}`;
// Can we get linked assignments with classrooms in single get request?
// No, if we want this, then we need to open an issue with the API
// TODO replace classifications_target with calculated percentage

// The trailing slash is inconsistent in React Router 4's match.url property...
const editURL = (props.match.url[props.match.url.length - 1] === '/') ? props.match.url : `${props.match.url}/`;
return (
<tbody className="manager-table__body" key={classroom.id}>
<TableRow>
<th className="manager-table__row-header" id="classroom" colSpan="4" scope="colgroup">
<Box pad="none" margin="none" justify="between" direction="row">
<span>
<Button
className="manager-table__button--edit"
path={`${editURL}classrooms/${classroom.id}`}
onClick={() => { Actions.classrooms.selectClassroom(classroom); }}
icon={<EditIcon size="small" />}
/>
{' '}{classroom.name}{' '}
<CopyToClipboard text={joinURL} onCopy={() => { Actions.classrooms.setToastState({ status: 'ok', message: 'Copied join link.' }); }}>
<Button type="button" className="manager-table__button--as-link" plain={true} onClick={() => {}}>
Copy Join Link
</Button>
</CopyToClipboard>
</span>
<Button
className="manager-table__button--delete"
type="button"
onClick={props.maybeDeleteClassroom.bind(null, classroom.id)}
>
<CloseIcon size="small" />
</Button>
</Box>
</th>
</TableRow>
{((props.assignments[classroom.id] &&
props.assignments[classroom.id].length === 0 &&
props.assignmentsStatus === ASSIGNMENTS_STATUS.FETCHING) ||
(Object.keys(props.assignments).length === 0 &&
props.assignmentsStatus === ASSIGNMENTS_STATUS.FETCHING)) &&
<TableRow className="manager-table__row-data">
<td colSpan="4"><Spinning /></td>
</TableRow>}
{(props.assignments[classroom.id] &&
props.assignments[classroom.id].length === 0 &&
props.assignmentsStatus === ASSIGNMENTS_STATUS.SUCCESS) &&
<TableRow className="manager-table__row-data">
<td colSpan="4">
<Box pad="none" margin="none" justify="between" direction="row">
<Paragraph>No assignments have been created yet.</Paragraph>
<Button
className="manager-table__button--create"
onClick={props.toggleAssignmentForm}
type="button"
>
<AddIcon size="small" />
</Button>
</Box>
</td>
</TableRow>}
{(props.assignments[classroom.id] &&
props.assignmentsStatus === ASSIGNMENTS_STATUS.SUCCESS) &&
props.assignments[classroom.id].map((assignment) => {
return (
<TableRow className="manager-table__row-data" key={assignment.id}>
<td headers="classroom assignments">
{assignment.name}
<Button
className="manager-table__button--edit"
onClick={props.toggleAssignmentForm}
icon={<EditIcon size="small" />}
/>
</td>
<td headers="classroom completed">
{(assignment.metadata && assignment.metadata.classifications_target) ?
assignment.metadata.classifications_target : ''}
</td>
<td headers="classroom export">
<Timestamp value={assignment.metadata.duedate} />
</td>
<td headers="classroom view-project">
<Anchor
className="manager-table__link"
href={`${config.zooniverse}/projects/wildcam/wildcam-darien/classify?workflow=${assignment.workflow_id}`}
target="_blank"
rel="noopener noreferrer"
>
Project Page{' '}
<i className="fa fa-mail-forward" aria-hidden="true" />
<Button
className="manager-table__button--delete"
type="button"
onClick={props.maybeDeleteAssignment.bind(null, assignment.id)}
>
<CloseIcon size="small" />
</Button>
</Anchor>
</td>
</TableRow>
);
})}
</tbody>
);
})}
</Table>
</Box>
);
};

DarienClassroomsTable.defaultProps = {
closeConfirmationDialog: () => {},
maybeDeleteAssignment: () => {},
maybeDeleteClassroom: () => {},
selectClassroom: () => {},
toggleAssignmentForm: () => {},
...CLASSROOMS_INITIAL_STATE,
...ASSIGNMENTS_INITIAL_STATE
};

DarienClassroomsTable.propTypes = {
closeConfirmationDialog: PropTypes.func,
maybeDeleteAssignment: PropTypes.func,
maybeDeleteClassroom: PropTypes.func,
selectClassroom: PropTypes.func,
toggleAssignmentForm: PropTypes.func,
...CLASSROOMS_PROPTYPES,
...ASSIGNMENTS_PROPTYPES
};

export default DarienClassroomsTable;
2 changes: 1 addition & 1 deletion src/components/darien/DarienHome.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class DarienHome extends React.Component {
<Paragraph>Are you an educator or a student/explorer? Make your selection to get started!</Paragraph>
</Box>
<Box align="center" direction="row" justify="between" pad="medium" size="medium">
<Button path="/wildcam-darien-lab/eduactors/" label="Educator" />
<Button path="/wildcam-darien-lab/educators/" label="Educator" />
<Button path="/wildcam-darien-lab/students/" label="Explorer" />
</Box>
</Section>
Expand Down
47 changes: 47 additions & 0 deletions src/containers/astro/AstroClassroomTableContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { Actions } from 'jumpstate';
import AstroClassroomsTable from '../../components/astro/AstroClassroomsTable';

class AstroClassroomsTableContainer extends React.Component {
constructor() {
super();

this.state = {
toExport: {
assignment: {},
classroom: {}
}
};

this.onExportModalClose = this.onExportModalClose.bind(this);
this.showExportModal = this.showExportModal.bind(this);
}

onExportModalClose() {
this.setState({ toExport: { assignment: {}, classroom: {} } });

Actions.caesarExports.showModal();
}

showExportModal(assignment, classroom) {
this.setState({ toExport: { assignment, classroom } });

Actions.caesarExports.showModal();
Actions.getCaesarExport({ assignment, classroom });
}

render() {
return (
<AstroClassroomsTable
{...this.props}
assignmentToExport={this.state.toExport.assignment}
onExportModalClose={this.onExportModalClose}
showExportModal={this.showExportModal}
>
{this.props.children}
</AstroClassroomsTable>
);
}
}

export default AstroClassroomsTableContainer;
47 changes: 47 additions & 0 deletions src/containers/astro/AstroClassroomsTableContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import { Actions } from 'jumpstate';
import AstroClassroomsTable from '../../components/astro/AstroClassroomsTable';

class AstroClassroomsTableContainer extends React.Component {
constructor() {
super();

this.state = {
toExport: {
assignment: {},
classroom: {}
}
};

this.onExportModalClose = this.onExportModalClose.bind(this);
this.showExportModal = this.showExportModal.bind(this);
}

onExportModalClose() {
this.setState({ toExport: { assignment: {}, classroom: {} } });

Actions.caesarExports.showModal();
}

showExportModal(assignment, classroom) {
this.setState({ toExport: { assignment, classroom } });

Actions.caesarExports.showModal();
Actions.getCaesarExport({ assignment, classroom });
}

render() {
return (
<AstroClassroomsTable
{...this.props}
assignmentToExport={this.state.toExport.assignment}
onExportModalClose={this.onExportModalClose}
showExportModal={this.showExportModal}
>
{this.props.children}
</AstroClassroomsTable>
);
}
}

export default AstroClassroomsTableContainer;
10 changes: 5 additions & 5 deletions src/containers/classrooms/ClassroomFormContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ export class ClassroomFormContainer extends React.Component {
const assignments = this.props.selectedProgram.metadata.assignments;
if (classroom) this.autoCreateAssignments(assignments, classroom);
}
})
}).then(() => {
Actions.classrooms.toggleFormVisibility();
Actions.getClassroomsAndAssignments(this.props.selectedProgram);
});
}

updateClassroom() {
Expand Down Expand Up @@ -93,10 +96,7 @@ export class ClassroomFormContainer extends React.Component {
};

Actions.createAssignment(assignmentData);
})).then(() => {
Actions.classrooms.toggleFormVisibility();
Actions.getClassroomsAndAssignments(this.props.selectedProgram);
});
}));
}

render() {
Expand Down
Loading

0 comments on commit 8777254

Please sign in to comment.