diff --git a/src/pretix/presale/forms/renderers.py b/src/pretix/presale/forms/renderers.py index f0ff35e75..e9ce48ccf 100644 --- a/src/pretix/presale/forms/renderers.py +++ b/src/pretix/presale/forms/renderers.py @@ -8,7 +8,7 @@ from django.utils.safestring import mark_safe from django.utils.translation import pgettext -def render_label(content, label_for=None, label_class=None, label_title='', optional=False): +def render_label(content, label_for=None, label_class=None, label_title='', optional=False, is_valid=None): """ Render a label with content """ @@ -20,6 +20,15 @@ def render_label(content, label_for=None, label_class=None, label_title='', opti if label_title: attrs['title'] = label_title + opt = "" + + if is_valid is not None: + if is_valid: + validation_text = pgettext('form', 'is valid') + else: + validation_text = pgettext('form', 'has errors') + opt += ' {}'.format(validation_text) + if text_value(content) == ' ': # Empty label, e.g. checkbox attrs.setdefault('class', '') @@ -27,16 +36,15 @@ def render_label(content, label_for=None, label_class=None, label_title='', opti # usually checkboxes have overall empty labels and special labels per checkbox # => remove for-attribute as well as "required"-text appended to label del(attrs['for']) - opt = "" else: - opt = mark_safe(' {}'.format(pgettext('form', 'required'))) if not optional else '' + opt += ', {}'.format(pgettext('form', 'required')) if not optional else '' builder = '<{tag}{attrs}>{content}{opt}' return format_html( builder, tag='label', attrs=mark_safe(flatatt(attrs)) if attrs else '', - opt=opt, + opt=mark_safe(opt), content=text_value(content), ) @@ -64,6 +72,26 @@ class CheckoutFieldRenderer(FieldRenderer): ) return form_group_class + def append_to_field(self, html): + help_text_and_errors = [] + help_text_and_errors += self.field_errors + if self.field_help: + help_text_and_errors.append(self.field_help) + for idx, text in enumerate(help_text_and_errors): + html += '
{text}
'.format(id=self.field.id_for_label, text=text, idx=idx) + return html + + def add_help_attrs(self, widget=None): + super().add_help_attrs(widget) + if widget is None: + widget = self.widget + help_cnt = len(self.field_errors) + if self.field_help: + help_cnt += 1 + if help_cnt > 0: + help_ids = ["help-for-{id}-{idx}".format(id=self.field.id_for_label, idx=idx) for idx in range(help_cnt)] + widget.attrs["aria-describedby"] = " ".join(help_ids) + def add_label(self, html): label = self.get_label() @@ -73,11 +101,16 @@ class CheckoutFieldRenderer(FieldRenderer): else: required = self.field.field.required + if self.field.form.is_bound: + is_valid = len(self.field.errors) == 0 + else: + is_valid = None html = render_label( label, label_for=self.field.id_for_label, label_class=self.get_label_class(), - optional=not required and not isinstance(self.widget, CheckboxInput) + optional=not required and not isinstance(self.widget, CheckboxInput), + is_valid=is_valid ) + html return html diff --git a/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html b/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html index e42b5c7c1..700c59fd7 100644 --- a/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html +++ b/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -57,7 +57,7 @@
{% if payment_provider.identifier != "free" %}
- + {% trans "Modify" %} @@ -79,7 +79,7 @@
- + {% trans "Modify" %} @@ -129,7 +129,7 @@
- + {% trans "Modify" %} diff --git a/src/pretix/presale/templates/pretixpresale/event/checkout_questions.html b/src/pretix/presale/templates/pretixpresale/event/checkout_questions.html index e88d0d632..c7afe9e6d 100644 --- a/src/pretix/presale/templates/pretixpresale/event/checkout_questions.html +++ b/src/pretix/presale/templates/pretixpresale/event/checkout_questions.html @@ -21,9 +21,9 @@
- {% bootstrap_form contact_form layout="horizontal" %} + {% bootstrap_form contact_form layout="checkout" %} {% if not invoice_address_asked and event.settings.invoice_name_required %} - {% bootstrap_form invoice_form layout="horizontal" %} + {% bootstrap_form invoice_form layout="checkout" %} {% endif %}
@@ -46,7 +46,7 @@ {{ event.settings.invoice_address_explanation_text|rich_text }}
{% endif %} - {% bootstrap_form invoice_form layout="horizontal" %} + {% bootstrap_form invoice_form layout="checkout" %}
diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html b/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html index 06ee8a1f7..871939dbe 100644 --- a/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html @@ -140,6 +140,7 @@ {% elif line.addon_to %}
 
+ {% trans "price per item" %} {% if event.settings.display_net_prices %} {{ line.net_price|money:event.currency }} {% else %} @@ -163,6 +164,7 @@ {% endif %} + {% trans "quantity" %} {{ line.count }} {% if editable %}
+ {% trans "price per item" %} {% if event.settings.display_net_prices %} {{ line.net_price|money:event.currency }} {% else %} @@ -197,6 +200,7 @@
{% endif %}
+ {% trans "price" %} {% if event.settings.display_net_prices %} {{ line.net_total|money:event.currency }} {% if line.tax_rate and line.total %} diff --git a/src/pretix/presale/templates/pretixpresale/event/order.html b/src/pretix/presale/templates/pretixpresale/event/order.html index c78a52f33..7b1e3e9f3 100644 --- a/src/pretix/presale/templates/pretixpresale/event/order.html +++ b/src/pretix/presale/templates/pretixpresale/event/order.html @@ -186,7 +186,7 @@
{% if order.can_modify_answers %}
- + {% trans "Change details" %} @@ -280,7 +280,7 @@ {% if invoice_address_asked or request.event.settings.invoice_name_required %} {% if order.can_modify_answers %}
- + {% trans "Change details" %} diff --git a/src/pretix/static/pretixpresale/js/ui/main.js b/src/pretix/static/pretixpresale/js/ui/main.js index 5b5eddfb3..47b62a359 100644 --- a/src/pretix/static/pretixpresale/js/ui/main.js +++ b/src/pretix/static/pretixpresale/js/ui/main.js @@ -292,6 +292,7 @@ $(function () { $("input[data-required-if], select[data-required-if], textarea[data-required-if]").each(function () { var dependent = $(this), + dependentLabel = $("label[for="+this.id+"]"), dependency = $($(this).attr("data-required-if")), update = function (ev) { var enabled = (dependency.attr("type") === 'checkbox' || dependency.attr("type") === 'radio') ? dependency.prop('checked') : !!dependency.val(); @@ -299,6 +300,12 @@ $(function () { dependent.prop('required', enabled); } dependent.closest('.form-group').toggleClass('required', enabled); + if (enabled) { + dependentLabel.append(', ' + gettext('required') + ''); + } + else { + dependentLabel.find(".label-required").remove(); + } }; update(); dependency.closest('.form-group').find('input[name=' + dependency.attr("name") + ']').on("change", update);