Skip to content

Commit

Permalink
Implement upgrades for amount_extra and extra_donation
Browse files Browse the repository at this point in the history
Various fixes and changes to the upgrade modal to support being able to upgrade your preordered merch or extra donation. Also slightly refactors how we handle upgrade fields so that it's less onerous to add new fields.
  • Loading branch information
kitsuta committed Aug 30, 2023
1 parent 2955574 commit f2f3499
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 48 deletions.
7 changes: 7 additions & 0 deletions uber/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pytz
import re
import redis
import six
import uuid
from collections import defaultdict, OrderedDict
from datetime import date, datetime, time, timedelta
Expand Down Expand Up @@ -109,6 +110,8 @@ def make_enums(self, config_section):

def make_integer_enums(self, config_section):
def is_intstr(s):
if not isinstance(s, six.string_types):
return isinstance(s, int)
if s and s[0] in ('-', '+'):
return str(s[1:]).isdigit()
return str(s).isdigit()
Expand Down Expand Up @@ -467,6 +470,10 @@ def SOLD_OUT_MERCH_TIERS(self):
return [price for price, name in self.DONATION_TIERS.items() if price >= self.SEASON_LEVEL]

return []

@property
def AVAILABLE_MERCH_TIERS(self):
return [price for price, name in self.DONATION_TIERS.items() if price not in self.SOLD_OUT_MERCH_TIERS]

@property
def FORMATTED_MERCH_TIERS(self):
Expand Down
4 changes: 1 addition & 3 deletions uber/forms/attendee.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from wtforms.validators import ValidationError, StopValidation

from uber.config import c
from uber.forms import AddressForm, MultiCheckbox, MagForm, SwitchInput, DollarInput, HiddenIntField, CustomValidation
from uber.forms import AddressForm, MultiCheckbox, MagForm, SwitchInput, NumberInputGroup, HiddenIntField, CustomValidation
from uber.custom_tags import popup_link
from uber.model_checks import invalid_phone_number

Expand Down Expand Up @@ -173,10 +173,8 @@ def valid_ec_phone(form, field):

class BadgeExtras(MagForm):
field_validation, new_or_changed_validation = CustomValidation(), CustomValidation()
field_aliases = {'badge_type': ['upgrade_badge_type']}

badge_type = HiddenIntField('Badge Type')
upgrade_badge_type = HiddenIntField('Badge Type')
amount_extra = HiddenIntField('Pre-order Merch', validators=[
validators.NumberRange(min=0, message="Amount extra must be a number that is 0 or higher.")
])
Expand Down
4 changes: 2 additions & 2 deletions uber/receipt_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def mailing_fee_cost(app):
Attendee.cost_changes = {
'overridden_price': ('Custom Badge Price', "calc_badge_cost_change"),
'badge_type': ('Badge ({})', "calc_badge_cost_change", c.BADGES),
'amount_extra': ('Kickin ({})', None, c.DONATION_TIERS),
'amount_extra': ('Preordered Merch ({})', None, c.DONATION_TIERS),
'extra_donation': ('Extra Donation', None),
}

Expand Down Expand Up @@ -96,7 +96,7 @@ def donation_cost(attendee):

