From d22427f5784cb7020c6adea058667ec8198b663e Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Thu, 16 Nov 2017 21:24:55 +0100 Subject: [PATCH] Implement progress indicator during checkout (#677) * Implement progress indicator during checkout * Do not extend bars to the edge * Wording * Add a bit more margin --- src/pretix/presale/checkoutflow.py | 23 ++++- .../pretixpresale/event/checkout_base.html | 1 + .../pretixpresale/event/checkout_confirm.html | 5 +- .../event/fragment_checkoutflow.html | 25 ++++++ src/pretix/presale/views/checkout.py | 4 +- .../static/pretixpresale/scss/_checkout.scss | 86 +++++++++++++++++++ .../static/pretixpresale/scss/main.scss | 1 + 7 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 src/pretix/presale/templates/pretixpresale/event/fragment_checkoutflow.html create mode 100644 src/pretix/static/pretixpresale/scss/_checkout.scss diff --git a/src/pretix/presale/checkoutflow.py b/src/pretix/presale/checkoutflow.py index a7451ec09..63affae32 100644 --- a/src/pretix/presale/checkoutflow.py +++ b/src/pretix/presale/checkoutflow.py @@ -6,7 +6,7 @@ from django.http import HttpResponseNotAllowed, JsonResponse from django.shortcuts import redirect from django.utils import translation from django.utils.functional import cached_property -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import pgettext_lazy, ugettext_lazy as _ from django.views.generic.base import TemplateResponseMixin from pretix.base.models import Order @@ -33,6 +33,7 @@ from pretix.presale.views.questions import QuestionsViewMixin class BaseCheckoutFlowStep: requires_valid_cart = True + icon = 'pencil' def __init__(self, event): self.event = event @@ -42,6 +43,10 @@ class BaseCheckoutFlowStep: def identifier(self): raise NotImplementedError() + @property + def label(self): + return pgettext_lazy('checkoutflow', 'Step') + @property def priority(self): return 100 @@ -137,6 +142,11 @@ class TemplateFlowStep(TemplateResponseMixin, BaseCheckoutFlowStep): kwargs.setdefault('step', self) kwargs.setdefault('event', self.event) kwargs.setdefault('prev_url', self.get_prev_url(self.request)) + kwargs.setdefault('checkout_flow', [ + step + for step in self.request._checkout_flow + if step.is_applicable(self.request) + ]) return kwargs def render(self, **kwargs): @@ -166,6 +176,8 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep): task = set_cart_addons known_errortypes = ['CartError'] requires_valid_cart = False + label = pgettext_lazy('checkoutflow', 'Add-on products') + icon = 'puzzle-piece' def is_applicable(self, request): if not hasattr(request, '_checkoutflow_addons_applicable'): @@ -173,6 +185,8 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep): return request._checkoutflow_addons_applicable def is_completed(self, request, warn=False): + if getattr(self, '_completed', None) is not None: + return self._completed for cartpos in get_cart(request).filter(addon_to__isnull=True).prefetch_related( 'item__addons', 'item__addons__addon_category', 'addons', 'addons__item' ): @@ -180,7 +194,9 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep): for iao in cartpos.item.addons.all(): found = len([1 for p in a if p.item.category_id == iao.addon_category_id]) if found < iao.min_count or found > iao.max_count: + self._completed = False return False + self._completed = True return True @cached_property @@ -283,6 +299,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep): priority = 50 identifier = "questions" template_name = "pretixpresale/event/checkout_questions.html" + label = pgettext_lazy('checkoutflow', 'Your information') def is_applicable(self, request): return True @@ -397,6 +414,8 @@ class PaymentStep(QuestionsViewMixin, CartMixin, TemplateFlowStep): priority = 200 identifier = "payment" template_name = "pretixpresale/event/checkout_payment.html" + label = pgettext_lazy('checkoutflow', 'Payment') + icon = 'credit-card' @cached_property def _total_order_value(self): @@ -479,6 +498,8 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep): template_name = "pretixpresale/event/checkout_confirm.html" task = perform_order known_errortypes = ['OrderError'] + label = pgettext_lazy('checkoutflow', 'Review order') + icon = 'eye' def is_applicable(self, request): return True diff --git a/src/pretix/presale/templates/pretixpresale/event/checkout_base.html b/src/pretix/presale/templates/pretixpresale/event/checkout_base.html index 6cf69c9cb..1420da0fb 100644 --- a/src/pretix/presale/templates/pretixpresale/event/checkout_base.html +++ b/src/pretix/presale/templates/pretixpresale/event/checkout_base.html @@ -40,6 +40,7 @@

{% trans "Checkout" %}

+ {% include "pretixpresale/event/fragment_checkoutflow.html" %} {% block inner %} {% endblock %} {% endblock %} diff --git a/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html b/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html index 4235865d6..94d37fe0b 100644 --- a/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html +++ b/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -3,9 +3,10 @@ {% load bootstrap3 %} {% load eventurl %} {% load eventsignal %} -{% block title %}{% trans "Confirm order" %}{% endblock %} +{% block title %}{% trans "Review order" %}{% endblock %} {% block content %} -

{% trans "Confirm order" %}

+

{% trans "Review order" %}

+ {% include "pretixpresale/event/fragment_checkoutflow.html" %}

{% trans "Please review the details below and confirm your order." %}

{% csrf_token %} diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_checkoutflow.html b/src/pretix/presale/templates/pretixpresale/event/fragment_checkoutflow.html new file mode 100644 index 000000000..35e7e7d30 --- /dev/null +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_checkoutflow.html @@ -0,0 +1,25 @@ +{% load i18n %} + diff --git a/src/pretix/presale/views/checkout.py b/src/pretix/presale/views/checkout.py index c3a773a22..1154443b2 100644 --- a/src/pretix/presale/views/checkout.py +++ b/src/pretix/presale/views/checkout.py @@ -41,7 +41,7 @@ class CheckoutView(View): except CartError as e: cart_error = e - flow = get_checkout_flow(self.request.event) + flow = request._checkout_flow = get_checkout_flow(self.request.event) previous_step = None for step in flow: if not step.is_applicable(request): @@ -64,4 +64,6 @@ class CheckoutView(View): return handler(request) else: previous_step = step + step.c_is_before = True + step.c_resolved_url = step.get_step_url(request) raise Http404() diff --git a/src/pretix/static/pretixpresale/scss/_checkout.scss b/src/pretix/static/pretixpresale/scss/_checkout.scss new file mode 100644 index 000000000..cfbce85eb --- /dev/null +++ b/src/pretix/static/pretixpresale/scss/_checkout.scss @@ -0,0 +1,86 @@ +.checkout-flow { + display: flex; + flex-direction: row; + margin: 15px 0 13px 0; + + .checkout-step { + flex: 1; + text-align: center; + padding: 10px 0; + position: relative; + + .checkout-step-icon { + border: 1px solid $text-muted; + display: inline-block; + width: 30px; + height: 30px; + line-height: 28px; + border-radius: 15px; + color: $text-muted; + z-index: 200; + background: white; + position: relative; + } + + .checkout-step-label { + padding-top: 10px; + color: $text-muted; + } + + .checkout-step-bar-left { + position: absolute; + top: 22px; + left: 0; + width: 50%; + height: 6px; + background: $gray-lighter; + z-index: 100; + } + .checkout-step-bar-right { + position: absolute; + top: 22px; + left: 50%; + width: 50%; + height: 6px; + background: $gray-lighter; + z-index: 100; + } + + &.step-done .checkout-step-icon { + border: 1px solid $brand-success; + background: $brand-success; + color: white; + } + &.step-done .checkout-step-label { + color: $brand-success; + } + &.step-done .checkout-step-bar-left, &.step-done .checkout-step-bar-right { + background: $brand-success; + } + + &.step-current .checkout-step-icon { + border: 1px solid darken($brand-info, 20%); + background: darken($brand-info, 20%); + color: white; + } + &.step-current .checkout-step-label { + color: darken($brand-info, 20%); + } + &.step-current .checkout-step-bar-left { + background: $brand-success; + } + &:last-child .checkout-step-bar-right, + &:first-child .checkout-step-bar-left { + background: transparent; + } + + &:hover, &:active { + text-decoration: none; + } + } +} +@media(max-width: $screen-sm-max) { + .checkout-step-label { + display: none; + } +} \ No newline at end of file diff --git a/src/pretix/static/pretixpresale/scss/main.scss b/src/pretix/static/pretixpresale/scss/main.scss index 2299c1d86..d58213c9f 100644 --- a/src/pretix/static/pretixpresale/scss/main.scss +++ b/src/pretix/static/pretixpresale/scss/main.scss @@ -7,6 +7,7 @@ @import "_cart.scss"; @import "_forms.scss"; @import "_calendar.scss"; +@import "_checkout.scss"; @import "../../pretixbase/scss/webfont.scss"; footer {