Compare commits

...

1 Commits

Author SHA1 Message Date
Felix Rindt
fa372b3df0 match copy answer button to fitting position 2020-09-11 12:58:39 +02:00
3 changed files with 68 additions and 29 deletions

View File

@@ -1,3 +1,4 @@
import itertools
import json
from collections import OrderedDict
from decimal import Decimal
@@ -53,7 +54,31 @@ class BaseQuestionsViewMixin:
data=(self.request.POST if self.request.method == 'POST' else None),
files=(self.request.FILES if self.request.method == 'POST' else None))
form.pos = cartpos or orderpos
form.show_copy_answers_to_addon_button = form.pos.addon_to and set(form.pos.addon_to.item.questions.all()) & set(form.pos.item.questions.all())
if form.pos.addon_to_id is not None:
form.copy_answer_from = None
# addons typically do not have the same item as the main position, so we look for overlapping questions
form.show_copy_answers_to_addon_button = bool(
set(form.pos.addon_to.item.questions.values_list("id", flat=True)) &
set(form.pos.item.questions.values_list("id", flat=True)))
else:
form.show_copy_answers_to_addon_button = False
# look for a position we can best copy answers from
form.copy_answer_from = next(
itertools.chain(
( # match a position with the same item
other_form.pos.id for other_form in formlist
if other_form.pos.addon_to_id is None and form.pos.item.id == other_form.pos.item.id
),
( # match a position with questions in common
other_form.pos.id for other_form in formlist
if other_form.pos.addon_to_id is None
and set(form.pos.item.questions.values_list("id", flat=True)) & set(other_form.pos.item.questions.values_list("id", flat=True))
)
),
None # didn't find a position to copy answers from
)
if len(form.fields) > 0:
formlist.append(form)
return formlist
@@ -105,8 +130,7 @@ class BaseQuestionsViewMixin:
if hasattr(field, 'answer'):
# We already have a cached answer object, so we don't
# have to create a new one
if v == '' or v is None or (isinstance(field, forms.FileField) and v is False) \
or (isinstance(v, QuerySet) and not v.exists()):
if v == '' or v is None or (isinstance(field, forms.FileField) and v is False) or (isinstance(v, QuerySet) and not v.exists()):
if field.answer.file:
field.answer.file.delete()
field.answer.delete()

View File

@@ -60,9 +60,9 @@
{% if pos.variation %}
{{ pos.variation }}
{% endif %}
{% if forloop.counter > 1 %}
{% if forms.0.copy_answer_from is not None %}
<span class="text-right flip">
<button type="button" data-id="{{ forloop.counter0 }}" name="copy" class="js-copy-answers btn btn-default btn-xs">{% trans "Copy answers from above" %}</button>
<button type="button" data-id="{{ forms.0.pos.id }}" data-copy-from="{{ forms.0.copy_answer_from }}" name="copy" class="js-copy-answers btn btn-default btn-xs">{% trans "Copy answers from above" %}</button>
<i class="fa fa-angle-down collapse-indicator"></i>
</span>
{% else %}
@@ -122,13 +122,13 @@
<legend>
{% if form.show_copy_answers_to_addon_button %}
<span class="pull-right flip">
<button type="button" data-id="{{ forloop.parentloop.counter0 }}" data-addonid="{{ forloop.counter0 }}" name="copy" class="js-copy-answers-addon btn btn-default btn-xs">{% trans "Copy answers" %}</button>
<button type="button" data-id="{{ forms.0.pos.id }}" data-addonid="{{ forloop.counter0 }}" name="copy" class="js-copy-answers-addon btn btn-default btn-xs">{% trans "Copy answers" %}</button>
</span>
{% endif %}
+ {{ form.pos.item.name }}{% if form.pos.variation %} {{ form.pos.variation.value }}{% endif %}
</legend>
{% endif %}
<div data-idx="{{ forloop.parentloop.counter0 }}" data-addonidx="{{ forloop.counter0 }}">
<div data-idx="{{ forms.0.pos.id }}" data-addonidx="{{ forloop.counter0 }}">
{% bootstrap_form form layout="checkout" %}
</div>
{% endfor %}

View File

@@ -16,9 +16,13 @@ function ngettext(singular, plural, count) {
function interpolate(fmt, object, named) {
if (named) {
return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
return fmt.replace(/%\(\w+\)s/g, function (match) {
return String(obj[match.slice(2, -2)])
});
} else {
return fmt.replace(/%s/g, function(match){return String(obj.shift())});
return fmt.replace(/%s/g, function (match) {
return String(obj.shift())
});
}
}
@@ -171,13 +175,13 @@ $(function () {
$(".js-copy-answers").click(function (e) {
e.preventDefault();
e.stopPropagation();
let idx = $(this).data('id');
const addonDivs = $('div[data-idx="' + idx +'"]')
const idx = $(this).data('id');
const copyFromIdx = $(this).data('copy-from')
const addonDivs = $('div[data-idx="' + idx + '"]')
addonDivs.each(function (index) {
const elements = $(this).find('input, select, textarea');
const addonIdx = $(this).attr("data-addonidx");
const answersDiv = $('div[data-idx="0"][data-addonidx="' + addonIdx + '"]');
const addonIdx = $(this).data("addonidx");
const answersDiv = $('div[data-idx="' + copyFromIdx + '"][data-addonidx="' + addonIdx + '"]');
const answers = answersDiv.find('input, select, textarea');
copy_answers(elements, answers);
@@ -189,7 +193,7 @@ $(function () {
e.stopPropagation();
const id = $(this).data('id');
const addonId = $(this).data('addonid');
const addonDiv = $('div[data-idx="' + id +'"][data-addonidx="' + addonId + '"]');
const addonDiv = $('div[data-idx="' + id + '"][data-addonidx="' + addonId + '"]');
const elements = addonDiv.find('input, select, textarea');
const answers = $('*[data-idx="' + id + '"] input, *[data-idx="' + id + '"] select, *[data-idx="' + id + '"] textarea');
copy_answers(elements, answers);
@@ -249,7 +253,7 @@ $(function () {
is_enabled = true;
}
});
$(".input-seat-selection option").each(function() {
$(".input-seat-selection option").each(function () {
if ($(this).val() && $(this).val() !== "" && $(this).prop('selected')) {
is_enabled = true;
}
@@ -257,7 +261,9 @@ $(function () {
}
if (!is_enabled && !$(".has-seating").length) {
$("#btn-add-to-cart").prop("disabled", !is_enabled).popover({
'content': function () { return gettext("Please enter a quantity for one of the ticket types.") },
'content': function () {
return gettext("Please enter a quantity for one of the ticket types.")
},
'placement': 'top',
'trigger': 'hover focus'
});
@@ -353,7 +359,9 @@ $(function () {
if (counter > curCounter) {
return; // Lost race
}
dependent.find("option").filter(function (t) {return !!$(this).attr("value")}).remove();
dependent.find("option").filter(function (t) {
return !!$(this).attr("value")
}).remove();
if (data.data.length > 0) {
$.each(data.data, function (k, s) {
dependent.append($("<option>").attr("value", s.code).text(s.name));
@@ -400,8 +408,7 @@ $(function () {
true
));
}
var cancel_fee_slider = $('#cancel-fee-slider').slider({
}).on('slide', function () {
var cancel_fee_slider = $('#cancel-fee-slider').slider({}).on('slide', function () {
cancel_fee_slider_update();
}).data('slider');
if (cancel_fee_slider) {
@@ -417,7 +424,7 @@ $(function () {
}
var local_tz = moment.tz.guess()
$("span[data-timezone]").each(function() {
$("span[data-timezone]").each(function () {
var t = moment.tz($(this).attr("data-time"), $(this).attr("data-timezone"))
var tz = moment.tz.zone($(this).attr("data-timezone"))
@@ -445,39 +452,47 @@ $(function () {
});
function copy_answers(elements, answers) {
elements.each(function (index) {
elements.each(function (index) {
var input = $(this),
tagName = input.prop('tagName').toLowerCase(),
attributeType = input.attr('type'),
suffix = input.attr('name').split('-')[1];
let a = false;
switch (tagName) {
case "textarea":
input.val(answers.filter("[name$=" + suffix + "]").val());
a = answers.filter("[name$=" + suffix + "]");
if (a.length) input.val(a.val());
break;
case "select":
input.val(answers.filter("[name$=" + suffix + "]").find(":selected").val()).change();
a = answers.filter("[name$=" + suffix + "]").find(":selected");
if (a.length) input.val(a.val()).change();
break;
case "input":
switch (attributeType) {
case "text":
case "number":
input.val(answers.filter("[name$=" + suffix + "]").val());
a = answers.filter("[name$=" + suffix + "]")
if (a.length) input.val(a.val());
break;
case "checkbox":
case "radio":
if (input.attr('value')) {
input.prop("checked", answers.filter("[name$=" + suffix + "][value=" + input.attr('value') + "]").prop("checked"));
a = answers.filter("[name$=" + suffix + "][value=" + input.attr('value') + "]")
if (a.length) input.prop("checked", a.prop("checked"));
} else {
input.prop("checked", answers.filter("[name$=" + suffix + "]").prop("checked"));
a = answers.filter("[name$=" + suffix + "]")
if (a.length) input.prop("checked", a.prop("checked"));
}
break;
default:
input.val(answers.filter("[name$=" + suffix + "]").val());
a = answers.filter("[name$=" + suffix + "]")
if (a.length) input.val(a.val());
}
break;
default:
input.val(answers.filter("[name$=" + suffix + "]").val());
a = answers.filter("[name$=" + suffix + "]")
if (a.length) input.val(a.val());
}
});
questions_toggle_dependent(true);