ECOM-7386 Added a program progress circle to program details page

This commit is contained in:
AlasdairSwan
2017-03-27 15:24:24 -04:00
parent 64372dd53e
commit a466b437f6
20 changed files with 762 additions and 109 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -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);

View File

@@ -1,47 +1,97 @@
(function(define) {
'use strict';
define(['backbone',
'jquery',
'underscore',
'gettext',
'js/learner_dashboard/views/explore_new_programs_view',
'js/learner_dashboard/views/certificate_view',
'text!../../../templates/learner_dashboard/sidebar.underscore'
],
function(
Backbone,
$,
_,
gettext,
NewProgramsView,
CertificateView,
sidebarTpl
) {
return Backbone.View.extend({
el: '.sidebar',
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),
tpl: _.template(sidebarTpl),
initialize: function(options) {
this.courseModel = options.courseModel || {};
this.certificateCollection = options.certificateCollection || [];
this.programCertificate = this.getProgramCertificate();
this.render();
},
initialize: function(data) {
this.context = data.context;
},
render: function() {
var data = $.extend({}, this.model.toJSON(), {
programCertificate: this.programCertificate ?
this.programCertificate.toJSON() : {}
});
render: function() {
this.$el.html(this.tpl(this.context));
this.postRender();
},
HtmlUtils.setHtml(this.$el, this.tpl(data));
this.postRender();
},
postRender: function() {
this.newProgramsView = new NewProgramsView({
context: this.context
});
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
}
});
this.newCertificateView = new CertificateView({
context: this.context
});
}
});
}
this.programProgressView = new ProgramProgressView({
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);

View File

@@ -34,6 +34,7 @@
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
@@ -61,7 +62,7 @@
remainingCount: remainingCount,
completedCount: completedCount
};
data = $.extend(data, this.options.programData);
data = $.extend(data, this.programModel.toJSON());
HtmlUtils.setHtml(this.$el, this.tpl(data));
this.postRender();
},
@@ -99,10 +100,12 @@
}).render();
}
new SidebarView({
el: '.sidebar',
context: this.options
}).render();
this.sidebarView = new SidebarView({
el: '.js-program-sidebar',
model: this.programModel,
courseModel: this.courseData,
certificateCollection: this.certificateCollection
});
}
});
}

File diff suppressed because one or more lines are too long

View File

@@ -743,6 +743,7 @@
'js/spec/learner_dashboard/program_card_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_sidebar_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_2017.js',

View File

@@ -6,5 +6,6 @@
@import 'elements/course-card';
@import 'elements/program-card';
@import 'elements-v2/icons';
@import 'elements/progress-circle';
@import 'views/program-details';
@import 'views/program-list';

View 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;
}
}

View File

