diff --git a/src/ducks/classrooms.js b/src/ducks/classrooms.js index 2ef9ebf7..97d69c35 100644 --- a/src/ducks/classrooms.js +++ b/src/ducks/classrooms.js @@ -171,7 +171,7 @@ Effect('createClassroom', (data) => { // Hmm.... Effects can only take one argument? Effect('updateClassroom', (data) => { Actions.classrooms.setStatus(CLASSROOMS_STATUS.UPDATING); - return put(`/teachers/classrooms/${data.id}`, data.payload) + return put(`/teachers/classrooms/${data.id}`, { data: { attributes: data.payload } } ) .then((response) => { if (!response) { throw 'ERROR (ducks/classrooms/updateClassroom): No response'; } if (response.ok) { diff --git a/src/ducks/programs.js b/src/ducks/programs.js index e22d7b6a..6bd3cbcf 100644 --- a/src/ducks/programs.js +++ b/src/ducks/programs.js @@ -274,7 +274,7 @@ Effect('createProgram', (data) => { Effect('updateProgram', (data) => { Actions.programs.setStatus(PROGRAMS_STATUS.UPDATING); - return put(`/programs/${data.id}`, data.payload) + return put(`/programs/${data.id}`, { data: { attributes: data.payload } }) .then((response) => { if (!response) { throw 'ERROR (ducks/programs/updateProgram): No response'; } if (response.ok) { diff --git a/src/lib/edu-api.js b/src/lib/edu-api.js index 32b14630..c4318e0c 100644 --- a/src/lib/edu-api.js +++ b/src/lib/edu-api.js @@ -29,7 +29,7 @@ export function put(endpoint, data) { return superagent.put(`${config.root}${endpoint}`) .set('Content-Type', 'application/json') .set('Authorization', apiClient.headers.Authorization) - .send({ data: { attributes: data } }) + .send(data) .then(response => response); } diff --git a/src/modules/wildcam-classrooms/components/AssignmentForm.jsx b/src/modules/wildcam-classrooms/components/AssignmentForm.jsx index 8c00facd..57c4ff1e 100644 --- a/src/modules/wildcam-classrooms/components/AssignmentForm.jsx +++ b/src/modules/wildcam-classrooms/components/AssignmentForm.jsx @@ -31,6 +31,7 @@ import Label from 'grommet/components/Label'; import List from 'grommet/components/List'; import ListItem from 'grommet/components/ListItem'; import TextInput from 'grommet/components/TextInput'; +import NumberInput from 'grommet/components/NumberInput'; import LinkPreviousIcon from 'grommet/components/icons/base/LinkPrevious'; import LinkNextIcon from 'grommet/components/icons/base/LinkNext'; @@ -76,7 +77,12 @@ const TEXT = { }, ASSIGNMENT_FORM: { NAME: 'Assignment name', - DESCRIPTION: 'Instructions for Students', + DESCRIPTION: 'Instructions for students', + CLASSIFICATIONS_TARGET: 'Number of subjects each student needs to classify', + DUEDATE: 'Due date', + }, + ASSIGNMENT_FORM_PLACEHOLDERS: { + DUEDATE: 'e.g. 2020-12-31', }, ERROR: { GENERAL: 'Something went wrong', @@ -91,6 +97,8 @@ const TEXT = { const INITIAL_FORM_DATA = { name: '', description: '', + classifications_target: '', + duedate: '', }; /* @@ -103,6 +111,7 @@ class AssignmentForm extends React.Component { this.state = { view: VIEWS.CREATE_NEW, form: INITIAL_FORM_DATA, //Contains basic Assignment data: name, description, etc. + formInitialised: false, //Has initialiseForm() already been run? filters: {}, subjects: [], students: [], @@ -127,7 +136,6 @@ class AssignmentForm extends React.Component { .../classrooms/123/assignments/456 - edit assignment 456 (i.e. assignment_id=456 supplied.) */ initialise(props = this.props) { - console.log('+++ initialise: '); const state = this.state; const classroom_id = (props.match && props.match.params) @@ -145,6 +153,7 @@ class AssignmentForm extends React.Component { //Data store update + Redundancy Check (prevent infinite loop, only trigger once) if (props.selectedClassroom !== selectedClassroom) { + //this.setState({ formInitialised: false }); Actions.wildcamClassrooms.setSelectedClassroom(selectedClassroom); } @@ -155,24 +164,13 @@ class AssignmentForm extends React.Component { } else { this.initialise_partTwo(props, classroom_id, assignment_id, props.assignmentsList); } - - //Check the connection to WildCam Maps: if the user recently selected - //Subjects for the Assignment, respect it. - console.log('+++ props.wccwcmSelectedSubjects: ', props.wccwcmSelectedSubjects); - if (props.wccwcmSelectedSubjects) { - this.setState({ - subjects: props.wccwcmSelectedSubjects, - filters: props.wccwcmSelectedFilters, - }); - Actions.wildcamMap.resetWccWcmAssignmentData(); - } } initialise_partTwo(props, classroom_id, assignment_id, assignmentsList) { //Create a new assignment if (!assignment_id) { //Note: there should never be assignment_id === 0 or '' this.setState({ view: VIEWS.CREATE_NEW }); - this.initialiseForm(null); + this.initialiseForm(props, null); //Edit an existing assignment... if we can find it. } else { @@ -188,7 +186,6 @@ class AssignmentForm extends React.Component { //Also extract initial subjects and filters used by the Assignment if (!this.state.subjects || this.state.subjects.length === 0) { - console.log('+++ selectedAssignment: ', selectedAssignment); const newSubjects = (selectedAssignment.metadata && selectedAssignment.metadata.subjects) ? selectedAssignment.metadata.subjects.map((subject) => { return { @@ -208,7 +205,7 @@ class AssignmentForm extends React.Component { //View update this.setState({ view: VIEWS.EDIT_EXISTING }); - this.initialiseForm(selectedAssignment); + this.initialiseForm(props, selectedAssignment); //Otherwise, uh oh. } else { @@ -223,28 +220,65 @@ class AssignmentForm extends React.Component { /* Initialises the classroom form. */ - initialiseForm(selectedAssignment) { + initialiseForm(props, selectedAssignment) { + //Only run this once per page load, thank you. + if (this.state.formInitialised) return; + this.setState({ formInitialised: true }); + if (!selectedAssignment) { this.setState({ form: INITIAL_FORM_DATA }); } else { const originalForm = INITIAL_FORM_DATA; const updatedForm = {}; Object.keys(originalForm).map((key) => { - updatedForm[key] = (selectedAssignment && selectedAssignment[key]) - ? selectedAssignment[key] - : originalForm[key]; + //The structure for Assignments is weird. + if (selectedAssignment && selectedAssignment.metadata && selectedAssignment.metadata[key]) { + updatedForm[key] = selectedAssignment.metadata[key]; + } else if (selectedAssignment && selectedAssignment.attributes && selectedAssignment.attributes[key]) { + updatedForm[key] = selectedAssignment.attributes[key]; + } else { + updatedForm[key] = originalForm[key]; + } }); this.setState({ form: updatedForm }); } + + //WildCam Map Selected Subjects: + //Check the connection to WildCam Maps to see if the user recently selected + //Subjects for the Assignment. + if (props.wccwcmSelectedSubjects && props.wccwcmSavedAssignmentState) { + this.setState({ + subjects: props.wccwcmSelectedSubjects, + filters: props.wccwcmSelectedFilters, + form: { + ...this.state.form, + ...props.wccwcmSavedAssignmentState, + classifications_target: props.wccwcmSelectedSubjects.length, + } + }); + Actions.wildcamMap.resetWccWcmAssignmentData(); + } } // ---------------------------------------------------------------- updateForm(e) { + let val = e.target.value; + + //Special case: classificatons_target + //The number of Classfications a Student needs to do cannot exceed the amount of Subjects selected. + if (e.target.id === 'classifications_target') { + let maxVal = (this.state.subjects) ? this.state.subjects.length : 0; + val = parseInt(val); + if (isNaN(val)) val = 0; + val = Math.min(maxVal, val); + val = Math.max(0, val); + } + this.setState({ form: { ...this.state.form, - [e.target.id]: e.target.value + [e.target.id]: val, } }); @@ -305,7 +339,7 @@ class AssignmentForm extends React.Component { assignmentData: state.form, filters, subjects, - students: state.students, + students: state.students, }).then(() => { //Message Actions.wildcamClassrooms.setToast({ message: TEXT.SUCCESS.ASSIGNMENT_EDITED, status: 'ok' }); @@ -428,6 +462,21 @@ class AssignmentForm extends React.Component { +
+ + + +
+ + { + //TODO: add (optional) Assignment link for students? + } + + {(state.subjects && state.subjects.length > 0) && ( +
+ + + +
+ )} + {TEXT.HEADINGS.STUDENTS} diff --git a/src/modules/wildcam-classrooms/components/SubjectsList.jsx b/src/modules/wildcam-classrooms/components/SubjectsList.jsx index 2c1f407c..9942ab6a 100644 --- a/src/modules/wildcam-classrooms/components/SubjectsList.jsx +++ b/src/modules/wildcam-classrooms/components/SubjectsList.jsx @@ -105,7 +105,7 @@ class SubjectsList extends React.Component { return ( {TEXT.HEADINGS.SUBJECTS} @@ -124,8 +124,10 @@ class SubjectsList extends React.Component { className="button" label={TEXT.ACTIONS.SELECT_SUBJECTS} onClick={() => { - //Save the return path + //Save the return path, assignment state, and the initial filters to be used by the map. Actions.wildcamMap.setWccWcmAssignmentPath(props.location.pathname); + Actions.wildcamMap.setWccWcmSavedAssignmentState(props.assignmentStateForSaving); + if (props.filters) Actions.wildcamMap.setFilters(props.filters); //Transition to: WildCam Map props.history.push(props.wccwcmMapPath); @@ -152,7 +154,17 @@ class SubjectsList extends React.Component { return Object.keys(props.filters).map((key, index) => { const val = props.filters[key]; return ( -
{key} : {val}
+
{key} : {(()=>{ + if (Array.isArray(val, index)) { + let output = ''; + val.map((v) => { + output += ((output !== '') ? ', ' : '' ) + v; + }); + return output; + } else { + return val; + } + })()}
); }); })()} @@ -198,6 +210,7 @@ class SubjectsList extends React.Component { SubjectsList.defaultProps = { filters: null, subjects: [], + assignmentStateForSaving: null, // ---------------- ...WILDCAMCLASSROOMS_INITIAL_STATE, ...WILDCAMMAP_INITIAL_STATE, @@ -206,6 +219,7 @@ SubjectsList.defaultProps = { SubjectsList.propTypes = { filters: PropTypes.object, subjects: PropTypes.array, + assignmentStateForSaving: PropTypes.object, // ---------------- ...WILDCAMCLASSROOMS_PROPTYPES, ...WILDCAMMAP_PROPTYPES, diff --git a/src/modules/wildcam-classrooms/containers/WildCamClassrooms.jsx b/src/modules/wildcam-classrooms/containers/WildCamClassrooms.jsx index 3738bf28..151a1583 100644 --- a/src/modules/wildcam-classrooms/containers/WildCamClassrooms.jsx +++ b/src/modules/wildcam-classrooms/containers/WildCamClassrooms.jsx @@ -106,16 +106,6 @@ class WildCamClassroom extends React.Component { /> - - -

Debug Panel

- - Classrooms Status: [{props.classroomsStatus}]
- Classrooms Count: [{props.classroomsList && props.classroomsList.length}]
- Assignments Status: [{props.assignmentsStatus}]
- Assignments Count: [{props.assignmentsList && props.assignmentsList.length}]
-
-
); diff --git a/src/modules/wildcam-classrooms/ducks/index.js b/src/modules/wildcam-classrooms/ducks/index.js index ac96d45c..c0014d21 100644 --- a/src/modules/wildcam-classrooms/ducks/index.js +++ b/src/modules/wildcam-classrooms/ducks/index.js @@ -357,7 +357,7 @@ Effect('wcc_teachers_editClassroom', ({ selectedClassroom, classroomData }) => { Actions.wildcamClassrooms.setClassroomsStatus(WILDCAMCLASSROOMS_DATA_STATUS.SENDING); - return put(`/teachers/classrooms/${selectedClassroom.id}`, classroomData) //NOTE: the put() function requires a different argument format than post(). + return put(`/teachers/classrooms/${selectedClassroom.id}`, { data: { attributes: classroomData } }) .then((response) => { if (!response) { throw 'ERROR (ducks/wildcam-classrooms/ducks/wcc_teachers_editClassrooms): No response'; } if (response.ok) { @@ -676,7 +676,7 @@ Effect('wcc_editAssignment', ({ selectedAssignment, assignmentData, students = [ } }; - return put(`/assignments/${selectedAssignment.id}`, assignmentData) //NOTE: the put() function requires a different argument format than post(). + return put(`/assignments/${selectedAssignment.id}`, requestBody) .then((response) => { if (!response) { throw 'ERROR (ducks/wildcam-classrooms/ducks/wcc_teachers_editClassrooms): No response'; } if (response.ok) { diff --git a/src/modules/wildcam-map/containers/MapControls.jsx b/src/modules/wildcam-map/containers/MapControls.jsx index 2ea0802a..42350f8d 100644 --- a/src/modules/wildcam-map/containers/MapControls.jsx +++ b/src/modules/wildcam-map/containers/MapControls.jsx @@ -74,7 +74,10 @@ class MapControls extends React.Component { return ( - + diff --git a/src/modules/wildcam-map/ducks/index.js b/src/modules/wildcam-map/ducks/index.js index 5c93cba4..a381298b 100644 --- a/src/modules/wildcam-map/ducks/index.js +++ b/src/modules/wildcam-map/ducks/index.js @@ -70,6 +70,7 @@ const WILDCAMMAP_INITIAL_STATE = { //Connection between WildCam Classroom and WildCam Map wccwcmMapPath: '', //The URL/path that the Teacher is taken to when they click on "Select subject" in the WildCam Classroom - Create Assignment stage. Must be registered early on in the Program. wccwcmAssignmentPath: '', //The URL/path that the Teacher is returned to when they finally finish selecting subjects on the WildCam Map. + wccwcmSavedAssignmentState: null, wccwcmSelectedSubjects: null, wccwcmSelectedFilters: null, }; @@ -105,6 +106,7 @@ const WILDCAMMAP_PROPTYPES = { wccwcmMapPath: PropTypes.string, wccwcmAssignmentPath: PropTypes.string, + wccwcmSavedAssignmentState: PropTypes.object, wccwcmSelectedSubjects: PropTypes.array, wccwcmSelectedFilters: PropTypes.object, }; @@ -183,6 +185,10 @@ const setActiveCameraDataStatus = (state, activeCameraDataStatus) => { return { ...state, activeCameraDataStatus }; }; +const setFilters = (state, filters) => { + return { ...state, filters }; +}; + /* Adds to a multi-choice filter selection. */ const addFilterSelectionItem = (state, item) => { @@ -229,6 +235,7 @@ const resetWccWcmAssignmentData = (state) => { ...state, //Maintain the Map Path, however wccwcmAssignmentPath: WILDCAMMAP_INITIAL_STATE.wccwcmAssignmentPath, + wccwcmSavedAssignmentState: WILDCAMMAP_INITIAL_STATE.wccwcmSavedAssignmentState, wccwcmSelectedSubjects: WILDCAMMAP_INITIAL_STATE.wccwcmSelectedSubjects, wccwcmSelectedFilters: WILDCAMMAP_INITIAL_STATE.wccwcmSelectedFilters, } @@ -239,6 +246,9 @@ const setWccWcmMapPath = (state, wccwcmMapPath) => { const setWccWcmAssignmentPath = (state, wccwcmAssignmentPath) => { return { ...state, wccwcmAssignmentPath }; } +const setWccWcmSavedAssignmentState = (state, wccwcmSavedAssignmentState) => { + return { ...state, wccwcmSavedAssignmentState }; +} const setWccWcmSelectedSubjects = (state, wccwcmSelectedSubjects) => { return { ...state, wccwcmSelectedSubjects }; } @@ -386,12 +396,14 @@ const wildcamMap = State('wildcamMap', { setActiveCameraMetadataStatus, setActiveCameraData, setActiveCameraDataStatus, + setFilters, addFilterSelectionItem, removeFilterSelectionItem, setFilterSelectionItem, resetWccWcmAssignmentData, setWccWcmMapPath, setWccWcmAssignmentPath, + setWccWcmSavedAssignmentState, setWccWcmSelectedSubjects, setWccWcmSelectedFilters, }); diff --git a/src/programs/darien/DarienProgram.jsx b/src/programs/darien/DarienProgram.jsx index f433a0c1..07c01f37 100644 --- a/src/programs/darien/DarienProgram.jsx +++ b/src/programs/darien/DarienProgram.jsx @@ -22,35 +22,44 @@ import Status401 from '../../components/common/Status401'; import Status404 from '../../components/common/Status404'; import GenericStatusPage from '../../components/common/GenericStatusPage'; -function DarienProgram(props) { - if (!props.initialised) { //User status unknown: wait. - return (); - } else if (!props.selectedProgram) { //Anomaly: program status not set. - //Users should _not_ see this, but might due to weird lifecycle/timing issues. - return (); - } else { - +class DarienProgram extends React.Component { + constructor() { + super(); + } + + componentWillReceiveProps(props = this.props) { //Register the connection between the WildCam Classrooms and the WildCam Maps. Actions.wildcamMap.setWccWcmMapPath(`${props.match.url}/map`); + } - if (props.user) { //User logged in: give access to all locations. - return ( - - - - - - - ); - } else { //User not logged in: give limited access. - return ( - - - - - - - ); + render() { + const props = this.props; + + if (!props.initialised) { //User status unknown: wait. + return (); + } else if (!props.selectedProgram) { //Anomaly: program status not set. + //Users should _not_ see this, but might due to weird lifecycle/timing issues. + return (); + } else { + if (props.user) { //User logged in: give access to all locations. + return ( + + + + + + + ); + } else { //User not logged in: give limited access. + return ( + + + + + + + ); + } } } } diff --git a/src/styles/components/wildcam-map.styl b/src/styles/components/wildcam-map.styl index a498960c..47f5e53e 100644 --- a/src/styles/components/wildcam-map.styl +++ b/src/styles/components/wildcam-map.styl @@ -40,6 +40,9 @@ &.selected background: TEAL_LIGHT + .wccwcm-connector + border: 1px solid GREY_5 + .zooniversal-translator .selected background: TEAL_LIGHT