From 8eb484c10957055bb1a83ade514c009827101940 Mon Sep 17 00:00:00 2001 From: AlasdairSwan Date: Tue, 23 Dec 2014 14:13:37 -0500 Subject: [PATCH] ECOM-811 design and layout changes --- .../verify_student/tests/test_views.py | 59 +++---- lms/djangoapps/verify_student/views.py | 17 +- lms/envs/common.py | 2 - lms/static/js/spec/main.js | 12 -- .../pay_and_verify_view_spec.js | 19 --- .../review_photos_step_view_spec.js | 22 --- .../js/verify_student/pay_and_verify.js | 19 ++- .../enrollment_confirmation_step_view.js | 3 +- .../views/face_photo_step_view.js | 6 + .../views/id_photo_step_view.js | 6 + .../verify_student/views/intro_step_view.js | 9 +- .../views/make_payment_step_view.js | 71 +++++--- .../views/pay_and_verify_view.js | 41 +++-- .../views/payment_confirmation_step_view.js | 16 +- .../js/verify_student/views/progress_view.js | 67 -------- .../verify_student/views/requirements_view.js | 29 ---- .../views/review_photos_step_view.js | 21 +-- .../js/verify_student/views/step_view.js | 14 -- lms/static/sass/application-extend2.scss.mako | 1 + .../sass/views/_decoupled-verification.scss | 157 ++++++++++++++++++ .../enrollment_confirmation_step.underscore | 29 ++-- .../verify_student/face_photo_step.underscore | 33 ++-- .../verify_student/id_photo_step.underscore | 24 +-- .../verify_student/intro_step.underscore | 56 ++++++- .../make_payment_step.underscore | 86 ++++++++-- .../verify_student/pay_and_verify.html | 18 +- .../payment_confirmation_step.underscore | 85 +++++++--- .../verify_student/progress.underscore | 22 --- .../verify_student/requirements.underscore | 76 --------- .../review_photos_step.underscore | 130 +++++---------- .../verify_student/webcam_photo.underscore | 5 +- 31 files changed, 588 insertions(+), 567 deletions(-) delete mode 100644 lms/static/js/verify_student/views/progress_view.js delete mode 100644 lms/static/js/verify_student/views/requirements_view.js create mode 100644 lms/static/sass/views/_decoupled-verification.scss delete mode 100644 lms/templates/verify_student/progress.underscore delete mode 100644 lms/templates/verify_student/requirements.underscore diff --git a/lms/djangoapps/verify_student/tests/test_views.py b/lms/djangoapps/verify_student/tests/test_views.py index 62e339b56f..3fb52a2a6e 100644 --- a/lms/djangoapps/verify_student/tests/test_views.py +++ b/lms/djangoapps/verify_student/tests/test_views.py @@ -992,7 +992,6 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): self._assert_requirements_displayed(response, [ PayAndVerifyView.PHOTO_ID_REQ, PayAndVerifyView.WEBCAM_REQ, - PayAndVerifyView.CREDIT_CARD_REQ, ]) @ddt.data("expired", "denied") @@ -1033,9 +1032,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): PayAndVerifyView.MAKE_PAYMENT_STEP ) self._assert_messaging(response, PayAndVerifyView.FIRST_TIME_VERIFY_MSG) - self._assert_requirements_displayed(response, [ - PayAndVerifyView.CREDIT_CARD_REQ, - ]) + self._assert_requirements_displayed(response, []) @ddt.data("verified", "professional") def test_start_flow_already_paid(self, course_mode): @@ -1068,9 +1065,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): PayAndVerifyView.PAYMENT_STEPS, PayAndVerifyView.MAKE_PAYMENT_STEP ) - self._assert_requirements_displayed(response, [ - PayAndVerifyView.CREDIT_CARD_REQ, - ]) + self._assert_requirements_displayed(response, []) def test_start_flow_unenrolled(self): course = self._create_course("verified") @@ -1086,9 +1081,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): PayAndVerifyView.PAYMENT_STEPS, PayAndVerifyView.MAKE_PAYMENT_STEP ) - self._assert_requirements_displayed(response, [ - PayAndVerifyView.CREDIT_CARD_REQ, - ]) + self._assert_requirements_displayed(response, []) @ddt.data( ("verified", "submitted"), @@ -1128,7 +1121,6 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): self._assert_requirements_displayed(response, [ PayAndVerifyView.PHOTO_ID_REQ, PayAndVerifyView.WEBCAM_REQ, - PayAndVerifyView.CREDIT_CARD_REQ, ]) def test_verify_now_already_verified(self): @@ -1237,28 +1229,8 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): self._assert_requirements_displayed(response, [ PayAndVerifyView.PHOTO_ID_REQ, PayAndVerifyView.WEBCAM_REQ, - PayAndVerifyView.CREDIT_CARD_REQ, ]) - def test_payment_confirmation_skip_first_step(self): - course = self._create_course("verified") - self._enroll(course.id, "verified") - response = self._get_page( - 'verify_student_payment_confirmation', - course.id, - skip_first_step=True - ) - - self._assert_messaging(response, PayAndVerifyView.PAYMENT_CONFIRMATION_MSG) - - # Expect that *all* steps are displayed, - # but we start on the first verify step - self._assert_steps_displayed( - response, - PayAndVerifyView.PAYMENT_STEPS + PayAndVerifyView.VERIFICATION_STEPS, - PayAndVerifyView.FACE_PHOTO_STEP, - ) - def test_payment_cannot_skip(self): """ Simple test to verify that certain steps cannot be skipped. This test sets up @@ -1358,7 +1330,6 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): self._assert_requirements_displayed(response, [ PayAndVerifyView.PHOTO_ID_REQ, PayAndVerifyView.WEBCAM_REQ, - PayAndVerifyView.CREDIT_CARD_REQ, ]) def test_upgrade_already_verified(self): @@ -1373,9 +1344,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): PayAndVerifyView.MAKE_PAYMENT_STEP ) self._assert_messaging(response, PayAndVerifyView.UPGRADE_MSG) - self._assert_requirements_displayed(response, [ - PayAndVerifyView.CREDIT_CARD_REQ, - ]) + self._assert_requirements_displayed(response, []) def test_upgrade_already_paid(self): course = self._create_course("verified") @@ -1486,7 +1455,6 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): ) self._assert_requirements_displayed(response, [ PayAndVerifyView.ACCOUNT_ACTIVATION_REQ, - PayAndVerifyView.CREDIT_CARD_REQ, PayAndVerifyView.PHOTO_ID_REQ, PayAndVerifyView.WEBCAM_REQ, ]) @@ -1516,6 +1484,22 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): response = self._get_page("verify_student_start_flow", course.id) self._assert_contribution_amount(response, "12.34") + def test_verification_deadline(self): + # Set a deadline on the course mode + course = self._create_course("verified") + mode = CourseMode.objects.get( + course_id=course.id, + mode_slug="verified" + ) + expiration = datetime(2999, 1, 2, tzinfo=pytz.UTC) + mode.expiration_datetime = expiration + mode.save() + + # Expect that the expiration date is set + response = self._get_page("verify_student_start_flow", course.id) + data = self._get_page_data(response) + self.assertEqual(data['verification_deadline'], "Jan 02, 2999 at 00:00 UTC") + def _create_course(self, *course_modes, **kwargs): """Create a new course with the specified course modes. """ course = CourseFactory.create() @@ -1648,7 +1632,8 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase): 'current_step': pay_and_verify_div['data-current-step'], 'requirements': json.loads(pay_and_verify_div['data-requirements']), 'message_key': pay_and_verify_div['data-msg-key'], - 'contribution_amount': pay_and_verify_div['data-contribution-amount'] + 'contribution_amount': pay_and_verify_div['data-contribution-amount'], + 'verification_deadline': pay_and_verify_div['data-verification-deadline'] } def _assert_redirects_to_dashboard(self, response): diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index 9d5999861d..5e1fec7ed7 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -47,6 +47,7 @@ from xmodule.modulestore.django import modulestore from microsite_configuration import microsite from util.json_request import JsonResponse +from util.date_utils import get_default_time_display log = logging.getLogger(__name__) @@ -259,10 +260,9 @@ class PayAndVerifyView(View): ENROLLMENT_CONFIRMATION_STEP ] - # These are steps that can be skipped, since there are no barring requirements. + # These steps can be skipped using the ?skip-first-step GET param SKIP_STEPS = [ INTRO_STEP, - PAYMENT_CONFIRMATION_STEP ] Step = namedtuple( @@ -287,15 +287,15 @@ class PayAndVerifyView(View): template_name="payment_confirmation_step" ), FACE_PHOTO_STEP: Step( - title=ugettext_lazy("Take Face Photo"), + title=ugettext_lazy("Take Photo"), template_name="face_photo_step" ), ID_PHOTO_STEP: Step( - title=ugettext_lazy("ID Photo"), + title=ugettext_lazy("Take a Photo of Your ID"), template_name="id_photo_step" ), REVIEW_PHOTOS_STEP: Step( - title=ugettext_lazy("Review Photos"), + title=ugettext_lazy("Review Your Info"), template_name="review_photos_step" ), ENROLLMENT_CONFIRMATION_STEP: Step( @@ -380,12 +380,10 @@ class PayAndVerifyView(View): ACCOUNT_ACTIVATION_REQ = "account-activation-required" PHOTO_ID_REQ = "photo-id-required" WEBCAM_REQ = "webcam-required" - CREDIT_CARD_REQ = "credit-card-required" STEP_REQUIREMENTS = { ID_PHOTO_STEP: [PHOTO_ID_REQ, WEBCAM_REQ], FACE_PHOTO_STEP: [WEBCAM_REQ], - MAKE_PAYMENT_STEP: [CREDIT_CARD_REQ], } @method_decorator(login_required) @@ -507,6 +505,10 @@ class PayAndVerifyView(View): 'course': course, 'course_key': unicode(course_key), 'course_mode': course_mode, + 'verification_deadline': ( + get_default_time_display(course_mode.expiration_datetime) + if course_mode.expiration_datetime else "" + ), 'courseware_url': courseware_url, 'current_step': current_step, 'disable_courseware_js': True, @@ -660,7 +662,6 @@ class PayAndVerifyView(View): self.ACCOUNT_ACTIVATION_REQ: not is_active, self.PHOTO_ID_REQ: False, self.WEBCAM_REQ: False, - self.CREDIT_CARD_REQ: False } display_steps = set(step['name'] for step in display_steps) diff --git a/lms/envs/common.py b/lms/envs/common.py index 5d3f1fe9a4..0003cd09a3 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -1083,8 +1083,6 @@ verify_student_js = [ 'js/verify_student/models/verification_model.js', 'js/verify_student/views/error_view.js', 'js/verify_student/views/webcam_photo_view.js', - 'js/verify_student/views/progress_view.js', - 'js/verify_student/views/requirements_view.js', 'js/verify_student/views/step_view.js', 'js/verify_student/views/intro_step_view.js', 'js/verify_student/views/make_payment_step_view.js', diff --git a/lms/static/js/spec/main.js b/lms/static/js/spec/main.js index 3350a19bbf..4db456cc9c 100644 --- a/lms/static/js/spec/main.js +++ b/lms/static/js/spec/main.js @@ -397,14 +397,6 @@ exports: 'edx.verify_student.WebcamPhotoView', deps: [ 'jquery', 'underscore', 'backbone', 'gettext' ] }, - 'js/verify_student/views/progress_view': { - exports: 'edx.verify_student.ProgressView', - deps: [ 'jquery', 'underscore', 'backbone', 'gettext' ] - }, - 'js/verify_student/views/requirements_view': { - exports: 'edx.verify_student.RequirementsView', - deps: [ 'jquery', 'backbone', 'underscore', 'gettext' ] - }, 'js/verify_student/views/step_view': { exports: 'edx.verify_student.StepView', deps: [ 'jquery', 'underscore', 'underscore.string', 'backbone', 'gettext' ] @@ -414,7 +406,6 @@ deps: [ 'jquery', 'js/verify_student/views/step_view', - 'js/verify_student/views/requirements_view' ] }, 'js/verify_student/views/make_payment_step_view': { @@ -426,7 +417,6 @@ 'jquery.cookie', 'jquery.url', 'js/verify_student/views/step_view', - 'js/verify_student/views/requirements_view' ] }, 'js/verify_student/views/payment_confirmation_step_view': { @@ -436,7 +426,6 @@ 'underscore', 'gettext', 'js/verify_student/views/step_view', - 'js/verify_student/views/requirements_view' ] }, 'js/verify_student/views/face_photo_step_view': { @@ -484,7 +473,6 @@ 'backbone', 'gettext', 'js/verify_student/models/verification_model', - 'js/verify_student/views/progress_view', 'js/verify_student/views/intro_step_view', 'js/verify_student/views/make_payment_step_view', 'js/verify_student/views/payment_confirmation_step_view', diff --git a/lms/static/js/spec/verify_student/pay_and_verify_view_spec.js b/lms/static/js/spec/verify_student/pay_and_verify_view_spec.js index ddd15b7720..baf3edbdf3 100644 --- a/lms/static/js/spec/verify_student/pay_and_verify_view_spec.js +++ b/lms/static/js/spec/verify_student/pay_and_verify_view_spec.js @@ -69,27 +69,8 @@ define(['jquery', 'js/common_helpers/template_helpers', 'js/verify_student/views }; var expectStepRendered = function( stepName, stepNum, numSteps ) { - var i, j, sel; - // Expect that the step container div rendered expect( $( '.' + stepName ).length > 0 ).toBe( true ); - - // Expect that the progress indicator shows the correct step - expect( $( '#progress-step-' + stepNum ).hasClass( 'is-current' ) ).toBe( true ); - - // Expect that all steps before this step are completed - for ( i = 1; i < stepNum; i++ ) { - sel = $( '#progress-step-' + i ); - expect( sel.hasClass('is-completed') ).toBe( true ); - expect( sel.hasClass('is-current') ).toBe( false ); - } - - // Expect that all steps after this step are neither completed nor current - for ( j = stepNum + 1; j <= numSteps; j++ ) { - sel = $( '#progress-step-' + j ); - expect( sel.hasClass('is-completed') ).toBe( false ); - expect( sel.hasClass('is-current') ).toBe( false ); - } }; beforeEach(function() { diff --git a/lms/static/js/spec/verify_student/review_photos_step_view_spec.js b/lms/static/js/spec/verify_student/review_photos_step_view_spec.js index 65e86ef098..1206d825c4 100644 --- a/lms/static/js/spec/verify_student/review_photos_step_view_spec.js +++ b/lms/static/js/spec/verify_student/review_photos_step_view_spec.js @@ -31,10 +31,6 @@ define([ }).render(); }; - var confirmPhotos = function( isConfirmed ) { - $('#confirm_pics_good').trigger( 'click' ); - }; - var submitPhotos = function( requests, expectedParams, succeeds ) { // Submit the photos $( '#next_step_button' ).click(); @@ -69,27 +65,11 @@ define([ TemplateHelpers.installTemplate( 'templates/verify_student/review_photos_step' ); }); - it( 'requires the user to confirm before submitting photos', function() { - createView(); - - // Initially disabled - expectSubmitEnabled( false ); - - // Confirm the photos, enabling submission - confirmPhotos( true ); - expectSubmitEnabled( true ); - - // Unconfirm the photos, disabling submission - confirmPhotos( false ); - expectSubmitEnabled( false ); - }); - it( 'allows the user to change her full name', function() { var requests = AjaxHelpers.requests( this ); createView(); setFullName( FULL_NAME ); - confirmPhotos( true ); submitPhotos( requests, { @@ -105,7 +85,6 @@ define([ var requests = AjaxHelpers.requests( this ); createView(); - confirmPhotos( true ); submitPhotos( requests, { @@ -124,7 +103,6 @@ define([ var view = createView(), requests = AjaxHelpers.requests( this ); - confirmPhotos( true ); submitPhotos( requests, { diff --git a/lms/static/js/verify_student/pay_and_verify.js b/lms/static/js/verify_student/pay_and_verify.js index e2a9cdf4a1..8430c0749f 100644 --- a/lms/static/js/verify_student/pay_and_verify.js +++ b/lms/static/js/verify_student/pay_and_verify.js @@ -46,6 +46,8 @@ var edx = edx || {}; isActive: el.data('is-active'), requirements: el.data('requirements'), courseKey: el.data('course-key'), + courseName: el.data('course-name'), + upgrade: el.data('data-msg-key') === 'upgrade', minPrice: el.data('course-mode-min-price'), contributionAmount: el.data('contribution-amount'), suggestedPrices: _.filter( @@ -53,12 +55,16 @@ var edx = edx || {}; function( price ) { return Boolean( price ); } ), currency: el.data('course-mode-currency'), - purchaseEndpoint: el.data('purchase-endpoint') + purchaseEndpoint: el.data('purchase-endpoint'), + verificationDeadline: el.data('verification-deadline') }, 'payment-confirmation-step': { + courseKey: el.data('course-key'), courseName: el.data('course-name'), courseStartDate: el.data('course-start-date'), - coursewareUrl: el.data('courseware-url') + coursewareUrl: el.data('courseware-url'), + platformName: el.data('platform-name'), + requirements: el.data('requirements') }, 'review-photos-step': { fullName: el.data('full-name'), @@ -67,7 +73,14 @@ var edx = edx || {}; 'enrollment-confirmation-step': { courseName: el.data('course-name'), courseStartDate: el.data('course-start-date'), - coursewareUrl: el.data('courseware-url') + coursewareUrl: el.data('courseware-url'), + platformName: el.data('platform-name') + }, + 'face-photo-step': { + platformName: el.data('platform-name') + }, + 'id-photo-step': { + platformName: el.data('platform-name') } } }).render(); diff --git a/lms/static/js/verify_student/views/enrollment_confirmation_step_view.js b/lms/static/js/verify_student/views/enrollment_confirmation_step_view.js index 06feb79527..6529402f91 100644 --- a/lms/static/js/verify_student/views/enrollment_confirmation_step_view.js +++ b/lms/static/js/verify_student/views/enrollment_confirmation_step_view.js @@ -21,7 +21,8 @@ var edx = edx || {}; return { courseName: '', courseStartDate: '', - coursewareUrl: '' + coursewareUrl: '', + platformName: '' }; } }); diff --git a/lms/static/js/verify_student/views/face_photo_step_view.js b/lms/static/js/verify_student/views/face_photo_step_view.js index 8008565a13..f1d573cf53 100644 --- a/lms/static/js/verify_student/views/face_photo_step_view.js +++ b/lms/static/js/verify_student/views/face_photo_step_view.js @@ -10,6 +10,12 @@ var edx = edx || {}; edx.verify_student.FacePhotoStepView = edx.verify_student.StepView.extend({ + defaultContext: function() { + return { + platformName: '' + }; + }, + postRender: function() { var webcam = new edx.verify_student.WebcamPhotoView({ el: $( '#facecam' ), diff --git a/lms/static/js/verify_student/views/id_photo_step_view.js b/lms/static/js/verify_student/views/id_photo_step_view.js index 35ed9e7b5d..60a02e4c7b 100644 --- a/lms/static/js/verify_student/views/id_photo_step_view.js +++ b/lms/static/js/verify_student/views/id_photo_step_view.js @@ -10,6 +10,12 @@ var edx = edx || {}; edx.verify_student.IDPhotoStepView = edx.verify_student.StepView.extend({ + defaultContext: function() { + return { + platformName: '' + }; + }, + postRender: function() { var webcam = new edx.verify_student.WebcamPhotoView({ el: $( '#idcam' ), diff --git a/lms/static/js/verify_student/views/intro_step_view.js b/lms/static/js/verify_student/views/intro_step_view.js index 0eead8f279..f9f6eac3c7 100644 --- a/lms/static/js/verify_student/views/intro_step_view.js +++ b/lms/static/js/verify_student/views/intro_step_view.js @@ -14,7 +14,9 @@ var edx = edx || {}; return { introTitle: '', introMsg: '', - isActive: false + isActive: false, + platformName: '', + requirements: {} }; }, @@ -25,11 +27,6 @@ var edx = edx || {}; // and if they reload the page we want them to stay on the // second step. postRender: function() { - new edx.verify_student.RequirementsView({ - el: $( '.requirements-container', this.el ), - requirements: this.stepData.requirements - }).render(); - // Track a virtual pageview, for easy funnel reconstruction. window.analytics.page( 'verification', this.templateName ); } diff --git a/lms/static/js/verify_student/views/make_payment_step_view.js b/lms/static/js/verify_student/views/make_payment_step_view.js index 3b24b625e2..3066c57957 100644 --- a/lms/static/js/verify_student/views/make_payment_step_view.js +++ b/lms/static/js/verify_student/views/make_payment_step_view.js @@ -15,41 +15,62 @@ var edx = edx || {}; isActive: true, suggestedPrices: [], minPrice: 0, - currency: "usd" + currency: 'usd', + upgrade: false, + verificationDeadline: '', + courseName: '', + requirements: {}, + platformName: '' }; }, postRender: function() { - // Render requirements - new edx.verify_student.RequirementsView({ - el: $( '.requirements-container', this.el ), - requirements: this.stepData.requirements - }).render(); + var templateContext = this.templateContext(), + hasVisibleReqs = _.some( + templateContext.requirements, + function( isVisible ) { return isVisible; } + ); // Track a virtual pageview, for easy funnel reconstruction. window.analytics.page( 'payment', this.templateName ); // Update the contribution amount with the amount the user // selected in a previous screen. - if ( this.stepData.contributionAmount ) { - this.selectPaymentAmount( this.stepData.contributionAmount ); + if ( templateContext.contributionAmount ) { + this.selectPaymentAmount( templateContext.contributionAmount ); } - if ( this.templateContext().suggestedPrices.length > 0 ) { + // The contribution section is hidden by default + // Display it if the user hasn't already selected an amount + // or is upgrading. + // In the short-term, we're also displaying this if there + // are no requirements (e.g. the user already verified). + // Otherwise, there's absolutely nothing to do on this page. + // In the future, we'll likely skip directly to payment + // from the track selection page if this happens. + if ( templateContext.upgrade || !templateContext.contributionAmount || !hasVisibleReqs ) { + $( '.wrapper-task' ).removeClass( 'hidden' ).removeAttr( 'aria-hidden' ); + } + + if ( templateContext.suggestedPrices.length > 0 ) { // Enable the payment button once an amount is chosen - $( "input[name='contribution']" ).on( 'click', _.bind( this.enablePaymentButton, this ) ); + $( 'input[name="contribution"]' ).on( 'click', _.bind( this.setPaymentEnabled, this ) ); } else { // If there is only one payment option, then the user isn't shown // radio buttons, so we need to enable the radio button. - this.enablePaymentButton(); + this.setPaymentEnabled( true ); } // Handle payment submission - $( "#pay_button" ).on( 'click', _.bind( this.createOrder, this ) ); + $( '#pay_button' ).on( 'click', _.bind( this.createOrder, this ) ); }, - enablePaymentButton: function() { - $("#pay_button").removeClass("is-disabled"); + setPaymentEnabled: function( isEnabled ) { + if ( _.isUndefined( isEnabled ) ) { + isEnabled = true; + } + + $( '#pay_button' ).toggleClass( 'is-disabled', !isEnabled ); }, createOrder: function() { @@ -60,7 +81,7 @@ var edx = edx || {}; }; // Disable the payment button to prevent multiple submissions - $("#pay_button").addClass("is-disabled"); + this.setPaymentEnabled( false ); // Create the order for the amount $.ajax({ @@ -84,16 +105,16 @@ var edx = edx || {}; // these parameters, then submit it to the payment processor. // This will send the user to a hosted order page, // where she can enter credit card information. - var form = $( "#payment-processor-form" ); + var form = $( '#payment-processor-form' ); - $( "input", form ).remove(); + $( 'input', form ).remove(); - form.attr( "action", this.stepData.purchaseEndpoint ); - form.attr( "method", "POST" ); + form.attr( 'action', this.stepData.purchaseEndpoint ); + form.attr( 'method', 'POST' ); _.each( paymentParams, function( value, key ) { - $("").attr({ - type: "hidden", + $('').attr({ + type: 'hidden', name: key, value: value }).appendTo(form); @@ -121,15 +142,15 @@ var edx = edx || {}; }); // Re-enable the button so the user can re-try - $( "#pay_button" ).removeClass("is-disabled"); + $( '#pay_button' ).removeClass( 'is-disabled' ); }, getPaymentAmount: function() { - var contributionInput = $("input[name='contribution']:checked", this.el), + var contributionInput = $( 'input[name="contribution"]:checked' , this.el), amount = null; if ( contributionInput.attr('id') === 'contribution-other' ) { - amount = $( "input[name='contribution-other-amt']", this.el ).val(); + amount = $( 'input[name="contribution-other-amt"]' , this.el ).val(); } else { amount = contributionInput.val(); } @@ -167,7 +188,7 @@ var edx = edx || {}; } // In either case, enable the payment button - this.enablePaymentButton(); + this.setPaymentEnabled(); return amount; }, diff --git a/lms/static/js/verify_student/views/pay_and_verify_view.js b/lms/static/js/verify_student/views/pay_and_verify_view.js index 343ef98139..ee410ebacc 100644 --- a/lms/static/js/verify_student/views/pay_and_verify_view.js +++ b/lms/static/js/verify_student/views/pay_and_verify_view.js @@ -1,8 +1,8 @@ /** * Base view for the payment/verification flow. * - * This view is responsible for the "progress steps" - * at the top of the page, but it delegates + * This view is responsible for keeping track of the + * current step, but it delegates to * to subviews to render individual steps. * */ @@ -27,21 +27,11 @@ var edx = edx || {}; initialize: function( obj ) { this.errorModel = obj.errorModel || null; this.displaySteps = obj.displaySteps || []; - - this.progressView = new edx.verify_student.ProgressView({ - el: this.el, - displaySteps: this.displaySteps, - - // Determine which step we're starting on - // Depending on how the user enters the flow, - // this could be anywhere in the sequence of steps. - currentStepIndex: _.indexOf( - _.pluck( this.displaySteps, 'name' ), - obj.currentStep - ) - }); - this.initializeStepViews( obj.stepInfo || {} ); + this.currentStepIndex = _.indexOf( + _.pluck( this.displaySteps, 'name' ), + obj.currentStep + ); }, initializeStepViews: function( stepInfo ) { @@ -94,7 +84,6 @@ var edx = edx || {}; subviewConfig = { errorModel: this.errorModel, templateName: this.displaySteps[i].templateName, - nextStepNum: (i + 2), // Next index, starting from 1 nextStepTitle: nextStepTitle, stepData: stepData }; @@ -118,7 +107,6 @@ var edx = edx || {}; }, render: function() { - this.progressView.render(); this.renderCurrentStep(); return this; }, @@ -137,19 +125,30 @@ var edx = edx || {}; // underscore template. // When the view is rendered, it will overwrite the existing // step in the DOM. - stepName = this.displaySteps[ this.progressView.currentStepIndex ].name; + stepName = this.displaySteps[ this.currentStepIndex ].name; stepView = this.subviews[ stepName ]; stepView.el = stepEl; stepView.render(); }, nextStep: function() { - this.progressView.nextStep(); + this.currentStepIndex = Math.min( + this.currentStepIndex + 1, + this.displaySteps.length - 1 + ); this.render(); }, goToStep: function( stepName ) { - this.progressView.goToStep( stepName ); + var stepIndex = _.indexOf( + _.pluck( this.displaySteps, 'name' ), + stepName + ); + + if ( stepIndex >= 0 ) { + this.currentStepIndex = stepIndex; + } + this.render(); } }); diff --git a/lms/static/js/verify_student/views/payment_confirmation_step_view.js b/lms/static/js/verify_student/views/payment_confirmation_step_view.js index 47f8443d1a..ab662aa8f9 100644 --- a/lms/static/js/verify_student/views/payment_confirmation_step_view.js +++ b/lms/static/js/verify_student/views/payment_confirmation_step_view.js @@ -9,6 +9,18 @@ var edx = edx || {}; edx.verify_student = edx.verify_student || {}; edx.verify_student.PaymentConfirmationStepView = edx.verify_student.StepView.extend({ + + defaultContext: function() { + return { + courseKey: '', + courseName: '', + courseStartDate: '', + coursewareUrl: '', + platformName: '', + requirements: [] + }; + }, + /** * Retrieve receipt information from the shopping cart. * @@ -64,9 +76,7 @@ var edx = edx || {}; /** * The "Verify Later" button goes directly to the dashboard, - * The "Verify Now" button reloads this page with the "skip-first-step" - * flag set. This allows the user to navigate back to the confirmation - * if he/she wants to. + * The "Verify Now" button sends the user to the verification flow. * For this reason, we don't need any custom click handlers here, except for * those used to track business intelligence events. */ diff --git a/lms/static/js/verify_student/views/progress_view.js b/lms/static/js/verify_student/views/progress_view.js deleted file mode 100644 index 8ebe6bf61a..0000000000 --- a/lms/static/js/verify_student/views/progress_view.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Show progress steps in the payment/verification flow. - */ - - var edx = edx || {}; - - (function( $, _, Backbone, gettext ) { - 'use strict'; - - edx.verify_student = edx.verify_student || {}; - - edx.verify_student.ProgressView = Backbone.View.extend({ - - template: '#progress-tpl', - - initialize: function( obj ) { - this.displaySteps = obj.displaySteps || {}; - this.currentStepIndex = obj.currentStepIndex || 0; - }, - - nextStep: function() { - this.currentStepIndex = Math.min( - this.currentStepIndex + 1, - this.displaySteps.length - 1 - ); - }, - - goToStep: function( stepName ) { - var stepIndex = _.indexOf( - _.pluck( this.displaySteps, 'name' ), - stepName - ); - - if ( stepIndex >= 0 ) { - this.currentStepIndex = stepIndex; - } - }, - - render: function() { - var renderedHtml, context; - - context = { - steps: this.steps() - }; - - renderedHtml = _.template( $(this.template).html(), context ); - $(this.el).html(renderedHtml); - }, - - steps: function() { - var i, - stepDescription, - steps = []; - - for ( i = 0; i < this.displaySteps.length; i++ ) { - stepDescription = { - title: this.displaySteps[i].title, - isCurrent: (i === this.currentStepIndex ), - isComplete: (i < this.currentStepIndex ) - }; - steps.push(stepDescription); - } - - return steps; - } - }); - })( $, _, Backbone, gettext ); diff --git a/lms/static/js/verify_student/views/requirements_view.js b/lms/static/js/verify_student/views/requirements_view.js deleted file mode 100644 index ec0430e1f3..0000000000 --- a/lms/static/js/verify_student/views/requirements_view.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * View for the requirements (webcam, credit card, etc.) - */ -var edx = edx || {}; - -(function( $, Backbone, _, gettext ) { - 'use strict'; - - edx.verify_student = edx.verify_student || {}; - - edx.verify_student.RequirementsView = Backbone.View.extend({ - - template: "#requirements-tpl", - - initialize: function( obj ) { - this.requirements = obj.requirements || {}; - }, - - render: function() { - var renderedHtml = _.template( - $( this.template ).html(), - { requirements: this.requirements } - ); - $( this.el ).html( renderedHtml ); - } - - }); - -})( jQuery, Backbone, _, gettext ); diff --git a/lms/static/js/verify_student/views/review_photos_step_view.js b/lms/static/js/verify_student/views/review_photos_step_view.js index 822fc3aec3..5e72c76df5 100644 --- a/lms/static/js/verify_student/views/review_photos_step_view.js +++ b/lms/static/js/verify_student/views/review_photos_step_view.js @@ -12,8 +12,8 @@ var edx = edx || {}; defaultContext: function() { return { - platformName: "", - fullName: "", + platformName: '', + fullName: '', }; }, @@ -27,9 +27,6 @@ var edx = edx || {}; $( '.is-expandable' ).addClass('is-ready'); $( '.is-expandable .title-expand' ).on( 'click', this.expandCallback ); - // Disable the submit button until user confirmation - $( '#confirm_pics_good' ).on( 'click', this.toggleSubmitEnabled ); - // Go back to the first photo step if we need to retake photos $( '#retake_photos_button' ).on( 'click', _.bind( this.retakePhotos, this ) ); @@ -40,10 +37,6 @@ var edx = edx || {}; window.analytics.page( 'verification', this.templateName ); }, - toggleSubmitEnabled: function() { - $( '#next_step_button' ).toggleClass( 'is-disabled' ); - }, - retakePhotos: function() { // Track the user's intent to retake their photos window.analytics.track( 'edx.bi.user.images.retaken', { @@ -73,11 +66,10 @@ var edx = edx || {}; }, handleSubmissionError: function( xhr ) { - var isConfirmChecked = $( "#confirm_pics_good" ).prop('checked'), - errorMsg = gettext( 'An unexpected error occurred. Please try again later.' ); + var errorMsg = gettext( 'An unexpected error occurred. Please try again later.' ); // Re-enable the submit button to allow the user to retry - $( '#next_step_button' ).toggleClass( 'is-disabled', !isConfirmChecked ); + $( '#next_step_button' ).removeClass( 'is-disabled' ); if ( xhr.status === 400 ) { errorMsg = xhr.responseText; @@ -91,11 +83,12 @@ var edx = edx || {}; }, expandCallback: function( event ) { + var title; + event.preventDefault(); $(this).next('.expandable-area' ).slideToggle(); - - var title = $( this ).parent(); + title = $( this ).parent(); title.toggleClass( 'is-expanded' ); title.attr( 'aria-expanded', !title.attr( 'aria-expanded' ) ); } diff --git a/lms/static/js/verify_student/views/step_view.js b/lms/static/js/verify_student/views/step_view.js index 5b3afa31cb..794726a672 100644 --- a/lms/static/js/verify_student/views/step_view.js +++ b/lms/static/js/verify_student/views/step_view.js @@ -43,19 +43,6 @@ return this; }, - handleResponse: function( data ) { - var context = { - nextStepNum: this.nextStepNum, - nextStepTitle: this.nextStepTitle - }; - - // Include step-specific information - _.extend( context, this.stepData ); - - // Track a virtual pageview, for easy funnel reconstruction. - window.analytics.page( 'verification', this.templateName ); - }, - handleError: function( errorTitle, errorMsg ) { this.errorModel.set({ errorTitle: errorTitle || gettext( "Error" ), @@ -66,7 +53,6 @@ templateContext: function() { var context = { - nextStepNum: this.nextStepNum, nextStepTitle: this.nextStepTitle }; return _.extend( context, this.defaultContext(), this.stepData ); diff --git a/lms/static/sass/application-extend2.scss.mako b/lms/static/sass/application-extend2.scss.mako index 83c7664286..b1ff2a6a21 100644 --- a/lms/static/sass/application-extend2.scss.mako +++ b/lms/static/sass/application-extend2.scss.mako @@ -47,6 +47,7 @@ // base - specific views @import 'views/login-register'; @import 'views/verification'; +@import 'views/decoupled-verification'; @import 'views/shoppingcart'; // applications diff --git a/lms/static/sass/views/_decoupled-verification.scss b/lms/static/sass/views/_decoupled-verification.scss new file mode 100644 index 0000000000..47b5f52422 --- /dev/null +++ b/lms/static/sass/views/_decoupled-verification.scss @@ -0,0 +1,157 @@ +// Updates for decoupled verification A/B test +.verification-process { + .pay-and-verify { + .review { + .title.center-col { + padding: 0 calc( ( 100% - 750px ) / 2 ) 10px; + } + } + + .instruction { + &.center-col { + width: 750px; + margin-left: auto; + margin-right: auto; + } + } + + .requirements-container { + + .list-reqs { + width: 645px; + margin: 50px auto; + + .req { + width: 300px; + height: 250px; + min-height: 250px; + margin-right: 45px; + + &:last-of-type { + margin-right: 0; + } + } + + &.account-not-activated { + width: 990px; + + .req { + height: 290px; + min-height: 290px; + } + } + } + } + + .no-content { + margin-bottom: 50px; + } + + .nav-wizard { + &.center { + text-align: center; + } + + .right { + float: right; + padding: 15px 50px; + } + + .nav-link { + line-height: 45px; + } + + .prompt-verify .title { + width: 600px; + position: relative; + display: inline; + float: left; + line-height: 45px; + } + + .wizard-steps { + width: auto; + } + } + + .retake-photos { + color: $blue; + + &:hover { + cursor: pointer; + } + } + + .tip { + .is-expandable { + .title-expand { + color: $blue !important; + } + } + + .expandable-area { + margin-top: 5px; + } + } + + .help-tips { + margin-left: 0 !important; + } + + .wrapper-task { + .msg-retake { + margin-top: 0; + } + + .wrapper-photos { + margin-bottom: 0 !important; + } + } + + .report-course { + .course-actions { + td:last-of-type { + width: 300px; + } + } + } + .enrollment-status-footer { + margin: 50px 0; + + h4 { + font-weight: 600; + } + + .verify-pending-msg { + margin: 20px 0; + } + } + } + + .tooltip { + @include transition(opacity $tmg-f3 ease-out 0s); + @include font-size(12); + position: absolute; + width: 350px; + top: 0; + left: 0; + padding: 10px 20px; + border-radius: 3px; + background: rgba(0, 0, 0, 0.85); + line-height: 26px; + color: $white; + pointer-events: none; + opacity: 0.0; + + &:after { + @include font-size(20); + content: '▾'; + display: block; + position: absolute; + bottom: -14px; + left: 50%; + margin-left: -7px; + color: rgba(0, 0, 0, 0.85); + } + } +} diff --git a/lms/templates/verify_student/enrollment_confirmation_step.underscore b/lms/templates/verify_student/enrollment_confirmation_step.underscore index 7226556271..be9075b097 100644 --- a/lms/templates/verify_student/enrollment_confirmation_step.underscore +++ b/lms/templates/verify_student/enrollment_confirmation_step.underscore @@ -1,15 +1,12 @@
-