@@ -47,8 +47,8 @@
}
.crumb {
@include float(left);
position: relative;
float: left;
font-size: font-size(x-small);
line-height: line-height(x-small);
color: palette(grayscale, dark);
@@ -78,14 +78,18 @@
}
// CSS for April 2017 version of Program Details Page
.program-details {
.window-wrap {
background-color: $white;
}
}
.program-details-wrapper {
.wrapper-footer {
@include clearfix();
clear: both;
}
}
.program-details-wrapper {
.program-details-header {
background-color: $light-gray4;
display: flex;
@@ -93,14 +97,20 @@
font-family: 'Open Sans';
font-weight: normal;
flex-wrap: wrap;
padding-top: 40px;
padding-bottom: 35px;
margin-left: 10px;
margin-right: 10px;
padding: 40px 10px 35px;
@media(min-width: $bp-screen-md) {
margin-left: 30px;
margin-right: 80px;
padding: {
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 {
@@ -111,7 +121,7 @@
}
.program-details-icon {
margin-left: 3px;
@include margin-left(3px);
margin-top: 10px;
height: auto;
@@ -177,7 +187,7 @@
margin-top: auto;
margin-bottom: auto;
@media(min-width: $bp-screen-md) {
margin: 10px 0 0 0;
@include margin-right(10px 0 0 0);
}
}
@@ -187,8 +197,8 @@
width: 30%;
.orgs .org-logo {
@include margin-left(2.5%);
width: 46.5%;
margin-left: 2.5%;
height: auto;
}
}
@@ -197,26 +207,49 @@
width: 25%;
}
}
}
.program-details-content {
width: 100%;
margin-bottom: 30px;
padding: 30px 10px;
@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 {
font-family: "Open Sans";
font-weight: bold;
text-transform: uppercase;
color: palette(primary, dark);
font-size: 0.9375em;
line-height: normal;
margin-top: 10px;
margin-bottom: 0;
padding-bottom: 5px;
border-bottom: 3px solid $divider-color;
margin: {
top: 10px;
bottom: 20px;
}
.status {
margin-right: 7px;
@include margin-right(7px);
}
}
@@ -225,27 +258,9 @@
}
.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 {
@include margin-left(15px);
font-size: 0.9375em;
margin-left: 15px;
width: 310px;
@media(min-width: $bp-screen-sm) {
width: auto;
@@ -264,11 +279,7 @@
}
.program-heading {
@media(min-width: $bp-screen-md) {
width: 70%;
}
width: 90%;
margin-top: 40px;
width: 100%;
margin-bottom: 40px;
.program-heading-title {
@@ -313,17 +324,17 @@
/* IE11 CSS styles */
@media(min-width: $bp-screen-md) and (-ms-high-contrast: none), (-ms-high-contrast: active) {
float: right;
@include float(right);
}
}
}
.select-choice {
@include margin-right(2px);
font-family: "Open Sans";
font-weight: bold;
font-size: 0.9375em;
color: palette(grayscale, base);
margin-top: 6px;
margin-right: 2px;
display: block;
@media(min-width: $bp-screen-md) {
@@ -339,21 +350,19 @@
}
}
.run-select {
@include margin-right(10px);
width: 95%;
@media(min-width: $bp-screen-sm) {
width: 300px;
}
height: 34px;
padding: 0;
margin-right: 10px;
}
}
.program-course-card {
@media(min-width: $bp-screen-md) {
width: 100%;
}
width: 100%;
padding: 15px;
margin-bottom: 10px;
@media(min-width: $bp-screen-md) {
@@ -363,12 +372,6 @@
.section {
display: flex;
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) {
flex-wrap: wrap;
@@ -400,9 +403,12 @@
.course-meta-container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
@media(min-width: $bp-screen-md) {
width: 100%;
flex-direction: row;
justify-content: space-between;
}
}
@@ -417,6 +423,10 @@
}
}
.course-certificate {
width: 100%;
}
.upgrade-message {
flex-wrap: wrap;
@@ -432,7 +442,7 @@
/* IE11 CSS styles */
@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;
}
}
}
}
.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 {
display: inline-block;
@@ -511,7 +624,7 @@
}
.expired-icon {
float: left;
@include float(left);
color: palette(primary, dark);
}
@@ -520,5 +633,12 @@
padding-left: 10px;
}
@media(min-width: $bp-screen-md) {
width: 100px;
}
@media(min-width: $bp-screen-lg) {
width: 120px;
}
}
}

View 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>

View File

@@ -20,7 +20,7 @@
</div>
</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 class="section action-msg-view"></div>

View File

@@ -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>

View File

@@ -1,6 +1,5 @@
<header class="js-program-header program-header full-width-banner"></header>
<div class="program-details-content">
<div class="js-program-progress-view"></div>
<section class="program-details-content">
<div class="program-heading">
<% if (inProgressCount === totalCount) { %>
<h3 class="program-heading-title"><%- gettext('Congratulations!') %></h3>
@@ -29,7 +28,6 @@
<span class="status"><%- gettext('COURSES IN PROGRESS') %></span>
<span class="count"><%- inProgressCount %></span>
</h4>
<div class="divider"></div>
<div class="course-list js-course-list-in-progress row"></div>
</div>
<% } %>
@@ -39,7 +37,6 @@
<span class="status"><%- gettext('REMAINING COURSES') %></span>
<span class="count"><%- remainingCount %></span>
</h4>
<div class="divider"></div>
<div class="course-list js-course-list-remaining row"></div>
</div>
<% } %>
@@ -48,7 +45,6 @@
<span class="status"><%- gettext('COMPLETED COURSES') %></span>
<span class="count"><%- completedCount %></span>
</h4>
<div class="divider"></div>
<% if (completedCount) { %>
<div class="course-list js-course-list-completed row"></div>
<% } else { %>
@@ -59,6 +55,5 @@
<% } %>
</div>
</div>
<aside class="js-course-sidebar"></aside>
</div>
</section>
<aside class="js-program-sidebar program-sidebar"></aside>