From 98cf69bac514679049f1281efbfc1a2109316538 Mon Sep 17 00:00:00 2001 From: Matthew Piatetsky Date: Wed, 16 Aug 2017 11:25:37 -0400 Subject: [PATCH 1/2] Revert "Revert "LEARNER-1510 Add unenrollment reason survey"" This reverts commit 441bccf4b6f830aade489085d1273e29a9d305d6. --- .../courseware/features/registration.feature | 10 -- lms/static/js/dashboard/legacy.js | 120 ------------- .../learner_dashboard/unenrollment_factory.js | 13 ++ .../learner_dashboard/views/unenroll_view.js | 167 ++++++++++++++++++ .../learner_dashboard/unenroll_view_spec.js | 45 +++++ lms/static/lms/js/build.js | 1 + lms/static/lms/js/spec/main.js | 1 + lms/static/sass/multicourse/_dashboard.scss | 39 ++++ lms/templates/dashboard.html | 12 +- .../dashboard/_dashboard_course_listing.html | 1 + lms/templates/dashboard/_reason_survey.html | 35 ++++ 11 files changed, 313 insertions(+), 131 deletions(-) create mode 100644 lms/static/js/learner_dashboard/unenrollment_factory.js create mode 100644 lms/static/js/learner_dashboard/views/unenroll_view.js create mode 100644 lms/static/js/spec/learner_dashboard/unenroll_view_spec.js create mode 100644 lms/templates/dashboard/_reason_survey.html diff --git a/lms/djangoapps/courseware/features/registration.feature b/lms/djangoapps/courseware/features/registration.feature index fe270a3f85..60b094de48 100644 --- a/lms/djangoapps/courseware/features/registration.feature +++ b/lms/djangoapps/courseware/features/registration.feature @@ -11,13 +11,3 @@ Feature: LMS.Register for a course When I register for the course "6.002x" Then I should see the course numbered "6.002x" in my dashboard And a "edx.course.enrollment.activated" server event is emitted - - Scenario: I can unenroll from a course - Given I am registered for the course "6.002x" - And I visit the dashboard - Then I should see the course numbered "6.002x" in my dashboard - When I unenroll from the course numbered "6.002x" - Then I should be on the dashboard page - And I should see an empty dashboard message - And I should NOT see the course numbered "6.002x" in my dashboard - And a "edx.course.enrollment.deactivated" server event is emitted diff --git a/lms/static/js/dashboard/legacy.js b/lms/static/js/dashboard/legacy.js index 80402ec519..06e6b032bf 100644 --- a/lms/static/js/dashboard/legacy.js +++ b/lms/static/js/dashboard/legacy.js @@ -62,7 +62,6 @@ } ); - // Generate the properties object to be passed along with business intelligence events. function generateProperties(element) { var $el = $(element), @@ -79,35 +78,6 @@ return properties; } - function setDialogAttributes(isPaidCourse, certNameLong, - courseNumber, courseName, enrollmentMode, showRefundOption) { - var diagAttr = {}; - - if (isPaidCourse) { - if (showRefundOption) { - diagAttr['data-refund-info'] = gettext('You will be refunded the amount you paid.'); - } else { - diagAttr['data-refund-info'] = gettext('You will not be refunded the amount you paid.'); - } - diagAttr['data-track-info'] = gettext('Are you sure you want to unenroll from the purchased course ' + - '%(courseName)s (%(courseNumber)s)?'); - } else if (enrollmentMode !== 'verified') { - diagAttr['data-track-info'] = gettext('Are you sure you want to unenroll from %(courseName)s ' + - '(%(courseNumber)s)?'); - } else if (showRefundOption) { - diagAttr['data-track-info'] = gettext('Are you sure you want to unenroll from the verified ' + - '%(certNameLong)s track of %(courseName)s (%(courseNumber)s)?'); - diagAttr['data-refund-info'] = gettext('You will be refunded the amount you paid.'); - } else { - diagAttr['data-track-info'] = gettext('Are you sure you want to unenroll from the verified ' + - '%(certNameLong)s track of %(courseName)s (%(courseNumber)s)?'); - diagAttr['data-refund-info'] = gettext('The refund deadline for this course has passed,' + - 'so you will not receive a refund.'); - } - - return diagAttr; - } - $('#failed-verification-button-dismiss').click(function() { $.ajax({ url: urls.verifyToggleBannerFailedOff, @@ -131,79 +101,6 @@ } edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event); }); - $('.action-unenroll').click(function(event) { - var isPaidCourse = $(event.target).data('course-is-paid-course') === 'True'; - var certNameLong = $(event.target).data('course-cert-name-long'); - var enrollmentMode = $(event.target).data('course-enrollment-mode'); - - var courseNumber = $(event.target).data('course-number'); - var courseName = $(event.target).data('course-name'); - var courseRefundUrl = $(event.target).data('course-refund-url'); - var dialogMessageAttr; - - var request = $.ajax({ - url: courseRefundUrl, - method: 'GET', - dataType: 'json' - }); - request.success(function(data, textStatus, xhr) { - if (xhr.status === 200) { - dialogMessageAttr = setDialogAttributes(isPaidCourse, certNameLong, - courseNumber, courseName, enrollmentMode, data.course_refundable_status); - - $('#track-info').empty(); - $('#refund-info').empty(); - - $('#track-info').html(interpolate(dialogMessageAttr['data-track-info'], { - courseNumber: ['', courseNumber, ''].join(''), - courseName: ['', courseName, ''].join(''), - certNameLong: ['', certNameLong, ''].join('') - }, true)); - - - if ('data-refund-info' in dialogMessageAttr) { - $('#refund-info').text(dialogMessageAttr['data-refund-info']); - } - - $('#unenroll_course_id').val($(event.target).data('course-id')); - } else { - $('#unenroll_error').text( - gettext('Unable to determine whether we should give you a refund because' + - ' of System Error. Please try again later.') - ).stop() - .css('display', 'block'); - - $('#unenroll_form input[type="submit"]').prop('disabled', true); - } - edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event); - }); - request.fail(function() { - $('#unenroll_error').text( - gettext('Unable to determine whether we should give you a refund because' + - ' of System Error. Please try again later.') - ).stop() - .css('display', 'block'); - - $('#unenroll_form input[type="submit"]').prop('disabled', true); - - edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event); - }); - $('#unenroll-modal').css('position', 'fixed'); - }); - - $('#unenroll_form').on('ajax:complete', function(event, xhr) { - if (xhr.status === 200) { - location.href = urls.dashboard; - } else if (xhr.status === 403) { - location.href = urls.signInUser + '?course_id=' + - encodeURIComponent($('#unenroll_course_id').val()) + '&enrollment_action=unenroll'; - } else { - $('#unenroll_error').text( - xhr.responseText ? xhr.responseText : gettext('An error occurred. Please try again later.') - ).stop() - .css('display', 'block'); - } - }); $('#email_settings_form').submit(function() { $.ajax({ @@ -236,22 +133,5 @@ $(this).attr('id', 'email-settings-' + index); }); - $('.action-unenroll').each(function(index) { - // a bit of a hack, but gets the unique selector for the modal trigger - var trigger = '#' + $(this).attr('id'); - accessibleModal( - trigger, - '#unenroll-modal .close-modal', - '#unenroll-modal', - '#dashboard-main' - ); - $(this).attr('id', 'unenroll-' + index); - }); - - $('#unregister_block_course').click(function(event) { - $('#unenroll_course_id').val($(event.target).data('course-id')); - $('#unenroll_course_number').text($(event.target).data('course-number')); - $('#unenroll_course_name').text($(event.target).data('course-name')); - }); }; })(jQuery, gettext, Logger, accessible_modal, interpolate); diff --git a/lms/static/js/learner_dashboard/unenrollment_factory.js b/lms/static/js/learner_dashboard/unenrollment_factory.js new file mode 100644 index 0000000000..4afca20bbb --- /dev/null +++ b/lms/static/js/learner_dashboard/unenrollment_factory.js @@ -0,0 +1,13 @@ +(function(define) { + 'use strict'; + + define([ + 'js/learner_dashboard/views/unenroll_view' + ], + function(UnenrollView) { + return function(options) { + var Unenroll = new UnenrollView(options); + return Unenroll; + }; + }); +}).call(this, define || RequireJS.define); diff --git a/lms/static/js/learner_dashboard/views/unenroll_view.js b/lms/static/js/learner_dashboard/views/unenroll_view.js new file mode 100644 index 0000000000..a0189018f6 --- /dev/null +++ b/lms/static/js/learner_dashboard/views/unenroll_view.js @@ -0,0 +1,167 @@ +(function(define) { + 'use strict'; + define(['backbone', + 'jquery', + 'underscore', + 'gettext', + 'edx-ui-toolkit/js/utils/html-utils' + ], + function( + Backbone, + $, + _, + gettext, + HtmlUtils + ) { + return Backbone.View.extend({ + el: '.unenroll-modal', + + unenrollClick: function(event) { + var isPaidCourse = $(event.target).data('course-is-paid-course') === 'True', + certNameLong = $(event.target).data('course-cert-name-long'), + enrollmentMode = $(event.target).data('course-enrollment-mode'), + courseNumber = $(event.target).data('course-number'), + courseName = $(event.target).data('course-name'), + courseRefundUrl = $(event.target).data('course-refund-url'), + dialogMessageAttr, + request = $.ajax({ + url: courseRefundUrl, + method: 'GET', + dataType: 'json' + }); + request.success(function(data, textStatus, xhr) { + if (xhr.status === 200) { + dialogMessageAttr = setDialogAttributes(isPaidCourse, certNameLong, + courseNumber, courseName, enrollmentMode, data.course_refundable_status); + + $('#track-info').empty(); + $('#refund-info').empty(); + + $('#track-info').html(interpolate(dialogMessageAttr['data-track-info'], { + courseNumber: ['', courseNumber, ''].join(''), + courseName: ['', courseName, ''].join(''), + certNameLong: ['', certNameLong, ''].join('') + }, true)); + + + if ('data-refund-info' in dialogMessageAttr) { + $('#refund-info').text(dialogMessageAttr['data-refund-info']); + } + + $('#unenroll_course_id').val($(event.target).data('course-id')); + } else { + $('#unenroll_error').text( + gettext('Unable to determine whether we should give you a refund because' + + ' of System Error. Please try again later.') + ).stop() + .css('display', 'block'); + + $('#unenroll_form input[type="submit"]').prop('disabled', true); + } + edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event); + }); + request.fail(function() { + $('#unenroll_error').text( + gettext('Unable to determine whether we should give you a refund because' + + ' of System Error. Please try again later.') + ).stop() + .css('display', 'block'); + + $('#unenroll_form input[type="submit"]').prop('disabled', true); + + edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event); + }); + + // Randomize survey option order + survey = document.querySelector('.options'); + for (i = survey.children.length - 1; i >= 0; i--) { + survey.appendChild(survey.children[Math.random() * i | 0]); + } + }, + + function setDialogAttributes(isPaidCourse, certNameLong, + courseNumber, courseName, enrollmentMode, showRefundOption) { + var diagAttr = {}; + + if (isPaidCourse) { + if (showRefundOption) { + diagAttr['data-refund-info'] = gettext('You will be refunded the amount you paid.'); + } else { + diagAttr['data-refund-info'] = gettext('You will not be refunded the amount you paid.'); + } + diagAttr['data-track-info'] = gettext('Are you sure you want to unenroll from the purchased course ' + + '%(courseName)s (%(courseNumber)s)?'); + } else if (enrollmentMode !== 'verified') { + diagAttr['data-track-info'] = gettext('Are you sure you want to unenroll from %(courseName)s ' + + '(%(courseNumber)s)?'); + } else if (showRefundOption) { + diagAttr['data-track-info'] = gettext('Are you sure you want to unenroll from the verified ' + + '%(certNameLong)s track of %(courseName)s (%(courseNumber)s)?'); + diagAttr['data-refund-info'] = gettext('You will be refunded the amount you paid.'); + } else { + diagAttr['data-track-info'] = gettext('Are you sure you want to unenroll from the verified ' + + '%(certNameLong)s track of %(courseName)s (%(courseNumber)s)?'); + diagAttr['data-refund-info'] = gettext('The refund deadline for this course has passed,' + + 'so you will not receive a refund.'); + } + + return diagAttr; + }, + + switchToSlideOne: function() { + var reasonsSurvey = HtmlUtils.HTML($('.reasons_survey')); + $('.inner-wrapper header').hide(); + $('#unenroll_form').after(HtmlUtils.ensureHtml(reasonsSurvey).toString()).hide(); + $('.reasons_survey .slide1').removeClass('hidden'); + }, + + switchToSlideTwo: function() { + var reason = $(".reasons_survey input[name='reason']:checked").attr('val'); + if (reason === 'Other') { + reason = $('.other_text').val(); + } + if (reason) { + window.analytics.track('unenrollment_reason.selected', { + category: 'user-engagement', + label: reason, + displayName: 'v1' + }); + } + HtmlUtils.setHtml($('.reasons_survey'), HtmlUtils.HTML($('.slide2').html())); + $('.reasons_survey .return_to_dashboard').attr('href', this.urls.dashboard); + $('.reasons_survey .browse_courses').attr('href', this.urls.browseCourses); + }, + + unenrollComplete: function(event, xhr) { + if (xhr.status === 200) { + this.switchToSlideOne(); + $('.submit_reasons').click(this.switchToSlideTwo.bind(this)); + } else if (xhr.status === 403) { + location.href = this.urls.signInUser + '?course_id=' + + encodeURIComponent($('#unenroll_course_id').val()) + '&enrollment_action=unenroll'; + } else { + $('#unenroll_error').text( + gettext('Unable to determine whether we should give you a refund because' + + ' of System Error. Please try again later.') + ).stop() + .css('display', 'block'); + } + }, + + initialize: function(options) { + this.urls = options.urls; + + $('.action-unenroll').click(this.unenrollClick); + + $('#unenroll_form').on('ajax:complete', this.unenrollComplete.bind(this)); + + $('#unregister_block_course').click(function(event) { + $('#unenroll_course_id').val($(event.target).data('course-id')); + $('#unenroll_course_number').text($(event.target).data('course-number')); + $('#unenroll_course_name').text($(event.target).data('course-name')); + }); + } + }); + } + ); +}).call(this, define || RequireJS.define); diff --git a/lms/static/js/spec/learner_dashboard/unenroll_view_spec.js b/lms/static/js/spec/learner_dashboard/unenroll_view_spec.js new file mode 100644 index 0000000000..b99ba17d0e --- /dev/null +++ b/lms/static/js/spec/learner_dashboard/unenroll_view_spec.js @@ -0,0 +1,45 @@ +define([ + 'backbone', + 'js/learner_dashboard/views/unenroll_view' +], function(Backbone, UnenrollView) { + 'use strict'; + + describe('Unenroll View', function() { + var view = null, + options = { + urls: { + dashboard: '/dashboard', + browseCourses: '/courses' + } + }, + initView; + + initView = function() { + return new UnenrollView(options); + }; + + beforeEach(function() { + setFixtures('
'); // eslint-disable-line max-len + }); + + afterEach(function() { + view.remove(); + }); + + it('should exist', function() { + view = initView(); + expect(view).toBeDefined(); + }); + + it('switch between slides', function() { + view = initView(); + expect($('.slide1').hasClass('hidden')).toEqual(true); + view.switchToSlideOne(); + expect($('.slide1').hasClass('hidden')).toEqual(false); + expect($('.slide2').hasClass('hidden')).toEqual(true); + view.switchToSlideTwo(); + expect($('.slide2').hasClass('hidden')).toEqual(true); + }); + }); +} +); diff --git a/lms/static/lms/js/build.js b/lms/static/lms/js/build.js index c2141007e0..b4a74f5a52 100644 --- a/lms/static/lms/js/build.js +++ b/lms/static/lms/js/build.js @@ -32,6 +32,7 @@ 'js/groups/views/cohorts_dashboard_factory', 'js/discussions_management/views/discussions_dashboard_factory', 'js/header_factory', + 'js/learner_dashboard/unenrollment_factory', 'js/learner_dashboard/program_details_factory', 'js/learner_dashboard/program_list_factory', 'js/student_account/logistration_factory', diff --git a/lms/static/lms/js/spec/main.js b/lms/static/lms/js/spec/main.js index e377f28e31..954a962606 100644 --- a/lms/static/lms/js/spec/main.js +++ b/lms/static/lms/js/spec/main.js @@ -759,6 +759,7 @@ 'js/spec/learner_dashboard/program_details_header_spec.js', 'js/spec/learner_dashboard/program_details_view_spec.js', 'js/spec/learner_dashboard/program_details_sidebar_view_spec.js', + 'js/spec/learner_dashboard/unenroll_view_spec.js', 'js/spec/learner_dashboard/course_card_view_spec.js', 'js/spec/learner_dashboard/course_enroll_view_spec.js', 'js/spec/markdown_editor_spec.js', diff --git a/lms/static/sass/multicourse/_dashboard.scss b/lms/static/sass/multicourse/_dashboard.scss index 0b3c9e4752..8dec25eb3a 100644 --- a/lms/static/sass/multicourse/_dashboard.scss +++ b/lms/static/sass/multicourse/_dashboard.scss @@ -1498,3 +1498,42 @@ a.fade-cover{ } } } + +.reasons_survey { + padding: 20px; + + .option { + margin-bottom: 10px; + display: block; + } + + input { + margin-right: 10px; + } + + .unenroll-header { + background-image: none; + } + + .other_text { + margin-top: 10px; + margin-top: 0; + } + + .other_radio { + margin-top: 10px; + } + + .submit_reasons { + margin-top: 10px; + } + + .survey_button { + width: 30%; + margin-top: 10px; + margin-left: 2.5%; + margin-right: 2.5%; + color: white; + } + +} diff --git a/lms/templates/dashboard.html b/lms/templates/dashboard.html index 552d815e3b..99e2ce6191 100644 --- a/lms/templates/dashboard.html +++ b/lms/templates/dashboard.html @@ -42,6 +42,16 @@ from openedx.core.djangolib.markup import HTML, Text }); }); + <%static:require_module module_name="js/learner_dashboard/unenrollment_factory" class_name="UnenrollmentFactory"> + UnenrollmentFactory({ + urls: { + dashboard: "${reverse('dashboard') | n, js_escaped_string}", + signInUser: "${reverse('signin_user') | n, js_escaped_string}", + changeEmailSettings: "${reverse('change_email_settings') | n, js_escaped_string}", + browseCourses: "${marketing_link('COURSES') | n, js_escaped_string}" + } + }); + % if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'): <%static:require_module module_name="course_search/js/dashboard_search_factory" class_name="DashboardSearchFactory"> DashboardSearchFactory(); @@ -244,7 +254,7 @@ from openedx.core.djangolib.markup import HTML, Text -
+