<%- gettext( "Congratulations! You are now enrolled in the verified track." ) %>

+

<%- _.sprintf( gettext( "Congratulations! You are now verified on %(platformName)s!" ), { platformName: platformName } ) %>

-

<%- gettext( "You are now enrolled as a verified student! Your enrollment details are below.") %>

+

<%- gettext( "You are now enrolled as an ID verified student for:" ) %>

  • -

    - <%- gettext( "You are enrolled in " ) %> : -

    @@ -17,7 +14,6 @@ - @@ -27,17 +23,18 @@ - + - + @@ -46,5 +43,11 @@ + + diff --git a/lms/templates/verify_student/face_photo_step.underscore b/lms/templates/verify_student/face_photo_step.underscore index c278f37a64..2559429a2a 100644 --- a/lms/templates/verify_student/face_photo_step.underscore +++ b/lms/templates/verify_student/face_photo_step.underscore @@ -16,21 +16,27 @@
    • <%- gettext( "Make sure your face is well-lit" ) %>
    • <%- gettext( "Be sure your entire face is inside the frame" ) %>
    • +
    • + <%= _.sprintf( gettext( "Once in position, use the camera button %(icon)s to capture your face" ), { icon: '()' } ) %> +
    • <%- gettext( "Can we match the photo you took with the one on your ID?" ) %>
    • -
    • <%- gettext( "Once in position, use the camera button" ) %> () <%- gettext( "to capture your picture" ) %>
    • +
    • <%- gettext( "Use the retake photo button if you are not pleased with your photo" ) %>

    <%- gettext( "Common Questions" ) %>

    -
    -
    <%- gettext( "Why do you need my photo?" ) %>
    -
    <%- gettext( "As part of the verification process, we need your photo to confirm that you are you." ) %>
    -
    <%- gettext( "What do you do with this picture?" ) %>
    -
    <%- gettext( "We only use it to verify your identity. It is not displayed anywhere." ) %>
    +
    + <%- _.sprintf( gettext( "Why does %(platformName)s need my photo?" ), { platformName: platformName } ) %> +
    +
    <%- gettext( "As part of the verification process, we need your photo to confirm your identity." ) %>
    +
    + <%- _.sprintf( gettext( "What does %(platformName)s do with this picture?" ), { platformName: platformName } ) %> +
    +
    <%- gettext( "We encrypt it and send it to our secure authorization service for review. We use the highest levels of security and do not save the photo or information anywhere once the match has been completed." ) %>
    @@ -39,18 +45,11 @@ <% if ( nextStepTitle ) { %> + <% } %> diff --git a/lms/templates/verify_student/id_photo_step.underscore b/lms/templates/verify_student/id_photo_step.underscore index e7d389b701..a934cc4b56 100644 --- a/lms/templates/verify_student/id_photo_step.underscore +++ b/lms/templates/verify_student/id_photo_step.underscore @@ -1,6 +1,6 @@
    -

    <%- gettext( "Show Us Your ID" ) %>

    +

    <%- gettext( "Take a Photo of Your ID" ) %>

    <%- gettext("Use your webcam to take a picture of your ID so we can match it with your photo and the name on your account.") %>

    @@ -9,7 +9,7 @@
    -
    +

    <%- gettext( "Tips on taking a successful photo" ) %>

    @@ -18,8 +18,10 @@
  • <%- gettext( "Check that there isn't any glare" ) %>
  • <%- gettext( "Ensure that you can see your photo and read your name" ) %>
  • <%- gettext( "Try to keep your fingers at the edge to avoid covering important information" ) %>
  • -
  • <%- gettext( "Acceptable IDs include drivers licenses, passports, or other goverment-issued IDs that include your name and photo" ) %>
  • -
  • <%- gettext( "Once in position, use the camera button ") %> () <%- gettext( "to capture your ID" ) %>
  • +
  • <%- gettext( "Acceptable IDs include driver's licenses, passports, or other government-issued IDs that include your name and photo" ) %>
  • +
  • + <%= _.sprintf( gettext( "Once in position, use the camera button %(icon)s to capture your ID" ), { icon: '()' } ) %> +
  • @@ -42,17 +44,9 @@ <% if ( nextStepTitle ) { %> <% } %>
    diff --git a/lms/templates/verify_student/intro_step.underscore b/lms/templates/verify_student/intro_step.underscore index e97723167b..dc26459479 100644 --- a/lms/templates/verify_student/intro_step.underscore +++ b/lms/templates/verify_student/intro_step.underscore @@ -1,9 +1,59 @@

    <%- introTitle %>

    -

    <%- introMsg %>

    + <% if ( introMsg ) { %> +

    <%- introMsg %>

    + <% } %> -
    +
    +
      + <% if ( requirements['account-activation-required'] ) { %> +
    • +

      <%- gettext( "Activate Your Account" ) %>

      +
      + +
      + +
      +

      + <%- gettext( "Check your email" ) %> + <%- + gettext( "You need to activate your account before you can register for courses. Check your inbox for an activation email." ) + %> + +

      +
      +
    • + <% } %> + + <% if ( requirements['photo-id-required'] ) { %> +
    • +

      <%- gettext( "Photo ID" ) %>

      +
      + + +
      + +
      +

      + <%- gettext( "A driver's license, passport, or government-issued ID with your name and picture" ) %> +

      +
      +
    • + <% } %> + + <% if ( requirements['webcam-required'] ) { %> +
    • +

      <%- gettext( "Webcam" ) %>

      +
      + +
      + +
      +
    • + <% } %> +
    +
    <% if ( nextStepTitle ) { %>
    <%- gettext( "A list of courses you have just enrolled in as a verified student" ) %>
    <%- gettext( "Course" ) %> <%- gettext( "Status" ) %><%- gettext( "Options" ) %>
    <%- _.sprintf( gettext( "Starts: %(start)s" ), { start: courseStartDate } ) %> - <% if ( coursewareUrl ) { %> - <%- gettext( "Go to Course" ) %> - <% } %> -
    - <%- gettext("Go to your dashboard") %> + + <% if ( coursewareUrl ) { %> + <%- gettext( "Explore your course!" ) %> + <% } %> + + <%- gettext("Go to your dashboard") %>