diff --git a/api/v1/serializers/content.py b/api/v1/serializers/content.py index 89daefa1..5b6d2c1d 100644 --- a/api/v1/serializers/content.py +++ b/api/v1/serializers/content.py @@ -1,5 +1,6 @@ from urllib.parse import urlparse, urlunparse from rest_framework import serializers +from django.conf import settings from apps.content.models import ( Baner, Content, Image, Text, ImageText, Video, @@ -46,12 +47,8 @@ class BanerSerializer(serializers.ModelSerializer): ) def get_image(self, baner): - request = self.context.get('request') - http_host = request.META.get('HTTP_ORIGIN') - if http_host and baner.image: - domain = urlparse(http_host).netloc.split(':')[0] - baner_url = 'http://' + domain + baner.image.url - return baner_url + if baner.image: + return 'http://' + settings.MAIN_HOST + '/' + baner.image.url else: return None diff --git a/api/v1/serializers/course.py b/api/v1/serializers/course.py index 551830ad..781af25e 100644 --- a/api/v1/serializers/course.py +++ b/api/v1/serializers/course.py @@ -1,9 +1,8 @@ +from django.contrib.auth import get_user_model from ipware import get_client_ip - from rest_framework import serializers from rest_framework.validators import UniqueValidator - -from django.contrib.auth import get_user_model +from drf_dynamic_fields import DynamicFieldsMixin from apps.course.models import ( Category, Course, @@ -13,17 +12,9 @@ from apps.course.models import ( LiveLessonComment) from .content import ( ImageObjectSerializer, ContentSerializer, ContentCreateSerializer, - GallerySerializer, GalleryImageSerializer, -) - -from apps.content.models import ( - Content, Image, Text, ImageText, Video, - Gallery, GalleryImage, ImageObject, - ContestWork) - -from .user import UserSerializer + GallerySerializer, ) from .mixins import DispatchContentMixin, DispatchGalleryMixin, DispatchMaterialMixin - +from .user import UserSerializer User = get_user_model() @@ -269,7 +260,7 @@ class LessonSerializer(serializers.ModelSerializer): ) -class CourseSerializer(serializers.ModelSerializer): +class CourseSerializer(DynamicFieldsMixin, serializers.ModelSerializer): author = UserSerializer() category = CategorySerializer() materials = MaterialSerializer(many=True) diff --git a/api/v1/views.py b/api/v1/views.py index bb7a0aca..37127268 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -19,7 +19,7 @@ from .serializers.course import ( MaterialSerializer, MaterialCreateSerializer, LessonSerializer, LessonCreateSerializer, LikeCreateSerializer, CourseCommentSerializer, LessonCommentSerializer, - LiveLessonCommentSerializer) + LiveLessonCommentSerializer,) from .serializers.content import ( BanerSerializer, ImageSerializer, ImageCreateSerializer, @@ -37,8 +37,7 @@ from .serializers.school import ( ) from .serializers.payment import ( AuthorBalanceSerializer, AuthorBalanceCreateSerializer, - PaymentSerializer, CoursePaymentSerializer, - SchoolPaymentSerializer, + PaymentSerializer, CoursePaymentCreateSerializer, SchoolPaymentCreateSerializer) from .serializers.user import ( AuthorRequestSerializer, @@ -49,7 +48,7 @@ from .serializers.contest import ( ) from .permissions import ( - IsAdmin, IsAdminOrIsSelf, + IsAdmin, IsAuthorOrAdmin, IsAuthorObjectOrAdmin, IsTeacherOrAdmin, ) @@ -242,6 +241,20 @@ class CourseViewSet(ExtendedModelViewSet): # 'delete': IsAdmin, # } + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset()) + + if request.query_params.get('page'): + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + else: + return Response({'results': []}) + + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + @list_route(methods=['get']) def draft(self, request): drafts = Course.objects.filter(author=request.user, status=Course.DRAFT) diff --git a/apps/course/templates/course/_items.html b/apps/course/templates/course/_items.html index 0bb7c7d2..ff72a87d 100644 --- a/apps/course/templates/course/_items.html +++ b/apps/course/templates/course/_items.html @@ -73,7 +73,6 @@
{{ course.author.get_full_name }}
-
{{ course.created_at_humanize }}
{% data_liked user course as liked %} Редактировать {% endif %} {% if course.author != request.user and not paid and course.price %} - + {% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %} + {% if not paid %} + Подарить другу {% endif %} - >{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %} +
{% endif %}
{{ course.author.get_full_name }}
-
-
{{ course.created_at_humanize }}
-
- + {% if course.price %}
@@ -112,8 +123,6 @@
{{ course.price|floatformat:"-2" }}₽
{% endif %} -
-
@@ -281,30 +290,19 @@ {% endif %}
{{ course.author.get_full_name }}
-
-
{{ course.created_at_humanize }}
-
- -
-
- - - -
-
{{ course.created_at | date:"d F Yг." }}
-
+
{% if course.price %}
diff --git a/apps/course/templates/course/course_only_lessons.html b/apps/course/templates/course/course_only_lessons.html index b603874b..9937f1d0 100644 --- a/apps/course/templates/course/course_only_lessons.html +++ b/apps/course/templates/course/course_only_lessons.html @@ -29,17 +29,22 @@ Редактировать {% endif %} {% if not paid and course.price and not has_full_access %} - + {% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %} + {% if not paid %} + Подарить другу {% endif %} - >{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %} +
{% endif %}
{{ course.author.get_full_name }}
-
-
{{ course.created_at_humanize }}
-
- -
-
- - - -
-
{{ course.created_at | date:"d F Yг." }}
-
+
{% if course.price %}
@@ -119,8 +113,6 @@
{{ course.price|floatformat:"-2" }}₽
{% endif %} -
-
diff --git a/apps/course/templates/course/lesson.html b/apps/course/templates/course/lesson.html index f6fb6a9c..e4f083f0 100644 --- a/apps/course/templates/course/lesson.html +++ b/apps/course/templates/course/lesson.html @@ -47,9 +47,6 @@ {% endif %}
{{ lesson.author.get_full_name }}
-
-
{{ lesson.created_at_humanize }}
-
diff --git a/apps/notification/tasks.py b/apps/notification/tasks.py index 28544d52..03f3cc9e 100644 --- a/apps/notification/tasks.py +++ b/apps/notification/tasks.py @@ -8,7 +8,7 @@ from django.db.models import Max from apps.notification.models import UserNotification from apps.notification.utils import send_email -from apps.payment.models import SchoolPayment, CoursePayment, Payment +from apps.payment.models import SchoolPayment, CoursePayment, Payment, UserGiftCertificate from project.celery import app from project.utils.db import format_sql, execute_sql from project.sengrid import get_sendgrid_client @@ -33,7 +33,7 @@ def send_certificates(email=None, date=None, dry_run=False): file.close() return - date = datetime.strptime(date, '%d-%m-%Y') if date else now().date() + date = datetime.strptime(date, '%d-%m-%Y').date() if date else now().date() today = now().date() users = set(list(SchoolPayment.objects.filter(date_end=date, add_days=False).values_list('user_id', flat=True))) user_notifications_qs = UserNotification.objects.filter(user_id__in=users) @@ -42,6 +42,7 @@ def send_certificates(email=None, date=None, dry_run=False): 'user_id', flat=True).distinct() for user_id in users: if user_id in notified_users: + print('skip', user_id) continue un = user_notifications.get(user_id, UserNotification(user_id=user_id)) print(un.user.email) @@ -54,7 +55,7 @@ def send_certificates(email=None, date=None, dry_run=False): file = open(staticfiles_storage.path(path_pattern % un.certificate_number), 'rb') try: send_email('Грамота от Lil School', un.user.email, 'notification/email/certificate.html', - attachments=[(file.name, file.read(), 'image/jpeg')]) + attachments=[(file.name, file.read(), 'image/jpeg')], user_notification=un) except: print('Not OK') continue @@ -98,3 +99,11 @@ def sendgrid_update_recipients(): sg = get_sendgrid_client() response = sg.client.contactdb.recipients.patch(request_body=data) print(response.body) + + +@app.task +def send_gift_certificate(user_gift_certificate): + user_gift_certificate = UserGiftCertificate.objects.get(id=user_gift_certificate) + send_email('Подарочный сертификат от Lil School', user_gift_certificate.user.email, 'notification/email/gift_certificate.html', + inline_images=[('twitter_icon', 'img/twitter.png'), ('fb_icon', 'img/fb.png'), ('instagram_icon', 'img/instagram.png'),], + user_gift_certificate=user_gift_certificate, gift_certificate=user_gift_certificate.gift_certificate) diff --git a/apps/notification/templates/notification/email/certificate.html b/apps/notification/templates/notification/email/certificate.html index 051a2d94..0185c4c3 100644 --- a/apps/notification/templates/notification/email/certificate.html +++ b/apps/notification/templates/notification/email/certificate.html @@ -1,28 +1,71 @@ {% extends "notification/email/_base.html" %} {% block content %} -

