-
@@ -174,6 +173,7 @@ LoggedInUser.propTypes = {
}).isRequired,
showWarning: PropTypes.bool.isRequired,
showDiscussionButton: PropTypes.bool.isRequired,
+ errorList: PropTypes.objectOf(PropTypes.string).isRequired,
};
export default LoggedInUser;
diff --git a/lms/djangoapps/support/static/support/jsx/single_support_form.jsx b/lms/djangoapps/support/static/support/jsx/single_support_form.jsx
index 9f13404a67..2e9ea76467 100644
--- a/lms/djangoapps/support/static/support/jsx/single_support_form.jsx
+++ b/lms/djangoapps/support/static/support/jsx/single_support_form.jsx
@@ -14,140 +14,158 @@ import LoggedInUser from './logged_in_user';
import LoggedOutUser from './logged_out_user';
import Success from './success';
+const initialFormErrors = {
+ course: undefined,
+ subject: undefined,
+ message: undefined,
+ request: undefined,
+};
class RenderForm extends React.Component {
constructor(props) {
super(props);
+ this.submitFormUrl = this.props.context.submitFormUrl;
this.userInformation = this.props.context.user;
const course = this.userInformation ? this.userInformation.course_id : '';
+ this.courseDiscussionURL = '/courses/{course_id}/discussion/forum';
this.state = {
currentRequest: null,
- errorList: [],
+ errorList: initialFormErrors,
success: false,
formData: {
+ course,
subject: '',
message: '',
- course,
},
};
+ this.formValidationErrors = {
+ course: gettext('Select a course or select "Not specific to a course" for your support request.'),
+ subject: gettext('Select a subject for your support request.'),
+ message: gettext('Enter some details for your support request.'),
+ request: gettext('Something went wrong. Please try again later.'),
+ };
this.submitForm = this.submitForm.bind(this);
this.reDirectUser = this.reDirectUser.bind(this);
- this.setErrorState = this.setErrorState.bind(this);
this.formOnChangeCallback = this.formOnChangeCallback.bind(this);
- this.showWarningMessage = this.showWarningMessage.bind(this);
- this.showDiscussionButton = this.showDiscussionButton.bind(this);
}
- setErrorState(errors) {
+ getFormDataFromState() {
+ return this.state.formData;
+ }
+
+ getFormErrorsFromState() {
+ return this.state.errorList;
+ }
+
+ clearErrorState() {
+ const formErrorsInState = this.getFormErrorsFromState();
+ Object.keys(formErrorsInState).map((index) => {
+ formErrorsInState[index] = undefined;
+ return formErrorsInState;
+ });
+ }
+
+ // eslint-disable-next-line class-methods-use-this
+ scrollToTop() {
+ return window.scrollTo(0, 0);
+ }
+
+ formHasErrors() {
+ const errorsList = this.getFormErrorsFromState();
+ return Object.keys(errorsList).filter(err => errorsList[err] !== undefined).length > 0;
+ }
+
+ updateErrorInState(key, error) {
+ const errorList = this.getFormErrorsFromState();
+ errorList[key] = error;
this.setState({
- errorList: errors,
+ errorList,
});
}
formOnChangeCallback(event) {
- const eventTarget = event.target;
- let formData = this.state.formData;
- formData[eventTarget.id] = eventTarget.value;
+ const formData = this.getFormDataFromState();
+ formData[event.target.id] = event.target.value;
this.setState({ formData });
}
showWarningMessage() {
- return this.state.formData && this.state.formData.subject === 'Course Content';
+ const formData = this.getFormDataFromState(),
+ selectedSubject = formData.subject;
+ return formData && selectedSubject === 'Course Content';
}
showDiscussionButton() {
- const selectCourse = this.state.formData.course;
- return this.state.formData && (selectCourse !== '' && selectCourse !== 'Not specific to a course');
+ const formData = this.getFormDataFromState(),
+ selectedCourse = formData.course;
+ return formData && (selectedCourse !== '' && selectedCourse !== 'Not specific to a course');
}
reDirectUser(event) {
event.preventDefault();
- window.location.href = `/courses/${this.state.formData.course}/discussion/forum`;
+ const formData = this.getFormDataFromState();
+ window.location.href = this.courseDiscussionURL.replace('{course_id}', formData.course);
}
submitForm(event) {
event.preventDefault();
- let subject,
- course;
- const url = this.props.context.submitFormUrl,
+ const formData = this.getFormDataFromState();
+ this.clearErrorState();
+ this.validateFormData(formData);
+ if (this.formHasErrors()) {
+ return this.scrollToTop();
+ }
+ this.createZendeskTicket(formData);
+ }
+
+ createZendeskTicket(formData) {
+ const url = this.submitFormUrl,
request = new XMLHttpRequest(),
- $course = $('#course'),
- $subject = $('#subject'),
data = {
comment: {
- body: this.state.formData.message,
+ body: formData.message,
},
+ subject: formData.subject, // Zendesk API requires 'subject'
+ custom_fields: [{
+ id: this.props.context.customFields.course_id,
+ value: formData.course,
+ }],
tags: this.props.context.tags,
- },
- errors = [];
+ requester: {
+ email: this.userInformation.email,
+ name: this.userInformation.username,
+ },
+ };
+ request.open('POST', url, true);
+ request.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
+ request.setRequestHeader('X-CSRFToken', $.cookie('csrftoken'));
+ request.send(JSON.stringify(data));
+ request.onreadystatechange = function success() {
+ if (request.readyState === 4 && request.status === 201) {
+ this.setState({
+ success: true,
+ });
+ }
+ }.bind(this);
- this.clearErrors();
-
- data.requester = {
- email: this.userInformation.email,
- name: this.userInformation.username,
- };
-
- course = $course.find(':selected').val();
- if (!course) {
- course = $course.val();
- }
- if (!course) {
- $('#course').closest('.form-group').addClass('has-error');
- errors.push(gettext('Select a course or select "Not specific to a course" for your support request.'));
- }
- data.custom_fields = [{
- id: this.props.context.customFields.course_id,
- value: course,
- }];
- subject = $subject.find(':selected').val();
- if (!subject) {
- subject = $subject.val();
- }
- if (!subject) {
- $subject.closest('.form-group').addClass('has-error');
- errors.push(gettext('Select a subject for your support request.'));
- }
- data.subject = subject; // Zendesk API requires 'subject'
-
- if (this.validateData(data, errors)) {
- request.open('POST', url, true);
- request.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
- request.setRequestHeader('X-CSRFToken', $.cookie('csrftoken'));
-
- request.send(JSON.stringify(data));
-
- request.onreadystatechange = function success() {
- if (request.readyState === 4 && request.status === 201) {
- this.setState({
- success: true,
- });
- }
- }.bind(this);
-
- request.onerror = function error() {
- this.setErrorState([gettext('Something went wrong. Please try again later.')]);
- }.bind(this);
- }
+ request.onerror = function error() {
+ this.updateErrorInState('request', this.formValidationErrors.request);
+ this.scrollToTop();
+ }.bind(this);
}
+ validateFormData(formData) {
+ const { course, subject, message } = formData;
- clearErrors() {
- this.setErrorState([]);
- $('.form-group').removeClass('has-error');
- }
+ let courseError,
+ subjectError,
+ messageError;
- validateData(data, errors) {
- if (!data.comment.body) {
- errors.push(gettext('Enter some details for your support request.'));
- $('#message').closest('.form-group').addClass('has-error');
- }
-
- if (!errors.length) {
- return true;
- }
-
- this.setErrorState(errors);
- return false;
+ courseError = (course === '') ? this.formValidationErrors.course : undefined;
+ this.updateErrorInState('course', courseError);
+ subjectError = (subject === '') ? this.formValidationErrors.subject : undefined;
+ this.updateErrorInState('subject', subjectError);
+ messageError = (message === '') ? this.formValidationErrors.message : undefined;
+ this.updateErrorInState('message', messageError);
}
renderSuccess() {
@@ -171,6 +189,7 @@ class RenderForm extends React.Component {
showWarning={this.showWarningMessage()}
showDiscussionButton={this.showDiscussionButton()}
reDirectUser={this.reDirectUser}
+ errorList={this.getFormErrorsFromState()}
/>);
} else {
userElement = (
{gettext('Contact Us')}