Merge pull request #10830 from edx/alasdair/ECOM-2824-fa-form
ECOM-2824 Financial Assistance Form
This commit is contained in:
@@ -522,15 +522,15 @@ class ViewsTestCase(ModuleStoreTestCase):
|
||||
effort = "I'm done, okay? You just give me my money, and you and I, we're done."
|
||||
data = {
|
||||
'username': username,
|
||||
'course_id': course,
|
||||
'legal_name': legal_name,
|
||||
'course': course,
|
||||
'name': legal_name,
|
||||
'email': self.user.email,
|
||||
'country': country,
|
||||
'income': income,
|
||||
'reason_for_applying': reason_for_applying,
|
||||
'goals': goals,
|
||||
'effort': effort,
|
||||
'marketing_permission': False,
|
||||
'mktg-permission': False,
|
||||
}
|
||||
response = self._submit_financial_assistance_form(data)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
@@ -560,15 +560,15 @@ class ViewsTestCase(ModuleStoreTestCase):
|
||||
def test_zendesk_submission_failed(self, _mock_record_feedback):
|
||||
response = self._submit_financial_assistance_form({
|
||||
'username': self.user.username,
|
||||
'course_id': '',
|
||||
'legal_name': '',
|
||||
'course': '',
|
||||
'name': '',
|
||||
'email': '',
|
||||
'country': '',
|
||||
'income': '',
|
||||
'reason_for_applying': '',
|
||||
'goals': '',
|
||||
'effort': '',
|
||||
'marketing_permission': False,
|
||||
'mktg-permission': False,
|
||||
})
|
||||
self.assertEqual(response.status_code, 500)
|
||||
|
||||
|
||||
@@ -1460,15 +1460,15 @@ def financial_assistance_request(request):
|
||||
if request.user.username != username:
|
||||
return HttpResponseForbidden()
|
||||
|
||||
course_id = data['course_id']
|
||||
legal_name = data['legal_name']
|
||||
course_id = data['course']
|
||||
legal_name = data['name']
|
||||
email = data['email']
|
||||
country = data['country']
|
||||
income = data['income']
|
||||
reason_for_applying = data['reason_for_applying']
|
||||
goals = data['goals']
|
||||
effort = data['effort']
|
||||
marketing_permission = data['marketing_permission']
|
||||
marketing_permission = data['mktg-permission']
|
||||
ip_address = get_ip(request)
|
||||
except ValueError:
|
||||
# Thrown if JSON parsing fails
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'js/financial-assistance/views/financial_assistance_form_view'
|
||||
],
|
||||
function (FinancialAssistanceFormView) {
|
||||
return function (options) {
|
||||
var formView = new FinancialAssistanceFormView({
|
||||
el: '.financial-assistance-wrapper',
|
||||
context: options
|
||||
});
|
||||
|
||||
return formView;
|
||||
};
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Model for Financial Assistance.
|
||||
*/
|
||||
(function (define) {
|
||||
'use strict';
|
||||
define(['backbone'], function (Backbone) {
|
||||
var FinancialAssistance = Backbone.Model.extend({
|
||||
initialize: function(options) {
|
||||
this.url = options.url;
|
||||
}
|
||||
});
|
||||
return FinancialAssistance;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -0,0 +1,48 @@
|
||||
<h1><%- gettext('Financial Assistance Application') %></h1>
|
||||
|
||||
<div class="intro">
|
||||
<% _.each(header_text, function(copy) { %>
|
||||
<p class="copy"><%- copy %></p>
|
||||
<% }); %>
|
||||
</div>
|
||||
|
||||
<form class="financial-assistance-form">
|
||||
<div class="status submission-error hidden" aria-live="polite">
|
||||
<h4 class="message-title"><%- gettext('Application not submitted') %></h4>
|
||||
<ul class="message-copy"></ul>
|
||||
</div>
|
||||
|
||||
<div class="user-info">
|
||||
<h2><%- gettext('About You') %></h2>
|
||||
<p><%- interpolate_text(
|
||||
gettext('The following information is already a part of your {platform} profile. We\'ve included it here for your application.'),
|
||||
{platform: platform_name}
|
||||
) %></p>
|
||||
<div class="info-column">
|
||||
<div class="title"><%- gettext('Username') %></div>
|
||||
<div class="data"><%- username %></div>
|
||||
</div>
|
||||
<div class="info-column">
|
||||
<div class="title"><%- gettext('Email address') %></div>
|
||||
<div class="data"><%- email %></div>
|
||||
</div>
|
||||
<div class="info-column">
|
||||
<div class="title"><%- gettext('Legal name') %></div>
|
||||
<div class="data"><%- name %></div>
|
||||
</div>
|
||||
<div class="info-column">
|
||||
<div class="title"><%- gettext('Country of residence') %></div>
|
||||
<div class="data"><%- country %></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= fields %>
|
||||
|
||||
<div class="cta-wrapper clearfix">
|
||||
<a href="<%- student_faq_url %>" class="nav-link"><%- interpolate_text(
|
||||
gettext('Back to {platform} FAQs'),
|
||||
{platform: platform_name}
|
||||
) %></a>
|
||||
<button type="submit" class="action action-primary action-update js-submit-form submit-form"><%- gettext("Submit Application") %></button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,8 @@
|
||||
<h1><%- gettext('Financial Assistance Application') %></h1>
|
||||
<p class="js-success-message success-message" tabindex="-1"><%- interpolate_text(
|
||||
gettext('Thank you for submitting your financial assistance application for {course_name}! You can expect a response in 2-4 business days.'), {course_name: course}
|
||||
) %>
|
||||
</p>
|
||||
<div class="cta-wrapper clearfix">
|
||||
<a href="<%- dashboard_url %>" class="btn btn-blue btn-dashboard"><%- gettext('Go to Dashboard') %></a>
|
||||
</div>
|
||||
@@ -0,0 +1,107 @@
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
|
||||
define(['backbone',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'gettext',
|
||||
'js/financial-assistance/models/financial_assistance_model',
|
||||
'text!js/financial-assistance/templates/financial_assessment_form.underscore',
|
||||
'text!js/financial-assistance/templates/financial_assessment_submitted.underscore',
|
||||
'js/student_account/views/FormView',
|
||||
'text!templates/student_account/form_field.underscore'
|
||||
],
|
||||
function(Backbone, $, _, gettext, FinancialAssistanceModel, formViewTpl, successTpl, FormView, formFieldTpl) {
|
||||
return FormView.extend({
|
||||
el: '.financial-assistance-wrapper',
|
||||
events: {
|
||||
'click .js-submit-form': 'submitForm'
|
||||
},
|
||||
tpl: formViewTpl,
|
||||
fieldTpl: formFieldTpl,
|
||||
formType: 'financial-assistance',
|
||||
requiredStr: '',
|
||||
submitButton: '.js-submit-form',
|
||||
|
||||
initialize: function(data) {
|
||||
var context = data.context,
|
||||
fields = context.fields;
|
||||
|
||||
// Add default option to array
|
||||
if ( fields[0].options.length > 1 ) {
|
||||
fields[0].options.unshift({
|
||||
name: '- ' + gettext('Choose one') + ' -',
|
||||
value: '',
|
||||
default: true
|
||||
});
|
||||
}
|
||||
|
||||
// Set non-form data needed to render the View
|
||||
this.context = {
|
||||
dashboard_url: context.dashboard_url,
|
||||
header_text: context.header_text,
|
||||
platform_name: context.platform_name,
|
||||
student_faq_url: context.student_faq_url
|
||||
};
|
||||
|
||||
// Make the value accessible to this View
|
||||
this.user_details = context.user_details;
|
||||
|
||||
// Initialize the model and set user details
|
||||
this.model = new FinancialAssistanceModel({
|
||||
url: context.submit_url
|
||||
});
|
||||
this.model.set( context.user_details );
|
||||
this.listenTo( this.model, 'error', this.saveError );
|
||||
this.model.on('sync', this.renderSuccess, this);
|
||||
|
||||
// Build the form
|
||||
this.buildForm( fields );
|
||||
},
|
||||
|
||||
render: function(html) {
|
||||
var data = _.extend( this.model.toJSON(), this.context, {
|
||||
fields: html || '',
|
||||
});
|
||||
|
||||
this.$el.html(_.template(this.tpl, data));
|
||||
|
||||
this.postRender();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
renderSuccess: function() {
|
||||
this.$el.html(_.template(successTpl, {
|
||||
course: this.model.get('course'),
|
||||
dashboard_url: this.context.dashboard_url
|
||||
}));
|
||||
|
||||
$('.js-success-message').focus();
|
||||
},
|
||||
|
||||
saveError: function(error) {
|
||||
/*jslint maxlen: 500 */
|
||||
var txt = [
|
||||
'An error has occurred. Wait a few minutes and then try to submit the application again.',
|
||||
'If you continue to have issues please contact support.'
|
||||
],
|
||||
msg = gettext(txt.join(' '));
|
||||
|
||||
if (error.status === 0) {
|
||||
msg = gettext('An error has occurred. Check your Internet connection and try again.');
|
||||
}
|
||||
|
||||
this.errors = ['<li>' + msg + '</li>'];
|
||||
this.setErrors();
|
||||
this.element.hide( this.$resetSuccess );
|
||||
this.toggleDisableButton(false);
|
||||
},
|
||||
|
||||
setExtraData: function(data) {
|
||||
return _.extend(data, this.user_details);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -0,0 +1,59 @@
|
||||
define([
|
||||
'backbone',
|
||||
'jquery',
|
||||
'js/financial-assistance/views/financial_assistance_form_view'
|
||||
], function (Backbone, $, FinancialAssistanceFormView) {
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('Financial Assistance View', function () {
|
||||
var view = null,
|
||||
context = {
|
||||
fields: [{
|
||||
defaultValue: '',
|
||||
form: 'financial-assistance',
|
||||
instructions: 'select a course',
|
||||
label: 'Course',
|
||||
name: 'course',
|
||||
options: [
|
||||
{'name': 'Verified with Audit', 'value': 'course-v1:HCFA+VA101+2015'},
|
||||
{'name': 'Something Else', 'value': 'course-v1:SomethingX+SE101+215'},
|
||||
{'name': 'Test Course', 'value': 'course-v1:TestX+T101+2015'}
|
||||
],
|
||||
placeholder: '',
|
||||
required: true,
|
||||
requiredStr: '',
|
||||
type: 'select'
|
||||
}],
|
||||
user_details: {
|
||||
country: 'UK',
|
||||
email: 'xsy@edx.org',
|
||||
name: 'xsy',
|
||||
username: 'xsy4ever'
|
||||
},
|
||||
header_text: ['Line one.', 'Line two.'],
|
||||
student_faq_url: '/faqs',
|
||||
dashboard_url: '/dashboard',
|
||||
platform_name: 'edx',
|
||||
submit_url: '/api/financial/v1/assistance'
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
setFixtures('<div class="financial-assistance-wrapper"></div>');
|
||||
view = new FinancialAssistanceFormView({
|
||||
el: '.financial-assistance-wrapper',
|
||||
context: context
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
view.undelegateEvents();
|
||||
view.remove();
|
||||
});
|
||||
|
||||
it('should exist', function() {
|
||||
expect(view).toBeDefined();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -213,6 +213,13 @@
|
||||
this.focusFirstError();
|
||||
},
|
||||
|
||||
/* Allows extended views to add non-form attributes
|
||||
* to the data before saving it to model
|
||||
*/
|
||||
setExtraData: function( data ) {
|
||||
return data;
|
||||
},
|
||||
|
||||
submitForm: function( event ) {
|
||||
var data = this.getFormData();
|
||||
|
||||
@@ -223,6 +230,7 @@
|
||||
this.toggleDisableButton(true);
|
||||
|
||||
if ( !_.compact(this.errors).length ) {
|
||||
data = this.setExtraData( data );
|
||||
this.model.set( data );
|
||||
this.model.save();
|
||||
this.toggleErrorMsg( false );
|
||||
|
||||
@@ -1,33 +1,39 @@
|
||||
%fa-copy {
|
||||
@extend %t-copy-base;
|
||||
padding: ($baseline/2) 0;
|
||||
margin: 0;
|
||||
color: $m-gray-d2;
|
||||
};
|
||||
|
||||
.financial-assistance-wrapper {
|
||||
margin: auto;
|
||||
padding: $baseline 0;
|
||||
padding: $baseline ($baseline/2);
|
||||
max-width: 1180px;
|
||||
|
||||
.financial-assistance {
|
||||
h1 {
|
||||
@extend %t-title4;
|
||||
@include text-align(left);
|
||||
margin: 0;
|
||||
padding: ($baseline/2) 0;
|
||||
border-bottom: 4px solid $gray-l5;
|
||||
color: $m-gray-d3;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@extend %t-title4;
|
||||
@include text-align(left);
|
||||
margin: 0;
|
||||
padding: ($baseline/2) 0;
|
||||
border-bottom: 4px solid $gray-l5;
|
||||
color: $m-gray-d3;
|
||||
}
|
||||
h2 {
|
||||
@extend %t-title6;
|
||||
@extend %t-strong;
|
||||
margin-top: ($baseline/2);
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@extend %t-title6;
|
||||
@extend %t-strong;
|
||||
margin-top: ($baseline/2);
|
||||
text-transform: none;
|
||||
}
|
||||
p {
|
||||
@extend %fa-copy;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
p {
|
||||
@extend %t-copy-base;
|
||||
padding: ($baseline/2) 0;
|
||||
margin: 0;
|
||||
color: $m-gray-d2;
|
||||
}
|
||||
.financial-assistance {
|
||||
padding-bottom: ($baseline/2);
|
||||
border-bottom: 4px solid $gray-l5;
|
||||
|
||||
.apply-form-list {
|
||||
padding: 0;
|
||||
@@ -73,4 +79,165 @@
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
// Application form View
|
||||
.intro {
|
||||
border-bottom: 4px solid $gray-l5;
|
||||
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.success-message {
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-dashboard {
|
||||
@include float(right);
|
||||
color: $white;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.user-info {
|
||||
@include clearfix();
|
||||
border-bottom: 2px solid $gray-l5;
|
||||
padding: 20px 0;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.info-column {
|
||||
@include float(left);
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
@extend %fa-copy;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.data {
|
||||
@extend %fa-copy;
|
||||
padding: 0;
|
||||
color: $black;
|
||||
font-size: 1.125em;
|
||||
}
|
||||
}
|
||||
|
||||
.financial-assistance-form {
|
||||
@extend .login-register;
|
||||
|
||||
.action-primary {
|
||||
@include float(left);
|
||||
width: auto;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
margin: 15px 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
form {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
select,
|
||||
input {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
input {
|
||||
border: {
|
||||
top: none;
|
||||
right: none;
|
||||
bottom: 3px solid $gray-l1;
|
||||
left: none;
|
||||
};
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 125px;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
height: auto;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
|
||||
& + label {
|
||||
@include margin-left(30px);
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cta-wrapper {
|
||||
border-top: 4px solid $gray-l5;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
@include media($bp-medium) {
|
||||
.user-info {
|
||||
.info-column {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.financial-assistance-form {
|
||||
.action-primary {
|
||||
@include float(right);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media($bp-large) {
|
||||
.user-info {
|
||||
.info-column {
|
||||
width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
.financial-assistance-form {
|
||||
.action-primary {
|
||||
@include float(right);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media($bp-huge) {
|
||||
.user-info {
|
||||
.info-column {
|
||||
width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
.financial-assistance-form {
|
||||
.action-primary {
|
||||
@include float(right);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<option value="<%= el.value%>"<% if ( el.default ) { %> data-isdefault="true"<% } %>><%= el.name %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
<% if ( instructions ) { %> <span class="tip tip-input" id="<%= form %>-<%= name %>-desc"><%= instructions %></span><% } %>
|
||||
<% } else if ( type === 'textarea' ) { %>
|
||||
<textarea id="<%= form %>-<%= name %>"
|
||||
type="<%= type %>"
|
||||
@@ -35,6 +36,7 @@
|
||||
<% });
|
||||
} %>
|
||||
<% if ( required ) { %> aria-required="true" required<% } %> ></textarea>
|
||||
<% if ( instructions ) { %> <span class="tip tip-input" id="<%= form %>-<%= name %>-desc"><%= instructions %></span><% } %>
|
||||
<% } else { %>
|
||||
<input id="<%= form %>-<%= name %>"
|
||||
type="<%= type %>"
|
||||
@@ -52,16 +54,15 @@
|
||||
<% if ( placeholder ) { %> placeholder="<%= placeholder %>"<% } %>
|
||||
value="<%- defaultValue %>"
|
||||
/>
|
||||
<% if ( type === 'checkbox' ) { %>
|
||||
<label for="<%= form %>-<%= name %>">
|
||||
<%= label %>
|
||||
<% if ( required && requiredStr ) { %> <%= requiredStr %><% } %>
|
||||
</label>
|
||||
<% } %>
|
||||
<% if ( instructions ) { %> <span class="tip tip-input" id="<%= form %>-<%= name %>-desc"><%= instructions %></span><% } %>
|
||||
<% } %>
|
||||
|
||||
<% if ( type === 'checkbox' ) { %>
|
||||
<label for="<%= form %>-<%= name %>">
|
||||
<%= label %>
|
||||
<% if ( required && requiredStr ) { %> <%= requiredStr %><% } %>
|
||||
</label>
|
||||
<% } %>
|
||||
|
||||
<% if( form === 'login' && name === 'password' ) { %>
|
||||
<a href="#" class="forgot-password field-link"><%- gettext("Forgot password?") %></a>
|
||||
<% } %>
|
||||
|
||||
Reference in New Issue
Block a user