@cost_calculation.Attendee
def kickin_cost(attendee):
return ("Kickin ({})".format(attendee.amount_extra_label),
return ("Preordered Merch ({})".format(attendee.amount_extra_label),
attendee.amount_extra * 100, 'amount_extra') if attendee.amount_extra else None

@credit_calculation.Attendee
Expand Down
84 changes: 57 additions & 27 deletions uber/templates/forms/attendee/badge_extras.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,51 @@
add_ons
perk_info
donation
upgrade_modal_js

Use these to add or rearrange fields. Remember to use {{ super() }} to print the original fields as-is.
#}

{% set id_upgrade_prepend = "upgrade_" if upgrade_modal else "" %}

{% block badge_type %}
{% if not upgrade_modal %}{{ badge_extras.badge_type }}{% endif %}
{{ badge_extras.badge_type(id=id_upgrade_prepend ~ "badge_type") }}

{% if c.BADGE_TYPE_PRICES and (not receipt or upgrade_modal) %}
<div class="row g-sm-3">
{{ form_macros.card_select(badge_extras.upgrade_badge_type if upgrade_modal else badge_extras.badge_type,
attendee.available_badge_type_opts, disabled_opts=c.SOLD_OUT_BADGE_TYPES) }}
</div>
{% if upgrade_modal %}
<script type="text/javascript">
$(function () {
$("#upgrade_badge_type").on('change', function() {
updateReceiptPreview('badge_type', this.value);
})
});
</script>
{% endif %}
<div class="row g-sm-3">
{{ form_macros.card_select(badge_extras.badge_type,
attendee.available_badge_type_opts, disabled_opts=c.SOLD_OUT_BADGE_TYPES,
target_field_id=id_upgrade_prepend ~ "badge_type") }}
</div>
{% else %}
<div class="row g-sm-3">
<div class="col-12 col-sm-6">
<div class="form-text">{{ badge_extras.badge_type.label.text }}</div>
<div class="form-control-plaintext h5">{{ attendee.badge_type_label }}{% if c.MAX_BADGE_TYPE_UPGRADE and attendee.badge_type != c.MAX_BADGE_TYPE_UPGRADE %}{{ macros.upgrade_button('badge-type') }}{% endif %}</div>
<div class="row g-sm-3">
<div class="col-12 col-sm-6">
<div class="form-text">{{ badge_extras.badge_type.label.text }}</div>
<div class="form-control-plaintext h5">{{ attendee.badge_type_label }}{% if c.MAX_BADGE_TYPE_UPGRADE and attendee.badge_type != c.MAX_BADGE_TYPE_UPGRADE %}{{ macros.upgrade_button('badge-type') }}{% endif %}</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}

{% block add_ons %}
{{ badge_extras.amount_extra }}
{% if c.DONATIONS_ENABLED %}
<div class="row g-sm-3">
{{ form_macros.card_select(badge_extras.amount_extra, attendee.available_amount_extra_opts, disabled_opts=c.SOLD_OUT_MERCH_TIERS) }}
</div>
{{ form_macros.toggle_fields_js(badge_extras.amount_extra, [badge_extras.shirt], off_values=["0"], toggle_required=True, closest_hide_selector='.row' if not attendee.gets_staff_shirt or c.SHIRT_OPTS == c.STAFF_SHIRT_OPTS else '.form-floating') }}
{{ badge_extras.amount_extra(id=id_upgrade_prepend ~ "amount_extra") }}

{% if c.DONATIONS_ENABLED and (not receipt or upgrade_modal) %}
<div class="row g-sm-3">
{{ form_macros.card_select(badge_extras.amount_extra,
attendee.available_amount_extra_opts, disabled_opts=c.SOLD_OUT_MERCH_TIERS,
target_field_id=id_upgrade_prepend ~ "amount_extra") }}
</div>
{{ form_macros.toggle_fields_js(badge_extras.amount_extra, [badge_extras.shirt], off_values=["0"], toggle_required=True,
closest_hide_selector='.row' if not attendee.gets_staff_shirt or c.SHIRT_OPTS == c.STAFF_SHIRT_OPTS else '.form-floating',
source_field_id=id_upgrade_prepend ~ "amount_extra") }}
{% else %}
<div class="row g-sm-3">
<div class="col-12 col-sm-6">
<div class="form-text">{{ badge_extras.amount_extra.label.text }}</div>
<div class="form-control-plaintext h5">{{ attendee.amount_extra_label }}{% if c.AVAILABLE_MERCH_TIERS and attendee.amount_extra < c.AVAILABLE_MERCH_TIERS[-1] %}{{ macros.upgrade_button('amount-extra') }}{% endif %}</div>
</div>
</div>
{% endif %}
{% endblock %}

Expand All @@ -65,21 +72,44 @@
{% block donation %}
{% if c.COLLECT_EXTRA_DONATION %}
<div class="row g-sm-3">
<div class="col-12 col-sm-6">{{ form_macros.form_input(badge_extras.extra_donation) }}</div>
<div class="col-12 col-sm-6">{{ form_macros.form_input(badge_extras.extra_donation, id=id_upgrade_prepend ~ "extra_donation") }}</div>

{% if not admin_area %}
<div class="col-12 col-sm-6">
<div class="alert alert-warning" role="alert">
<strong>This donation <em>is not a kick-in</em> and does not come with merchandise.</strong><br/>
{{ c.ORGANIZATION_NAME }} is a 501(c)(3) charitable organization, and additional donations may be tax deductible.
Your employer may also have a charitable donation matching program. Email [email protected] for details.
Your employer may also have a charitable donation matching program. Contact us at {{ c.CONTACT_URL }} for details.
</div>
</div>
{% endif %}

{% if receipt and not upgrade_modal and not admin_area %}
<div class="form-text">&nbsp;</div>
<div class="col-12 col-sm-6">{{ macros.upgrade_button('extra-donation', extra_classes="") }}</div>
{% endif %}

{% if c.EXTRA_DONATION_URL %}
<div class="col-12 col-sm-6">If you're interested in kicking in an extra donation, you can{% if c.COLLECT_EXTRA_DONATION %} also{% endif %} do so at any time of year at <a href="{{ c.EXTRA_DONATION_URL }}" target="_blank">{{ c.EXTRA_DONATION_URL }}</a>!</div>
{% endif %}
</div>
{% endif %}
{% endblock %}

{% block upgrade_modal_js %}
{% if upgrade_modal %}
<script type="text/javascript">
$(function () {
$("#upgrade_badge_type").on('change', function() {
updateReceiptPreview('badge_type', this.value);
})
$("#upgrade_amount_extra").on('change', function() {
updateReceiptPreview('amount_extra', this.value);
})
$("#upgrade_extra_donation").on('blur', function() {
updateReceiptPreview('extra_donation', this.value);
})
});
</script>
{% endif %}
{% endblock %}
28 changes: 15 additions & 13 deletions uber/templates/forms/macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
{% macro form_input(field, help_text='', admin_text='', extra_field=None, no_margin=False) %}

{% set custom_kwargs = kwargs %}
{% if locked_fields|default(None, true) and field.name in locked_fields %}
{% if locked_fields|default(None, true) and field.name in locked_fields and not upgrade_modal %}
{% set _ = custom_kwargs.update({'readonly': true}) %}
{% endif %}

Expand Down Expand Up @@ -86,20 +86,21 @@
{{ toggle_fields_js(checkbox_field, target_fields, off_values=[1 if hide_on_checked == True else 0], toggle_required=toggle_required, prop=prop, closest_hide_selector=closest_hide_selector) }}
{%- endmacro %}

