Convert learner_dashboard to es2015

This commit is contained in:
Michael Terry
2018-01-18 13:07:44 -05:00
committed by Michael Terry
parent acf7de7c02
commit c9318c3e51
74 changed files with 4035 additions and 4307 deletions

View File

@@ -1,35 +1,23 @@
(function(define) {
'use strict';
define(['backbone',
'jquery',
'underscore',
'gettext',
'text!../../../templates/learner_dashboard/certificate_list.underscore'
],
function(
Backbone,
$,
_,
gettext,
certificateTpl
) {
return Backbone.View.extend({
tpl: _.template(certificateTpl),
import _ from 'underscore';
import Backbone from 'backbone';
initialize: function(options) {
this.title = options.title || false;
this.render();
},
import certificateTpl from '../../../templates/learner_dashboard/certificate_list.underscore';
render: function() {
var data = {
title: this.title,
certificateList: this.collection.toJSON()
};
class CertificateListView extends Backbone.View {
initialize(options) {
this.tpl = _.template(certificateTpl);
this.title = options.title || false;
this.render();
}
this.$el.html(this.tpl(data));
}
});
}
);
}).call(this, define || RequireJS.define);
render() {
const data = {
title: this.title,
certificateList: this.collection.toJSON(),
};
this.$el.html(this.tpl(data));
}
}
export default CertificateListView;

View File

@@ -1,38 +1,24 @@
(function(define) {
'use strict';
define(['backbone',
'jquery',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'text!../../../templates/learner_dashboard/certificate_status.underscore',
'text!../../../templates/learner_dashboard/certificate_icon.underscore'
],
function(
Backbone,
$,
_,
gettext,
HtmlUtils,
certificateStatusTpl,
certificateIconTpl
) {
return Backbone.View.extend({
statusTpl: HtmlUtils.template(certificateStatusTpl),
iconTpl: HtmlUtils.template(certificateIconTpl),
import Backbone from 'backbone';
initialize: function(options) {
this.$el = options.$el;
this.render();
},
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
render: function() {
var data = this.model.toJSON();
import certificateStatusTpl from '../../../templates/learner_dashboard/certificate_status.underscore';
import certificateIconTpl from '../../../templates/learner_dashboard/certificate_icon.underscore';
data = $.extend(data, {certificateSvg: this.iconTpl()});
HtmlUtils.setHtml(this.$el, this.statusTpl(data));
}
});
}
);
}).call(this, define || RequireJS.define);
class CertificateStatusView extends Backbone.View {
initialize(options) {
this.statusTpl = HtmlUtils.template(certificateStatusTpl);
this.iconTpl = HtmlUtils.template(certificateIconTpl);
this.$el = options.$el;
this.render();
}
render() {
let data = this.model.toJSON();
data = $.extend(data, { certificateSvg: this.iconTpl() });
HtmlUtils.setHtml(this.$el, this.statusTpl(data));
}
}
export default CertificateStatusView;

View File

@@ -1,68 +1,53 @@
(function(define) {
'use strict';
import Backbone from 'backbone';
define(['backbone',
'jquery',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/string-utils',
'edx-ui-toolkit/js/utils/html-utils',
'text!../../../templates/learner_dashboard/empty_programs_list.underscore'
],
function(Backbone,
$,
_,
gettext,
StringUtils,
HtmlUtils,
emptyProgramsListTpl) {
return Backbone.View.extend({
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
import StringUtils from 'edx-ui-toolkit/js/utils/string-utils';
initialize: function(data) {
this.childView = data.childView;
this.context = data.context;
this.titleContext = data.titleContext;
},
import emptyProgramsListTpl from '../../../templates/learner_dashboard/empty_programs_list.underscore';
render: function() {
var childList;
class CollectionListView extends Backbone.View {
initialize(data) {
this.childView = data.childView;
this.context = data.context;
this.titleContext = data.titleContext;
}
if (!this.collection.length) {
if (this.context.marketingUrl) {
// Only show the advertising panel if the link is passed in
HtmlUtils.setHtml(this.$el, HtmlUtils.template(emptyProgramsListTpl)(this.context));
}
} else {
childList = [];
render() {
if (!this.collection.length) {
if (this.context.marketingUrl) {
// Only show the advertising panel if the link is passed in
HtmlUtils.setHtml(this.$el, HtmlUtils.template(emptyProgramsListTpl)(this.context));
}
} else {
const childList = [];
this.collection.each(function(model) {
var child = new this.childView({
model: model,
context: this.context
});
childList.push(child.el);
}, this);
this.collection.each((model) => {
const child = new this.childView({ // eslint-disable-line new-cap
model,
context: this.context,
});
childList.push(child.el);
}, this);
if (this.titleContext) {
this.$el.before(HtmlUtils.ensureHtml(this.getTitleHtml()).toString());
}
if (this.titleContext) {
this.$el.before(HtmlUtils.ensureHtml(this.getTitleHtml()).toString());
}
this.$el.html(childList);
}
},
this.$el.html(childList);
}
}
getTitleHtml: function() {
var titleHtml = HtmlUtils.joinHtml(
HtmlUtils.HTML('<'),
this.titleContext.el,
HtmlUtils.HTML(' class="sr-only collection-title">'),
StringUtils.interpolate(this.titleContext.title),
HtmlUtils.HTML('</'),
this.titleContext.el,
HtmlUtils.HTML('>'));
return titleHtml;
}
});
}
);
}).call(this, define || RequireJS.define);
getTitleHtml() {
const titleHtml = HtmlUtils.joinHtml(
HtmlUtils.HTML('<'),
this.titleContext.el,
HtmlUtils.HTML(' class="sr-only collection-title">'),
StringUtils.interpolate(this.titleContext.title),
HtmlUtils.HTML('</'),
this.titleContext.el,
HtmlUtils.HTML('>'));
return titleHtml;
}
}
export default CollectionListView;

View File

@@ -1,130 +1,116 @@
(function(define) {
'use strict';
import Backbone from 'backbone';
define(['backbone',
'jquery',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'js/learner_dashboard/models/course_enroll_model',
'js/learner_dashboard/views/upgrade_message_view',
'js/learner_dashboard/views/certificate_status_view',
'js/learner_dashboard/views/expired_notification_view',
'js/learner_dashboard/views/course_enroll_view',
'js/learner_dashboard/views/course_entitlement_view',
'text!../../../templates/learner_dashboard/course_card.underscore'
],
function(
Backbone,
$,
_,
gettext,
HtmlUtils,
EnrollModel,
UpgradeMessageView,
CertificateStatusView,
ExpiredNotificationView,
CourseEnrollView,
EntitlementView,
pageTpl
) {
return Backbone.View.extend({
className: 'program-course-card',
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
tpl: HtmlUtils.template(pageTpl),
import EnrollModel from '../models/course_enroll_model';
import UpgradeMessageView from './upgrade_message_view';
import CertificateStatusView from './certificate_status_view';
import ExpiredNotificationView from './expired_notification_view';
import CourseEnrollView from './course_enroll_view';
import EntitlementView from './course_entitlement_view';
initialize: function(options) {
this.enrollModel = new EnrollModel();
if (options.context) {
this.urlModel = new Backbone.Model(options.context.urls);
this.enrollModel.urlRoot = this.urlModel.get('commerce_api_url');
}
this.context = options.context || {};
this.grade = this.context.courseData.grades[this.model.get('course_run_key')];
this.grade = this.grade * 100;
this.collectionCourseStatus = this.context.collectionCourseStatus || '';
this.entitlement = this.model.get('user_entitlement');
import pageTpl from '../../../templates/learner_dashboard/course_card.underscore';
this.render();
this.listenTo(this.model, 'change', this.render);
},
class CourseCardView extends Backbone.View {
constructor(options) {
const defaults = {
className: 'program-course-card',
};
super(Object.assign({}, defaults, options));
}
render: function() {
var data = $.extend(this.model.toJSON(), {
enrolled: this.context.enrolled || ''
});
HtmlUtils.setHtml(this.$el, this.tpl(data));
this.postRender();
},
initialize(options) {
this.tpl = HtmlUtils.template(pageTpl);
this.enrollModel = new EnrollModel();
if (options.context) {
this.urlModel = new Backbone.Model(options.context.urls);
this.enrollModel.urlRoot = this.urlModel.get('commerce_api_url');
}
this.context = options.context || {};
this.grade = this.context.courseData.grades[this.model.get('course_run_key')];
this.grade = this.grade * 100;
this.collectionCourseStatus = this.context.collectionCourseStatus || '';
this.entitlement = this.model.get('user_entitlement');
postRender: function() {
var $upgradeMessage = this.$('.upgrade-message'),
$certStatus = this.$('.certificate-status'),
$expiredNotification = this.$('.expired-notification'),
expired = this.model.get('expired'),
courseUUID = this.model.get('uuid'),
containerSelector = '#course-' + courseUUID;
this.render();
this.listenTo(this.model, 'change', this.render);
}
this.enrollView = new CourseEnrollView({
$parentEl: this.$('.course-actions'),
model: this.model,
grade: this.grade,
collectionCourseStatus: this.collectionCourseStatus,
urlModel: this.urlModel,
enrollModel: this.enrollModel
});
render() {
const data = $.extend(this.model.toJSON(), {
enrolled: this.context.enrolled || '',
});
HtmlUtils.setHtml(this.$el, this.tpl(data));
this.postRender();
}
if (this.entitlement) {
this.sessionSelectionView = new EntitlementView({
el: this.$(containerSelector + ' .course-entitlement-selection-container'),
$parentEl: this.$el,
courseCardModel: this.model,
enrollModel: this.enrollModel,
triggerOpenBtn: '.course-details .change-session',
courseCardMessages: '',
courseImageLink: '',
courseTitleLink: containerSelector + ' .course-details .course-title',
dateDisplayField: containerSelector + ' .course-details .course-text',
enterCourseBtn: containerSelector + ' .view-course-button',
availableSessions: JSON.stringify(this.model.get('course_runs')),
entitlementUUID: this.entitlement.uuid,
currentSessionId: this.model.isEnrolledInSession() ?
postRender() {
const $upgradeMessage = this.$('.upgrade-message');
const $certStatus = this.$('.certificate-status');
const $expiredNotification = this.$('.expired-notification');
const expired = this.model.get('expired');
const courseUUID = this.model.get('uuid');
const containerSelector = `#course-${courseUUID}`;
this.enrollView = new CourseEnrollView({
$parentEl: this.$('.course-actions'),
model: this.model,
grade: this.grade,
collectionCourseStatus: this.collectionCourseStatus,
urlModel: this.urlModel,
enrollModel: this.enrollModel,
});
if (this.entitlement) {
this.sessionSelectionView = new EntitlementView({
el: this.$(`${containerSelector} .course-entitlement-selection-container`),
$parentEl: this.$el,
courseCardModel: this.model,
enrollModel: this.enrollModel,
triggerOpenBtn: '.course-details .change-session',
courseCardMessages: '',
courseImageLink: '',
courseTitleLink: `${containerSelector} .course-details .course-title`,
dateDisplayField: `${containerSelector} .course-details .course-text`,
enterCourseBtn: `${containerSelector} .view-course-button`,
availableSessions: JSON.stringify(this.model.get('course_runs')),
entitlementUUID: this.entitlement.uuid,
currentSessionId: this.model.isEnrolledInSession() ?
this.model.get('course_run_key') : null,
enrollUrl: this.model.get('enroll_url'),
courseHomeUrl: this.model.get('course_url'),
expiredAt: this.entitlement.expired_at,
daysUntilExpiration: this.entitlement.days_until_expiration
});
}
enrollUrl: this.model.get('enroll_url'),
courseHomeUrl: this.model.get('course_url'),
expiredAt: this.entitlement.expired_at,
daysUntilExpiration: this.entitlement.days_until_expiration,
});
}
if (this.model.get('upgrade_url') && !(expired === true)) {
this.upgradeMessage = new UpgradeMessageView({
$el: $upgradeMessage,
model: this.model
});
if (this.model.get('upgrade_url') && !(expired === true)) {
this.upgradeMessage = new UpgradeMessageView({
$el: $upgradeMessage,
model: this.model,
});
$certStatus.remove();
} else if (this.model.get('certificate_url') && !(expired === true)) {
this.certificateStatus = new CertificateStatusView({
$el: $certStatus,
model: this.model
});
$certStatus.remove();
} else if (this.model.get('certificate_url') && !(expired === true)) {
this.certificateStatus = new CertificateStatusView({
$el: $certStatus,
model: this.model,
});
$upgradeMessage.remove();
} else {
// Styles are applied to these elements which will be visible if they're empty.
$upgradeMessage.remove();
$certStatus.remove();
}
$upgradeMessage.remove();
} else {
// Styles are applied to these elements which will be visible if they're empty.
$upgradeMessage.remove();
$certStatus.remove();
}
if (expired) {
this.expiredNotification = new ExpiredNotificationView({
$el: $expiredNotification,
model: this.model
});
}
}
});
}
);
}).call(this, define || RequireJS.define);
if (expired) {
this.expiredNotification = new ExpiredNotificationView({
$el: $expiredNotification,
model: this.model,
});
}
}
}
export default CourseCardView;