diff --git a/lms/templates/dashboard/_dashboard_course_listing.html b/lms/templates/dashboard/_dashboard_course_listing.html index ec2874228d..943d6fd347 100644 --- a/lms/templates/dashboard/_dashboard_course_listing.html +++ b/lms/templates/dashboard/_dashboard_course_listing.html @@ -220,6 +220,7 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_
diff --git a/themes/edx.org/lms/templates/dashboard.html b/themes/edx.org/lms/templates/dashboard.html index e6d9ee4bd6..deaf0cb484 100644 --- a/themes/edx.org/lms/templates/dashboard.html +++ b/themes/edx.org/lms/templates/dashboard.html @@ -43,6 +43,17 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers }); }); + <%static:require_module module_name="js/learner_dashboard/unenrollment_factory" class_name="UnenrollmentFactory"> + UnenrollmentFactory({ + urls: { + dashboard: "${reverse('dashboard') | n, js_escaped_string}", + signInUser: "${reverse('signin_user') | n, js_escaped_string}", + changeEmailSettings: "${reverse('change_email_settings') | n, js_escaped_string}", + browseCourses: "${marketing_link('COURSES') | n, js_escaped_string}" + }, + isEdx: true + }); + % if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'): <%static:require_module module_name="course_search/js/dashboard_search_factory" class_name="DashboardSearchFactory"> DashboardSearchFactory(); @@ -258,7 +269,7 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers -
+