Nicer membership form.
authorRadek Czajka <rczajka@rczajka.pl>
Mon, 20 May 2019 13:00:23 +0000 (15:00 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Mon, 20 May 2019 13:00:23 +0000 (15:00 +0200)
16 files changed:
src/club/forms.py
src/club/migrations/0009_auto_20190510_1510.py [new file with mode: 0644]
src/club/models.py
src/club/payment_methods.py
src/club/static/club/form.js [new file with mode: 0644]
src/club/static/club/paypal.png [new file with mode: 0644]
src/club/static/club/payu/blik.png [new file with mode: 0644]
src/club/static/club/payu/payu.png [new file with mode: 0644]
src/club/templates/club/membership_form.html
src/club/templates/club/payment/paypal-re.html [deleted file]
src/club/templates/club/payment/paypal.html [new file with mode: 0644]
src/club/templates/club/payment/payu-re.html
src/club/templates/club/payment/payu.html
src/club/views.py
src/wolnelektury/settings/static.py
src/wolnelektury/static/scss/main/form.scss

index bede0cb..b818d1c 100644 (file)
@@ -1,6 +1,7 @@
+from decimal import Decimal
 from django import forms
 from . import models
-from .payment_methods import method_by_slug 
+from .payment_methods import method_by_slug, methods
 from .payu.forms import CardTokenForm
 
 
@@ -16,16 +17,29 @@ class ScheduleForm(forms.ModelForm):
     def __init__(self, *args, request=None, **kwargs):
         super(ScheduleForm, self).__init__(*args, **kwargs)
         self.request = request
-        self.fields['plan'].empty_label = None
+        self.plans = models.Plan.objects.all()
+        self.payment_methods = methods
+        self.fields['amount'].required = False
 
     def clean(self):
         cleaned_data = super(ScheduleForm, self).clean()
+
+        if 'plan' in cleaned_data:
+            cleaned_data['amount'] = self.fields['amount'].clean(
+                self.request.POST['amount-{}'.format(cleaned_data['plan'].id)]
+            )
+
+            if cleaned_data['amount'] < cleaned_data['plan'].min_amount:
+                self.add_error(
+                    'amount',
+                    'Minimalna kwota dla tego planu to %d zł.' % cleaned_data['plan'].min_amount
+                )
+
         if 'method' in cleaned_data:
             method = method_by_slug[cleaned_data['method']]
             if method not in cleaned_data['plan'].payment_methods():
-                self.add_error('method', 'Metoda płatności niedostępna dla tego planu.')
-        if cleaned_data['amount'] < cleaned_data['plan'].min_amount:
-            self.add_error('amount', 'Minimalna kwota dla tego planu to %d zł.' % cleaned_data['plan'].min_amount)
+                self.add_error('method', 'Wybrana metoda płatności nie jest dostępna dla tego planu.')
+
 
 
 class PayUCardTokenForm(CardTokenForm):
diff --git a/src/club/migrations/0009_auto_20190510_1510.py b/src/club/migrations/0009_auto_20190510_1510.py
new file mode 100644 (file)
index 0000000..57a110f
--- /dev/null
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2019-05-10 13:10
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('club', '0008_membership_name'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='plan',
+            name='default_amount',
+            field=models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='default amount'),
+            preserve_default=False,
+        ),
+        migrations.AlterField(
+            model_name='plan',
+            name='min_amount',
+            field=models.DecimalField(decimal_places=2, max_digits=10, verbose_name='min amount'),
+        ),
+        migrations.AlterField(
+            model_name='schedule',
+            name='method',
+            field=models.CharField(choices=[('payu', 'PayU'), ('payu-re', 'PayU Recurring'), ('paypal', 'PayPal')], max_length=255, verbose_name='method'),
+        ),
+    ]
index 1128070..d7be287 100644 (file)
@@ -22,7 +22,8 @@ class Plan(models.Model):
     ]
 
     interval = models.SmallIntegerField(_('inteval'), choices=intervals)
-    min_amount = models.DecimalField(_('min_amount'), max_digits=10, decimal_places=2)
+    min_amount = models.DecimalField(_('min amount'), max_digits=10, decimal_places=2)
+    default_amount = models.DecimalField(_('default amount'), max_digits=10, decimal_places=2)
     allow_recurring = models.BooleanField(_('allow recurring'))
     allow_one_time = models.BooleanField(_('allow one time'))
     active = models.BooleanField(_('active'), default=True)
