Merge remote-tracking branch 'origin/dev' into dev

remotes/origin/hasaccess
Vitaly Baev 8 years ago
commit 4fbec109d6
  1. 10
      api/v1/serializers/course.py
  2. 10
      api/v1/serializers/mixins.py
  3. 8
      api/v1/views.py
  4. 10
      apps/course/templates/course/_items.html
  5. 2
      apps/course/templates/course/blocks/comment.html
  6. 2
      apps/course/templates/course/course.html
  7. 2
      apps/notification/templates/notification/email/_base.html
  8. 2
      apps/payment/views.py
  9. 6
      project/templates/lilcity/edit_index.html
  10. 6
      project/templates/lilcity/index.html
  11. 4
      web/build/index.html
  12. 4
      web/build/ui-kit.html
  13. 6
      web/src/components/CourseRedactor.vue
  14. 2
      web/src/js/modules/comments.js
  15. 4
      web/src/js/modules/courses.js
  16. 54
      web/src/js/modules/mixpanel.js
  17. 5
      web/src/sass/_common.sass
  18. 2
      web/src/templates/partials/_header.pug
  19. 2
      web/src/templates/partials/_popups.pug

@ -203,6 +203,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
t.title = cdata['title']
t.lesson = lesson
t.txt = cdata['txt']
t.uuid = cdata['uuid']
t.save()
else:
t = Text.objects.create(
@ -210,6 +211,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
title=cdata['title'],
lesson=lesson,
txt=cdata['txt'],
uuid=cdata['uuid'],
)
elif ctype == 'image':
if 'id' in cdata and cdata['id']:
@ -218,6 +220,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
image.title = cdata['title']
image.lesson = lesson
image.img = ImageObject.objects.get(id=cdata['img'])
image.uuid = cdata['uuid']
image.save()
else:
image = Image.objects.create(
@ -225,6 +228,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
title=cdata['title'],
lesson=lesson,
img=ImageObject.objects.get(id=cdata['img']),
uuid=cdata['uuid'],
)
elif ctype == 'image-text':
if 'id' in cdata and cdata['id']:
@ -234,6 +238,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
it.lesson = lesson
it.img = ImageObject.objects.get(id=cdata['img'])
it.txt = cdata['txt']
it.uuid = cdata['uuid']
it.save()
else:
it = ImageText.objects.create(
@ -242,6 +247,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
lesson=lesson,
img=ImageObject.objects.get(id=cdata['img']),
txt=cdata['txt'],
uuid=cdata['uuid'],
)
elif ctype == 'video':
if 'id' in cdata and cdata['id']:
@ -250,6 +256,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
v.title = cdata['title']
v.lesson = lesson
v.url = cdata['url']
v.uuid = cdata['uuid']
v.save()
else:
v = Video.objects.create(
@ -257,6 +264,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
title=cdata['title'],
lesson=lesson,
url=cdata['url'],
uuid=cdata['uuid'],
)
elif ctype == 'images':
if 'id' in cdata and cdata['id']:
@ -264,6 +272,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
g.position = cdata['position']
g.title = cdata['title']
g.lesson = lesson
g.uuid = cdata['uuid']
g.save()
if 'images' in cdata:
for image in cdata['images']:
@ -276,6 +285,7 @@ class LessonCreateSerializer(serializers.ModelSerializer):
lesson=lesson,
position=cdata['position'],
title=cdata['title'],
uuid=cdata['uuid'],
)
if 'images' in cdata:
for image in cdata['images']:

@ -21,6 +21,7 @@ class DispatchContentMixin(object):
t.title = cdata['title']
t.course = course
t.txt = cdata['txt']
t.uuid = cdata['uuid']
t.save()
else:
t = Text.objects.create(
@ -28,6 +29,7 @@ class DispatchContentMixin(object):
title=cdata['title'],
course=course,
txt=cdata['txt'],
uuid=cdata['uuid'],
)
elif ctype == 'image':
if 'id' in cdata and cdata['id']:
@ -36,6 +38,7 @@ class DispatchContentMixin(object):
image.title = cdata['title']
image.course = course
image.img = ImageObject.objects.get(id=cdata['img'])
image.uuid = cdata['uuid']
image.save()
else:
image = Image.objects.create(
@ -43,6 +46,7 @@ class DispatchContentMixin(object):
title=cdata['title'],
course=course,
img=ImageObject.objects.get(id=cdata['img']),
uuid=cdata['uuid'],
)
elif ctype == 'image-text':
if 'id' in cdata and cdata['id']:
@ -52,6 +56,7 @@ class DispatchContentMixin(object):
it.course = course
it.img = ImageObject.objects.get(id=cdata['img'])
it.txt = cdata['txt']
it.uuid = cdata['uuid']
it.save()
else:
it = ImageText.objects.create(
@ -60,6 +65,7 @@ class DispatchContentMixin(object):
course=course,
img=ImageObject.objects.get(id=cdata['img']),
txt=cdata['txt'],
uuid=cdata['uuid'],
)
elif ctype == 'video':
if 'id' in cdata and cdata['id']:
@ -68,6 +74,7 @@ class DispatchContentMixin(object):
v.title = cdata['title']
v.course = course
v.url = cdata['url']
v.uuid = cdata['uuid']
v.save()
else:
v = Video.objects.create(
@ -75,6 +82,7 @@ class DispatchContentMixin(object):
title=cdata['title'],
course=course,
url=cdata['url'],
uuid=cdata['uuid'],
)
elif ctype == 'images':
if 'id' in cdata and cdata['id']:
@ -82,6 +90,7 @@ class DispatchContentMixin(object):
g.course = course
g.position = cdata['position']
g.title = cdata['title']
g.uuid = cdata['uuid']
g.save()
if 'images' in cdata:
for image in cdata['images']:
@ -99,6 +108,7 @@ class DispatchContentMixin(object):
course=course,
position=cdata['position'],
title=cdata['title'],
uuid=cdata['uuid'],
)
if 'images' in cdata:
for image in cdata['images']:

@ -341,11 +341,13 @@ class CommentViewSet(ExtendedModelViewSet):
queryset = self.queryset
is_deactivated = self.request.query_params.get('is_deactivated', '0')
if is_deactivated == '0':
return queryset
queryset = queryset
elif is_deactivated == '1':
return queryset.filter(deactivated_at__isnull=True)
queryset = queryset.filter(deactivated_at__isnull=True)
elif is_deactivated == '2':
return queryset.filter(deactivated_at__isnull=False)
queryset = queryset.filter(deactivated_at__isnull=False)
return queryset
class AuthorRequestViewSet(ExtendedModelViewSet):

@ -33,6 +33,16 @@
<div class="courses__time">ЧЕРНОВИК</div>
</div>
<div class="courses__label courses__label_draft"></div>
{% elif course.status == 3 %}
<div class="courses__soon">
<div class="courses__time">В АРХИВЕ</div>
</div>
<div class="courses__label courses__label_draft"></div>
{% elif course.status == 4 %}
<div class="courses__soon">
<div class="courses__time">ОТКЛОНЕН</div>
</div>
<div class="courses__label courses__label_draft"></div>
{% endif %}
</a>
<div class="courses__details">

@ -1,7 +1,7 @@
{% load static %}
{% if not node.deactivated_at %}
<a class="questions__anchor" id="question__{{ node.id }}"></a>
<div class="questions__item {% if node.is_child_node %}questions__item_reply{% endif %}">
<div id="question__replyto__{{ node.id }}" class="questions__item {% if node.is_child_node %}questions__item_reply{% endif %}">
{% if node.author.photo %}
<div class="questions__ava ava">
<img class="ava__pic" src="{{ node.author.photo.url }}">

@ -174,7 +174,7 @@
{% if course.is_deferred_start %}
<div class="video__soon">
<div class="video__title">Курс начнется:</div>
<div class="video__time">{{ course.deferred_start_at_humanize }}</div>
<div class="video__time" data-future>{{ course.deferred_start_at_humanize }}</div>
</div>
{% else %}
{% comment %} <svg class="icon icon-play">

