diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index dec0f6a3a6b5d70c0df3e754e53aed82c5a5e76c..9fc071c837ba85d2df9cce1f4029f632383c3220 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -83,10 +83,14 @@ from openedx.core.djangoapps.external_auth.login_and_register import register as from openedx.core.djangoapps.external_auth.models import ExternalAuthMap from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY from openedx.core.djangoapps.programs.models import ProgramsApiConfig -from openedx.core.djangoapps.programs.utils import ProgramProgressMeter +from openedx.core.djangoapps.programs.utils import ( + ProgramDataExtender, + ProgramProgressMeter +) from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.theming import helpers as theming_helpers from openedx.core.djangoapps.user_api.preferences import api as preferences_api +from openedx.core.djangoapps.waffle_utils import WaffleFlagNamespace, WaffleFlag from openedx.core.djangolib.markup import HTML from openedx.features.course_experience import course_home_url_name from openedx.features.enterprise_support.api import get_dashboard_consent_notification @@ -780,8 +784,28 @@ def dashboard(request): # is passed in the template context to allow rendering of program-related # information on the dashboard. meter = ProgramProgressMeter(request.site, user, enrollments=course_enrollments) + ecommerce_service = EcommerceService() inverted_programs = meter.invert_programs() + urls, program_data = {}, {} + bundles_on_dashboard_flag = WaffleFlag(WaffleFlagNamespace(name=u'student.experiments'), u'bundles_on_dashboard') + + if (bundles_on_dashboard_flag.is_enabled()): + programs_data = meter.programs + if programs_data: + program_data = meter.programs[0] + program_data = ProgramDataExtender(program_data, request.user).extend() + course_data = meter.progress(programs=[program_data], count_only=False)[0] + + program_data.pop('courses') + skus = program_data.get('skus') + + urls = { + 'commerce_api_url': reverse('commerce_api:v0:baskets:create'), + 'buy_button_url': ecommerce_service.get_checkout_page_url(*skus) + } + urls['completeProgramURL'] = urls['buy_button_url'] + '&bundle=' + program_data.get('uuid') + # Construct a dictionary of course mode information # used to render the course list. We re-use the course modes dict # we loaded earlier to avoid hitting the database. @@ -879,6 +903,8 @@ def dashboard(request): course_enrollments = [enr for enr in course_enrollments if entitlement.enrollment_course_run.course_id != enr.course_id] # pylint: disable=line-too-long context = { + 'urls': urls, + 'program_data': program_data, 'enterprise_message': enterprise_message, 'consent_required_courses': consent_required_courses, 'enterprise_customer_name': enterprise_customer_name, @@ -921,7 +947,6 @@ def dashboard(request): 'display_sidebar_on_dashboard': display_sidebar_on_dashboard, } - ecommerce_service = EcommerceService() if ecommerce_service.is_enabled(request.user): context.update({ 'use_ecommerce_payment_flow': True, diff --git a/lms/static/sass/_experiments.scss b/lms/static/sass/_experiments.scss index e63167ded87ff369bb6b4ecedf3dcdc67ce07a75..22127b3f9cb24d72f41dfd12ca158b5cd300c408 100644 --- a/lms/static/sass/_experiments.scss +++ b/lms/static/sass/_experiments.scss @@ -4,396 +4,38 @@ // Please list the ticket number of the experiment // -------------------- -// LEARNER-1726 Track Selection V3 +// LEARNER-3072 Program Purchase on dashboard -/* This css was added as part of the LEARNER-1726 experiment */ -.v2.register-choice { - margin: 0 2% 20px 0 !important -} - -.v2.register-choice-certificate .list-actions { - text-align: left !important; -} - -.v2.register-choice-continue .list-actions { - margin-bottom: 0 !important; -} - -.v2.register-choice-continue .action-select { - display: inline-block !important; - list-style-type: none !important; - width: 100% !important; -} - -.v2.register-choice-continue .continue-link { - display: inline-block !important; - padding: 10px 15px !important; - border-radius: 3px !important; - border: 1px solid #d7548e !important; - box-shadow: 0 2px 1px 0 #982c62 !important; - background: white !important; - text-align: center !important; - color: #d7548e !important; - float: left !important; - font-size: 15px; - font-weight: 500 !important; -} - -.v2.register-choice-v2-donate { - height: 300px; - background: none !important; - border-top-color: grey !important; - border-top-width: 1px !important; -} - -@media screen and (min-width: 375px) { - .v2.register-choice-v2-donate { - height: 250px; - } -} - -.v2.register-choice-v2-donate .list-actions { - margin-bottom: 0 !important; -} - -.v2.register-choice-v2-donate .list-actions a { - background: transparent !important; - color: #0075b4 !important; - box-shadow: none !important; - text-decoration: underline !important; - border: none !important; - white-space: normal; -} - -.v2.register-choice-v2-donate .wrapper-copy-inline { - height: 70px !important; - width: 100% !important; - display: flex !important; -} - -.v2.register-choice-v2-donate .wrapper-copy { - width: 70% !important; - height: auto !important; -} - -.v2.page-header { - padding: 0; -} - -.v2 img { - margin-top: 20px; - margin-left: 5px; -} - -.v2 .continue-link { - font-weight: bold !important; -} - -.v2.register-choice-certificate, -.v2.register-choice-continue, -.v2.register-choice-view { - width: 100%; -} - -.v2.register-choice-continue { - border-color: #d7548e !important; -} - -.v2 .wrapper-copy-inline { - max-height: 115px; -} - -.v2.register-choice-v2-donate .wrapper-copy-inline { - display: block !important; -} - -.v2.register-choice-v2-donate .copy-inline { - width: 100% !important; -} +/* This css was added as part of the LEARNER-3072 experiment */ -.v2.register-choice-v2-donate .list-actions { - width: 100% !important; - margin-top: 20px !important; - text-align: center !important; -} - -.v2 .wrapper-copy-inline .wrapper-copy { - width: 100% !important; -} - -.v2 input, .v2 a { - font-size: 15px !important; -} - -.v2 button { - background-color: rgb(0, 103, 0); - border-color: rgb(0, 103, 0); - border-radius: 2px; - box-shadow: rgb(0, 77, 0) 0 2px 1px 0; +.complete-program-dashboard-button { + float: right; + display: block; + box-sizing: border-box; + color: rgb(242, 248, 251) !important; cursor: pointer; - font-family: $font-family-sans-serif; - height: auto; - margin-right: 4px; - margin-top: 0; - padding: 10px 15px; - width: initial; - background-image: none !important; - font-size: 14px !important; - font-weight: 500 !important; - - &:hover, - &:focus { - background-color: #009b00 !important; - border-color: #009b00; - box-shadow: #004d00 0 2px 1px 0; - } -} - -.savings-message { - margin-top: 10px; - font-size: 11px; -} - -@media screen and (min-width: 375px) { - .savings-message { - font-size: 13px; - margin-left: 16px; - } + background: rgb(0, 129, 0) none repeat scroll 0% 0% / auto padding-box border-box !important; + font: normal normal 600 normal 15px / normal "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 7px; + transition: color 0.25s ease-in-out 0s, background 0.25s ease-in-out 0s, box-shadow 0.25s ease-in-out 0s; + text-decoration: none !important; + border: none !important; + margin-top: 5px; } -.v2 .continue-link, .v2 input, .v2 button, .v2 a { - width: 100%; +.complete-program-dashboard-button:hover { + background: #009b00 !important; } -.v2 img { - display: none; +.complete-program-dashboard-span { + display: block; + float: right; } -.v2 .deco-divider { +.complete-program-dashboard-div { display: none; -} - -.v2 .visual-reference { - width: 38%; -} - -@media (min-width: 420px) { - .v2 button { - height: 45px; - font-size: 16px !important; - } -} - -@media (min-width: 768px) { - .v2.register-choice-certificate, - .v2.register-choice-continue, - .v2.deco-divider { - width: 46.5% !important; - display: inline-block; - min-height: 270px; - } - - .v2.register-choice-v2-donate .wrapper-copy-inline { - display: flex !important; - } - - .v2.register-choice-v2-donate .copy-inline { - width: 40% !important; - } - - .v2.register-choice-v2-donate .list-actions { - margin-top: 0 !important; - text-align: right !important; - } - - .v2 .wrapper-copy-inline .wrapper-copy { - width: 100% !important; - } - - .v2 input, .v2 a { - font-size: 15px !important; - } - - .v2 .continue-link, .v2.register-choice-certificate button, .v2.register-choice-certificate input { - margin-top: 20px; - width: initial; - } - - .v2.register-choice-v2-donate a { - width: 100% !important; - } - - .v2.register-choice-view { - height: 250px; - } - - .v2 img { - display: initial; - } - - .v2.register-choice { - margin: 0 2% 20px 0; - } - - .v2.register-choice-continue .wrapper-copy-inline .wrapper-copy, .v2.register-choice-certificate .wrapper-copy-inline .wrapper-copy { - width: 60%; - } - - .v2.register-choice-view .wrapper-copy-inline .wrapper-copy { - width: 100%; - } - - .v2.register-choice { - padding: 15px !important; - } - - .v2.register-choice-continue .wrapper-copy-inline .wrapper-copy, .v2.register-choice-certificate .wrapper-copy-inline .wrapper-copy { - width: 60%; - } - - .v2.register-choice { - padding: 20px !important; - } - - .v2.register-choice.register-choice-view { - margin-right: 0; - } - - .v2.register-choice .list-actions:last-child { - float: left; - width: 100%; - margin-top: 0; - } - - .v2.register-choice .action-select { - width: 100% !important; - } - - .v2 .continue-link:hover, - .v2 .continue-link:focus { - background-color: #d7548e !important; - color: white !important; - text-decoration: none; - } - - .v2 .continue-link:hover { - cursor: pointer; - } - - .v2 .copy li { - margin-bottom: 5px; - } - - .v2.register-choice .copy-inline { - width: 100%; - } - - .v2 .register-choice-view { - border-color: #2991c3 !important; - } - - .v2 .visual-reference { - vertical-align: top; - } - - .v2 .wrapper-copy-inline .wrapper-copy ul { - margin-top: 0; - padding-left: 30px; - } - - .v2 .img-certificate { - border: 2px solid #009b00 !important; - float: right; - height: 120px; - width: auto; - margin-top: 0 !important; - display: none; - } - - .v2 .img-donate { - margin-top: 0; - float: right; - border: 2px solid #d7548e !important; - display: none; - } - - .v2 .img-view { - border: 2px solid #2991c3 !important; - } - - .v2.register-choice .title { - width: 100%; - margin-bottom: 20px; - } - - .v2.register-choice.register-choice-view .action-select { - border: 1px solid transparent !important; - border-radius: 3px; - } - - .v2.register-choice.register-choice-view .action-select button { - border: 1px solid transparent !important; - } - - .v2.register-choice.register-choice-view .action-select:hover { - border: 1px solid #0075b4 !important; - } - - .v2.deco-divider { - width: 3% !important; - box-sizing: border-box; - float: left; - display: inline-block; - height: 250px; - margin: 0 0 40px 0 !important; - border-left: 4px solid #f5f5f5 !important; border-top:none !important; - - .copy { - position: absolute; - top: 110px !important; - left: calc(50% - 40px) !important; - margin-left: 20px; - background: white; - text-align: center; - color: #474747; - width: 10px; - padding: 0 !important; - } - } -} - -@media (min-width: 835px) { - .v2.register-choice-certificate, - .v2.register-choice-continue, - .v2.deco-divider { - min-height: 250px; - } -} - -@media (min-width: 1024px) { - .v2 .continue-link { - width: 55%; - } - - .v2.deco-divider .copy { - margin-left: 15px; - } -} - -@media (min-width: 1096px) { - .v2.register-choice-certificate, - .v2.register-choice-continue, - .v2.deco-divider { - min-height: 260px; - } - - .v2 .img-certificate, .v2 .img-donate { - margin-top: 10px; - display: initial; - } - - .v2 .continue-link, .v2.register-choice-certificate button, - .v2.register-choice-certificate input { - margin-top: -22px !important; - } + float: right; + width: auto; + max-width: 420px; + margin: 0 0 10px 0; } diff --git a/lms/templates/dashboard/_dashboard_course_listing.html b/lms/templates/dashboard/_dashboard_course_listing.html index 9d5f52fae530939029b3aad38a7dccedaba2c1d5..939294c758559d475b05b9a2a1e19b9144040f6d 100644 --- a/lms/templates/dashboard/_dashboard_course_listing.html +++ b/lms/templates/dashboard/_dashboard_course_listing.html @@ -326,17 +326,31 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_ %endif % if related_programs: - <div class="message message-related-programs is-shown"> - <span class="related-programs-preface" tabindex="0">${_('Related Programs')}:</span> - <ul> - % for program in related_programs: - <li> - <span class="category-icon ${program['type'].lower()}-icon" aria-hidden="true"></span> - <span><a href="${program['detail_url']}">${u'{title} {type}'.format(title=program['title'], type=program['type'])}</a></span> - </li> - % endfor - </ul> - </div> + <div class="message message-related-programs is-shown"> + <span class="related-programs-preface" tabindex="0">${_('Related Programs')}:</span> + <ul> + % for program in related_programs: + <li> + <span class="category-icon ${program['type'].lower()}-icon" aria-hidden="true"></span> + <span><a href="${program['detail_url']}">${u'{title} {type}'.format(title=program['title'], type=program['type'])}</a></span> + </li> + % endfor + </ul> + % if program_data.get('is_learner_eligible_for_one_click_purchase'): + <div class="complete-program-dashboard-div"><span class="complete-program-dashboard-span">${_('Buy all remaining courses in this program and save 10%')}</span> + <a href="${urls.get('completeProgramURL')}" class="btn-brand btn cta-primary upgrade-button complete-program-dashboard-button"> + ${_('Upgrade All Remaining Courses (')} + % if program_data.get('discount_data', {}).get('is_discounted'): + <span class='list-price'> + ${_('{"{0:.2f}".format(a)}'.format(program_data['discount_data']['total_incl_tax_excl_discounts'], 2))} + </span> + % endif + ${_(' ${price:.2f} {currency} ) '.format(price=program_data.get('full_program_price'), + currency=program_data.get('discount_data', {}).get('currency')))} + </a> + </div> + % endif + </div> % endif % if cert_status: