diff --git a/api/v1/serializers/config.py b/api/v1/serializers/config.py index 526d2066..9298a024 100644 --- a/api/v1/serializers/config.py +++ b/api/v1/serializers/config.py @@ -27,6 +27,9 @@ class ConfigSerializer(serializers.ModelSerializer): REFERRAL_BONUS = serializers.IntegerField() MAIN_PAGE_VIDEO_URL = serializers.CharField(required=False) MAIN_PAGE_VIDEO_PREVIEW_IMG = serializers.SerializerMethodField() + NUMBER_OF_STUDENTS = serializers.IntegerField() + NUMBER_OF_COUNTRIES = serializers.IntegerField() + NUMBER_OF_CITIES = serializers.IntegerField() class Meta: model = Config @@ -50,6 +53,9 @@ class ConfigSerializer(serializers.ModelSerializer): 'REFERRAL_BONUS', 'MAIN_PAGE_VIDEO_URL', 'MAIN_PAGE_VIDEO_PREVIEW_IMG', + 'NUMBER_OF_STUDENTS', + 'NUMBER_OF_COUNTRIES', + 'NUMBER_OF_CITIES', ) def get_SCHOOL_LOGO_IMAGE(self, config): diff --git a/api/v1/views.py b/api/v1/views.py index 2a9c1b0f..4bde8660 100644 --- a/api/v1/views.py +++ b/api/v1/views.py @@ -212,6 +212,15 @@ class LikeViewSet(ExtendedModelViewSet): headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + @action(methods=['get'], detail=False, url_path='course-liked') + def course_liked(self, request): + user_id = request.query_params.get('user_id') + course_id = request.query_params.get('course_id') + course = Course.objects.get(id=course_id) + return Response({ + 'is_liked': course.likes.filter(user_id=user_id).exists() + }) + class CategoryViewSet(BothListFormatMixin, ExtendedModelViewSet): queryset = Category.objects.order_by('-id') diff --git a/apps/config/migrations/0014_auto_20190605_1338.py b/apps/config/migrations/0014_auto_20190605_1338.py new file mode 100644 index 00000000..a89f0dc8 --- /dev/null +++ b/apps/config/migrations/0014_auto_20190605_1338.py @@ -0,0 +1,28 @@ +# Generated by Django 2.0.7 on 2019-06-05 13:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('config', '0013_config_main_page_video_preview_img'), + ] + + operations = [ + migrations.AddField( + model_name='config', + name='NUMBER_OF_CITIES', + field=models.IntegerField(default=107), + ), + migrations.AddField( + model_name='config', + name='NUMBER_OF_COUNTRIES', + field=models.IntegerField(default=81), + ), + migrations.AddField( + model_name='config', + name='NUMBER_OF_STUDENTS', + field=models.IntegerField(default=17000), + ), + ] diff --git a/apps/config/models.py b/apps/config/models.py index 18756e20..daf9bc0e 100644 --- a/apps/config/models.py +++ b/apps/config/models.py @@ -23,6 +23,9 @@ class Config(models.Model): REFERRAL_BONUS = models.IntegerField(default=10) MAIN_PAGE_VIDEO_URL = models.URLField(max_length=126, default='https://www.youtube.com/embed/1w3amQGtQyc') MAIN_PAGE_VIDEO_PREVIEW_IMG = models.ImageField(null=True, blank=True) + NUMBER_OF_STUDENTS = models.IntegerField(default=17000) + NUMBER_OF_COUNTRIES = models.IntegerField(default=81) + NUMBER_OF_CITIES = models.IntegerField(default=107) def save(self, *args, **kwargs): self.pk = 1 @@ -57,5 +60,8 @@ class Config(models.Model): 'REFERRAL_BONUS': '', 'MAIN_PAGE_VIDEO_URL': '', 'MAIN_PAGE_VIDEO_PREVIEW_IMG': '', + 'NUMBER_OF_STUDENTS': '', + 'NUMBER_OF_COUNTRIES': '', + 'NUMBER_OF_CITIES': '', } return obj diff --git a/apps/content/models.py b/apps/content/models.py index d4135f12..872d8044 100644 --- a/apps/content/models.py +++ b/apps/content/models.py @@ -99,7 +99,7 @@ class Video(Content): url = models.URLField('Ссылка') def video_index(self): - if 'youtu.be' in self.url or 'youtube.com' in self.url and 'watch' in self.url: + if self.is_youtube_video: url = urlparse(self.url) query = url.query.split('&') for q in query: @@ -107,6 +107,14 @@ class Video(Content): return q.split('=')[-1] return self.url.split('/')[-1] + @property + def is_youtube_video(self): + return 'youtu.be' in self.url or 'youtube.com' in self.url and 'watch' in self.url + + @property + def is_vimeo_video(self): + return 'vimeo.com' in self.url + class Gallery(Content): pass diff --git a/apps/content/templates/content/blocks/video.html b/apps/content/templates/content/blocks/video.html index 8c707388..da44d655 100644 --- a/apps/content/templates/content/blocks/video.html +++ b/apps/content/templates/content/blocks/video.html @@ -4,12 +4,14 @@ {{ content.title }}
- {% if 'youtube.com' in content.url or 'youtu.be' in content.url %} - - {% elif 'vimeo.com' in content.url %} - + {% elif content.is_vimeo_video %} + {% endif %}
diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index fb9c351f..79bab74d 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -394,4 +394,5 @@ {% block foot %} {% include "templates/blocks/popup_course_buy.html" %} + {% endblock foot %} diff --git a/apps/course/templates/course/lesson.html b/apps/course/templates/course/lesson.html index 0c33cf6b..cfbc0976 100644 --- a/apps/course/templates/course/lesson.html +++ b/apps/course/templates/course/lesson.html @@ -123,3 +123,7 @@ {% endblock content %} + +{% block foot %} + +{% endblock foot %} diff --git a/apps/course/templatetags/plural.py b/apps/course/templatetags/plural.py index 8637d05c..e6327347 100644 --- a/apps/course/templatetags/plural.py +++ b/apps/course/templatetags/plural.py @@ -4,14 +4,23 @@ from django.template.defaultfilters import stringfilter register = template.Library() -@register.filter(is_safe=False) -@stringfilter -def rupluralize(value, arg): +def _ruplural(value, arg): args = arg.split(',') try: _value = str(0 if not value or int(value) <= 0 else value)[-1:] - value = value + ' ' + args[0 if _value == '1' else (1 if _value in '234' else 2)] - return value + return args[0 if _value == '1' else (1 if _value in '234' else 2)] except: raise template.TemplateSyntaxError return '' + + +@register.filter(is_safe=False) +@stringfilter +def rupluralize(value, arg): + return value + ' ' + _ruplural(value, arg) + + +@register.filter(is_safe=False) +@stringfilter +def ruplural(value, arg): + return _ruplural(value, arg) diff --git a/apps/notification/tasks.py b/apps/notification/tasks.py index 714392ba..462e22bc 100644 --- a/apps/notification/tasks.py +++ b/apps/notification/tasks.py @@ -3,12 +3,14 @@ from datetime import datetime, date, timedelta from PIL import Image from PIL import ImageFont from PIL import ImageDraw +from unidecode import unidecode from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.contrib.staticfiles.storage import staticfiles_storage from django.utils.timezone import now from django.conf import settings +from django.utils.text import slugify from apps.notification.models import UserNotification from apps.notification.utils import send_email @@ -16,6 +18,7 @@ from apps.payment.models import SchoolPayment, CoursePayment, Payment, UserGiftC from project.celery import app from project.utils.db import format_sql, execute_sql from project.sengrid import get_sendgrid_client +from apps.user.models import Child User = get_user_model() @@ -36,7 +39,11 @@ def draw_cert(path, email, first_name, last_name, x=None, y=900, color=(29, 115, os.mkdir(fn) except: pass - fn = os.path.join(fn, '%scertificate-for-%s.jpg' % (fn_prefix, email)) + if first_name: + name = '-'.join(filter(None, [first_name, last_name])) + fn = os.path.join(fn, '%scertificate-for-%s-%s.jpg' % (fn_prefix, email, slugify(unidecode(name)))) + else: + fn = os.path.join(fn, '%scertificate-for-%s.jpg' % (fn_prefix, email)) img.save(fn) img.close() return fn @@ -76,25 +83,30 @@ def send_certificates(email=None, date_end=None, dry_run=False): if dry_run: continue + file_names = [] un.certificate_number = un.certificate_number + 1 \ if un.certificate_number and staticfiles_storage.exists(path_pattern % (un.certificate_number + 1)) \ else 1 - if un.user.child_first_name: + if un.user.child_filled: fn = staticfiles_storage.path(signed_path_pattern % un.certificate_number) - fn = draw_cert(fn, un.user.email, un.user.child_first_name, un.user.child_last_name) + for child in un.user.childs.all(): + file_names.append(draw_cert(fn, un.user.email, child.first_name, child.last_name)) else: - fn = staticfiles_storage.path(path_pattern % un.certificate_number) - file = open(fn, 'rb') + file_names.append(staticfiles_storage.path(path_pattern % un.certificate_number)) + files = [open(fn, 'rb') for fn in file_names] try: send_email('Грамота от Lil School', un.user.email, 'notification/email/certificate.html', - attachments=[(file.name, file.read(), 'image/jpeg')], user_notification=un) + attachments=[(f.name, f.read(), 'image/jpeg') for f in files], user_notification=un, + many_childs=un.user.childs.all().count() > 1) except: print('Not OK') continue finally: - file.close() - if un.user.child_first_name: - os.remove(fn) + for f in files: + f.close() + if un.user.child_filled: + for fn in file_names: + os.remove(fn) un.certificate_last_email = now() un.save() @@ -148,19 +160,20 @@ def send_gift_certificate(user_gift_certificate): @app.task def send_child_birthday_email_and_bonuses(): - for u in User.objects.exclude(child_first_name='', child_last_name='',).filter(child_birthday=now().date()): - if not UserBonus.objects.filter(user=u, is_service=True, action_name=UserBonus.ACTION_CHILD_BIRTHDAY).count(): - print('user', u.email) - UserBonus.objects.create(user=u, amount=UserBonus.AMOUNT_CHILD_BIRTHDAY, is_service=True, + for user_id, email in set(Child.objects.filter(birthday=now().date()).select_related('user') + .values_list('user_id', 'user__email')): + print('user', email) + if not UserBonus.objects.filter(user=user_id, is_service=True, action_name=UserBonus.ACTION_CHILD_BIRTHDAY).count(): + UserBonus.objects.create(user=user_id, amount=UserBonus.AMOUNT_CHILD_BIRTHDAY, is_service=True, action_name=UserBonus.ACTION_CHILD_BIRTHDAY) - try: - fn = staticfiles_storage.path('img/birthday_postcard.jpg') - file = open(fn, 'rb') - send_email('С Днем Рождения!', u.email, 'notification/email/birthday_postcard.html', - attachments=[(file.name, file.read(), 'image/jpeg')],) - print('email has been sent') - finally: - file.close() + try: + fn = staticfiles_storage.path('img/birthday_postcard.jpg') + file = open(fn, 'rb') + send_email('С Днем Рождения!', email, 'notification/email/birthday_postcard.html', + attachments=[(file.name, file.read(), 'image/jpeg')],) + print('email has been sent') + finally: + file.close() @app.task def send_camp_certificates(email=None, dry_run=False, certificate_number=None): @@ -178,7 +191,6 @@ def send_camp_certificates(email=None, dry_run=False, certificate_number=None): font_size=120, y=1000, color=color) file = open(fn, 'rb') try: - pass send_email('Грамота от Lil School', email, 'notification/email/camp_certificate.html', attachments=[(file.name, file.read(), 'image/jpeg')], certificate_number=certificate_number) except: @@ -205,23 +217,27 @@ def send_camp_certificates(email=None, dry_run=False, certificate_number=None): if dry_run: continue - if un.user.child_first_name: + file_names = [] + if un.user.child_filled: fn = staticfiles_storage.path(signed_path_pattern % certificate_number) - fn = draw_cert(fn, un.user.email, un.user.child_first_name, un.user.child_last_name, - font_size=120, y=1000, color=color) + for child in un.user.childs.all(): + file_names.append(draw_cert(fn, un.user.email, child.first_name, child.last_name, + font_size=120, y=1000, color=color)) else: - fn = staticfiles_storage.path(path_pattern % certificate_number) - file = open(fn, 'rb') + file_names.append(staticfiles_storage.path(path_pattern % certificate_number)) + files = [open(fn, 'rb') for fn in file_names] try: send_email('Грамота от Lil School', un.user.email, 'notification/email/camp_certificate.html', - attachments=[(file.name, file.read(), 'image/jpeg')], user_notification=un, - certificate_number=certificate_number) + attachments=[(f.name, f.read(), 'image/jpeg') for f in files], user_notification=un, + certificate_number=certificate_number, many_childs=un.user.childs.all().count() > 1) except: print('Not OK') continue finally: - file.close() - if un.user.child_first_name: - os.remove(fn) + for f in files: + f.close() + if un.user.child_filled: + for fn in file_names: + os.remove(fn) un.camp_certificate_last_email = date_end un.save() diff --git a/apps/notification/templates/notification/email/buy_email.html b/apps/notification/templates/notification/email/buy_email.html new file mode 100644 index 00000000..82224777 --- /dev/null +++ b/apps/notification/templates/notification/email/buy_email.html @@ -0,0 +1,22 @@ +{% extends "notification/email/_base.html" %} +{% load settings %} + +{% block content %} + {% if product_type == 'course' %} +

