supporters: Use data-binding for amount rather than events
Also tidied forms.py a little by consolidating into widget and moving help text to template.
This commit is contained in:
parent
c843e1c59f
commit
48048f349a
4 changed files with 27 additions and 19 deletions
|
@ -58,7 +58,17 @@ class SustainerForm(forms.ModelForm):
|
||||||
attrs={
|
attrs={
|
||||||
'x-model': 'recurring',
|
'x-model': 'recurring',
|
||||||
# Reset the amount field and option when changing monthly/annually.
|
# Reset the amount field and option when changing monthly/annually.
|
||||||
'x-on:change': '$refs.amount.value = null; amount_option = null',
|
'x-on:change': 'amount = ""; amount_option = null',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'amount': forms.widgets.NumberInput(
|
||||||
|
# Keeping default widget, just neater to add many attrs here.
|
||||||
|
attrs={
|
||||||
|
# So we can update the amount field from the amount_option selected.
|
||||||
|
'x-model': 'amount',
|
||||||
|
'x-bind:min': 'amount_minimum',
|
||||||
|
'onblur': 'this.reportValidity()',
|
||||||
|
'style': 'width: 5rem',
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -70,15 +80,6 @@ class SustainerForm(forms.ModelForm):
|
||||||
|
|
||||||
self.fields['recurring'].label = ''
|
self.fields['recurring'].label = ''
|
||||||
self.fields['amount'].initial = self.YEAR_OPTIONS[0]
|
self.fields['amount'].initial = self.YEAR_OPTIONS[0]
|
||||||
# So we can write to this field easily from Alpine JS.
|
|
||||||
self.fields['amount'].widget.attrs['x-ref'] = 'amount'
|
|
||||||
self.fields['amount'].widget.attrs['style'] = 'width: 5rem'
|
|
||||||
self.fields['amount'].widget.attrs['onblur'] = 'this.reportValidity()'
|
|
||||||
self.fields['amount'].widget.attrs['x-bind:min'] = 'amount_minimum'
|
|
||||||
self.fields['email'].help_text = 'For your payment receipt'
|
|
||||||
self.fields['tshirt_size'].help_text = mark_safe(
|
|
||||||
"""Sizing chart: <a href="/videos/women-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Women's</a>, <a href="/videos/men-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Men's</a>"""
|
|
||||||
)
|
|
||||||
self.fields['tshirt_size'].widget.attrs['x-model'] = 'tshirt_size'
|
self.fields['tshirt_size'].widget.attrs['x-model'] = 'tshirt_size'
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
<!-- Custom <span> wrapper around the label to enable radio fields to be styled like buttons. -->
|
{# Custom <span> wrapper around the label to enable radio fields to be styled like buttons. #}
|
||||||
{% if widget.wrap_label %}<label onclick="click()"{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}{% include "django/forms/widgets/input.html" %}{% if widget.wrap_label %} <span>{{ widget.label }}</span></label>{% endif %}
|
{% if widget.wrap_label %}<label onclick="click()"{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}{% include "django/forms/widgets/input.html" %}{% if widget.wrap_label %} <span>{{ widget.label }}</span></label>{% endif %}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- Labels on a separate line, custom help text layout -->
|
{# Labels on a separate line, custom help text layout #}
|
||||||
{% if field.use_fieldset %}
|
{% if field.use_fieldset %}
|
||||||
<fieldset{% if field.help_text and field.auto_id and "aria-describedby" not in field.field.widget.attrs %} aria-describedby="{{ field.auto_id }}_helptext"{% endif %}>
|
<fieldset{% if field.help_text and field.auto_id and "aria-describedby" not in field.field.widget.attrs %} aria-describedby="{{ field.auto_id }}_helptext"{% endif %}>
|
||||||
{% if field.label %}{{ field.legend_tag }}{% endif %}
|
{% if field.label %}{{ field.legend_tag }}{% endif %}
|
||||||
|
|
|
@ -52,8 +52,9 @@
|
||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
<!-- Alpine JS is used to show different payments amounts for monthly/annual, write the selected payment amount into the "amount" field, reset the seleted amount when you change monthly/annual and pop out the address when you select a T-shirt. -->
|
{# Alpine JS is used to show different payments amounts for monthly/annual, write the selected payment amount into the "amount" field, reset the seleted amount when you change monthly/annual and pop out the address when you select a T-shirt. #}
|
||||||
<form method="post" action="."
|
<form method="post" action="."
|
||||||
|
{# Pre-fill field defaults in case of server-side validation error. Otherwise Alpine JS will override them. #}
|
||||||
x-data="{
|
x-data="{
|
||||||
recurring: '{{ form.recurring.value|escapejs }}',
|
recurring: '{{ form.recurring.value|escapejs }}',
|
||||||
amount: parseInt('{{ form.amount.value|escapejs }}'),
|
amount: parseInt('{{ form.amount.value|escapejs }}'),
|
||||||
|
@ -82,34 +83,40 @@
|
||||||
<template x-for="m in amount_options">
|
<template x-for="m in amount_options">
|
||||||
{# Additional click handler ensures a click-drag activates the radio (similar to a real button). #}
|
{# Additional click handler ensures a click-drag activates the radio (similar to a real button). #}
|
||||||
<label onclick="this.click()">
|
<label onclick="this.click()">
|
||||||
{# It seems to be important that all radios have a unique value to avoid UI glitches. #}
|
{# All radios have a unique value to avoid UI glitches (even though the value isn't actually used). #}
|
||||||
<input type="radio" name="amount_option" x-bind:value="m" x-on:change="$refs.amount.value = m" x-model="amount_option" required>
|
<input type="radio" name="amount_option" x-bind:value="m" x-on:change="amount = m" x-model="amount_option" required>
|
||||||
<span>$<span x-text="m.toLocaleString()"></span></span>
|
<span>$<span x-text="m.toLocaleString()"></span></span>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
<!-- Hide if no JS -->
|
<!-- Hide if no JS -->
|
||||||
<template x-if="true">
|
<template x-if="true">
|
||||||
<label onclick="this.click()">
|
<label onclick="this.click()">
|
||||||
<input type="radio" name="amount_option" value="other" x-on:change="$refs.amount.value = ''" x-model="amount_option" required>
|
<input type="radio" name="amount_option" value="other" x-on:change="amount = ''" x-model="amount_option" required>
|
||||||
<span>Other</span>
|
<span>Other</span>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt2" x-show="amount_option === 'other'">
|
<div class="mt2" x-show="amount_option === 'other'">
|
||||||
{{ form.amount.as_field_group }}
|
{{ form.amount.as_field_group }}
|
||||||
|
<p class="f7 black-60 mt1">Minimum $<span x-text="amount_minimum"></span>. <a href="/donate" class="black-60">Donate smaller amounts here</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt3">{{ form.name.as_field_group }}</div>
|
<div class="mt3">{{ form.name.as_field_group }}</div>
|
||||||
|
|
||||||
<div class="mt2">{{ form.email.as_field_group }}</div>
|
<div class="mt2">
|
||||||
|
{{ form.email.as_field_group }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt3"><label class="lh-title">{{ form.acknowledge_publicly }} Acknowledge me on the <a href="/sponsors#sustainers" target="_blank">list of sustainers</a></label></div>
|
<div class="mt3"><label class="lh-title">{{ form.acknowledge_publicly }} Acknowledge me on the <a href="/sponsors#sustainers" target="_blank">list of sustainers</a></label></div>
|
||||||
|
|
||||||
<div class="mt3"><label class="lh-title">{{ form.add_to_mailing_list }} Add me to the <a href="https://lists.sfconservancy.org/pipermail/announce/">announcements</a> email list</label></div>
|
<div class="mt3"><label class="lh-title">{{ form.add_to_mailing_list }} Add me to the <a href="https://lists.sfconservancy.org/pipermail/announce/">announcements email list</a></label></div>
|
||||||
|
|
||||||
<div class="mt3">
|
<div class="mt3">
|
||||||
{{ form.tshirt_size.as_field_group }}
|
{{ form.tshirt_size.as_field_group }}
|
||||||
|
<p class="f7 black-60 mt1">Sizing chart:
|
||||||
|
<a href="/videos/women-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Women's</a>,
|
||||||
|
<a href="/videos/men-2017-to-2020-t-shirt-sizing.jpg" target="_blank" class="black-60">Men's</a></p>
|
||||||
<figure class="mt2">
|
<figure class="mt2">
|
||||||
<img src="/static/img/tshirt-2023.png" alt="Software Freedom Conservancy T-shirt" width="200">
|
<img src="/static/img/tshirt-2023.png" alt="Software Freedom Conservancy T-shirt" width="200">
|
||||||
</figure>
|
</figure>
|
||||||
|
@ -130,7 +137,7 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p class="f7 mt3">Credit card and ACH payments are processed with Stripe. We also accept payment by PayPal, paper check and wire transfer (see below). Our sustainer program has a minimum of $120 USD per year, but we also accept <a href="/donate/">donations of smaller amounts</a>.</p>
|
<p class="f7 mt3">Credit card and ACH payments are processed with Stripe. We also accept payment by PayPal, paper check and wire transfer.</p>
|
||||||
|
|
||||||
<details id="paypal">
|
<details id="paypal">
|
||||||
<summary class="f6">PayPal</summary>
|
<summary class="f6">PayPal</summary>
|
||||||
|
|
Loading…
Reference in a new issue