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 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: Women's, Men's""" ) 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. Donate smaller amounts here.' ), ) 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')