Дорогие родители!

+

Привет!

-

Это письмо адресовано вашим детям - ученикам Lil School. - Если они еще не умеют читать, прочитайте им наше послание, громко и с выражением.
- Спасибо! + {% if not user_notification or user_notification.certificate_number == 1 %} +

+ Поздравляем! Вы прошли месяц обучения в Lil School.
+К письму прикреплена грамота. Распечатайте её и вручите вашим детям.
+Ждём вас в следующем месяце на наших творческих занятиях!

+ {% endif %} + {% if user_notification and user_notification.certificate_number == 2 %} +

+ Вы помните, что каждый месяц вам приходит грамота за прекрасную учебу в нашей творческой школе?
+ Скачивайте. Распечатывайте. И соберите свою коллекцию! +

+ {% endif %} + {% if user_notification and user_notification.certificate_number == 3 %} +

+Вам понравился наш творческий месяц?
+В письме вы найдёте грамоту, она для вашей семьи.
+Как здорово, что у нас есть такие ученики!
+Ждём вас в следующем месяце. +

+ {% endif %} + {% if user_notification and user_notification.certificate_number == 4 %} +

+ Прошёл целый месяц обучения на платформе Lil School - месяц творчества, креатива и невероятных идей.
+Во вложении вас ждёт грамота.
+До встречи на занятиях! +

+ {% endif %} + {% if user_notification and user_notification.certificate_number == 5 %} +

+ Ваша грамота за успехи в учебе в Lil School ждёт вас во вложении.
+Скорее распечатайте её!
+Вам есть чем гордится!
+До встречи в следующем месяце! +

+ {% endif %} + {% if user_notification and user_notification.certificate_number == 6 %} +

+ Как здорово вы потрудились на занятиях в этом месяце!
+И наша грамота уже ждёт вас!
+Спасибо за творчество и креатив.
+Ждём вас в следующем месяце! +

+ {% endif %} + {% if user_notification and user_notification.certificate_number == 7 %} +

+ Какой классный месяц у нас был! Вместе мы очень здорово и креативно потрудились.
+Во вложении вас ждёт заслуженная грамота!
+До встречи на уроках! +

+ {% endif %} + {% if user_notification and user_notification.certificate_number == 8 %} +

+ Месяц творчества и креатива пролетел как один миг! А как много работ мы вместе сделали!
+Вы - большие молодцы.
+Во вложении ваш ждёт грамота!
+До встречи на занятиях. +

+ {% endif %}
-
- Привет, друг!
- Вот и прошёл месяц обучения на платформе «Lil School» - месяц удивительных, творческих приключений и открытий. - Ты так много узнал и столько всего нарисовал! - Как же здорово у тебя все получается! -
-
- Скорее смотри что прикреплено к письму. Да это же ГРАМОТА! Ее можно распечатать и повесить - на видное место в твоей комнате. - Можно показать ее друзьям вместе со всеми работами, над которыми ты так трудился. -
-
- Поздравляем! Мы ждём тебя в новом месяце в рядах наших учеников. -
-
- Команда «Lil School». +
+

+ Команда «Lil School». +

{% endblock content %} diff --git a/apps/notification/templates/notification/email/gift_certificate.html b/apps/notification/templates/notification/email/gift_certificate.html new file mode 100644 index 00000000..f1d8a3f9 --- /dev/null +++ b/apps/notification/templates/notification/email/gift_certificate.html @@ -0,0 +1,112 @@ +{% extends "notification/email/_base.html" %} +{% load settings %} + +{% block content %} +

Поздравляем с успешной + покупкой!

+
+

Вы получаете {{ gift_certificate.price }} лиликов на счет! 1 LIL = 1 руб.
+ Накапливайте монеты и тратьте их на оплату школы и курсов. +