index 29221ee..b215b1d 100644 (file)
@@ -3,6 +3,7 @@ from django.urls import reverse
 
 
 class PaymentMethod(object):
+    is_onetime = False
     is_recurring = False
 
     def initiate(self, request, schedule):
@@ -10,6 +11,7 @@ class PaymentMethod(object):
 
 
 class PayU(PaymentMethod):
+    is_onetime = True
     slug = 'payu'
     name = 'PayU'
     template_name = 'club/payment/payu.html'
@@ -51,11 +53,12 @@ class PayURe(PaymentMethod):
         return order.put()
         
 
-class PayPalRe(PaymentMethod):
-    slug='paypal-re'
-    name = 'PayPal Recurring'
-    template_name = 'club/payment/paypal-re.html'
+class PayPal(PaymentMethod):
+    slug='paypal'
+    name = 'PayPal'
+    template_name = 'club/payment/paypal.html'
     is_recurring = True
+    is_onetime = True
 
     def initiate(self, request, schedule):
         return reverse('club_dummy_payment', args=[schedule.key])
@@ -78,7 +81,7 @@ else:
     payure_method = None
 
 
-methods.append(PayPalRe())
+methods.append(PayPal())
 
 
 method_by_slug = {
diff --git a/src/club/static/club/form.js b/src/club/static/club/form.js
new file mode 100644 (file)
index 0000000..6be8220
--- /dev/null
@@ -0,0 +1,20 @@
+$(function() {
+
+    function update_methods() {
+        $("#payment-form .payment-method").addClass("disabled");
+        $("#payment-form .payment-method input").prop("disabled", true);
+        var plan = $("#payment-form .plan:checked");
+        if (plan.length) {
+            $.each(
+                $("#payment-form .plan:checked").attr('data-methods').trim().split(" "),
+                function(i, slug) {
+                    $("#payment-method-" + slug).removeClass("disabled");
+                    $("#payment-method-" + slug + " input").prop("disabled", false);
+                }
+            );
+        }
+    }
+    update_methods();
+    $("#payment-form .plan").change(update_methods);
+    
+});
diff --git a/src/club/static/club/paypal.png b/src/club/static/club/paypal.png
new file mode 100644 (file)
index 0000000..14613cc
Binary files /dev/null and b/src/club/static/club/paypal.png differ
diff --git a/src/club/static/club/payu/blik.png b/src/club/static/club/payu/blik.png
new file mode 100644 (file)
index 0000000..9bc5063
Binary files /dev/null and b/src/club/static/club/payu/blik.png differ
diff --git a/src/club/static/club/payu/payu.png b/src/club/static/club/payu/payu.png
new file mode 100644 (file)
index 0000000..c1bd5c2
Binary files /dev/null and b/src/club/static/club/payu/payu.png differ
index db2a8ed..3a9a006 100644 (file)
@@ -5,15 +5,78 @@
 
 
 {% block body %}
+
+<style>
+  .payment-method.disabled {
+  opacity: .5;
+  filter: grayscale(100%);
+  }
+  
+  </style>
+
+
 <div class="white-box normal-text">
 
        <h1>{% if membership %}Odnów swoje członkostwo w Towarzystwie Wolnych Lektur{% else %}Dołącz do Towarzystwa Wolnych Lektur{% endif %}</h1>
 
-<form method="POST" action="">
+<form method="POST" action="" id="payment-form" class="wlform">
   {% csrf_token %}
 
-  {{ form.as_p }}
-  <button type='submit'>Dołącz</button>
+  <h2>Składka</h2>
+  
+  <ul class="errorlist">
+  {% for e in  form.non_field_errors %}
+    <li>{{ e }}</li>
+  {% endfor %}
+    {% for e in  form.plan.errors %}
+    <li>{{ e }}</li>
+  {% endfor %}
+    {% for e in  form.amount.errors %}
+    <li>{{ e }}</li>
+    {% endfor %}
+  </ul>
+
+  {% for plan in form.plans %}
+  
+  <div>
+    <input class="plan" type="radio" name="plan" value="{{ plan.id }}" id="plan{{ plan.id }}" data-methods="{% for m in plan.payment_methods %}{{ m.slug }} {% endfor %}">
+    <label for="plan{{ plan.id }}">
+      <input
+        name="amount-{{ plan.id }}"
+        type="number"
+        placeholder="min. {{ plan.min_amount|floatformat:0 }}"
+        value="{{ plan.default_amount|floatformat:0 }}"
+        min="{{ plan.min_amount|floatformat:0 }}"
+        step="1"
+        style="width: 5em;"
+        > zł
+      {{ plan.get_interval_display }}
+    </label>
+  </div>
+
+  {% endfor %}
+
+  <h2>Metoda płatności</h2>
+  
+  <ul class="errorlist">
+  {% for e in form.method.errors %}
+  <li>{{ e }}</li>
+  {% endfor %}
+  </ul>
+  
+  {% for payment_method in form.payment_methods %}
+  <div class="payment-method" id="payment-method-{{ payment_method.slug }}">
+    <input type="radio" id="method{{ payment_method.slug }}" name="method" value="{{ payment_method.slug }}">
+    <label for="method{{ payment_method.slug }}" style="display:inline-block">
+      {% include payment_method.template_name %}
+    </label>
+  </div>
+  {% endfor %}
+
+  <p style="margin-top: 2em;">
+    {{ form.email.label }}:
+    {{ form.email }}</p>
+  <button class="submit" type='submit'>Dołącz</button>
 </form>
 
 </div>
diff --git a/src/club/templates/club/payment/paypal-re.html b/src/club/templates/club/payment/paypal-re.html
deleted file mode 100644 (file)
index 42563e8..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-PayPal
-
diff --git a/src/club/templates/club/payment/paypal.html b/src/club/templates/club/payment/paypal.html
new file mode 100644 (file)
index 0000000..befd9f2
--- /dev/null
@@ -0,0 +1,3 @@
+{% load staticfiles %}
+<img src="{% static 'club/paypal.png' %}">
+
index 28b6936..712b27c 100644 (file)
@@ -1 +1,5 @@
-Kartą
+{% load staticfiles %}
+<img src="{% static 'club/payu/payu.png' %}" height="50">
+<span class="method">karta</span>
+<span class="method">⟳ płatność cykliczna</span>
+
index 6da90e3..7979115 100644 (file)
@@ -1 +1,5 @@
-Czekolada / karta płatnicza
+{% load staticfiles %}
+<img src="{% static 'club/payu/payu.png' %}" height="50">
+<img src="{% static 'club/payu/blik.png' %}" height="50">
+<span class="method">karta</span>
+<span class="method">przelew</span>
index 9816b4b..93afadd 100644 (file)
@@ -38,9 +38,11 @@ class JoinView(CreateView):
         kwargs['request'] = self.request
         return kwargs
 
-    def get_context_data(self, form=None):
-        c = super(JoinView, self).get_context_data()
+    def get_context_data(self, **kwargs):
+        c = super(JoinView, self).get_context_data(**kwargs)
         c['membership'] = getattr(self.request.user, 'membership', None)
+        #if hasattr(form, 'errors'):
+        #    print(form.errors)
         return c
 
     def get_initial(self):
index f6411cc..0d599f2 100644 (file)
@@ -108,6 +108,7 @@ PIPELINE = {
                 'player/openplayer.js',
                 'js/search.js',
                 'funding/funding.js',
+                'club/form.js',
 
                 'js/annoy.js',
                 ),
index d3c7638..ef15365 100755 (executable)
@@ -29,3 +29,64 @@ form table {
         padding-left: 0;
     }
 }
+
+.wlform {
+    .errorlist {
+        color: red;
+        margin: 0;
+        padding: 0;
+        list-style: none;
+    }
+
+    h2 {
+        margin-top: 1.5em;
+    }
+    
+    input[type=number] {
+        height: 1.3em;
+    }
+    input[type=radio] {
+        margin-right: 1em;
+    }
+    
+    .submit {
+        background: #018189;
+        color: white;
+        border: 0;
+        border-radius: 10px;
+        padding: 1em 2em;
+        font-size: 1.2em;
+    }
+}
+
+.wlform {
+    .payment-method {
+        input {
+            height: 50px;
+            vertical-align: middle;
+        }
+        
+        img {
+            vertical-align: middle;
+            margin-right: 1em;
+        }
+        label {
+            margin: .5em 0;
+            vertical-align: middle;
+        }
+
+        
+        .method {
+            //        border: 1px solid black;
+            border-radius: 10px;
+            line-height: 48px;
+            display: inline-block;
+            vertical-align: middle;
+            padding: 0 1em;
+            margin-right: 1em;
+            font-size: 1.25em;
+            background: #01818980;
+            color: white;
+        }
+    }
+}