@ -45,7 +45,7 @@
<table style="width:100%;padding:0;border-collapse:collapse;border-top:1px solid #979797">
<tbody>
<tr>
<td style="width:33.33%;padding:35px 0;vertical-align:middle;font-size:12px;color:#888888">2017 © Lil City, UAB. </td>
<td style="width:33.33%;padding:35px 0;vertical-align:middle;font-size:12px;color:#888888">{% now "Y" %} © Lil City, UAB. </td>
<td style="width:33.33%;padding:35px 0;vertical-align:middle;text-align:center">
<a href="#" style="display:inline-block;margin:0 5px;vertical-align:middle;font-size:0">
<img width="16" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAAEzo7pQAAABL1BMVEUAAAD///9VVVVAQEAzM2YrK1UkSUkuLkYnJzsiM0QgMEAeLTwmJkAkMT0hLDcdJzskLjciKzwgKDgeLTweKzceKjUcKDkgKzUdJjogKjggKTcfKDYeJjUeKTgdKDYcKTYfKDgeJzcdJjgdKTcdKDYeJzceKDcdJzUdJjccKDYcKDYeJzUeJzcdKDceJzYdJzYdJzUdJzYdJjcdKDYdJzUcJzcdJjYdKDUcJjUcJjceJzYdJjcdJzYcJzUdJzYdJzYdJjUdJjUcJzYdJzUdJjYdJzYcJjYdJzUcJjUdJzUdJjYdJjYcJzUcJzYdJjUcJzYdJzUdJzYdJjYcJjYcJzUcJzYdJzYdJjUcJzYcJzYcJzUdJjYdJjYdJjUdJzUdJzUcJzYcJjYcJjUcJjUcJjWqhzSBAAAAZHRSTlMAAQMEBQYHCw0PEBEUFRcaHB4gIiorLTA1Nzg5Q0RHUVJUV1hZXGZpamxtbm90d3t8hIyNj5GTlJmam5+qrLC4uru8wMHDyM3O0tTV19nd4Obn6Onr7O3w8vP09vf4+fr7/P3+8ZYTQAAAAZRJREFUOMt1k1dj00AQhD8TK4gOCQIEoofeQdgodAOGWJQkkGDR0c3//w08nM9eKXhfpJsZbZk9gTRG/AEJqAASCTiPjcQ/MsQVuAmIznH+ehBgA0pJysF5cQ2w/QAEfIYvyBNCgNh1FhFbDOjBwKWhbryqLmXa6GUi3fx6aOGpTL5owgbgzVWAaAawV5IueVVetJIS1ZpFDyAfh+MoAboq4qBP3QDqzKZwSZghaEoPdJ7pw5JpfVnXOalHpvX9AIoDsO8bANceB+BoCcCZ58Y9gPLcFHg1Am4bP3giaatpbnB13AJWc/rr5pwJ6Bs76i5A1LcWWbrXBehrveGISbihHuQqmBuFcirFFlrsNLrW2N9SAI4MJf100q87syVqKlhY0+vD/vXid13eKXirlel3naG/FVZwTGum9mm9bAvi+sfumeCW32ejxIqqpcDf06fFHQIOvNfv+6cOLl8YSnf/MwXAiYfvNj++uLEHO2apdL6TqUYktcvm8ZmrE4gGckUat8k4LZwG0eS3z8uqveuqzBOAf9XIdaH2KUvyAAAAAElFTkSuQmCC">

