{% load sorl_thumbnail %}
<article class="l-books__item" data-pop="{{ book.popularity.count }}">
- <figure class="l-books__item__img">
+ <figure class="l-books__item__img" style="height:240px">
<a href="{{ book.get_absolute_url }}">
{% if book.is_picture %}
{% if book.image_file %}
- {% thumbnail book.image_file "170x170" as im %}
- <img style="float: left; margin:{{ im|margin:"170x170" }}" src="{{ im.url }}" width="{{ im.x }}" height="{{ im.y }}" />
+ {% thumbnail book.image_file "170x240" crop="center" as im %}
+ <img style="float: left; margin:{{ im|margin:"170x240" }}" src="{{ im.url }}" width="{{ im.x }}" height="{{ im.y }}" />
{% endthumbnail %}
{% endif %}
{% else %}
--- /dev/null
+{% extends '2022/base_real.html' %}
+{% load pagination_tags %}
+
+{% block content %}
+ <div class="l-container">
+ <div class="l-breadcrumb">
+ <a href="/"><span>Strona główna</span></a>
+ <a href="/katalog/"><span>Katalog</span></a>
+ <a href="/katalog/motyw/"><span>Motyw</span></a>
+ <span>{{ tags.0.name }}</span>
+ </div>
+ </div>
+
+ <main class="l-main">
+
+ <div class="l-section">
+ <div class="l-author__header">
+ <h1><span>Motyw:</span> {{ tags.0.name }}</h1>
+ </div>
+ </div>
+
+ <!-- div class="l-section">
+ <div class="l-books__header">
+ <div class="l-books__input">
+ <i class="icon icon-filter"></i>
+ <input type="text" placeholder="filtry, tytuł">
+ </div>
+ <div class="l-books__sorting">
+ <span>Sortuj:</span>
+ <div>
+ <button>autorzy</button>
+ <button>epoki</button>
+ <button>gatunki</button>
+ <button>rodzaje</button>
+ <button class="is-active">motywy</button>
+ </div>
+ </div>
+ </div>
+ </div -->
+
+ {% autopaginate object_list 10 %}
+
+ <div class="l-section l-section--col">
+ <div class="l-theme">
+ <div class="l-theme__col">
+ <div class="l-books__col">
+
+ {% for fragment in object_list %}
+ <article class="l-books__item">
+ <figure class="l-books__item__img">
+ <a href="{{ fragment.get_absolute_url }}">
+ {% if fragment.book.cover_clean %}
+ <img src="{{ fragment.book.cover_clean.url }}" alt="{{ fragment.book.title }}">
+ {% endif %}
+ </a>
+ </figure>
+ <div class="l-books__item__content">
+ <div class="l-books__item__actions">
+ <a href="#" class="icon icon-book-alt"></a>
+ {% if fragment.book.has_mp3 %}
+ <a href="#" class="icon icon-audio"></a>
+ {% endif %}
+ </div>
+ <h3>
+ {% for author in fragment.book.authors %}
+ <a href="{{ author.get_absolute_url }}">{{ author }}</a>{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+ </h3>
+ <h2><a href="{{ fragment.book.get_absolute_url }}">{{ fragment.book.title }}</a></h2>
+ <div class="fragment-text">
+ {{ fragment.short_text|safe }}
+ </div>
+ <a href="{{ fragment.get_absolute_url }}">Czytaj więcej</a>
+ </div>
+ </article>
+ {% endfor %}
+ {% paginate using '2022/paginate.html' %}
+ </div>
+ </div>
+ <div class="l-theme__col">
+ <div class="l-theme__info">
+ <h3>Motyw: {{ tags.0.name }}</h3>
+ {{ tags.0.description|safe }}
+ <!--
+ <h3>Motyw w sztuce <i class="icon icon-arrow-left"></i> <i class="icon icon-arrow-right"></i></h3>
+ <div class="l-theme__info__slider">
+ <img src="images/motyw.jpg" alt="">
+ <img src="images/motyw.jpg" alt="">
+ <img src="images/motyw.jpg" alt="">
+ </div>
+ -->
+ </div>
+ </div>
+ </div>
+ </div>
+ </main>
+
+{% endblock %}
self.assertEqual([t.slug for t in book.tags.filter(category='kind')],
['kind'])
self.assertEqual([(tag.name, tag.count) for tag in related_themes],
- [('ChildTheme', 1), ('ParentTheme', 1), ('Theme', 2)])
+ [('Theme', 2), ('ChildTheme', 1), ('ParentTheme', 1)])
result.update(extra)
is_author = len(tags) == 1 and tags[0].category == 'author'
+ is_theme = len(tags) == 1 and tags[0].category == 'theme'
new_layout = request.EXPERIMENTS['layout']
if is_author and new_layout.value:
template = 'catalogue/2022/author_detail.html'
+ elif is_theme and new_layout.value:
+ template = 'catalogue/2022/theme_detail.html'
else:
template = 'catalogue/tagged_object_list.html'
from . import models
-admin.site.register(models.Club)
+class SingleAmountInline(admin.TabularInline):
+ model = models.SingleAmount
+
+
+class MonthlyAmountInline(admin.TabularInline):
+ model = models.MonthlyAmount
+
+
+@admin.register(models.Club)
+class ClubAdmin(admin.ModelAdmin):
+ inlines = [
+ SingleAmountInline,
+ MonthlyAmountInline
+ ]
class PayUOrderInline(admin.TabularInline):
class PayUCardTokenForm(CardTokenForm):
def get_queryset(self, view):
return view.get_schedule().payucardtoken_set
+
+
+
+class DonationStep1Form(forms.ModelForm):
+ switch = forms.CharField()
+ single_amount = forms.IntegerField(required=False)
+ monthly_amount = forms.IntegerField(required=False)
+ single_amount_selected = forms.IntegerField(required=False)
+ monthly_amount_selected = forms.IntegerField(required=False)
+ custom_amount = forms.IntegerField(required=False)
+
+ amount = forms.IntegerField(required=False) # hidden
+
+ class Meta:
+ model = models.Schedule
+ fields = [
+ 'amount',
+ 'monthly'
+ ]
+
+
+ def clean(self):
+ state = {}
+ state['monthly'] = self.cleaned_data['switch'] == 'monthly'
+ which = 'monthly' if state['monthly'] else 'single'
+ state['amount'] = \
+ self.cleaned_data[f'{which}_amount'] or \
+ self.cleaned_data['custom_amount'] or \
+ self.cleaned_data[f'{monthly}_amount_selected']
+
+ return state
+
+
+
+class DonationStep2Form(forms.ModelForm, NewsletterForm):
+ class Meta:
+ model = models.Schedule
+ fields = [
+ 'first_name', 'last_name',
+ 'email', 'phone',
+ 'postal',
+ 'postal_code', 'postal_town', 'postal_country',
+ ]
+ widgets = {
+ 'amount': forms.HiddenInput,
+ 'monthly': forms.HiddenInput,
+ }
+
+ def __init__(self, referer=None, **kwargs):
+ self.referer = referer
+ super().__init__(**kwargs)
+
+ self.fields['first_name'].required = True
+ self.fields['last_name'].required = True
+ self.fields['phone'].required = True
+
+ self.consent = []
+ for c in models.Consent.objects.filter(active=True).order_by('order'):
+ key = f'consent{c.id}'
+ self.fields[key] = forms.BooleanField(
+ label=c.text,
+ required=c.required
+ )
+ self.consent.append((
+ c, key, (lambda k: lambda: self[k])(key)
+ ))
+
+
+
+ def save(self, *args, **kwargs):
+ NewsletterForm.save(self, *args, **kwargs)
+ self.instance.source = self.referer or ''
+ instance = super().save(*args, **kwargs)
+
+ consents = []
+ for consent, key, consent_field in self.consent:
+ if self.cleaned_data[key]:
+ instance.consent.add(consent)
+
+ return instance
+
--- /dev/null
+# Generated by Django 2.2.27 on 2022-08-26 12:39
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('club', '0039_auto_20220421_0109'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='SingleAmount',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('amount', models.IntegerField()),
+ ('description', models.TextField(blank=True)),
+ ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='club.Club')),
+ ],
+ options={
+ 'ordering': ['amount'],
+ },
+ ),
+ migrations.CreateModel(
+ name='MonthlyAmount',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('amount', models.IntegerField()),
+ ('description', models.TextField(blank=True)),
+ ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='club.Club')),
+ ],
+ options={
+ 'ordering': ['amount'],
+ },
+ ),
+ ]
--- /dev/null
+# Generated by Django 2.2.27 on 2022-08-26 12:39
+
+from django.db import migrations
+
+
+def move_amounts(apps, schema_editor):
+ Club = apps.get_model('club', 'Club')
+ for club in Club.objects.all():
+ for amount in [int(x) for x in club.single_amounts.split(',')]:
+ club.singleamount_set.create(amount=amount)
+ for amount in [int(x) for x in club.monthly_amounts.split(',')]:
+ club.monthlyamount_set.create(amount=amount)
+
+
+def move_amounts_back(apps, schema_editor):
+ Club = apps.get_model('club', 'Club')
+ for club in Club.objects.all():
+ club.single_amounts = ','.join(
+ str(x) for x in
+ club.singleamount_set.order_by('amount')
+ )
+ club.monthly_amounts = ','.join(
+ str(x) for x in
+ club.monthlyamount_set.order_by('amount')
+ )
+ club.save()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('club', '0040_amount'),
+ ]
+
+ operations = [
+ migrations.RunPython(
+ move_amounts,
+ move_amounts_back
+ )
+ ]
--- /dev/null
+# Generated by Django 2.2.27 on 2022-08-26 12:58
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('club', '0041_move_amounts'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='club',
+ name='monthly_amounts',
+ ),
+ migrations.RemoveField(
+ model_name='club',
+ name='single_amounts',
+ ),
+ ]
class Club(models.Model):
min_amount = models.IntegerField(_('minimum amount'))
min_for_year = models.IntegerField(_('minimum amount for year'))
- single_amounts = models.CharField(_('proposed amounts for single payment'), max_length=255)
default_single_amount = models.IntegerField(_('default single amount'))
- monthly_amounts = models.CharField(_('proposed amounts for monthly payments'), max_length=255)
default_monthly_amount = models.IntegerField(_('default monthly amount'))
class Meta:
def __str__(self):
return 'Klub'
-
- def proposed_single_amounts(self):
- return [int(x) for x in self.single_amounts.split(',')]
- def proposed_monthly_amounts(self):
- return [int(x) for x in self.monthly_amounts.split(',')]
+
+class SingleAmount(models.Model):
+ club = models.ForeignKey(Club, models.CASCADE)
+ amount = models.IntegerField()
+ description = models.TextField(blank=True)
+
+ class Meta:
+ ordering = ['amount']
+
+class MonthlyAmount(models.Model):
+ club = models.ForeignKey(Club, models.CASCADE)
+ amount = models.IntegerField()
+ description = models.TextField(blank=True)
+
+ class Meta:
+ ordering = ['amount']
class Consent(models.Model):
def get_payment_method(self):
return [m for m in methods if m.slug == self.method][0]
+ def get_payment_methods(self):
+ for method in methods:
+ if (self.monthly or self.yearly) and method.is_recurring:
+ yield method
+ elif not (self.monthly or self.yearly) and method.is_onetime:
+ yield method
+
def is_expired(self):
return self.expires_at is not None and self.expires_at <= now()
# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
+from hashlib import sha256
from django.conf import settings
from django.urls import reverse
from django.utils.timezone import now
+from django.utils.translation import get_language
from paypal.rest import agreement_approval_url
+from django.template.loader import render_to_string
+from .payu import POSS
class PaymentMethod(object):
def __init__(self, pos_id):
self.pos_id = pos_id
+ def invite_widget(self, schedule):
+ return render_to_string(
+ 'club/payment/payu_invite.html',
+ {
+ 'schedule': schedule,
+ }
+ )
+
def initiate(self, request, schedule):
# Create Order at once.
from .models import PayUOrder
def initiate(self, request, schedule):
return reverse('club_payu_rec_payment', args=[schedule.key])
+ def invite_widget(self, schedule):
+ from . import forms
+ pos = POSS[self.pos_id]
+ widget_args = {
+ 'merchant-pos-id': pos.pos_id,
+ 'shop-name': "SHOW NAME",
+ 'total-amount': str(int(schedule.amount * 100)),
+ 'currency-code': pos.currency_code,
+ 'customer-language': get_language(), # filter to pos.languages
+ 'customer-email': schedule.email,
+ 'store-card': 'true',
+ 'recurring-payment': 'true',
+ }
+ widget_sig = sha256(
+ (
+ "".join(v for (k, v) in sorted(widget_args.items())) +
+ pos.secondary_key
+ ).encode('utf-8')
+ ).hexdigest()
+
+ return render_to_string(
+ 'payu/rec_widget.html',
+ {
+ 'form': forms.PayUCardTokenForm(),
+ 'pos': POSS[self.pos_id],
+ 'widget_args': widget_args,
+ 'widget_sig': widget_sig,
+ }
+ )
+
def pay(self, request, schedule):
# Create order, put it and see what happens next.
from .models import PayUOrder
is_recurring = True
is_onetime = False
+ def invite_widget(self, schedule):
+ return render_to_string(
+ 'club/payment/paypal_invite.html',
+ {
+ 'schedule': schedule,
+ }
+ )
+
def initiate(self, request, schedule):
app = request.GET.get('app')
return agreement_approval_url(schedule.amount, schedule.key, app=app)
--- /dev/null
+{% extends 'club/2022/donation_step_base.html' %}
+{% load club %}
+{% load static %}
+
+
+{% block donation-step-content %}
+ <form method="post">
+ {% csrf_token %}
+ {{ form.errors }}
+ <input type="radio" name="switch" id="switch-once" value="single" class="toggle-input" {% if schedule and not schedule.monthly %}checked{% endif %}>
+ <input type="radio" name="switch" id="switch-monthly" value="monthly" class="toggle-input" {% if not schedule or schedule.monthly %}checked{% endif %}>
+ <div class="l-switch__wrapper">
+
+ <div class="l-switch white">
+ <label class='toggle-for' for="switch-once">Jednorazowo</label>
+ <label class='toggle-for' for="switch-monthly">Miesięcznie</label>
+ <span class="toggle"></span>
+ </div>
+ </div>
+
+ <div class="l-checkout__payments payments-once">
+ {% for amount in club.singleamount_set.all %}
+ <div class="l-checkout__payments__box once{% if not schedule.monthly and schedule.amount == amount.amount or not schedule and club.default_single_amount == amount.amount %} is-active{% endif %}{% if forloop.last %} l-checkout__payments__box--xl{% endif %}">
+ <div>
+ <h3>{{ amount.amount }} zł</h3>
+ <div class="l-checkout__payments__box__btn-wrp">
+ <button name="single_amount" value="{{ amount.amount }}">Wybierz</button>
+ </div>
+ </div>
+ {% if amount.description %}
+ <div>
+ <p>{{ amount.description|safe }}</p>
+ </div>
+ {% endif %}
+ </div>
+ {% endfor %}
+ <input type="hidden"
+ name="single_amount_selected"
+ value="{% if schedule and not schedule.monthly %}{{ schedule.amount|floatformat }}{% else %}{{ club.default_single_amount }}{% endif %}">
+
+ </div>
+
+
+ <div class="l-checkout__payments payments-recurring">
+ {% for amount in club.monthlyamount_set.all %}
+ <div class="l-checkout__payments__box{% if schedule.monthly and schedule.amount == amount.amount or not schedule and amount.amount == club.default_monthly_amount %} is-active{% endif %}">
+ <h3>{{ amount.amount }} zł <span>/mies.</span></h3>
+ <div class="l-checkout__payments__box__btn-wrp">
+ <p>{{ amount.description|safe }}</p>
+ <button name="monthly_amount" value="{{ amount.amount }}">Wybierz</button>
+ </div>
+ </div>
+ {% endfor %}
+ <input type="hidden"
+ name="monthly_amount_selected"
+ value="{% if schedule and schedule.monthly %}{{ schedule.amount|floatformat }}{% else %}{{ club.default_monthly_amount }}{% endif %}">
+ </div>
+
+ <div class="l-checkout__amount">
+ <div class="l-checkout__input">
+ <label for="kwota">Inna kwota</label>
+ <!-- input type="text" id="kwota" name="custom_amount" value="" -->
+ {{ form.custom_amount }}
+ </div>
+ <button>Dalej</button>
+ </div>
+ </form>
+
+ <img src="{% static '2022/images/checkout-footer.png' %}" alt="Bezpieczne płatności zapewniają" class="l-checkout__footer__img">
+ <div class="l-checkout__cols bt-w">
+ <div class="l-checkout__col full">
+ <div class="l-checkout__form">
+ <div class="l-checkout__form__row full">
+ <h3>Dane do przelewu tradycyjnego:</h3>
+ <div class="l-checkout__info">
+ <div class="l-checkout__info__item">
+ <div>nazwa odbiorcy</div>
+ <h3>Fundacja Nowoczesna Polska</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="Fundacja Nowoczesna Polska">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>adres odbiorcy</div>
+ <h3>ul. Marszałkowska 84/92 lok. 125, 00-514 Warszawa</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="ul. Marszałkowska 84/92 lok. 125, 00-514 Warszawa">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>numer konta</div>
+ <h3>75 1090 2851 0000 0001 4324 3317</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="75 1090 2851 0000 0001 4324 3317">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>tytuł przelewu</div>
+ <h3>Darowizna na Wolne Lektury + twoja nazwa użytkownika lub e-mail</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="Darowizna na Wolne Lektury + twoja nazwa użytkownika lub e-mail">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>wpłaty w EUR</div>
+ <h3>PL88 1090 2851 0000 0001 4324 3374</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="PL88 1090 2851 0000 0001 4324 3374">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>Wpłaty w USD</div>
+ <h3>PL82 1090 2851 0000 0001 4324 3385</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="PL88 1090 2851 0000 0001 4324 3374">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>SWIFT</div>
+ <h3>WBKPPLPP</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="WBKPPLPP">
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+{% endblock %}
--- /dev/null
+{% extends 'club/2022/donation_step_base.html' %}
+
+{% load static %}
+
+
+{% block donation-jumbo-image %}{% static '2022/images/checkout-img-2.jpg' %}{% endblock %}
+
+
+{% block donation-step-content %}
+
+ <div class="l-checkout__cols">
+ <div class="l-checkout__col">
+ <div class="l-checkout__payments__box is-active">
+ <h3>
+ {{ schedule.amount|floatformat }} zł
+ {% if schedule.monthly %}
+ <span>/mies.</span>
+ {% endif %}
+ </h3>
+ <img src="{% static '2022/images/checkout-img-3.jpg' %}" alt="">
+ </div>
+ </div>
+ <div class="l-checkout__col">
+
+ <form method='post'>
+ {% csrf_token %}
+ {{ form.errors }}
+ {{ form.amount }}
+ {{ form.monthly }}
+ <div class="l-checkout__form">
+ <div class="l-checkout__form__row">
+ <div class="l-checkout__input">
+ <label for="imie"><span>*</span> Imię</label>
+ {{ form.first_name }}
+ {{ form.first_name.errors }}
+ </div>
+ <div class="l-checkout__input">
+ <label for="nazwisko"><span>*</span> Nazwisko</label>
+ {{ form.last_name }}
+ {{ form.last_name.errors }}
+ </div>
+ </div>
+ <div class="l-checkout__form__row">
+ <div class="l-checkout__input">
+ <label for="email"><span>*</span> E-mail</label>
+ {{ form.email }}
+ {{ form.email.errors }}
+ </div>
+ <div class="l-checkout__input">
+ <label for="telefon"><span>*</span> Telefon</label>
+ {{ form.phone }}
+ {{ form.phone.errors }}
+ </div>
+ </div>
+ <div class="l-checkout__form__row full">
+ <div class="l-checkout__input">
+ <label for="adres-pocztowy">Adres pocztowy</label>
+ {{ form.postal }}
+ {{ form.postal.errors }}
+ </div>
+ </div>
+ <div class="l-checkout__form__row">
+ <div class="l-checkout__input">
+ <label for="kod-pocztowy">Kod pocztowy</label>
+ {{ form.postal_code }}
+ {{ form.postal_code.errors }}
+ </div>
+ <div class="l-checkout__input">
+ <label for="miejscowosc">Miejscowość</label>
+ {{ form.postal_town }}
+ {{ form.postal_town.errors }}
+ </div>
+ </div>
+ <div class="l-checkout__form__row full">
+ <div class="l-checkout__input">
+ <label for="kod-pocztowy">Kraj</label>
+ {{ form.postal_country }}
+ {{ form.postal_country.errors }}
+ </div>
+ </div>
+ <div class="l-checkout__form__row full">
+ {% for consent, key, field in form.consent %}
+ {{ field.errors }}
+ <div class="c-checkbox">
+ {{ field }}
+ <label for="id_{{ key }}">
+ <p>{% if field.field.required %}<span>*</span> {% endif %}{{ field.label }}</p>
+ </label>
+ </div>
+ {% endfor %}
+ <div class="c-checkbox">
+ {{ form.agree_newsletter }}
+ <label for="id_agree_newsletter">
+ <p>Zapisuję się na newsletter.</p>
+ </label>
+ </div>
+ </div>
+ <div class="l-checkout__form__row confirm">
+ <a href=".#{{ view.step1_hash }}">Powrót</a>
+ <div class="l-checkout__input">
+ <button>Dalej</button>
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ <img src="{% static '2022/images/checkout-footer.png' %}" alt="Bezpieczne płatności zapewniają" class="l-checkout__footer__img">
+{% endblock %}
--- /dev/null
+{% extends 'club/2022/donation_step_base.html' %}
+
+{% load static %}
+{% load club %}
+
+
+{% block donation-jumbo-image %}{% static '2022/images/checkout-img-4.jpg' %}{% endblock %}
+
+
+{% block donation-step-content %}
+ <div class="l-checkout__cols">
+ <div class="l-checkout__col">
+ <div class="l-checkout__payments__box is-active">
+ <h3>
+ {{ schedule.amount|floatformat }} zł
+ {% if schedule.monthly %}
+ <span>/mies.</span>
+ {% endif %}</h3>
+ <img src="{% static '2022/images/checkout-img-3.jpg' %}" alt="">
+ </div>
+ </div>
+ <div class="l-checkout__col">
+ <div class="l-checkout__form">
+ <div class="l-checkout__form__row full">
+ <!-- h3>Wybierz bezpieczną płatność:</h3-->
+ <div class="iframe">
+ {% for method in schedule.get_payment_methods %}
+ {% invite_payment method schedule %}
+ {% endfor %}
+ </div>
+ </div>
+ <div class="l-checkout__form__row full">
+ <h3>Możesz też ustawić stały przelew na konto:</h3>
+ <div class="l-checkout__info">
+ <div class="l-checkout__info__item">
+ <div>nazwa odbiorcy</div>
+ <h3>Fundacja Nowoczesna Polska</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="Fundacja Nowoczesna Polska">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>adres odbiorcy</div>
+ <h3>ul. Marszałkowska 84/92 lok. 125, 00-514 Warszawa</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="ul. Marszałkowska 84/92 lok. 125, 00-514 Warszawa">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>numer konta</div>
+ <h3>75 1090 2851 0000 0001 4324 3317</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="75 1090 2851 0000 0001 4324 3317">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>tytuł przelewu</div>
+ <h3>Darowizna na Wolne Lektury + twoja nazwa użytkownika lub e-mail</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="Darowizna na Wolne Lektury + twoja nazwa użytkownika lub e-mail">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>wpłaty w EUR</div>
+ <h3>PL88 1090 2851 0000 0001 4324 3374</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="PL88 1090 2851 0000 0001 4324 3374">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>Wpłaty w USD</div>
+ <h3>PL82 1090 2851 0000 0001 4324 3385</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="PL88 1090 2851 0000 0001 4324 3374">
+ </div>
+ <div class="l-checkout__info__item">
+ <div>SWIFT</div>
+ <h3>WBKPPLPP</h3>
+ <button title="Kopiuj tekst" class="js-copy">
+ <img src="{% static '2022/images/copy.svg' %}" alt="Kopiuj">
+ </button>
+ <input type="text" value="WBKPPLPP">
+ </div>
+ </div>
+ </div>
+ <div class="l-checkout__form__row confirm">
+ <a href="#">Powrót</a>
+ <!-- div class="l-checkout__input">
+ <a href="#">Dalej</a>
+ </div-->
+ </div>
+ </div>
+ </div>
+ </div>
+ <img src="{% static '2022/images/checkout-footer.png' %}" alt="Bezpieczne płatności zapewniają" class="l-checkout__footer__img">
+{% endblock %}
--- /dev/null
+{% extends 'club/2022/donation_step_base.html' %}
+
+
+{% block donation-step-content %}
+ <div class="l-checkout__completed">
+ <div class="l-checkout__completed__wrapper">
+ <h1>Dziękujemy za wpłatę!</h1>
+ <p>
+ Zajrzyj teraz do e-maila. Tam znajdziesz link,
+ który powiąże płatność z Twoim kontem użytkownika Wolnych Lektur.
+ </p>
+ <a href="#">Wracam do lektur</a>
+ </div>
+ </div>
+{% endblock %}
--- /dev/null
+{% extends '2022/base.html' %}
+{% load club %}
+{% load static %}
+
+
+{% block content %}
+ <div class="l-container">
+ <div class="l-breadcrumb">
+ <a href="#"><span>Strona główna</span></a>
+ <a href="#"><span>Wesprzyj nas</span></a>
+ </div>
+ </div>
+
+
+ <main class="l-main">
+ <div class="l-checkout__support">
+ <div class="l-checkout__support__bar">
+ <span data-label="Jest nas {% club_count_recurring %}" style="width: calc({% club_count_recurring %}% / 10);"></span>
+ </div>
+ <div class="l-checkout__support__footer">
+ <p>Dołącz do naszych stałych <strong>darczyńców</strong>!</p>
+ <p>Minimalną stabilność uzyskamy przy <strong>1000</strong> regularnych darczyńców!</p>
+ </div>
+ </div>
+
+ <div class="l-checkout__box">
+ <div class="l-checkout__box__header">
+ <img src="{% block donation-jumbo-image %}{% static '2022/images/checkout-img-1.jpg' %}{% endblock %}" alt="Wspieraj Wolne Lektury">
+ <div class="l-checkout__box__header__content">
+ <h1>Wspieraj Wolne Lektury</h1>
+ <p>Dziękujemy, że chcesz razem z nami uwalniać książki!</p>
+ <p>Wspieraj Wolne Lektury stałą wpłatą – nawet niewielka ma wielką moc! Możesz też wesprzeć Wolne Lektury jednorazowo.</p>
+ </div>
+ </div>
+ <div class="l-checkout__steps">
+
+ {% if view.step > 1 and view.step != 4 %}
+ <a href="{% url 'donation_step1' schedule.key %}">
+ {% endif %}
+ <div class="{% if view.step == 1 %}is-current{% else %}is-completed{% endif %}">
+ <span>1</span>
+ <p>Rodzaj wsparcia</p>
+ </div>
+ {% if view.step > 1 and view.step != 4 %}
+ </a>
+ {% endif %}
+
+ {% if view.step != 2 and schedule and view.step != 4 %}
+ <a href="{% url 'donation_step2' schedule.key %}">
+ {% endif %}
+ <div class="{% if view.step == 2 %}is-current{% elif not schedule %}is-inactive{% else %}is-completed{% endif %}">
+ <span>2</span>
+ <p>Dane</p>
+ </div>
+ {% if view.step != 2 and schedule and view.step != 4 %}
+ </a>
+ {% endif %}
+
+ {% if view.step != 3 and schedule.email and view.step != 4 %}
+ <a href="{{ schedule.get_absolute_url }}">
+ {% endif %}
+ <div class="{% if view.step == 3 %}is-current{% elif not schedule or not schedule.method %}is-inactive{% else %}is-completed{% endif %}">
+ <span>3</span>
+ <p>Forma płatności</p>
+ </div>
+ {% if view.step != 3 and schedule.email and view.step != 4 %}
+ </a>
+ {% endif %}
+
+ <div class="{% if view.step == 4 %}is-completed{% else %}is-inactive{% endif %}">
+ <span>4</span>
+ <p>Gotowe</p>
+ </div>
+ </div>
+
+
+ {% block donation-step-content %}{% endblock %}
+
+ </div>
+
+ <div class="l-checkout__footer">
+ <div class="l-checkout__footer__content">
+ <div class="l-checkout__footer__content__item">
+ <h3>Transparentność jest dla nas bardzo ważna.</h3>
+ <div>
+ <div class="l-article__overlay" data-max-height="91">
+ <p>
+ O tym, jak wydajemy pozyskane pieniądze, dowiesz się ze sprawozdań finansowych i merytorycznych, które znajdziesz na naszej stronie lub w bazie Narodowego Instytutu Wolności, wpisując nr KRS 0000070056.
+ </p>
+ </div>
+ </div>
+ </div>
+ <div class="l-checkout__footer__content__item">
+ <h3>Informacja o przetwarzaniu danych osobowych</h3>
+ <div>
+ <div class="l-article__overlay" data-max-height="91">
+ <p>
+ W każdej chwili możesz zrezygnować z subskrypcji. <a href="#">Więcej informacji. Polityka prywatności.</a>
+ Administratorem Twoich danych osobowych jest Fundacja Nowoczesna Polska z siedzibą w Warszawie, przy ul. Marszałkowskiej 84/92 lok. 125, 00-514 Warszawa (dalej: Fundacja).
+ </p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. A alias consequatur deserunt doloribus facilis, ipsum nisi nulla quam rem totam!
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. A alias consequatur deserunt doloribus facilis, ipsum nisi nulla quam rem totam!
+ </p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. A alias consequatur deserunt doloribus facilis, ipsum nisi nulla quam rem totam!
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. A alias consequatur deserunt doloribus facilis, ipsum nisi nulla quam rem totam!
+ </p>
+ </div>
+ <button class="l-article__read-more" aria-label="Kliknij aby rozwinąć" data-label="Więcej" data-action="Mniej">Więcej</button>
+ </div>
+ </div>
+ <div class="l-checkout__footer__content__item">
+ <h3>FAQ</h3>
+ <div>
+ <div class="l-article__overlay" data-max-height="91">
+ <p>
+ Chcesz zmienić wysokość darowizny, anulować przyszłe płatności lub masz inne pytania? Tu znajdziesz odpowiedzi na nie:
+ <a href="#">Pytania i odpowiedzi dotyczące płatności</a>.
+ </p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </main>
+
+
+
+{% endblock %}
--- /dev/null
+{% load static %}
+<h3>Wolisz wpłacić przez PayPal?</h3>
+<a href="{% url 'paypal_init' schedule.key %}">
+ <div class="iframe">
+ <img src="{% static 'club/paypal.png' %}" alt="PayPal">
+ </div>
+</a>
--- /dev/null
+{% load static %}
+<a href="{% url 'club_payu_payment' schedule.key %}">
+ <h3>Przejdź do płatności →</h3>
+ <img src="{% static 'club/payu/banki.png' %}" style="width: 100%">
+</a>
<h3>2. {% trans "Choose the amount" %}</h3>
<div class="plan" id="plan-single" style="display:none;" data-monthly="False" data-min-for-year="{{ club.min_for_year }}" data-amount="{{ club.default_single_amount }}">
- {% for amount in club.proposed_single_amounts %}
- <span class="button kwota{% if amount == club.default_single_amount %} active{% endif %}{% if amount >= club.min_for_year %} yearly{% endif %}">{{ amount }}</span>
+ {% for amount in club.singleamount_set.all %}
+ <span class="button kwota{% if amount.amount == club.default_single_amount %} active{% endif %}{% if amount.amount >= club.min_for_year %} yearly{% endif %}">{{ amount.amount }}</span>
{% endfor %}
<span class="inna">
<div class="plan" id="plan-monthly" data-monthly="True" data-amount="{{ club.default_monthly_amount }}">
- {% for amount in club.proposed_monthly_amounts %}
- <span class="button kwota{% if amount == club.default_monthly_amount %} active{% endif %}">{{ amount }}</span>
+ {% for amount in club.monthlyamount_set.all %}
+ <span class="button kwota{% if amount.amount == club.default_monthly_amount %} active{% endif %}">{{ amount.amount }}</span>
{% endfor %}
<span class="inna">
--- /dev/null
+<h3>Podaj dane karty płatniczej</h3>
+<div class="iframe">
+ <form id="theform" method='POST'>
+ {% csrf_token %}
+ {{ form }}
+ </form>
+
+ <script>
+ function paymentcallback(data) {
+ $("#theform #id_token").val(data.value);
+ $("#theform").submit()
+ }
+ </script>
+
+ <div id="payu-widget"></div>
+ <script
+ src="{{ pos.get_api_host }}/front/widget/js/payu-bootstrap.js"
+
+ {% for k, v in widget_args.items %}
+ {{ k }}="{{ v }}"
+ {% endfor %}
+
+ success-callback="paymentcallback"
+ sig="{{ widget_sig }}">
+ </script>
+</div>
def club_monthly_missing_since(start, target):
return target - Schedule.objects.filter(
monthly=True, payed_at__gte=start).count()
+
+
+@register.simple_tag
+def invite_payment(payment_method, schedule):
+ return payment_method.invite_widget(schedule)
path('plan/<key>/', banner_exempt(views.ScheduleView.as_view()), name='club_schedule'),
path('plan/<key>/dziekujemy/', banner_exempt(views.ScheduleThanksView.as_view()), name='club_thanks'),
path('plan/<key>/zestawienie/<int:year>/', banner_exempt(views.YearSummaryView.as_view()), name='club_year_summary'),
+ path('plan/<key>/rodzaj/', banner_exempt(views.DonationStep1.as_view()), name='donation_step1'),
+ path('plan/<key>/dane/', banner_exempt(views.DonationStep2.as_view()), name='donation_step2'),
path('przylacz/<key>/', views.claim, name='club_claim'),
path('anuluj/<key>/', views.cancel, name='club_cancel'),
from django.db.models import Sum
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
+from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
from django.views.generic import FormView, CreateView, TemplateView, DetailView, UpdateView
from .payu import POSS
from .payu import views as payu_views
from .forms import ScheduleForm, PayUCardTokenForm
+from . import forms
from . import models
from .helpers import get_active_schedule
from .payment_methods import recurring_payment_method
return ctx
-class JoinView(CreateView):
- form_class = ScheduleForm
- template_name = 'club/membership_form.html'
+class DonationStep1(UpdateView):
+ queryset = models.Schedule.objects.filter(payed_at=None)
+ form_class = forms.DonationStep1Form
+ slug_field = slug_url_kwarg = 'key'
+ template_name = 'club/2022/donation_step1.html'
+ step = 1
+
+ def get_context_data(self, **kwargs):
+ c = super().get_context_data(**kwargs)
+ c['club'] = models.Club.objects.first()
+ return c
+
+ def get_success_url(self):
+ return reverse('donation_step2', args=[self.object.key])
+
+
+class DonationStep2(UpdateView):
+ queryset = models.Schedule.objects.filter(payed_at=None)
+ form_class = forms.DonationStep2Form
+ slug_field = slug_url_kwarg = 'key'
+ template_name = 'club/2022/donation_step2.html'
+ step = 2
+
+
+class JoinView(CreateView):
+ @property
+ def club(self):
+ return models.Club.objects.first()
+
+ @property
+ def new_layout(self):
+ return self.request.EXPERIMENTS['layout'].value
+
+ def get_template_names(self):
+ if self.new_layout:
+ return 'club/2022/donation_step1.html'
+ return 'club/membership_form.html'
+
+ def get_form_class(self):
+ if self.new_layout:
+ return forms.DonationStep1Form
+ return ScheduleForm
+
def is_app(self):
return self.request.GET.get('app')
def get(self, request):
- # TODO: configure as app-allowed hosts.
if settings.CLUB_APP_HOST and self.is_app() and request.META['HTTP_HOST'] != settings.CLUB_APP_HOST:
return HttpResponseRedirect("https://" + settings.CLUB_APP_HOST + request.get_full_path())
request.session['from_app'] = True
elif request.session and 'from_app' in request.session:
del request.session['from_app']
- return super(JoinView, self).get(request)
+
+ return super().get(request)
def get_context_data(self, **kwargs):
c = super(JoinView, self).get_context_data(**kwargs)
def get_initial(self):
if self.request.user.is_authenticated and self.request.user.email:
return {
- 'email': self.request.user.email,
+ 'email': self.request.user.email
}
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
- kwargs['referer'] = self.request.META.get('HTTP_REFERER', '')
+ if not self.new_layout:
+ kwargs['referer'] = self.request.META.get('HTTP_REFERER', '')
return kwargs
def form_valid(self, form):
return retval
def get_success_url(self):
+ if self.new_layout:
+ return reverse('donation_step2', args=[self.object.key])
return self.object.initiate_payment(self.request)
@method_decorator(never_cache, name='dispatch')
class ScheduleView(DetailView):
- model = models.Schedule
+ queryset = models.Schedule.objects.exclude(email='')
slug_field = slug_url_kwarg = 'key'
template_name = 'club/schedule.html'
-
+ step = 3
+
+ def get_template_names(self):
+ if self.request.EXPERIMENTS['layout'].value:
+ if not self.object.payed_at:
+ return 'club/2022/donation_step3.html'
+ return 'club/schedule.html'
+
def get_context_data(self, *args, **kwargs):
ctx = super().get_context_data(*args, **kwargs)
ctx['active_menu_item'] = 'club'
def post(self, request, key):
schedule = models.Schedule.objects.get(key=key)
schedule.create_payment()
+
return HttpResponseRedirect(schedule.get_absolute_url())
def get(self, request, key):
schedule = self.get_object()
+ schedule.method = 'payu'
+ schedule.save(update_fields=['method'])
return HttpResponseRedirect(schedule.initiate_payment(request))
return POSS[pos_id]
def get_success_url(self):
- return self.get_schedule().pay(self.request)
+ schedule = self.get_schedule()
+ schedule.method = 'payu-re'
+ schedule.save(update_fields=['method'])
+ return schedule.pay(self.request)
class PayUNotifyView(payu_views.NotifyView):
class ScheduleThanksView(DetailView):
model = models.Schedule
slug_field = slug_url_kwarg = 'key'
- template_name = 'club/thanks.html'
+ step = 4
+ def get_template_names(self):
+ if self.request.EXPERIMENTS['layout'].value:
+ return 'club/2022/donation_step4.html'
+ return 'club/thanks.html'
+
def get_context_data(self, *args, **kwargs):
ctx = super().get_context_data(*args, **kwargs)
ctx['active_menu_item'] = 'club'
path('form/', RedirectView.as_view(url='/pomagam/')),
path('app-form/', RedirectView.as_view(url='/pomagam/?pk_campaign=aplikacja')),
+ path('init/<key>/', views.paypal_init, name='paypal_init'),
path('return/<key>/', views.paypal_return, name='paypal_return'),
path('app-return/<key>/', views.paypal_return, kwargs={'app': True}, name='paypal_app_return'),
path('cancel/', views.paypal_cancel, name='paypal_cancel'),
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.http.response import HttpResponseRedirect, HttpResponseForbidden
-from django.shortcuts import get_object_or_404, render
+from django.shortcuts import get_object_or_404, render, redirect
from api.utils import HttpResponseAppRedirect
from club.models import Schedule
return render(request, 'paypal/form.html', {'form': form})
+def paypal_init(request, key):
+ schedule = get_object_or_404(Schedule, key=key)
+ schedule.method = 'paypal'
+ schedule.save(update_fields=['method'])
+ app = request.GET.get('app')
+ return redirect(agreement_approval_url(schedule.amount, schedule.key, app=app))
+
+
@login_required
def paypal_return(request, key, app=False):
schedule = get_object_or_404(Schedule, key=key)
})();
-//Switch
(function() {
- let $switchOnce = $('#switch-once');
- let $switchMonthly = $('#switch-monthly');
-
- $switchMonthly.on('click', function() {
- $('.payments-once').hide();
- $('.payments-recurring').show();
- });
-
- $switchOnce.on('click', function() {
- $('.payments-recurring').hide();
- $('.payments-once').show();
- });
-})();
-
-
-(function() {
-
$('.l-checkout__payments__box button').on('click', function() {
let container = $(this).closest('.l-checkout__payments');
- $('input', container).val($(this).attr('data-amount'));
+ $('input', container).val($(this).val());
$('.is-active', container).removeClass('is-active');
$(this).closest('.l-checkout__payments__box').addClass('is-active');
$('#kwota').val('');
order: attr(data-pop);
}
}
+
+
+
+.toggle-input {
+ left: calc(50% - 77px/2);
+ opacity: 0;
+ position: absolute;
+ //top: 0;
+ height: 26px;
+ width: 77px;
+ z-index: 2;
+
+ &:checked {
+ z-index: 0;
+ }
+}
+
+
+.l-checkout__payments {
+ display: none;
+}
+.toggle-input{
+ &#switch-once:checked {
+ ~.l-switch__wrapper {
+ label {
+ &:nth-of-type(1) { color: #003C3C; }
+ &:nth-of-type(2) { color: #74BDC2; }
+ }
+ .toggle {
+ left: 5px;
+ }
+ }
+ ~.payments-once {
+ display: flex;
+ }
+ }
+ &#switch-monthly:checked {
+ ~.l-switch__wrapper {
+ label {
+ &:nth-of-type(1) { color: #74BDC2; }
+ &:nth-of-type(2) { color: #003C3C; }
+ }
+ .toggle {
+ left: 39px;
+ }
+ }
+ ~.payments-recurring {
+ display: flex;
+ }
+ }
+}
+
+
+
+.l-checkout__form__row {
+ .iframe {
+ margin-bottom: 16px;
+ }
+}
+
+
+
+.l-books__item__content {
+ width: 100%;
+
+ .fragment-text {
+ max-width: 390px;
+ margin-top: 20px;
+ font-weight: 400;
+ font-size: 18px;
+ font-size: 1.125rem;
+ line-height: 150%;
+ color: #474747;
+
+ h1, h2, h3, h4 {
+ font-size: 1em;
+ }
+ }
+}
@import "breadcrumb";
@import "content";
@import "checkout";
+@import "pagination";
+@import "theme";
--- /dev/null
+.l-pagination {
+ display: flex;
+ align-content: center;
+ margin: 40px auto 40px auto;
+
+ ul {
+ display: flex;
+ margin: 0;
+ padding: 0;
+ align-content: center;
+ list-style: none;
+
+ li {
+ margin: 0 5px;
+ font-weight: $regular;
+ @include font-size(18px);
+ line-height: 150%;
+ text-align: center;
+ color: #474747;
+
+ &.is-active {
+ a {
+ color: #ffffff;
+ background-color: #083F4D;
+ border-radius: 50%;
+ }
+ }
+
+ a {
+ text-align: center;
+ line-height: 35px;
+ display: block;
+ width: 35px;
+ height: 35px;
+ }
+ }
+ }
+}
+
+.l-pagination__arrow {
+ display: flex;
+ align-content: center;
+ @include font-size(10px);
+ margin: 0 20px;
+
+ .icon {
+ display: flex;
+ align-content: center;
+ line-height: 35px;
+ }
+}
+
--- /dev/null
+.l-theme {
+ display: flex;
+}
+
+.l-theme__col {
+ display: flex;
+ flex-direction: column;
+}
+
+.l-theme__info {
+ padding: 34px;
+ border-radius: 10px;
+ background-color: #E1F1F2;
+ margin-top: 34px;
+ max-width: 415px;
+ h3 {
+ display: flex;
+ align-content: center;
+ justify-content: space-between;
+ margin-top: 34px;
+ font-weight: $semibold;
+ @include font-size(21px);
+ line-height: 140%;
+ letter-spacing: -0.01em;
+ color: #007880;
+
+ &:first-of-type {
+ margin-top: 0;
+ }
+
+ .icon {
+ display: flex;
+ align-items: center;
+ @include font-size(14px);
+ margin-right: 0;
+ margin-left: auto;
+ cursor: pointer;
+
+ &.icon-arrow-right {
+ margin-left: 20px;
+ }
+ }
+ }
+
+ p {
+ margin-top: 10px;
+ font-weight: $regular;
+ @include font-size(18px);
+ line-height: 150%;
+ color: #474747;
+ }
+}
+
+.l-theme__info__slider {
+ width: 100%;
+ font-size: 0;
+ margin-top: 16px;
+
+ & * {
+ outline: 0 !important;
+ }
+
+ img {
+ max-width: 100%;
+ margin-right: 17px;
+ }
+}
\ No newline at end of file
--- /dev/null
+{% if is_paginated %}
+ <div class="l-pagination">
+ {% if page_obj.has_previous %}
+ <a href="?page={{ page_obj.previous_page_number }}{{ getvars }}" class="l-pagination__arrow">
+ <i class="icon icon-arrow-left"></i>
+ </a>
+ {% endif %}
+ <ul>
+ {% for page in pages %}
+ {% if page %}
+ {% if page == page_obj.number %}
+ <li class="is-active"><a>{{ page }}</a></li>
+ {% else %}
+ <li><a href="?page={{ page }}{{ getvars }}">{{ page }}</a></li>
+ {% endif %}
+ {% else %}
+ <li><a>…</a></li>
+ {% endif %}
+ {% endfor %}
+ </ul>
+ {% if page_obj.has_next %}
+ <a href="?page={{ page_obj.next_page_number }}{{ getvars }}" class="l-pagination__arrow">
+ <i class="icon icon-arrow-right"></i>
+ </a>
+ {% endif %}
+ </div>
+{% endif %}