Commit 638f80bb by Renzo Lucioni

Merge pull request #282 from edx/renzo/credit-detail

Update CAT detail view to support display of credit seats
parents 18f7315a e454e648
......@@ -18,7 +18,13 @@ define([
return Backbone.RelationalModel.extend({
urlRoot: '/api/v2/products/',
nestedAttributes: ['certificate_type', 'course_key', 'id_verification_required'],
nestedAttributes: [
'certificate_type',
'course_key',
'id_verification_required',
'credit_provider',
'credit_hours'
],
parse: function (response) {
// Un-nest the attributes
......
......@@ -43,5 +43,33 @@ define([
});
});
});
describe('filterSeats', function () {
it('should filter CourseSeats of the requested type', function () {
var honor = new HonorSeat(),
verified = new VerifiedSeat(),
professional = new ProfessionalSeat(),
expected = [
{
input: [honor, verified, professional],
output: {
filtered: [verified],
residual: [honor, professional]
}
},
{
input: [honor, verified, verified, professional],
output: {
filtered: [verified, verified],
residual: [honor, professional]
}
},
];
_.each(expected, function (expected) {
expect(CourseUtils.filterSeats(expected.input, 'verified')).toEqual(expected.output);
});
});
});
}
);
......@@ -85,6 +85,70 @@ define([
}
],
is_available_to_buy: false
},
{
id: 6,
url: 'http://ecommerce.local:8002/api/v2/products/6/',
structure: 'child',
product_class: 'Seat',
title: 'Seat in edX Demonstration Course with credit certificate (and ID verification)',
price: '150.00',
expires: null,
attribute_values: [
{
name: 'certificate_type',
value: 'credit'
},
{
name: 'course_key',
value: 'edX/DemoX/Demo_Course'
},
{
name: 'id_verification_required',
value: true
},
{
name: 'credit_provider',
value: 'Harvard'
},
{
name: 'credit_hours',
value: 1
}
],
is_available_to_buy: true
},
{
id: 5,
url: 'http://ecommerce.local:8002/api/v2/products/5/',
structure: 'child',
product_class: 'Seat',
title: 'Seat in edX Demonstration Course with credit certificate (and ID verification)',
price: '100.00',
expires: null,
attribute_values: [
{
name: 'certificate_type',
value: 'credit'
},
{
name: 'course_key',
value: 'edX/DemoX/Demo_Course'
},
{
name: 'id_verification_required',
value: true
},
{
name: 'credit_provider',
value: 'MIT'
},
{
name: 'credit_hours',
value: 2
}
],
is_available_to_buy: true
}
]
};
......@@ -103,9 +167,16 @@ define([
it('should list the course seats', function () {
var $seats = view.$el.find('.course-seat'),
$creditSeats = view.$el.find('.credit-seats .course-seat'),
products = _.where(data.products, {structure: 'child'});
expect($seats.length).toEqual(products.length);
expect($creditSeats.length).toEqual(
_.where(
products,
{title: 'Seat in edX Demonstration Course with credit certificate (and ID verification)'}
).length
);
// TODO Verify the rendered info matches the data
});
......
......@@ -55,7 +55,26 @@ define([
return _.sortBy(seatTypes, function (seatType) {
return this.seatSortObj[seatType];
}, this);
}
},
/**
* Given an array of CourseSeats and seatType, returns an object
* containing two arrays of CourseSeats, one containing seats of
* type seatType and a second containing all seats of other types.
*
* @param {CourseSeat[]} seats
* @param {String} seatType
* @returns {Object}
*/
filterSeats: function (seats, seatType) {
return _.groupBy(seats, function (seat) {
if (seat.getSeatType() === seatType) {
return 'filtered';
}
return 'residual';
});
},
};
}
);
......@@ -6,6 +6,7 @@ define([
'moment',
'text!templates/course_detail.html',
'text!templates/_course_seat.html',
'text!templates/_course_credit_seats.html',
'utils/course_utils'
],
function ($,
......@@ -15,6 +16,7 @@ define([
moment,
CourseDetailTemplate,
CourseSeatTemplate,
CourseCreditSeatsTemplate,
CourseUtils) {
'use strict';
......@@ -49,10 +51,16 @@ define([
seats = CourseUtils.orderSeatsForDisplay(this.model.seats()),
$seatHolder = $('.course-seats', this.$el);
_.each(seats, function (seat) {
seats = CourseUtils.filterSeats(seats, 'credit');
_.each(seats.residual, function (seat) {
html += _.template(CourseSeatTemplate)({seat: seat, moment: moment});
});
if (seats.filtered && seats.filtered.length > 0) {
html += _.template(CourseCreditSeatsTemplate)({creditSeats: seats.filtered, moment: moment});
}
$seatHolder.html(html);
}
});
......
......@@ -38,13 +38,13 @@
.course-seats {
.course-seat {
margin-bottom: spacing-vertical(mid-small);
}
.seat-type {
margin-bottom: 5px;
border-bottom: 1px solid $page-header-border-color;
padding-bottom: 3px;
font-weight: bold;
}
.seat-type {
margin-bottom: 5px;
border-bottom: 1px solid $page-header-border-color;
padding-bottom: 3px;
font-weight: bold;
}
}
}
......
<div class="row">
<div class="col-sm-12">
<div class="seat-type"><%= creditSeats[0].getSeatTypeDisplayName() %></div>
</div>
<div class="col-sm-4 col-sm-offset-4 seat-certificate-type">
<%= creditSeats[0].getCertificateDisplayName() %>
</div>
<div class="col-md-8">
<table class="table table-striped credit-seats">
<thead>
<tr>
<th><%= gettext('Credit Provider') %></th>
<th><%= gettext('Price (USD)') %></th>
<th><%= gettext('Credit Hours') %></th>
<th><%= gettext('Upgrade Deadline') %></th>
</tr>
</thead>
<tbody>
<% _.each(creditSeats, function (seat) { %>
<tr class="course-seat">
<td class="seat-credit-provider"><%= seat.get('credit_provider') %></td>
<td class="seat-price"><%= '$' + Number(seat.get('price')).toLocaleString() %></td>
<td class="seat-credit-hours"><%= seat.get('credit_hours') %></td>
<td class="seat-expires"><% var expires = seat.get('expires');
if (expires) {
print(moment.utc(expires).format('lll z'));
}
%></td>
</tr>
<% }); %>
</tbody>
</table>
</div>
<div class="col-sm-4 seat-additional-info"></div>
</div>
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