supporters: Fix monthly amounts, validate minimum amount in frontend, update PayPal info

This commit is contained in:
Ben Sturmfels 2024-10-23 18:10:43 +11:00
parent e9f0909d8c
commit 68c5199bb5
Signed by: bsturmfels
GPG key ID: 023C05E2C9C068F0
5 changed files with 34 additions and 17 deletions

View file

@ -3,9 +3,6 @@ from django.utils.safestring import mark_safe
from django.urls import reverse
from .models import SustainerOrder
MONTH_MINIMUM = 10
YEAR_MINIMUM = 120
class SustainerFormRenderer(forms.renderers.DjangoTemplates):
# Customised layout with labels on own row
field_template_name = 'supporters/field.html'
@ -33,8 +30,10 @@ class SustainerForm(forms.ModelForm):
template_name = 'supporters/sustainer_form.html'
MONTH_OPTIONS = [12, 25, 50, 100]
YEAR_OPTIONS = [128, 250, 500, 1000]
MONTH_OPTIONS = [12, 23, 45, 87]
YEAR_OPTIONS = [128, 256, 512, 1024]
MONTH_MINIMUM = 10
YEAR_MINIMUM = 120
class Meta:
model = SustainerOrder
@ -70,6 +69,8 @@ class SustainerForm(forms.ModelForm):
# 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'
@ -78,7 +79,7 @@ class SustainerForm(forms.ModelForm):
super().clean()
recurring = self.cleaned_data.get('recurring', '')
amount = self.cleaned_data.get('amount', 0)
minimum = MONTH_MINIMUM if recurring == 'month' else YEAR_MINIMUM
minimum = self.MONTH_MINIMUM if recurring == 'month' else self.YEAR_MINIMUM
donate_url = reverse('donate')
if amount < minimum:
self.add_error(

View file

@ -7,6 +7,7 @@
.button-select label > span {
text-align: center;
display: inline-block;
user-select: none;
padding: 0.5rem 0;
width: 100%;
background: #ddd;

View file

@ -1,2 +1,2 @@
<!-- Custom <span> wrapper around the label to enable radio fields to be styled like buttons. -->
{% if widget.wrap_label %}<label{% 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 %}

View file

@ -4,5 +4,12 @@
{% block category %}sustainer{% endblock %}
{% block content %}
<h1 class="lh-title tc mt4 mb4">Thanks!</h1>
<h1 class="lh-title tc mt4">Thanks</h1>
<p class="measure-wide center tc">Thank you for helping make our work possible!</p>
<div class="mt4 tc">
<img src="{% static 'img/conservancy-supporter-heart.png' %}" alt="Heart with Conservancy logo">
</div>
<p class="tc mt4 mb5">Return to our <a href="{% url 'sustainers' %}">sustainers</a> page.</p>
{% endblock %}

View file

@ -63,25 +63,33 @@
let year_options = {{ form.YEAR_OPTIONS|escapejs }};
return this.recurring === 'month' ? month_options : year_options;
},
amount_minimum: function() {
let month_minimum = {{ form.MONTH_MINIMUM|escapejs }};
let year_minimum = {{ form.YEAR_MINIMUM|escapejs }};
return this.recurring === 'month' ? month_minimum : year_minimum;
},
tshirt_size: '{{ form.tshirt_size.value|escapejs }}',
}">
{% csrf_token %}
<fieldset class="bg-black-05 pa3 br3 center" style="border: 1px solid #ccc">
<legend class="b f5">Become a Sustainer</legend>
{{ form.non_field_errors }}
<div>{{ form.recurring.as_field_group }}</div>
<div class="mt3">
<div id="amount_options" class="button-select">
<template x-for="m in amount_options">
<label>
<input type="radio" name="amount_option" x-bind:value="m" x-on:change="$refs.amount.value = $event.currentTarget.value" x-model="amount_option" required>
{# Additional click handler ensures a click-drag activates the radio (similar to a real button). #}
<label onclick="this.click()">
{# It seems to be important that all radios have a unique value to avoid UI glitches. #}
<input type="radio" name="amount_option" x-bind:value="m" x-on:change="$refs.amount.value = m" x-model="amount_option" required>
<span>$<span x-text="m.toLocaleString()"></span></span>
</label>
</template>
<!-- Hide if no JS -->
<template x-if="true">
<label>
<label onclick="this.click()">
<input type="radio" name="amount_option" value="other" x-on:change="$refs.amount.value = ''" x-model="amount_option" required>
<span>Other</span>
</label>
@ -122,7 +130,12 @@
</fieldset>
</form>
<p class="f7 mt3">Credit card and ACH payments are processed with Stripe. We also accept payment by paper check and wire transfer and PayPal (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 (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>
<details id="paypal">
<summary class="f6">PayPal</summary>
<p>If you would prefer not to use our Stripe payment service above you can use <a href="{% url 'sustainer_paypal' %}">PayPal</a>.</p>
</details>
<details id="wire-transfer">
<summary class="f6">Wire Transfer</summary>
@ -139,11 +152,6 @@
<p>Please write <q>SUSTAINER</q>, T-shirt size, if you are renewing, and if
you want public acknowledgment in memo line.</p>
</details>
<details id="paypal">
<summary class="f6">PayPal</summary>
<p>Please visit our <a href="{% url 'sustainer_paypal' %}">Become a Sustainer by PayPal</a> page.</p>
</details>
</section>
<section style="grid-row: 1 / span 2">