View File

@@ -1,120 +1,111 @@
(function(define) {
'use strict';
import _ from 'underscore';
import Backbone from 'backbone';
define(['backbone',
'jquery',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'text!../../../templates/learner_dashboard/course_enroll.underscore'
],
function(
Backbone,
$,
_,
gettext,
HtmlUtils,
pageTpl
) {
return Backbone.View.extend({
className: 'course-enroll-view',
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
tpl: HtmlUtils.template(pageTpl),
import pageTpl from '../../../templates/learner_dashboard/course_enroll.underscore';
events: {
'click .enroll-button': 'handleEnroll',
'change .run-select': 'updateEnrollUrl'
},
class CourseEnrollView extends Backbone.View {
constructor(options) {
const defaults = {
className: 'course-enroll-view',
events: {
'click .enroll-button': 'handleEnroll',
'change .run-select': 'updateEnrollUrl',
},
};
super(Object.assign({}, defaults, options));
}
initialize: function(options) {
this.$parentEl = options.$parentEl;
this.enrollModel = options.enrollModel;
this.urlModel = options.urlModel;
this.grade = options.grade;
this.collectionCourseStatus = options.collectionCourseStatus;
this.render();
},
initialize(options) {
this.tpl = HtmlUtils.template(pageTpl);
this.$parentEl = options.$parentEl;
this.enrollModel = options.enrollModel;
this.urlModel = options.urlModel;
this.grade = options.grade;
this.collectionCourseStatus = options.collectionCourseStatus;
this.render();
}
render: function() {
var filledTemplate,
context = this.model.toJSON();
if (this.$parentEl && this.enrollModel) {
context.grade = this.grade;
context.collectionCourseStatus = this.collectionCourseStatus;
filledTemplate = this.tpl(context);
HtmlUtils.setHtml(this.$el, filledTemplate);
HtmlUtils.setHtml(this.$parentEl, HtmlUtils.HTML(this.$el));
}
this.postRender();
},
render() {
let filledTemplate;
const context = this.model.toJSON();
if (this.$parentEl && this.enrollModel) {
context.grade = this.grade;
context.collectionCourseStatus = this.collectionCourseStatus;
filledTemplate = this.tpl(context);
HtmlUtils.setHtml(this.$el, filledTemplate);
HtmlUtils.setHtml(this.$parentEl, HtmlUtils.HTML(this.$el));
}
this.postRender();
}
postRender: function() {
if (this.urlModel) {
this.trackSelectionUrl = this.urlModel.get('track_selection_url');
}
},
postRender() {
if (this.urlModel) {
this.trackSelectionUrl = this.urlModel.get('track_selection_url');
}
}
handleEnroll: function() {
// Enrollment click event handled here
if (this.model.get('is_mobile_only') !== true) {
var courseRunKey = $('.run-select').val() || this.model.get('course_run_key'); // eslint-disable-line vars-on-top, max-len
this.model.updateCourseRun(courseRunKey);
if (this.model.get('is_enrolled')) {
// Create the enrollment.
this.enrollModel.save({
course_id: courseRunKey
}, {
success: _.bind(this.enrollSuccess, this),
error: _.bind(this.enrollError, this)
});
}
}
},
handleEnroll() {
// Enrollment click event handled here
if (this.model.get('is_mobile_only') !== true) {
const courseRunKey = $('.run-select').val() || this.model.get('course_run_key');
this.model.updateCourseRun(courseRunKey);
if (this.model.get('is_enrolled')) {
// Create the enrollment.
this.enrollModel.save({
course_id: courseRunKey,
}, {
success: _.bind(this.enrollSuccess, this),
error: _.bind(this.enrollError, this),
});
}
}
}
enrollSuccess: function() {
var courseRunKey = this.model.get('course_run_key');
window.analytics.track('edx.bi.user.program-details.enrollment');
if (this.trackSelectionUrl) {
// Go to track selection page
this.redirect(this.trackSelectionUrl + courseRunKey);
} else {
this.model.set({
is_enrolled: true
});
}
},
enrollSuccess() {
const courseRunKey = this.model.get('course_run_key');
window.analytics.track('edx.bi.user.program-details.enrollment');
if (this.trackSelectionUrl) {
// Go to track selection page
CourseEnrollView.redirect(this.trackSelectionUrl + courseRunKey);
} else {
this.model.set({
is_enrolled: true,
});
}
}
enrollError: function(model, response) {
if (response.status === 403 && response.responseJSON.user_message_url) {
/**
* Check if we've been blocked from the course
* because of country access rules.
* If so, redirect to a page explaining to the user
* why they were blocked.
*/
this.redirect(response.responseJSON.user_message_url);
} else if (this.trackSelectionUrl) {
/**
* Otherwise, go to the track selection page as usual.
* This can occur, for example, when a course does not
* have a free enrollment mode, so we can't auto-enroll.
*/
this.redirect(this.trackSelectionUrl + this.model.get('course_run_key'));
}
},
enrollError(model, response) {
if (response.status === 403 && response.responseJSON.user_message_url) {
/**
* Check if we've been blocked from the course
* because of country access rules.
* If so, redirect to a page explaining to the user
* why they were blocked.
*/
CourseEnrollView.redirect(response.responseJSON.user_message_url);
} else if (this.trackSelectionUrl) {
/**
* Otherwise, go to the track selection page as usual.
* This can occur, for example, when a course does not
* have a free enrollment mode, so we can't auto-enroll.
*/
CourseEnrollView.redirect(this.trackSelectionUrl + this.model.get('course_run_key'));
}
}
updateEnrollUrl: function() {
if (this.model.get('is_mobile_only') === true) {
var courseRunKey = $('.run-select').val(), // eslint-disable-line vars-on-top
href = 'edxapp://enroll?course_id=' + courseRunKey + '&email_opt_in=true';
$('.enroll-course-button').attr('href', href);
}
},
updateEnrollUrl() {
if (this.model.get('is_mobile_only') === true) {
const courseRunKey = $('.run-select').val();
const href = `edxapp://enroll?course_id=${courseRunKey}&email_opt_in=true`;
$('.enroll-course-button').attr('href', href);
}
}
redirect: function(url) {
window.location.href = url;
}
});
}
);
}).call(this, define || RequireJS.define);
static redirect(url) {
window.location.href = url;
}
}
export default CourseEnrollView;

View File

