Commit 4911bf80 by Simon Chen Committed by GitHub

ECOM-4219 - Add the course states to course cards and make sure the display…

ECOM-4219 - Add the course states to course cards and make sure the display follows course states (#12844)

Please enter the commit message for your changes. Lines starting
parent aa078dfd
......@@ -15,19 +15,46 @@
}
},
getUnselectedRunMode: function(runModes) {
if(runModes && runModes.length > 0){
return {
course_image_url: runModes[0].course_image_url,
marketing_url: runModes[0].marketing_url,
is_enrollment_open: runModes[0].is_enrollment_open,
enrollment_open_date: runModes[0].enrollment_open_date
};
}
return {};
},
getRunMode: function(runModes){
//we should populate our model by looking at the run_modes
if (runModes.length > 0){
if(runModes.length === 1){
return runModes[0];
var enrolled_mode = _.findWhere(runModes, {is_enrolled: true}),
openEnrollmentRunModes = this.getEnrollableRunModes(),
desiredRunMode;
//we populate our model by looking at the run_modes
if (enrolled_mode){
// If we have a run_mode we are already enrolled in,
// return that one always
desiredRunMode = enrolled_mode;
} else if (openEnrollmentRunModes.length > 0){
if(openEnrollmentRunModes.length === 1){
desiredRunMode = openEnrollmentRunModes[0];
}else{
//We need to implement logic here to select the
//most relevant run mode for the student to enroll
return runModes[0];
desiredRunMode = this.getUnselectedRunMode(openEnrollmentRunModes);
}
}else{
return null;
}
desiredRunMode = this.getUnselectedRunMode(runModes);
}
return desiredRunMode;
},
getEnrollableRunModes: function(){
return _.where(this.context.run_modes,
{
is_enrollment_open: true,
is_enrolled: false,
is_course_ended: false
});
},
setActiveRunMode: function(runMode){
......@@ -38,18 +65,29 @@
course_key: runMode.course_key,
course_url: runMode.course_url || '',
display_name: this.context.display_name,
start_date: runMode.start_date,
end_date: runMode.end_date,
is_enrolled: runMode.is_enrolled,
is_enrollment_open: runMode.is_enrollment_open,
key: this.context.key,
marketing_url: runMode.marketing_url || '',
is_course_ended: runMode.is_course_ended,
mode_slug: runMode.mode_slug,
run_key: runMode.run_key,
start_date: runMode.start_date
enrollment_open_date: runMode.enrollment_open_date || '',
enrollable_run_modes: this.getEnrollableRunModes()
});
}
},
setUnselected: function(){
//This should be called to reset the model
//back to the unselected state
var unselectedMode = this.getUnselectedRunMode(
this.get('enrollable_run_modes'));
this.setActiveRunMode(unselectedMode);
},
updateRun: function(runKey){
var selectedRun = _.findWhere(this.get('run_modes'), {run_key: runKey});
if (selectedRun){
......
......@@ -11,7 +11,7 @@
return Backbone.Model.extend({
defaults: {
course_id: '',
optIn: false,
optIn: false
}
});
}
......
......@@ -29,17 +29,14 @@
this.enrollModel = options.enrollModel;
this.urlModel = options.urlModel;
this.render();
if(this.urlModel){
if (this.urlModel){
this.trackSelectionUrl = this.urlModel.get('track_selection_url');
}
},
render: function() {
var filledTemplate;
if (this.$parentEl &&
this.enrollModel &&
this.model.get('course_key')){
if (this.$parentEl && this.enrollModel){
filledTemplate = this.tpl(this.model.toJSON());
HtmlUtils.setHtml(this.$el, filledTemplate);
HtmlUtils.setHtml(this.$parentEl, HtmlUtils.HTML(this.$el));
......@@ -48,7 +45,9 @@
handleEnroll: function(){
//Enrollment click event handled here
if (!this.model.get('is_enrolled')){
if (!this.model.get('course_key')){
this.$('.select-error').css('visibility','visible');
} else if (!this.model.get('is_enrolled')){
// actually enroll
this.enrollModel.save({
course_id: this.model.get('course_key')
......@@ -65,6 +64,9 @@
runKey = $(event.target).val();
if (runKey){
this.model.updateRun(runKey);
} else {
//Set back the unselected states
this.model.setUnselected();
}
}
},
......@@ -74,7 +76,7 @@
if (this.trackSelectionUrl) {
// Go to track selection page
this.redirect( this.trackSelectionUrl + courseKey );
}else{
} else {
this.model.set({
is_enrolled: true
});
......
......@@ -20,7 +20,7 @@ define([
},
run_modes: [{
start_date: 'Apr 25, 2016',
end_date: 'Jun 13, 2016',
end_date: 'Jun 13, 2019',
course_key: 'course-v1:ANUx+ANU-ASTRO1x+3T2015',
course_url: 'http://localhost:8000/courses/course-v1:edX+DemoX+Demo_Course/info',
marketing_url: 'https://www.edx.org/course/astrophysics-exploring',
......@@ -29,12 +29,15 @@ define([
run_key: '2T2016',
course_started: true,
is_enrolled: true,
certificate_url: ''
is_course_ended: false,
is_enrollment_open: true,
certificate_url: '',
enrollment_open_date: 'Mar 03, 2016'
}]
},
setupView = function(data, isEnrolled){
context.run_modes[0].is_enrolled = isEnrolled;
data.run_modes[0].is_enrolled = isEnrolled;
setFixtures('<div class="course-card card"></div>');
courseCardModel = new CourseCardModel(data);
view = new CourseCardView({
......@@ -94,6 +97,28 @@ define([
expect(view.$('.certificate-status').length).toEqual(1);
expect(view.$('.certificate-status .cta-secondary').attr('href')).toEqual(certUrl);
});
it('should render the course card with coming soon', function(){
view.remove();
context.run_modes[0].is_enrollment_open = false;
setupView(context, false);
expect(view.$('.header-img').attr('src')).toEqual(context.run_modes[0].course_image_url);
expect(view.$('.course-details .course-title').text().trim()).toEqual(context.display_name);
expect(view.$('.course-details .course-title-link').length).toBe(0);
expect(view.$('.course-details .course-text .course-key').html()).toEqual(context.key);
expect(view.$('.course-details .course-text .run-period').length).toBe(0);
expect(view.$('.no-action-message').text().trim()).toBe('Coming Soon');
expect(view.$('.enroll-open-date').text().trim())
.toBe(context.run_modes[0].enrollment_open_date);
});
it('should render if enrollment_open_date is not provided', function(){
view.remove();
context.run_modes[0].is_enrollment_open = true;
delete context.run_modes[0].enrollment_open_date;
setupView(context, false);
validateCourseInfoDisplay();
});
});
}
);
......@@ -21,9 +21,11 @@ define([
course_url: 'http://localhost:8000/courses/course-v1:edX+DemoX+Demo_Course/info',
course_image_url: 'http://test.com/image1',
marketing_url: 'http://test.com/image2',
is_course_ended: false,
mode_slug: 'audit',
run_key: '2T2016',
is_enrolled: false
is_enrolled: false,
is_enrollment_open: true
}],
multiRunModeList = [{
start_date: 'May 21, 2015',
......@@ -33,8 +35,10 @@ define([
course_image_url: 'http://test.com/run_2_image_1',
marketing_url: 'http://test.com/run_2_image_2',
mode_slug: 'verified',
is_course_ended: false,
run_key: '1T2015',
is_enrolled: false
is_enrolled: false,
is_enrollment_open: true,
},{
start_date: 'Sep 22, 2015',
end_date: 'Dec 28, 2015',
......@@ -42,9 +46,11 @@ define([
course_url: 'http://localhost:8000/courses/course-v1:edX+DemoX+Demo_Course/info',
course_image_url: 'http://test.com/run_3_image_1',
marketing_url: 'http://test.com/run_3_image_2',
is_course_ended: false,
mode_slug: 'verified',
run_key: '2T2015',
is_enrolled: false
is_enrolled: false,
is_enrollment_open: true
}],
context = {
display_name: 'Edx Demo course',
......@@ -117,13 +123,14 @@ define([
it('should render run selection drop down if mulitple run available', function(){
setupView(multiRunModeList);
expect(view.$('.run-select').length).toBe(1);
expect(view.$('.run-select').val()).toEqual(multiRunModeList[0].run_key);
expect(view.$('.run-select').val()).toEqual('');
expect(view.$('.run-select option').length).toBe(3);
});
it('should switch run context if dropdown selection changed', function(){
setupView(multiRunModeList);
spyOn(courseCardModel, 'updateRun').and.callThrough();
expect(view.$('.run-select').val()).toEqual(multiRunModeList[0].run_key);
expect(view.$('.run-select').val()).toEqual('');
view.$('.run-select').val(multiRunModeList[1].run_key);
view.$('.run-select').trigger("change");
expect(view.$('.run-select').val()).toEqual(multiRunModeList[1].run_key);
......
......@@ -11,7 +11,7 @@
padding: $baseline/2 $baseline;
}
.course-image-link {
.course-image-container{
@include float(left);
.header-img {
......@@ -47,8 +47,26 @@
margin-bottom: $baseline/2;
text-transform: uppercase;
}
.run-select-container {
.select-error{
color: palette(error, base);
margin-bottom: $baseline/4;
font-size: font-size(small);
visibility: hidden;
}
.no-action-message{
margin-bottom: $baseline/2;
color: palette(grayscale, black);
font-size: font-size(large);
text-align: center;
}
.enrollment-opens{
text-align: center;
margin-bottom: $baseline/2;
}
.enroll-open-date{
text-align: center;
}
.run-select-container{
margin-bottom: $baseline;
.run-select {
......@@ -61,8 +79,8 @@
text-align: center;
}
.view-course-link {
width: $baseline*10;
.view-course-link{
min-width: $baseline*10;
text-align: center;
}
}
......
<div class="section">
<div class="course-meta-container col-12 md-col-8 sm-col-12">
<a href="<%- course_url %>" class="course-image-link">
<img
class="header-img"
src="<%- course_image_url %>"
alt="<%= interpolate(gettext('%(courseName)s Home Page.'), {courseName: display_name}, true) %>"/>
</a>
<div class="course-image-container">
<% if (course_url){ %>
<a href="<%- course_url %>" class="course-image-link">
<img
class="header-img"
src="<%- course_image_url %>"
alt="<%= interpolate(gettext('%(courseName)s Home Page.'), {courseName: display_name}, true) %>"/>
</a>
<% } else { %>
<img
class="header-img"
src="<%- course_image_url %>"
alt="" />
<% } %>
</div>
<div class="course-details">
<h3 class="course-title">
<a href="<%- course_url %>" class="course-title-link">
<% if (course_url){ %>
<a href="<%- course_url %>" class="course-title-link">
<%- display_name %>
</a>
<% }else{ %>
<%- display_name %>
</a>
<% } %>
</h3>
<div class="course-text">
<span class="run-period"><%- start_date %> - <%- end_date %></span>
-
<% if (start_date && end_date){ %>
<span class="run-period"><%- start_date %> - <%- end_date %></span>
-
<% } %>
<span class="course-key"><%- key %></span>
</div>
</div>
......
<% if (is_enrolled){ %>
<div class="enrollment-info"><%- gettext('enrolled') %></div>
<a href="<%- course_url %>" class="btn-neutral btn view-course-link">
<%- gettext('View Course') %>
</a>
<% if (is_enrollment_open || is_course_ended){ %>
<a href="<%- course_url %>" class="btn-neutral btn view-course-link">
<% if (is_enrollment_open){ %>
<%- gettext('View Course') %>
<% } else if (course_ended){ %>
<%- gettext('View Archived Course') %>
<% } %>
</a>
<% } %>
<% }else{ %>
<div class="enrollment-info"><%- gettext('not enrolled') %></div>
<% if (run_modes.length > 1){ %>
<div class="run-select-container">
<label class="sr-only" for="select-<%- course_key %>-run">Select Course Run</label>
<select id="select-<%- course_key %>-run" class="run-select" autocomplete="off">
<% _.each (run_modes, function(runMode){ %>
<option
value="<%- runMode.run_key %>"
<% if(run_key === runMode.run_key){ %>
selected="selected"
<% }%>
>
<%= interpolate(
gettext('Starts %(start)s'),
{ start: runMode.start_date },
true)
%>
<% if (enrollable_run_modes.length > 0){ %>
<div class="enrollment-info"><%- gettext('not enrolled') %></div>
<% if (enrollable_run_modes.length > 1){ %>
<div class="run-select-container">
<div class="select-error">
<%- gettext('Please select a course date') %>
</div>
<label class="sr-only" for="select-<%- course_key %>-run">
<%- gettext('Select Course Run') %>
</label>
<select id="select-<%- course_key %>-run" class="run-select" autocomplete="off">
<option value="" selected="selected">
<%- gettext('Choose Course Date') %>
</option>
<% }); %>
</select>
<% _.each (enrollable_run_modes, function(runMode){ %>
<option
value="<%- runMode.run_key %>"
<% if (run_key === runMode.run_key){ %>
selected="selected"
<% }%>
>
<%= interpolate(
gettext('Starts %(start)s'),
{ start: runMode.start_date },
true)
%>
</option>
<% }); %>
</select>
</div>
<% } %>
<button type="button" class="btn-brand btn cta-primary enroll-button">
<%- gettext('Enroll Now') %>
</button>
<% } else {%>
<div class="no-action-message">
<%- gettext('Coming Soon') %>
</div>
<div class="enrollment-opens">
<%- gettext('Enrollment Opens') %>
</div>
<div class="enroll-open-date">
<%- enrollment_open_date %>
</div>
<% } %>
<button type="button" class="btn-brand btn cta-primary enroll-button">
<%- gettext('Enroll Now') %>
</button>
<% } %>
......@@ -702,7 +702,6 @@ class TestSupplementProgramData(ProgramsApiConfigMixin, ModuleStoreTestCase):
def _assert_supplemented(self, actual, **kwargs):
"""DRY helper used to verify that program data is extended correctly."""
course_overview = CourseOverview.get_from_id(self.course.id) # pylint: disable=no-member
run_mode = dict(
factories.RunMode(
course_key=unicode(self.course.id), # pylint: disable=no-member
......@@ -710,6 +709,7 @@ class TestSupplementProgramData(ProgramsApiConfigMixin, ModuleStoreTestCase):
course_image_url=course_overview.course_image_url,
start_date=self.course.start.strftime(self.human_friendly_format),
end_date=self.course.end.strftime(self.human_friendly_format),
is_course_ended=self.course.end < timezone.now(),
is_enrolled=False,
is_enrollment_open=True,
marketing_url='',
......@@ -745,7 +745,15 @@ class TestSupplementProgramData(ProgramsApiConfigMixin, ModuleStoreTestCase):
data = utils.supplement_program_data(self.program, self.user)
self._assert_supplemented(data, is_enrollment_open=is_enrollment_open)
if is_enrollment_open:
self._assert_supplemented(
data,
is_enrollment_open=is_enrollment_open)
else:
self._assert_supplemented(
data,
is_enrollment_open=is_enrollment_open,
enrollment_open_date=self.course.enrollment_start.strftime(self.human_friendly_format))
@ddt.data(True, False)
@mock.patch(UTILS_MODULE + '.certificate_api.certificate_downloadable_status')
......@@ -792,3 +800,11 @@ class TestSupplementProgramData(ProgramsApiConfigMixin, ModuleStoreTestCase):
mock_get_organization_by_short_name.return_value = {'logo': None}
data = utils.supplement_program_data(self.program, self.user)
self.assertEqual(data['organizations'][0].get('img'), None)
@ddt.data(-1, 0, 1)
def test_course_course_ended(self, days_offset):
self.course.end = timezone.now() + datetime.timedelta(days=days_offset)
self.course = self.update_course(self.course, self.user.id) # pylint: disable=no-member
data = utils.supplement_program_data(self.program, self.user)
self._assert_supplemented(data)
......@@ -348,6 +348,7 @@ def supplement_program_data(program_data, user):
end_date = course_overview.end or datetime.datetime.max.replace(tzinfo=pytz.UTC)
run_mode['start_date'] = start_date.strftime(human_friendly_format)
run_mode['end_date'] = end_date.strftime(human_friendly_format)
run_mode['is_course_ended'] = end_date < timezone.now()
run_mode['is_enrolled'] = CourseEnrollment.is_enrolled(user, course_key)
......@@ -355,6 +356,9 @@ def supplement_program_data(program_data, user):
enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=pytz.UTC)
is_enrollment_open = enrollment_start <= timezone.now() < enrollment_end
run_mode['is_enrollment_open'] = is_enrollment_open
if not is_enrollment_open:
# Only render this enrollment open date if the enrollment open is in the future
run_mode['enrollment_open_date'] = enrollment_start.strftime(human_friendly_format)
# TODO: Currently unavailable on LMS.
run_mode['marketing_url'] = ''
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment