Commit cb5cc595 by asadiqbal Committed by Asad Iqbal

UI changes on basket page.

parent 332fe1d2
...@@ -17,3 +17,4 @@ Awais Jibran <awaisdar001@gmail.com> ...@@ -17,3 +17,4 @@ Awais Jibran <awaisdar001@gmail.com>
Bill DeRusha <bill@edx.org> Bill DeRusha <bill@edx.org>
Ivan Ivić <iivic@edx.org> Ivan Ivić <iivic@edx.org>
Saleem Latif <saleem_ee@hotmail.com> Saleem Latif <saleem_ee@hotmail.com>
Asad Iqbal <aiqbal@edx.org>
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-15 09:43-0500\n" "POT-Creation-Date: 2018-03-07 13:03+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -166,39 +166,39 @@ msgstr "" ...@@ -166,39 +166,39 @@ msgstr ""
msgid "Problem occurred during checkout. Please contact support." msgid "Problem occurred during checkout. Please contact support."
msgstr "" msgstr ""
#: ecommerce/static/js/pages/basket_page.js:99 #: ecommerce/static/js/pages/basket_page.js:100
msgid "This field is required" msgid "This field is required"
msgstr "" msgstr ""
#: ecommerce/static/js/pages/basket_page.js:134 #: ecommerce/static/js/pages/basket_page.js:135
msgid "Invalid card number" msgid "Invalid card number"
msgstr "" msgstr ""
#: ecommerce/static/js/pages/basket_page.js:136 #: ecommerce/static/js/pages/basket_page.js:137
msgid "Unsupported card type" msgid "Unsupported card type"
msgstr "" msgstr ""
#: ecommerce/static/js/pages/basket_page.js:138 #: ecommerce/static/js/pages/basket_page.js:139
msgid "Invalid security number" msgid "Invalid security number"
msgstr "" msgstr ""
#: ecommerce/static/js/pages/basket_page.js:143 #: ecommerce/static/js/pages/basket_page.js:144
msgid "Invalid month" msgid "Invalid month"
msgstr "" msgstr ""
#: ecommerce/static/js/pages/basket_page.js:145 #: ecommerce/static/js/pages/basket_page.js:146
msgid "Invalid year" msgid "Invalid year"
msgstr "" msgstr ""
#: ecommerce/static/js/pages/basket_page.js:147 #: ecommerce/static/js/pages/basket_page.js:148
msgid "Card expired" msgid "Card expired"
msgstr "" msgstr ""
#: ecommerce/static/js/pages/basket_page.js:465 #: ecommerce/static/js/pages/basket_page.js:486
msgid "<Choose state/province>" msgid "<Choose state/province>"
msgstr "" msgstr ""
#: ecommerce/static/js/pages/basket_page.js:466 #: ecommerce/static/js/pages/basket_page.js:487
msgid "State/Province (required)" msgid "State/Province (required)"
msgstr "" msgstr ""
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-12-15 09:43-0500\n" "POT-Creation-Date: 2018-03-07 13:03+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
......
...@@ -47,6 +47,23 @@ def get_lms_courseware_url(course_run_id): ...@@ -47,6 +47,23 @@ def get_lms_courseware_url(course_run_id):
return get_lms_url('courses/{}/info'.format(course_run_id)) return get_lms_url('courses/{}/info'.format(course_run_id))
def get_lms_course_about_url(course_key):
"""
Return the courseware about URL for the given course key.
Returns:
string: The course about page URL.
"""
return get_lms_url('courses/{}/about'.format(course_key))
def get_lms_explore_courses_url():
"""
Return the explore courses url.
"""
return get_lms_url('courses')
def get_lms_dashboard_url(): def get_lms_dashboard_url():
site_configuration = _get_site_configuration() site_configuration = _get_site_configuration()
return site_configuration.student_dashboard_url return site_configuration.student_dashboard_url
......
...@@ -19,7 +19,7 @@ from requests.exceptions import ConnectionError, Timeout ...@@ -19,7 +19,7 @@ from requests.exceptions import ConnectionError, Timeout
from slumber.exceptions import SlumberBaseException from slumber.exceptions import SlumberBaseException
from ecommerce.core.exceptions import SiteConfigurationError from ecommerce.core.exceptions import SiteConfigurationError
from ecommerce.core.url_utils import get_lms_url from ecommerce.core.url_utils import get_lms_course_about_url, get_lms_url
from ecommerce.courses.utils import get_certificate_type_display_value, get_course_info_from_catalog from ecommerce.courses.utils import get_certificate_type_display_value, get_course_info_from_catalog
from ecommerce.enterprise.entitlements import get_enterprise_code_redemption_redirect from ecommerce.enterprise.entitlements import get_enterprise_code_redemption_redirect
from ecommerce.enterprise.utils import CONSENT_FAILED_PARAM, get_enterprise_customer_from_voucher, has_enterprise_offer from ecommerce.enterprise.utils import CONSENT_FAILED_PARAM, get_enterprise_customer_from_voucher, has_enterprise_offer
...@@ -202,6 +202,26 @@ class BasketSummaryView(BasketView): ...@@ -202,6 +202,26 @@ class BasketSummaryView(BasketView):
except (ConnectionError, SlumberBaseException, Timeout): except (ConnectionError, SlumberBaseException, Timeout):
logger.exception('Failed to retrieve data from Discovery Service for course [%s].', course_key) logger.exception('Failed to retrieve data from Discovery Service for course [%s].', course_key)
if self.request.basket.num_items == 1 and product.is_enrollment_code_product:
course_key = CourseKey.from_string(product.attr.course_key)
course_about = get_lms_course_about_url(course_key=course_key)
messages.info(
self.request,
_(
'{strong_start}Purchasing access just for yourself?{strong_end}{paragraph_start}If you are '
'purchasing a single code for someone else, please continue with checkout. However, if you are the '
'learner {link_start}go back{link_end} to enroll directly.{paragraph_end}'
).format(
strong_start='<strong>',
strong_end='</strong>',
paragraph_start='<p>',
paragraph_end='</p>',
link_start='<a href="{course_about}">'.format(course_about=course_about),
link_end='</a>'
),
extra_tags='safe'
)
return { return {
'product_title': course_name, 'product_title': course_name,
'course_key': course_key, 'course_key': course_key,
...@@ -259,8 +279,26 @@ class BasketSummaryView(BasketView): ...@@ -259,8 +279,26 @@ class BasketSummaryView(BasketView):
line_data = self._get_course_data(line.product) line_data = self._get_course_data(line.product)
show_voucher_form = False show_voucher_form = False
order_details_msg = _( order_details_msg = _(
'You will receive an email at {user_email} with your enrollment code(s).' '{paragraph_start}By purchasing, you and your organization agree to the following terms:'
).format(user_email=self.request.user.email) '{paragraph_end} {ul_start} {li_start}Each code is valid for the one course covered and can be '
'used only one time.{li_end} {li_start}You are responsible for distributing codes to your learners.'
'{li_end} {li_start}Each code will expire in one year from date of purchase or, if earlier, once '
'the course is closed.{li_end} {li_start}If a course is not designated as self-paced, you should '
'confirm that a course run is available before expiration. {li_end} {li_start}You may not resell '
'codes to third parties.{li_end} {ul_end} {strong_start}All sales final. No refunds.{strong_end} '
'{paragraph_start}You will receive an email at {user_email} with your enrollment code(s). '
'{paragraph_end}'
).format(
strong_start='<strong>',
strong_end='</strong>',
paragraph_start='<p>',
paragraph_end='</p>',
ul_start='<ul>',
li_start='<li>',
li_end='</li>',
ul_end='</ul>',
user_email=self.request.user.email
)
else: else:
line_data = { line_data = {
'product_title': line.product.title, 'product_title': line.product.title,
...@@ -401,14 +439,16 @@ class BasketSummaryView(BasketView): ...@@ -401,14 +439,16 @@ class BasketSummaryView(BasketView):
) )
except ValueError: except ValueError:
total_benefit = None total_benefit = None
num_of_items = self.request.basket.num_items
context.update({ context.update({
'formset_lines_data': zip(formset, lines_data), 'formset_lines_data': zip(formset, lines_data),
'free_basket': context['order_total'].incl_tax == 0, 'free_basket': context['order_total'].incl_tax == 0,
'homepage_url': get_lms_url(''), 'homepage_url': get_lms_url(''),
'min_seat_quantity': 1, 'min_seat_quantity': 1,
'max_seat_quantity': 100,
'payment_processors': payment_processors, 'payment_processors': payment_processors,
'total_benefit': total_benefit 'total_benefit': total_benefit,
'line_price': (self.request.basket.total_incl_tax_excl_discounts / num_of_items) if num_of_items > 0 else 0
}) })
return context return context
......
...@@ -12,7 +12,12 @@ from django.views.generic import RedirectView, TemplateView ...@@ -12,7 +12,12 @@ from django.views.generic import RedirectView, TemplateView
from oscar.apps.checkout.views import * # pylint: disable=wildcard-import, unused-wildcard-import from oscar.apps.checkout.views import * # pylint: disable=wildcard-import, unused-wildcard-import
from oscar.core.loading import get_class, get_model from oscar.core.loading import get_class, get_model
from ecommerce.core.url_utils import get_lms_courseware_url, get_lms_dashboard_url, get_lms_program_dashboard_url from ecommerce.core.url_utils import (
get_lms_courseware_url,
get_lms_dashboard_url,
get_lms_explore_courses_url,
get_lms_program_dashboard_url
)
from ecommerce.enterprise.utils import has_enterprise_offer from ecommerce.enterprise.utils import has_enterprise_offer
from ecommerce.extensions.checkout.exceptions import BasketNotFreeError from ecommerce.extensions.checkout.exceptions import BasketNotFreeError
from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin
...@@ -183,6 +188,12 @@ class ReceiptResponseView(ThankYouView): ...@@ -183,6 +188,12 @@ class ReceiptResponseView(ThankYouView):
}) })
context.update(self.get_order_dashboard_context(order)) context.update(self.get_order_dashboard_context(order))
context.update(self.get_order_verification_context(order)) context.update(self.get_order_verification_context(order))
context.update({
'explore_courses_url': get_lms_explore_courses_url(),
'has_enrollment_code_product': any(
line.product.is_enrollment_code_product for line in order.basket.all_lines()
)
})
return context return context
def get_object(self): def get_object(self):
......
...@@ -44,6 +44,9 @@ class PaymentForm(forms.Form): ...@@ -44,6 +44,9 @@ class PaymentForm(forms.Form):
def __init__(self, user, request, *args, **kwargs): def __init__(self, user, request, *args, **kwargs):
super(PaymentForm, self).__init__(*args, **kwargs) super(PaymentForm, self).__init__(*args, **kwargs)
self.request = request self.request = request
self.basket_has_enrollment_code_product = any(
line.product.is_enrollment_code_product for line in self.request.basket.all_lines()
)
update_basket_queryset_filter(self, user) update_basket_queryset_filter(self, user)
self.helper = FormHelper(self) self.helper = FormHelper(self)
...@@ -100,6 +103,20 @@ class PaymentForm(forms.Form): ...@@ -100,6 +103,20 @@ class PaymentForm(forms.Form):
self.fields[bound_field.name].label = _('{label} (required)').format(label=bound_field.label) self.fields[bound_field.name].label = _('{label} (required)').format(label=bound_field.label)
bound_field.field.widget.attrs['required'] = 'required' bound_field.field.widget.attrs['required'] = 'required'
if self.basket_has_enrollment_code_product and 'organization' not in self.fields:
# If basket has any enrollment code items then we will add an organization
# field next to "last_name."
self.fields['organization'] = forms.CharField(max_length=60, label=_('Organization (required)'))
organization_div = Div(
Div(
Div('organization'),
HTML('<p class="help-block"></p>'),
css_class='form-item col-md-6'
),
css_class='row'
)
self.helper.layout.fields.insert(self.fields.keys().index('last_name') + 1, organization_div)
basket = forms.ModelChoiceField( basket = forms.ModelChoiceField(
queryset=Basket.objects.all(), queryset=Basket.objects.all(),
widget=forms.HiddenInput(), widget=forms.HiddenInput(),
...@@ -108,16 +125,16 @@ class PaymentForm(forms.Form): ...@@ -108,16 +125,16 @@ class PaymentForm(forms.Form):
'invalid_choice': _('There was a problem retrieving your basket. Refresh the page to try again.'), 'invalid_choice': _('There was a problem retrieving your basket. Refresh the page to try again.'),
} }
) )
first_name = forms.CharField(max_length=60, label=_('First Name')) first_name = forms.CharField(max_length=60, label=_('First Name (required)'))
last_name = forms.CharField(max_length=60, label=_('Last Name')) last_name = forms.CharField(max_length=60, label=_('Last Name (required)'))
address_line1 = forms.CharField(max_length=60, label=_('Address'), required=False) address_line1 = forms.CharField(max_length=60, label=_('Address (required)'), required=False)
address_line2 = forms.CharField(max_length=29, required=False, label=_('Suite/Apartment Number')) address_line2 = forms.CharField(max_length=29, required=False, label=_('Suite/Apartment Number'))
city = forms.CharField(max_length=32, label=_('City')) city = forms.CharField(max_length=32, label=_('City (required)'))
# max_length for state field is set to default 60, if it needs to be changed, # max_length for state field is set to default 60, if it needs to be changed,
# the equivalent (maxlength) attribute in the basket page JS code needs to be changed too. # the equivalent (maxlength) attribute in the basket page JS code needs to be changed too.
state = forms.CharField(max_length=60, required=False, label=_('State/Province')) state = forms.CharField(max_length=60, required=False, label=_('State/Province'))
postal_code = forms.CharField(max_length=10, required=False, label=_('Zip/Postal Code')) postal_code = forms.CharField(max_length=10, required=False, label=_('Zip/Postal Code'))
country = forms.ChoiceField(choices=country_choices, label=_('Country')) country = forms.ChoiceField(choices=country_choices, label=_('Country (required)'))
def clean_basket(self): def clean_basket(self):
basket = self.cleaned_data['basket'] basket = self.cleaned_data['basket']
......
...@@ -2,12 +2,19 @@ from __future__ import unicode_literals ...@@ -2,12 +2,19 @@ from __future__ import unicode_literals
import ddt import ddt
import pycountry import pycountry
from oscar.core.loading import get_model
from oscar.test import factories
from waffle.models import Switch from waffle.models import Switch
from ecommerce.core.constants import ENROLLMENT_CODE_PRODUCT_CLASS_NAME, ENROLLMENT_CODE_SWITCH
from ecommerce.core.tests import toggle_switch
from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.extensions.payment.forms import PaymentForm from ecommerce.extensions.payment.forms import PaymentForm
from ecommerce.extensions.test.factories import create_basket from ecommerce.extensions.test.factories import create_basket
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
Product = get_model('catalogue', 'Product')
@ddt.ddt @ddt.ddt
class PaymentFormTests(TestCase): class PaymentFormTests(TestCase):
...@@ -16,6 +23,29 @@ class PaymentFormTests(TestCase): ...@@ -16,6 +23,29 @@ class PaymentFormTests(TestCase):
self.user = self.create_user() self.user = self.create_user()
self.basket = create_basket(owner=self.user) self.basket = create_basket(owner=self.user)
def create_basket_and_add_product(self, product):
basket = factories.BasketFactory(owner=self.user, site=self.site)
basket.add_product(product, 1)
return basket
def prepare_course_seat_and_enrollment_code(self, seat_type='verified', id_verification=False):
"""Helper function that creates a new course, enables enrollment codes and creates a new
seat and enrollment code for it.
Args:
seat_type (str): Seat/certification type.
is_verification (bool): Whether or not id verification is required for the seat.
Returns:
The newly created course, seat and enrollment code.
"""
course = CourseFactory()
toggle_switch(ENROLLMENT_CODE_SWITCH, True)
self.site.siteconfiguration.enable_enrollment_codes = True
self.site.siteconfiguration.save()
seat = course.create_or_update_seat(seat_type, id_verification, 10, self.partner, create_enrollment_code=True)
enrollment_code = Product.objects.get(product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME)
return course, seat, enrollment_code
def _generate_data(self, **kwargs): def _generate_data(self, **kwargs):
data = { data = {
'basket': self.basket.id, 'basket': self.basket.id,
...@@ -132,3 +162,45 @@ class PaymentFormTests(TestCase): ...@@ -132,3 +162,45 @@ class PaymentFormTests(TestCase):
actual = list(form.fields['country'].choices) actual = list(form.fields['country'].choices)
actual.pop(0) # Remove the "Choose country" placeholder actual.pop(0) # Remove the "Choose country" placeholder
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_organization_field_in_form(self):
""" Verify the field 'organization' is present in the form when the basket has an enrollment code product. """
__, __, enrollment_code = self.prepare_course_seat_and_enrollment_code()
basket = self.create_basket_and_add_product(enrollment_code)
self.request.basket = basket
data = {
'basket': basket.id,
'first_name': 'Test',
'last_name': 'User',
'address_line1': '141 Portland Ave.',
'address_line2': 'Floor 9',
'city': 'Cambridge',
'state': 'MA',
'postal_code': '02139',
'country': 'US',
}
form = PaymentForm(user=self.user, data=data, request=self.request)
self.assertTrue('organization' in form.fields)
def test_organization_field_not_in_form(self):
"""
Verify the field 'organization' is not present in the form when the basket does not have an enrollment
code product.
"""
course = CourseFactory()
product1 = course.create_or_update_seat("Verified", True, 0, self.partner)
basket = self.create_basket_and_add_product(product1)
self.request.basket = basket
data = {
'basket': basket.id,
'first_name': 'Test',
'last_name': 'User',
'address_line1': '141 Portland Ave.',
'address_line2': 'Floor 9',
'city': 'Cambridge',
'state': 'MA',
'postal_code': '02139',
'country': 'US',
}
form = PaymentForm(user=self.user, data=data, request=self.request)
self.assertFalse('organization' in form.fields)
/* jshint -W065 */
define([ define([
'jquery', 'jquery',
'underscore', 'underscore',
...@@ -79,6 +80,7 @@ define([ ...@@ -79,6 +80,7 @@ define([
'input[name=first_name]', 'input[name=first_name]',
'input[name=last_name]', 'input[name=last_name]',
'input[name=city]', 'input[name=city]',
'input[name=organization]',
'select[name=country]' 'select[name=country]'
], ],
countriesWithRequiredStateAndPostalCodeValues = ['US', 'CA'], countriesWithRequiredStateAndPostalCodeValues = ['US', 'CA'],
...@@ -148,6 +150,24 @@ define([ ...@@ -148,6 +150,24 @@ define([
} }
}, },
showErrorState: function(e, msg) {
e.preventDefault();
$('#input-quantity-field').addClass('error-state');
$('div#error-msg').text(gettext(msg));
},
validateQuantity: function(e) {
var inputQuantity = $('#input-quantity-field').val();
var quantity = isNaN(parseInt(inputQuantity, 10)) ? '' : parseInt(inputQuantity, 10);
if (quantity === '') {
this.showErrorState(e, 'Please enter a quantity from 1 to 100.');
} else if (quantity > 100) {
this.showErrorState(e, 'Quantity must be less than or equal to 100.');
} else if (quantity < 1) {
this.showErrorState(e, 'Quantity must be greater than or equal to 1.');
}
},
detectCreditCard: function() { detectCreditCard: function() {
var card, var card,
$input = $('#card-number'), $input = $('#card-number'),
...@@ -483,6 +503,10 @@ define([ ...@@ -483,6 +503,10 @@ define([
BasketPage.detectCreditCard(); BasketPage.detectCreditCard();
}); });
$('#quantity-update').on('click', function(e) {
BasketPage.validateQuantity(e);
});
$('#payment-button').click(function(e) { $('#payment-button').click(function(e) {
_.each($('.help-block'), function(errorMsg) { _.each($('.help-block'), function(errorMsg) {
$(errorMsg).empty(); // Clear existing validation error messages. $(errorMsg).empty(); // Clear existing validation error messages.
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
<div aria-labelledby="order-details-region"></div> <div aria-labelledby="order-details-region"></div>
<div class="spinner"> <div class="spinner">
<input class="quantity" id="id_form-0-quantity" min="1" max="10" name="form-0-quantity" type="number" value="1"> <input class="quantity" id="input-quantity-field" min="1" max="10" name="form-0-quantity" type="number" value="1">
<div class="input-group-btn-vertical"> <div class="input-group-btn-vertical">
<button class="btn btn-primary" type="button"> <button class="btn btn-primary" type="button">
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
</button> </button>
</div> </div>
</div> </div>
<button id="quantity-update" class="update-button quantity-update" type="submit" data-loading-text="Updating...">Update</button>
<div id="error-msg"></div>
<div id="voucher_form_container"> <div id="voucher_form_container">
<input id="id_code"> <input id="id_code">
......
...@@ -153,6 +153,30 @@ define([ ...@@ -153,6 +153,30 @@ define([
expect($quantity.first().val()).toEqual('1'); expect($quantity.first().val()).toEqual('1');
}); });
it('should show error message when quantity is greater than 100', function() {
BasketPage.onReady();
$('input.quantity').first().val(101);
$('#quantity-update').trigger('click');
expect($('div#error-msg').text()).toEqual('Quantity must be less than or equal to 100.');
});
it('should show error message when quantity is less than 1', function() {
BasketPage.onReady();
$('input.quantity').first().val(0);
$('#quantity-update').trigger('click');
expect($('div#error-msg').text()).toEqual('Quantity must be greater than or equal to 1.');
});
it('should show error message when quantity is not given', function() {
BasketPage.onReady();
$('input.quantity').first().val('');
$('#quantity-update').trigger('click');
expect($('div#error-msg').text()).toEqual('Please enter a quantity from 1 to 100.');
});
it('should recognize the credit card', function() { it('should recognize the credit card', function() {
var validCardList = [ var validCardList = [
{number: '378282246310005', name: 'amex'}, {number: '378282246310005', name: 'amex'},
......
...@@ -480,11 +480,31 @@ ...@@ -480,11 +480,31 @@
.quantity-group { .quantity-group {
padding-left: 0; padding-left: 0;
.max-quantity {
font-size: 10px;
color: #4a4a4a;
font-weight: 600;
margin: 0 0 0 85px;
display: block;
}
#error-msg {
font-size: 12px;
color: red;
margin: 5px 0 5px 85px;
}
.error-state {
border-color: red;
}
} }
.quantity-label { .quantity-label {
display: inline-block; display: inline-block;
padding-right: 10px; padding-right: 10px;
vertical-align: top;
margin: 5px 0 0;
} }
.checkout-quantity { .checkout-quantity {
...@@ -492,7 +512,7 @@ ...@@ -492,7 +512,7 @@
} }
.spinner { .spinner {
width: 70px; width: 80px;
float: left; float: left;
padding-right: 10px; padding-right: 10px;
} }
...@@ -519,18 +539,31 @@ ...@@ -519,18 +539,31 @@
} }
.amount { .amount {
padding-bottom: 20px; padding: 0;
color: $gray-color; color: $gray-color;
font-size: 18px; font-size: 18px;
span {
float: none !important;
display: block;
text-align: unset;
.price {
font-weight: bold; &:first-child {
margin: 0 0 20px;
}
} }
} }
#basket-total { #basket-total {
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
.price {
font-weight: bold;
text-align: unset;
padding: 0;
}
} }
#voucher-information { #voucher-information {
......
...@@ -152,6 +152,10 @@ ...@@ -152,6 +152,10 @@
padding: 10px 15px; padding: 10px 15px;
} }
} }
#find-more-courses-link {
margin: 20px;
float: right;
}
.dashboard-link { .dashboard-link {
line-height: 20px; line-height: 20px;
......
...@@ -36,11 +36,18 @@ ...@@ -36,11 +36,18 @@
{% captureas link_start %} {% captureas link_start %}
<a href="mailto:{{ order.user.email }}"> <a href="mailto:{{ order.user.email }}">
{% endcaptureas %} {% endcaptureas %}
{% blocktrans trimmed with email=order.user.email link_end="</a>" %} {% if has_enrollment_code_product %}
Your order is complete. If you need a receipt, you can print this page. {% blocktrans trimmed with email=order.user.email link_end="</a>" %}
You will also receive a confirmation message with this information at Your order is complete. You will receive a confirmation message and your enrollment codes(s) at
{{ link_start }}{{ email }}{{ link_end }}. {{ link_start }}{{ email }}{{ link_end }}. If you need a receipt, you can print this page.
{% endblocktrans %} {% endblocktrans %}
{% else %}
{% blocktrans trimmed with email=order.user.email link_end="</a>" %}
Your order is complete. If you need a receipt, you can print this page.
You will also receive a confirmation message with this information at
{{ link_start }}{{ email }}{{ link_end }}.
{% endblocktrans %}
{% endif %}
</div> </div>
{% if order.billing_address %} {% if order.billing_address %}
...@@ -86,7 +93,7 @@ ...@@ -86,7 +93,7 @@
{% endif %} {% endif %}
</dd> </dd>
<dt class="line-price sr">{% trans "Item Price:" %}</dt> <dt class="line-price sr">{% trans "Item Price:" %}</dt>
<dd class="line-price price">{{ line.line_price_before_discounts_incl_tax|currency:order.currency }}</dd> <dd class="line-price price">{{ line.unit_price_incl_tax|currency:order.currency }}</dd>
</div> </div>
{% endfor %} {% endfor %}
</dl> </dl>
...@@ -165,6 +172,11 @@ ...@@ -165,6 +172,11 @@
</a> </a>
</div> </div>
{% endif %} {% endif %}
<div id="find-more-courses-link">
<a class="find-more-courses-link nav-link" href="{{ explore_courses_url }}" target="_blank">
{% trans "Find more courses" %}
</a>
</div>
</div> </div>
</div> </div>
{% endblock content %} {% endblock content %}
{% load i18n %}
{% load currency_filters %}
<p>
{% if basket.is_empty %}
{% trans "Your basket is now empty" %}
{% else %}
{% if basket.is_tax_known %}
{% blocktrans with num_items=basket.num_items total=basket.total_incl_tax|currency:basket.currency strong_start='<b>' strong_end='</b>' paragraph_start='<p>' paragraph_end='</p>' %}
{{ strong_start }}We’ve updated your quantity.{{ strong_end }}
{{ paragraph_start }}Your cart includes {{ num_items }} enrollment codes at a total cost of {{ total }}, that you will receive via email.{{ paragraph_end }}
{% endblocktrans %}
{% else %}
{% blocktrans with num_items=basket.num_items total=basket.total_excl_tax|currency:basket.currency strong_start='<b>' strong_end='</b>' paragraph_start='<p>' paragraph_end='</p>' %}
{{ strong_start }}We’ve updated your quantity.{{ strong_end }}
{{ paragraph_start }}Your cart includes {{ num_items }} enrollment codes at a total cost of {{ total }}, that you will receive via email.{{ paragraph_end }}
{% endblocktrans %}
{% endif %}
{% endif %}
</p>
{% if include_buttons %}
<p>
<a href="{% url 'basket:summary' %}" class="btn btn-info">{% trans "View basket" %}</a>
<a href="{% url 'checkout:index' %}" class="btn btn-info">{% trans "Checkout now" %}</a>
</p>
{% endif %}
...@@ -27,14 +27,16 @@ ...@@ -27,14 +27,16 @@
</div> </div>
{% if line_data.enrollment_code %} {% if line_data.enrollment_code %}
<div class="col-sm-12 col-xs-12 form-inline quantity-group"> <div class="col-sm-12 col-xs-12 form-inline quantity-group">
<label class="product-price-label text-muted quantity-label">{% trans 'Quantity' %}</label> <label for="input-quantity-field " class="product-price-label text-muted quantity-label">{% trans 'Quantity' %}</label>
<div class="checkout-quantity form-group"> <div class="checkout-quantity form-group">
<div class="input-group spinner {% if form.errors %}error{% endif %}"> <div class="input-group spinner {% if form.errors %}error{% endif %}">
{% render_field form.quantity class+="quantity form-control" min=min_seat_quantity %} {% render_field form.quantity class+="quantity form-control" min=min_seat_quantity max=max_seat_quantity title="Quantity" id="input-quantity-field" %}
</div> </div>
<button class="btn btn-primary update-button quantity-update" type="submit" <button id="quantity-update" class="btn btn-primary update-button quantity-update" type="submit"
data-loading-text="{% trans 'Updating...' %}">{% trans "Update" %}</button> data-loading-text="{% trans 'Updating...' %}">{% trans "Update" %}</button>
</div> </div>
<div id="error-msg" role="alert"></div>
<span class="max-quantity">Max: 100</span>
</div> </div>
{% endif %} {% endif %}
</div> </div>
...@@ -43,9 +45,19 @@ ...@@ -43,9 +45,19 @@
</form> </form>
<div role="region" aria-labelledby="summary-region" aria-live="polite"> <div role="region" aria-labelledby="summary-region" aria-live="polite">
<h2 id="summary-region" class="title">{% trans "summary" %}</h2> <h2 id="summary-region" class="title">{% trans "summary" %}</h2>
<div id="line-price" class="amount row"> <div class="row">
<span class="description">{% trans "Price" %}</span> <div id="line-price" class="amount col-xs-4 col-sm-4">
<span class="price">{{basket.total_incl_tax_excl_discounts|currency:basket.currency}}</span> <span class="description">{% trans "Price" %}</span>
<span class="price">{{line_price|currency:basket.currency}}</span>
</div>
<div id="line-price" class="amount col-xs-4 col-sm-4">
<span class="description float-none">{% trans "Quanity" %}</span>
<span>{{basket.num_items}}</span>
</div>
<div id="line-price" class="amount col-xs-4 col-sm-4">
<span class="description">{% trans "Subtotal" %}</span>
<span class="price">{{order_total.incl_tax|currency:basket.currency}}</span>
</div>
</div> </div>
{% if basket.total_discount %} {% if basket.total_discount %}
<div id="line-discount" class="amount row"> <div id="line-discount" class="amount row">
...@@ -98,14 +110,15 @@ ...@@ -98,14 +110,15 @@
{% endif %} {% endif %}
</div> </div>
<div id="basket-total" class="row" aria-describedby="total-price-desc"> <div id="basket-total" class="row" aria-describedby="total-price-desc">
<span id="total-price-desc" class="description">{% trans "TOTAL" %}</span> <span id="total-price-desc col-xs-4 col-sm-4" class="description">{% trans "TOTAL" %}</span>
<span class="price">{{ order_total.incl_tax|currency:basket.currency }}</span> <span class="col-xs-4 col-sm-4"></span>
<span class="price col-xs-4 col-sm-4">{{ order_total.incl_tax|currency:basket.currency }}</span>
</div> </div>
</div> </div>
{% if order_details_msg %} {% if order_details_msg %}
<div role="region" aria-labelledby="order-details-region"> <div role="region" aria-labelledby="order-details-region">
<h2 id="order-details-region" class="title">{% trans "order details" %}</h2> <h2 id="order-details-region" class="title">{% trans "order details" %}</h2>
<p>{{ order_details_msg }}</p> <p>{{ order_details_msg | safe}}</p>
</div> </div>
{% endif %} {% endif %}
</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