{% macro toggle_fields_js(source_field, target_fields, off_values=[], on_values=[], toggle_required=False, prop='visibility', closest_hide_selector='.row') %}
{% set suffix = "_" ~ source_field.id %}
{% macro toggle_fields_js(source_field, target_fields, off_values=[], on_values=[], toggle_required=False, prop='visibility', closest_hide_selector='.row', source_field_id="") %}
{% set source_id = source_field_id or source_field.id %}
{% set suffix = "_" ~ source_id %}
<script type="text/javascript">
var toggleField{{ suffix }} = function() {
var toggleOn, toggleVal;

// Get the checked/unchecked status if the field is a checkbox, otherwise get the field's value
if ($("#{{ source_field.id }}").is(':checkbox')) {
toggleVal = $('#{{ source_field.id }}').prop('checked') ? "1" : "0";
} else if ($("#{{ source_field.id }}").is("fieldset")) {
if ($("#{{ source_id }}").is(':checkbox')) {
toggleVal = $('#{{ source_id }}').prop('checked') ? "1" : "0";
} else if ($("#{{ source_id }}").is("fieldset")) {
// For checkgroup source fields, we only support a single on_value
toggleOn = $('#{{ source_field.id }}-{{ on_values[0]|string }}').prop('checked') ? true : false;
toggleOn = $('#{{ source_id }}-{{ on_values[0]|string }}').prop('checked') ? true : false;
} else {
toggleVal = $("#{{ source_field.id }}").val();
toggleVal = $("#{{ source_id }}").val();
}

// Set whether we should toggle the target fields "on" or "off" based on the values provided
Expand Down Expand Up @@ -132,12 +133,12 @@

$(function () {
toggleField{{ suffix }}();
$('#{{ source_field.id }}').change(toggleField{{ suffix }});
$('#{{ source_id }}').change(toggleField{{ suffix }});
});
</script>
{%- endmacro %}

{% macro card_select(target_field, opts, text_class="text-center", help_text="", disabled_opts=[], disabled_card_text="Sold Out") %}
{% macro card_select(target_field, opts, text_class="text-center", help_text="", disabled_opts=[], target_field_id="", disabled_card_text="Sold Out") %}
{#
A set of card elements generated from opts. Each item in opts should include the following fields:
name (str): The title displayed at the top of the card.
Expand All @@ -153,6 +154,7 @@

{% set is_preview = 'landing' in c.PAGE_PATH %}
{% set label_required = kwargs['required'] if 'required' in kwargs else None %}
{% set field_id = target_field_id or target_field.id %}
{% set show_desc = not admin_area and not is_preview and (help_text or target_field.description) %}

{% if is_preview %}
Expand All @@ -168,7 +170,7 @@
{% set disabled_card = opt.value in disabled_opts %}
<div class="card {{ target_field.name }}_card {{ text_class }}{% if disabled_card %} disabled-card bg-secondary text-white{% endif %}" style="max-width: 18rem;">
<div class="card-header" style="transform: rotate(0);">
<label for="{{ target_field.id }}-{{ opt.value }}" class="h5 card-title mb-0 text-nowrap">
<label for="{{ field_id }}-{{ opt.value }}" class="h5 card-title mb-0 text-nowrap">
{% if disabled_card or is_preview %}
{{ opt.name }}{% if opt.price %}: {{ opt.price|format_currency }}{% endif %}
{% else %}
Expand All @@ -194,7 +196,7 @@
{% else %}
<a href="#" class="card-link stretched-link text-reset text-decoration-none {{ target_field.name }}_card_link"
data-value="{{ opt.value }}">{{ opt.desc|safe }}</a>
<input type="radio" id="{{ target_field.id }}-{{ opt.value }}" name="card-{{ target_field.id }}" class="visually-hidden" value="{{ opt.value }}" />
<input type="radio" id="{{ field_id }}-{{ opt.value }}" name="card-{{ field_id }}" class="visually-hidden" value="{{ opt.value }}" />
{% endif %}
</div>
{% if opt.link %}
Expand All @@ -212,7 +214,7 @@

<script type="text/javascript">
var setField_{{ target_field.name }} = function(value) {
$("#{{ target_field.id }}").val(value).trigger('change');
$("#{{ field_id }}").val(value).trigger('change');
$(".{{ target_field.name }}_card").each(function(){
var header = $(this).children('.card-header');
var body = $(this).children('.card-body');
Expand Down
4 changes: 2 additions & 2 deletions uber/templates/macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
{%- endmacro %}


{% macro upgrade_button(upgrade_btn_id, text="Upgrade") %}
{% macro upgrade_button(upgrade_btn_id, text="Upgrade", extra_classes=" btn-sm") %}
{% if receipt and receipt.current_amount_owed <= 0 %}
<a class="btn btn-sm btn-success" id="upgrade-{{ upgrade_btn_id }}-btn" data-bs-toggle="modal" data-bs-target="#upgradeModal">
<a class="btn btn-success{{ extra_classes }}" id="upgrade-{{ upgrade_btn_id }}-btn" data-bs-toggle="modal" data-bs-target="#upgradeModal">
<span class="glyphicon glyphicon-circle-arrow-up"></span> {{ text }}
</a>
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion uber/templates/preregistration/upgrade_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
<form method="post" action="purchase_upgrades" id="purchase-upgrades" class="form-horizontal">
{{ csrf_token() }}
<input type="hidden" name="id" value="{{ attendee.id }}" />
<input type="hidden" id="upgrade_badge_type" name="badge_type" value="{{ attendee.badge_type }}" />
<input type="hidden" name="receipt_id" value="{{ receipt.id }}" />
{% set upgrade_modal = true %}
{% include "forms/attendee/badge_extras.html" %}
Expand Down Expand Up @@ -98,6 +97,7 @@
$btn.prop('disabled', false);
if (result.error) {
showErrorMessage(result.error, 'upgrade-message-alert');
window.scrollTo(0,0);
} else if (result.success) {
upgradeModal.hide();
callStripeAction(autoTrigger=true);
Expand Down

0 comments on commit f2f3499

Please sign in to comment.