forked from CGM_Public/pretix_original
Implement progress indicator during checkout (#677)
* Implement progress indicator during checkout * Do not extend bars to the edge * Wording * Add a bit more margin
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<h2>{% trans "Checkout" %}</h2>
|
||||
{% include "pretixpresale/event/fragment_checkoutflow.html" %}
|
||||
{% block inner %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
{% load bootstrap3 %}
|
||||
{% load eventurl %}
|
||||
{% load eventsignal %}
|
||||
{% block title %}{% trans "Confirm order" %}{% endblock %}
|
||||
{% block title %}{% trans "Review order" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>{% trans "Confirm order" %}</h2>
|
||||
<h2>{% trans "Review order" %}</h2>
|
||||
{% include "pretixpresale/event/fragment_checkoutflow.html" %}
|
||||
<p>{% trans "Please review the details below and confirm your order." %}</p>
|
||||
<form method="post" data-asynctask>
|
||||
{% csrf_token %}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{% load i18n %}
|
||||
<div class="checkout-flow">
|
||||
{% for step in checkout_flow %}
|
||||
<a {% if step.c_is_before %}href="{{ step.c_resolved_url }}"{% endif %} class="checkout-step {% if step.c_is_before %}step-done{% elif request.resolver_match.kwargs.step == step.identifier %}step-current{% endif %}">
|
||||
<div class="checkout-step-bar-left"></div>
|
||||
<div class="checkout-step-bar-right"></div>
|
||||
<div class="checkout-step-icon">
|
||||
<span class="fa {% if step.c_is_before %}fa-check{% elif step.icon %}fa-{{ step.icon }}{% else %}fa-pencil{% endif %}"></span>
|
||||
</div>
|
||||
<div class="checkout-step-label">
|
||||
{{ step.label }}
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
<a class="checkout-step">
|
||||
<div class="checkout-step-bar-left"></div>
|
||||
<div class="checkout-step-bar-right"></div>
|
||||
<div class="checkout-step-icon">
|
||||
<span class="fa fa-ticket"></span>
|
||||
</div>
|
||||
<div class="checkout-step-label">
|
||||
{% trans "Order confirmed" context "checkoutflow" %}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
@@ -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()
|
||||
|
||||
86
src/pretix/static/pretixpresale/scss/_checkout.scss
Normal file
86
src/pretix/static/pretixpresale/scss/_checkout.scss
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
@import "_cart.scss";
|
||||
@import "_forms.scss";
|
||||
@import "_calendar.scss";
|
||||
@import "_checkout.scss";
|
||||
@import "../../pretixbase/scss/webfont.scss";
|
||||
|
||||
footer {
|
||||
|
||||
Reference in New Issue
Block a user