+
+
+
+
+ + + + + + + +
Подарочный сертификат + {{ gift_certificate.price }} р. +
+
+
+
Чтобы воспользоваться сертификатом, перейдите по ссылке
+ +
+ Или воспользуйтесь сертификатом, введя уникальный код на в разделе + вашего профиля на сайте lil.school +
+
Ваш код +
+
+
{{ user_gift_certificate.code }} +
+
+
+ Вы так же можете отправить это письмо, ссылку или код вашему другу, чтобы подарить ему этот сертификат. +
+
+ Присоединяйтесь к нам! +
+
+ {% if config.SERVICE_TWITTER_URL %} + + + + {% endif %} + {% if config.SERVICE_FB_URL %} + + + + {% endif %} + {% if config.SERVICE_INSTAGRAM_URL %} + + + + {% endif %} +
+ +{% endblock content %} diff --git a/apps/notification/utils.py b/apps/notification/utils.py index d556bd96..07510c41 100644 --- a/apps/notification/utils.py +++ b/apps/notification/utils.py @@ -1,16 +1,27 @@ +from django.contrib.staticfiles.storage import staticfiles_storage from twilio.rest import Client - -from django.core.mail import EmailMessage +from django.core.mail import EmailMessage, EmailMultiAlternatives +from anymail.message import attach_inline_image_file from django.conf import settings from django.template.loader import get_template + from project.celery import app @app.task -def send_email(subject, to_email, template_name, attachments=[], **kwargs): - html = get_template(template_name).render(kwargs) - email = EmailMessage(subject, html, to=[to_email], attachments=attachments) - email.content_subtype = 'html' +def send_email(subject, to_email, template_name, attachments=[], inline_images=[], **kwargs): + if inline_images: + email = EmailMultiAlternatives(subject, '', to=[to_email], attachments=attachments) + context = kwargs + for name, path in inline_images: + cid = attach_inline_image_file(email, staticfiles_storage.path(path)) + context[name] = cid + html = get_template(template_name).render(context) + email.attach_alternative(html, "text/html") + else: + html = get_template(template_name).render(kwargs) + email = EmailMessage(subject, html, to=[to_email], attachments=attachments) + email.content_subtype = 'html' email.send() diff --git a/apps/payment/admin.py b/apps/payment/admin.py index 19166563..b26b03a2 100644 --- a/apps/payment/admin.py +++ b/apps/payment/admin.py @@ -5,7 +5,7 @@ from polymorphic.admin import ( PolymorphicChildModelFilter, ) -from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment +from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, GiftCertificate @admin.register(AuthorBalance) @@ -61,3 +61,8 @@ class PaymentAdmin(PolymorphicParentModelAdmin): CoursePayment, SchoolPayment, ) + + +@admin.register(GiftCertificate) +class GiftCertificateAdmin(admin.ModelAdmin): + pass diff --git a/apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py b/apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py new file mode 100644 index 00000000..3f9dcc34 --- /dev/null +++ b/apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py @@ -0,0 +1,34 @@ +# Generated by Django 2.0.6 on 2018-10-29 14:36 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('payment', '0024_auto_20181002_0338'), + ] + + operations = [ + migrations.CreateModel( + name='GiftCertificate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('price', models.DecimalField(decimal_places=2, default=0, editable=False, max_digits=8)), + ], + ), + migrations.CreateModel( + name='UserGiftCertificate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('recipient', models.EmailField(max_length=254, blank=True, null=True,)), + ('bonuses_sent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='payment.UserBonus')), + ('gift_certificate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='payment.GiftCertificate')), + ('payment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='payment.Payment')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='gift_certificates', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/apps/payment/migrations/0026_auto_20181101_1546.py b/apps/payment/migrations/0026_auto_20181101_1546.py new file mode 100644 index 00000000..45cd9906 --- /dev/null +++ b/apps/payment/migrations/0026_auto_20181101_1546.py @@ -0,0 +1,39 @@ +# Generated by Django 2.0.6 on 2018-11-01 15:46 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0025_giftcertificate_usergiftcertificate'), + ] + + operations = [ + migrations.CreateModel( + name='GiftCertificatePayment', + fields=[ + ('payment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='payment.Payment')), + ], + options={ + 'verbose_name': 'Платеж за подарочный сертификат', + 'verbose_name_plural': 'Платежи за подарочные сертификаты', + }, + bases=('payment.payment',), + ), + migrations.AlterModelOptions( + name='giftcertificate', + options={'ordering': ('price',)}, + ), + migrations.AlterField( + model_name='giftcertificate', + name='price', + field=models.DecimalField(decimal_places=2, default=0, max_digits=8), + ), + migrations.AddField( + model_name='giftcertificatepayment', + name='gift_certificate', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='payment.GiftCertificate', verbose_name='Подарочный сертификат'), + ), + ] diff --git a/apps/payment/migrations/0027_auto_20181109_1402.py b/apps/payment/migrations/0027_auto_20181109_1402.py new file mode 100644 index 00000000..a228e06b --- /dev/null +++ b/apps/payment/migrations/0027_auto_20181109_1402.py @@ -0,0 +1,22 @@ +# Generated by Django 2.0.6 on 2018-11-09 14:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0026_auto_20181101_1546'), + ] + + operations = [ + migrations.RemoveField( + model_name='usergiftcertificate', + name='recipient', + ), + migrations.AddField( + model_name='giftcertificate', + name='cover', + field=models.CharField(blank=True, default='', max_length=255), + ), + ] diff --git a/apps/payment/migrations/0028_add_gift_certificates.py b/apps/payment/migrations/0028_add_gift_certificates.py new file mode 100644 index 00000000..18c0ac4f --- /dev/null +++ b/apps/payment/migrations/0028_add_gift_certificates.py @@ -0,0 +1,21 @@ +# Generated by Django 2.0.6 on 2018-11-09 14:03 + +from django.db import migrations +from django.contrib.staticfiles.storage import staticfiles_storage + + +def add_gift_certificates(apps, schema_editor): + GiftCertificate = apps.get_model('payment', 'GiftCertificate') + for price in [1000, 2000, 3000, 5000, 10000]: + GiftCertificate.objects.create(price=price, + cover=staticfiles_storage.url('img/gift-certificates/%d.jpg' % price)) + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0027_auto_20181109_1402'), + ] + + operations = [ + migrations.RunPython(add_gift_certificates), + ] diff --git a/apps/payment/models.py b/apps/payment/models.py index a953d5e8..413e8bf8 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -1,5 +1,6 @@ from decimal import Decimal import arrow +import short_url from django.db.models import Func, F from paymentwall import Pingback @@ -13,6 +14,7 @@ from django.core.validators import RegexValidator from django.utils.timezone import now from django.conf import settings +from apps.content.models import ImageObject from project.utils import weekdays_in_date_range from apps.course.models import Course @@ -147,6 +149,8 @@ class Payment(PolymorphicModel): referrer_bonus = user.referral.referrer_bonus if payment and payment.is_paid(): price = payment.amount + elif isinstance(payment, GiftCertificatePayment): + price = payment.gift_certificate.price elif course: price = course.price else: @@ -244,6 +248,12 @@ class Payment(PolymorphicModel): else: author_balance.amount = self.amount author_balance.save() + if isinstance(self, GiftCertificatePayment) and self.is_paid(): + ugs, created = UserGiftCertificate.objects.get_or_create(user=self.user, gift_certificate=self.gift_certificate, + payment=self) + if created: + from apps.notification.tasks import send_gift_certificate + send_gift_certificate(ugs.id) # Если юзер реферал и нет платежа, где применялась скидка if hasattr(self.user, 'referral') and not self.user.referral.payment and self.is_paid(): # Платеж - как сигнал, что скидка применилась @@ -283,6 +293,15 @@ class SchoolPayment(Payment): return arrow.get(self.date_end, settings.TIME_ZONE).humanize(locale='ru') +class GiftCertificatePayment(Payment): + gift_certificate = models.ForeignKey('GiftCertificate', on_delete=models.CASCADE, + verbose_name='Подарочный сертификат', related_name='payments') + + class Meta: + verbose_name = 'Платеж за подарочный сертификат' + verbose_name_plural = 'Платежи за подарочные сертификаты' + + class UserBonus(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='bonuses') amount = models.DecimalField(max_digits=8, decimal_places=2, default=0, editable=False) @@ -292,3 +311,22 @@ class UserBonus(models.Model): class Meta: ordering = ('created_at',) + + +class GiftCertificate(models.Model): + price = models.DecimalField(max_digits=8, decimal_places=2, default=0) + cover = models.CharField(max_length=255, blank=True, default='') + + class Meta: + ordering = ('price',) + + +class UserGiftCertificate(models.Model): + gift_certificate = models.ForeignKey(GiftCertificate, on_delete=models.CASCADE,) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='gift_certificates') + payment = models.ForeignKey(Payment, on_delete=models.SET_NULL, null=True) + bonuses_sent = models.ForeignKey(UserBonus, on_delete=models.CASCADE, blank=True, null=True) + + @property + def code(self): + return short_url.encode_url(self.id) if self.id else None diff --git a/apps/payment/templates/payment/gift_certificate.html b/apps/payment/templates/payment/gift_certificate.html new file mode 100644 index 00000000..7d291137 --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate.html @@ -0,0 +1,17 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} + +{% block content %} +
+
+
Вам подарок!
+
+ Пользователь ХХХ дарит вам сертификат на сумму ХХХ руб +
+ +
+
+{% endblock content %} diff --git a/apps/payment/templates/payment/gift_certificate_get.html b/apps/payment/templates/payment/gift_certificate_get.html new file mode 100644 index 00000000..6242f060 --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate_get.html @@ -0,0 +1,15 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} +
+
+
+
Бонусы зачислены на ваш счет!
+
Вы можете оплатить с их помощью курс или онлайн-школу
+ +
+
+
+{% endblock content %} diff --git a/apps/payment/templates/payment/gift_certificate_item.html b/apps/payment/templates/payment/gift_certificate_item.html new file mode 100644 index 00000000..5f4a318e --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate_item.html @@ -0,0 +1,36 @@ +{% load thumbnail %} +{% load static %} +{% load data_liked from data_liked %} + +
+
+ +
+
+ + {% if user_gift_certificate and not user_gift_certificate.bonuses_sent %} + подарочный сертификат + {% else %}подарочный сертификат{% endif %} +
{{ gift_certificate.price|floatformat:"-2" }}₽
+
+ {% if user_gift_certificate %} + {% if user_gift_certificate.bonuses_sent %} +
+ + Получено +
+ {% else %} +
+ + Ожидает получения +
+ {% endif %} + {% else %} + Купить сертификат + {% endif %} +
diff --git a/apps/payment/templates/payment/gift_certificate_items.html b/apps/payment/templates/payment/gift_certificate_items.html new file mode 100644 index 00000000..05730083 --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate_items.html @@ -0,0 +1,4 @@ +{% for gift_certificate in gift_certificates %} + {% cycle 'theme_pink2' 'theme_cyan' 'theme_violet2' as theme_color silent %} + {% include "payment/gift_certificate_item.html" %} +{% endfor %} diff --git a/apps/payment/templates/payment/gift_certificate_payment_success.html b/apps/payment/templates/payment/gift_certificate_payment_success.html new file mode 100644 index 00000000..793eb25e --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate_payment_success.html @@ -0,0 +1,13 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} +
+
+
+
Вы успешно приобрели подарочный сертификат!
+
Мы отправили письмо с сертификатом на вашу почту.
+ +
+
+
+{% endblock content %} diff --git a/apps/payment/templates/payment/gift_certificates.html b/apps/payment/templates/payment/gift_certificates.html new file mode 100644 index 00000000..5fa4bf2e --- /dev/null +++ b/apps/payment/templates/payment/gift_certificates.html @@ -0,0 +1,35 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} + +{% block content %} +
+
+
+ Подарочные сертификаты +
+
+
+ +
+
+
Подарочный сертификат Lil City - отличный презент на любой праздник.
+А также сертификат можно подарить без повода - развитие
творческого потенциала всегда уместно и актуально. +При покупке подарочного сертификата мы высылаем письмо,
+где будет уникальный код и уникальная ссылка. Письмо или ссылку
+на него вы можете переслать другому. Сертификатом можно
+воспользоваться, перейдя по ссылке. + +
+
Сертификаты
+
+ {% include "payment/gift_certificate_items.html" %} +
+
+
+{% endblock content %} diff --git a/apps/payment/templates/payment/payment_success.html b/apps/payment/templates/payment/payment_success.html index 7d174018..9d256634 100644 --- a/apps/payment/templates/payment/payment_success.html +++ b/apps/payment/templates/payment/payment_success.html @@ -7,10 +7,17 @@ - {% else %} + {% endif %} + {% if course %}
Вы успешно приобрели курс!
+ {% endif %} + {% if gift_certificate %} +
Вы успешно приобрели подарочный сертификат!
+ {% endif %}
diff --git a/apps/payment/views.py b/apps/payment/views.py index 6be7734f..02a2b874 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -1,5 +1,5 @@ from decimal import Decimal - +import short_url import arrow import json import logging @@ -12,9 +12,9 @@ import calendar from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.http import HttpResponse +from django.http import HttpResponse, Http404 from django.shortcuts import redirect, get_object_or_404 -from django.views.generic import View, TemplateView +from django.views.generic import View, TemplateView, DetailView from django.views.decorators.csrf import csrf_exempt from django.urls import reverse_lazy from django.utils.decorators import method_decorator @@ -27,7 +27,8 @@ from apps.course.models import Course from apps.school.models import SchoolSchedule from apps.payment.tasks import transaction_to_mixpanel, product_payment_to_mixpanel, transaction_to_roistat -from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus +from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus, GiftCertificate, \ + GiftCertificatePayment, UserGiftCertificate logger = logging.getLogger('django') @@ -216,6 +217,8 @@ class PaymentwallCallbackView(View): product_payment_class = CoursePayment elif product_type_name == 'school': product_payment_class = SchoolPayment + elif product_type_name == 'gift_certificate': + product_payment_class = GiftCertificatePayment else: return HttpResponse(status=403) @@ -264,6 +267,15 @@ class PaymentwallCallbackView(View): 'created_at': payment.created_at, 'update_at': payment.update_at, } + elif product_type_name == 'gift_certificate': + properties = { + 'payment_id': payment.id, + 'amount': payment.amount, + 'status': payment.status, + 'gift_certificate': payment.gift_certificate.id, + 'created_at': payment.created_at, + 'update_at': payment.update_at, + } payment.save() product_payment_to_mixpanel.delay( @@ -297,3 +309,84 @@ class PaymentwallCallbackView(View): else: raise DisallowedPingbackHost return HttpResponse(status=403) + + +class GiftCertificatesView(TemplateView): + model = GiftCertificate + template_name = 'payment/gift_certificates.html' + + def get(self, request, *args, **kwargs): + gift_certificates = GiftCertificate.objects.all() + context = self.get_context_data(**kwargs) + context['gift_certificates'] = gift_certificates + return self.render_to_response(context) + + +@method_decorator(login_required, name='dispatch') +class GiftCertificateBuyView(TemplateView): + model = GiftCertificate + template_name = 'payment/paymentwall_widget.html' + + def get(self, request, pk, *args, **kwargs): + gift_certificate = get_object_or_404(GiftCertificate, pk=pk) + roistat_visit = request.COOKIES.get('roistat_visit', None) + gift_certificate_payment = GiftCertificatePayment.objects.create( + user=request.user, + gift_certificate=gift_certificate, + roistat_visit=roistat_visit,) + context = self.get_context_data(**kwargs) + product = Product( + f'gift_certificate_{gift_certificate_payment.id}', + gift_certificate_payment.amount, + 'RUB', + 'Подарочный сертификат', + ) + host = urlsplit(self.request.META.get('HTTP_REFERER')) + host = str(host[0]) + '://' + str(host[1]) + widget = Widget( + str(request.user.id), + 'p1_1', + [product], + extra_params={ + 'lang': 'ru', + 'evaluation': 1, + 'demo': 1, + 'test_mode': 1, + 'success_url': host + str(reverse_lazy('gift-certificate-payment-success', args=[gift_certificate_payment.id])), + 'failure_url': host + str(reverse_lazy('payment-error')), + } + ) + context['widget'] = widget.get_html_code() + return self.render_to_response(context) + + +@method_decorator(login_required, name='dispatch') +class GiftCertificateBuySuccessView(TemplateView): + template_name = 'payment/gift_certificate_payment_success.html' + + def get(self, request, payment_id=None, *args, **kwargs): + try: + GiftCertificatePayment.objects.get(id=payment_id) + except: + raise Http404() + return self.render_to_response(context={'gift_certificate': True}) + + +@method_decorator(login_required, name='dispatch') +class GiftCertificateGetView(TemplateView): + template_name = 'payment/gift_certificate_get.html' + + def get(self, request, slug, *args, **kwargs): + try: + ugs = get_object_or_404(UserGiftCertificate, pk=short_url.decode_url(slug)) + except: + raise Http404() + if UserBonus.objects.filter(payment=ugs.payment).exists(): + raise Http404() + bonuses = UserBonus.objects.create(user=request.user, amount=ugs.gift_certificate.price, + payment=ugs.payment) + ugs.bonuses_sent = bonuses + ugs.save() + context = self.get_context_data(**kwargs) + return self.render_to_response(context) + diff --git a/apps/school/templates/school/livelesson_detail.html b/apps/school/templates/school/livelesson_detail.html index d3e5c5af..095dbb01 100644 --- a/apps/school/templates/school/livelesson_detail.html +++ b/apps/school/templates/school/livelesson_detail.html @@ -5,9 +5,15 @@ {% block twurl %}{{ request.build_absolute_uri }}{% endblock twurl %} {% block ogtitle %}{{ livelesson.title }} - {{ block.super }}{% endblock ogtitle %} {% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %} -{% if livelesson.cover and livelesson.cover.image %} -{% block ogimage %}http://{{request.META.HTTP_HOST}}{{ livelesson.cover.image.url }}{% endblock ogimage %} -{% endif %} +{% block ogimage %} + {% if livelesson.cover and livelesson.cover.image %}http://{{request.META.HTTP_HOST}}{{ livelesson.cover.image.url }}{% else %}{{ block.super }}{% endif %} +{% endblock ogimage %} +{% block ogimage-width %} + {% if livelesson.cover and livelesson.cover.image %}{{ livelesson.cover.image.width }}{% else %}{{ block.super }}{% endif %} +{% endblock ogimage-width %} +{% block ogimage-height %} + {% if livelesson.cover and livelesson.cover.image %}{{ livelesson.cover.image.height }}{% else %}{{ block.super }}{% endif %} +{% endblock ogimage-height %} {% block content %}
diff --git a/apps/school/templates/school/livelesson_detail_unauth.html b/apps/school/templates/school/livelesson_detail_unauth.html new file mode 100644 index 00000000..4493928f --- /dev/null +++ b/apps/school/templates/school/livelesson_detail_unauth.html @@ -0,0 +1,27 @@ +{% extends "templates/lilcity/index.html" %} +{% load static %} +{% block title %}{{ livelesson.title }} - {{ block.super }}{% endblock title %} + +{% block twurl %}{{ request.build_absolute_uri }}{% endblock twurl %} +{% block ogtitle %}{{ livelesson.title }} - {{ block.super }}{% endblock ogtitle %} +{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %} +{% block ogimage %} + {% if livelesson.cover and livelesson.cover.image %}http://{{request.META.HTTP_HOST}}{{ livelesson.cover.image.url }}{% else %}{{ block.super }}{% endif %} +{% endblock ogimage %} +{% block ogimage-width %} + {% if livelesson.cover and livelesson.cover.image %}{{ livelesson.cover.image.width }}{% else %}{{ block.super }}{% endif %} +{% endblock ogimage-width %} +{% block ogimage-height %} + {% if livelesson.cover and livelesson.cover.image %}{{ livelesson.cover.image.height }}{% else %}{{ block.super }}{% endif %} +{% endblock ogimage-height %} + +{% block content %} +
+
+
+ Чтобы посмотреть урок необходимо авторизоваться +
+ +
+
+{% endblock content %} diff --git a/apps/school/templates/summer/promo.html b/apps/school/templates/summer/promo.html index 8b1ed12e..7d12fac4 100644 --- a/apps/school/templates/summer/promo.html +++ b/apps/school/templates/summer/promo.html @@ -20,6 +20,9 @@ {% if is_purchased %}ваша подписка истекает {{ subscription_ends_humanize }}
перейти к оплате{% endif %} + {% if not is_purchased and not is_purchased_future %} + Подарить другу + {% endif %}
diff --git a/apps/school/views.py b/apps/school/views.py index a3317ad0..a3cc379b 100644 --- a/apps/school/views.py +++ b/apps/school/views.py @@ -57,26 +57,28 @@ class LiveLessonsView(ListView): return queryset -@method_decorator(login_required, name='dispatch') class LiveLessonsDetailView(DetailView): model = LiveLesson template_name = 'school/livelesson_detail.html' def get(self, request, pk=None): self.object = self.get_object() - is_purchased = SchoolPayment.objects.filter( - user=request.user, - date_start__lte=now(), - date_end__gte=now() - timedelta(days=7), - status__in=[ - Pingback.PINGBACK_TYPE_REGULAR, - Pingback.PINGBACK_TYPE_GOODWILL, - Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, - ], - weekdays__contains=[self.object.date.weekday() + 1], - ).exists() - if not is_purchased and request.user.role not in [User.ADMIN_ROLE, User.TEACHER_ROLE]: - raise Http404 + if request.user.is_authenticated: + is_purchased = SchoolPayment.objects.filter( + user=request.user, + date_start__lte=now(), + date_end__gte=now() - timedelta(days=7), + status__in=[ + Pingback.PINGBACK_TYPE_REGULAR, + Pingback.PINGBACK_TYPE_GOODWILL, + Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, + ], + weekdays__contains=[self.object.date.weekday() + 1], + ).exists() + if not is_purchased and request.user.role not in [User.ADMIN_ROLE, User.TEACHER_ROLE]: + raise Http404 + else: + self.template_name = 'school/livelesson_detail_unauth.html' context = self.get_context_data(object=self.object) return self.render_to_response(context) diff --git a/apps/user/templates/user/profile.html b/apps/user/templates/user/profile.html index 9ad4d552..b8e19a19 100644 --- a/apps/user/templates/user/profile.html +++ b/apps/user/templates/user/profile.html @@ -2,7 +2,10 @@
- Редактировать +
{% thumbnail user.photo "120x120" crop="center" as im %} @@ -91,10 +94,16 @@ Перейти в онлайн-школу
{% endif %} + {% if user_gift_certificates.exists %} + {% for ugs in user_gift_certificates %} + {% cycle 'theme_pink2' 'theme_cyan' 'theme_violet2' as theme_color silent %} + {% include "payment/gift_certificate_item.html" with gift_certificate=ugs.gift_certificate user_gift_certificate=ugs %} + {% endfor %} + {% endif %} {% if paid.exists %} {% include "course/course_items.html" with course_items=paid %} {% endif %} - {% if not is_school_purchased and not paid.exists %} + {% if not is_school_purchased and not paid.exists and not user_gift_certificates.exists %}
Вы пока ничего не приобрели...
diff --git a/apps/user/views.py b/apps/user/views.py index f4ea0ebb..e456af1c 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -24,7 +24,7 @@ from apps.config.models import Config from apps.course.models import Course from apps.notification.utils import send_email from apps.school.models import SchoolSchedule -from apps.payment.models import AuthorBalance, CoursePayment, SchoolPayment, Payment +from apps.payment.models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserGiftCertificate from apps.user.models import AuthorRequest, EmailSubscription, SubscriptionCategory from .forms import AuthorRequesForm, UserEditForm, WithdrawalForm @@ -59,6 +59,7 @@ class ProfileView(TemplateView): author=self.object, ) context['is_author'] = context['published'] or self.request.user.role == User.AUTHOR_ROLE + context['user_gift_certificates'] = UserGiftCertificate.objects.filter(user=self.request.user) context['paid'] = Course.objects.filter( payments__in=CoursePayment.objects.filter( user=self.object, diff --git a/docker/.env.example b/docker/.env.example index 94fdccb7..f637abb9 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -9,7 +9,7 @@ POSTGRES_PASSWORD=GPVs/E/{5&qe DJANGO_SETTINGS_MODULE=project.settings DATABASE_SERVICE_HOST=db SECRET_KEY=jelm*91lj(_-o20+6^a+bgv!4s6e_efry^#+f#=1ak&s1xr-2j -MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390cb +MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390c MAILGUN_SENDER_DOMAIN=mail.9ev.ru DEFAULT_FROM_EMAIL=postmaster@mail.9ev.ru TWILIO_ACCOUNT=ACdf4a96b776cc764bc3ec0f0e136ba550 diff --git a/docker/.env.review b/docker/.env.review index 351dc795..dc0ff7ab 100644 --- a/docker/.env.review +++ b/docker/.env.review @@ -9,7 +9,7 @@ POSTGRES_PASSWORD=GPVs/E/{5&qe DJANGO_SETTINGS_MODULE=project.settings DATABASE_SERVICE_HOST=db SECRET_KEY=jelm*91lj(_-o20+6^a+bgv!4s6e_efry^#+f#=1ak&s1xr-2j -MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390cb +MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390c MAILGUN_SENDER_DOMAIN=mail.9ev.ru DEFAULT_FROM_EMAIL=postmaster@mail.9ev.ru TWILIO_ACCOUNT=ACdf4a96b776cc764bc3ec0f0e136ba550 diff --git a/project/settings.py b/project/settings.py index e92ac41b..3bd90b7d 100644 --- a/project/settings.py +++ b/project/settings.py @@ -185,8 +185,8 @@ LOGIN_URL = '/' # https://github.com/anymail/django-anymail ANYMAIL = { - 'MAILGUN_API_KEY': os.getenv('MAILGUN_API_KEY', 'key-ec6af2d43d031d59bff6b1c8fb9390cb'), - 'MAILGUN_SENDER_DOMAIN': os.getenv('MAILGUN_SENDER_DOMAIN', 'mail.9ev.ru'), + 'MAILGUN_API_KEY': os.getenv('MAILGUN_API_KEY', ''), + 'MAILGUN_SENDER_DOMAIN': os.getenv('MAILGUN_SENDER_DOMAIN', ''), } EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'postmaster@mail.9ev.ru') diff --git a/project/templates/blocks/baner.html b/project/templates/blocks/baner.html index df8b81e0..4f289bce 100644 --- a/project/templates/blocks/baner.html +++ b/project/templates/blocks/baner.html @@ -1,11 +1,13 @@ {% if baner %} {% elif user.is_authenticated and online_coming_soon and school_schedule and school_schedule.start_at_humanize %}
@@ -45,6 +49,10 @@ {% endif %} class="main__btn btn" >{% if not school_schedule.weekday in school_schedules_purchased %}Получить доступ{% else %}Смотреть урок{% endif %} + + {% if not is_purchased and not is_purchased_future %} + Подарить другу + {% endif %}
{% else %}
@@ -59,6 +67,7 @@ > купить доступ от {{ min_school_price }} руб./месяц + Подарить другу {% else %} Подробнее {% endif %} diff --git a/project/templates/lilcity/index.html b/project/templates/lilcity/index.html index 60d189f9..fb789f50 100644 --- a/project/templates/lilcity/index.html +++ b/project/templates/lilcity/index.html @@ -18,9 +18,9 @@ {% comment %} {% endcomment %} - - - + + + @@ -145,6 +145,10 @@ {% if course %} {% include "templates/blocks/popup_course_buy.html" %} {% endif %} + {% if is_gift_certificate_url %} + {% include "templates/blocks/popup_gift_certificate.html" %} + {% endif %} + {% include "templates/blocks/popup_enter_gift_code.html" %} {% include "templates/blocks/popup_course_lock.html" %} {% include "templates/blocks/popup_subscribe.html" %} {% include "templates/blocks/popup_capture_email.html" %} diff --git a/project/urls.py b/project/urls.py index 38773131..4946c577 100644 --- a/project/urls.py +++ b/project/urls.py @@ -34,8 +34,8 @@ from apps.user.views import ( from apps.payment.views import ( CourseBuySuccessView, CourseBuyView, PaymentwallCallbackView, SchoolBuySuccessView, - SchoolBuyView, -) + SchoolBuyView, GiftCertificatesView, GiftCertificateBuyView, + GiftCertificateBuySuccessView, GiftCertificateGetView) from .views import AboutView, IndexView, SchoolSchedulesView @@ -91,6 +91,11 @@ urlpatterns = [ path('contest//', ContestView.as_view(), name='contest'), path('contest-work//', ContestWorkView.as_view(), name='contest_work'), path('contest-work//comment', contest_work_comment, name='contest_work_comment'), + path('gift-certificates', GiftCertificatesView.as_view(), name='gift-certificates'), + path('gift-certificate//checkout', GiftCertificateBuyView.as_view(), name='gift-certificate-checkout'), + path('payments/gift-certificate//success', GiftCertificateBuySuccessView.as_view(), + name='gift-certificate-payment-success'), + path('gift-certificate//get', GiftCertificateGetView.as_view(), name='gift-certificate-get'), ] diff --git a/project/views.py b/project/views.py index 29189994..d563f0c6 100644 --- a/project/views.py +++ b/project/views.py @@ -9,7 +9,7 @@ from paymentwall.pingback import Pingback from apps.course.models import Course from apps.school.models import SchoolSchedule -from apps.payment.models import SchoolPayment +from apps.payment.models import SchoolPayment, UserGiftCertificate User = get_user_model() @@ -23,6 +23,7 @@ class IndexView(TemplateView): def get_context_data(self): referrer = self.request.GET.get('referrer') + user_gift_certificate = self.request.GET.get('gift-certificate') is_referral_url = bool(referrer) context = super().get_context_data() @@ -77,7 +78,17 @@ class IndexView(TemplateView): else: referrer = None + if user_gift_certificate: + try: + user_gift_certificate = short_url.decode_url(user_gift_certificate) + user_gift_certificate = UserGiftCertificate.objects.get(pk=user_gift_certificate, bonuses_sent__isnull=True) + except: + user_gift_certificate = None + context.update({ + 'gift_certificate': user_gift_certificate.gift_certificate if user_gift_certificate else None, + 'user_gift_certificate': user_gift_certificate, + 'is_gift_certificate_url': bool(user_gift_certificate), 'referrer': referrer, 'is_referral_url': is_referral_url, 'online': online, diff --git a/requirements.txt b/requirements.txt index 052bb32e..27daa65a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,3 +33,4 @@ django-imagekit pusher==2.0.1 short_url sendgrid +drf_dynamic_fields diff --git a/web/src/img/clock.png b/web/src/img/clock.png new file mode 100644 index 00000000..dd49d20c Binary files /dev/null and b/web/src/img/clock.png differ diff --git a/web/src/img/done.png b/web/src/img/done.png new file mode 100644 index 00000000..28a2934a Binary files /dev/null and b/web/src/img/done.png differ diff --git a/web/src/img/fb.png b/web/src/img/fb.png new file mode 100644 index 00000000..a31cdcd3 Binary files /dev/null and b/web/src/img/fb.png differ diff --git a/web/src/img/gift-certificates/1000.jpg b/web/src/img/gift-certificates/1000.jpg new file mode 100644 index 00000000..9d682d7f Binary files /dev/null and b/web/src/img/gift-certificates/1000.jpg differ diff --git a/web/src/img/gift-certificates/10000.jpg b/web/src/img/gift-certificates/10000.jpg new file mode 100644 index 00000000..9695841d Binary files /dev/null and b/web/src/img/gift-certificates/10000.jpg differ diff --git a/web/src/img/gift-certificates/2000.jpg b/web/src/img/gift-certificates/2000.jpg new file mode 100644 index 00000000..ec0483ac Binary files /dev/null and b/web/src/img/gift-certificates/2000.jpg differ diff --git a/web/src/img/gift-certificates/3000.jpg b/web/src/img/gift-certificates/3000.jpg new file mode 100644 index 00000000..bb6f3970 Binary files /dev/null and b/web/src/img/gift-certificates/3000.jpg differ diff --git a/web/src/img/gift-certificates/5000.jpg b/web/src/img/gift-certificates/5000.jpg new file mode 100644 index 00000000..5bb83064 Binary files /dev/null and b/web/src/img/gift-certificates/5000.jpg differ diff --git a/web/src/img/instagram.png b/web/src/img/instagram.png new file mode 100644 index 00000000..da7043cc Binary files /dev/null and b/web/src/img/instagram.png differ diff --git a/web/src/img/twitter.png b/web/src/img/twitter.png new file mode 100644 index 00000000..9ce49587 Binary files /dev/null and b/web/src/img/twitter.png differ diff --git a/web/src/js/modules/popup.js b/web/src/js/modules/popup.js index 22f27ffb..22505dc8 100644 --- a/web/src/js/modules/popup.js +++ b/web/src/js/modules/popup.js @@ -245,6 +245,21 @@ $(document).ready(function () { } popup.data('next-url', nextUrl); } + + if( data === '.js-popup-enter-gift-code') { + const $giftCode = popup.find('.enter-gift-code__code'); + const $giftError = popup.find('.enter-gift-code__error'); + $giftCode.val(''); + $giftError.text(''); + popup.find('.enter-gift-code__btn').one('click', () => { + const code = $giftCode.val(); + if(! code){ + $giftError.text('Укажите код'); + return false; + } + window.location.href = `/gift-certificate/${code}/get`; + }); + } }); $('.js-popup-close').on('click', function(e){ @@ -271,7 +286,13 @@ $(document).ready(function () { } }); - function showPopup(popupName){ + + if(window.LIL_STORE.flags.isGiftCertificateUrl){ + popup = $('.js-popup-gift-certificate'); + showPopup(); + } + + function showPopup(){ if(! popup && popupName){ popup = $(popupName); } diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index feb8e121..657907f5 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -26,12 +26,15 @@ $pink: #FF9393 $pink-light: #FDF8F9 +$pink2: #FEB9B9 $purple: #B995D9 $green: #8ECFC0 $green-light: #5BD700 +$cyan: #B6DFD6 $gray: #A7A7A7 $blue: #4A90E2 $viol: #B995D9 +$viol2: #A186BD $bg: #f8f8f8 $border: #E6E6E6 $cl: #191919 @@ -206,6 +209,10 @@ button &_pink background: #ff9393 color: white + &_stroke-black + background: none + border: 1px solid $cl + color: $cl &_md padding: 18px 24px 17px +m @@ -1063,6 +1070,7 @@ a[name] flex: 0 0 40px &__info flex: 0 0 calc(100% - 50px) + display: flex &__name, &__date +fb @@ -1070,9 +1078,8 @@ a[name] text-transform: uppercase letter-spacing: 0.5px line-height: 1.1 - &__name - margin-bottom: 5px &__meta + flex: 1 display: flex align-items: center &__date @@ -1635,6 +1642,12 @@ a.grey-link color: $green &_violet color: $viol + &_violet2 + color: $viol2 + &_pink2 + color: $pink2 + &_cyan + color: $cyan &_lg font-size: 20px letter-spacing: 4px @@ -2546,6 +2559,7 @@ a.grey-link &__item display: flex align-items: center + color: inherit &__item //&:not(:last-child) margin-right: 40px @@ -3202,12 +3216,16 @@ a.grey-link background: transparent border: 1px solid $gray color: $gray - &_edit - position: absolute - top: 0 - right: 0 - +m - display: none !important + margin-bottom: 10px + width: 100% + &__btns + position: absolute + top: 0 + right: 0 + display: flex + flex-direction: column + +m + display: none !important &__row display: flex margin-bottom: 25px @@ -3987,7 +4005,7 @@ a.grey-link padding-right: 20px flex: 0 0 90px &--content - flex: 0 0 calc(100% - 254px) + flex: 1 +m flex: 0 0 calc(100% - 114px) &--toggle @@ -4306,6 +4324,13 @@ a background: white +m height: auto + &__image-wrap + background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(159,159,159,0) 72%, rgba(221,221,221,0.65) 100%) + background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(159,159,159,0) 72%,rgba(221,221,221,0.65) 100%) + background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(159,159,159,0) 72%,rgba(221,221,221,0.65) 100%) + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#a6dddddd',GradientType=0 ) + width: 100% + height: 100% &__image height: 100% +m @@ -4410,3 +4435,64 @@ a &__lil-coin-img margin-bottom: -5px margin-right: 4px + + +.gift-certificates + display: flex + margin: 0 -10px + flex-wrap: wrap + +m + display: block + margin: 0 + &__item + display: block + margin: 0 10px 60px + color: $cl + flex: 0 0 calc(33.33% - 20px) + +t + margin-bottom: 50px !important + +m + margin: 0 0 30px + &__preview + display: block + position: relative + margin-bottom: 15px + border-radius: 2px + color: $cl + overflow: hidden + width: 300px + height: 200px + +t + margin-bottom: 10px + &__cover + object-fit: cover; + width: 100%; + &__details + display: flex + margin-bottom: 10px + &__price + margin-left: auto + +fb + font-size: 12px + letter-spacing: 2px + color: $cl + &__title + text-transform: uppercase + &__status + font-family: 'ProximaNova-Bold', serif + font-size: 12px + letter-spacing: 2px + text-transform: uppercase + & .icon + width: 16px + display: inline-block + height: 16px + margin-bottom: -4px + &__buy-btn + width: 100% + &__preview.theme_pink2 + background: $pink2 + &__preview.theme_cyan + background: $cyan + &__preview.theme_violet2 + background: $viol2 diff --git a/web/webpack.config.js b/web/webpack.config.js index 7455fec9..8156ab03 100644 --- a/web/webpack.config.js +++ b/web/webpack.config.js @@ -14,6 +14,7 @@ module.exports = { sprite: glob('./src/icons/*.svg'), images: glob('./src/img/*.*'), imagesCertificates: glob('./src/img/user-certificates/*'), + imagesGiftCertificates: glob('./src/img/gift-certificates/*'), fonts: glob('./src/fonts/*') }, output: { @@ -85,7 +86,8 @@ module.exports = { }, { test: /\.(png|gif|jpg|svg)$/, - exclude: [path.resolve(__dirname, 'src/icons'), path.resolve(__dirname, 'src/img/user-certificates')], + exclude: [path.resolve(__dirname, 'src/icons'), path.resolve(__dirname, 'src/img/user-certificates'), + path.resolve(__dirname, 'src/img/gift-certificates')], loader: 'file-loader?name=[name].[ext]&outputPath=./img/' }, { @@ -93,6 +95,11 @@ module.exports = { include: path.resolve(__dirname, 'src/img/user-certificates'), loader: 'file-loader?name=[name].[ext]&outputPath=./img/user-certificates/' }, + { + test: /\.(png|jpg)$/, + include: path.resolve(__dirname, 'src/img/gift-certificates'), + loader: 'file-loader?name=[name].[ext]&outputPath=./img/gift-certificates/' + }, { test: /\.(ttf|otf|eot|woff(2)?)(\?[a-z0-9]+)?$/, loader: 'file-loader?name=[name].[ext]'