From c75eff38c79947fc1d3c9739f305da1c74482742 Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Thu, 25 Jan 2018 13:40:58 +0100 Subject: [PATCH 1/9] Change some icons --- .../FailuresListItem/FailuresListItem.js | 7 ++++--- src/components/layout/Sidebar/Admin.js | 2 +- src/pages/SubmissionFailures/SubmissionFailures.js | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/SubmissionFailures/FailuresListItem/FailuresListItem.js b/src/components/SubmissionFailures/FailuresListItem/FailuresListItem.js index c4b01c858..4b6d61669 100644 --- a/src/components/SubmissionFailures/FailuresListItem/FailuresListItem.js +++ b/src/components/SubmissionFailures/FailuresListItem/FailuresListItem.js @@ -16,9 +16,10 @@ const FailuresListItem = ({ id, createActions, failure }) => } >
- {failure.type === 'broker_reject' && } - {failure.type === 'evaluation_failure' && } - {failure.type === 'loading_failure' && } + {failure.type === 'broker_reject' && } + {failure.type === 'evaluation_failure' && + } + {failure.type === 'loading_failure' && }
diff --git a/src/components/layout/Sidebar/Admin.js b/src/components/layout/Sidebar/Admin.js index d59a05c70..789064e35 100644 --- a/src/components/layout/Sidebar/Admin.js +++ b/src/components/layout/Sidebar/Admin.js @@ -43,7 +43,7 @@ const Admin = ({ link={USERS_URI} /> ), - iconName: 'fort-awesome' + iconName: 'bomb' } ]} > @@ -110,7 +110,7 @@ class SubmissionFailures extends Component { + + } + /> + } + + + + + + ); } @@ -58,19 +118,27 @@ class SisIntegration extends Component { SisIntegration.propTypes = { userId: PropTypes.string.isRequired, isSuperAdmin: PropTypes.bool.isRequired, - loadAsync: PropTypes.func.isRequired + loadAsync: PropTypes.func.isRequired, + fetchStatus: PropTypes.string, + createNewTerm: PropTypes.func, + deleteTerm: PropTypes.func, + sisTerms: PropTypes.array.isRequired }; const mapStateToProps = state => { return { userId: loggedInUserIdSelector(state), - isSuperAdmin: isLoggedAsSuperAdmin(state) + isSuperAdmin: isLoggedAsSuperAdmin(state), + fetchStatus: fetchManyStatus(state), + sisTerms: readySisTermsSelector(state) }; }; const mapDispatchToProps = (dispatch, { params }) => ({ loadAsync: (userId, isSuperAdmin) => - SisIntegration.loadAsync(params, dispatch, userId, isSuperAdmin) + SisIntegration.loadAsync(params, dispatch, userId, isSuperAdmin), + createNewTerm: data => dispatch(create(data)), + deleteTerm: id => dispatch(deleteTerm(id)) }); export default connect(mapStateToProps, mapDispatchToProps)(SisIntegration); diff --git a/src/redux/modules/sisTerms.js b/src/redux/modules/sisTerms.js new file mode 100644 index 000000000..57b0121f3 --- /dev/null +++ b/src/redux/modules/sisTerms.js @@ -0,0 +1,30 @@ +import { handleActions } from 'redux-actions'; +import factory, { initialState } from '../helpers/resourceManager'; + +/** + * Create actions & reducer + */ + +const resourceName = 'sisTerms'; +const { actions, reduceActions } = factory({ + resourceName, + apiEndpointFactory: id => `/extensions/sis/terms/${id}` +}); + +export const fetchManyEndpoint = '/extensions/sis/terms'; + +export const fetchAllTerms = actions.fetchMany({ + endpoint: fetchManyEndpoint +}); +export const fetchTermsIfNeeded = actions.fetchIfNeeded; +export const fetchTermIfNeeded = actions.fetchOneIfNeeded; +export const create = actions.addResource; +export const editTerm = actions.updateResource; +export const deleteTerm = actions.removeResource; + +const reducer = handleActions( + Object.assign({}, reduceActions, {}), + initialState +); + +export default reducer; diff --git a/src/redux/reducer.js b/src/redux/reducer.js index 0300459ec..f5f5186ac 100644 --- a/src/redux/reducer.js +++ b/src/redux/reducer.js @@ -44,6 +44,7 @@ import sisStatus from './modules/sisStatus'; import sisSubscribedGroups from './modules/sisSubscribedGroups'; import sisSupervisedCourses from './modules/sisSupervisedCourses'; import sisPossibleParents from './modules/sisPossibleParents'; +import sisTerms from './modules/sisTerms'; import referenceSolutionEvaluations from './modules/referenceSolutionEvaluations'; import submissionEvaluations from './modules/submissionEvaluations'; import submissionFailures from './modules/submissionFailures'; @@ -92,6 +93,7 @@ const createRecodexReducers = token => ({ sisSubscribedGroups, sisSupervisedCourses, sisPossibleParents, + sisTerms, submissionEvaluations, submissionFailures }); diff --git a/src/redux/selectors/sisTerms.js b/src/redux/selectors/sisTerms.js new file mode 100644 index 000000000..30b9ca3d2 --- /dev/null +++ b/src/redux/selectors/sisTerms.js @@ -0,0 +1,23 @@ +import { createSelector } from 'reselect'; +import { fetchManyEndpoint } from '../modules/sisTerms'; +import { isReady, getJsData } from '../helpers/resourceManager'; + +const getTerms = state => state.sisTerms; +const getResources = exercises => exercises.get('resources'); + +export const termsSelector = createSelector(getTerms, getResources); +export const termSelector = termId => + createSelector(termsSelector, terms => terms.get(termId)); + +export const fetchManyStatus = createSelector(getTerms, state => + state.getIn(['fetchManyStatus', fetchManyEndpoint]) +); + +export const readySisTermsSelector = createSelector(termsSelector, terms => + terms + .toList() + .filter(isReady) + .map(getJsData) + .sort((a, b) => a.year * 10 + a.term < b.year * 10 + b.term) + .toArray() +); From c7e4118e2c1b37fae9d9defd224cde6ab0aba3d6 Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Sun, 28 Jan 2018 23:05:50 +0100 Subject: [PATCH 6/9] Edit SIS terms Initial values do not work yet ... --- .../SisIntegration/EditTerm/EditTerm.js | 110 ++++++++++++++++++ .../SisIntegration/EditTerm/index.js | 1 + src/pages/SisIntegration/SisIntegration.js | 50 +++++++- 3 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 src/components/SisIntegration/EditTerm/EditTerm.js create mode 100644 src/components/SisIntegration/EditTerm/index.js diff --git a/src/components/SisIntegration/EditTerm/EditTerm.js b/src/components/SisIntegration/EditTerm/EditTerm.js new file mode 100644 index 000000000..888e27e1b --- /dev/null +++ b/src/components/SisIntegration/EditTerm/EditTerm.js @@ -0,0 +1,110 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Modal, Button } from 'react-bootstrap'; +import { CloseIcon } from '../../icons'; +import { FormattedMessage } from 'react-intl'; +import { Field, reduxForm } from 'redux-form'; +import DatetimeField from '../../forms/Fields/DatetimeField'; +import SubmitButton from '../../forms/SubmitButton'; + +const EditTerm = ({ + isOpen, + onClose, + submitting, + handleSubmit, + anyTouched, + submitFailed = false, + submitSucceeded = false, + invalid, + reset +}) => + + + + + + + + + } + /> + + } + /> + + } + /> + + + handleSubmit(data).then(() => reset())} + submitting={submitting} + dirty={anyTouched} + hasSucceeded={submitSucceeded} + hasFailed={submitFailed} + invalid={invalid} + messages={{ + submit: ( + + ), + submitting: ( + + ), + success: ( + + ) + }} + /> + + + + ; + +EditTerm.propTypes = { + handleSubmit: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, + submitFailed: PropTypes.bool, + anyTouched: PropTypes.bool, + submitSucceeded: PropTypes.bool, + submitting: PropTypes.bool, + invalid: PropTypes.bool, + reset: PropTypes.func, + onClose: PropTypes.func.isRequired, + isOpen: PropTypes.bool.isRequired +}; + +export default reduxForm({ form: 'edit-sis-term' })(EditTerm); diff --git a/src/components/SisIntegration/EditTerm/index.js b/src/components/SisIntegration/EditTerm/index.js new file mode 100644 index 000000000..9e22b7450 --- /dev/null +++ b/src/components/SisIntegration/EditTerm/index.js @@ -0,0 +1 @@ +export default from './EditTerm'; diff --git a/src/pages/SisIntegration/SisIntegration.js b/src/pages/SisIntegration/SisIntegration.js index 28d1cbbba..d31446db7 100644 --- a/src/pages/SisIntegration/SisIntegration.js +++ b/src/pages/SisIntegration/SisIntegration.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { FormattedMessage } from 'react-intl'; import { Row, Col, Button } from 'react-bootstrap'; +import moment from 'moment'; import PageContent from '../../components/layout/PageContent'; import { isLoggedAsSuperAdmin } from '../../redux/selectors/users'; @@ -10,7 +11,8 @@ import { loggedInUserIdSelector } from '../../redux/selectors/auth'; import { fetchAllTerms, create, - deleteTerm + deleteTerm, + editTerm } from '../../redux/modules/sisTerms'; import FetchManyResourceRenderer from '../../components/helpers/FetchManyResourceRenderer'; import { @@ -21,9 +23,12 @@ import AddSisTermForm from '../../components/forms/AddSisTermForm/AddSisTermForm import TermsList from '../../components/SisIntegration/TermsList/TermsList'; import Box from '../../components/widgets/Box/Box'; import Confirm from '../../components/forms/Confirm'; -import { DeleteIcon } from '../../components/icons'; +import { EditIcon, DeleteIcon } from '../../components/icons'; +import EditTerm from '../../components/SisIntegration/EditTerm'; class SisIntegration extends Component { + state = { openEdit: null }; + static loadAsync = (params, dispatch, userId, isSuperAdmin) => Promise.all([dispatch(fetchAllTerms)]); @@ -33,7 +38,13 @@ class SisIntegration extends Component { } render() { - const { fetchStatus, createNewTerm, deleteTerm, sisTerms } = this.props; + const { + fetchStatus, + createNewTerm, + deleteTerm, + editTerm, + sisTerms + } = this.props; return (
+ deleteTerm(id)} @@ -101,6 +124,15 @@ class SisIntegration extends Component { /> + this.setState({ openEdit: null })} + onSubmit={data => + editTerm(this.state.openEdit, data).then(() => + this.setState({ openEdit: null }) + )} + />
} /> } @@ -122,6 +154,7 @@ SisIntegration.propTypes = { fetchStatus: PropTypes.string, createNewTerm: PropTypes.func, deleteTerm: PropTypes.func, + editTerm: PropTypes.func, sisTerms: PropTypes.array.isRequired }; @@ -138,7 +171,16 @@ const mapDispatchToProps = (dispatch, { params }) => ({ loadAsync: (userId, isSuperAdmin) => SisIntegration.loadAsync(params, dispatch, userId, isSuperAdmin), createNewTerm: data => dispatch(create(data)), - deleteTerm: id => dispatch(deleteTerm(id)) + deleteTerm: id => dispatch(deleteTerm(id)), + editTerm: (id, data) => { + // convert deadline times to timestamps + const processedData = Object.assign({}, data, { + beginning: moment(data.beginning).unix(), + end: moment(data.end).unix(), + advertiseUntil: moment(data.advertiseUntil).unix() + }); + return dispatch(editTerm(id, processedData)); + } }); export default connect(mapStateToProps, mapDispatchToProps)(SisIntegration); From 4c8a2d105cc396f2977a9637f207fb3bd964bfeb Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Mon, 29 Jan 2018 18:30:00 +0100 Subject: [PATCH 7/9] Fix multiple SIS terms forms initial values --- .../SisIntegration/EditTerm/EditTerm.js | 6 +++- .../TermsListItem/TermsListItem.js | 33 +++++++++---------- src/pages/SisIntegration/SisIntegration.js | 9 +++-- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/components/SisIntegration/EditTerm/EditTerm.js b/src/components/SisIntegration/EditTerm/EditTerm.js index 888e27e1b..54cd2a6ca 100644 --- a/src/components/SisIntegration/EditTerm/EditTerm.js +++ b/src/components/SisIntegration/EditTerm/EditTerm.js @@ -107,4 +107,8 @@ EditTerm.propTypes = { isOpen: PropTypes.bool.isRequired }; -export default reduxForm({ form: 'edit-sis-term' })(EditTerm); +export default reduxForm({ + form: 'edit-sis-term', + enableReinitialize: true, + keepDirtyOnReinitialize: false +})(EditTerm); diff --git a/src/components/SisIntegration/TermsListItem/TermsListItem.js b/src/components/SisIntegration/TermsListItem/TermsListItem.js index 9912a8a87..629527188 100644 --- a/src/components/SisIntegration/TermsListItem/TermsListItem.js +++ b/src/components/SisIntegration/TermsListItem/TermsListItem.js @@ -2,46 +2,43 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage, FormattedDate, FormattedTime } from 'react-intl'; -const TermsListItem = ({ - data: { id, year, term, beginning, end, advertiseUntil }, - createActions -}) => +const TermsListItem = ({ data, createActions }) => - {year} + {data.year} - {term === 1 && + {data.term === 1 && } - {term === 2 && + {data.term === 2 && } - {term !== 1 && - term !== 2 && + {data.term !== 1 && + data.term !== 2 && - {term} + {data.term} } - {beginning - ? + {data.beginning + ? : } - {end - ? + {data.end + ? : } - {advertiseUntil + {data.advertiseUntil ? - + {', '} - + : } - {createActions && createActions(id)} + {createActions && createActions(data.id, data)} ; diff --git a/src/pages/SisIntegration/SisIntegration.js b/src/pages/SisIntegration/SisIntegration.js index d31446db7..14053a217 100644 --- a/src/pages/SisIntegration/SisIntegration.js +++ b/src/pages/SisIntegration/SisIntegration.js @@ -88,7 +88,7 @@ class SisIntegration extends Component { > + createActions={(id, data) =>
} /> From 854004c274bf3f2be5f59a4284f969e81ad83f07 Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Mon, 29 Jan 2018 22:05:56 +0100 Subject: [PATCH 8/9] Fix loading assignments on dashboard --- src/redux/selectors/usersGroups.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/redux/selectors/usersGroups.js b/src/redux/selectors/usersGroups.js index 26750ef27..13b374bbd 100644 --- a/src/redux/selectors/usersGroups.js +++ b/src/redux/selectors/usersGroups.js @@ -56,7 +56,10 @@ export const loggedInStudentOfGroupsAssignmentsSelector = createSelector( const groupAssignments = group && assignments && isReady(group) ? group - .getIn(['data', 'assignments', 'public'], EMPTY_LIST) + .getIn( + ['data', 'privateData', 'assignments', 'public'], + EMPTY_LIST + ) .map(assignmentId => assignments.getIn(['resources', assignmentId]) ) From bbc8dca734fdca9c93f426dcb345fc897aba9a9c Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Tue, 30 Jan 2018 11:30:37 +0100 Subject: [PATCH 9/9] Edit SIS term validation --- .../SisIntegration/EditTerm/EditTerm.js | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/components/SisIntegration/EditTerm/EditTerm.js b/src/components/SisIntegration/EditTerm/EditTerm.js index 54cd2a6ca..f249bcc7b 100644 --- a/src/components/SisIntegration/EditTerm/EditTerm.js +++ b/src/components/SisIntegration/EditTerm/EditTerm.js @@ -107,8 +107,55 @@ EditTerm.propTypes = { isOpen: PropTypes.bool.isRequired }; +const validate = ({ beginning, end, advertiseUntil }) => { + const errors = {}; + + if (!beginning) { + errors['beginning'] = ( + + ); + } + + if (!end) { + errors['end'] = ( + + ); + } + + if (!advertiseUntil) { + errors['advertiseUntil'] = ( + + ); + } + + const bDate = new Date(beginning * 1000); + const eDate = new Date(end * 1000); + const aDate = new Date(advertiseUntil * 1000); + + if (aDate < bDate || aDate > eDate) { + errors['advertiseUntil'] = ( + + ); + } + + return errors; +}; + export default reduxForm({ form: 'edit-sis-term', enableReinitialize: true, - keepDirtyOnReinitialize: false + keepDirtyOnReinitialize: false, + validate })(EditTerm);