add dashboard entitlement actions
This commit is contained in:
@@ -98,7 +98,7 @@ var accessible_modal = function(trigger, closeButtonId, modalId, mainPageId) {
|
||||
});
|
||||
|
||||
// get modal to exit on escape key
|
||||
$('.modal').on('keydown', function(e) {
|
||||
$(modalId).on('keydown', function(e) {
|
||||
var keyCode = e.keyCode || e.which;
|
||||
// 27 is the javascript keycode for the ESC key
|
||||
if (keyCode === 27) {
|
||||
|
||||
@@ -8,10 +8,10 @@ var edx = edx || {};
|
||||
|
||||
// Generate the properties object to be passed along with business intelligence events.
|
||||
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu = function(event) {
|
||||
// define variables for code legibility
|
||||
var dashboardIndex = $(event.currentTarget).data().dashboardIndex,
|
||||
$dropdown = $('#actions-dropdown-' + dashboardIndex),
|
||||
$dropdownButton = $('#actions-dropdown-link-' + dashboardIndex),
|
||||
var $target = $(event.currentTarget),
|
||||
dashboardIndex = $target.data().dashboardIndex,
|
||||
$dropdown = $($target.data('dropdownSelector') || '#actions-dropdown-' + dashboardIndex),
|
||||
$dropdownButton = $($target.data('dropdownButtonSelector') || '#actions-dropdown-link-' + dashboardIndex),
|
||||
ariaExpandedState = ($dropdownButton.attr('aria-expanded') === 'true'),
|
||||
menuItems = $dropdown.find('a');
|
||||
|
||||
@@ -76,14 +76,15 @@ var edx = edx || {};
|
||||
});
|
||||
};
|
||||
|
||||
edx.dashboard.dropdown.bindToggleButtons = function() {
|
||||
$('.action-more').bind(
|
||||
edx.dashboard.dropdown.bindToggleButtons = function(selector) {
|
||||
$(selector).bind(
|
||||
'click',
|
||||
edx.dashboard.dropdown.toggleCourseActionsDropdownMenu
|
||||
);
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
edx.dashboard.dropdown.bindToggleButtons();
|
||||
edx.dashboard.dropdown.bindToggleButtons('.action-more');
|
||||
edx.dashboard.dropdown.bindToggleButtons('.js-entitlement-action-more');
|
||||
});
|
||||
}(jQuery));
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
(function(define) {
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'js/learner_dashboard/views/entitlement_unenrollment_view'
|
||||
],
|
||||
function(EntitlementUnenrollmentView) {
|
||||
return function(options) {
|
||||
return new EntitlementUnenrollmentView(options);
|
||||
};
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -0,0 +1,134 @@
|
||||
(function(define) {
|
||||
'use strict';
|
||||
define(['backbone',
|
||||
'jquery',
|
||||
'gettext',
|
||||
'edx-ui-toolkit/js/utils/html-utils'
|
||||
],
|
||||
function(Backbone, $, gettext, HtmlUtils) {
|
||||
return Backbone.View.extend({
|
||||
el: '.js-entitlement-unenrollment-modal',
|
||||
closeButtonSelector: '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-close-btn',
|
||||
headerTextSelector: '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-header-text',
|
||||
errorTextSelector: '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-error-text',
|
||||
submitButtonSelector: '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-submit',
|
||||
triggerSelector: '.js-entitlement-action-unenroll',
|
||||
mainPageSelector: '#dashboard-main',
|
||||
genericErrorMsg: gettext('Your unenrollment request could not be processed. Please try again later.'),
|
||||
|
||||
initialize: function(options) {
|
||||
var view = this;
|
||||
this.dashboardPath = options.dashboardPath;
|
||||
this.signInPath = options.signInPath;
|
||||
|
||||
this.$submitButton = $(this.submitButtonSelector);
|
||||
this.$headerText = $(this.headerTextSelector);
|
||||
this.$errorText = $(this.errorTextSelector);
|
||||
|
||||
this.$submitButton.on('click', this.handleSubmit.bind(this));
|
||||
|
||||
$(this.triggerSelector).each(function() {
|
||||
var $trigger = $(this);
|
||||
|
||||
$trigger.on('click', view.handleTrigger.bind(view));
|
||||
|
||||
if (window.accessible_modal) {
|
||||
window.accessible_modal(
|
||||
'#' + $trigger.attr('id'),
|
||||
view.closeButtonSelector,
|
||||
'#' + view.$el.attr('id'),
|
||||
view.mainPageSelector
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleTrigger: function(event) {
|
||||
var $trigger = $(event.target),
|
||||
courseName = $trigger.data('courseName'),
|
||||
courseNumber = $trigger.data('courseNumber'),
|
||||
apiEndpoint = $trigger.data('entitlementApiEndpoint');
|
||||
|
||||
this.resetModal();
|
||||
this.setHeaderText(courseName, courseNumber);
|
||||
this.setSubmitData(apiEndpoint);
|
||||
|
||||
if (window.edx && window.edx.dashboard && window.edx.dashboard.dropdown) {
|
||||
window.edx.dashboard.dropdown.toggleCourseActionsDropdownMenu(event);
|
||||
this.$el.css('position', 'fixed');
|
||||
}
|
||||
},
|
||||
|
||||
handleSubmit: function() {
|
||||
var apiEndpoint = this.$submitButton.data('entitlementApiEndpoint');
|
||||
|
||||
if (apiEndpoint === undefined) {
|
||||
this.setError(this.genericErrorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$submitButton.prop('disabled', true);
|
||||
$.ajax({
|
||||
url: apiEndpoint,
|
||||
method: 'DELETE',
|
||||
complete: this.onComplete.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
resetModal: function() {
|
||||
this.$submitButton.removeData();
|
||||
this.$submitButton.prop('disabled', false);
|
||||
this.$headerText.empty();
|
||||
this.$errorText.removeClass('entitlement-unenrollment-modal-error-text-visible');
|
||||
this.$errorText.empty();
|
||||
},
|
||||
|
||||
setError: function(message) {
|
||||
this.$submitButton.prop('disabled', true);
|
||||
this.$errorText.empty();
|
||||
HtmlUtils.setHtml(
|
||||
this.$errorText,
|
||||
message
|
||||
);
|
||||
this.$errorText.addClass('entitlement-unenrollment-modal-error-text-visible');
|
||||
},
|
||||
|
||||
setHeaderText: function(courseName, courseNumber) {
|
||||
this.$headerText.empty();
|
||||
HtmlUtils.setHtml(
|
||||
this.$headerText,
|
||||
HtmlUtils.interpolateHtml(
|
||||
gettext('Are you sure you want to unenroll from {courseName} ({courseNumber})? You will be refunded the amount you paid.'), // eslint-disable-line max-len
|
||||
{
|
||||
courseName: courseName,
|
||||
courseNumber: courseNumber
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
setSubmitData: function(apiEndpoint) {
|
||||
this.$submitButton.removeData();
|
||||
this.$submitButton.data('entitlementApiEndpoint', apiEndpoint);
|
||||
},
|
||||
|
||||
onComplete: function(xhr) {
|
||||
var status = xhr.status,
|
||||
message = xhr.responseJSON && xhr.responseJSON.detail;
|
||||
|
||||
if (status === 204) {
|
||||
this.redirectTo(this.dashboardPath);
|
||||
} else if (status === 401 && message === 'Authentication credentials were not provided.') {
|
||||
this.redirectTo(this.signInPath + '?next=' + encodeURIComponent(this.dashboardPath));
|
||||
} else {
|
||||
this.setError(this.genericErrorMsg);
|
||||
}
|
||||
},
|
||||
|
||||
redirectTo: function(path) {
|
||||
window.location.href = path;
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -31,7 +31,7 @@ define(['js/dashboard/dropdown', 'jquery.simulate'],
|
||||
describe('edx.dashboard.dropdown.toggleCourseActionsDropdownMenu', function() {
|
||||
beforeEach(function() {
|
||||
loadFixtures('js/fixtures/dashboard/dashboard.html');
|
||||
window.edx.dashboard.dropdown.bindToggleButtons();
|
||||
window.edx.dashboard.dropdown.bindToggleButtons('.action-more');
|
||||
});
|
||||
|
||||
it('Clicking the .action-more button toggles the menu', function() {
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
define([
|
||||
'backbone',
|
||||
'jquery',
|
||||
'js/learner_dashboard/views/entitlement_unenrollment_view'
|
||||
], function(Backbone, $, EntitlementUnenrollmentView) {
|
||||
'use strict';
|
||||
|
||||
describe('EntitlementUnenrollmentView', function() {
|
||||
var view = null,
|
||||
options = {
|
||||
dashboardPath: '/dashboard',
|
||||
signInPath: '/login'
|
||||
},
|
||||
|
||||
initView = function() {
|
||||
return new EntitlementUnenrollmentView(options);
|
||||
},
|
||||
|
||||
modalHtml = '<a id="link1" class="js-entitlement-action-unenroll" ' +
|
||||
' data-course-name="Test Course 1" ' +
|
||||
' data-course-number="test1" ' +
|
||||
' data-entitlement-api-endpoint="/test/api/endpoint/1">Unenroll</a> ' +
|
||||
'<a id="link2" class="js-entitlement-action-unenroll" ' +
|
||||
' data-course-name="Test Course 2" ' +
|
||||
' data-course-number="test2" ' +
|
||||
' data-entitlement-api-endpoint="/test/api/endpoint/2">Unenroll</a> ' +
|
||||
'<div class="js-entitlement-unenrollment-modal"> ' +
|
||||
' <span class="js-entitlement-unenrollment-modal-header-text"></span> ' +
|
||||
' <span class="js-entitlement-unenrollment-modal-error-text"></span> ' +
|
||||
' <button class="js-entitlement-unenrollment-modal-submit">Unenroll</button> ' +
|
||||
'</div> ';
|
||||
|
||||
beforeEach(function() {
|
||||
setFixtures(modalHtml);
|
||||
view = initView();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
view.remove();
|
||||
});
|
||||
|
||||
describe('when an unenroll link is clicked', function() {
|
||||
it('should reset the modal and set the correct values for header/submit', function() {
|
||||
var $link1 = $('#link1'),
|
||||
$link2 = $('#link2'),
|
||||
$headerTxt = $('.js-entitlement-unenrollment-modal-header-text'),
|
||||
$errorTxt = $('.js-entitlement-unenrollment-modal-error-text'),
|
||||
$submitBtn = $('.js-entitlement-unenrollment-modal-submit');
|
||||
|
||||
$link1.trigger('click');
|
||||
expect($headerTxt.html().startsWith('Are you sure you want to unenroll from Test Course 1')).toBe(true);
|
||||
expect($submitBtn.data()).toEqual({entitlementApiEndpoint: '/test/api/endpoint/1'});
|
||||
expect($submitBtn.prop('disabled')).toBe(false);
|
||||
expect($errorTxt.html()).toEqual('');
|
||||
expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(false);
|
||||
|
||||
// Set an error so that we can see that the modal is reset properly when clicked again
|
||||
view.setError('This is an error');
|
||||
expect($errorTxt.html()).toEqual('This is an error');
|
||||
expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(true);
|
||||
expect($submitBtn.prop('disabled')).toBe(true);
|
||||
|
||||
$link2.trigger('click');
|
||||
expect($headerTxt.html().startsWith('Are you sure you want to unenroll from Test Course 2')).toBe(true);
|
||||
expect($submitBtn.data()).toEqual({entitlementApiEndpoint: '/test/api/endpoint/2'});
|
||||
expect($submitBtn.prop('disabled')).toBe(false);
|
||||
expect($errorTxt.html()).toEqual('');
|
||||
expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the unenroll submit button is clicked', function() {
|
||||
it('should send a DELETE request to the configured apiEndpoint', function() {
|
||||
var $submitBtn = $('.js-entitlement-unenrollment-modal-submit'),
|
||||
apiEndpoint = '/test/api/endpoint/1';
|
||||
|
||||
view.setSubmitData(apiEndpoint);
|
||||
|
||||
spyOn($, 'ajax').and.callFake(function(opts) {
|
||||
expect(opts.url).toEqual(apiEndpoint);
|
||||
expect(opts.method).toEqual('DELETE');
|
||||
expect(opts.complete).toBeTruthy();
|
||||
});
|
||||
|
||||
$submitBtn.trigger('click');
|
||||
expect($.ajax).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set an error and disable submit if the apiEndpoint has not been properly set', function() {
|
||||
var $errorTxt = $('.js-entitlement-unenrollment-modal-error-text'),
|
||||
$submitBtn = $('.js-entitlement-unenrollment-modal-submit');
|
||||
|
||||
expect($submitBtn.data()).toEqual({});
|
||||
expect($submitBtn.prop('disabled')).toBe(false);
|
||||
expect($errorTxt.html()).toEqual('');
|
||||
expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(false);
|
||||
|
||||
spyOn($, 'ajax');
|
||||
$submitBtn.trigger('click');
|
||||
expect($.ajax).not.toHaveBeenCalled();
|
||||
|
||||
expect($submitBtn.data()).toEqual({});
|
||||
expect($submitBtn.prop('disabled')).toBe(true);
|
||||
expect($errorTxt.html()).toEqual(view.genericErrorMsg);
|
||||
expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(true);
|
||||
});
|
||||
|
||||
describe('when the unenroll request is complete', function() {
|
||||
it('should redirect to the dashboard if the request was successful', function() {
|
||||
var $submitBtn = $('.js-entitlement-unenrollment-modal-submit'),
|
||||
apiEndpoint = '/test/api/endpoint/1';
|
||||
|
||||
view.setSubmitData(apiEndpoint);
|
||||
|
||||
spyOn($, 'ajax').and.callFake(function(opts) {
|
||||
expect(opts.url).toEqual(apiEndpoint);
|
||||
expect(opts.method).toEqual('DELETE');
|
||||
expect(opts.complete).toBeTruthy();
|
||||
|
||||
opts.complete({
|
||||
status: 204,
|
||||
responseJSON: {detail: 'success'}
|
||||
});
|
||||
});
|
||||
spyOn(view, 'redirectTo');
|
||||
|
||||
$submitBtn.trigger('click');
|
||||
expect($.ajax).toHaveBeenCalled();
|
||||
expect(view.redirectTo).toHaveBeenCalledWith(view.dashboardPath);
|
||||
});
|
||||
|
||||
it('should redirect to the login page if the request failed with an auth error', function() {
|
||||
var $submitBtn = $('.js-entitlement-unenrollment-modal-submit'),
|
||||
apiEndpoint = '/test/api/endpoint/1';
|
||||
|
||||
view.setSubmitData(apiEndpoint);
|
||||
|
||||
spyOn($, 'ajax').and.callFake(function(opts) {
|
||||
expect(opts.url).toEqual(apiEndpoint);
|
||||
expect(opts.method).toEqual('DELETE');
|
||||
expect(opts.complete).toBeTruthy();
|
||||
|
||||
opts.complete({
|
||||
status: 401,
|
||||
responseJSON: {detail: 'Authentication credentials were not provided.'}
|
||||
});
|
||||
});
|
||||
spyOn(view, 'redirectTo');
|
||||
|
||||
$submitBtn.trigger('click');
|
||||
expect($.ajax).toHaveBeenCalled();
|
||||
expect(view.redirectTo).toHaveBeenCalledWith(
|
||||
view.signInPath + '?next=' + encodeURIComponent(view.dashboardPath)
|
||||
);
|
||||
});
|
||||
|
||||
it('should set an error and disable submit if a non-auth error occurs', function() {
|
||||
var $errorTxt = $('.js-entitlement-unenrollment-modal-error-text'),
|
||||
$submitBtn = $('.js-entitlement-unenrollment-modal-submit'),
|
||||
apiEndpoint = '/test/api/endpoint/1';
|
||||
|
||||
view.setSubmitData(apiEndpoint);
|
||||
|
||||
spyOn($, 'ajax').and.callFake(function(opts) {
|
||||
expect(opts.url).toEqual(apiEndpoint);
|
||||
expect(opts.method).toEqual('DELETE');
|
||||
expect(opts.complete).toBeTruthy();
|
||||
|
||||
opts.complete({
|
||||
status: 400,
|
||||
responseJSON: {detail: 'Bad request.'}
|
||||
});
|
||||
});
|
||||
spyOn(view, 'redirectTo');
|
||||
|
||||
expect($submitBtn.prop('disabled')).toBe(false);
|
||||
expect($errorTxt.html()).toEqual('');
|
||||
expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(false);
|
||||
|
||||
$submitBtn.trigger('click');
|
||||
|
||||
expect($submitBtn.prop('disabled')).toBe(true);
|
||||
expect($errorTxt.html()).toEqual(view.genericErrorMsg);
|
||||
expect($errorTxt.hasClass('entitlement-unenrollment-modal-error-text-visible')).toBe(true);
|
||||
|
||||
expect($.ajax).toHaveBeenCalled();
|
||||
expect(view.redirectTo).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -29,7 +29,7 @@
|
||||
var o = options;
|
||||
|
||||
$(this).click(function(e) {
|
||||
$('.modal').hide();
|
||||
$('.modal, .js-modal').hide();
|
||||
|
||||
var modal_id = $(this).attr('href');
|
||||
|
||||
@@ -113,8 +113,12 @@
|
||||
|
||||
$(document).ready(function($) {
|
||||
$('a[rel*=leanModal]').each(function() {
|
||||
$(this).leanModal({top: 120, overlay: 1, closeButton: '.close-modal', position: 'absolute'});
|
||||
embed = $($(this).attr('href')).find('iframe');
|
||||
var $link = $(this),
|
||||
closeButton = $link.data('modalCloseButtonSelector') || '.close-modal',
|
||||
embed;
|
||||
|
||||
$link.leanModal({top: 120, overlay: 1, closeButton: closeButton, position: 'absolute'});
|
||||
embed = $($link.attr('href')).find('iframe');
|
||||
if (embed.length > 0 && embed.attr('src')) {
|
||||
var sep = (embed.attr('src').indexOf('?') > 0) ? '&' : '?';
|
||||
embed.data('src', embed.attr('src') + sep + 'autoplay=1&rel=0');
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
'js/header_factory',
|
||||
'js/learner_dashboard/course_entitlement_factory',
|
||||
'js/learner_dashboard/unenrollment_factory',
|
||||
'js/learner_dashboard/entitlement_unenrollment_factory',
|
||||
'js/learner_dashboard/program_details_factory',
|
||||
'js/learner_dashboard/program_list_factory',
|
||||
'js/student_account/logistration_factory',
|
||||
|
||||
@@ -764,6 +764,7 @@
|
||||
'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/entitlement_unenrollment_view_spec.js',
|
||||
'js/spec/learner_dashboard/course_card_view_spec.js',
|
||||
'js/spec/learner_dashboard/course_enroll_view_spec.js',
|
||||
'js/spec/learner_dashboard/course_entitlement_view_spec.js',
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
// shared - platform
|
||||
@import 'multicourse/home';
|
||||
@import 'multicourse/dashboard';
|
||||
@import 'multicourse/entitlement_dashboard';
|
||||
@import 'multicourse/account';
|
||||
@import 'multicourse/courses';
|
||||
@import 'multicourse/course_about';
|
||||
|
||||
@@ -1534,6 +1534,12 @@ a.fade-cover {
|
||||
|
||||
#unenroll-modal {
|
||||
margin-top: -60px;
|
||||
|
||||
.modal-form-error {
|
||||
background: tint($red, 95%);
|
||||
margin-left: $baseline;
|
||||
margin-right: $baseline;
|
||||
}
|
||||
}
|
||||
|
||||
.reasons_survey {
|
||||
|
||||
69
lms/static/sass/multicourse/_entitlement_dashboard.scss
Normal file
69
lms/static/sass/multicourse/_entitlement_dashboard.scss
Normal file
@@ -0,0 +1,69 @@
|
||||
.entitlement-actions-wrapper {
|
||||
@extend .wrapper-action-more;
|
||||
|
||||
.entitlement-action {
|
||||
@extend .action;
|
||||
}
|
||||
|
||||
.entitlement-action-more {
|
||||
@extend .action-more;
|
||||
}
|
||||
|
||||
.entitlement-actions-dropdown {
|
||||
@extend .actions-dropdown;
|
||||
|
||||
.entitlement-actions-dropdown-list {
|
||||
@extend .actions-dropdown-list;
|
||||
|
||||
.entitlement-actions-item {
|
||||
@extend .actions-item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.entitlement-unenrollment-modal {
|
||||
@extend .modal;
|
||||
|
||||
margin-top: -3*$baseline;
|
||||
|
||||
.entitlement-unenrollment-modal-inner-wrapper {
|
||||
@extend .inner-wrapper;
|
||||
|
||||
.entitlement-unenrollment-modal-close-btn {
|
||||
@extend .close-modal;
|
||||
}
|
||||
|
||||
.entitlement-unenrollment-modal-header {
|
||||
@extend .unenroll-header;
|
||||
}
|
||||
|
||||
.entitlement-unenrollment-modal-error-text {
|
||||
@extend .modal-form-error;
|
||||
|
||||
background: tint($red, 95%);
|
||||
margin-left: $baseline;
|
||||
margin-right: $baseline;
|
||||
}
|
||||
|
||||
.entitlement-unenrollment-modal-error-text-visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.entitlement-unenrollment-modal-submit-wrapper {
|
||||
margin-bottom: $baseline*0.6;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: $baseline ($baseline*2);
|
||||
|
||||
.entitlement-unenrollment-modal-submit {
|
||||
display: block;
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,12 @@ from student.models import CourseEnrollment
|
||||
isEdx: false
|
||||
});
|
||||
</%static:require_module>
|
||||
<%static:require_module module_name="js/learner_dashboard/entitlement_unenrollment_factory" class_name="EntitlementUnenrollmentFactory">
|
||||
EntitlementUnenrollmentFactory({
|
||||
dashboardPath: "${reverse('dashboard') | n, js_escaped_string}",
|
||||
signInPath: "${reverse('signin_user') | n, js_escaped_string}"
|
||||
});
|
||||
</%static:require_module>
|
||||
% if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'):
|
||||
<%static:require_module module_name="course_search/js/dashboard_search_factory" class_name="DashboardSearchFactory">
|
||||
DashboardSearchFactory();
|
||||
@@ -161,6 +167,7 @@ from student.models import CourseEnrollment
|
||||
session_id = enrollment.course_id
|
||||
show_courseware_link = (session_id in show_courseware_links_for)
|
||||
cert_status = cert_statuses.get(session_id)
|
||||
can_refund_entitlement = entitlement and entitlement.is_entitlement_refundable()
|
||||
can_unenroll = (not cert_status) or cert_status.get('can_unenroll') if not unfulfilled_entitlement else False
|
||||
credit_status = credit_statuses.get(session_id)
|
||||
show_email_settings = (session_id in show_email_settings_for)
|
||||
@@ -173,7 +180,7 @@ from student.models import CourseEnrollment
|
||||
show_consent_link = (session_id in consent_required_courses)
|
||||
course_overview = enrollment.course_overview
|
||||
%>
|
||||
<%include file='dashboard/_dashboard_course_listing.html' args='course_overview=course_overview, course_card_index=dashboard_index, enrollment=enrollment, is_unfulfilled_entitlement=is_unfulfilled_entitlement, is_fulfilled_entitlement=is_fulfilled_entitlement, entitlement=entitlement, entitlement_session=entitlement_session, entitlement_available_sessions=entitlement_available_sessions, entitlement_expiration_date=entitlement_expiration_date, entitlement_expired_at=entitlement_expired_at, show_courseware_link=show_courseware_link, cert_status=cert_status, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs, display_course_modes_on_dashboard=display_course_modes_on_dashboard, show_consent_link=show_consent_link, enterprise_customer_name=enterprise_customer_name' />
|
||||
<%include file='dashboard/_dashboard_course_listing.html' args='course_overview=course_overview, course_card_index=dashboard_index, enrollment=enrollment, is_unfulfilled_entitlement=is_unfulfilled_entitlement, is_fulfilled_entitlement=is_fulfilled_entitlement, entitlement=entitlement, entitlement_session=entitlement_session, entitlement_available_sessions=entitlement_available_sessions, entitlement_expiration_date=entitlement_expiration_date, entitlement_expired_at=entitlement_expired_at, show_courseware_link=show_courseware_link, cert_status=cert_status, can_refund_entitlement=can_refund_entitlement, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs, display_course_modes_on_dashboard=display_course_modes_on_dashboard, show_consent_link=show_consent_link, enterprise_customer_name=enterprise_customer_name' />
|
||||
% endfor
|
||||
|
||||
</ul>
|
||||
@@ -322,3 +329,5 @@ from student.models import CourseEnrollment
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%include file="dashboard/_dashboard_entitlement_unenrollment_modal.html"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%page args="course_overview, enrollment, entitlement, entitlement_session, course_card_index, is_unfulfilled_entitlement, is_fulfilled_entitlement, entitlement_available_sessions, entitlement_expiration_date, entitlement_expired_at, show_courseware_link, cert_status, can_unenroll, credit_status, show_email_settings, course_mode_info, is_paid_course, is_course_blocked, verification_status, course_requirements, dashboard_index, share_settings, related_programs, display_course_modes_on_dashboard, show_consent_link, enterprise_customer_name" expression_filter="h"/>
|
||||
<%page args="course_overview, enrollment, entitlement, entitlement_session, course_card_index, is_unfulfilled_entitlement, is_fulfilled_entitlement, entitlement_available_sessions, entitlement_expiration_date, entitlement_expired_at, show_courseware_link, cert_status, can_refund_entitlement, can_unenroll, credit_status, show_email_settings, course_mode_info, is_paid_course, is_course_blocked, verification_status, course_requirements, dashboard_index, share_settings, related_programs, display_course_modes_on_dashboard, show_consent_link, enterprise_customer_name" expression_filter="h"/>
|
||||
|
||||
<%!
|
||||
import urllib
|
||||
@@ -237,7 +237,12 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_
|
||||
% endif
|
||||
% endif
|
||||
|
||||
% if not entitlement:
|
||||
## Right now, the gear dropdown for entitlements only contains the 'unenroll' link, so we should hide the
|
||||
## gear altogether if the user is unable to unenroll/refund their entitlement. Later, when we add more options
|
||||
## to the gear dropdown, we can remove this check.
|
||||
% if entitlement and can_refund_entitlement:
|
||||
<%include file='_dashboard_entitlement_actions.html' args='course_overview=course_overview,entitlement=entitlement,dashboard_index=dashboard_index, can_refund_entitlement=can_refund_entitlement'/>
|
||||
% elif not entitlement:
|
||||
<div class="wrapper-action-more" data-course-key="${enrollment.course_id}">
|
||||
<button type="button" class="action action-more" id="actions-dropdown-link-${dashboard_index}" aria-haspopup="true" aria-expanded="false" aria-controls="actions-dropdown-${dashboard_index}" data-course-number="${course_overview.number}" data-course-name="${course_overview.display_name_with_default}" data-dashboard-index="${dashboard_index}">
|
||||
<span class="sr">${_('Course options for')}</span>
|
||||
@@ -342,7 +347,11 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_
|
||||
<%include file="_dashboard_credit_info.html" args="credit_status=credit_status"/>
|
||||
% endif
|
||||
|
||||
% if is_course_blocked:
|
||||
% if is_course_blocked and entitlement:
|
||||
<p id="block-course-msg" class="course-block">
|
||||
${_("You can no longer access this course because payment has not yet been received. You can contact the account holder to request payment, or you can unenroll from this course")}
|
||||
</p>
|
||||
% elif is_course_blocked:
|
||||
<p id="block-course-msg" class="course-block">
|
||||
${Text(_("You can no longer access this course because payment has not yet been received. "
|
||||
"You can {contact_link_start}contact the account holder{contact_link_end} "
|
||||
|
||||
48
lms/templates/dashboard/_dashboard_entitlement_actions.html
Normal file
48
lms/templates/dashboard/_dashboard_entitlement_actions.html
Normal file
@@ -0,0 +1,48 @@
|
||||
<%page args="course_overview, entitlement, dashboard_index, can_refund_entitlement" expression_filter="h"/>
|
||||
|
||||
<%!
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.urlresolvers import reverse
|
||||
%>
|
||||
|
||||
<%
|
||||
dropdown_id = "entitlement-actions-dropdown-{}".format(dashboard_index)
|
||||
dropdown_btn_id = "entitlement-actions-dropdown-btn-{}".format(dashboard_index)
|
||||
%>
|
||||
|
||||
<div class="entitlement-actions-wrapper">
|
||||
## id, data-dropdown-selector, and data-dropdown-button-selector must be defined for compatibility with
|
||||
## lms/static/js/dashboard/dropdown.js
|
||||
<button id="${dropdown_btn_id}"
|
||||
class="entitlement-action entitlement-action-more js-entitlement-action-more"
|
||||
type="button"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
aria-controls="${dropdown_id}"
|
||||
data-dropdown-selector="#${dropdown_id}"
|
||||
data-dropdown-button-selector="#${dropdown_btn_id}">
|
||||
<span class="sr">${_('Course options for {courseName}').format(courseName=course_overview.display_name_with_default)}</span>
|
||||
<span class="fa fa-cog" aria-hidden="true"></span>
|
||||
</button>
|
||||
|
||||
<div id="${dropdown_id}" class="entitlement-actions-dropdown" tabindex="-1">
|
||||
<ul class="entitlement-actions-dropdown-list" aria-label="${_('Available Actions')}" role="menu">
|
||||
% if can_refund_entitlement:
|
||||
<li class="entitlement-actions-item" role="menuitem">
|
||||
## href, id, rel, and data-model-close-button-selector must be defined for compatibility with lms/static/js/leanModal.js
|
||||
## data-dropdown-selector and data-dropdown-button-selector must be defined for compatibility with lms/static/js/dashboard/dropdown.js
|
||||
## class="js-entitlement-action-unenroll" must be set for compatibility with lms/static/js/learner_dashboard/views/entitlement_unenrollment_view.js
|
||||
<a href="#entitlement-unenrollment-modal" id="entitlement-action-unenroll-${dashboard_index}" class="entitlement-action js-entitlement-action-unenroll" rel="leanModal"
|
||||
data-modal-close-button-selector=".js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-close-btn"
|
||||
data-dropdown-selector="#${dropdown_id}"
|
||||
data-dropdown-button-selector="#${dropdown_btn_id}"
|
||||
data-course-name="${course_overview.display_name_with_default}"
|
||||
data-course-number="${course_overview.number}"
|
||||
data-entitlement-api-endpoint="${reverse('entitlements_api:v1:enrollments', args=[unicode(entitlement.uuid)]) + '?is_refund=true'}">
|
||||
${_('Unenroll')}
|
||||
</a>
|
||||
</li>
|
||||
% endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,32 @@
|
||||
<%page expression_filter="h"/>
|
||||
|
||||
<%!
|
||||
from django.utils.translation import ugettext as _
|
||||
%>
|
||||
|
||||
<div id="entitlement-unenrollment-modal" class="entitlement-unenrollment-modal js-entitlement-unenrollment-modal js-modal" aria-hidden="true">
|
||||
<div class="entitlement-unenrollment-modal-inner-wrapper" role="dialog" aria-modal="true" aria-labelledby="entitlement-unenrollment-modal-title">
|
||||
<button class="entitlement-unenrollment-modal-close-btn js-entitlement-unenrollment-modal-close-btn">
|
||||
<span class="icon fa fa-remove" aria-hidden="true"></span>
|
||||
<span class="sr">
|
||||
## Translators: this is a control to allow users to exit out of this modal interface (a menu or piece of UI that takes the full focus of the screen)
|
||||
${_("Close")}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<header class="entitlement-unenrollment-modal-header">
|
||||
<h2 id="entitlement-unenrollment-modal-title">
|
||||
<span class='js-entitlement-unenrollment-modal-header-text'></span>
|
||||
<span class="sr">,
|
||||
## Translators: this text gives status on if the modal interface (a menu or piece of UI that takes the full focus of the screen) is open or not
|
||||
${_("window open")}
|
||||
</span>
|
||||
</h2>
|
||||
<hr/>
|
||||
</header>
|
||||
<div class="entitlement-unenrollment-modal-error-text js-entitlement-unenrollment-modal-error-text"></div>
|
||||
<div class="entitlement-unenrollment-modal-submit-wrapper">
|
||||
<button class="entitlement-unenrollment-modal-submit js-entitlement-unenrollment-modal-submit">${_("Unenroll")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,6 +62,12 @@ from student.models import CourseEnrollment
|
||||
isEdx: true
|
||||
});
|
||||
</%static:require_module>
|
||||
<%static:require_module module_name="js/learner_dashboard/entitlement_unenrollment_factory" class_name="EntitlementUnenrollmentFactory">
|
||||
EntitlementUnenrollmentFactory({
|
||||
dashboardPath: "${reverse('dashboard') | n, js_escaped_string}",
|
||||
signInPath: "${reverse('signin_user') | n, js_escaped_string}"
|
||||
});
|
||||
</%static:require_module>
|
||||
% if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'):
|
||||
<%static:require_module module_name="course_search/js/dashboard_search_factory" class_name="DashboardSearchFactory">
|
||||
DashboardSearchFactory();
|
||||
@@ -157,6 +163,7 @@ from student.models import CourseEnrollment
|
||||
session_id = enrollment.course_id
|
||||
show_courseware_link = (session_id in show_courseware_links_for)
|
||||
cert_status = cert_statuses.get(session_id)
|
||||
can_refund_entitlement = entitlement and entitlement.is_entitlement_refundable()
|
||||
can_unenroll = (not cert_status) or cert_status.get('can_unenroll') if not unfulfilled_entitlement else False
|
||||
credit_status = credit_statuses.get(session_id)
|
||||
show_email_settings = (session_id in show_email_settings_for)
|
||||
@@ -169,7 +176,7 @@ from student.models import CourseEnrollment
|
||||
show_consent_link = (session_id in consent_required_courses)
|
||||
course_overview = enrollment.course_overview
|
||||
%>
|
||||
<%include file='dashboard/_dashboard_course_listing.html' args='course_overview=course_overview, course_card_index=dashboard_index, enrollment=enrollment, is_unfulfilled_entitlement=is_unfulfilled_entitlement, is_fulfilled_entitlement=is_fulfilled_entitlement, entitlement=entitlement, entitlement_session=entitlement_session, entitlement_available_sessions=entitlement_available_sessions, entitlement_expiration_date=entitlement_expiration_date, entitlement_expired_at=entitlement_expired_at, show_courseware_link=show_courseware_link, cert_status=cert_status, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs, display_course_modes_on_dashboard=display_course_modes_on_dashboard, show_consent_link=show_consent_link, enterprise_customer_name=enterprise_customer_name' />
|
||||
<%include file='dashboard/_dashboard_course_listing.html' args='course_overview=course_overview, course_card_index=dashboard_index, enrollment=enrollment, is_unfulfilled_entitlement=is_unfulfilled_entitlement, is_fulfilled_entitlement=is_fulfilled_entitlement, entitlement=entitlement, entitlement_session=entitlement_session, entitlement_available_sessions=entitlement_available_sessions, entitlement_expiration_date=entitlement_expiration_date, entitlement_expired_at=entitlement_expired_at, show_courseware_link=show_courseware_link, cert_status=cert_status, can_refund_entitlement=can_refund_entitlement, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs, display_course_modes_on_dashboard=display_course_modes_on_dashboard, show_consent_link=show_consent_link, enterprise_customer_name=enterprise_customer_name' />
|
||||
% endfor
|
||||
</ul>
|
||||
% else:
|
||||
@@ -332,3 +339,5 @@ from student.models import CourseEnrollment
|
||||
<%include file='dashboard/_reason_survey.html' />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%include file="dashboard/_dashboard_entitlement_unenrollment_modal.html"/>
|
||||
|
||||
Reference in New Issue
Block a user