Курс ждет вас по ссылке + https://{% setting 'MAIN_HOST' %}{{ url }}

+ {% endif %} + {% if product_type == 'school' %} +

Школа ждет вас по ссылке + https://{% setting 'MAIN_HOST' %}{% url 'school:school' %}

+ {% endif %} + {% if product_type == 'drawing_camp' %} +

Рисовальный лагерь ждет вас по ссылке + https://{% setting 'MAIN_HOST' %}{% url 'school:drawing-camp' %}

+ {% endif %} +

Так же вы можете найти ссылку в личном кабинете в разделе «Мои покупки».

+ +

Занимайтесь с удовольствием!

+ +

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

+{% endblock content %} diff --git a/apps/notification/templates/notification/email/camp_certificate.html b/apps/notification/templates/notification/email/camp_certificate.html index f4152c79..8a4c830a 100644 --- a/apps/notification/templates/notification/email/camp_certificate.html +++ b/apps/notification/templates/notification/email/camp_certificate.html @@ -6,20 +6,21 @@ {% if certificate_number == 1 %}

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

{% endif %} {% if certificate_number == 2 %}

-Вы помните, что каждый месяц вам приходит грамота за прекрасную учебу в рисовальном лагере?
+Вы помните, что каждый месяц вам {% if many_childs %}приходят грамоты{% else %}приходит грамота{% endif %} за прекрасную учебу в рисовальном лагере?
Скачивайте. Распечатывайте. И соберите свою коллекцию!