@@ -1,419 +1,409 @@
(function(define) {
'use strict';
/* globals gettext */
define(['backbone',
'jquery',
'underscore',
'gettext',
'moment',
'edx-ui-toolkit/js/utils/html-utils',
'js/learner_dashboard/models/course_entitlement_model',
'js/learner_dashboard/models/course_card_model',
'text!../../../templates/learner_dashboard/course_entitlement.underscore',
'text!../../../templates/learner_dashboard/verification_popover.underscore',
'bootstrap'
],
function(
Backbone,
$,
_,
gettext,
moment,
HtmlUtils,
EntitlementModel,
CourseCardModel,
pageTpl,
verificationPopoverTpl
) {
return Backbone.View.extend({
tpl: HtmlUtils.template(pageTpl),
verificationTpl: HtmlUtils.template(verificationPopoverTpl),
import 'bootstrap';
events: {
'change .session-select': 'updateEnrollBtn',
'click .enroll-btn': 'handleEnrollChange',
'keydown .final-confirmation-btn': 'handleVerificationPopoverA11y',
'click .popover-dismiss': 'hideDialog'
},
import _ from 'underscore';
import Backbone from 'backbone';
import moment from 'moment';
initialize: function(options) {
// Set up models and reload view on change
this.courseCardModel = options.courseCardModel || new CourseCardModel();
this.enrollModel = options.enrollModel;
this.entitlementModel = new EntitlementModel({
availableSessions: this.formatDates(JSON.parse(options.availableSessions)),
entitlementUUID: options.entitlementUUID,
currentSessionId: options.currentSessionId,
expiredAt: options.expiredAt,
expiresAtDate: this.courseCardModel.formatDate(
new moment().utc().add(options.daysUntilExpiration, 'days')
),
courseName: options.courseName
});
this.listenTo(this.entitlementModel, 'change', this.render);
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
// Grab URLs that handle changing of enrollment and entering a newly selected session.
this.enrollUrl = options.enrollUrl;
this.courseHomeUrl = options.courseHomeUrl;
import EntitlementModel from '../models/course_entitlement_model';
import CourseCardModel from '../models/course_card_model';
// Grab elements from the parent card that work with this view
this.$parentEl = options.$parentEl; // Containing course card (must be a backbone view root el)
this.$enterCourseBtn = $(options.enterCourseBtn); // Button link to course home page
this.$courseCardMessages = $(options.courseCardMessages); // Additional session messages
this.$courseTitleLink = $(options.courseTitleLink); // Title link to course home page
this.$courseImageLink = $(options.courseImageLink); // Image link to course home page
this.$policyMsg = $(options.policyMsg); // Message for policy information
import pageTpl from '../../../templates/learner_dashboard/course_entitlement.underscore';
import verificationPopoverTpl from '../../../templates/learner_dashboard/verification_popover.underscore';
// Bind action elements with associated events to objects outside this view
this.$dateDisplayField = this.$parentEl ? this.$parentEl.find(options.dateDisplayField) :
$(options.dateDisplayField); // Displays current session dates
this.$triggerOpenBtn = this.$parentEl ? this.$parentEl.find(options.triggerOpenBtn) :
$(options.triggerOpenBtn); // Opens/closes session selection view
this.$triggerOpenBtn.on('click', this.toggleSessionSelectionPanel.bind(this));
class CourseEntitlementView extends Backbone.View {
constructor(options) {
const defaults = {
events: {
'change .session-select': 'updateEnrollBtn',
'click .enroll-btn': 'handleEnrollChange',
'keydown .final-confirmation-btn': 'handleVerificationPopoverA11y',
'click .popover-dismiss': 'hideDialog',
},
};
super(Object.assign({}, defaults, options));
}
this.render(options);
this.postRender();
},
initialize(options) {
this.tpl = HtmlUtils.template(pageTpl);
this.verificationTpl = HtmlUtils.template(verificationPopoverTpl);
render: function() {
HtmlUtils.setHtml(this.$el, this.tpl(this.entitlementModel.toJSON()));
this.delegateEvents();
this.updateEnrollBtn();
return this;
},
// Set up models and reload view on change
this.courseCardModel = options.courseCardModel || new CourseCardModel();
this.enrollModel = options.enrollModel;
this.entitlementModel = new EntitlementModel({
availableSessions: this.formatDates(JSON.parse(options.availableSessions)),
entitlementUUID: options.entitlementUUID,
currentSessionId: options.currentSessionId,
expiredAt: options.expiredAt,
expiresAtDate: CourseCardModel.formatDate(
new moment().utc().add(options.daysUntilExpiration, 'days'), // eslint-disable-line new-cap
),
courseName: options.courseName,
});
this.listenTo(this.entitlementModel, 'change', this.render);
postRender: function() {
// Close any visible popovers on click-away
$(document).on('click', function(e) {
if (this.$('.popover:visible').length &&
!($(e.target).closest('.enroll-btn-initial, .popover').length)) {
this.hideDialog(this.$('.enroll-btn-initial'));
}
}.bind(this));
// Grab URLs that handle changing of enrollment and entering a newly selected session.
this.enrollUrl = options.enrollUrl;
this.courseHomeUrl = options.courseHomeUrl;
// Initialize focus to cancel button on popover load
$(document).on('shown.bs.popover', function() {
this.$('.final-confirmation-btn:first').focus();
}.bind(this));
},
// Grab elements from the parent card that work with this view
this.$parentEl = options.$parentEl; // Containing course card (must be a backbone view root el)
this.$enterCourseBtn = $(options.enterCourseBtn); // Button link to course home page
this.$courseCardMessages = $(options.courseCardMessages); // Additional session messages
this.$courseTitleLink = $(options.courseTitleLink); // Title link to course home page
this.$courseImageLink = $(options.courseImageLink); // Image link to course home page
this.$policyMsg = $(options.policyMsg); // Message for policy information
handleEnrollChange: function() {
/*
Handles enrolling in a course, unenrolling in a session and changing session.
The new session id is stored as a data attribute on the option in the session-select element.
*/
var isLeavingSession;
// Bind action elements with associated events to objects outside this view
this.$dateDisplayField = this.$parentEl ? this.$parentEl.find(options.dateDisplayField) :
$(options.dateDisplayField); // Displays current session dates
this.$triggerOpenBtn = this.$parentEl ? this.$parentEl.find(options.triggerOpenBtn) :
$(options.triggerOpenBtn); // Opens/closes session selection view
this.$triggerOpenBtn.on('click', this.toggleSessionSelectionPanel.bind(this));
// Do not allow for enrollment when button is disabled
if (this.$('.enroll-btn-initial').hasClass('disabled')) return;
this.render(options);
this.postRender();
}
// Grab the id for the desired session, an leave session event will return null
this.currentSessionSelection = this.$('.session-select')
.find('option:selected').data('session_id');
isLeavingSession = !this.currentSessionSelection;
render() {
HtmlUtils.setHtml(this.$el, this.tpl(this.entitlementModel.toJSON()));
this.delegateEvents();
this.updateEnrollBtn();
return this;
}
// Display the indicator icon
HtmlUtils.setHtml(this.$dateDisplayField,
HtmlUtils.HTML('<span class="fa fa-spinner fa-spin" aria-hidden="true"></span>')
);
postRender() {
// Close any visible popovers on click-away
$(document).on('click', (e) => {
if (this.$('.popover:visible').length &&
!($(e.target).closest('.enroll-btn-initial, .popover').length)) {
this.hideDialog(this.$('.enroll-btn-initial'));
}
});
$.ajax({
type: isLeavingSession ? 'DELETE' : 'POST',
url: this.enrollUrl,
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify({
course_run_id: this.currentSessionSelection
}),
statusCode: {
201: _.bind(this.enrollSuccess, this),
204: _.bind(this.unenrollSuccess, this)
},
error: _.bind(this.enrollError, this)
});
},
// Initialize focus to cancel button on popover load
$(document).on('shown.bs.popover', () => {
this.$('.final-confirmation-btn:first').focus();
});
}
enrollSuccess: function(data) {
/*
Update external elements on the course card to represent the now available course session.
handleEnrollChange() {
/*
Handles enrolling in a course, unenrolling in a session and changing session.
The new session id is stored as a data attribute on the option in the session-select element.
*/
// Do not allow for enrollment when button is disabled
if (this.$('.enroll-btn-initial').hasClass('disabled')) return;
1) Show the change session toggle button.
2) Add the new session's dates to the date field on the main course card.
3) Hide the 'View Course' button to the course card.
*/
var successIconEl = '<span class="fa fa-check" aria-hidden="true"></span>';
// Grab the id for the desired session, an leave session event will return null
this.currentSessionSelection = this.$('.session-select')
.find('option:selected').data('session_id');
const isLeavingSession = !this.currentSessionSelection;
// With a containing backbone view, we can simply re-render the parent card
if (this.$parentEl) {
this.courseCardModel.updateCourseRun(this.currentSessionSelection);
return;
}
// Display the indicator icon
HtmlUtils.setHtml(this.$dateDisplayField,
HtmlUtils.HTML('<span class="fa fa-spinner fa-spin" aria-hidden="true"></span>'),
);
// Update the model with the new session Id
this.entitlementModel.set({currentSessionId: this.currentSessionSelection});
$.ajax({
type: isLeavingSession ? 'DELETE' : 'POST',
url: this.enrollUrl,
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify({
course_run_id: this.currentSessionSelection,
}),
statusCode: {
201: _.bind(this.enrollSuccess, this),
204: _.bind(this.unenrollSuccess, this),
},
error: _.bind(this.enrollError, this),
});
}
// Allow user to change session
this.$triggerOpenBtn.removeClass('hidden');
enrollSuccess(data) {
/*
Update external elements on the course card to represent the now available course session.
// Display a success indicator
HtmlUtils.setHtml(this.$dateDisplayField,
HtmlUtils.joinHtml(
HtmlUtils.HTML(successIconEl),
this.getAvailableSessionWithId(data.course_run_id).session_dates
)
);
1) Show the change session toggle button.
2) Add the new session's dates to the date field on the main course card.
3) Hide the 'View Course' button to the course card.
*/
const successIconEl = '<span class="fa fa-check" aria-hidden="true"></span>';
// Ensure the view course button links to new session home page and place focus there
this.$enterCourseBtn
.attr('href', this.formatCourseHomeUrl(data.course_run_id))
.removeClass('hidden')
.focus();
this.toggleSessionSelectionPanel();
},
// With a containing backbone view, we can simply re-render the parent card
if (this.$parentEl) {
this.courseCardModel.updateCourseRun(this.currentSessionSelection);
return;
}
unenrollSuccess: function() {
/*
Update external elements on the course card to represent the unenrolled state.
// Update the model with the new session Id
this.entitlementModel.set({ currentSessionId: this.currentSessionSelection });
1) Hide the change session button and the date field.
2) Hide the 'View Course' button.
3) Remove the messages associated with the enrolled state.
4) Remove the link from the course card image and title.
*/
// With a containing backbone view, we can simply re-render the parent card
if (this.$parentEl) {
this.courseCardModel.setUnselected();
return;
}
// Allow user to change session
this.$triggerOpenBtn.removeClass('hidden');
// Update the model with the new session Id;
this.entitlementModel.set({currentSessionId: this.currentSessionSelection});
// Display a success indicator
HtmlUtils.setHtml(this.$dateDisplayField,
HtmlUtils.joinHtml(
HtmlUtils.HTML(successIconEl),
this.getAvailableSessionWithId(data.course_run_id).session_dates,
),
);
// Reset the card contents to the unenrolled state
this.$triggerOpenBtn.addClass('hidden');
this.$enterCourseBtn.addClass('hidden');
// Remove all message except for related programs, which should always be shown
// (Even other messages might need to be shown again in future: LEARNER-3523.)
this.$courseCardMessages.filter(':not(.message-related-programs)').remove();
this.$policyMsg.remove();
this.$('.enroll-btn-initial').focus();
HtmlUtils.setHtml(
this.$dateDisplayField,
HtmlUtils.joinHtml(
HtmlUtils.HTML('<span class="icon fa fa-warning" aria-hidden="true"></span>'),
HtmlUtils.HTML(gettext('You must select a session to access the course.'))
)
);
// Ensure the view course button links to new session home page and place focus there
this.$enterCourseBtn
.attr('href', this.formatCourseHomeUrl(data.course_run_id))
.removeClass('hidden')
.focus();
this.toggleSessionSelectionPanel();
}
// Remove links to previously enrolled sessions
this.$courseImageLink.replaceWith( // xss-lint: disable=javascript-jquery-insertion
HtmlUtils.joinHtml(
HtmlUtils.HTML('<div class="'),
this.$courseImageLink.attr('class'),
HtmlUtils.HTML('" tabindex="-1">'),
HtmlUtils.HTML(this.$courseImageLink.html()),
HtmlUtils.HTML('</div>')
).text
);
this.$courseTitleLink.replaceWith( // xss-lint: disable=javascript-jquery-insertion
HtmlUtils.joinHtml(
HtmlUtils.HTML('<span>'),
this.$courseTitleLink.text(),
HtmlUtils.HTML('</span>')
).text
);
},
unenrollSuccess() {
/*
Update external elements on the course card to represent the unenrolled state.
enrollError: function() {
// Display a success indicator
var errorMsgEl = HtmlUtils.joinHtml(
HtmlUtils.HTML('<span class="enroll-error">'),
gettext('There was an error. Please reload the page and try again.'),
HtmlUtils.HTML('</spandiv>')
).text;
1) Hide the change session button and the date field.
2) Hide the 'View Course' button.
3) Remove the messages associated with the enrolled state.
4) Remove the link from the course card image and title.
*/
// With a containing backbone view, we can simply re-render the parent card
if (this.$parentEl) {
this.courseCardModel.setUnselected();
return;
}
this.$dateDisplayField
.find('.fa.fa-spin')
.removeClass('fa-spin fa-spinner')
.addClass('fa-close');
// Update the model with the new session Id;
this.entitlementModel.set({ currentSessionId: this.currentSessionSelection });
this.$dateDisplayField.append(errorMsgEl);
this.hideDialog(this.$('.enroll-btn-initial'));
},
// Reset the card contents to the unenrolled state
this.$triggerOpenBtn.addClass('hidden');
this.$enterCourseBtn.addClass('hidden');
// Remove all message except for related programs, which should always be shown
// (Even other messages might need to be shown again in future: LEARNER-3523.)
this.$courseCardMessages.filter(':not(.message-related-programs)').remove();
this.$policyMsg.remove();
this.$('.enroll-btn-initial').focus();
HtmlUtils.setHtml(
this.$dateDisplayField,
HtmlUtils.joinHtml(
HtmlUtils.HTML('<span class="icon fa fa-warning" aria-hidden="true"></span>'),
HtmlUtils.HTML(gettext('You must select a session to access the course.')),
),
);
updateEnrollBtn: function() {
/*
This function is invoked on load, on opening the view and on changing the option on the session
selection dropdown. It plays three roles:
1) Enables and disables enroll button
2) Changes text to describe the action taken
3) Formats the confirmation popover to allow for two step authentication
*/
var enrollText,
currentSessionId = this.entitlementModel.get('currentSessionId'),
newSessionId = this.$('.session-select').find('option:selected').data('session_id'),
enrollBtnInitial = this.$('.enroll-btn-initial');
// Remove links to previously enrolled sessions
this.$courseImageLink.replaceWith( // xss-lint: disable=javascript-jquery-insertion
HtmlUtils.joinHtml(
HtmlUtils.HTML('<div class="'),
this.$courseImageLink.attr('class'),
HtmlUtils.HTML('" tabindex="-1">'),
HtmlUtils.HTML(this.$courseImageLink.html()),
HtmlUtils.HTML('</div>'),
).text,
);
this.$courseTitleLink.replaceWith( // xss-lint: disable=javascript-jquery-insertion
HtmlUtils.joinHtml(
HtmlUtils.HTML('<span>'),
this.$courseTitleLink.text(),
HtmlUtils.HTML('</span>'),
).text,
);
}
// Disable the button if the user is already enrolled in that session.
if (currentSessionId === newSessionId) {
enrollBtnInitial.addClass('disabled');
this.removeDialog(enrollBtnInitial);
return;
}
enrollBtnInitial.removeClass('disabled');
enrollError() {
// Display a success indicator
const errorMsgEl = HtmlUtils.joinHtml(
HtmlUtils.HTML('<span class="enroll-error">'),
gettext('There was an error. Please reload the page and try again.'),
HtmlUtils.HTML('</spandiv>'),
).text;
// Update button text specifying if the user is initially enrolling, changing or leaving a session.
if (newSessionId) {
enrollText = currentSessionId ? gettext('Change Session') : gettext('Select Session');
} else {
enrollText = gettext('Leave Current Session');
}
enrollBtnInitial.text(enrollText);
this.initializeVerificationDialog(enrollBtnInitial);
},
this.$dateDisplayField
.find('.fa.fa-spin')
.removeClass('fa-spin fa-spinner')
.addClass('fa-close');
toggleSessionSelectionPanel: function() {
/*
Opens and closes the session selection panel.
*/
this.$el.toggleClass('hidden');
if (!this.$el.hasClass('hidden')) {
// Set focus to the session selection for a11y purposes
this.$('.session-select').focus();
this.hideDialog(this.$('.enroll-btn-initial'));
}
this.updateEnrollBtn();
},
this.$dateDisplayField.append(errorMsgEl);
this.hideDialog(this.$('.enroll-btn-initial'));
}
initializeVerificationDialog: function(invokingElement) {
/*
Instantiates an instance of the Bootstrap v4 dialog modal and attaches it to the passed in element.
updateEnrollBtn() {
/*
This function is invoked on load, on opening the view and on changing the option on the session
selection dropdown. It plays three roles:
1) Enables and disables enroll button
2) Changes text to describe the action taken
3) Formats the confirmation popover to allow for two step authentication
*/
let enrollText;
const currentSessionId = this.entitlementModel.get('currentSessionId');
const newSessionId = this.$('.session-select').find('option:selected').data('session_id');
const enrollBtnInitial = this.$('.enroll-btn-initial');
This dialog acts as the second step in verifying the user's action to select, change or leave an
available course session.
*/
var confirmationMsgTitle,
confirmationMsgBody,
currentSessionId = this.entitlementModel.get('currentSessionId'),
newSessionId = this.$('.session-select').find('option:selected').data('session_id');
// Disable the button if the user is already enrolled in that session.
if (currentSessionId === newSessionId) {
enrollBtnInitial.addClass('disabled');
this.removeDialog(enrollBtnInitial);
return;
}
enrollBtnInitial.removeClass('disabled');
// Update the button popover text to enable two step authentication.
if (newSessionId) {
confirmationMsgTitle = !currentSessionId ?
gettext('Are you sure you want to select this session?') :
gettext('Are you sure you want to change to a different session?');
confirmationMsgBody = !currentSessionId ? '' :
gettext('Any course progress or grades from your current session will be lost.');
} else {
confirmationMsgTitle = gettext('Are you sure that you want to leave this session?');
confirmationMsgBody = gettext('Any course progress or grades from your current session will be lost.'); // eslint-disable-line max-len
}
// Update button text specifying if the user is initially enrolling,
// changing or leaving a session.
if (newSessionId) {
enrollText = currentSessionId ? gettext('Change Session') : gettext('Select Session');
} else {
enrollText = gettext('Leave Current Session');
}
enrollBtnInitial.text(enrollText);
this.initializeVerificationDialog(enrollBtnInitial);
}
// Re-initialize the popover
invokingElement.popover({
placement: 'bottom',
container: this.$el,
html: true,
trigger: 'click',
content: this.verificationTpl({
confirmationMsgTitle: confirmationMsgTitle,
confirmationMsgBody: confirmationMsgBody
}).text
});
},
toggleSessionSelectionPanel() {
/*
Opens and closes the session selection panel.
*/
this.$el.toggleClass('hidden');
if (!this.$el.hasClass('hidden')) {
// Set focus to the session selection for a11y purposes
this.$('.session-select').focus();
this.hideDialog(this.$('.enroll-btn-initial'));
}
this.updateEnrollBtn();
}
removeDialog: function(el) {
/* Removes the Bootstrap v4 dialog modal from the update session enrollment button. */
var $el = el instanceof jQuery ? el : this.$('.enroll-btn-initial');
if (this.$('popover').length) {
$el.popover('dispose');
}
},
initializeVerificationDialog(invokingElement) {
/*
Instantiates an instance of the Bootstrap v4 dialog modal and attaches it to the
passed in element.
hideDialog: function(el, returnFocus) {
/* Hides the modal if it is visible without removing it from the DOM. */
var $el = el instanceof jQuery ? el : this.$('.enroll-btn-initial');
if (this.$('.popover:visible').length) {
$el.popover('hide');
if (returnFocus) {
$el.focus();
}
}
},
This dialog acts as the second step in verifying the user's action to select, change
or leave an available course session.
*/
let confirmationMsgTitle;
let confirmationMsgBody;
const currentSessionId = this.entitlementModel.get('currentSessionId');
const newSessionId = this.$('.session-select').find('option:selected').data('session_id');
handleVerificationPopoverA11y: function(e) {
/* Ensure that the second step verification popover is treated as an a11y compliant dialog */
var $nextButton,
$verificationOption = $(e.target),
openButton = $(e.target).closest('.course-entitlement-selection-container')
.find('.enroll-btn-initial');
if (e.key === 'Tab') {
e.preventDefault();
$nextButton = $verificationOption.is(':first-child') ?
// Update the button popover text to enable two step authentication.
if (newSessionId) {
confirmationMsgTitle = !currentSessionId ?
gettext('Are you sure you want to select this session?') :
gettext('Are you sure you want to change to a different session?');
confirmationMsgBody = !currentSessionId ? '' :
gettext('Any course progress or grades from your current session will be lost.');
} else {
confirmationMsgTitle = gettext('Are you sure that you want to leave this session?');
confirmationMsgBody = gettext('Any course progress or grades from your current session will be lost.'); // eslint-disable-line max-len
}
// Re-initialize the popover
invokingElement.popover({
placement: 'bottom',
container: this.$el,
html: true,
trigger: 'click',
content: this.verificationTpl({
confirmationMsgTitle,
confirmationMsgBody,
}).text,
});
}
removeDialog(el) {
/* Removes the Bootstrap v4 dialog modal from the update session enrollment button. */
const $el = el instanceof jQuery ? el : this.$('.enroll-btn-initial');
if (this.$('popover').length) {
$el.popover('dispose');
}
}
hideDialog(el, returnFocus) {
/* Hides the modal if it is visible without removing it from the DOM. */
const $el = el instanceof jQuery ? el : this.$('.enroll-btn-initial');
if (this.$('.popover:visible').length) {
$el.popover('hide');
if (returnFocus) {
$el.focus();
}
}
}
handleVerificationPopoverA11y(e) {
/* Ensure that the second step verification popover is treated as an a11y compliant dialog */
let $nextButton;
const $verificationOption = $(e.target);
const openButton = $(e.target).closest('.course-entitlement-selection-container')
.find('.enroll-btn-initial');
if (e.key === 'Tab') {
e.preventDefault();
$nextButton = $verificationOption.is(':first-child') ?
$verificationOption.next('.final-confirmation-btn') :
$verificationOption.prev('.final-confirmation-btn');
$nextButton.focus();
} else if (e.key === 'Escape') {
this.hideDialog(openButton);
openButton.focus();
}
},
$nextButton.focus();
} else if (e.key === 'Escape') {
this.hideDialog(openButton);
openButton.focus();
}
}
formatCourseHomeUrl: function(sessionKey) {
/*
Takes the base course home URL and updates it with the new session id, leveraging the
the fact that all course keys contain a '+' symbol.
*/
var oldSessionKey = this.courseHomeUrl.split('/')
formatCourseHomeUrl(sessionKey) {
/*
Takes the base course home URL and updates it with the new session id, leveraging the
the fact that all course keys contain a '+' symbol.
*/
const oldSessionKey = this.courseHomeUrl.split('/')
.filter(
function(urlParam) {
return urlParam.indexOf('+') > 0;
}
urlParam => urlParam.indexOf('+') > 0,
)[0];
return this.courseHomeUrl.replace(oldSessionKey, sessionKey);
},
return this.courseHomeUrl.replace(oldSessionKey, sessionKey);
}
formatDates: function(sessionData) {
/*
Takes a data object containing the upcoming available sessions for an entitlement and returns
the object with a session_dates attribute representing a formatted date string that highlights
the start and end dates of the particular session.
*/
var formattedSessionData = sessionData,
startDate,
endDate,
dateFormat;
// Set the date format string to the user's selected language
moment.locale(document.documentElement.lang);
dateFormat = moment.localeData().longDateFormat('L').indexOf('DD') >
moment.localeData().longDateFormat('L').indexOf('MM') ? 'MMMM D, YYYY' : 'D MMMM, YYYY';
formatDates(sessionData) {
/*
Takes a data object containing the upcoming available sessions for an entitlement and returns
the object with a session_dates attribute representing a formatted date string that highlights
the start and end dates of the particular session.
*/
const formattedSessionData = sessionData;
let startDate;
let endDate;
// Set the date format string to the user's selected language
moment.locale(document.documentElement.lang);
const dateFormat = moment.localeData().longDateFormat('L').indexOf('DD') >
moment.localeData().longDateFormat('L').indexOf('MM') ? 'MMMM D, YYYY' : 'D MMMM, YYYY';
return _.map(formattedSessionData, function(session) {
var formattedSession = session;
startDate = this.formatDate(formattedSession.start, dateFormat);
endDate = this.formatDate(formattedSession.end, dateFormat);
formattedSession.enrollment_end = this.formatDate(formattedSession.enrollment_end, dateFormat);
formattedSession.session_dates = this.courseCardModel.formatDateString({
start_date: startDate,
advertised_start: session.advertised_start,
end_date: endDate,
pacing_type: formattedSession.pacing_type
});
return formattedSession;
}, this);
},
return _.map(formattedSessionData, (session) => {
const formattedSession = session;
startDate = CourseEntitlementView.formatDate(formattedSession.start, dateFormat);
endDate = CourseEntitlementView.formatDate(formattedSession.end, dateFormat);
formattedSession.enrollment_end = CourseEntitlementView.formatDate(
formattedSession.enrollment_end,
dateFormat);
formattedSession.session_dates = this.courseCardModel.formatDateString({
start_date: startDate,
advertised_start: session.advertised_start,
end_date: endDate,
pacing_type: formattedSession.pacing_type,
});
return formattedSession;
}, this);
}
formatDate: function(date, dateFormat) {
return date ? moment((new Date(date))).format(dateFormat) : '';
},
static formatDate(date, dateFormat) {
return date ? moment((new Date(date))).format(dateFormat) : '';
}
getAvailableSessionWithId: function(sessionId) {
/* Returns an available session given a sessionId */
return this.entitlementModel.get('availableSessions').find(function(session) {
return session.session_id === sessionId;
});
}
});
}
);
}).call(this, define || RequireJS.define);
getAvailableSessionWithId(sessionId) {
/* Returns an available session given a sessionId */
return this.entitlementModel.get('availableSessions').find(session => session.session_id === sessionId);
}
}
export default CourseEntitlementView;

View File

@@ -1,130 +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.'),
/* globals gettext */
initialize: function(options) {
var view = this;
this.dashboardPath = options.dashboardPath;
this.signInPath = options.signInPath;
import Backbone from 'backbone';
this.$submitButton = $(this.submitButtonSelector);
this.$headerText = $(this.headerTextSelector);
this.$errorText = $(this.errorTextSelector);
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
this.$submitButton.on('click', this.handleSubmit.bind(this));
class EntitlementUnenrollmentView extends Backbone.View {
constructor(options) {
const defaults = {
el: '.js-entitlement-unenrollment-modal',
};
super(Object.assign({}, defaults, options));
}
$(this.triggerSelector).each(function() {
var $trigger = $(this);
initialize(options) {
const view = this;
$trigger.on('click', view.handleTrigger.bind(view));
this.closeButtonSelector = '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-close-btn';
this.headerTextSelector = '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-header-text';
this.errorTextSelector = '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-error-text';
this.submitButtonSelector = '.js-entitlement-unenrollment-modal .js-entitlement-unenrollment-modal-submit';
this.triggerSelector = '.js-entitlement-action-unenroll';
this.mainPageSelector = '#dashboard-main';
this.genericErrorMsg = gettext('Your unenrollment request could not be processed. Please try again later.');
if (window.accessible_modal) {
window.accessible_modal(
'#' + $trigger.attr('id'),
view.closeButtonSelector,
'#' + view.$el.attr('id'),
view.mainPageSelector
);
}
});
},
this.dashboardPath = options.dashboardPath;
this.signInPath = options.signInPath;
handleTrigger: function(event) {
var $trigger = $(event.target),
courseName = $trigger.data('courseName'),
courseNumber = $trigger.data('courseNumber'),
apiEndpoint = $trigger.data('entitlementApiEndpoint');
this.$submitButton = $(this.submitButtonSelector);
this.$headerText = $(this.headerTextSelector);
this.$errorText = $(this.errorTextSelector);
this.resetModal();
this.setHeaderText(courseName, courseNumber);
this.setSubmitData(apiEndpoint);
this.$el.css('position', 'fixed');
},
this.$submitButton.on('click', this.handleSubmit.bind(this));
handleSubmit: function() {
var apiEndpoint = this.$submitButton.data('entitlementApiEndpoint');
$(this.triggerSelector).each(function setUpTrigger() {
const $trigger = $(this);
if (apiEndpoint === undefined) {
this.setError(this.genericErrorMsg);
return;
}
$trigger.on('click', view.handleTrigger.bind(view));
this.$submitButton.prop('disabled', true);
$.ajax({
url: apiEndpoint,
method: 'DELETE',
complete: this.onComplete.bind(this)
});
},
if (window.accessible_modal) {
window.accessible_modal(
`#${$trigger.attr('id')}`,
view.closeButtonSelector,
`#${view.$el.attr('id')}`,
view.mainPageSelector,
);
}
});
}
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();
},
handleTrigger(event) {
const $trigger = $(event.target);
const courseName = $trigger.data('courseName');
const courseNumber = $trigger.data('courseNumber');
const apiEndpoint = $trigger.data('entitlementApiEndpoint');
setError: function(message) {
this.$submitButton.prop('disabled', true);
this.$errorText.empty();
HtmlUtils.setHtml(
this.resetModal();
this.setHeaderText(courseName, courseNumber);
this.setSubmitData(apiEndpoint);
this.$el.css('position', 'fixed');
}
handleSubmit() {
const 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() {
this.$submitButton.removeData();
this.$submitButton.prop('disabled', false);
this.$headerText.empty();
this.$errorText.removeClass('entitlement-unenrollment-modal-error-text-visible');
this.$errorText.empty();
}
setError(message) {
this.$submitButton.prop('disabled', true);
this.$errorText.empty();
HtmlUtils.setHtml(
this.$errorText,
message
message,
);
this.$errorText.addClass('entitlement-unenrollment-modal-error-text-visible');
},
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;
}
});
}
setHeaderText(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,
courseNumber,
},
),
);
}).call(this, define || RequireJS.define);
}
setSubmitData(apiEndpoint) {
this.$submitButton.removeData();
this.$submitButton.data('entitlementApiEndpoint', apiEndpoint);
}
onComplete(xhr) {
const status = xhr.status;
const message = xhr.responseJSON && xhr.responseJSON.detail;
if (status === 204) {
EntitlementUnenrollmentView.redirectTo(this.dashboardPath);
} else if (status === 401 && message === 'Authentication credentials were not provided.') {
EntitlementUnenrollmentView.redirectTo(`${this.signInPath}?next=${encodeURIComponent(this.dashboardPath)}`);
} else {
this.setError(this.genericErrorMsg);
}
}
static redirectTo(path) {
window.location.href = path;
}
}
export default EntitlementUnenrollmentView;

View File

@@ -1,33 +1,20 @@
(function(define) {
'use strict';
define(['backbone',
'jquery',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'text!../../../templates/learner_dashboard/expired_notification.underscore'
],
function(
Backbone,
$,
_,
gettext,
HtmlUtils,
expiredNotificationTpl
) {
return Backbone.View.extend({
expiredNotificationTpl: HtmlUtils.template(expiredNotificationTpl),
import Backbone from 'backbone';
initialize: function(options) {
this.$el = options.$el;
this.render();
},
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
render: function() {
var data = this.model.toJSON();
HtmlUtils.setHtml(this.$el, this.expiredNotificationTpl(data));
}
});
}
);
}).call(this, define || RequireJS.define);
import expiredNotificationTpl from '../../../templates/learner_dashboard/expired_notification.underscore';
class ExpiredNotificationView extends Backbone.View {
initialize(options) {
this.expiredNotificationTpl = HtmlUtils.template(expiredNotificationTpl);
this.$el = options.$el;
this.render();
}
render() {
const data = this.model.toJSON();
HtmlUtils.setHtml(this.$el, this.expiredNotificationTpl(data));
}
}
export default ExpiredNotificationView;

View File

@@ -1,44 +1,33 @@
(function(define) {
'use strict';
import _ from 'underscore';
import Backbone from 'backbone';
define(['backbone',
'jquery',
'underscore',
'gettext',
'text!../../../templates/learner_dashboard/explore_new_programs.underscore'
],
function(
Backbone,
$,
_,
gettext,
exploreTpl
) {
return Backbone.View.extend({
el: '.program-advertise',
import exploreTpl from '../../../templates/learner_dashboard/explore_new_programs.underscore';
tpl: _.template(exploreTpl),
class ExploreNewProgramsView extends Backbone.View {
constructor(options) {
const defaults = {
el: '.program-advertise',
};
super(Object.assign({}, defaults, options));
}
initialize: function(data) {
this.context = data.context;
this.$parentEl = $(this.parentEl);
initialize(data) {
this.tpl = _.template(exploreTpl);
this.context = data.context;
this.$parentEl = $(this.parentEl);
if (this.context.marketingUrl) {
// Only render if there is a link
this.render();
} else {
/**
* If not rendering remove el because
* styles are applied to it
*/
this.remove();
}
},
if (this.context.marketingUrl) {
// Only render if there is a link
this.render();
} else {
// If not rendering, remove el because styles are applied to it
this.remove();
}
}
render: function() {
this.$el.html(this.tpl(this.context));
}
});
}
);
}).call(this, define || RequireJS.define);
render() {
this.$el.html(this.tpl(this.context));
}
}
export default ExploreNewProgramsView;

View File

@@ -1,114 +1,102 @@
(function(define) {
'use strict';
/* globals gettext */
define(['backbone',
'jquery',
'underscore',
'gettext',
'text!../../../templates/learner_dashboard/program_card.underscore',
'picturefill'
],
function(
Backbone,
$,
_,
gettext,
programCardTpl,
picturefill
) {
return Backbone.View.extend({
import _ from 'underscore';
import Backbone from 'backbone';
import picturefill from 'picturefill';
className: 'program-card',
import programCardTpl from '../../../templates/learner_dashboard/program_card.underscore';
attributes: function() {
return {
'aria-labelledby': 'program-' + this.model.get('uuid'),
role: 'group'
};
},
class ProgramCardView extends Backbone.View {
constructor(options) {
const defaults = {
className: 'program-card',
attributes: function attr() {
return {
'aria-labelledby': `program-${this.model.get('uuid')}`,
role: 'group',
};
},
};
super(Object.assign({}, defaults, options));
}
tpl: _.template(programCardTpl),
initialize(data) {
this.tpl = _.template(programCardTpl);
this.progressCollection = data.context.progressCollection;
if (this.progressCollection) {
this.progressModel = this.progressCollection.findWhere({
uuid: this.model.get('uuid'),
});
}
this.render();
}
initialize: function(data) {
this.progressCollection = data.context.progressCollection;
if (this.progressCollection) {
this.progressModel = this.progressCollection.findWhere({
uuid: this.model.get('uuid')
});
}
this.render();
},
render: function() {
var orgList = _.map(this.model.get('authoring_organizations'), function(org) {
return gettext(org.key);
}),
data = $.extend(
this.model.toJSON(),
this.getProgramProgress(),
{orgList: orgList.join(' ')}
);
this.$el.html(this.tpl(data));
this.postRender();
},
postRender: function() {
if (navigator.userAgent.indexOf('MSIE') !== -1 ||
navigator.appVersion.indexOf('Trident/') > 0) {
/* Microsoft Internet Explorer detected in. */
window.setTimeout(function() {
this.reLoadBannerImage();
}.bind(this), 100);
}
},
// Calculate counts for progress and percentages for styling
getProgramProgress: function() {
var progress = this.progressModel ? this.progressModel.toJSON() : false;
if (progress) {
progress.total = progress.completed +
progress.in_progress +
progress.not_started;
progress.percentage = {
completed: this.getWidth(progress.completed, progress.total),
in_progress: this.getWidth(progress.in_progress, progress.total)
};
}
return {
progress: progress
};
},
getWidth: function(val, total) {
var int = (val / total) * 100;
return int + '%';
},
// Defer loading the rest of the page to limit FOUC
reLoadBannerImage: function() {
var $img = this.$('.program_card .banner-image'),
imgSrcAttr = $img ? $img.attr('src') : {};
if (!imgSrcAttr || imgSrcAttr.length < 0) {
try {
this.reEvaluatePicture();
} catch (err) {
// Swallow the error here
}
}
},
reEvaluatePicture: function() {
picturefill({
reevaluate: true
});
}
});
}
render() {
const orgList = _.map(this.model.get('authoring_organizations'), org => gettext(org.key));
const data = $.extend(
this.model.toJSON(),
this.getProgramProgress(),
{ orgList: orgList.join(' ') },
);
}).call(this, define || RequireJS.define);
this.$el.html(this.tpl(data));
this.postRender();
}
postRender() {
if (navigator.userAgent.indexOf('MSIE') !== -1 ||
navigator.appVersion.indexOf('Trident/') > 0) {
/* Microsoft Internet Explorer detected in. */
window.setTimeout(() => {
this.reLoadBannerImage();
}, 100);
}
}
// Calculate counts for progress and percentages for styling
getProgramProgress() {
const progress = this.progressModel ? this.progressModel.toJSON() : false;
if (progress) {
progress.total = progress.completed +
progress.in_progress +
progress.not_started;
progress.percentage = {
completed: ProgramCardView.getWidth(progress.completed, progress.total),
in_progress: ProgramCardView.getWidth(progress.in_progress, progress.total),
};
}
return {
progress,
};
}
static getWidth(val, total) {
const int = (val / total) * 100;
return `${int}%`;
}
// Defer loading the rest of the page to limit FOUC
reLoadBannerImage() {
const $img = this.$('.program_card .banner-image');
const imgSrcAttr = $img ? $img.attr('src') : {};
if (!imgSrcAttr || imgSrcAttr.length < 0) {
try {
ProgramCardView.reEvaluatePicture();
} catch (err) {
// Swallow the error here
}
}
}
static reEvaluatePicture() {
picturefill({
reevaluate: true,
});
}
}
export default ProgramCardView;

View File

@@ -1,97 +1,82 @@
(function(define) {
'use strict';
/* globals gettext */
define([
'backbone',
'jquery',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'edx-ui-toolkit/js/utils/string-utils',
'common/js/components/views/progress_circle_view',
'js/learner_dashboard/views/certificate_list_view',
'text!../../../templates/learner_dashboard/program_details_sidebar.underscore'
],
function(
Backbone,
$,
_,
gettext,
HtmlUtils,
StringUtils,
ProgramProgressView,
CertificateView,
sidebarTpl
) {
return Backbone.View.extend({
tpl: HtmlUtils.template(sidebarTpl),
import Backbone from 'backbone';
initialize: function(options) {
this.courseModel = options.courseModel || {};
this.certificateCollection = options.certificateCollection || [];
this.programCertificate = this.getProgramCertificate();
this.render();
},
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
import StringUtils from 'edx-ui-toolkit/js/utils/string-utils';
render: function() {
var data = $.extend({}, this.model.toJSON(), {
programCertificate: this.programCertificate ?
this.programCertificate.toJSON() : {}
});
import CertificateView from './certificate_list_view';
import ProgramProgressView from './progress_circle_view';
HtmlUtils.setHtml(this.$el, this.tpl(data));
this.postRender();
},
import sidebarTpl from '../../../templates/learner_dashboard/program_details_sidebar.underscore';
postRender: function() {
if (!this.programCertificate) {
this.progressModel = new Backbone.Model({
title: StringUtils.interpolate(
gettext('{type} Progress'),
{type: this.model.get('type')}
),
label: gettext('Earned Certificates'),
progress: {
completed: this.courseModel.get('completed').length,
in_progress: this.courseModel.get('in_progress').length,
not_started: this.courseModel.get('not_started').length
}
});
class ProgramDetailsSidebarView extends Backbone.View {
initialize(options) {
this.tpl = HtmlUtils.template(sidebarTpl);
this.courseModel = options.courseModel || {};
this.certificateCollection = options.certificateCollection || [];
this.programCertificate = this.getProgramCertificate();
this.render();
}
this.programProgressView = new ProgramProgressView({
el: '.js-program-progress',
model: this.progressModel
});
}
render() {
const data = $.extend({}, this.model.toJSON(), {
programCertificate: this.programCertificate ?
this.programCertificate.toJSON() : {},
});
if (this.certificateCollection.length) {
this.certificateView = new CertificateView({
el: '.js-course-certificates',
collection: this.certificateCollection,
title: gettext('Earned Certificates')
});
}
},
HtmlUtils.setHtml(this.$el, this.tpl(data));
this.postRender();
}
getProgramCertificate: function() {
var certificate = this.certificateCollection.findWhere({type: 'program'}),
base = '/static/images/programs/program-certificate-';
postRender() {
if (!this.programCertificate) {
this.progressModel = new Backbone.Model({
title: StringUtils.interpolate(
gettext('{type} Progress'),
{ type: this.model.get('type') },
),
label: gettext('Earned Certificates'),
progress: {
completed: this.courseModel.get('completed').length,
in_progress: this.courseModel.get('in_progress').length,
not_started: this.courseModel.get('not_started').length,
},
});
if (certificate) {
certificate.set({
img: base + this.getType() + '.gif'
});
}
this.programProgressView = new ProgramProgressView({
el: '.js-program-progress',
model: this.progressModel,
});
}
return certificate;
},
if (this.certificateCollection.length) {
this.certificateView = new CertificateView({
el: '.js-course-certificates',
collection: this.certificateCollection,
title: gettext('Earned Certificates'),
});
}
}
getType: function() {
var type = this.model.get('type').toLowerCase();
getProgramCertificate() {
const certificate = this.certificateCollection.findWhere({ type: 'program' });
const base = '/static/images/programs/program-certificate-';
return type.replace(/\s+/g, '-');
}
});
}
);
}).call(this, define || RequireJS.define);
if (certificate) {
certificate.set({
img: `${base + this.getType()}.gif`,
});
}
return certificate;
}
getType() {
const type = this.model.get('type').toLowerCase();
return type.replace(/\s+/g, '-');
}
}
export default ProgramDetailsSidebarView;

View File

@@ -1,137 +1,128 @@
(function(define) {
'use strict';
define(['backbone',
'jquery',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'js/learner_dashboard/collections/course_card_collection',
'js/learner_dashboard/views/program_header_view',
'js/learner_dashboard/views/collection_list_view',
'js/learner_dashboard/views/course_card_view',
'js/learner_dashboard/views/program_details_sidebar_view',
'text!../../../templates/learner_dashboard/program_details_view.underscore'
],
function(
Backbone,
$,
_,
gettext,
HtmlUtils,
CourseCardCollection,
HeaderView,
CollectionListView,
CourseCardView,
SidebarView,
pageTpl
) {
return Backbone.View.extend({
el: '.js-program-details-wrapper',
/* globals gettext */
tpl: HtmlUtils.template(pageTpl),
import Backbone from 'backbone';
events: {
'click .complete-program': 'trackPurchase'
},
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
initialize: function(options) {
this.options = options;
this.programModel = new Backbone.Model(this.options.programData);
this.courseData = new Backbone.Model(this.options.courseData);
this.certificateCollection = new Backbone.Collection(this.options.certificateData);
this.completedCourseCollection = new CourseCardCollection(
this.courseData.get('completed') || [],
this.options.userPreferences
);
this.inProgressCourseCollection = new CourseCardCollection(
this.courseData.get('in_progress') || [],
this.options.userPreferences
);
this.remainingCourseCollection = new CourseCardCollection(
this.courseData.get('not_started') || [],
this.options.userPreferences
);
import CollectionListView from './collection_list_view';
import CourseCardCollection from '../collections/course_card_collection';
import CourseCardView from './course_card_view';
import HeaderView from './program_header_view';
import SidebarView from './program_details_sidebar_view';
this.render();
},
import pageTpl from '../../../templates/learner_dashboard/program_details_view.underscore';
getUrl: function(base, programData) {
if (programData.uuid) {
return base + '&bundle=' + encodeURIComponent(programData.uuid);
}
return base;
},
class ProgramDetailsView extends Backbone.View {
constructor(options) {
const defaults = {
el: '.js-program-details-wrapper',
events: {
'click .complete-program': 'trackPurchase',
},
};
super(Object.assign({}, defaults, options));
}
render: function() {
var completedCount = this.completedCourseCollection.length,
inProgressCount = this.inProgressCourseCollection.length,
remainingCount = this.remainingCourseCollection.length,
totalCount = completedCount + inProgressCount + remainingCount,
buyButtonUrl = this.getUrl(this.options.urls.buy_button_url, this.options.programData),
data = {
totalCount: totalCount,
inProgressCount: inProgressCount,
remainingCount: remainingCount,
completedCount: completedCount,
completeProgramURL: buyButtonUrl
};
data = $.extend(data, this.programModel.toJSON());
HtmlUtils.setHtml(this.$el, this.tpl(data));
this.postRender();
},
postRender: function() {
this.headerView = new HeaderView({
model: new Backbone.Model(this.options)
});
if (this.remainingCourseCollection.length > 0) {
new CollectionListView({
el: '.js-course-list-remaining',
childView: CourseCardView,
collection: this.remainingCourseCollection,
context: $.extend(this.options, {collectionCourseStatus: 'remaining'})
}).render();
}
if (this.completedCourseCollection.length > 0) {
new CollectionListView({
el: '.js-course-list-completed',
childView: CourseCardView,
collection: this.completedCourseCollection,
context: $.extend(this.options, {collectionCourseStatus: 'completed'})
}).render();
}
if (this.inProgressCourseCollection.length > 0) {
// This is last because the context is modified below
new CollectionListView({
el: '.js-course-list-in-progress',
childView: CourseCardView,
collection: this.inProgressCourseCollection,
context: $.extend(this.options,
{enrolled: gettext('Enrolled'), collectionCourseStatus: 'in_progress'}
)
}).render();
}
this.sidebarView = new SidebarView({
el: '.js-program-sidebar',
model: this.programModel,
courseModel: this.courseData,
certificateCollection: this.certificateCollection
});
},
trackPurchase: function() {
var data = this.options.programData;
window.analytics.track('edx.bi.user.dashboard.program.purchase', {
category: data.variant + ' bundle',
label: data.title,
uuid: data.uuid
});
}
});
}
initialize(options) {
this.options = options;
this.tpl = HtmlUtils.template(pageTpl);
this.programModel = new Backbone.Model(this.options.programData);
this.courseData = new Backbone.Model(this.options.courseData);
this.certificateCollection = new Backbone.Collection(this.options.certificateData);
this.completedCourseCollection = new CourseCardCollection(
this.courseData.get('completed') || [],
this.options.userPreferences,
);
}).call(this, define || RequireJS.define);
this.inProgressCourseCollection = new CourseCardCollection(
this.courseData.get('in_progress') || [],
this.options.userPreferences,
);
this.remainingCourseCollection = new CourseCardCollection(
this.courseData.get('not_started') || [],
this.options.userPreferences,
);
this.render();
}
static getUrl(base, programData) {
if (programData.uuid) {
return `${base}&bundle=${encodeURIComponent(programData.uuid)}`;
}
return base;
}
render() {
const completedCount = this.completedCourseCollection.length;
const inProgressCount = this.inProgressCourseCollection.length;
const remainingCount = this.remainingCourseCollection.length;
const totalCount = completedCount + inProgressCount + remainingCount;
const buyButtonUrl = ProgramDetailsView.getUrl(
this.options.urls.buy_button_url,
this.options.programData);
let data = {
totalCount,
inProgressCount,
remainingCount,
completedCount,
completeProgramURL: buyButtonUrl,
};
data = $.extend(data, this.programModel.toJSON());
HtmlUtils.setHtml(this.$el, this.tpl(data));
this.postRender();
}
postRender() {
this.headerView = new HeaderView({
model: new Backbone.Model(this.options),
});
if (this.remainingCourseCollection.length > 0) {
new CollectionListView({
el: '.js-course-list-remaining',
childView: CourseCardView,
collection: this.remainingCourseCollection,
context: $.extend(this.options, { collectionCourseStatus: 'remaining' }),
}).render();
}
if (this.completedCourseCollection.length > 0) {
new CollectionListView({
el: '.js-course-list-completed',
childView: CourseCardView,
collection: this.completedCourseCollection,
context: $.extend(this.options, { collectionCourseStatus: 'completed' }),
}).render();
}
if (this.inProgressCourseCollection.length > 0) {
// This is last because the context is modified below
new CollectionListView({
el: '.js-course-list-in-progress',
childView: CourseCardView,
collection: this.inProgressCourseCollection,
context: $.extend(this.options,
{ enrolled: gettext('Enrolled'), collectionCourseStatus: 'in_progress' },
),
}).render();
}
this.sidebarView = new SidebarView({
el: '.js-program-sidebar',
model: this.programModel,
courseModel: this.courseData,
certificateCollection: this.certificateCollection,
});
}
trackPurchase() {
const data = this.options.programData;
window.analytics.track('edx.bi.user.dashboard.program.purchase', {
category: `${data.variant} bundle`,
label: data.title,
uuid: data.uuid,
});
}
}
export default ProgramDetailsView;

View File

@@ -1,57 +1,55 @@
(function(define) {
'use strict';
import Backbone from 'backbone';
define(['backbone',
'jquery',
'edx-ui-toolkit/js/utils/html-utils',
'text!../../../templates/learner_dashboard/program_header_view.underscore',
'text!../../../images/programs/micromasters-program-details.svg',
'text!../../../images/programs/xseries-program-details.svg',
'text!../../../images/programs/professional-certificate-program-details.svg'
],
function(Backbone, $, HtmlUtils, pageTpl, MicroMastersLogo,
XSeriesLogo, ProfessionalCertificateLogo) {
return Backbone.View.extend({
breakpoints: {
min: {
medium: '768px',
large: '1180px'
}
},
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
el: '.js-program-header',
import pageTpl from '../../../templates/learner_dashboard/program_header_view.underscore';
import MicroMastersLogo from '../../../images/programs/micromasters-program-details.svg';
import XSeriesLogo from '../../../images/programs/xseries-program-details.svg';
import ProfessionalCertificateLogo from '../../../images/programs/professional-certificate-program-details.svg';
tpl: HtmlUtils.template(pageTpl),
class ProgramHeaderView extends Backbone.View {
constructor(options) {
const defaults = {
el: '.js-program-header',
};
super(Object.assign({}, defaults, options));
}
initialize: function() {
this.render();
},
initialize() {
this.breakpoints = {
min: {
medium: '768px',
large: '1180px',
},
};
this.tpl = HtmlUtils.template(pageTpl);
this.render();
}
getLogo: function() {
var logo = false,
type = this.model.get('programData').type;
getLogo() {
const type = this.model.get('programData').type;
let logo = false;
if (type === 'MicroMasters') {
logo = MicroMastersLogo;
} else if (type === 'XSeries') {
logo = XSeriesLogo;
} else if (type === 'Professional Certificate') {
logo = ProfessionalCertificateLogo;
}
return logo;
},
if (type === 'MicroMasters') {
logo = MicroMastersLogo;
} else if (type === 'XSeries') {
logo = XSeriesLogo;
} else if (type === 'Professional Certificate') {
logo = ProfessionalCertificateLogo;
}
return logo;
}
render: function() {
var data = $.extend(this.model.toJSON(), {
breakpoints: this.breakpoints,
logo: this.getLogo()
});
render() {
const data = $.extend(this.model.toJSON(), {
breakpoints: this.breakpoints,
logo: this.getLogo(),
});
if (this.model.get('programData')) {
HtmlUtils.setHtml(this.$el, this.tpl(data));
}
}
});
}
);
}).call(this, define || RequireJS.define);
if (this.model.get('programData')) {
HtmlUtils.setHtml(this.$el, this.tpl(data));
}
}
}
export default ProgramHeaderView;

View File

@@ -0,0 +1,81 @@
import _ from 'underscore';
import Backbone from 'backbone';
import progressViewTpl from '../../../templates/learner_dashboard//progress_circle_view.underscore';
import progressSegmentTpl from '../../../templates/learner_dashboard/progress_circle_segment.underscore';
class ProgressCircleView extends Backbone.View {
initialize() {
this.x = 22;
this.y = 22;
this.radius = 16;
this.degrees = 180;
this.strokeWidth = 1.2;
this.viewTpl = _.template(progressViewTpl);
this.segmentTpl = _.template(progressSegmentTpl);
const progress = this.model.get('progress');
this.model.set({
totalCourses: progress.completed + progress.in_progress + progress.not_started,
});
this.render();
}
render() {
const data = $.extend({}, this.model.toJSON(), {
circleSegments: this.getProgressSegments(),
x: this.x,
y: this.y,
radius: this.radius,
strokeWidth: this.strokeWidth,
});
this.$el.html(this.viewTpl(data));
}
static getDegreeIncrement(total) {
return 360 / total;
}
static getOffset(total) {
return 100 - ((1 / total) * 100);
}
getProgressSegments() {
const progressHTML = [];
const total = this.model.get('totalCourses');
const segmentDash = 2 * Math.PI * this.radius;
const degreeInc = ProgressCircleView.getDegreeIncrement(total);
const data = {
// Remove strokeWidth to show a gap between the segments
dashArray: segmentDash - this.strokeWidth,
degrees: this.degrees,
offset: ProgressCircleView.getOffset(total),
x: this.x,
y: this.y,
radius: this.radius,
strokeWidth: this.strokeWidth,
};
for (let i = 0; i < total; i += 1) {
const segmentData = $.extend({}, data, {
classList: (i >= this.model.get('progress').completed) ? 'incomplete' : 'complete',
degrees: data.degrees + (i * degreeInc),
});
// Want the incomplete segments to have no gaps
if (segmentData.classList === 'incomplete' && (i + 1) < total) {
segmentData.dashArray = segmentDash;
}
progressHTML.push(this.segmentTpl(segmentData));
}
return progressHTML.join('');
}
}
export default ProgressCircleView;

View File

@@ -1,41 +1,33 @@
(function(define) {
'use strict';
import _ from 'underscore';
import Backbone from 'backbone';
define(['backbone',
'jquery',
'underscore',
'gettext',
'js/learner_dashboard/views/explore_new_programs_view',
'text!../../../templates/learner_dashboard/sidebar.underscore'
],
function(
Backbone,
$,
_,
gettext,
NewProgramsView,
sidebarTpl
) {
return Backbone.View.extend({
el: '.sidebar',
import NewProgramsView from './explore_new_programs_view';
tpl: _.template(sidebarTpl),
import sidebarTpl from '../../../templates/learner_dashboard/sidebar.underscore';
initialize: function(data) {
this.context = data.context;
},
class SidebarView extends Backbone.View {
constructor(options) {
const defaults = {
el: '.sidebar',
};
super(Object.assign({}, defaults, options));
}
render: function() {
this.$el.html(this.tpl(this.context));
this.postRender();
},
initialize(data) {
this.tpl = _.template(sidebarTpl);
this.context = data.context;
}
postRender: function() {
this.newProgramsView = new NewProgramsView({
context: this.context
});
}
});
}
);
}).call(this, define || RequireJS.define);
render() {
this.$el.html(this.tpl(this.context));
this.postRender();
}
postRender() {
this.newProgramsView = new NewProgramsView({
context: this.context,
});
}
}
export default SidebarView;

View File

@@ -1,77 +1,72 @@
(function(define) {
'use strict';
define(['backbone',
'jquery',
'underscore',
'gettext'
],
function(
Backbone,
$,
_,
gettext
) {
return Backbone.View.extend({
el: '.unenroll-modal',
/* globals gettext */
switchToSlideOne: function() {
var survey, i;
// 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]);
}
this.$('.inner-wrapper header').hide();
this.$('#unenroll_form').hide();
this.$('.slide1').removeClass('hidden');
},
import Backbone from 'backbone';
switchToSlideTwo: function() {
var reason = this.$(".reasons_survey input[name='reason']:checked").attr('val');
if (reason === 'Other') {
reason = this.$('.other_text').val();
}
if (reason) {
window.analytics.track('unenrollment_reason.selected', {
category: 'user-engagement',
label: reason,
displayName: 'v1'
});
}
this.$('.slide1').addClass('hidden');
this.$('.survey_course_name').text(this.$('#unenroll_course_name').text());
this.$('.slide2').removeClass('hidden');
this.$('.reasons_survey .return_to_dashboard').attr('href', this.urls.dashboard);
this.$('.reasons_survey .browse_courses').attr('href', this.urls.browseCourses);
},
class UnenrollView extends Backbone.View {
unenrollComplete: function(event, xhr) {
if (xhr.status === 200) {
if (!this.isEdx) {
location.href = this.urls.dashboard;
} else {
this.switchToSlideOne();
this.$('.reasons_survey:first .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');
}
},
constructor(options) {
const defaults = {
el: '.unenroll-modal',
};
super(Object.assign({}, defaults, options));
}
initialize: function(options) {
this.urls = options.urls;
this.isEdx = options.isEdx;
switchToSlideOne() {
// Randomize survey option order
const survey = document.querySelector('.options');
for (let i = survey.children.length - 1; i >= 0; i -= 1) {
survey.appendChild(survey.children[Math.trunc(Math.random() * i)]);
}
this.$('.inner-wrapper header').hide();
this.$('#unenroll_form').hide();
this.$('.slide1').removeClass('hidden');
}
$('#unenroll_form').on('ajax:complete', this.unenrollComplete.bind(this));
}
});
}
);
}).call(this, define || RequireJS.define);
switchToSlideTwo() {
let reason = this.$(".reasons_survey input[name='reason']:checked").attr('val');
if (reason === 'Other') {
reason = this.$('.other_text').val();
}
if (reason) {
window.analytics.track('unenrollment_reason.selected', {
category: 'user-engagement',
label: reason,
displayName: 'v1',
});
}
this.$('.slide1').addClass('hidden');
this.$('.survey_course_name').text(this.$('#unenroll_course_name').text());
this.$('.slide2').removeClass('hidden');
this.$('.reasons_survey .return_to_dashboard').attr('href', this.urls.dashboard);
this.$('.reasons_survey .browse_courses').attr('href', this.urls.browseCourses);
}
unenrollComplete(event, xhr) {
if (xhr.status === 200) {
if (!this.isEdx) {
location.href = this.urls.dashboard;
} else {
this.switchToSlideOne();
this.$('.reasons_survey:first .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(options) {
this.urls = options.urls;
this.isEdx = options.isEdx;
$('#unenroll_form').on('ajax:complete', this.unenrollComplete.bind(this));
}
}
export default UnenrollView;

View File

@@ -1,34 +1,20 @@
(function(define) {
'use strict';
define(['backbone',
'jquery',
'underscore',
'gettext',
'edx-ui-toolkit/js/utils/html-utils',
'text!../../../templates/learner_dashboard/upgrade_message.underscore'
],
function(
Backbone,
$,
_,
gettext,
HtmlUtils,
upgradeMessageTpl
) {
return Backbone.View.extend({
messageTpl: HtmlUtils.template(upgradeMessageTpl),
import Backbone from 'backbone';
initialize: function(options) {
this.$el = options.$el;
this.render();
},
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
render: function() {
var data = this.model.toJSON();
import upgradeMessageTpl from '../../../templates/learner_dashboard/upgrade_message.underscore';
HtmlUtils.setHtml(this.$el, this.messageTpl(data));
}
});
}
);
}).call(this, define || RequireJS.define);
class UpgradeMessageView extends Backbone.View {
initialize(options) {
this.messageTpl = HtmlUtils.template(upgradeMessageTpl);
this.$el = options.$el;
this.render();
}
render() {
const data = this.model.toJSON();
HtmlUtils.setHtml(this.$el, this.messageTpl(data));
}
}
export default UpgradeMessageView;