Merge pull request #23981 from edx/aj/details/code-refactor
Code Refactor
This commit is contained in:
@@ -6,25 +6,23 @@ import PropTypes from 'prop-types';
|
||||
|
||||
class ShowErrors extends React.Component {
|
||||
render() {
|
||||
if (this.props.errorList.length > 0) {
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
return this.props.errorList.length > 0 &&
|
||||
<div className="col-sm-12">
|
||||
return (
|
||||
this.props.hasErrors && <div className="col-sm-12">
|
||||
<div className="alert alert-danger" role="alert">
|
||||
<strong>{gettext('Please fix the following errors:')}</strong>
|
||||
<ul>
|
||||
{this.props.errorList.map((error, i) =>
|
||||
<li key={i}>{error}</li>,
|
||||
{ Object.keys(this.props.errorList).map(key =>
|
||||
this.props.errorList[key] && <li key={key}>{this.props.errorList[key]}</li>,
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>;
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
ShowErrors.propTypes = {
|
||||
errorList: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
errorList: PropTypes.objectOf(PropTypes.string).isRequired,
|
||||
hasErrors: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default ShowErrors;
|
||||
|
||||
@@ -5,8 +5,7 @@ import PropTypes from 'prop-types';
|
||||
import { Button, StatusAlert } from '@edx/paragon';
|
||||
import StringUtils from 'edx-ui-toolkit/js/utils/string-utils';
|
||||
|
||||
|
||||
function LoggedInUser({ userInformation, onChangeCallback, submitForm, showWarning, showDiscussionButton, reDirectUser }) {
|
||||
function LoggedInUser({ userInformation, onChangeCallback, submitForm, showWarning, showDiscussionButton, reDirectUser, errorList }) {
|
||||
let courseElement;
|
||||
let detailElement;
|
||||
let discussionElement = '';
|
||||
@@ -97,7 +96,7 @@ function LoggedInUser({ userInformation, onChangeCallback, submitForm, showWarni
|
||||
<div>
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
<div className="form-group">
|
||||
<div className={`form-group ${errorList.message ? 'has-error' : ''}`}>
|
||||
<label htmlFor="message">{gettext('Details')}</label>
|
||||
<p className="message-desc">{gettext('the more quickly and helpfully we can respond!')}</p>
|
||||
<textarea aria-describedby="message" className="form-control" rows="7" id="message" />
|
||||
@@ -138,7 +137,7 @@ function LoggedInUser({ userInformation, onChangeCallback, submitForm, showWarni
|
||||
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
<div className="form-group">
|
||||
<div className={`form-group ${errorList.subject ? 'has-error' : ''}`}>
|
||||
{subjectElement}
|
||||
</div>
|
||||
</div>
|
||||
@@ -146,7 +145,7 @@ function LoggedInUser({ userInformation, onChangeCallback, submitForm, showWarni
|
||||
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
<div className="form-group">
|
||||
<div className={`form-group ${errorList.course ? 'has-error' : ''}`}>
|
||||
{courseElement}
|
||||
</div>
|
||||
</div>
|
||||
@@ -174,6 +173,7 @@ LoggedInUser.propTypes = {
|
||||
}).isRequired,
|
||||
showWarning: PropTypes.bool.isRequired,
|
||||
showDiscussionButton: PropTypes.bool.isRequired,
|
||||
errorList: PropTypes.objectOf(PropTypes.string).isRequired,
|
||||
};
|
||||
|
||||
export default LoggedInUser;
|
||||
|
||||
@@ -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 = (<LoggedOutUser
|
||||
@@ -194,9 +213,8 @@ class RenderForm extends React.Component {
|
||||
<h2>{gettext('Contact Us')}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row form-errors">
|
||||
<ShowErrors errorList={this.state.errorList} />
|
||||
<ShowErrors errorList={this.getFormErrorsFromState()} hasErrors={this.formHasErrors()} />
|
||||
</div>
|
||||
|
||||
<div className="row">
|
||||
@@ -228,7 +246,6 @@ class RenderForm extends React.Component {
|
||||
if (this.state.success) {
|
||||
return this.renderSuccess();
|
||||
}
|
||||
|
||||
return this.renderSupportForm();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user