@ -46,7 +46,7 @@ class CourseBuyView(TemplateView):
host = request.scheme + '://' + request.get_host()
course = Course.objects.get(id=pk)
if request.user == course.author:
messages.error('Вы не можете приобрести свой курс.')
messages.error(request, 'Вы не можете приобрести свой курс.')
return redirect(reverse_lazy('course', args=[course.id]))
course_payment = CoursePayment.objects.create(
user=request.user,

@ -278,7 +278,7 @@
<div class="buy__row">
<div class="buy__col">
<div class="buy__head buy__head_main">
<div class="buy__title">Выбор урока/дня</div>
<div class="buy__title">Выбор курса/дня</div>
<div class="buy__content">При записи на 5 уроков скидка 10%.</div>
</div>
</div>
@ -345,6 +345,10 @@
</div>
</div>
<script type="text/javascript" src={% static "app.js" %}></script>
<script>
var schoolDiscount = parseFloat({{ config.SERVICE_DISCOUNT }});
var schoolAmountForDiscount = parseFloat({{ config.SERVICE_DISCOUNT_MIN_AMOUNT }});
</script>
{% block foot %}{% endblock foot %}
</body>

@ -131,7 +131,7 @@
</a> {% endcomment %}
</div>
</div>
<div class="header__group"><a class="header__section header__section_sub js-header-section {% active_link 'courses' %}" href="{% url 'courses' %}">ВИДЕО-КУРСЫ</a>
<div class="header__group"><a class="header__section header__section_sub js-header-section {% active_link 'courses' %}" href="{% url 'courses' %}">ВИДЕОКУРСЫ</a>
<div class="header__list js-header-list">
{% category_menu_items category %}
</div>
@ -248,7 +248,7 @@
</div>
<div class="footer__col footer__col_lg">
<div class="footer__group">
<div class="footer__copyright">2017 © Lil City, UAB.</div>
<div class="footer__copyright">{% now "Y" %} © Lil City, UAB.</div>
<div class="footer__links">
<a class="footer__link" href="{% url 'terms' %}">Договор-оферта</a>
<div class="footer__divider">|</div>
@ -439,7 +439,7 @@
<div class="buy__row">
<div class="buy__col">
<div class="buy__head buy__head_main">
<div class="buy__title">Выбор урока/дня</div>
<div class="buy__title">Выбор курса/дня</div>
<!-- <div class="buy__content">При записи на 5 уроков скидка 10%.</div> -->
</div>
</div>

@ -87,7 +87,7 @@
</a>
</div>
</div>
<div class="header__group"><a class="header__section header__section_sub js-header-section" href="#">ВИДЕО-КУРСЫ</a>
<div class="header__group"><a class="header__section header__section_sub js-header-section" href="#">ВИДЕОКУРСЫ</a>
<div class="header__list js-header-list">
<a class="header__link" href="#">
<div class="header__title">ПЕРСОНАЖ</div>
@ -264,7 +264,7 @@
<div class="buy__row">
<div class="buy__col">
<div class="buy__head buy__head_main">
<div class="buy__title">Выбор урока/дня</div>
<div class="buy__title">Выбор курса/дня</div>
<div class="buy__content">При записи на 5 уроков скидка 10%.</div>
</div>
</div>

@ -87,7 +87,7 @@
</a>
</div>
</div>
<div class="header__group"><a class="header__section header__section_sub js-header-section" href="#">ВИДЕО-КУРСЫ</a>
<div class="header__group"><a class="header__section header__section_sub js-header-section" href="#">ВИДЕОКУРСЫ</a>
<div class="header__list js-header-list">
<a class="header__link" href="#">
<div class="header__title">ПЕРСОНАЖ</div>
@ -352,7 +352,7 @@
<div class="buy__row">
<div class="buy__col">
<div class="buy__head buy__head_main">
<div class="buy__title">Выбор урока/дня</div>
<div class="buy__title">Выбор курса/дня</div>
<div class="buy__content">При записи на 5 уроков скидка 10%.</div>
</div>
</div>

@ -602,7 +602,7 @@
this.courseSyncHook = false;
this.courseSaving = false;
});
}, 2000);
}, 3000);
}
},
mounted() {
@ -663,10 +663,10 @@
},
displayPrice: {
get: function () {
return this.course.is_paid ? this.course.price : 0;
return this.course.is_paid ? (this.course.price || 0) : 0;
},
set: function (value) {
this.course.price = value;
this.course.price = value || 0;
}
},
categorySelect: {

@ -22,7 +22,7 @@ $(document).ready(function () {
.done(function (data) {
if (data.success === true) {
if (replyToValue > 0) {
$(`#question__${replyToValue}`).after(data.comment);
$(`#question__replyto__${replyToValue}`).after(data.comment);
} else {
$('.questions__list').append(data.comment);
}

@ -17,7 +17,9 @@ $(document).ready(function () {
$('div[data-future-course]').each((_, element) => {
const courseTime = parseInt($(element).attr('data-future-course-time')) + LIL_SERVER_TIME_DIFF;
const relativeTimeString = moment(courseTime, 'X').fromNow();
$(element).find('div.courses__time').text(relativeTimeString);
$(element).find('div.video__time').text(relativeTimeString);
});
}, 1000);
@ -31,7 +33,7 @@ $(document).ready(function () {
e.preventDefault();
const currentCategory = $(this).attr('data-category-name');
$('[data-category-name]').removeClass('active');
$(`[data-category-name=${currentCategory}]`).addClass('active');
$(`[data-category-name='${currentCategory}']`).addClass('active');
history.replace($(this).attr('data-category-url'));
load_courses($(this).attr('data-category-url'), true);
});

@ -1,34 +1,36 @@
import $ from 'jquery';
$(document).ready(function (e) {
mixpanel.identify(USER_ID);
let body = $('body'),
cource = $('.course');
if (typeof mixpanel != 'undefined') {
mixpanel.identify(USER_ID);
let body = $('body'),
cource = $('.course');
if (cource.length) {
mixpanel.track(
'Open course',
{ 'course_id': COURSE_ID }
);
};
body.on('click', '[data-popup]', function (e) {
let data = $(this).data('popup');
if (data === '.js-popup-buy') {
if (cource.length) {
mixpanel.track(
'Open school buy popup'
'Open course',
{ 'course_id': COURSE_ID }
);
}
});
body.on('click', '[data-course-buy]', function (e) {
e.preventDefault();
let href = $(this).attr('href');
let t = mixpanel.track(
'Click course buy button',
{ 'course_id': COURSE_ID },
function () {
window.location = href;
};
body.on('click', '[data-popup]', function (e) {
let data = $(this).data('popup');
if (data === '.js-popup-buy') {
mixpanel.track(
'Open school buy popup'
);
}
);
});
});
body.on('click', '[data-course-buy]', function (e) {
e.preventDefault();
let href = $(this).attr('href');
let t = mixpanel.track(
'Click course buy button',
{ 'course_id': COURSE_ID },
function () {
window.location = href;
}
);
});
}
});

@ -24,7 +24,7 @@
=fl
font-family: 'ProximaNova-Light', serif
$pink: #FF9393
$pink: #FF9393linear-gradient(-225deg, #FFE2EB 0%, #D8F5F5 100%)
$pink-light: #FDF8F9
$purple: #B995D9
$green: #8ECFC0
@ -780,6 +780,7 @@ a[name]
width: 100%
height: 100%
border-radius: 50%
object-fit: cover
&__input
position: absolute
top: 0
@ -1858,7 +1859,7 @@ a.grey-link
left: -10px
right: -10px
height: 2px
margin-top: 5px
margin-top: -2px
background-image: linear-gradient(-225deg, #FFE2EB 0%, #D8F5F5 100%)
&__title
display: table

@ -65,7 +65,7 @@ else
a.header__link(href='#')
div.header__title Контакты
.header__group
a.header__section.header__section_sub.js-header-section(href='#') ВИДЕО-КУРСЫ
a.header__section.header__section_sub.js-header-section(href='#') ВИДЕОКУРСЫ
.header__list.js-header-list
a.header__link(href='#')
.header__title ПЕРСОНАЖ

@ -71,7 +71,7 @@
.buy__row
.buy__col
.buy__head.buy__head_main
.buy__title Выбор урока/дня
.buy__title Выбор курса/дня
.buy__content При записи на 5 уроков скидка 10%.
.buy__col
.buy__head

Loading…
Cancel
Save