Commit a6cb0fef by Anthony Mangano

add dashboard entitlement actions

parent 6388391d
......@@ -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));
(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);
(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() {
......
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 {
......
.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"/>
<%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} "
......
<%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>
<%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"/>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment