ECOM-7386 Added a program progress circle to program details page
This commit is contained in:
@@ -0,0 +1,96 @@
|
|||||||
|
(function(define) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
define(['backbone',
|
||||||
|
'jquery',
|
||||||
|
'underscore',
|
||||||
|
'gettext',
|
||||||
|
'text!../../../templates/components/progress_circle_view.underscore',
|
||||||
|
'text!../../../templates/components/progress_circle_segment.underscore'
|
||||||
|
],
|
||||||
|
function(
|
||||||
|
Backbone,
|
||||||
|
$,
|
||||||
|
_,
|
||||||
|
gettext,
|
||||||
|
progressViewTpl,
|
||||||
|
progressSegmentTpl
|
||||||
|
) {
|
||||||
|
return Backbone.View.extend({
|
||||||
|
x: 22,
|
||||||
|
y: 22,
|
||||||
|
radius: 16,
|
||||||
|
degrees: 180,
|
||||||
|
strokeWidth: 1.2,
|
||||||
|
|
||||||
|
viewTpl: _.template(progressViewTpl),
|
||||||
|
segmentTpl: _.template(progressSegmentTpl),
|
||||||
|
|
||||||
|
initialize: function() {
|
||||||
|
var progress = this.model.get('progress');
|
||||||
|
|
||||||
|
this.model.set({
|
||||||
|
totalCourses: progress.completed + progress.in_progress + progress.not_started
|
||||||
|
});
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var 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));
|
||||||
|
},
|
||||||
|
|
||||||
|
getDegreeIncrement: function(total) {
|
||||||
|
return 360 / total;
|
||||||
|
},
|
||||||
|
|
||||||
|
getOffset: function(total) {
|
||||||
|
return 100 - ((1 / total) * 100);
|
||||||
|
},
|
||||||
|
|
||||||
|
getProgressSegments: function() {
|
||||||
|
var progressHTML = [],
|
||||||
|
total = this.model.get('totalCourses'),
|
||||||
|
segmentDash = 2 * Math.PI * this.radius,
|
||||||
|
degreeInc = this.getDegreeIncrement(total),
|
||||||
|
data = {
|
||||||
|
// Remove strokeWidth to show a gap between the segments
|
||||||
|
dashArray: segmentDash - this.strokeWidth,
|
||||||
|
degrees: this.degrees,
|
||||||
|
offset: this.getOffset(total),
|
||||||
|
x: this.x,
|
||||||
|
y: this.y,
|
||||||
|
radius: this.radius,
|
||||||
|
strokeWidth: this.strokeWidth
|
||||||
|
},
|
||||||
|
i,
|
||||||
|
segmentData;
|
||||||
|
|
||||||
|
for (i = 0; i < total; i++) {
|
||||||
|
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('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}).call(this, define || RequireJS.define);
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
define([
|
||||||
|
'backbone',
|
||||||
|
'jquery',
|
||||||
|
'edx-ui-toolkit/js/utils/spec-helpers/spec-helpers',
|
||||||
|
'common/js/components/views/progress_circle_view'
|
||||||
|
], function(Backbone, $, SpecHelpers, ProgressCircleView) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('Progress Circle View', function() {
|
||||||
|
var view = null,
|
||||||
|
context = {
|
||||||
|
title: 'XSeries Progress',
|
||||||
|
label: 'Earned Certificates',
|
||||||
|
progress: {
|
||||||
|
completed: 2,
|
||||||
|
in_progress: 1,
|
||||||
|
not_started: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
testCircle,
|
||||||
|
testText,
|
||||||
|
initView,
|
||||||
|
getProgress,
|
||||||
|
testProgress;
|
||||||
|
|
||||||
|
testCircle = function(progress) {
|
||||||
|
var $circle = view.$('.progress-circle');
|
||||||
|
|
||||||
|
expect($circle.find('.complete').length).toEqual(progress.completed);
|
||||||
|
expect($circle.find('.incomplete').length).toEqual(progress.in_progress + progress.not_started);
|
||||||
|
};
|
||||||
|
|
||||||
|
testText = function(progress) {
|
||||||
|
var $numbers = view.$('.numbers'),
|
||||||
|
total = progress.completed + progress.in_progress + progress.not_started;
|
||||||
|
|
||||||
|
expect(view.$('.progress-heading').html()).toEqual('XSeries Progress');
|
||||||
|
expect(parseInt($numbers.find('.complete').html(), 10)).toEqual(progress.completed);
|
||||||
|
expect(parseInt($numbers.find('.total').html(), 10)).toEqual(total);
|
||||||
|
};
|
||||||
|
|
||||||
|
getProgress = function(x, y, z) {
|
||||||
|
return {
|
||||||
|
completed: x,
|
||||||
|
in_progress: y,
|
||||||
|
not_started: z
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testProgress = function(x, y, z) {
|
||||||
|
var progress = getProgress(x, y, z);
|
||||||
|
|
||||||
|
view = initView(progress);
|
||||||
|
view.render();
|
||||||
|
|
||||||
|
testCircle(progress);
|
||||||
|
testText(progress);
|
||||||
|
};
|
||||||
|
|
||||||
|
initView = function(progress) {
|
||||||
|
var data = $.extend({}, context, {
|
||||||
|
progress: progress
|
||||||
|
});
|
||||||
|
|
||||||
|
return new ProgressCircleView({
|
||||||
|
el: '.js-program-progress',
|
||||||
|
model: new Backbone.Model(data)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
setFixtures('<div class="js-program-progress"></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
view.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should exist', function() {
|
||||||
|
var progress = getProgress(2, 1, 3);
|
||||||
|
|
||||||
|
view = initView(progress);
|
||||||
|
view.render();
|
||||||
|
expect(view).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the progress circle based on the passed in model', function() {
|
||||||
|
var progress = getProgress(2, 1, 3);
|
||||||
|
|
||||||
|
view = initView(progress);
|
||||||
|
view.render();
|
||||||
|
testCircle(progress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the progress text based on the passed in model', function() {
|
||||||
|
var progress = getProgress(2, 1, 3);
|
||||||
|
|
||||||
|
view = initView(progress);
|
||||||
|
view.render();
|
||||||
|
testText(progress);
|
||||||
|
});
|
||||||
|
|
||||||
|
SpecHelpers.withData({
|
||||||
|
'should render the progress text with only completed courses': [5, 0, 0],
|
||||||
|
'should render the progress text with only in progress courses': [0, 4, 0],
|
||||||
|
'should render the progress circle with only not started courses': [0, 0, 5],
|
||||||
|
'should render the progress text with no completed courses': [0, 2, 3],
|
||||||
|
'should render the progress text with no in progress courses': [2, 0, 7],
|
||||||
|
'should render the progress text with no not started courses': [2, 4, 0]
|
||||||
|
}, testProgress);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -164,6 +164,7 @@
|
|||||||
'common/js/spec/components/paginated_view_spec.js',
|
'common/js/spec/components/paginated_view_spec.js',
|
||||||
'common/js/spec/components/paging_header_spec.js',
|
'common/js/spec/components/paging_header_spec.js',
|
||||||
'common/js/spec/components/paging_footer_spec.js',
|
'common/js/spec/components/paging_footer_spec.js',
|
||||||
|
'common/js/spec/components/progress_circle_view_spec.js',
|
||||||
'common/js/spec/components/search_field_spec.js',
|
'common/js/spec/components/search_field_spec.js',
|
||||||
'common/js/spec/components/view_utils_spec.js',
|
'common/js/spec/components/view_utils_spec.js',
|
||||||
'common/js/spec/utils/edx.utils.validate_spec.js'
|
'common/js/spec/utils/edx.utils.validate_spec.js'
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<circle class="<%- classList %>"
|
||||||
|
r="<%- radius %>" cx="<%- x %>" cy="<%- y %>"
|
||||||
|
transform="rotate(<%- degrees %>, <%- x %>, <%- y %>)"
|
||||||
|
stroke-width="<%- strokeWidth %>"
|
||||||
|
fill="none"
|
||||||
|
stroke-dasharray="<%- dashArray %>"
|
||||||
|
stroke-dashoffset="<%- offset %>">
|
||||||
|
</circle>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<% if (title) { %>
|
||||||
|
<h2 class="progress-heading"><%- title %></h2>
|
||||||
|
<% } %>
|
||||||
|
<div class="progress-circle-wrapper">
|
||||||
|
<svg class="progress-circle" viewBox="0 0 44 44" aria-hidden="true">
|
||||||
|
<circle class="js-circle bg" r="<%- radius %>" cx="<%- x %>" cy="<%- y %>" stroke-width="<%- strokeWidth %>" fill="none"></circle>
|
||||||
|
<%= circleSegments %>
|
||||||
|
</svg>
|
||||||
|
<div class="progress-label">
|
||||||
|
<div class="numbers">
|
||||||
|
<span class="complete"><%- progress.completed %></span>/<span class="total"><%- totalCourses %></span>
|
||||||
|
</div>
|
||||||
|
<div class="label"><% if (label) { %><%- label %><% } %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
BIN
lms/static/images/programs/program-certificate-micromasters.gif
Normal file
BIN
lms/static/images/programs/program-certificate-micromasters.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
BIN
lms/static/images/programs/program-certificate-xseries.gif
Normal file
BIN
lms/static/images/programs/program-certificate-xseries.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,35 @@
|
|||||||
|
(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),
|
||||||
|
|
||||||
|
initialize: function(options) {
|
||||||
|
this.title = options.title || false;
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var data = {
|
||||||
|
title: this.title,
|
||||||
|
certificateList: this.collection.toJSON()
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$el.html(this.tpl(data));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}).call(this, define || RequireJS.define);
|
||||||
@@ -1,47 +1,97 @@
|
|||||||
(function(define) {
|
(function(define) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
define(['backbone',
|
define([
|
||||||
'jquery',
|
'backbone',
|
||||||
'underscore',
|
'jquery',
|
||||||
'gettext',
|
'underscore',
|
||||||
'js/learner_dashboard/views/explore_new_programs_view',
|
'gettext',
|
||||||
'js/learner_dashboard/views/certificate_view',
|
'edx-ui-toolkit/js/utils/html-utils',
|
||||||
'text!../../../templates/learner_dashboard/sidebar.underscore'
|
'edx-ui-toolkit/js/utils/string-utils',
|
||||||
],
|
'common/js/components/views/progress_circle_view',
|
||||||
function(
|
'js/learner_dashboard/views/certificate_list_view',
|
||||||
Backbone,
|
'text!../../../templates/learner_dashboard/program_details_sidebar.underscore'
|
||||||
$,
|
],
|
||||||
_,
|
function(
|
||||||
gettext,
|
Backbone,
|
||||||
NewProgramsView,
|
$,
|
||||||
CertificateView,
|
_,
|
||||||
sidebarTpl
|
gettext,
|
||||||
) {
|
HtmlUtils,
|
||||||
return Backbone.View.extend({
|
StringUtils,
|
||||||
el: '.sidebar',
|
ProgramProgressView,
|
||||||
|
CertificateView,
|
||||||
|
sidebarTpl
|
||||||
|
) {
|
||||||
|
return Backbone.View.extend({
|
||||||
|
tpl: HtmlUtils.template(sidebarTpl),
|
||||||
|
|
||||||
tpl: _.template(sidebarTpl),
|
initialize: function(options) {
|
||||||
|
this.courseModel = options.courseModel || {};
|
||||||
|
this.certificateCollection = options.certificateCollection || [];
|
||||||
|
this.programCertificate = this.getProgramCertificate();
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
initialize: function(data) {
|
render: function() {
|
||||||
this.context = data.context;
|
var data = $.extend({}, this.model.toJSON(), {
|
||||||
},
|
programCertificate: this.programCertificate ?
|
||||||
|
this.programCertificate.toJSON() : {}
|
||||||
|
});
|
||||||
|
|
||||||
render: function() {
|
HtmlUtils.setHtml(this.$el, this.tpl(data));
|
||||||
this.$el.html(this.tpl(this.context));
|
this.postRender();
|
||||||
this.postRender();
|
},
|
||||||
},
|
|
||||||
|
|
||||||
postRender: function() {
|
postRender: function() {
|
||||||
this.newProgramsView = new NewProgramsView({
|
if (!this.programCertificate) {
|
||||||
context: this.context
|
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
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.newCertificateView = new CertificateView({
|
this.programProgressView = new ProgramProgressView({
|
||||||
context: this.context
|
el: '.js-program-progress',
|
||||||
});
|
model: this.progressModel
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
if (this.certificateCollection.length) {
|
||||||
|
this.certificateView = new CertificateView({
|
||||||
|
el: '.js-course-certificates',
|
||||||
|
collection: this.certificateCollection,
|
||||||
|
title: gettext('Earned Certificates')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getProgramCertificate: function() {
|
||||||
|
var certificate = this.certificateCollection.findWhere({type: 'program'}),
|
||||||
|
base = '/static/images/programs/program-certificate-';
|
||||||
|
|
||||||
|
if (certificate) {
|
||||||
|
certificate.set({
|
||||||
|
img: base + this.getType() + '.gif'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificate;
|
||||||
|
},
|
||||||
|
|
||||||
|
getType: function() {
|
||||||
|
var type = this.model.get('type').toLowerCase();
|
||||||
|
|
||||||
|
return type.replace(/\s+/g, '-');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}).call(this, define || RequireJS.define);
|
}).call(this, define || RequireJS.define);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
this.options = options;
|
this.options = options;
|
||||||
this.programModel = new Backbone.Model(this.options.programData);
|
this.programModel = new Backbone.Model(this.options.programData);
|
||||||
this.courseData = new Backbone.Model(this.options.courseData);
|
this.courseData = new Backbone.Model(this.options.courseData);
|
||||||
|
this.certificateCollection = new Backbone.Collection(this.options.certificateData);
|
||||||
this.completedCourseCollection = new CourseCardCollection(
|
this.completedCourseCollection = new CourseCardCollection(
|
||||||
this.courseData.get('completed') || [],
|
this.courseData.get('completed') || [],
|
||||||
this.options.userPreferences
|
this.options.userPreferences
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
remainingCount: remainingCount,
|
remainingCount: remainingCount,
|
||||||
completedCount: completedCount
|
completedCount: completedCount
|
||||||
};
|
};
|
||||||
data = $.extend(data, this.options.programData);
|
data = $.extend(data, this.programModel.toJSON());
|
||||||
HtmlUtils.setHtml(this.$el, this.tpl(data));
|
HtmlUtils.setHtml(this.$el, this.tpl(data));
|
||||||
this.postRender();
|
this.postRender();
|
||||||
},
|
},
|
||||||
@@ -99,10 +100,12 @@
|
|||||||
}).render();
|
}).render();
|
||||||
}
|
}
|
||||||
|
|
||||||
new SidebarView({
|
this.sidebarView = new SidebarView({
|
||||||
el: '.sidebar',
|
el: '.js-program-sidebar',
|
||||||
context: this.options
|
model: this.programModel,
|
||||||
}).render();
|
courseModel: this.courseData,
|
||||||
|
certificateCollection: this.certificateCollection
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -743,6 +743,7 @@
|
|||||||
'js/spec/learner_dashboard/program_card_view_spec.js',
|
'js/spec/learner_dashboard/program_card_view_spec.js',
|
||||||
'js/spec/learner_dashboard/sidebar_view_spec.js',
|
'js/spec/learner_dashboard/sidebar_view_spec.js',
|
||||||
'js/spec/learner_dashboard/program_details_header_spec.js',
|
'js/spec/learner_dashboard/program_details_header_spec.js',
|
||||||
|
'js/spec/learner_dashboard/program_details_sidebar_view_spec.js',
|
||||||
'js/spec/learner_dashboard/course_card_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_enroll_view_spec.js',
|
||||||
'js/spec/learner_dashboard/course_enroll_view_spec_2017.js',
|
'js/spec/learner_dashboard/course_enroll_view_spec_2017.js',
|
||||||
|
|||||||
@@ -6,5 +6,6 @@
|
|||||||
@import 'elements/course-card';
|
@import 'elements/course-card';
|
||||||
@import 'elements/program-card';
|
@import 'elements/program-card';
|
||||||
@import 'elements-v2/icons';
|
@import 'elements-v2/icons';
|
||||||
|
@import 'elements/progress-circle';
|
||||||
@import 'views/program-details';
|
@import 'views/program-details';
|
||||||
@import 'views/program-list';
|
@import 'views/program-list';
|
||||||
|
|||||||
66
lms/static/sass/elements/_progress-circle.scss
Normal file
66
lms/static/sass/elements/_progress-circle.scss
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
$progress-title-color: $blue-d1 !default;
|
||||||
|
$progress-complete-color: $blue-u1 !default;
|
||||||
|
$progress-incomplete-color: $gray-l3 !default;
|
||||||
|
$progress-complete-number-color: $blue-d1 !default;
|
||||||
|
$progress-incomplete-number-color: $gray !default;
|
||||||
|
$progress-number-label-color: palette(grayscale, base) !default;
|
||||||
|
|
||||||
|
.program-progress {
|
||||||
|
width: 300px;
|
||||||
|
margin: 0 auto 30px;
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-md) {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-heading {
|
||||||
|
color: $progress-title-color;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font: {
|
||||||
|
size: 1.1em;
|
||||||
|
weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-circle-wrapper {
|
||||||
|
position: relative;
|
||||||
|
margin-top: -20px;
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
|
||||||
|
.progress-label {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
top: 92px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.numbers {
|
||||||
|
font-size: 3em;
|
||||||
|
color: $progress-incomplete-number-color;
|
||||||
|
|
||||||
|
.complete {
|
||||||
|
color: $progress-complete-number-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font: {
|
||||||
|
size: 1.1em;
|
||||||
|
weight: 600;
|
||||||
|
}
|
||||||
|
color: $progress-number-label-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-circle {
|
||||||
|
.complete {
|
||||||
|
stroke: $progress-complete-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incomplete {
|
||||||
|
stroke: $progress-incomplete-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,8 +47,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.crumb {
|
.crumb {
|
||||||
|
@include float(left);
|
||||||
position: relative;
|
position: relative;
|
||||||
float: left;
|
|
||||||
font-size: font-size(x-small);
|
font-size: font-size(x-small);
|
||||||
line-height: line-height(x-small);
|
line-height: line-height(x-small);
|
||||||
color: palette(grayscale, dark);
|
color: palette(grayscale, dark);
|
||||||
@@ -78,14 +78,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CSS for April 2017 version of Program Details Page
|
// CSS for April 2017 version of Program Details Page
|
||||||
|
|
||||||
.program-details {
|
.program-details {
|
||||||
.window-wrap {
|
.window-wrap {
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.program-details-wrapper {
|
|
||||||
|
|
||||||
|
.wrapper-footer {
|
||||||
|
@include clearfix();
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.program-details-wrapper {
|
||||||
.program-details-header {
|
.program-details-header {
|
||||||
background-color: $light-gray4;
|
background-color: $light-gray4;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -93,14 +97,20 @@
|
|||||||
font-family: 'Open Sans';
|
font-family: 'Open Sans';
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding-top: 40px;
|
padding: 40px 10px 35px;
|
||||||
padding-bottom: 35px;
|
|
||||||
margin-left: 10px;
|
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
@media(min-width: $bp-screen-md) {
|
@media(min-width: $bp-screen-md) {
|
||||||
margin-left: 30px;
|
padding: {
|
||||||
margin-right: 80px;
|
left: 30px;
|
||||||
|
right: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $lms-max-width) {
|
||||||
|
padding: {
|
||||||
|
left: calc(((100% - 1180px) / 2) + 30px);
|
||||||
|
right: calc(((100% - 1180px) / 2) + 30px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.hd-1 {
|
.hd-1 {
|
||||||
@@ -111,7 +121,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.program-details-icon {
|
.program-details-icon {
|
||||||
margin-left: 3px;
|
@include margin-left(3px);
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
||||||
@@ -177,7 +187,7 @@
|
|||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
@media(min-width: $bp-screen-md) {
|
@media(min-width: $bp-screen-md) {
|
||||||
margin: 10px 0 0 0;
|
@include margin-right(10px 0 0 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -187,8 +197,8 @@
|
|||||||
width: 30%;
|
width: 30%;
|
||||||
|
|
||||||
.orgs .org-logo {
|
.orgs .org-logo {
|
||||||
|
@include margin-left(2.5%);
|
||||||
width: 46.5%;
|
width: 46.5%;
|
||||||
margin-left: 2.5%;
|
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,26 +207,49 @@
|
|||||||
width: 25%;
|
width: 25%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.program-details-content {
|
.program-details-content {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding: 30px 10px;
|
||||||
|
|
||||||
@media(min-width: $bp-screen-md) {
|
@media(min-width: $bp-screen-md) {
|
||||||
margin-left: 30px;
|
@include float(left);
|
||||||
|
padding: {
|
||||||
|
left: 30px;
|
||||||
|
right: 30px;
|
||||||
|
}
|
||||||
|
width: calc( 100% - 330px );
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-lg) {
|
||||||
|
width: calc( 100% - 510px );
|
||||||
|
max-width: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $lms-max-width) {
|
||||||
|
@include margin-left(calc((100% - 1180px) / 2));
|
||||||
}
|
}
|
||||||
margin-left: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.course-list-heading {
|
.course-list-heading {
|
||||||
font-family: "Open Sans";
|
font-family: "Open Sans";
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
color: palette(primary, dark);
|
color: palette(primary, dark);
|
||||||
font-size: 0.9375em;
|
font-size: 0.9375em;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
margin-top: 10px;
|
padding-bottom: 5px;
|
||||||
margin-bottom: 0;
|
border-bottom: 3px solid $divider-color;
|
||||||
|
margin: {
|
||||||
|
top: 10px;
|
||||||
|
bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
margin-right: 7px;
|
@include margin-right(7px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,27 +258,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.course-list-headings {
|
.course-list-headings {
|
||||||
width: 700px;
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
background-color: $divider-color;
|
|
||||||
margin-top: 5px;
|
|
||||||
height: 3px;
|
|
||||||
width: 315px;
|
|
||||||
@media(min-width: $bp-screen-sm) {
|
|
||||||
width: 550px;
|
|
||||||
}
|
|
||||||
@media(min-width: $bp-screen-md) {
|
|
||||||
width: 700px;
|
|
||||||
}
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.motivating-section {
|
.motivating-section {
|
||||||
|
@include margin-left(15px);
|
||||||
font-size: 0.9375em;
|
font-size: 0.9375em;
|
||||||
margin-left: 15px;
|
|
||||||
width: 310px;
|
width: 310px;
|
||||||
@media(min-width: $bp-screen-sm) {
|
@media(min-width: $bp-screen-sm) {
|
||||||
width: auto;
|
width: auto;
|
||||||
@@ -264,11 +279,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.program-heading {
|
.program-heading {
|
||||||
@media(min-width: $bp-screen-md) {
|
width: 100%;
|
||||||
width: 70%;
|
|
||||||
}
|
|
||||||
width: 90%;
|
|
||||||
margin-top: 40px;
|
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
|
|
||||||
.program-heading-title {
|
.program-heading-title {
|
||||||
@@ -313,17 +324,17 @@
|
|||||||
|
|
||||||
/* IE11 CSS styles */
|
/* IE11 CSS styles */
|
||||||
@media(min-width: $bp-screen-md) and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
@media(min-width: $bp-screen-md) and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
||||||
float: right;
|
@include float(right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.select-choice {
|
.select-choice {
|
||||||
|
@include margin-right(2px);
|
||||||
font-family: "Open Sans";
|
font-family: "Open Sans";
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 0.9375em;
|
font-size: 0.9375em;
|
||||||
color: palette(grayscale, base);
|
color: palette(grayscale, base);
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
margin-right: 2px;
|
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
@media(min-width: $bp-screen-md) {
|
@media(min-width: $bp-screen-md) {
|
||||||
@@ -339,21 +350,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.run-select {
|
.run-select {
|
||||||
|
@include margin-right(10px);
|
||||||
width: 95%;
|
width: 95%;
|
||||||
@media(min-width: $bp-screen-sm) {
|
@media(min-width: $bp-screen-sm) {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
}
|
}
|
||||||
height: 34px;
|
height: 34px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-right: 10px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.program-course-card {
|
.program-course-card {
|
||||||
@media(min-width: $bp-screen-md) {
|
width: 100%;
|
||||||
width: 100%;
|
padding: 15px;
|
||||||
}
|
|
||||||
|
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
||||||
@media(min-width: $bp-screen-md) {
|
@media(min-width: $bp-screen-md) {
|
||||||
@@ -363,12 +372,6 @@
|
|||||||
.section {
|
.section {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-right: 40px;
|
|
||||||
margin-left: 15px;
|
|
||||||
|
|
||||||
@media(min-width: $bp-screen-sm) {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(min-width: $bp-screen-md) {
|
@media(min-width: $bp-screen-md) {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -400,9 +403,12 @@
|
|||||||
.course-meta-container {
|
.course-meta-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
@media(min-width: $bp-screen-md) {
|
@media(min-width: $bp-screen-md) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,6 +423,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.course-certificate {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.upgrade-message {
|
.upgrade-message {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
@@ -432,7 +442,7 @@
|
|||||||
|
|
||||||
/* IE11 CSS styles */
|
/* IE11 CSS styles */
|
||||||
@media(min-width: $bp-screen-md) and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
@media(min-width: $bp-screen-md) and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
||||||
float: right;
|
@include float(right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,6 +505,109 @@
|
|||||||
font-size: 0.9375em;
|
font-size: 0.9375em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.program-sidebar {
|
||||||
|
padding: 30px 10px;
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-md) {
|
||||||
|
@include float(right);
|
||||||
|
width: 300px;
|
||||||
|
padding-right: 30px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-lg) {
|
||||||
|
width: 450px;
|
||||||
|
|
||||||
|
.program-progress {
|
||||||
|
@include margin-left(50px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $lms-max-width) {
|
||||||
|
@include margin-right(calc((100% - 1180px) / 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-heading {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-md) {
|
||||||
|
@include margin-right(30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-lg) {
|
||||||
|
@include margin-left(10px);
|
||||||
|
@include margin-right(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.program-cert-link {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
.program-cert {
|
||||||
|
border-color: $blue-d1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.program-cert {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid $divider-color;
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-md) {
|
||||||
|
width: calc(100% - 30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-lg) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-list {
|
||||||
|
@include margin(0, 0, 0, 10px);
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.certificate {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 5px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.certificate-link {
|
||||||
|
@include margin-left(20px);
|
||||||
|
color: $black;
|
||||||
|
font: {
|
||||||
|
size: 1.1em;
|
||||||
|
weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-md) {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-lg) {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:hover {
|
||||||
|
.sample-cert {
|
||||||
|
border-color: $blue-d1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sample-cert {
|
||||||
|
width: 120px;
|
||||||
|
border: 3px solid $gray-l3;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
.expired-notification {
|
.expired-notification {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -511,7 +624,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.expired-icon {
|
.expired-icon {
|
||||||
float: left;
|
@include float(left);
|
||||||
color: palette(primary, dark);
|
color: palette(primary, dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,5 +633,12 @@
|
|||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-md) {
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: $bp-screen-lg) {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
lms/templates/learner_dashboard/certificate_list.underscore
Normal file
13
lms/templates/learner_dashboard/certificate_list.underscore
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<div class="certificate-container">
|
||||||
|
<% if (title) { %>
|
||||||
|
<h2 class="course-list-heading"><%- title %></h2>
|
||||||
|
<% } %>
|
||||||
|
<ul class="certificate-list">
|
||||||
|
<% _.each(certificateList, function(certificate){ %>
|
||||||
|
<li class="certificate">
|
||||||
|
<a class="image-link" href="<%- certificate.url %>" aria-hidden="true" tabindex="-1"><img src="/static/images/programs/sample-cert.png" class="sample-cert" alt=""></a>
|
||||||
|
<a class="certificate-link" href="<%- certificate.url %>"><%- certificate.title %></a>
|
||||||
|
</li>
|
||||||
|
<% }); %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="course-actions col-12 md-col-4 sm-col-12"></div>
|
<div class="course-actions col-12 md-col-4 sm-col-12"></div>
|
||||||
<div class="certificate-status"></div>
|
<div class="course-certificate certificate-status"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section action-msg-view"></div>
|
<div class="section action-msg-view"></div>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<aside class="aside js-program-progress program-progress">
|
||||||
|
<% if (programCertificate) { %>
|
||||||
|
<h2 class="progress-heading certificate-heading"><%- StringUtils.interpolate(gettext('Your {program} Certificate'), {program: type}, true) %></h2>
|
||||||
|
|
||||||
|
<a href="<%- programCertificate.url %>" class="program-cert-link">
|
||||||
|
<img src="<%- programCertificate.img %>" class="program-cert" alt="<%- interpolate(gettext('Open the certificate you earned for the %(title)s program.'), {title: programCertificate.title}, true) %>" />
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</aside>
|
||||||
|
<aside class="aside js-course-certificates"></aside>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<header class="js-program-header program-header full-width-banner"></header>
|
<header class="js-program-header program-header full-width-banner"></header>
|
||||||
<div class="program-details-content">
|
<section class="program-details-content">
|
||||||
<div class="js-program-progress-view"></div>
|
|
||||||
<div class="program-heading">
|
<div class="program-heading">
|
||||||
<% if (inProgressCount === totalCount) { %>
|
<% if (inProgressCount === totalCount) { %>
|
||||||
<h3 class="program-heading-title"><%- gettext('Congratulations!') %></h3>
|
<h3 class="program-heading-title"><%- gettext('Congratulations!') %></h3>
|
||||||
@@ -29,7 +28,6 @@
|
|||||||
<span class="status"><%- gettext('COURSES IN PROGRESS') %></span>
|
<span class="status"><%- gettext('COURSES IN PROGRESS') %></span>
|
||||||
<span class="count"><%- inProgressCount %></span>
|
<span class="count"><%- inProgressCount %></span>
|
||||||
</h4>
|
</h4>
|
||||||
<div class="divider"></div>
|
|
||||||
<div class="course-list js-course-list-in-progress row"></div>
|
<div class="course-list js-course-list-in-progress row"></div>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
<% } %>
|
||||||
@@ -39,7 +37,6 @@
|
|||||||
<span class="status"><%- gettext('REMAINING COURSES') %></span>
|
<span class="status"><%- gettext('REMAINING COURSES') %></span>
|
||||||
<span class="count"><%- remainingCount %></span>
|
<span class="count"><%- remainingCount %></span>
|
||||||
</h4>
|
</h4>
|
||||||
<div class="divider"></div>
|
|
||||||
<div class="course-list js-course-list-remaining row"></div>
|
<div class="course-list js-course-list-remaining row"></div>
|
||||||
</div>
|
</div>
|
||||||
<% } %>
|
<% } %>
|
||||||
@@ -48,7 +45,6 @@
|
|||||||
<span class="status"><%- gettext('COMPLETED COURSES') %></span>
|
<span class="status"><%- gettext('COMPLETED COURSES') %></span>
|
||||||
<span class="count"><%- completedCount %></span>
|
<span class="count"><%- completedCount %></span>
|
||||||
</h4>
|
</h4>
|
||||||
<div class="divider"></div>
|
|
||||||
<% if (completedCount) { %>
|
<% if (completedCount) { %>
|
||||||
<div class="course-list js-course-list-completed row"></div>
|
<div class="course-list js-course-list-completed row"></div>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
@@ -59,6 +55,5 @@
|
|||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
<aside class="js-course-sidebar"></aside>
|
<aside class="js-program-sidebar program-sidebar"></aside>
|
||||||
</div>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user