website/conservancy/supporters/forms.py

105 lines
3.6 KiB
Python

from django import forms
from django.utils.safestring import mark_safe
from django.urls import reverse
from .models import SustainerOrder
class SustainerFormRenderer(forms.renderers.DjangoTemplates):
# Customised layout with labels on own row
field_template_name = 'supporters/field.html'
class ButtonRadioSelect(forms.widgets.RadioSelect):
"""Radio button styled like a button. BYO CSS."""
# Extra <span> wrappers to support CSS
option_template_name = 'supporters/buttonradio_option.html'
use_fieldset = False
class Media:
css = {
'all': ['css/buttonradio.css'],
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attrs['class'] = 'button-select'
class SustainerForm(forms.ModelForm):
# Used to pre-fill the price selector
amount_option = forms.CharField(required=False)
template_name = 'supporters/sustainer_form.html'
MONTH_OPTIONS = [12, 23, 45, 87]
YEAR_OPTIONS = [128, 256, 512, 1024]
MONTH_MINIMUM = 10
YEAR_MINIMUM = 120
class Meta:
model = SustainerOrder
fields = [
'recurring',
'amount',
'name',
'email',
'acknowledge_publicly',
'add_to_mailing_list',
'tshirt_size',
'street',
'city',
'state',
'zip_code',
'country',
]
widgets = {
'recurring': ButtonRadioSelect(
attrs={
'x-model': 'recurring',
# Reset the amount field and option when changing monthly/annually.
'x-on:change': '$refs.amount.value = null; amount_option = null',
}
),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.renderer = SustainerFormRenderer()
self.fields['recurring'].label = ''
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'
def clean(self):
super().clean()
recurring = self.cleaned_data.get('recurring', '')
amount = self.cleaned_data.get('amount', 0)
minimum = self.MONTH_MINIMUM if recurring == 'month' else self.YEAR_MINIMUM
donate_url = reverse('donate')
if amount < minimum:
self.add_error(
'',
mark_safe(
f'${minimum:d} is a minimum for Conservancy Sustainers. <a href="{donate_url}">Donate smaller amounts here</a>.'
),
)
tshirt_size = self.cleaned_data.get('tshirt_size')
if tshirt_size and not all(
[
self.cleaned_data.get('street'),
self.cleaned_data.get('city'),
self.cleaned_data.get('country'),
]
):
self.add_error('street', 'No address provided')