diff --git a/donations/forms.py b/donations/forms.py index 155693b39..dac5f9cdc 100644 --- a/donations/forms.py +++ b/donations/forms.py @@ -1,26 +1,33 @@ -import json import base64 +import json + from django import forms from django.utils.safestring import mark_safe from .models import DonationCampaign + class DonateForm(forms.Form): RADIO_CHOICES = [] donation_type = forms.ChoiceField( - widget=forms.RadioSelect(), choices=RADIO_CHOICES, - label=mark_safe('Please choose the name that will appear with the donation:')) - name_option = forms.CharField( - required=False, max_length=255, label=False) + widget=forms.RadioSelect(), + choices=RADIO_CHOICES, + label=mark_safe('Please choose the name that will appear with the donation:') + ) + name_option = forms.CharField(required=False, max_length=255, label=False) amount = forms.FloatField( - initial=10.0, min_value=0., label=mark_safe('Donation amount (€):')) - recurring = forms.BooleanField(required=False, initial=False, - label='I want this to be a recurring monthly donation',) - show_amount = forms.BooleanField( - label='Make donated amount public', - required=False, - initial=True) + initial=10.0, + min_value=1, + max_value=999999.99, # https://stripe.com/docs/currencies#minimum-and-maximum-charge-amounts + label=mark_safe('Donation amount (€):') + ) + recurring = forms.BooleanField( + required=False, + initial=False, + label='I want this to be a recurring monthly donation', + ) + show_amount = forms.BooleanField(label='Make donated amount public', required=False, initial=True) def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) @@ -56,23 +63,14 @@ def __init__(self, *args, **kwargs): def clean(self): cleaned_data = super().clean() - amount = cleaned_data.get('amount') - try: - if not amount or float(amount) < 1: - raise forms.ValidationError('The amount must be more than 1') - except ValueError: - raise forms.ValidationError('The amount must be a valid number, use \'.\' for decimals') campaign = DonationCampaign.objects.order_by('date_start').last() - returned_data = { - "campaign_id": campaign.id, - "display_amount": cleaned_data.get('show_amount') - } + returned_data = {"campaign_id": campaign.id, "display_amount": cleaned_data.get('show_amount')} annon = cleaned_data.get('donation_type') - # We store the user even if the donation is annonymous (but don't display the name) - if self.user_id : + # We store the user even if the donation is anonymous (but don't display the name) + if self.user_id: returned_data['user_id'] = self.user_id if annon == '1': @@ -82,6 +80,6 @@ def clean(self): if returned_data['name'] == '': raise forms.ValidationError('You have to enter a name to display') - # Paypal gives only one field to add extra data so we send it as b64 + # Paypal gives only one field to add extra data, so we send it as b64 self.encoded_data = base64.b64encode(json.dumps(returned_data).encode()).decode() return cleaned_data diff --git a/freesound/static/bw-frontend/src/pages/donate.js b/freesound/static/bw-frontend/src/pages/donate.js index 9e09157f5..94b8f036f 100644 --- a/freesound/static/bw-frontend/src/pages/donate.js +++ b/freesound/static/bw-frontend/src/pages/donate.js @@ -24,14 +24,34 @@ nameOptionRadioButtons.forEach(element => { // Disable credit card button if recurring option is checked recurringCheckboxElement.addEventListener('change', () => { - if (recurringCheckboxElement.checked) { - donationButtonCreditCardElement.disabled = true; - } else { - donationButtonCreditCardElement.disabled = false; - } + donationButtonCreditCardElement.disabled = !!recurringCheckboxElement.checked; }); +function handleErrors(errors) { + Object.entries(errors).forEach(([key, values]) => { + const liElement = document.createElement("li"); + liElement.innerText = key; + const newList = document.createElement("ul"); + newList.classList.add("errorlist", "v-spacing-3"); + values.forEach(error => { + const newLi = document.createElement("li"); + newLi.innerText = error; + newList.appendChild(newLi); + }); + // if we can find the element with the key as id, we add the error message to it + const elemError = document.getElementById(`id_${key}`); + if (elemError) { + // add an error message below the input field + elemError.insertAdjacentElement('afterend', newList); + } else { + // otherwise, we add it to the general error list (e.g., when key is '__all__') + formErrorlistElement.appendChild(liElement); + formErrorlistElement.appendChild(newList); + } + }); +} + // Add actions for donate credit card/paypal buttons donationButtonPaypalElement.addEventListener('click', (event) => { event.preventDefault(); // Stop propagation of submit event @@ -43,12 +63,7 @@ donationButtonPaypalElement.addEventListener('click', (event) => { if (req.status >= 200 && req.status < 300) { const data = JSON.parse(req.responseText); if (data.errors != null) { - formErrorlistElement.innerHTML = ''; - data.errors['__all__'].forEach(error => { - const liElement = document.createElement("li"); - liElement.innerText = error; - formErrorlistElement.appendChild(liElement); - }); + handleErrors(data.errors); } else { const hiddenFormElement = document.createElement("form"); hiddenFormElement.setAttribute("action", data.url); @@ -68,7 +83,7 @@ donationButtonPaypalElement.addEventListener('click', (event) => { } } req.onerror = () => { - // Unexpected errors happened while processing request: how error in toast + // Unexpected errors happened while processing request: show error in toast showToast('Some errors occurred while processing the form. Please try again later.') }; req.send(params); // Send request @@ -87,12 +102,7 @@ donationButtonCreditCardElement.addEventListener('click', (event) => { if (req.status >= 200 && req.status < 300) { const data = JSON.parse(req.responseText); if (data.errors != null) { - formErrorlistElement.innerHTML = ''; - data.errors['__all__'].forEach(error => { - const liElement = document.createElement("li"); - liElement.innerText = error; - formErrorlistElement.appendChild(liElement); - }); + handleErrors(data.errors) } else { const session = data.session_id; stripe.redirectToCheckout({ @@ -112,4 +122,3 @@ donationButtonCreditCardElement.addEventListener('click', (event) => { }; req.send(params); // Send request }); -