{% endif %} {% if certificate_number == 3 %}

-Вот и закончился лагерь, пора переходить к основным урокам.
-Спасибо, что были с нами, успехов вам в дальнейшем обучении! +Поздравляем с успешным окончаниемм рисовального лагеря!
+В письме вы найдёте {% if many_childs %}грамоты, они{% else %}грамоту, она{% endif %} для вашей семьи.
+До встречи в онлайн школе!

{% endif %} diff --git a/apps/notification/templates/notification/email/certificate.html b/apps/notification/templates/notification/email/certificate.html index 0185c4c3..277817e0 100644 --- a/apps/notification/templates/notification/email/certificate.html +++ b/apps/notification/templates/notification/email/certificate.html @@ -6,20 +6,20 @@ {% if not user_notification or user_notification.certificate_number == 1 %}

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

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

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

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

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

@@ -27,22 +27,27 @@ {% if user_notification and user_notification.certificate_number == 4 %}

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

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

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

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

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

@@ -50,7 +55,7 @@ {% if user_notification and user_notification.certificate_number == 7 %}

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

{% endif %} @@ -58,7 +63,7 @@

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

{% endif %} diff --git a/apps/payment/models.py b/apps/payment/models.py index 99bc64cf..4f498d9b 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -356,7 +356,7 @@ class SchoolPayment(Payment): class DrawingCampPayment(Payment): - MONTH_PRICE = Decimal(1490) + MONTH_PRICE = Decimal(1990) WEEKDAYS = {1, 3, 5} date_start = models.DateField('Дата начала подписки', null=True, blank=True) diff --git a/apps/payment/views.py b/apps/payment/views.py index acb8b08a..54fbac30 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -1,6 +1,5 @@ from decimal import Decimal import short_url -import arrow import json import logging @@ -8,24 +7,22 @@ from datetime import timedelta from urllib.parse import urlsplit import datetime -import calendar from django.contrib import messages from django.contrib.auth.decorators import login_required from django.http import HttpResponse, Http404 from django.shortcuts import redirect, get_object_or_404 -from django.views.generic import View, TemplateView, DetailView +from django.views.generic import View, TemplateView from django.views.decorators.csrf import csrf_exempt from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.utils.timezone import now -from django.conf import settings from paymentwall import Pingback, Product, Widget 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 apps.notification.utils import send_email from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus, GiftCertificate, \ GiftCertificatePayment, UserGiftCertificate, DrawingCampPayment @@ -325,7 +322,6 @@ class PaymentwallCallbackView(View): product_type_name, ) - if product_type_name == 'course': properties = { 'payment_id': payment.id, @@ -386,6 +382,13 @@ class PaymentwallCallbackView(View): payment.roistat_visit, ) + if product_type_name == 'course': + send_email.delay('Спасибо за покупку!', payment.user.email, 'notification/email/buy_email.html', + product_type=product_type_name, url=payment.course.url) + elif product_type_name != 'gift_certificate': + send_email.delay('Спасибо за покупку!', payment.user.email, 'notification/email/buy_email.html', + product_type=product_type_name) + author_balance = getattr(payment, 'author_balance', None) if author_balance and author_balance.type == AuthorBalance.IN: if pingback.is_deliverable(): diff --git a/apps/school/templates/blocks/schedule_item.html b/apps/school/templates/blocks/schedule_item.html index 593d0426..66e80182 100644 --- a/apps/school/templates/blocks/schedule_item.html +++ b/apps/school/templates/blocks/schedule_item.html @@ -7,7 +7,7 @@
{{ school_schedule }} {% if not is_purchased and request.user_agent.is_mobile and school_schedule.trial_lesson %} - Пробный урок + Пробный урок {% endif %}
{% if is_purchased and live_lesson %} @@ -24,8 +24,8 @@ {% include './day_pay_btn.html' %} {% endif %} {% endif %} - {% if not is_purchased and not request.user_agent.is_mobile and school_schedule.trial_lesson and not is_drawing_camp %} - Пробный урок + {% if not is_purchased and not request.user_agent.is_mobile and school_schedule.trial_lesson %} + Пробный урок {% endif %} @@ -56,13 +56,11 @@ {% endif %}
- {% comment %} {% if live_lesson and live_lesson.short_description %} - {{ live_lesson.short_description }} + {{ live_lesson.short_description|safe }} {% else %} - {% endcomment %} - {{ school_schedule.description }} - {% comment %}{% endif %}{% endcomment %} + {{ school_schedule.description|safe }} + {% endif %}
Материалы
diff --git a/apps/school/templates/school/livelesson_detail.html b/apps/school/templates/school/livelesson_detail.html index 9ee47ad8..a971f408 100644 --- a/apps/school/templates/school/livelesson_detail.html +++ b/apps/school/templates/school/livelesson_detail.html @@ -15,12 +15,6 @@ {% if livelesson.cover and livelesson.cover.image %}{{ livelesson.cover.image.height }}{% else %}{{ block.super }}{% endif %} {% endblock ogimage-height %} -{% block head %} - {% if livelesson.stream_index %} - - {% endif %} -{% endblock head %} - {% block content %}
@@ -29,12 +23,11 @@
{{ livelesson.short_description | safe | linebreaks }}
{% if livelesson.stream_index %} - Если видео не загрузилось, - уменьшите качество видео или обновите страницу - {% else %} {% if livelesson.cover %} @@ -60,29 +53,5 @@ {% endblock content %} {% block foot %} - {% if livelesson.stream_index %} - - {% endif %} + {% endblock foot %} diff --git a/apps/school/views.py b/apps/school/views.py index 37a63dc2..0bbe959b 100644 --- a/apps/school/views.py +++ b/apps/school/views.py @@ -38,12 +38,12 @@ class LiveLessonsView(ListView): def get_queryset(self): date_start = (now() - timedelta(days=7)).date() - date_start, date_end = SchoolPayment.get_date_range(date_start, days=17, is_camp=False) + date_start, date_end = SchoolPayment.get_date_range(date_start, days=16, is_camp=False) date_range = Q( date__range=[date_start, date_end] ) exist_dates = LiveLesson.objects.filter(date_range, is_camp=False).values_list('date', flat=True) - for i in range((date_end - date_start).days): + for i in range((date_end - date_start).days + 1): d = date_start + timedelta(days=i) if d not in exist_dates: try: @@ -74,7 +74,7 @@ class DrawingCampLessonsView(ListView): date__range=[date_start, date_end] ) exist_dates = LiveLesson.objects.filter(date_range, is_camp=True).values_list('date', flat=True) - for i in range((date_end - date_start).days): + for i in range((date_end - date_start).days + 1): d = date_start + timedelta(days=i) if d.isoweekday() in DrawingCampPayment.WEEKDAYS and d not in exist_dates: try: @@ -289,7 +289,7 @@ class DrawingCampView(TemplateView): next_schedule = school_schedule next_lesson_start = next_schedule.start_at_humanize if not next_schedule: - next_camp_lesson = LiveLesson.objects.filter(date__gt=date_now, is_camp=True).first() + next_camp_lesson = LiveLesson.objects.filter(date__gt=date_now, is_camp=True).order_by('date').first() if next_camp_lesson and next_camp_lesson.school_schedule: next_schedule = next_camp_lesson.school_schedule next_lesson_start = arrow.get(datetime.combine(next_camp_lesson.date, next_schedule.start_at), diff --git a/apps/user/forms.py b/apps/user/forms.py index 9a9e68d5..b0f6d51d 100644 --- a/apps/user/forms.py +++ b/apps/user/forms.py @@ -2,7 +2,8 @@ from django import forms from django.contrib.auth import get_user_model from phonenumber_field.formfields import PhoneNumberField -from .fields import CreditCardField +from apps.user.fields import CreditCardField +from apps.user.models import Child User = get_user_model() @@ -31,11 +32,6 @@ class UserEditForm(forms.ModelForm): site = forms.URLField(required=False) photo = forms.ImageField(required=False) - child_gender = forms.CharField(required=False) - child_birthday = forms.DateField(input_formats=['%d/%m/%Y'], required=False) - child_first_name = forms.CharField(required=False) - child_last_name = forms.CharField(required=False) - class Meta: model = User fields = ( @@ -61,10 +57,6 @@ class UserEditForm(forms.ModelForm): 'vkontakte', 'site', 'photo', - 'child_gender', - 'child_birthday', - 'child_first_name', - 'child_last_name', ) @@ -79,3 +71,11 @@ class AuthorRequesForm(forms.Form): email = forms.CharField() about = forms.CharField() facebook = forms.URLField(required=False) + + +class ChildForm(forms.ModelForm): + birthday = forms.DateField(input_formats=['%d/%m/%Y'], required=False) + + class Meta: + model = Child + fields = '__all__' diff --git a/apps/user/migrations/0032_child.py b/apps/user/migrations/0032_child.py new file mode 100644 index 00000000..95c2921c --- /dev/null +++ b/apps/user/migrations/0032_child.py @@ -0,0 +1,26 @@ +# Generated by Django 2.0.7 on 2019-06-06 21:05 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('user', '0031_user_review_url'), + ] + + operations = [ + migrations.CreateModel( + name='Child', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('first_name', models.CharField(max_length=30, verbose_name='Имя ребенка')), + ('last_name', models.CharField(blank=True, default='', max_length=150, verbose_name='Фамилия ребенка')), + ('gender', models.CharField(choices=[('n', 'не указан'), ('m', 'Мужчина'), ('f', 'Женщина')], default='n', max_length=1, verbose_name='Пол ребенка')), + ('birthday', models.DateField(blank=True, null=True, verbose_name='День рождения ребенка')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='childs', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/apps/user/migrations/0033_add_childs.py b/apps/user/migrations/0033_add_childs.py new file mode 100644 index 00000000..a3cc360e --- /dev/null +++ b/apps/user/migrations/0033_add_childs.py @@ -0,0 +1,21 @@ +# Generated by Django 2.0.7 on 2019-06-07 20:48 + +from django.db import migrations + + +def add_childs(apps, schema_editor): + User = apps.get_model('user', 'User') + Child = apps.get_model('user', 'Child') + for user in User.objects.exclude(child_first_name=''): + Child.objects.get_or_create(user=user, first_name=user.child_first_name, last_name=user.child_last_name, + gender=user.child_gender, birthday=user.child_birthday) + +class Migration(migrations.Migration): + + dependencies = [ + ('user', '0032_child'), + ] + + operations = [ + migrations.RunPython(add_childs), + ] diff --git a/apps/user/migrations/0034_auto_20190612_1852.py b/apps/user/migrations/0034_auto_20190612_1852.py new file mode 100644 index 00000000..5bcb4a9c --- /dev/null +++ b/apps/user/migrations/0034_auto_20190612_1852.py @@ -0,0 +1,29 @@ +# Generated by Django 2.0.7 on 2019-06-12 18:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('user', '0033_add_childs'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='child_birthday', + ), + migrations.RemoveField( + model_name='user', + name='child_first_name', + ), + migrations.RemoveField( + model_name='user', + name='child_gender', + ), + migrations.RemoveField( + model_name='user', + name='child_last_name', + ), + ] diff --git a/apps/user/models.py b/apps/user/models.py index aca57b23..11cb5c2d 100644 --- a/apps/user/models.py +++ b/apps/user/models.py @@ -88,12 +88,6 @@ class User(AbstractUser): 'content.Gallery', on_delete=models.CASCADE, verbose_name='Галерея', null=True, blank=True, ) - - child_first_name = models.CharField('Имя ребенка', max_length=30, blank=True) - child_last_name = models.CharField('Фамилия ребенка', max_length=150, blank=True) - child_gender = models.CharField( - 'Пол ребенка', max_length=1, default='n', choices=GENDER_CHOICES) - child_birthday = models.DateField('День рождения ребенка', null=True, blank=True) review_url = models.URLField('Ссылка на видеоотзыв', blank=True, default='') objects = UserManager() @@ -104,6 +98,14 @@ class User(AbstractUser): class Meta(AbstractUser.Meta): ordering = ('-date_joined',) + @cached_property + def has_child(self): + return self.childs.all().count() > 0 + + def child_filled(self): + child = self.childs.all().first() + return child and child.first_name and child.last_name and child.birthday + @property def url(self): return reverse('user', args=[self.slug or self.id]) @@ -338,3 +340,23 @@ class EmailLog(models.Model): email = models.EmailField(_('email address')) created_at = models.DateTimeField(auto_now_add=True) source = models.PositiveSmallIntegerField(choices=SOURCE_CHOICES) + + +class Child(models.Model): + NOT_DEFINED = 'n' + MALE = 'm' + FEMALE = 'f' + GENDER_CHOICES = ( + (NOT_DEFINED, 'не указан'), + (MALE, 'Мужчина'), + (FEMALE, 'Женщина'), + ) + user = models.ForeignKey(User, related_name='childs', on_delete=models.CASCADE) + first_name = models.CharField('Имя ребенка', max_length=30) + last_name = models.CharField('Фамилия ребенка', max_length=150, blank=True, default='') + gender = models.CharField( + 'Пол ребенка', max_length=1, default='n', choices=GENDER_CHOICES) + birthday = models.DateField('День рождения ребенка', null=True, blank=True) + + class Meta: + ordering = ('id',) diff --git a/apps/user/templates/user/bonus-history.html b/apps/user/templates/user/bonus-history.html index 0170f356..2edff50a 100644 --- a/apps/user/templates/user/bonus-history.html +++ b/apps/user/templates/user/bonus-history.html @@ -83,7 +83,7 @@
+{{ amount_child_birthday }} LIL
ЗАПОЛНИТЬ КАРТОЧКУ РЕБЕНКА
-
{% if request.user.child_first_name and request.user.child_last_name and request.user.child_birthday %} +
{% if request.user.child_filled %} {% endif %}
diff --git a/apps/user/templates/user/profile-settings.html b/apps/user/templates/user/profile-settings.html index 3d382497..5e3af1c7 100644 --- a/apps/user/templates/user/profile-settings.html +++ b/apps/user/templates/user/profile-settings.html @@ -67,101 +67,41 @@ {% endfor %}
-
-
Почта
-
- -
- {% for error in form.email.errors %} -
{{ error }}
- {% endfor %} -
-
-
Ссылка
-
-
{% setting 'MAIN_HOST' %}/user/
- -
- {% for error in form.slug.errors %} -
{{ error }}
- {% endfor %} -
- - -
-
Карточка ребёнка
-
- Вся информация конфиденциальна и не передается третьим лицам. Необходима только для персонализации наград, - поздравлений с Днем Рождения и других персонализированных акций. -
-
-
-
ИМЯ РЕБЕНКА
-
- -
- {% for error in form.child_first_name.errors %} -
{{ error }}
- {% endfor %} -
-
-
ФАМИЛИЯ РЕБЕНКА
-
- -
- {% for error in form.child_last_name.errors %} -
{{ error }}
- {% endfor %} -
-
-
-
ДАТА РОЖДЕНИЯ
+
+
Почта
- +
- {% for error in form.child_birthday.errors %} + {% for error in form.email.errors %}
{{ error }}
{% endfor %}
-
-
ПОЛ
+
+
НОМЕР ТЕЛЕФОНА
-
-
- {% if user.child_gender == 'f' %}Ж{% elif user.child_gender == 'm' %}M{% else %}М / Ж{% endif %} -
-
-
-
М
-
-
-
Ж
-
-
- -
+
- {% for error in form.child_gender.errors %} + {% for error in form.phone.errors %}
{{ error }}
{% endfor %}
-
-
НОМЕР ТЕЛЕФОНА РОДИТЕЛЯ
-
- +
+
Ссылка
+
+
{% setting 'MAIN_HOST' %}/user/
+
- {% for error in form.phone.errors %} + {% for error in form.slug.errors %}
{{ error }}
{% endfor %}
- + + +
@@ -393,5 +333,8 @@ {% endblock foot %} {% block pre_app_js %} + {% endblock pre_app_js %} diff --git a/apps/user/templates/user/profile.html b/apps/user/templates/user/profile.html index 6df83e4a..a1885b8a 100644 --- a/apps/user/templates/user/profile.html +++ b/apps/user/templates/user/profile.html @@ -1,6 +1,6 @@ {% extends "templates/lilcity/index.html" %} {% load static %} {% load thumbnail %} {% block content %} -{% if not user.child_first_name or not user.child_birthday %} +{% if not user.has_child %}
@@ -142,7 +142,7 @@ {% if paid.exists %} {% include "course/course_items.html" with course_items=paid %} {% endif %} - {% if not is_school_purchased and not paid.exists and not user_gift_certificates.exists %} + {% if not is_school_purchased and not is_camp_purchased and not paid.exists and not user_gift_certificates.exists %}
Вы пока ничего не приобрели...
diff --git a/apps/user/views.py b/apps/user/views.py index 8a80e4cd..f43a13f5 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -26,9 +26,9 @@ from apps.notification.utils import send_email from apps.school.models import SchoolSchedule from apps.payment.models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserGiftCertificate, UserBonus, \ DrawingCampPayment -from apps.user.models import AuthorRequest, EmailSubscription, SubscriptionCategory +from apps.user.models import AuthorRequest, EmailSubscription, SubscriptionCategory, Child -from .forms import AuthorRequesForm, UserEditForm, WithdrawalForm +from .forms import AuthorRequesForm, UserEditForm, WithdrawalForm, ChildForm User = get_user_model() @@ -73,7 +73,7 @@ class ProfileView(TemplateView): ).distinct() school_payment = SchoolPayment.objects.filter( user=self.object, - date_end__gte=now(), + date_end__gte=now() - timedelta(7), status__in=[ Pingback.PINGBACK_TYPE_REGULAR, Pingback.PINGBACK_TYPE_GOODWILL, @@ -93,7 +93,7 @@ class ProfileView(TemplateView): camp_payment = DrawingCampPayment.objects.filter( user=self.object, - date_end__gte=now(), + date_end__gte=now() - timedelta(7), status__in=DrawingCampPayment.PW_PAID_STATUSES, ) context['is_camp_purchased'] = camp_payment.exists() @@ -217,6 +217,26 @@ class ProfileEditView(UpdateView): context['is_teacher'] = self.object.role == User.TEACHER_ROLE return context + def get(self, request, *args, **kwargs): + context = self.get_context_data(**kwargs) + if self.object.childs.all().count(): + context['childs'] = [{ + 'id': c.id, + 'first_name': c.first_name, + 'last_name': c.last_name, + 'gender': c.gender or 'n', + 'birthday': c.birthday.strftime('%Y-%m-%d') if c.birthday else '', + } for c in self.object.childs.all()] + else: + context['childs'] = [{ + 'id': '', + 'first_name': '', + 'last_name': '', + 'gender': 'n', + 'birthday': '', + }] + return self.render_to_response(context) + def post(self, request, *args, **kwargs): # it's magic *-*-*-*-* if 'photo' in request.FILES: @@ -256,15 +276,47 @@ class ProfileEditView(UpdateView): login(request, request.user) else: messages.error(request, 'Неверный пароль.') - messages.info(request, 'Данные сохранены.') - response = super().post(request, *args, **kwargs) - user = User.objects.get(pk=request.user.id) - # начисляемм бонусы, если заполнил профиль - if user.phone and user.first_name and user.last_name and not user.bonuses.filter( - is_service=True, action_name=UserBonus.ACTION_FILL_PROFILE).count(): - UserBonus.objects.create(user=user, amount=UserBonus.AMOUNT_FILL_PROFILE, is_service=True, - action_name=UserBonus.ACTION_FILL_PROFILE) - return response + + child_ids = request.POST.getlist('child_id') + child_first_names = request.POST.getlist('child_first_name') + child_last_names = request.POST.getlist('child_last_name') + child_genders = request.POST.getlist('child_gender') + child_birthdays = request.POST.getlist('child_birthday') + ziped = zip(child_ids, child_first_names, child_last_names, child_genders, child_birthdays) + childs = [dict(zip(['id', 'first_name', 'last_name', 'gender', 'birthday'], z)) for z in ziped] + childs_saved = [] + child_errors = False + for child in childs: + child_instance = None + if child.get('id'): + child_instance = Child.objects.get(pk=child.get('id')) + child['user'] = request.user.id + child['gender'] = child['gender'] or 'n' + child_form = ChildForm(data=child, instance=child_instance) + if child_form.is_valid(): + childs_saved.append(child_form.save().id) + elif not(len(childs) == 1 and not child.get('id') and not child.get('first_name')): + child_errors = True + child['errors'] = {f: e[0] for f, e in child_form.errors.items()} + if not child_errors: + request.user.childs.exclude(id__in=childs_saved).delete() + + form = self.get_form() + if form.is_valid() and not child_errors: + messages.info(request, 'Данные сохранены.') + response = self.form_valid(form) + # начисляем бонусы, если заполнил профиль + if self.object.phone and self.object.first_name and self.object.last_name and not self.object.bonuses.filter( + is_service=True, action_name=UserBonus.ACTION_FILL_PROFILE).count(): + UserBonus.objects.create(user=self.object, amount=UserBonus.AMOUNT_FILL_PROFILE, is_service=True, + action_name=UserBonus.ACTION_FILL_PROFILE) + return response + + else: + context = self.get_context_data(**kwargs) + context['childs'] = childs + return self.render_to_response(context) + def get_success_url(self): return reverse('user-edit-profile') diff --git a/project/templates/blocks/about.html b/project/templates/blocks/about.html index d86d6cf7..71a8b0b6 100644 --- a/project/templates/blocks/about.html +++ b/project/templates/blocks/about.html @@ -1,4 +1,5 @@ {% load static %} +{% load ruplural from plural %}
@@ -20,26 +21,26 @@
-
> 12000
-
учеников
+
> {{ config.NUMBER_OF_STUDENTS }}
+
{{ config.NUMBER_OF_STUDENTS|ruplural:'ученик,ученика,учеников' }}
прошли обучение в Lil School
{{ works_count }}
-
работ
+
{{ works_count|ruplural:'работа,работы,работ' }}
создано учениками Lil School.
Большую часть из них легко найти в инстаграм
-
32
-
страны
+
{{ config.NUMBER_OF_COUNTRIES }}
+
{{ config.NUMBER_OF_COUNTRIES|ruplural:'страна,страны,стран' }}
где живут талантливые ученики Lil School
-
107
-
городов
+
{{ config.NUMBER_OF_CITIES }}
+
{{ config.NUMBER_OF_CITIES|ruplural:'город,города,городов' }}
со всего мира со счастливыми учениками Lil School
diff --git a/project/templates/blocks/video.html b/project/templates/blocks/video.html index c0eebf5a..2d1fcada 100644 --- a/project/templates/blocks/video.html +++ b/project/templates/blocks/video.html @@ -1,10 +1,10 @@
Пробный урок
- Смотреть бесплатно -
Много развивающих видео на нашем YouTube канале
+ data-video-url="{{ config.MAIN_PAGE_VIDEO_URL|safe }}" data-trial-lesson="1">Смотреть бесплатно +
Много развивающих видео на нашем YouTube канале
diff --git a/project/tests/test_features.py b/project/tests/test_features.py index dba636a6..88f52095 100644 --- a/project/tests/test_features.py +++ b/project/tests/test_features.py @@ -59,8 +59,7 @@ class AutoAddingUserBonusTestCase(TestCase): user = UserFactory.create(role=User.USER_ROLE, first_name='', last_name='', phone='') self.assertEqual(user.bonus, 0, 'При создании есть бонусы') self.client.force_login(user) - data = {k: user.__dict__[k] for k in ['email', 'slug', 'child_first_name', - 'child_last_name', 'child_birthday', 'child_gender', 'city', + data = {k: user.__dict__[k] for k in ['email', 'slug', 'city', 'country', 'birthday', 'gender', 'about', 'instagram', 'facebook', 'twitter', 'pinterest', 'youtube', 'vkontakte', 'site']} diff --git a/web/src/components/Childs.vue b/web/src/components/Childs.vue new file mode 100644 index 00000000..56149313 --- /dev/null +++ b/web/src/components/Childs.vue @@ -0,0 +1,120 @@ + + + diff --git a/web/src/components/blocks/BlockImages.vue b/web/src/components/blocks/BlockImages.vue index f48836ac..cfd4c33f 100644 --- a/web/src/components/blocks/BlockImages.vue +++ b/web/src/components/blocks/BlockImages.vue @@ -72,11 +72,11 @@ }, onRemoveImage(index) { let images = this.images; - let id = images[index].image_id; + const id = images[index].image_id; api.removeImage(id, this.accessToken) .then(response => { - images.splice(index, 1); + images = images.filter(image => image.image_id != id); this.$emit('update:images', images); }); } diff --git a/web/src/img/heart.png b/web/src/img/heart.png new file mode 100644 index 00000000..2cfccaed Binary files /dev/null and b/web/src/img/heart.png differ diff --git a/web/src/img/video-ended-popup-like.jpg b/web/src/img/video-ended-popup-like.jpg new file mode 100644 index 00000000..d437a971 Binary files /dev/null and b/web/src/img/video-ended-popup-like.jpg differ diff --git a/web/src/img/video-ended-popup-logo.png b/web/src/img/video-ended-popup-logo.png new file mode 100644 index 00000000..a84b49e7 Binary files /dev/null and b/web/src/img/video-ended-popup-logo.png differ diff --git a/web/src/img/video-ended-popup-ref.jpg b/web/src/img/video-ended-popup-ref.jpg new file mode 100644 index 00000000..fbfe3225 Binary files /dev/null and b/web/src/img/video-ended-popup-ref.jpg differ diff --git a/web/src/js/app.js b/web/src/js/app.js index 7193a6e8..14e66e65 100644 --- a/web/src/js/app.js +++ b/web/src/js/app.js @@ -19,7 +19,6 @@ import "./modules/mixpanel"; import "../sass/app.sass"; -import $ from 'jquery'; import Vue from 'vue'; import Vuelidate from 'vuelidate'; import VueAutosize from '../components/directives/autosize' diff --git a/web/src/js/modules/api.js b/web/src/js/modules/api.js index cb88cf69..cbf27e8f 100644 --- a/web/src/js/modules/api.js +++ b/web/src/js/modules/api.js @@ -75,6 +75,13 @@ export const api = { } }); }, + removeGalleryImage: (galleryImageId, accessToken) => { + return api.delete(`/api/v1/gallery-images/${galleryImageId}/`, { + headers: { + 'Authorization': `Token ${accessToken}`, + } + }); + }, loadCourse: (courseId, accessToken) => { return api.get(`/api/v1/courses/${courseId}/`, { headers: { diff --git a/web/src/js/modules/content.js b/web/src/js/modules/content.js new file mode 100644 index 00000000..6be64da8 --- /dev/null +++ b/web/src/js/modules/content.js @@ -0,0 +1,62 @@ +import $ from 'jquery'; +import {loadScript} from '../utils'; + +function getVideoPopup($videoEl){ + const $container = $videoEl.parent(); + let $popup = $container.children('.video-ended-popup'); + if(! $popup[0]){ + $popup = $('
' + + '
' + + '
'); + $container.append($popup); + } + return $popup; +} + +$(document).ready(function () { + $('.js-video').each(function (){ + const $iframe = $(this); + if($iframe.data('isYoutube')){ + + } + if($iframe.data('isVimeo')){ + loadScript('https://player.vimeo.com/api/player.js').then(() => { + const player = new Vimeo.Player(this); + player.on('pause', function(data) { + if(data.percent == 1){ + const $popup = getVideoPopup($iframe); + $popup.show().animate({opacity: 1}, 200); + if (document.fullscreenElement){ + document.exitFullscreen(); + } + const courseId = $iframe.data('courseId'); + if(courseId && window.LIL_STORE.user.id && !window.LIL_STORE.data.courseLiked){ + $popup.addClass('video-ended-popup_loading'); + $.get('/api/v1/likes/course-liked/', { + course_id: courseId, + user_id: window.LIL_STORE.user.id + }).then(response => { + if(! response.is_liked){ + $popup.addClass('video-ended-popup_like'); + $popup.find('.video-ended-popup__like-btn').click(() => { + $.post(`/course/${courseId}/like`).then(response => { + $popup.removeClass('video-ended-popup_like'); + window.LIL_STORE.data.courseLiked = response.success; + }); + }); + } + $popup.removeClass('video-ended-popup_loading'); + }); + } + } + }); + player.on('play', function() { + const $p = getVideoPopup($iframe); + if($p.is(':visible')){ + $p.animate({opacity: 0}, 800).then(() => {$p.hide().attr('class', 'video-ended-popup');}); + } + }); + }); + } + }); +}); diff --git a/web/src/js/modules/courses.js b/web/src/js/modules/courses.js index 8b94d36d..f6cb4152 100644 --- a/web/src/js/modules/courses.js +++ b/web/src/js/modules/courses.js @@ -91,9 +91,11 @@ $(document).ready(function () { if (data.is_liked) { likedCourseElement.addClass('active'); likedCourseElement.attr('data-liked', '1'); + window.LIL_STORE.data.courseLiked = true; } else { likedCourseElement.removeClass('active'); likedCourseElement.attr('data-liked', '0'); + window.LIL_STORE.data.courseLiked = false; } } }) diff --git a/web/src/js/modules/popup.js b/web/src/js/modules/popup.js index 1894241e..c774c6ef 100644 --- a/web/src/js/modules/popup.js +++ b/web/src/js/modules/popup.js @@ -81,7 +81,7 @@ $(document).ready(function () { channel = 'youtube'; } if(url.indexOf('youtube.com') > -1 && url.indexOf('watch') > -1){ - const m = /[?&]v=([a-zA-Z]+)(&.)?/.exec(url); + const m = /[?&]v=([^&]+)(&.+)?$/.exec(url); channel = 'youtube'; videoId = m && m[1]; } diff --git a/web/src/js/pages/course.js b/web/src/js/pages/course.js new file mode 100644 index 00000000..525f893c --- /dev/null +++ b/web/src/js/pages/course.js @@ -0,0 +1 @@ +import "../modules/content"; diff --git a/web/src/js/pages/live-lesson.js b/web/src/js/pages/live-lesson.js new file mode 100644 index 00000000..525f893c --- /dev/null +++ b/web/src/js/pages/live-lesson.js @@ -0,0 +1 @@ +import "../modules/content"; diff --git a/web/src/js/pages/profile.js b/web/src/js/pages/profile.js index 3156c84b..326b2fa5 100644 --- a/web/src/js/pages/profile.js +++ b/web/src/js/pages/profile.js @@ -2,6 +2,10 @@ import $ from 'jquery'; import slugify from 'slugify'; import ClipboardJS from 'clipboard'; import {showNotification} from '../modules/notification'; +import Childs from '../../components/Childs.vue'; + + +window.LIL_STORE.components['childs'] = Childs; $(document).ready(function () { if(window.LIL_STORE.urlIs('userBonuses')){ @@ -25,7 +29,7 @@ $(document).ready(function () { $(`[data-gender=${currentGender}]`).addClass('active'); $input.val(currentGender) }); - }) + }); $('#user-photo-upload').change(file => { diff --git a/web/src/js/utils.js b/web/src/js/utils.js index 9d822ef3..b2bee1ea 100644 --- a/web/src/js/utils.js +++ b/web/src/js/utils.js @@ -1,14 +1,21 @@ -export const rupluralize = (value, args, addValue=true) => { - let digit = Math.trunc(value) + ''; - digit = digit[digit.length - 1]; - return (addValue ? value + ' ' : '') + - args[(+value > 10 && +value < 20) - ? 2 - : (digit == '1' ? 0 : ('234'.search(digit) > -1 ? 1 : 2))]; -} +export const rupluralize = (value, args, addValue = true) => { + let digit = Math.trunc(value) + ''; + digit = digit[digit.length - 1]; + return (addValue ? value + ' ' : '') + + args[(+value > 10 && +value < 20) + ? 2 + : (digit == '1' ? 0 : ('234'.search(digit) > -1 ? 1 : 2))]; +}; export const loadScript = (url, onload) => { - const tag = document.createElement('script'); - tag.url = url; - document.getElementsByTagName('body'); -} + return new Promise(resolve => { + const script = document.createElement('script'); + script.async = true; + document.body.appendChild(script); + script.onload = function () { + onload && onload.apply(this, arguments); + resolve.apply(this, arguments); + }; + script.src = url; + }); +}; diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index 565b7e83..0e8435db 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -4130,11 +4130,6 @@ a.grey-link letter-spacing: 1px +m margin-right: 0 - &__item a.timing__btn - opacity: 0; - transition: 0.5s ease; - &__item:hover a.timing__btn - opacity: 1; &__pic display: block width: 100% @@ -4358,11 +4353,13 @@ a &.pic position: relative width: 100% + &__video + position: relative &__video iframe - height: 360px; - width: 640px; - max-width: 100%; - max-height: 60%; + height: 360px + width: 640px + max-width: 100% + max-height: 60% @media only screen and (max-width: 639px) .content-block__video iframe @@ -4433,6 +4430,8 @@ a margin: 0 auto display: flex padding: 0 40px + +t + width: 100% +m width: 100% padding: 0 10px @@ -4706,12 +4705,24 @@ a border-radius: 20px box-shadow: 0 10px 24px 0 rgba(0, 0, 0, 0.05) + +t + margin: 30px -80px 30px -60px + + +m + background: white + margin: 30px -80px 30px -60px + padding: 30px 80px 40px 60px + &__description margin-bottom: 40px margin-top: -20px color: #333333 font-size: 12px + &__tabs + justify-content: left + overflow-x: auto + overflow-y: hidden .bonuses-table margin-left: -120px @@ -4771,3 +4782,64 @@ a &__text margin: 40px 0 + +.video-ended-popup + display: none + position: absolute + top: 90px + height: 200px + background: url(../img/video-ended-popup-ref.jpg) no-repeat center white + left: 50% + width: 600px + margin-left: -300px + border-radius: 5px + padding: 30px + box-shadow: 0 2px 20px rgba(0,0,0,0.10) + opacity: 0 + + +m + width: 100% + left: 0 + top: 0 + height: 153px + background-position: center bottom + background-size: auto 130px + border-radius: 0 + background-color: #151a1e + margin-left: 0 + + & .loading-loader + display: none + + &_like + background-image: url(../img/video-ended-popup-like.jpg) + + &_black + background: url(../img/video-ended-popup-logo.png) no-repeat center #141a1d + + &_loading + background: #141a1d + + &_loading .loading-loader + display: block + + &__like-btn + background: url(../img/heart.png) no-repeat center + position: absolute + top: 90px + display: none + width: 91px + height: 52px + cursor: pointer + left: 50% + margin-left: -45px + + +m + background-size: contain + width: 54px + margin-left: -27px + bottom: 27px + top: auto + + &_like &__like-btn + display: block diff --git a/web/webpack.config.js b/web/webpack.config.js index 7e3590a3..498b4e3a 100644 --- a/web/webpack.config.js +++ b/web/webpack.config.js @@ -9,10 +9,12 @@ const SpriteLoaderPlugin = require('svg-sprite-loader/plugin'); module.exports = { entry: { app: "./src/js/app.js", + course: "./src/js/pages/course.js", courseEdit: "./src/js/pages/course-edit.js", contest: "./src/js/pages/contest.js", contestEdit: "./src/js/pages/contest-edit.js", profile: "./src/js/pages/profile.js", + liveLesson: "./src/js/pages/live-lesson.js", userGalleryEdit: "./src/js/pages/user-gallery-edit.js", mixpanel: "./src/js/third_party/mixpanel-2-latest.js", sprite: glob('./src/icons/*.svg'),